| /******************************************************************************* |
| * Copyright (c) 2011, 2015 Ericsson |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Marc Dumais - Initial implementation |
| * Francois Chouinard - Misc improvements, DSF signal handling, dynamic experiment |
| * Patrick Tasse - Updated for TMF 2.0 |
| * Bernd Hufmann - Fixed deadlock during shutdown |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.internal.gdbtrace.core.trace; |
| |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.RejectedExecutionException; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| |
| import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.DsfExecutor; |
| import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; |
| import org.eclipse.cdt.dsf.concurrent.Query; |
| import org.eclipse.cdt.dsf.datamodel.DMContexts; |
| import org.eclipse.cdt.dsf.datamodel.IDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IBreakpoints; |
| import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; |
| import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService; |
| import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; |
| import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants; |
| import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; |
| import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch; |
| import org.eclipse.cdt.dsf.gdb.service.GDBTraceControl_7_2.TraceRecordSelectedChangedEvent; |
| import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl; |
| import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordDMContext; |
| import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordDMData; |
| import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordSelectedChangedDMEvent; |
| import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceStatusDMData; |
| import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceTargetDMContext; |
| import org.eclipse.cdt.dsf.mi.service.IMICommandControl; |
| import org.eclipse.cdt.dsf.mi.service.IMIProcesses; |
| import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData; |
| import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; |
| import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakListInfo; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint; |
| import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; |
| import org.eclipse.cdt.dsf.service.DsfServicesTracker; |
| import org.eclipse.cdt.dsf.service.DsfSession; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfigurationType; |
| import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| import org.eclipse.debug.core.ILaunchesListener2; |
| import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; |
| import org.eclipse.debug.ui.AbstractDebugView; |
| import org.eclipse.debug.ui.DebugUITools; |
| import org.eclipse.debug.ui.IDebugUIConstants; |
| import org.eclipse.debug.ui.contexts.DebugContextEvent; |
| import org.eclipse.debug.ui.contexts.IDebugContextListener; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.tracecompass.internal.gdbtrace.core.GdbTraceCorePlugin; |
| import org.eclipse.tracecompass.internal.gdbtrace.core.event.GdbTraceEvent; |
| import org.eclipse.tracecompass.internal.gdbtrace.core.event.GdbTraceEventContent; |
| import org.eclipse.tracecompass.tmf.core.event.TmfEventField; |
| import org.eclipse.tracecompass.tmf.core.event.TmfEventType; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment; |
| import org.eclipse.tracecompass.tmf.ui.editors.ITmfTraceEditor; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorReference; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IViewPart; |
| import org.eclipse.ui.IWorkbench; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.ide.IDE; |
| |
| /** |
| * Adaptor to access GDB Tracepoint frames, previously collected and saved in a |
| * file by GDB. One instance of this maps to a single DSF-GDB session. |
| * <p> |
| * This class offers the functions of starting a post-mortem GDB session with a |
| * tracepoint data file, navigate the data frames and return the data contained |
| * in a given tracepoint frame. |
| * <p> |
| * Note: GDB 7.2 or later is required to handle tracepoints |
| * |
| * @author Marc Dumais |
| * @author Francois Chouinard |
| */ |
| @SuppressWarnings("restriction") |
| public class DsfGdbAdaptor { |
| |
| private static final Object SESSION_LOCK = new Object(); |
| private static final String INTERRUPTION_EXCEPTION = "Interruption exception"; //$NON-NLS-1$ |
| private static final String GDB_EXCEPTION = "GDB exception"; //$NON-NLS-1$ |
| private static final String REQUEST_REJECTED_EXCEPTION = "Request rejected exception"; //$NON-NLS-1$ |
| private static final String TIMEOUT = "Timeout"; //$NON-NLS-1$ |
| |
| private GdbTrace fGdbTrace; |
| |
| private int fNumberOfFrames = 0; |
| private boolean fIsTimeoutEnabled; |
| private int fTimeout; |
| |
| private ILaunch fLaunch; |
| private boolean isTerminating; |
| private DsfSession fDsfSession = null; |
| private String fSessionId; |
| |
| private String tracedExecutable = ""; //$NON-NLS-1$ |
| |
| private String gdb72Executable = ""; //$NON-NLS-1$ |
| private String fTraceFilePath = ""; //$NON-NLS-1$ |
| private String fTraceFile = ""; //$NON-NLS-1$ |
| private String sourceLocator = ""; //$NON-NLS-1$ |
| |
| // To save tracepoints detailed info. The key is the rank of the |
| // breakpoint (tracepoint is a kind of breakpoint) |
| private Map<Integer, MIBreakpointDMData> fTpInfo = new HashMap<>(); |
| |
| private TmfEventType tmfEventType = new TmfEventType("GDB Tracepoint", TmfEventField.makeRoot(new String[] { "Content" })); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| { |
| new DsfGdbPlatformEventListener(); |
| } |
| |
| /** |
| * <b><u>DsfGdbPlatformEventListener</u></b> |
| * <p> |
| * Listens to platform and DSF-GDB events that announce important events |
| * about the launchers or a change in debug context that we might need to |
| * react-to. |
| * <p> |
| * |
| * @author Francois Chouinard |
| */ |
| private class DsfGdbPlatformEventListener implements |
| ILaunchesListener2, IDebugContextListener { |
| |
| /** |
| * |
| */ |
| public DsfGdbPlatformEventListener() { |
| Display.getDefault().syncExec(() -> { |
| DebugPlugin.getDefault().getLaunchManager().addLaunchListener(DsfGdbPlatformEventListener.this); |
| IWorkbench wb = PlatformUI.getWorkbench(); |
| IWorkbenchWindow win = wb.getActiveWorkbenchWindow(); |
| DebugUITools.getDebugContextManager().getContextService(win).addDebugContextListener(DsfGdbPlatformEventListener.this); |
| }); |
| } |
| |
| @Override |
| public synchronized void launchesRemoved(ILaunch[] launches) { |
| // Do nothing |
| } |
| |
| @Override |
| public synchronized void launchesAdded(ILaunch[] launches) { |
| // Do nothing |
| } |
| |
| @Override |
| public synchronized void launchesChanged(ILaunch[] launches) { |
| // Do nothing |
| } |
| |
| @Override |
| public synchronized void launchesTerminated(ILaunch[] launches) { |
| for (ILaunch launch : launches) { |
| String sessionId = ((GdbLaunch) launch).getSession().getId(); |
| closeGdbTraceEditor(sessionId); |
| } |
| } |
| |
| private String fCurrentSessionId = ""; //$NON-NLS-1$ |
| @Override |
| public void debugContextChanged(DebugContextEvent event) { |
| ISelection selection = event.getContext(); |
| if (selection instanceof IStructuredSelection) { |
| List<?> eventContextList = ((IStructuredSelection) selection).toList(); |
| for (Object eventContext : eventContextList) { |
| if (eventContext instanceof IAdaptable) { |
| Object contextObject = ((IAdaptable) eventContext).getAdapter(IDMContext.class); |
| IDMContext context = (IDMContext) contextObject; |
| if (context != null) { |
| String sessionId; |
| synchronized (SESSION_LOCK) { |
| sessionId = context.getSessionId(); |
| if (sessionId.equals(fCurrentSessionId)) { |
| return; |
| } |
| fCurrentSessionId = sessionId; |
| } |
| // Get the current trace record |
| final DsfExecutor executor = DsfSession.getSession(sessionId).getExecutor(); |
| final DsfServicesTracker tracker = new DsfServicesTracker(GdbTraceCorePlugin.getBundleContext(), sessionId); |
| Query<ITraceRecordDMContext> getCurrentRecordQuery = new Query<ITraceRecordDMContext>() { |
| @Override |
| public void execute(final DataRequestMonitor<ITraceRecordDMContext> queryRm) { |
| final IGDBTraceControl traceControl = tracker.getService(IGDBTraceControl.class); |
| final ICommandControlService commandControl = tracker.getService(ICommandControlService.class); |
| if (traceControl != null && commandControl != null) { |
| ITraceTargetDMContext traceContext = (ITraceTargetDMContext) commandControl.getContext(); |
| traceControl.getCurrentTraceRecordContext(traceContext, queryRm); |
| } else { |
| queryRm.done(); |
| } |
| } |
| }; |
| try { |
| executor.execute(getCurrentRecordQuery); |
| ITraceRecordDMContext record; |
| if (DsfGdbAdaptor.this.fIsTimeoutEnabled) { |
| record = getCurrentRecordQuery.get(fTimeout, TimeUnit.MILLISECONDS); |
| } else { |
| record = getCurrentRecordQuery.get(); |
| } |
| // If we get a trace record, it means that this can be used |
| if (record != null && record.getRecordId() != null) { |
| int recordId = Integer.parseInt(record.getRecordId()); |
| selectGdbTraceEditor(sessionId, recordId); |
| break; |
| } |
| } catch (InterruptedException e) { |
| GdbTraceCorePlugin.logError(INTERRUPTION_EXCEPTION, e); |
| Thread.currentThread().interrupt(); |
| } catch (ExecutionException e) { |
| GdbTraceCorePlugin.logError(GDB_EXCEPTION, e); |
| } catch (RejectedExecutionException e) { |
| GdbTraceCorePlugin.logError(REQUEST_REJECTED_EXCEPTION, e); |
| } catch (TimeoutException e) { |
| GdbTraceCorePlugin.logError(TIMEOUT, e); |
| } finally { |
| tracker.dispose(); |
| } |
| // else not DSF-GDB or GDB < 7.2 |
| } |
| } |
| // else not DSF |
| } |
| } |
| } |
| } // class DsfGdbPlatformEventListener |
| |
| /** |
| * Constructor for DsfGdbAdaptor. This is used when we want to launch a |
| * DSF-GDB session and use it as source in our tracing perspective. |
| * i.e. when launching from the Project Explorer |
| * |
| * @param trace the GDB trace |
| * @param gdbExec GDB executable. Must be version 7.2 or later. |
| * @param traceFile previously generated GDB tracepoint file |
| * @param tracedExecutable executable that was used to generate the tracefile |
| * workspace, where the traced executable was taken from. |
| */ |
| public DsfGdbAdaptor(GdbTrace trace, String gdbExec, String traceFile, String tracedExecutable) { |
| this.fGdbTrace = trace; |
| this.gdb72Executable = gdbExec; |
| this.fTraceFilePath = traceFile; |
| this.fTraceFile = traceFile.substring(traceFile.lastIndexOf(IPath.SEPARATOR) + 1); |
| this.tracedExecutable = tracedExecutable; |
| |
| try { |
| launchDGBPostMortemTrace(); |
| } catch (CoreException e) { |
| GdbTraceCorePlugin.logError(e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * Builds a launcher and launches a Post-mortem GDB session, based on a |
| * previously-gathered GDB Tracepoint file. The information used to |
| * create the launcher is provided to the constructor of this class, |
| * at instantiation time. |
| * <p> |
| * Note: Requires GDB 7.2 or later |
| */ |
| private void launchDGBPostMortemTrace() throws CoreException { |
| fIsTimeoutEnabled = Platform.getPreferencesService().getBoolean(GdbPlugin.PLUGIN_ID, IGdbDebugPreferenceConstants.PREF_COMMAND_TIMEOUT, false, null); |
| if (fIsTimeoutEnabled) { |
| fTimeout = Platform.getPreferencesService().getInt(GdbPlugin.PLUGIN_ID, IGdbDebugPreferenceConstants.PREF_COMMAND_TIMEOUT_VALUE, IGdbDebugPreferenceConstants.COMMAND_TIMEOUT_VALUE_DEFAULT, null); |
| } |
| |
| ILaunchConfigurationType configType = DebugPlugin |
| .getDefault() |
| .getLaunchManager() |
| .getLaunchConfigurationType("org.eclipse.cdt.launch.postmortemLaunchType"); //$NON-NLS-1$ |
| ILaunchConfigurationWorkingCopy wc = configType.newInstance(null, fTraceFile); |
| |
| wc.setAttribute("org.eclipse.cdt.dsf.gdb.DEBUG_NAME", gdb72Executable); //$NON-NLS-1$ |
| wc.setAttribute("org.eclipse.cdt.dsf.gdb.POST_MORTEM_TYPE", "TRACE_FILE"); //$NON-NLS-1$ //$NON-NLS-2$ |
| wc.setAttribute("org.eclipse.cdt.launch.ATTR_BUILD_BEFORE_LAUNCH_ATTR", 0); //$NON-NLS-1$ |
| wc.setAttribute("org.eclipse.cdt.launch.COREFILE_PATH", fTraceFilePath); //$NON-NLS-1$ |
| wc.setAttribute("org.eclipse.cdt.launch.DEBUGGER_START_MODE", "core"); //$NON-NLS-1$ //$NON-NLS-2$ |
| wc.setAttribute("org.eclipse.cdt.launch.PROGRAM_NAME", tracedExecutable); //$NON-NLS-1$ |
| // So that the GDB launch is synchronous |
| wc.setAttribute("org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND", false); //$NON-NLS-1$ |
| |
| if (!sourceLocator.isEmpty()) { |
| wc.setAttribute("org.eclipse.debug.core.source_locator_memento", sourceLocator); //$NON-NLS-1$ |
| } |
| |
| // Launch GDB session |
| fLaunch = wc.doSave().launch("debug", null); //$NON-NLS-1$ |
| isTerminating = false; |
| |
| if (fLaunch instanceof GdbLaunch) { |
| fSessionId = ((GdbLaunch) fLaunch).getSession().getId(); |
| } |
| |
| fDsfSession = ((GdbLaunch) fLaunch).getSession(); |
| fDsfSession.addServiceEventListener(this, null); |
| |
| // Find the number of frames contained in the tracepoint file |
| fNumberOfFrames = findNumFrames(); |
| } |
| |
| /** |
| * This method terminates the current DSF-GDB session |
| */ |
| public void dispose() { |
| if (fLaunch != null && fLaunch.canTerminate() && !isTerminating) { |
| isTerminating = true; |
| try { |
| fLaunch.terminate(); |
| } catch (DebugException e) { |
| GdbTraceCorePlugin.logError(e.getMessage(), e); |
| } |
| fLaunch = null; |
| } |
| } |
| |
| /** |
| * This method will try (once per call) to get the number of GDB tracepoint |
| * frames for the current session, from DSF-GDB, until it succeeds at |
| * getting an amount different than zero. |
| * |
| * @return The number of frames in current session or zero if unsuccessful |
| */ |
| public int getNumberOfFrames() { |
| if (fNumberOfFrames == 0) { |
| fNumberOfFrames = findNumFrames(); |
| } |
| return fNumberOfFrames; |
| } |
| |
| /** |
| * Wrapper around the selecting of a frame and the reading of its |
| * information. this is a work-around for the potential problem of |
| * concurrent access to these functions by more than one thread, where two |
| * clients might interfere with each other. |
| * <p> |
| * Note: We also try to get the tracepoint info here, if it's not already |
| * filled-in. |
| * |
| * @param rank |
| * a long corresponding to the number of the frame to be selected |
| * and read |
| * @return A GdbTraceEvent object, or null in case of failure. |
| */ |
| public synchronized GdbTraceEvent selectAndReadFrame(final long rank) { |
| // lazy init of tracepoints info |
| if (fTpInfo.isEmpty()) { |
| getTracepointInfo(); |
| } |
| if (selectDataFrame(rank, false)) { |
| GdbTraceEvent event = getTraceFrameData(rank); |
| long ts = event.getTimestamp().getValue(); |
| if (ts == rank) { |
| return event; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * This class implements a best-effort look-up of the detailed tracepoint |
| * information (source code filename, line number, etc...). |
| */ |
| private void getTracepointInfo() { |
| |
| // Get the latest executor/service tracker |
| final DsfExecutor executor = DsfSession.getSession(fSessionId).getExecutor(); |
| final DsfServicesTracker tracker = new DsfServicesTracker(GdbTraceCorePlugin.getBundleContext(), fSessionId); |
| |
| Query<Object> selectRecordQuery = new Query<Object>() { |
| @Override |
| public void execute(final DataRequestMonitor<Object> drm) { |
| |
| // A breakpoint is no longer GDB-global but tied to a specific process |
| // So we need to find our process and the ask for its breakpoints |
| IMIProcesses procService = tracker.getService(IMIProcesses.class); |
| final ICommandControlService cmdControl = tracker.getService(ICommandControlService.class); |
| if (procService == null || cmdControl == null) { |
| drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Could not find necessary services", null)); //$NON-NLS-1$ |
| drm.done(); |
| return; |
| } |
| |
| ITraceTargetDMContext context = (ITraceTargetDMContext) cmdControl.getContext(); |
| ICommandControlDMContext cmdControlDMC = DMContexts.getAncestorOfType(context, ICommandControlDMContext.class); |
| |
| procService.getProcessesBeingDebugged( |
| cmdControlDMC, |
| new DataRequestMonitor<IDMContext[]>(executor, drm) { |
| @Override |
| protected void handleSuccess() { |
| if (getData() == null || getData().length < 1) { |
| drm.done(); |
| return; |
| } |
| |
| // Choose the first process for now, until gdb can tell |
| // us which process the trace record is associated with. |
| IContainerDMContext containerDMC = (IContainerDMContext)(getData()[0]); |
| IBreakpointsTargetDMContext bpTargetDMC = DMContexts.getAncestorOfType(containerDMC , IBreakpointsTargetDMContext.class); |
| |
| IMICommandControl commandService = tracker.getService(IMICommandControl.class); |
| if (commandService == null) { |
| drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Could not find necessary services", null)); //$NON-NLS-1$ |
| drm.done(); |
| return; |
| } |
| |
| CommandFactory cmdFactory = commandService.getCommandFactory(); |
| IBreakpoints bpService = tracker.getService(MIBreakpoints.class); |
| if (cmdFactory == null || bpService == null) { |
| drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Could not find necessary services", null)); //$NON-NLS-1$ |
| drm.done(); |
| return; |
| } |
| |
| // Execute the command |
| cmdControl.queueCommand(cmdFactory.createMIBreakList(bpTargetDMC), |
| new DataRequestMonitor<MIBreakListInfo>(executor, drm) { |
| @Override |
| protected void handleSuccess() { |
| MIBreakpoint[] breakpoints = getData().getMIBreakpoints(); |
| for (int i = 0; i < breakpoints.length; i++) { |
| // Use deprecated constructor for Neon backward compatibility |
| MIBreakpointDMData breakpoint = new MIBreakpointDMData(breakpoints[i]); |
| String type = breakpoint.getBreakpointType(); |
| // Only save info if the current breakpoint is of type tracepoint |
| if(type.compareTo(MIBreakpoints.TRACEPOINT) == 0 ) { |
| fTpInfo.put(Integer.valueOf(breakpoint.getReference()), breakpoint); |
| } |
| } |
| drm.done(); |
| } |
| }); |
| } |
| }); |
| } |
| }; |
| try { |
| executor.execute(selectRecordQuery); |
| if (fIsTimeoutEnabled) { |
| selectRecordQuery.get(fTimeout, TimeUnit.MILLISECONDS); // blocks until time out |
| } else { |
| selectRecordQuery.get(); // blocks |
| } |
| } catch (InterruptedException e) { |
| GdbTraceCorePlugin.logError(INTERRUPTION_EXCEPTION, e); |
| Thread.currentThread().interrupt(); |
| } catch (ExecutionException e) { |
| GdbTraceCorePlugin.logError(GDB_EXCEPTION, e); |
| } catch (RejectedExecutionException e) { |
| GdbTraceCorePlugin.logError(REQUEST_REJECTED_EXCEPTION, e); |
| } catch (TimeoutException e) { |
| GdbTraceCorePlugin.logError(TIMEOUT, e); |
| } finally { |
| tracker.dispose(); |
| } |
| } |
| |
| /** |
| * Returns the number of frames contained in currently loaded tracepoint GDB |
| * session. |
| * <p> |
| * Note: A postmortem GDB session must be started before calling |
| * this method |
| * |
| * @return the number of frames contained in currently loaded tracepoint GDB |
| * session or zero in case of error |
| */ |
| private synchronized int findNumFrames() { |
| int frameNum = 0; |
| |
| if (DsfSession.getSession(fSessionId) == null) { |
| return 0; |
| } |
| |
| final DsfExecutor executor = DsfSession.getSession(fSessionId) |
| .getExecutor(); |
| final DsfServicesTracker tracker = new DsfServicesTracker( |
| GdbTraceCorePlugin.getBundleContext(), fSessionId); |
| |
| Query<ITraceStatusDMData> selectRecordQuery = new Query<ITraceStatusDMData>() { |
| @Override |
| public void execute( |
| final DataRequestMonitor<ITraceStatusDMData> queryRm) { |
| final IGDBTraceControl traceControl = tracker |
| .getService(IGDBTraceControl.class); |
| |
| final ICommandControlService commandControl = tracker.getService(ICommandControlService.class); |
| if (commandControl == null) { |
| queryRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Could not find necessary services", null)); //$NON-NLS-1$ |
| queryRm.done(); |
| return; |
| } |
| final ITraceTargetDMContext dmc = (ITraceTargetDMContext) commandControl |
| .getContext(); |
| |
| if (traceControl != null) { |
| traceControl.getTraceStatus(dmc, queryRm); |
| } else { |
| queryRm.done(); |
| } |
| } |
| }; |
| try { |
| executor.execute(selectRecordQuery); |
| ITraceStatusDMData data; |
| if (fIsTimeoutEnabled) { |
| data = selectRecordQuery.get(fTimeout, TimeUnit.MILLISECONDS); // blocks until time out |
| } else { |
| data = selectRecordQuery.get(); // blocks |
| } |
| |
| frameNum = data.getNumberOfCollectedFrame(); |
| } catch (InterruptedException e) { |
| GdbTraceCorePlugin.logError(INTERRUPTION_EXCEPTION, e); |
| Thread.currentThread().interrupt(); |
| } catch (ExecutionException e) { |
| GdbTraceCorePlugin.logError(GDB_EXCEPTION, e); |
| } catch (RejectedExecutionException e) { |
| GdbTraceCorePlugin.logError(REQUEST_REJECTED_EXCEPTION, e); |
| } catch (TimeoutException e) { |
| GdbTraceCorePlugin.logError(TIMEOUT, e); |
| } finally { |
| tracker.dispose(); |
| } |
| return frameNum; |
| } |
| |
| /** |
| * This method uses the DSF-GDB interface to select a given frame number in |
| * the current GDB tracepoint session. |
| * |
| * @param rank |
| * the rank of the tracepoint frame to select. |
| * @param update |
| * true if visualization should be updated |
| * @return boolean true if select worked. |
| */ |
| public boolean selectDataFrame(final long rank, final boolean update) { |
| boolean status = true; |
| |
| final DsfSession dsfSession = DsfSession.getSession(fSessionId); |
| if (dsfSession == null) { |
| return false; |
| } |
| |
| if (update) { |
| /* |
| * Clear the selection to ensure that the new selection is not |
| * prevented from overriding the current selection by the DSF |
| * selection policy. This could be removed when DSF provides |
| * an API to force the trace record selection in the Debug view. |
| */ |
| Display.getDefault().syncExec(() -> { |
| for (IWorkbenchWindow wbWindow : PlatformUI.getWorkbench().getWorkbenchWindows()) { |
| for (IWorkbenchPage wbPage : wbWindow.getPages()) { |
| IViewPart vp = wbPage.findView(IDebugUIConstants.ID_DEBUG_VIEW); |
| if (vp instanceof AbstractDebugView) { |
| Viewer viewer = ((AbstractDebugView) vp).getViewer(); |
| if (viewer instanceof ITreeModelViewer) { |
| ((ITreeModelViewer) viewer).setSelection(StructuredSelection.EMPTY, false, true); |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| final DsfExecutor executor = dsfSession.getExecutor(); |
| final DsfServicesTracker tracker = new DsfServicesTracker(GdbTraceCorePlugin.getBundleContext(), fSessionId); |
| |
| Query<Object> selectRecordQuery = new Query<Object>() { |
| @Override |
| public void execute(final DataRequestMonitor<Object> queryRm) { |
| final IGDBTraceControl traceControl = tracker.getService(IGDBTraceControl.class); |
| |
| final ICommandControlService commandControl = tracker.getService(ICommandControlService.class); |
| if (commandControl == null) { |
| queryRm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Could not find necessary services", null)); //$NON-NLS-1$ |
| queryRm.done(); |
| return; |
| } |
| |
| final ITraceTargetDMContext dmc = (ITraceTargetDMContext) commandControl.getContext(); |
| |
| if (traceControl != null) { |
| ITraceRecordDMContext newCtx = traceControl.createTraceRecordContext(dmc, Integer.toString((int) rank)); |
| if (update) { |
| dsfSession.dispatchEvent(new TraceRecordSelectedChangedEvent(newCtx), new Hashtable<String, String>()); |
| } |
| traceControl.selectTraceRecord(newCtx, queryRm); |
| } else { |
| queryRm.done(); |
| } |
| } |
| }; |
| try { |
| executor.execute(selectRecordQuery); |
| if (fIsTimeoutEnabled) { |
| selectRecordQuery.get(fTimeout, TimeUnit.MILLISECONDS); // blocks until time out |
| } else { |
| selectRecordQuery.get(); // blocks |
| } |
| } catch (InterruptedException e) { |
| status = false; |
| GdbTraceCorePlugin.logError(INTERRUPTION_EXCEPTION, e); |
| Thread.currentThread().interrupt(); |
| } catch (ExecutionException e) { |
| status = false; |
| GdbTraceCorePlugin.logError(GDB_EXCEPTION, e); |
| } catch (RejectedExecutionException e) { |
| status = false; |
| GdbTraceCorePlugin.logError(REQUEST_REJECTED_EXCEPTION, e); |
| } catch (TimeoutException e) { |
| status = false; |
| GdbTraceCorePlugin.logError(TIMEOUT, e); |
| } finally { |
| tracker.dispose(); |
| } |
| return status; |
| } |
| |
| /** |
| * This method uses DSF-GDB to read the currently selected GDB tracepoint |
| * data frame. An object of type GdbTraceEvent is build based on the |
| * information contained in the data frame and returned to the caller. |
| * <p> |
| * NOTE : A frame must be selected before calling this method! |
| * |
| * @param rank for internal purposes - does <b>not</b> control which |
| * frame will be read! |
| * @return parsed tp frame, in the form of a GdbTraceEvent |
| */ |
| private GdbTraceEvent getTraceFrameData(final long rank) { |
| |
| if (DsfSession.getSession(fSessionId) == null) { |
| return null; |
| } |
| |
| final DsfExecutor executor = DsfSession.getSession(fSessionId).getExecutor(); |
| final DsfServicesTracker tracker = new DsfServicesTracker(GdbTraceCorePlugin.getBundleContext(), fSessionId); |
| |
| Query<ITraceRecordDMData> getFrameDataQuery = new Query<ITraceRecordDMData>() { |
| @Override |
| public void execute(final DataRequestMonitor<ITraceRecordDMData> rm) { |
| final IGDBTraceControl traceControl = tracker.getService(IGDBTraceControl.class); |
| |
| final ICommandControlService commandControl = tracker.getService(ICommandControlService.class); |
| if (commandControl == null) { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, "Could not find necessary services", null)); //$NON-NLS-1$ |
| rm.done(); |
| return; |
| } |
| final ITraceTargetDMContext dmc = (ITraceTargetDMContext) commandControl.getContext(); |
| |
| if (traceControl != null) { |
| traceControl.getCurrentTraceRecordContext(dmc, |
| new DataRequestMonitor<ITraceRecordDMContext>(executor, rm) { |
| @Override |
| protected void handleSuccess() { |
| traceControl.getTraceRecordData(getData(), rm); |
| } |
| }); |
| } else { |
| rm.done(); |
| } |
| } |
| }; |
| try { |
| // Execute the above query |
| executor.execute(getFrameDataQuery); |
| ITraceRecordDMData data; |
| if (fIsTimeoutEnabled) { |
| data = getFrameDataQuery.get(fTimeout, TimeUnit.MILLISECONDS); // blocking call until time out |
| } else { |
| data = getFrameDataQuery.get(); |
| } |
| |
| if (data == null) { |
| return null; |
| } |
| |
| String ts = data.getTimestamp(); |
| if (ts == null) { |
| ts = "0"; //$NON-NLS-1$ |
| } |
| |
| // get corresponding TP data |
| String tmfEventRef; |
| MIBreakpointDMData bp = fTpInfo.get(Integer.valueOf(data.getTracepointNumber())); |
| if (bp != null) { |
| tmfEventRef = bp.getFileName() + ":" + bp.getLineNumber() + " :: " + bp.getFunctionName(); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| tmfEventRef = tracedExecutable; |
| } |
| |
| GdbTraceEventContent evContent = new GdbTraceEventContent( |
| data.getContent(), |
| Integer.parseInt(data.getTracepointNumber()), |
| Integer.parseInt(data.getRecordId())); |
| |
| GdbTraceEvent ev = new GdbTraceEvent(fGdbTrace, |
| TmfTimestamp.fromSeconds(Integer.parseInt(data.getRecordId())), |
| "Tracepoint: " + data.getTracepointNumber() + ", Frame: " + data.getRecordId(), //$NON-NLS-1$ //$NON-NLS-2$ |
| tmfEventType, |
| evContent, |
| tmfEventRef); |
| |
| return ev; |
| |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| return createExceptionEvent(rank, INTERRUPTION_EXCEPTION); |
| } catch (java.util.concurrent.ExecutionException e) { |
| return createExceptionEvent(rank, GDB_EXCEPTION); |
| } catch (RejectedExecutionException e) { |
| return createExceptionEvent(rank, REQUEST_REJECTED_EXCEPTION); |
| } catch (TimeoutException e) { |
| return createExceptionEvent(rank, TIMEOUT); |
| } |
| |
| finally { |
| tracker.dispose(); |
| } |
| } |
| |
| /** |
| * This is a helper method for getTraceFrameData, to create for it a |
| * "best effort" GdbTraceEvent when a problem occurs during the reading. |
| * |
| * @param rank |
| * long containing the number of the frame where the problem |
| * occurred |
| * @param message |
| * String containing a brief explanation of problem. |
| * @return a GdbTraceEvent object, filled as best as possible |
| */ |
| private GdbTraceEvent createExceptionEvent(final long rank, final String message) { |
| // get corresponding TP data |
| String tmfEventRef; |
| String tmfEventSrc; |
| MIBreakpointDMData bp = fTpInfo.get((int) rank); |
| if (bp != null) { |
| tmfEventRef = bp.getFileName() + ":" + bp.getLineNumber() + " :: " + bp.getFunctionName(); //$NON-NLS-1$ //$NON-NLS-2$ |
| tmfEventSrc = bp.getFileName() + " :: " + bp.getFunctionName() + ", line: " + bp.getLineNumber(); //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| tmfEventRef = tracedExecutable; |
| tmfEventSrc = "Tracepoint: n/a"; //$NON-NLS-1$ |
| } |
| |
| GdbTraceEventContent evContent = new GdbTraceEventContent("ERROR: " + message, 0, 0); //$NON-NLS-1$ |
| |
| GdbTraceEvent ev = new GdbTraceEvent(fGdbTrace, |
| TmfTimestamp.fromSeconds(rank), |
| tmfEventSrc, |
| tmfEventType, |
| evContent, |
| tmfEventRef); |
| |
| return ev; |
| } |
| |
| /** |
| * @return DSF-GDB session id of the current session. |
| */ |
| public String getSessionId() { |
| return fSessionId; |
| } |
| |
| /** |
| * Handler method that catches the DSF "record selected changed" event. |
| * It in turn creates a TMF "time sync" signal. |
| * @param event TraceRecordSelectedChangedEvent: The DSF event. |
| */ |
| @DsfServiceEventHandler |
| public void handleDSFRecordSelectedEvents(final ITraceRecordSelectedChangedDMEvent event) { |
| if (event instanceof TraceRecordSelectedChangedEvent) { |
| TraceRecordSelectedChangedEvent traceEvent = (TraceRecordSelectedChangedEvent) event; |
| ITraceRecordDMContext context = traceEvent.getDMContext(); |
| final String reference = context.getRecordId(); |
| if (reference != null) { |
| int recordId = Integer.parseInt(reference); |
| selectGdbTraceEditor(context.getSessionId(), recordId); |
| } |
| } |
| } |
| |
| private static void closeGdbTraceEditor(final String sessionId) { |
| Display.getDefault().asyncExec(() -> { |
| for (IWorkbenchWindow wbWindow : PlatformUI.getWorkbench().getWorkbenchWindows()) { |
| for (IWorkbenchPage wbPage : wbWindow.getPages()) { |
| for (IEditorReference editorReference : wbPage.getEditorReferences()) { |
| IEditorPart editor = editorReference.getEditor(false); |
| if (editor instanceof ITmfTraceEditor) { |
| ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace(); |
| if (trace instanceof GdbTrace) { |
| if (((GdbTrace) trace).getDsfSessionId().equals(sessionId)) { |
| wbPage.closeEditor(editor, false); |
| } |
| } |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| private static void selectGdbTraceEditor(final String sessionId, final int recordId) { |
| Display.getDefault().asyncExec(() -> { |
| for (IWorkbenchWindow wbWindow : PlatformUI.getWorkbench().getWorkbenchWindows()) { |
| for (IWorkbenchPage wbPage : wbWindow.getPages()) { |
| for (IEditorReference editorReference : wbPage.getEditorReferences()) { |
| IEditorPart editor = editorReference.getEditor(false); |
| if (editor instanceof ITmfTraceEditor) { |
| ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace(); |
| if (trace instanceof GdbTrace) { |
| if (((GdbTrace) trace).getDsfSessionId().equals(sessionId)) { |
| wbPage.bringToTop(editor); |
| if (recordId != -1) { |
| gotoRank(editor, recordId); |
| } |
| return; |
| } |
| } else if (trace instanceof TmfExperiment) { |
| TmfExperiment experiment = (TmfExperiment) trace; |
| List<ITmfTrace> expTraces = experiment.getTraces(); |
| int nbTraces = expTraces.size(); |
| for (int i = 0; i < nbTraces; i++) { |
| GdbTrace gdbTrace = (GdbTrace) expTraces.get(i); |
| if (gdbTrace.getDsfSessionId().equals(sessionId)) { |
| wbPage.bringToTop(editor); |
| if (recordId != -1) { |
| int rank = recordId * nbTraces + i; |
| gotoRank(editor, rank); |
| } |
| return; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| private static void gotoRank(IEditorPart editor, int rank) { |
| IEditorInput editorInput = editor.getEditorInput(); |
| if (editorInput instanceof IFileEditorInput) { |
| IFile file = ((IFileEditorInput) editorInput).getFile(); |
| try { |
| final IMarker marker = file.createMarker(IMarker.MARKER); |
| marker.setAttribute(IMarker.LOCATION, (Integer) rank); |
| IDE.gotoMarker(editor, marker); |
| marker.delete(); |
| } catch (CoreException e) { |
| GdbTraceCorePlugin.logError(e.getMessage(), e); |
| } |
| } |
| } |
| } |