Initial version of the "function name mapper" for the Callstack View.
It currently only supports a text file listing addresses followed by
names, like the output of "nm [binary] > out.txt".
The next steps will be:
- Add support for reading the binary directly, by tapping into the
relevant CDT components.
- Add a separate dialog that shows the currently imported file name(s).
- Add support for dynamically-loaded libraries. This will require some
work on the tracer side.
Change-Id: Id622cce487965b8c770fc40218685ec7605924ff
Signed-off-by: Alexandre Montplaisir <alexmonthy@voxpopuli.im>
Reviewed-on: https://git.eclipse.org/r/17978
Tested-by: Hudson CI
Reviewed-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
IP-Clean: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Tested-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, start);
assertEquals(1, cs.length);
- assertEquals("0x40472b", cs[0]);
+ assertEquals("40472b", cs[0]);
}
/**
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, 1379361250310000000L);
assertEquals(2, cs.length);
- assertEquals("0x40472b", cs[0]);
- assertEquals("0x403d60", cs[1]);
+ assertEquals("40472b", cs[0]);
+ assertEquals("403d60", cs[1]);
}
/**
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, 1379361250498400000L);
assertEquals(3, cs.length);
- assertEquals("0x40472b", cs[0]);
- assertEquals("0x403b14", cs[1]);
- assertEquals("0x401b23", cs[2]);
+ assertEquals("40472b", cs[0]);
+ assertEquals("403b14", cs[1]);
+ assertEquals("401b23", cs[2]);
}
/**
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, 1379361250499759000L);
assertEquals(4, cs.length);
- assertEquals("0x40472b", cs[0]);
- assertEquals("0x4045c8", cs[1]);
- assertEquals("0x403760", cs[2]);
- assertEquals("0x401aac", cs[3]);
+ assertEquals("40472b", cs[0]);
+ assertEquals("4045c8", cs[1]);
+ assertEquals("403760", cs[2]);
+ assertEquals("401aac", cs[3]);
}
/**
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, end);
assertEquals(3, cs.length);
- assertEquals("0x40472b", cs[0]);
- assertEquals("0x4045c8", cs[1]);
- assertEquals("0x403760", cs[2]);
+ assertEquals("40472b", cs[0]);
+ assertEquals("4045c8", cs[1]);
+ assertEquals("403760", cs[2]);
}
}
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, start);
assertEquals(1, cs.length);
- assertEquals("0x40472b", cs[0]);
+ assertEquals("40472b", cs[0]);
}
/**
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, 1378850463600000000L);
assertEquals(2, cs.length);
- assertEquals("0x40472b", cs[0]);
- assertEquals("0x403d60", cs[1]);
+ assertEquals("40472b", cs[0]);
+ assertEquals("403d60", cs[1]);
}
/**
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, 1378850463770000000L);
assertEquals(3, cs.length);
- assertEquals("0x40472b", cs[0]);
- assertEquals("0x403b14", cs[1]);
- assertEquals("0x401b23", cs[2]);
+ assertEquals("40472b", cs[0]);
+ assertEquals("403b14", cs[1]);
+ assertEquals("401b23", cs[2]);
}
/**
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, 1378850463868753000L);
assertEquals(4, cs.length);
- assertEquals("0x40472b", cs[0]);
- assertEquals("0x4045c8", cs[1]);
- assertEquals("0x403760", cs[2]);
- assertEquals("0x401aac", cs[3]);
+ assertEquals("40472b", cs[0]);
+ assertEquals("4045c8", cs[1]);
+ assertEquals("403760", cs[2]);
+ assertEquals("401aac", cs[3]);
}
/**
String[] cs = TestUtils.getCallStack(fixture, PROCNAME, end);
assertEquals(3, cs.length);
- assertEquals("0x40472b", cs[0]);
- assertEquals("0x4045c8", cs[1]);
- assertEquals("0x403760", cs[2]);
+ assertEquals("40472b", cs[0]);
+ assertEquals("4045c8", cs[1]);
+ assertEquals("403760", cs[2]);
}
}
FUNC_EXIT_EVENTS.add("lttng_ust_cyg_profile_fast:func_exit"); //$NON-NLS-1$
}
+ /**
+ * Version number of this state provider. Please bump this if you modify
+ * the contents of the generated state history in some way.
+ */
+ private static final int VERSION = 1;
+
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
return new LttngUstCallStackProvider(getTrace());
}
+ @Override
+ public int getVersion() {
+ return VERSION;
+ }
+
// ------------------------------------------------------------------------
// Methods from CallStackStateProvider
// ------------------------------------------------------------------------
return null;
}
Long address = (Long) event.getContent().getField(FIELD_ADDR).getValue();
- return getFunctionNameFromAddress(address.longValue());
+ return Long.toHexString(address);
}
@Override
return CallStackStateProvider.UNDEFINED;
}
Long address = (Long) field.getValue();
- return getFunctionNameFromAddress(address.longValue());
+ return Long.toHexString(address);
}
@Override
return new String(procName + '-' + vtid.toString());
}
-
- // ------------------------------------------------------------------------
- // Internal helper methods
- // ------------------------------------------------------------------------
-
- private static String getFunctionNameFromAddress(long address) {
- /*
- * We do not support getting the real function name yet, just print the
- * hex string.
- */
- return new String("0x" + Long.toHexString(address)); //$NON-NLS-1$
- }
}
/** Dummy function name for when no function is expected */
private static final String NO_FUNCTION = "no function"; //$NON-NLS-1$
- /**
- * Version number of this state provider. Please bump this if you modify
- * the contents of the generated state history in some way.
- */
- private static final int VERSION = 0;
-
/**
* Default constructor
*
super(trace, ITmfEvent.class, ID);
}
- @Override
- public int getVersion() {
- return VERSION;
- }
-
@Override
protected void eventHandle(ITmfEvent event) {
if (!considerEvent(event)) {
schema/
src.includes = about.html,\
schema/
+additional.bundles = org.eclipse.jdt.annotation
+jars.extra.classpath = platform:/plugin/org.eclipse.jdt.annotation
public static String CallStackView_DurationColumn;
public static String CallStackView_StackInfoNotAvailable;
+ public static String CallStackView_ImportMappingButtonText;
+ public static String CallStackView_ImportMappingButtonTooltip;
+ public static String CallStackView_ImportMappingDialogTitle;
+ public static String CallStackView_ImportMappingJobName;
+
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
TmfView_PinActionNameText=Pin View
TmfView_PinActionToolTipText=Pin View
+# Call Stack View
CallStackView_FunctionColumn=Function
CallStackView_DepthColumn=Depth
CallStackView_EntryTimeColumn=Entry time
CallStackView_ExitTimeColumn=Exit time
CallStackView_DurationColumn=Duration
CallStackView_StackInfoNotAvailable=Stack info not available
+
+CallStackView_ImportMappingButtonText=Import mapping file...
+CallStackView_ImportMappingButtonTooltip=Import a text file containing the mapping between addresses and function names
+CallStackView_ImportMappingDialogTitle=Select Mapping File
+CallStackView_ImportMappingJobName=Updating Call Stack view function mapping
import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException;
import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
-import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.StateItem;
/** Number of colors used for call stack events */
public static final int NUM_COLORS = 360;
+ private final CallStackView fView;
+
private enum State {
MULTIPLE (new RGB(100, 100, 100)),
EXEC (new RGB(0, 200, 0));
}
}
+ /**
+ * Constructor
+ *
+ * @param view
+ * The callstack view that will contain the time events
+ */
+ public CallStackPresentationProvider(CallStackView view) {
+ fView = view;
+ }
+
@Override
public String getStateTypeName() {
// Empty string since no generic name
CallStackEntry entry = (CallStackEntry) event.getEntry();
ITmfStateSystem ss = entry.getTrace().getStateSystems().get(CallStackStateProvider.ID);
try {
- ITmfStateInterval value = ss.querySingleState(event.getTime(), entry.getQuark());
- if (!value.getStateValue().isNull()) {
- ITmfStateValue state = value.getStateValue();
- return state.toString();
+ ITmfStateValue value = ss.querySingleState(event.getTime(), entry.getQuark()).getStateValue();
+ if (!value.isNull()) {
+ String address = value.toString();
+ return fView.getFunctionName(address);
}
} catch (AttributeNotFoundException e) {
Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
CallStackEntry entry = (CallStackEntry) event.getEntry();
ITmfStateSystem ss = entry.getTrace().getStateSystems().get(CallStackStateProvider.ID);
try {
- ITmfStateInterval value = ss.querySingleState(event.getTime(), entry.getQuark());
- if (!value.getStateValue().isNull()) {
- ITmfStateValue state = value.getStateValue();
+ ITmfStateValue value = ss.querySingleState(event.getTime(), entry.getQuark()).getStateValue();
+ if (!value.isNull()) {
+ String address = value.toString();
+ String name = fView.getFunctionName(address);
gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
- Utils.drawText(gc, state.toString(), bounds.x, bounds.y - 2, bounds.width, true, true);
+ Utils.drawText(gc, name, bounds.x, bounds.y - 2, bounds.width, true, true);
}
} catch (AttributeNotFoundException e) {
Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
package org.eclipse.linuxtools.tmf.ui.views.callstack;
+import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
private static final Image THREAD_IMAGE = Activator.getDefault().getImageFromPath("icons/obj16/thread_obj.gif"); //$NON-NLS-1$
private static final Image STACKFRAME_IMAGE = Activator.getDefault().getImageFromPath("icons/obj16/stckframe_obj.gif"); //$NON-NLS-1$
+ private static final String IMPORT_MAPPING_ICON_PATH = "icons/etool16/import.gif"; //$NON-NLS-1$
+
// ------------------------------------------------------------------------
// Fields
// ------------------------------------------------------------------------
// The trace to build thread hash map
private final Map<ITmfTrace, BuildThread> fBuildThreadMap = new HashMap<ITmfTrace, BuildThread>();
+ /** The map to map function addresses to function names */
+ private Map<String, String> fNameMapping;
+
// The start time
private long fStartTime;
// The previous item action
private Action fPreviousItemAction;
+ /** The action to import a function-name mapping file */
+ private Action fImportMappingAction;
+
// The zoom thread
private ZoomThread fZoomThread;
fTimeGraphCombo.getTreeViewer().getTree().getColumn(3).setWidth(COLUMN_WIDTHS[3]);
fTimeGraphCombo.getTreeViewer().getTree().getColumn(4).setWidth(COLUMN_WIDTHS[4]);
- fTimeGraphCombo.setTimeGraphProvider(new CallStackPresentationProvider());
+ fTimeGraphCombo.setTimeGraphProvider(new CallStackPresentationProvider(this));
fTimeGraphCombo.getTimeGraphViewer().setTimeFormat(TimeFormat.CALENDAR);
fTimeGraphCombo.getTimeGraphViewer().addRangeListener(new ITimeGraphRangeListener() {
// ------------------------------------------------------------------------
// Internal
// ------------------------------------------------------------------------
+
private void loadTrace() {
synchronized (fEntryListMap) {
fEntryList = fEntryListMap.get(fTrace);
String name = ""; //$NON-NLS-1$
try {
if (nameValue.getType() == Type.STRING) {
- name = nameValue.unboxStr();
+ String address = nameValue.unboxStr();
+ name = getFunctionName(address);
} else if (nameValue.getType() == Type.INTEGER) {
name = "0x" + Integer.toHexString(nameValue.unboxInt()); //$NON-NLS-1$
} else if (nameValue.getType() == Type.LONG) {
}
private void fillLocalToolBar(IToolBarManager manager) {
+ manager.add(getImportMappingAction());
manager.add(fTimeGraphCombo.getTimeGraphViewer().getResetScaleAction());
manager.add(getPreviousEventAction());
manager.add(getNextEventAction());
return fPrevEventAction;
}
+ // ------------------------------------------------------------------------
+ // Methods related to function name mapping
+ // ------------------------------------------------------------------------
+
+ /**
+ * Toolbar icon to import the function address-to-name mapping file.
+ */
+ private Action getImportMappingAction() {
+ if (fImportMappingAction != null) {
+ return fImportMappingAction;
+ }
+ fImportMappingAction = new Action() {
+ @Override
+ public void run() {
+ FileDialog dialog = new FileDialog(getViewSite().getShell());
+ dialog.setText(Messages.CallStackView_ImportMappingDialogTitle);
+ String filePath = dialog.open();
+ if (filePath == null) {
+ /* No file was selected, don't change anything */
+ return;
+ }
+ /*
+ * Start the mapping import in a separate thread (we do not want
+ * to UI thread to do this).
+ */
+ Job job = new ImportMappingJob(new File(filePath));
+ job.schedule();
+ }
+ };
+
+ fImportMappingAction.setText(Messages.CallStackView_ImportMappingButtonText);
+ fImportMappingAction.setToolTipText(Messages.CallStackView_ImportMappingButtonTooltip);
+ fImportMappingAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(IMPORT_MAPPING_ICON_PATH));
+
+ return fImportMappingAction;
+ }
+
+ private class ImportMappingJob extends Job {
+ private final File fMappingFile;
+
+ public ImportMappingJob(File mappingFile) {
+ super(Messages.CallStackView_ImportMappingJobName);
+ fMappingFile = mappingFile;
+ }
+
+ @Override
+ public IStatus run(IProgressMonitor monitor) {
+ fNameMapping = FunctionNameMapper.mapFromNmTextFile(fMappingFile);
+
+ /* Refresh the time graph and the list of entries */
+ buildThreadList(fTrace, new NullProgressMonitor());
+ redraw();
+
+ return Status.OK_STATUS;
+ }
+ }
+
+ String getFunctionName(String address) {
+ if (fNameMapping == null) {
+ /* No mapping available, just print the addresses */
+ return address;
+ }
+ String ret = fNameMapping.get(address);
+ if (ret == null) {
+ /* We didn't find this address in the mapping file, just use the address */
+ return address;
+ }
+ return ret;
+ }
+
}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2013 Ericsson
+ *
+ * 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:
+ * Alexandre Montplaisir - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.linuxtools.tmf.ui.views.callstack;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Class containing the different methods to import an address->name mapping.
+ *
+ * @author Alexandre Montplaisir
+ */
+class FunctionNameMapper {
+
+ public static @Nullable Map<String, String> mapFromNmTextFile(File mappingFile) {
+ Map<String, String> map = new HashMap<String, String>();
+
+ FileReader fr;
+ try {
+ fr = new FileReader(mappingFile);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ BufferedReader reader = new BufferedReader(fr);
+
+ try {
+ for (String line = reader.readLine(); line != null; line = reader.readLine()) {
+ String[] elems = line.split(" "); //$NON-NLS-1$
+ /* Only lines with 3 elements contain addresses */
+ if (elems.length == 3) {
+ /* Strip the leading zeroes from the address */
+ String address = elems[0].replaceFirst("^0+(?!$)", ""); //$NON-NLS-1$ //$NON-NLS-2$;
+ String name = elems[elems.length - 1];
+ map.put(address, name);
+ }
+ }
+
+ } catch (IOException e) {
+ /* Stop reading the file at this point */
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException e) {}
+ }
+
+ if (map.isEmpty()) {
+ return null;
+ }
+ return Collections.unmodifiableMap(map);
+ }
+
+}