/******************************************************************************* * Copyright (c) 2014, 2015 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.tcf.internal.debug.ui.commands; import java.io.File; 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.LinkedHashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IStorage; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.layout.PixelConverter; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.ColumnPixelData; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableColorProvider; import org.eclipse.jface.viewers.ITableFontProvider; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.TreeViewer; 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.Device; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.FillLayout; 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.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.tcf.core.ErrorReport; import org.eclipse.tcf.internal.debug.launch.TCFLaunchDelegate; import org.eclipse.tcf.internal.debug.launch.TCFLaunchDelegate.PathMapRule; import org.eclipse.tcf.internal.debug.launch.TCFSourceLookupDirector; import org.eclipse.tcf.internal.debug.model.TCFLaunch; import org.eclipse.tcf.internal.debug.model.TCFMemoryRegion; import org.eclipse.tcf.internal.debug.model.TCFSymFileRef; import org.eclipse.tcf.internal.debug.ui.Activator; import org.eclipse.tcf.internal.debug.ui.ColorCache; import org.eclipse.tcf.internal.debug.ui.ImageCache; import org.eclipse.tcf.internal.debug.ui.model.TCFChildren; import org.eclipse.tcf.internal.debug.ui.model.TCFModel; import org.eclipse.tcf.internal.debug.ui.model.TCFNode; import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext; import org.eclipse.tcf.internal.debug.ui.model.TCFNodeLaunch; import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.protocol.JSON; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.services.IMemory; import org.eclipse.tcf.services.IMemoryMap; import org.eclipse.tcf.services.IPathMap; import org.eclipse.tcf.util.TCFDataCache; import org.eclipse.tcf.util.TCFTask; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; public class MemoryMapWidget { private static final int SIZING_TABLE_WIDTH = 500, SIZING_TABLE_HEIGHT = 300; private static final String PROP_CHILDREN = "_CHILDREN"; private static final String[] column_names = { "File", //$NON-NLS-1$ "Address", //$NON-NLS-1$ "Size", //$NON-NLS-1$ "Flags", //$NON-NLS-1$ "File offset/section", //$NON-NLS-1$ "Context query", //$NON-NLS-1$ }; private final Display display; private TCFModel model; private IChannel channel; private TCFNode selection; private Combo ctx_text; private Tree map_table; private TreeViewer 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 ArrayList<ModifyListener> modify_listeners = new ArrayList<ModifyListener>(); private Color color_error; private boolean editing; private boolean changed; private boolean disposed; private final ITreeContentProvider content_provider = new ITreeContentProvider() { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public void dispose() { } @Override public boolean hasChildren(Object element) { if (element instanceof IMemoryMap.MemoryRegion) { IMemoryMap.MemoryRegion region = (IMemoryMap.MemoryRegion)element; return region.getProperties().containsKey(PROP_CHILDREN); } return false; } @Override public Object getParent(Object element) { return null; } @Override public Object[] getElements(Object input) { ArrayList<IMemoryMap.MemoryRegion> all = new ArrayList<IMemoryMap.MemoryRegion>(); ArrayList<IMemoryMap.MemoryRegion> lst = cur_maps.get(input); if (lst != null) all.addAll(lst); all.addAll(target_map); ArrayList<IMemoryMap.MemoryRegion> roots = new ArrayList<IMemoryMap.MemoryRegion>(); ArrayList<IMemoryMap.MemoryRegion> removed = new ArrayList<IMemoryMap.MemoryRegion>(); for (int i = 0; i < all.size(); i++) { IMemoryMap.MemoryRegion region1 = all.get(i); boolean multiple = false; ArrayList<IMemoryMap.MemoryRegion> children = new ArrayList<IMemoryMap.MemoryRegion>(); if (region1.getFileName() != null) { for (int j = i + 1; j < all.size(); j++) { IMemoryMap.MemoryRegion region2 = all.get(j); if (!region1.equals(region2) && region1.getFileName().equals(region2.getFileName())) { multiple = true; children.add(region2); removed.add(region2); } } } if (!removed.contains(region1)) { if (multiple) { children.add(0, region1); removed.add(region1); Map<String, Object> props = new HashMap<String, Object>(); props.put(IMemoryMap.PROP_FILE_NAME, region1.getFileName()); props.put(PROP_CHILDREN, children.toArray()); roots.add(new TCFMemoryRegion(props)); } else { roots.add(region1); } } } return roots.toArray(); } @Override public Object[] getChildren(Object parentElement) { if (parentElement instanceof IMemoryMap.MemoryRegion) { IMemoryMap.MemoryRegion region = (IMemoryMap.MemoryRegion) parentElement; return (Object[]) region.getProperties().get(PROP_CHILDREN); } return null; } }; private final MapLabelProvider label_provider = new MapLabelProvider(); private class MapLabelProvider extends LabelProvider implements ITableLabelProvider, ITableColorProvider, ITableFontProvider { public Image getColumnImage(Object element, int column) { return null; } public String getColumnText(Object element, int column) { TCFMemoryRegion r = (TCFMemoryRegion) element; if (r.getProperties().containsKey(PROP_CHILDREN) && column != 0) { return ""; //$NON-NLS-1$ } switch (column) { case 0: return r.getFileName(); case 1: case 2: { BigInteger x = column == 1 ? r.addr : r.size; if (x == null) return ""; //$NON-NLS-1$ 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; //$NON-NLS-1$ } case 3: { int n = r.getFlags(); char[] bf = new char[3]; bf[0] = bf[1] = bf[2] = '-'; if ((n & IMemoryMap.FLAG_READ) != 0) bf[0] = 'r'; if ((n & IMemoryMap.FLAG_WRITE) != 0) bf[1] = 'w'; if ((n & IMemoryMap.FLAG_EXECUTE) != 0) bf[2] = 'x'; return new String(bf); } 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; //$NON-NLS-1$ } String s = r.getSectionName(); if (s != null) return s; return ""; //$NON-NLS-1$ } case 5: { String s = r.getContextQuery(); if (s == null) s = ""; return s; } } return ""; //$NON-NLS-1$ } 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 display.getSystemColor(SWT.COLOR_DARK_GREEN); } return display.getSystemColor(SWT.COLOR_DARK_BLUE); } String file_info = getSymbolFileInfo(r); // Set or reset the symbol file error tooltip marker TreeItem[] items = map_table.getItems(); for (TreeItem item : items) { if (item.getData() != null && item.getData().equals(r)) { item.setData("_TOOLTIP", file_info); //$NON-NLS-1$ } } if (file_info != null && file_info.contains("Symbol file error:") && color_error != null) { //$NON-NLS-1$ return color_error; } return map_table.getForeground(); } @Override public Font getFont(Object element, int columnIndex) { switch (columnIndex) { case 1: case 2: case 4: return JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT); } return null; } public String getText(Object element) { return element.toString(); } } private final IMemoryMap.MemoryMapListener listener = new IMemoryMap.MemoryMapListener() { @Override public void changed(final String context_id) { asyncExec(new Runnable() { @Override public void run() { if (disposed) return; if (mem_ctx == null) return; if (mem_ctx.getID() == null) return; if (!mem_ctx.getID().equals(context_id)) return; if (editing) { changed = true; } else if (cfg != null) { loadData(cfg); } } }); } }; public MemoryMapWidget(Composite composite, TCFNode node) { display = composite.getDisplay(); setTCFNode(node); createContextText(composite); createMemoryMapTable(composite); color_error = new Color(display, ColorCache.rgb_error); } /** * Dispose the widget and cleanup the created resources and listeners. */ public void dispose() { if (disposed) return; disposed = true; if (color_error != null) { color_error.dispose(); color_error = null; } // Remove the memory map listener if (channel != null) { Protocol.invokeAndWait(new Runnable() { @Override public void run() { IMemoryMap svc = channel.getRemoteService(IMemoryMap.class); if (svc != null) svc.removeListener(listener); } }); } model = null; channel = null; selection = null; } public boolean setTCFNode(TCFNode node) { if (node == selection) return false; // Remove the memory map listener if (channel != null) { Protocol.invokeAndWait(new Runnable() { @Override public void run() { IMemoryMap svc = channel.getRemoteService(IMemoryMap.class); if (svc != null) svc.removeListener(listener); } }); } if (node != null) { model = node.getModel(); channel = node.getChannel(); selection = node; // Register the memory map listener if (channel != null) { Protocol.invokeAndWait(new Runnable() { @Override public void run() { IMemoryMap svc = channel.getRemoteService(IMemoryMap.class); if (svc != null) svc.addListener(listener); } }); } } else { model = null; channel = null; selection = null; } return true; } 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); layout.marginHeight = 0; layout.marginWidth = 0; 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:"); //$NON-NLS-1$ 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); layout.marginHeight = 0; layout.marginWidth = 0; composite.setFont(font); composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_BOTH)); map_table = new Tree(composite, SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL); map_table.setFont(font); configureTable(map_table); map_table.addSelectionListener(new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { final IMemoryMap.MemoryRegion r = (IMemoryMap.MemoryRegion)((IStructuredSelection)table_viewer.getSelection()).getFirstElement(); if (r == null) return; // Async exec is used to workaround exception in jface asyncExec(new Runnable() { @Override public void run() { editRegion(r); } }); } @Override public void widgetSelected(SelectionEvent e) { update_map_buttons.run(); } }); table_viewer = new TreeViewer(map_table); table_viewer.setUseHashlookup(true); table_viewer.setColumnProperties(column_names); table_viewer.setContentProvider(content_provider); table_viewer.setLabelProvider(label_provider); map_table.pack(); createMapButtons(composite); } protected String getColumnText(int column) { if (column < column_names.length && column >= 0) return column_names[column]; return ""; //$NON-NLS-1$ } protected void configureTable(final Tree table) { GridData data = new GridData(GridData.FILL_BOTH); data.widthHint = SIZING_TABLE_WIDTH; data.heightHint = SIZING_TABLE_HEIGHT; table.setLayoutData(data); final TreeColumn col_file = new TreeColumn(table, 0); col_file.setResizable(true); col_file.setAlignment(SWT.LEFT); col_file.setText(getColumnText(0)); final TreeColumn col_addr = new TreeColumn(table, 1); col_addr.setResizable(true); col_addr.setAlignment(SWT.LEFT); col_addr.setText(getColumnText(1)); final TreeColumn col_size = new TreeColumn(table, 2); col_size.setResizable(true); col_size.setAlignment(SWT.LEFT); col_size.setText(getColumnText(2)); final TreeColumn col_flags = new TreeColumn(table, 3); col_flags.setResizable(true); col_flags.setAlignment(SWT.LEFT); col_flags.setText(getColumnText(3)); final TreeColumn col_offset = new TreeColumn(table, 4); col_offset.setResizable(true); col_offset.setAlignment(SWT.LEFT); col_offset.setText(getColumnText(4)); final TreeColumn col_context = new TreeColumn(table, 5); col_context.setResizable(true); col_context.setAlignment(SWT.LEFT); col_context.setText(getColumnText(5)); TableLayout layout = new TableLayout(); layout.addColumnData(new ColumnPixelData(300)); layout.addColumnData(new ColumnPixelData(100)); layout.addColumnData(new ColumnPixelData(80)); layout.addColumnData(new ColumnPixelData(50)); layout.addColumnData(new ColumnPixelData(140)); layout.addColumnData(new ColumnPixelData(140)); // "Symbol File Errors" are displayed as tooltip on the table item. // See // http://git.eclipse.org/c/platform/eclipse.platform.swt.git/tree/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java. // Disable native tooltip table.setToolTipText(""); //$NON-NLS-1$ // Implement a "fake" tooltip final Listener labelListener = new Listener() { public void handleEvent(Event event) { Label label = (Label) event.widget; Shell shell = label.getShell(); switch (event.type) { case SWT.MouseDown: Event e = new Event(); e.item = (TreeItem) label.getData("_TABLEITEM"); //$NON-NLS-1$ // Assuming table is single select, set the selection as if // the mouse down event went through to the table table.setSelection(new TreeItem[] { (TreeItem) e.item }); table.notifyListeners(SWT.Selection, e); shell.dispose(); table.setFocus(); break; case SWT.MouseExit: shell.dispose(); break; } } }; Listener table_listener = new Listener() { Shell tip = null; Label label = null; public void handleEvent(Event event) { switch (event.type) { case SWT.Dispose: case SWT.KeyDown: case SWT.MouseMove: { if (tip == null) break; tip.dispose(); tip = null; label = null; break; } case SWT.MouseHover: { TreeItem item = table.getItem(new Point(event.x, event.y)); if (item != null && item.getData("_TOOLTIP") instanceof String) { //$NON-NLS-1$ if (tip != null && !tip.isDisposed()) tip.dispose(); tip = new Shell(table.getShell(), SWT.ON_TOP | SWT.NO_FOCUS | SWT.TOOL); tip.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); FillLayout layout = new FillLayout(); layout.marginWidth = 2; tip.setLayout(layout); label = new Label(tip, SWT.NONE); label.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); label.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); label.setData("_TABLEITEM", item); //$NON-NLS-1$ label.setText((String) item.getData("_TOOLTIP")); //$NON-NLS-1$ label.addListener(SWT.MouseExit, labelListener); label.addListener(SWT.MouseDown, labelListener); Point size = tip.computeSize(SWT.DEFAULT, SWT.DEFAULT); Point pt = table.toDisplay(event.x - 20, event.y - size.y); tip.setBounds(pt.x, pt.y, size.x, size.y); tip.setVisible(true); } } } } }; table.addListener(SWT.Dispose, table_listener); table.addListener(SWT.KeyDown, table_listener); table.addListener(SWT.MouseMove, table_listener); table.addListener(SWT.MouseHover, table_listener); table.setLayout(layout); table.setHeaderVisible(true); table.setLinesVisible(true); } protected final TreeViewer getViewer() { return table_viewer; } private void createMapButtons(Composite parent) { Font font = parent.getFont(); Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; 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... "); //$NON-NLS-1$ GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); PixelConverter converter = new PixelConverter(button_add); gd.widthHint = converter.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH); button_add.setLayoutData(gd); 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(); notifyModifyListeners(); } } }); final MenuItem item_add = new MenuItem(menu, SWT.PUSH); item_add.setText("&Add..."); //$NON-NLS-1$ item_add.addSelectionListener(sel_adapter); item_add.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD)); final Button button_edit = new Button(composite, SWT.PUSH); button_edit.setText(" E&dit... "); //$NON-NLS-1$ 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..."); //$NON-NLS-1$ item_edit.addSelectionListener(sel_adapter); final Button button_remove = new Button(composite, SWT.PUSH); button_remove.setText(" &Remove "); //$NON-NLS-1$ 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(); notifyModifyListeners(); } }); final MenuItem item_remove = new MenuItem(menu, SWT.PUSH); item_remove.setText("&Remove"); //$NON-NLS-1$ item_remove.addSelectionListener(sel_adapter); item_remove.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_ETOOL_DELETE)); final Button button_locate = new Button(composite, SWT.PUSH | SWT.WRAP); button_locate.setText(" Locate File... "); //$NON-NLS-1$ GridData layoutData = new GridData(GridData.FILL_HORIZONTAL); layoutData.widthHint = 50; button_locate.setLayoutData(layoutData); button_locate.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; locateSymbolFile(r); } }); new MenuItem(menu, SWT.SEPARATOR); final MenuItem item_locate = new MenuItem(menu, SWT.PUSH); item_locate.setText("Locate File..."); //$NON-NLS-1$ item_locate.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 && !r.getProperties().containsKey(PROP_CHILDREN)); button_remove.setEnabled(manual); item_add.setEnabled(selected_mem_map_id != null); item_edit.setEnabled(r != null && !r.getProperties().containsKey(PROP_CHILDREN)); item_remove.setEnabled(manual); String symbolFileInfo = getSymbolFileInfo(r); boolean enabled = symbolFileInfo != null && symbolFileInfo.contains("Symbol file error:") //$NON-NLS-1$ && r.getFileName() != null; button_locate.setEnabled(enabled); item_locate.setEnabled(enabled); } }; update_map_buttons.run(); } private void editRegion(IMemoryMap.MemoryRegion r) { try { editing = true; 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) { for (int n = 0; n < lst.size(); n++) { if (lst.get(n) == r) { lst.set(n, new TCFMemoryRegion(props)); table_viewer.refresh(); notifyModifyListeners(); } } } } } finally { editing = false; if (changed) { loadData(cfg); changed = false; } } } private void locateSymbolFile(IMemoryMap.MemoryRegion r) { Assert.isNotNull(r); Map<String, Object> props = new HashMap<String, Object>(r.getProperties()); FileDialog dialog = new FileDialog(map_table.getShell(), SWT.OPEN); IPath workSpacePath = ResourcesPlugin.getWorkspace().getRoot().getLocation(); dialog.setFilterPath(workSpacePath.toString()); dialog.setText("Locate Symbol File"); //$NON-NLS-1$ String symbolFile = dialog.open(); if (symbolFile != null && new File(symbolFile).exists()) { // Create the new path map rule Map<String, Object> properties = new LinkedHashMap<String, Object>(); properties.put(IPathMap.PROP_SOURCE, props.get(IMemoryMap.PROP_FILE_NAME)); properties.put(IPathMap.PROP_DESTINATION, symbolFile); PathMapRule rule = new PathMapRule(properties); if (cfg != null) { try { ILaunchConfigurationWorkingCopy wc = cfg instanceof ILaunchConfigurationWorkingCopy ? (ILaunchConfigurationWorkingCopy) cfg : cfg .getWorkingCopy(); String s = wc.getAttribute(TCFLaunchDelegate.ATTR_PATH_MAP, ""); //$NON-NLS-1$ List<PathMapRule> map = TCFLaunchDelegate.parsePathMapAttribute(s); map.add(0, rule); StringBuilder bf = new StringBuilder(); for (IPathMap.PathMapRule m : map) { bf.append(m.toString()); } if (bf.length() == 0) wc.removeAttribute(TCFLaunchDelegate.ATTR_PATH_MAP); else wc.setAttribute(TCFLaunchDelegate.ATTR_PATH_MAP, bf.toString()); if (wc.isDirty()) wc.doSave(); } catch (CoreException e) { Activator.getDefault().getLog().log(e.getStatus()); } } } } 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); //$NON-NLS-1$ } } private void writeMemoryMapAttribute(ILaunchConfigurationWorkingCopy copy) throws Exception { String s = null; final ArrayList<Map<String, Object>> lst = new ArrayList<Map<String, Object>>(); for (ArrayList<IMemoryMap.MemoryRegion> x : cur_maps.values()) { for (IMemoryMap.MemoryRegion r : x) lst.add(r.getProperties()); } 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 = ""; //$NON-NLS-1$ 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")); //$NON-NLS-1$ 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; // Don't log error. This is expected if the selected node has no containing memory context // Activator.log("Cannot get selected memory node", x); return null; } } private String getSymbolFileInfo(IMemoryMap.MemoryRegion region) { if (region != null && region.getProperties().containsKey(PROP_CHILDREN)) { region = (IMemoryMap.MemoryRegion)((Object[])region.getProperties().get(PROP_CHILDREN))[0]; } final IMemoryMap.MemoryRegion r = region; if (channel == null || channel.getState() != IChannel.STATE_OPEN) return null; if (r == null || r.getAddress() == null || r.getFileName() == null) return null; try { String symFileInfo = new TCFTask<String>(channel) { public void run() { if (selected_mem_map_node == null) { done(null); return; } TCFDataCache<TCFNodeExecContext> mem_cache = model.searchMemoryContext(selected_mem_map_node); if (mem_cache == null) { done(null); return; } if (!mem_cache.validate(this)) return; if (mem_cache.getError() != null) { error(mem_cache.getError()); return; } StringBuilder symbolFileInfo = new StringBuilder(); final TCFNodeExecContext mem_node = mem_cache.getData(); if (mem_node != null) { TCFDataCache<TCFSymFileRef> sym_cache = mem_node.getSymFileInfo(JSON.toBigInteger(r.getAddress())); if (sym_cache != null) { if (!sym_cache.validate(this)) return; TCFSymFileRef sym_data = sym_cache.getData(); if (sym_data != null) { if (sym_data.props != null) { String sym_file_name = (String)sym_data.props.get("FileName"); //$NON-NLS-1$ if (sym_file_name != null && !sym_file_name.equals(r.getFileName())) symbolFileInfo.append("Symbol file name: ").append(sym_file_name); //$NON-NLS-1$ @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>)sym_data.props.get("FileError"); //$NON-NLS-1$ if (map != null) { if (symbolFileInfo.length() > 0) symbolFileInfo.append("\n"); //$NON-NLS-1$ String msg = TCFModel.getErrorMessage(new ErrorReport("", map), false); //$NON-NLS-1$ symbolFileInfo.append("Symbol file error: ").append(msg); //$NON-NLS-1$ } } else if (sym_data.error == null) { symbolFileInfo.append(r.getFileName()); symbolFileInfo.append(", "); //$NON-NLS-1$ symbolFileInfo.append(mem_node.getID()); } if (sym_data.error != null) { symbolFileInfo.append("Symbol file error: ").append(TCFModel.getErrorMessage(sym_data.error, false)); //$NON-NLS-1$ } } } } done(symbolFileInfo.length() > 0 ? symbolFileInfo.toString() : null); } }.get(); if (symFileInfo != null && symFileInfo.startsWith(r.getFileName())) { String id = symFileInfo.split(", ")[1]; //$NON-NLS-1$ symFileInfo = null; if (!new File(r.getFileName()).exists()) { final TCFLaunch launch = findLaunch(); if (launch != null) { Object mapped = TCFSourceLookupDirector.lookup(launch, id, r.getFileName()); if (!(mapped instanceof IStorage) || !((IStorage)mapped).getFullPath().toFile().exists()) { symFileInfo = "Symbol file error: No such file or directory"; //$NON-NLS-1$ } } } } return symFileInfo; } catch (Exception x) { if (channel.getState() != IChannel.STATE_OPEN) return null; Activator.log("Cannot get selected symbol file info", x); //$NON-NLS-1$ return null; } } protected TCFLaunch findLaunch() { for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { if (launch instanceof TCFLaunch && launch.getLaunchConfiguration().equals(cfg instanceof ILaunchConfigurationWorkingCopy ? ((ILaunchConfigurationWorkingCopy)cfg).getOriginal() : cfg)) { return (TCFLaunch)launch; } } 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); //$NON-NLS-1$ } } private boolean isLocalEntry(IMemoryMap.MemoryRegion r) { /* Check if launch configuration contains the entry */ if (r == null) return false; String f0 = r.getFileName(); BigInteger a0 = JSON.toBigInteger(r.getAddress()); ArrayList<IMemoryMap.MemoryRegion> map = cur_maps.get(selected_mem_map_id); if (map == null) return false; for (IMemoryMap.MemoryRegion c : map) { String f1 = c.getFileName(); if (f0 != f1) { if (f0 == null) continue; if (!f0.equals(f1)) continue; } Number a1 = c.getAddress(); if (a0 != a1) { if (a0 == null) continue; } return true; } return false; } 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()) { if (isLocalEntry(m.region)) { String fnm = m.region.getFileName(); if (fnm != null) loaded_files.add(fnm); } else { /* * Foreign entry, added by another client or * by the target itself */ target_map.add(new TCFMemoryRegion(m.region.getProperties())); } } } } done(true); } }.get(); } catch (Exception x) { if (channel.getState() != IChannel.STATE_OPEN) return; Activator.log("Cannot load target memory map", x); //$NON-NLS-1$ } } 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; } public void addModifyListener(ModifyListener l) { modify_listeners.add(l); } private void notifyModifyListeners() { for (ModifyListener l : modify_listeners) { l.modifyText(null); } } private void asyncExec(Runnable r) { synchronized (Device.class) { if (display.isDisposed()) return; display.asyncExec(r); } } }