/******************************************************************************* * Copyright (c) 2011 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tm.internal.tcf.debug.ui.commands; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableColorProvider; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.tm.internal.tcf.debug.launch.TCFLaunchDelegate; import org.eclipse.tm.internal.tcf.debug.model.TCFMemoryRegion; import org.eclipse.tm.internal.tcf.debug.ui.Activator; import org.eclipse.tm.internal.tcf.debug.ui.ImageCache; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFChildren; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFModel; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNode; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeExecContext; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeLaunch; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.JSON; import org.eclipse.tm.tcf.services.IMemory; import org.eclipse.tm.tcf.services.IMemoryMap; import org.eclipse.tm.tcf.util.TCFDataCache; import org.eclipse.tm.tcf.util.TCFTask; public class MemoryMapWidget { private static final int SIZING_TABLE_WIDTH = 700, SIZING_TABLE_HEIGHT = 300; private static final String[] column_names = { "File", "Address", "Size", "Flags", "File offset/section", }; private final TCFModel model; private final IChannel channel; private final TCFNode selection; private Combo ctx_text; private Table map_table; private TableViewer table_viewer; private Runnable update_map_buttons; private final Map<String,ArrayList<IMemoryMap.MemoryRegion>> org_maps = new HashMap<String,ArrayList<IMemoryMap.MemoryRegion>>(); private final Map<String,ArrayList<IMemoryMap.MemoryRegion>> cur_maps = new HashMap<String,ArrayList<IMemoryMap.MemoryRegion>>(); private final ArrayList<IMemoryMap.MemoryRegion> target_map = new ArrayList<IMemoryMap.MemoryRegion>(); private final HashMap<String,TCFNodeExecContext> target_map_nodes = new HashMap<String,TCFNodeExecContext>(); private TCFNodeExecContext selected_mem_map_node; private IMemory.MemoryContext mem_ctx; private ILaunchConfiguration cfg; private final HashSet<String> loaded_files = new HashSet<String>(); private String selected_mem_map_id; private final IStructuredContentProvider content_provider = new IStructuredContentProvider() { public Object[] getElements(Object input) { ArrayList<IMemoryMap.MemoryRegion> res = new ArrayList<IMemoryMap.MemoryRegion>(); ArrayList<IMemoryMap.MemoryRegion> lst = cur_maps.get((String)input); if (lst != null) res.addAll(lst); res.addAll(target_map); return res.toArray(); } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } }; private class MapLabelProvider extends LabelProvider implements ITableLabelProvider, ITableColorProvider { public Image getColumnImage(Object element, int column) { return null; } public String getColumnText(Object element, int column) { TCFMemoryRegion r = (TCFMemoryRegion)element; switch (column) { case 0: return r.getFileName(); case 1: case 2: { BigInteger x = column == 1 ? r.addr : r.size; if (x == null) return ""; String s = x.toString(16); int sz = 0; if (mem_ctx != null) sz = mem_ctx.getAddressSize() * 2; int l = sz - s.length(); if (l < 0) l = 0; if (l > 16) l = 16; return "0x0000000000000000".substring(0, 2 + l) + s; } case 3: { int n = r.getFlags(); StringBuffer bf = new StringBuffer(); if ((n & IMemoryMap.FLAG_READ) != 0) bf.append('r'); if ((n & IMemoryMap.FLAG_WRITE) != 0) bf.append('w'); if ((n & IMemoryMap.FLAG_EXECUTE) != 0) bf.append('x'); return bf.toString(); } case 4: { Number n = r.getOffset(); if (n != null) { BigInteger x = JSON.toBigInteger(n); String s = x.toString(16); int l = 16 - s.length(); if (l < 0) l = 0; if (l > 16) l = 16; return "0x0000000000000000".substring(0, 2 + l) + s; } String s = r.getSectionName(); if (s != null) return s; return ""; } } return ""; } public Color getBackground(Object element, int columnIndex) { return map_table.getBackground(); } public Color getForeground(Object element, int columnIndex) { TCFMemoryRegion r = (TCFMemoryRegion)element; if (r.getProperties().get(IMemoryMap.PROP_ID) != null) { String fnm = r.getFileName(); if (fnm != null && loaded_files.contains(fnm)) { return map_table.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN); } return map_table.getDisplay().getSystemColor(SWT.COLOR_DARK_BLUE); } return map_table.getForeground(); } public String getText(Object element) { return element.toString(); } } public MemoryMapWidget(Composite composite, TCFNode node) { if (node != null) { model = node.getModel(); channel = node.getChannel(); selection = node; } else { model = null; channel = null; selection = null; } createContextText(composite); createMemoryMapTable(composite); } public String getMemoryMapID() { return selected_mem_map_id; } private void createContextText(Composite parent) { Font font = parent.getFont(); Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(2, false); composite.setFont(font); composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); Label props_label = new Label(composite, SWT.WRAP); props_label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); props_label.setFont(font); props_label.setText("&Debug context:"); ctx_text = new Combo(composite, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); ctx_text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); ctx_text.setFont(font); ctx_text.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { selected_mem_map_id = ctx_text.getText(); selected_mem_map_node = target_map_nodes.get(selected_mem_map_id); loadTargetMemoryMap(); table_viewer.setInput(selected_mem_map_id); if (selected_mem_map_id.length() == 0) selected_mem_map_id = null; update_map_buttons.run(); } }); } private void createMemoryMapTable(Composite parent) { Font font = parent.getFont(); Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(2, false); composite.setFont(font); composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_BOTH)); map_table = new Table(composite, SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); map_table.setFont(font); GridData data = new GridData(GridData.FILL_BOTH); data.widthHint = SIZING_TABLE_WIDTH; data.heightHint = SIZING_TABLE_HEIGHT; map_table.setLayoutData(data); int w = SIZING_TABLE_WIDTH / (column_names.length + 12); for (int i = 0; i < column_names.length; i++) { final TableColumn column = new TableColumn(map_table, SWT.LEAD, i); column.setMoveable(false); column.setText(column_names[i]); switch (i) { case 0: column.setWidth(w * 10); break; case 1: case 2: case 4: column.setWidth(w * 2); break; default: column.setWidth(w); break; } } map_table.setHeaderVisible(true); map_table.setLinesVisible(true); map_table.addSelectionListener(new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { IMemoryMap.MemoryRegion r = (IMemoryMap.MemoryRegion)((IStructuredSelection) table_viewer.getSelection()).getFirstElement(); if (r == null) return; editRegion(r); } @Override public void widgetSelected(SelectionEvent e) { update_map_buttons.run(); } }); table_viewer = new TableViewer(map_table); table_viewer.setUseHashlookup(true); table_viewer.setColumnProperties(column_names); table_viewer.setContentProvider(content_provider); table_viewer.setLabelProvider(new MapLabelProvider()); createMapButtons(composite); } private void createMapButtons(Composite parent) { Font font = parent.getFont(); Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); composite.setFont(font); composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL)); Menu menu = new Menu(map_table); SelectionAdapter sel_adapter = null; final Button button_add = new Button(composite, SWT.PUSH); button_add.setText("&Add..."); button_add.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); button_add.addSelectionListener(sel_adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String id = ctx_text.getText(); if (id == null || id.length() == 0) return; Map<String,Object> props = new HashMap<String,Object>(); Image image = ImageCache.getImage(ImageCache.IMG_MEMORY_MAP); if (new MemoryMapItemDialog(map_table.getShell(), image, props, true).open() == Window.OK) { props.put(IMemoryMap.PROP_ID, id); ArrayList<IMemoryMap.MemoryRegion> lst = cur_maps.get(id); if (lst == null) cur_maps.put(id, lst = new ArrayList<IMemoryMap.MemoryRegion>()); lst.add(new TCFMemoryRegion(props)); table_viewer.refresh(); } } }); final MenuItem item_add = new MenuItem(menu, SWT.PUSH); item_add.setText("&Add..."); item_add.addSelectionListener(sel_adapter); final Button button_edit = new Button(composite, SWT.PUSH); button_edit.setText("E&dit..."); button_edit.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); button_edit.addSelectionListener(sel_adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { IMemoryMap.MemoryRegion r = (IMemoryMap.MemoryRegion)((IStructuredSelection) table_viewer.getSelection()).getFirstElement(); if (r == null) return; editRegion(r); } }); final MenuItem item_edit = new MenuItem(menu, SWT.PUSH); item_edit.setText("E&dit..."); item_edit.addSelectionListener(sel_adapter); final Button button_remove = new Button(composite, SWT.PUSH); button_remove.setText("&Remove"); button_remove.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); button_remove.addSelectionListener(sel_adapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String id = ctx_text.getText(); if (id == null || id.length() == 0) return; IMemoryMap.MemoryRegion r = (IMemoryMap.MemoryRegion)((IStructuredSelection) table_viewer.getSelection()).getFirstElement(); if (r == null) return; ArrayList<IMemoryMap.MemoryRegion> lst = cur_maps.get(id); if (lst != null && lst.remove(r)) table_viewer.refresh(); } }); final MenuItem item_remove = new MenuItem(menu, SWT.PUSH); item_remove.setText("&Remove"); item_remove.addSelectionListener(sel_adapter); map_table.setMenu(menu); update_map_buttons = new Runnable() { public void run() { IMemoryMap.MemoryRegion r = (IMemoryMap.MemoryRegion)((IStructuredSelection) table_viewer.getSelection()).getFirstElement(); boolean manual = r != null && r.getProperties().get(IMemoryMap.PROP_ID) != null; button_add.setEnabled(selected_mem_map_id != null); button_edit.setEnabled(r != null); button_remove.setEnabled(manual); item_add.setEnabled(selected_mem_map_id != null); item_edit.setEnabled(r != null); item_remove.setEnabled(manual); } }; update_map_buttons.run(); } private void editRegion(IMemoryMap.MemoryRegion r) { String id = ctx_text.getText(); if (id == null || id.length() == 0) return; Map<String,Object> props = r.getProperties(); boolean enable_editing = props.get(IMemoryMap.PROP_ID) != null; if (enable_editing) props = new HashMap<String,Object>(props); Image image = ImageCache.getImage(ImageCache.IMG_MEMORY_MAP); if (new MemoryMapItemDialog(map_table.getShell(), image, props, enable_editing).open() == Window.OK && enable_editing) { ArrayList<IMemoryMap.MemoryRegion> lst = cur_maps.get(id); if (lst != null) { int n = lst.indexOf(r); if (n >= 0) { lst.set(n, new TCFMemoryRegion(props)); table_viewer.refresh(); } } } } private void readMemoryMapAttribute() { cur_maps.clear(); try { new TCFTask<Boolean>() { public void run() { try { TCFLaunchDelegate.getMemMapsAttribute(cur_maps, cfg); done(true); } catch (Exception e) { error(e); } } }.get(); } catch (Exception x) { Activator.log("Invalid launch cofiguration attribute", x); } } private void writeMemoryMapAttribute(ILaunchConfigurationWorkingCopy copy) throws Exception { String s = null; final ArrayList<IMemoryMap.MemoryRegion> lst = new ArrayList<IMemoryMap.MemoryRegion>(); for (ArrayList<IMemoryMap.MemoryRegion> x : cur_maps.values()) lst.addAll(x); if (lst.size() > 0) { s = new TCFTask<String>() { public void run() { try { done(JSON.toJSON(lst)); } catch (IOException e) { error(e); } } }.getIO(); } copy.setAttribute(TCFLaunchDelegate.ATTR_MEMORY_MAP, s); } public void loadData(ILaunchConfiguration cfg) { this.cfg = cfg; cur_maps.clear(); org_maps.clear(); loadTargetMemoryNodes(); readMemoryMapAttribute(); for (String id : cur_maps.keySet()) { org_maps.put(id, new ArrayList<IMemoryMap.MemoryRegion>(cur_maps.get(id))); } // Update controls String map_id = getSelectedMemoryNode(); HashSet<String> ids = new HashSet<String>(target_map_nodes.keySet()); if (map_id != null) ids.add(map_id); ids.addAll(cur_maps.keySet()); String[] arr = ids.toArray(new String[ids.size()]); Arrays.sort(arr); ctx_text.removeAll(); for (String id : arr) ctx_text.add(id); if (map_id == null && arr.length > 0) map_id = arr[0]; if (map_id == null) map_id = ""; ctx_text.setText(map_id); } private String getSelectedMemoryNode() { if (channel == null || channel.getState() != IChannel.STATE_OPEN) return null; try { return new TCFTask<String>(channel) { public void run() { TCFDataCache<TCFNodeExecContext> mem_cache = model.searchMemoryContext(selection); if (mem_cache == null) { error(new Exception("Context does not provide memory access")); return; } if (!mem_cache.validate(this)) return; if (mem_cache.getError() != null) { error(mem_cache.getError()); return; } String id = null; TCFNodeExecContext mem_node = mem_cache.getData(); if (mem_node != null) { TCFDataCache<TCFNodeExecContext> syms_cache = mem_node.getSymbolsNode(); if (!syms_cache.validate(this)) return; TCFNodeExecContext syms_node = syms_cache.getData(); if (syms_node != null) { TCFDataCache<IMemory.MemoryContext> mem_ctx = syms_node.getMemoryContext(); if (!mem_ctx.validate(this)) return; if (mem_ctx.getData() != null) { if (syms_node.getModel().getLaunch().isMemoryMapPreloadingSupported()) { TCFDataCache<String> name_cache = syms_node.getFullName(); if (!name_cache.validate(this)) return; id = name_cache.getData(); } else { id = mem_ctx.getData().getName(); } if (id == null) id = syms_node.getID(); } } } done(id); } }.get(); } catch (Exception x) { if (channel.getState() != IChannel.STATE_OPEN) return null; Activator.log("Cannot get selected memory node", x); return null; } } private void loadTargetMemoryNodes() { target_map_nodes.clear(); if (channel == null || channel.getState() != IChannel.STATE_OPEN) return; try { new TCFTask<Boolean>(channel) { public void run() { TCFNodeLaunch n = model.getRootNode(); if (!collectMemoryNodes(n.getFilteredChildren())) return; done(true); } private boolean collectMemoryNodes(TCFChildren children) { if (!children.validate(this)) return false; Map<String,TCFNode> m = children.getData(); if (m != null) { for (TCFNode n : m.values()) { if (n instanceof TCFNodeExecContext) { TCFNodeExecContext exe = (TCFNodeExecContext)n; if (!collectMemoryNodes(exe.getChildren())) return false; TCFDataCache<TCFNodeExecContext> syms_cache = exe.getSymbolsNode(); if (!syms_cache.validate(this)) return false; TCFNodeExecContext syms_node = syms_cache.getData(); if (syms_node != null) { TCFDataCache<IMemory.MemoryContext> mem_ctx = syms_node.getMemoryContext(); if (!mem_ctx.validate(this)) return false; if (mem_ctx.getData() != null) { String id = null; if (syms_node.getModel().getLaunch().isMemoryMapPreloadingSupported()) { TCFDataCache<String> name_cache = syms_node.getFullName(); if (!name_cache.validate(this)) return false; id = name_cache.getData(); } else { id = mem_ctx.getData().getName(); } if (id == null) id = syms_node.getID(); target_map_nodes.put(id, syms_node); } } } } } return true; } }.get(); } catch (Exception x) { if (channel.getState() != IChannel.STATE_OPEN) return; Activator.log("Cannot load target memory context info", x); } } private void loadTargetMemoryMap() { loaded_files.clear(); target_map.clear(); mem_ctx = null; if (channel == null || channel.getState() != IChannel.STATE_OPEN) return; try { new TCFTask<Boolean>(channel) { public void run() { if (selected_mem_map_node != null && !selected_mem_map_node.isDisposed()) { TCFDataCache<IMemory.MemoryContext> mem_cache = selected_mem_map_node.getMemoryContext(); if (!mem_cache.validate(this)) return; if (mem_cache.getError() != null) { error(mem_cache.getError()); return; } mem_ctx = mem_cache.getData(); TCFDataCache<TCFNodeExecContext.MemoryRegion[]> map_cache = selected_mem_map_node.getMemoryMap(); if (!map_cache.validate(this)) return; if (map_cache.getError() != null) { error(map_cache.getError()); return; } if (map_cache.getData() != null) { for (TCFNodeExecContext.MemoryRegion m : map_cache.getData()) { Map<String,Object> props = m.region.getProperties(); if (props.get(IMemoryMap.PROP_ID) != null) { String fnm = m.region.getFileName(); if (fnm != null) loaded_files.add(fnm); } else { target_map.add(new TCFMemoryRegion(props)); } } } } done(true); } }.get(); } catch (Exception x) { if (channel.getState() != IChannel.STATE_OPEN) return; Activator.log("Cannot load target memory map", x); } } public boolean saveData(ILaunchConfigurationWorkingCopy copy) throws Exception { boolean loaded_files_ok = true; if (selected_mem_map_id != null) { ArrayList<IMemoryMap.MemoryRegion> lst = cur_maps.get(selected_mem_map_id); if (lst != null) { for (IMemoryMap.MemoryRegion r : lst) { String fnm = r.getFileName(); if (fnm != null && !loaded_files.contains(fnm)) loaded_files_ok = false; } if (lst.size() == 0) cur_maps.remove(selected_mem_map_id); } } if (!loaded_files_ok || !org_maps.equals(cur_maps)) { writeMemoryMapAttribute(copy); return true; } return false; } }