/*******************************************************************************
* Copyright (c) 2010, 2011 Ericsson 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:
* Ericsson - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
import java.util.Hashtable;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
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.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.internal.service.command.events.MITracepointSelectedEvent;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.events.MIEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.CLITraceDumpInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIResultRecord;
import org.eclipse.cdt.dsf.mi.service.command.output.MITraceFindInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MITraceListVariablesInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MITraceListVariablesInfo.MITraceVariableInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MITraceStatusInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MITraceStopInfo;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.osgi.framework.BundleContext;
/**
* This class implements the IGDBTraceControl interface which gives access
* to the debugger's tracing functionality.
*
* @since 3.0
*/
public class GDBTraceControl_7_2 extends AbstractDsfService implements IGDBTraceControl, ICachingService {
@Immutable
protected static final class MITraceRecordDMContext extends AbstractDMContext implements ITraceRecordDMContext {
// The trace record GDB reference
private final String fReference;
/**
* @param session the DsfSession for this service
* @param parents the parent contexts
* @param reference the trace record reference
* @since 4.0
*/
public MITraceRecordDMContext(DsfSession session, ITraceTargetDMContext parent, String reference) {
super(session.getId(), new IDMContext[] { parent });
fReference = reference;
}
/** @since 4.0 */
public String getRecordId() {
return fReference;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
return baseEquals(obj) && (fReference == null ? ((MITraceRecordDMContext) obj).fReference == null :
(fReference.equals(((MITraceRecordDMContext) obj).fReference)));
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#hashCode()
*/
@Override
public int hashCode() {
return baseHashCode() ^ (fReference == null ? 0 : fReference.hashCode());
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return baseToString() + ".reference(" + fReference + ")"; //$NON-NLS-1$//$NON-NLS-2$
}
}
/**
* Trace record context used to indicate that there is no current trace record selected.
*/
@Immutable
protected static final class InvalidTraceRecordDMContext extends AbstractDMContext implements ITraceRecordDMContext {
/**
* @param session the DsfSession for this service
* @param parents the parent contexts
* @param reference the trace record reference
*/
public InvalidTraceRecordDMContext(DsfSession session, ITraceTargetDMContext parent) {
super(session.getId(), new IDMContext[] { parent });
}
/** @since 4.0 */
public String getRecordId() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
return baseEquals(obj);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.datamodel.AbstractDMContext#hashCode()
*/
@Override
public int hashCode() {
return baseHashCode();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return baseToString() + ".noTraceRecord"; //$NON-NLS-1$
}
}
private class TraceVariableDMData implements ITraceVariableDMData {
private String fName;
private String fValue;
private String fInitialValue;
public TraceVariableDMData(String name, String initial, String value) {
fName = name;
fInitialValue = initial;
fValue = value;
}
public String getName() {
return fName;
}
public String getValue() {
return fValue;
}
public String getInitialValue() {
return fInitialValue;
}
}
private class TraceRecordDMData implements ITraceRecordDMData {
private String fContent;
private String fTracepointNum;
private String fTimestamp;
private String fFrameNumber;
public TraceRecordDMData(String content, String tracepointNum, String frameNumber, String timestamp) {
fContent = content;
fTracepointNum = tracepointNum;
fTimestamp = timestamp;
fFrameNumber = frameNumber;
}
public String getContent() {
return fContent;
}
public String getTracepointNumber() {
return fTracepointNum;
}
public String getRecordId() {
return fFrameNumber;
}
public String getTimestamp() {
return fTimestamp;
}
}
private class TraceStatusDMData implements ITraceStatusDMData {
private int fFreeBufferSize;
private int fTotalBufferSize;
private int fNumberOfCollectedFrames;
private boolean fTracingActive;
private boolean fTracingSupported;
private STOP_REASON_ENUM fStopReason;
private Integer fStoppingTracepoint;
/**
* Create a status when tracing is supported
*/
public TraceStatusDMData(boolean active, int free, int total, int frames,
STOP_REASON_ENUM reason, Integer tracepoint) {
fFreeBufferSize = free;
fTotalBufferSize = total;
fNumberOfCollectedFrames = frames;
fTracingActive = active;
fTracingSupported = true;
fStopReason = reason;
fStoppingTracepoint = tracepoint;
}
/**
* Status without a Stop reason
*/
public TraceStatusDMData(boolean active, int free, int total, int frames) {
this(active, free, total, frames, null, null);
}
/**
* Create a status when tracing is not supported
*/
public TraceStatusDMData() {
this(false, 0, 0, 0);
fTracingSupported = false;
}
public int getFreeBufferSize() {
return fFreeBufferSize;
}
public int getNumberOfCollectedFrame() {
return fNumberOfCollectedFrames;
}
public int getTotalBufferSize() {
return fTotalBufferSize;
}
public boolean isTracingActive() {
return fTracingActive;
}
public boolean isTracingSupported() {
return fTracingSupported;
}
public STOP_REASON_ENUM getStopReason() {
return fStopReason;
}
public Integer getStoppingTracepoint() {
if (fStopReason == null) {
return null;
}
return fStoppingTracepoint;
}
@SuppressWarnings("nls")
@Override
public String toString() {
String str = "\n";
if (!fTracingSupported) {
return "\nTracing is not supported\n";
}
if (fBackend.getSessionType() == SessionType.CORE) {
str += "Off-line trace visualization\n";
} else {
str += "Tracing with live execution\n";
}
if (fCurrentRecordDmc instanceof MITraceRecordDMContext) {
str += "Looking at trace frame " +
((MITraceRecordDMContext)fCurrentRecordDmc).getRecordId() +
", tracepoint " + fTracepointIndexForTraceRecord + "\n\n";
} else {
str += "Not currently looking at any trace frame\n\n";
}
str += "Tracing is currently" + (!fTracingActive ? " not":"") + " active\n";
str += "Buffer contains " + fNumberOfCollectedFrames + " trace frame" +
(fNumberOfCollectedFrames>1?"s":"") + "\n";//" (out of ? created in total)\n";
str += "Currently using " + (fTotalBufferSize - fFreeBufferSize) +
" bytes out of " + fTotalBufferSize + "\n";
if (fStopReason != null) {
assert !fTracingActive;
str += "Tracing stopped because of";
if (fStopReason == STOP_REASON_ENUM.REQUEST) {
str += " user request";
} else if (fStopReason == STOP_REASON_ENUM.PASSCOUNT) {
str += " passcount";
if (fStoppingTracepoint != null) {
str += " of tracepoint number " + fStoppingTracepoint;
}
} else if (fStopReason == STOP_REASON_ENUM.OVERFLOW) {
str += " buffer full";
} else if (fStopReason == STOP_REASON_ENUM.DISCONNECTION) {
str += " disconnection";
} else if (fStopReason == STOP_REASON_ENUM.ERROR) {
str += " error";
} else {
str += " unknow reason";
}
str += "\n";
}
return str;
}
}
private static class TracingSupportedChangeEvent extends AbstractDMEvent<ITraceTargetDMContext>
implements ITracingSupportedChangeDMEvent {
private final boolean fTracingSupported;
public TracingSupportedChangeEvent(ITraceTargetDMContext context, boolean supported) {
super(context);
fTracingSupported = supported;
}
public boolean isTracingSupported() {
return fTracingSupported;
}
}
private static class TracingStartedEvent extends AbstractDMEvent<ITraceTargetDMContext>
implements ITracingStartedDMEvent {
public TracingStartedEvent(ITraceTargetDMContext context) {
super(context);
}
}
private static class TracingStoppedEvent extends AbstractDMEvent<ITraceTargetDMContext>
implements ITracingStoppedDMEvent {
public TracingStoppedEvent(ITraceTargetDMContext context) {
super(context);
}
}
public static class TraceRecordSelectedChangedEvent extends AbstractDMEvent<ITraceRecordDMContext>
implements ITraceRecordSelectedChangedDMEvent {
final boolean fVisualModeEnabled;
public TraceRecordSelectedChangedEvent(ITraceRecordDMContext context) {
super(context);
fVisualModeEnabled = !(context instanceof InvalidTraceRecordDMContext);
}
public boolean isVisualizationModeEnabled() {
return fVisualModeEnabled;
}
}
private CommandCache fTraceCache;
private ICommandControlService fConnection;
private CommandFactory fCommandFactory;
private IGDBBackend fBackend;
private ITraceRecordDMContext fCurrentRecordDmc;
private int fTracepointIndexForTraceRecord;
private boolean fIsTracingActive;
private boolean fIsTracingCurrentlySupported;
private boolean fIsTracingFeatureAvailable = true;
private int fTraceRecordsStored;
public GDBTraceControl_7_2(DsfSession session, ILaunchConfiguration config) {
super(session);
}
/**
* This method initializes this service.
*
* @param requestMonitor
* The request monitor indicating the operation is finished
*/
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize(new RequestMonitor(ImmediateExecutor.getInstance(), requestMonitor) {
@Override
protected void handleSuccess() {
doInitialize(requestMonitor);
}
});
}
/**
* This method initializes this service after our superclass's initialize()
* method succeeds.
*
* @param requestMonitor
* The call-back object to notify when this service's
* initialization is done.
*/
private void doInitialize(RequestMonitor requestMonitor) {
// Register this service.
register(new String[] {IGDBTraceControl.class.getName()},
new Hashtable<String, String>());
fConnection = getServicesTracker().getService(ICommandControlService.class);
fTraceCache = new CommandCache(getSession(), fConnection);
fTraceCache.setContextAvailable(fConnection.getContext(), true);
fBackend = getServicesTracker().getService(IGDBBackend.class);
fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
requestMonitor.done();
}
/**
* This method shuts down this service. It unregisters the service, stops
* receiving service events, and calls the superclass shutdown() method to
* finish the shutdown process.
*
* @return void
*/
@Override
public void shutdown(RequestMonitor requestMonitor) {
unregister();
super.shutdown(requestMonitor);
}
/**
* @return The bundle context of the plug-in to which this service belongs.
*/
@Override
protected BundleContext getBundleContext() {
return GdbPlugin.getBundleContext();
}
public void canStartTracing(ITraceTargetDMContext context, final DataRequestMonitor<Boolean> rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fIsTracingCurrentlySupported == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Tracing not supported", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fBackend.getSessionType() == SessionType.CORE) {
rm.setData(false);
rm.done();
return;
}
if (fCurrentRecordDmc != null) {
// We are visualizing data, no more tracing possible.
rm.setData(false);
rm.done();
return;
}
rm.setData(true);
rm.done();
}
public void startTracing(final ITraceTargetDMContext context, final RequestMonitor rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
canStartTracing(context, new DataRequestMonitor<Boolean>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (!getData()) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot start tracing", null)); //$NON-NLS-1$
rm.done();
return;
}
fConnection.queueCommand(
fCommandFactory.createMITraceStart(context),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
fIsTracingActive = true;
getSession().dispatchEvent(new TracingStartedEvent(context), getProperties());
rm.done();
}
@Override
protected void handleError() {
// Send an event to cause a refresh of the button states
IDMEvent<ITraceTargetDMContext> event;
if (fIsTracingActive) {
event = new TracingStartedEvent(context);
} else {
event = new TracingStoppedEvent(context);
}
getSession().dispatchEvent(event, getProperties());
rm.done();
}
});
}
});
}
public void canStopTracing(ITraceTargetDMContext context, DataRequestMonitor<Boolean> rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fIsTracingCurrentlySupported == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Tracing not supported", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fBackend.getSessionType() == SessionType.CORE) {
rm.setData(false);
rm.done();
return;
}
if (fCurrentRecordDmc != null) {
// We are visualizing data, no more tracing possible.
rm.setData(false);
rm.done();
return;
}
isTracing(context, rm);
}
public void stopTracing(final ITraceTargetDMContext context, final RequestMonitor rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
canStopTracing(context, new DataRequestMonitor<Boolean>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (!getData()) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot stop tracing", null)); //$NON-NLS-1$
rm.done();
return;
}
fConnection.queueCommand(
fCommandFactory.createMITraceStop(context),
new DataRequestMonitor<MITraceStopInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
MITraceStopInfo info = getData();
// Update the tracing state in case it was stopped by the backend
if (fIsTracingActive != info.isTracingActive()) {
fIsTracingActive = info.isTracingActive();
if (!fIsTracingActive) {
getSession().dispatchEvent(new TracingStoppedEvent(context), getProperties());
}
}
fTraceRecordsStored = info.getNumberOfCollectedFrame();
rm.done();
}
});
}
});
}
public void isTracing(ITraceTargetDMContext context, final DataRequestMonitor<Boolean> rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fBackend.getSessionType() == SessionType.CORE) {
rm.setData(false);
rm.done();
return;
}
// Although tracing can be automatically stopped on the target, we
// don't go to the backend for this call, or we would make too many calls
// Instead, we can use our buffered state; we simply won't know about an
// automatic stop until a forced refresh. (Note that the MI notification
// about automatic stops, is not available until GDB 7.2 is released)
rm.setData(fIsTracingActive);
rm.done();
}
public void canSaveTraceData(ITraceTargetDMContext context, DataRequestMonitor<Boolean> rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fIsTracingCurrentlySupported == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Tracing not supported", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fBackend.getSessionType() == SessionType.CORE) {
rm.setData(false);
rm.done();
return;
}
rm.setData(fTraceRecordsStored > 0);
rm.done();
}
public void saveTraceData(final ITraceTargetDMContext context, final String file,
final boolean remoteSave, final RequestMonitor rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
canSaveTraceData(context, new DataRequestMonitor<Boolean>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (!getData()) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot save trace data", null)); //$NON-NLS-1$
rm.done();
return;
}
fConnection.queueCommand(
fCommandFactory.createMITraceSave(context, file, remoteSave),
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
}
});
}
public void canLoadTraceData(ITraceTargetDMContext context, DataRequestMonitor<Boolean> rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
// If this service has been instantiated, it means we support loading trace data.
// Unlike the other operations, loading trace data does not require any GDB special state
// (like being connected to a target)
rm.setData(true);
rm.done();
}
public void loadTraceData(final ITraceTargetDMContext context, final String file, final RequestMonitor rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
canLoadTraceData(context, new DataRequestMonitor<Boolean>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (!getData()) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Cannot load trace data", null)); //$NON-NLS-1$
rm.done();
return;
}
fConnection.queueCommand(
fCommandFactory.createMITargetSelectTFile(context, file),
new DataRequestMonitor<MIInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
fIsTracingCurrentlySupported = true;
// Workaround for GDB pre-release where we don't get the details
// of the frame when we load a trace file.
// To get around this, we can force a select of record 0
final ITraceRecordDMContext initialRecord = createTraceRecordContext(context, "0"); //$NON-NLS-1$
selectTraceRecord(initialRecord, new RequestMonitor(ImmediateExecutor.getInstance(), rm) {
@Override
protected void handleSuccess() {
// This event will indicate to the other services that we are visualizing trace data.
getSession().dispatchEvent(new TraceRecordSelectedChangedEvent(initialRecord), getProperties());
rm.done();
}
});
}
});
}
});
}
public void getTraceStatus(final ITraceTargetDMContext context, final DataRequestMonitor<ITraceStatusDMData> rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fIsTracingFeatureAvailable == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Tracing not supported", null)); //$NON-NLS-1$
rm.done();
return;
}
fConnection.queueCommand(
fCommandFactory.createMITraceStatus(context),
new DataRequestMonitor<MITraceStatusInfo>(getExecutor(), rm) {
@Override
protected void handleError() {
// The MI command
fIsTracingFeatureAvailable = false;
super.handleError();
}
@Override
protected void handleSuccess() {
MITraceStatusInfo info = getData();
if (fIsTracingCurrentlySupported != info.isTracingSupported()) {
fIsTracingCurrentlySupported = info.isTracingSupported();
getSession().dispatchEvent(new TracingSupportedChangeEvent(context, fIsTracingCurrentlySupported), getProperties());
}
if (fIsTracingCurrentlySupported) {
// Update the tracing state in case it was stopped by the backend
if (fIsTracingActive != info.isTracingActive()) {
fIsTracingActive = info.isTracingActive();
if (fIsTracingActive) {
getSession().dispatchEvent(new TracingStartedEvent(context), getProperties());
} else {
getSession().dispatchEvent(new TracingStoppedEvent(context), getProperties());
}
}
fTraceRecordsStored = info.getNumberOfCollectedFrame();
STOP_REASON_ENUM stopReason = info.getStopReason();
if (stopReason == null) {
rm.setData(new TraceStatusDMData(info.isTracingActive(),
info.getFreeBufferSize(),
info.getTotalBufferSize(),
info.getNumberOfCollectedFrame()));
} else {
rm.setData(new TraceStatusDMData(info.isTracingActive(),
info.getFreeBufferSize(),
info.getTotalBufferSize(),
info.getNumberOfCollectedFrame(),
stopReason,
info.getStopTracepoint()));
}
} else {
fTraceRecordsStored = 0;
fIsTracingActive = false;
rm.setData(new TraceStatusDMData());
}
rm.done();
}
});
}
public void createTraceVariable(ITraceTargetDMContext context,
String varName,
String varValue,
RequestMonitor rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fIsTracingCurrentlySupported == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Tracing not supported", null)); //$NON-NLS-1$
rm.done();
return;
}
if (varValue == null) {
fConnection.queueCommand(
fCommandFactory.createMITraceDefineVariable(context, varName),
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
} else {
fConnection.queueCommand(
fCommandFactory.createMITraceDefineVariable(context, varName, varValue),
new DataRequestMonitor<MIInfo>(getExecutor(), rm));
}
}
public void getTraceVariables(ITraceTargetDMContext context, final DataRequestMonitor<ITraceVariableDMData[]> rm) {
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_STATE, "Invalid context", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fIsTracingCurrentlySupported == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Tracing not supported", null)); //$NON-NLS-1$
rm.done();
return;
}
// It may be possible to cache this call, if we can figure out that all the cases
// where to data can change, to clear the cache in those cases
fConnection.queueCommand(
fCommandFactory.createMITraceListVariables(context),
new DataRequestMonitor<MITraceListVariablesInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
MITraceVariableInfo[] vars = getData().getTraceVariables();
TraceVariableDMData[] varDataArray = new TraceVariableDMData[vars.length];
for (int i = 0; i < vars.length; i++) {
varDataArray[i] = new TraceVariableDMData(vars[i].getName(),
vars[i].getInitialValue(),
vars[i].getCurrentValue());
}
rm.setData(varDataArray);
rm.done();
}
});
}
/**
* Create a trace record context
* @since 4.0
*/
public ITraceRecordDMContext createTraceRecordContext(ITraceTargetDMContext ctx, String recordId) {
return new MITraceRecordDMContext(getSession(), ctx, recordId);
}
public ITraceRecordDMContext createNextRecordContext(ITraceRecordDMContext ctx) {
ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(ctx, ITraceTargetDMContext.class);
if (ctx instanceof InvalidTraceRecordDMContext) {
// No specified context, so we return the context for the first
return createTraceRecordContext(targetDmc, "0"); //$NON-NLS-1$
}
if (ctx instanceof MITraceRecordDMContext) {
String recordId = ((MITraceRecordDMContext)ctx).getRecordId();
int recordIndex = Integer.parseInt(recordId);
recordIndex++;
if (recordIndex == fTraceRecordsStored) {
// Loop back to the front
recordIndex = 0;
}
return new MITraceRecordDMContext(getSession(),
targetDmc,
Integer.toString(recordIndex));
}
return null;
}
public ITraceRecordDMContext createPrevRecordContext(ITraceRecordDMContext ctx) {
if (ctx instanceof MITraceRecordDMContext) {
ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(ctx, ITraceTargetDMContext.class);
String recordId = ((MITraceRecordDMContext)ctx).getRecordId();
int recordIndex = Integer.parseInt(recordId);
if (recordIndex == 0) {
// Loop back to the end
recordIndex = fTraceRecordsStored; // The last index of a trace record (zero-based)
}
recordIndex--;
return new MITraceRecordDMContext(getSession(),
targetDmc,
Integer.toString(recordIndex));
}
return null;
}
public void getCurrentTraceRecordContext(ITraceTargetDMContext context, DataRequestMonitor<ITraceRecordDMContext> drm) {
if (fCurrentRecordDmc == null) {
drm.setData(new InvalidTraceRecordDMContext(getSession(), context));
} else {
drm.setData(fCurrentRecordDmc);
}
drm.done();
}
public void selectTraceRecord(final ITraceRecordDMContext context, final RequestMonitor rm) {
if (fIsTracingCurrentlySupported == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Tracing not supported", null)); //$NON-NLS-1$
rm.done();
return;
}
if (context instanceof MITraceRecordDMContext) {
ITraceTargetDMContext targetDmc = DMContexts.getAncestorOfType(context, ITraceTargetDMContext.class);
String recordId = ((MITraceRecordDMContext)context).getRecordId();
final int reference = Integer.parseInt(recordId);
if (reference < 0) {
// This is how we indicate that we want to exit visualization mode
// We should have a specific call in the IGDBTraceControl interface to do this.
stopVisualizingTraceData(targetDmc, rm);
return;
}
fConnection.queueCommand(
fCommandFactory.createMITraceFindFrameNumber(targetDmc, reference),
new DataRequestMonitor<MITraceFindInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (getData().isFound() == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Could not find trace record", null)); //$NON-NLS-1$
rm.done();
return;
}
fCurrentRecordDmc = context;
fTracepointIndexForTraceRecord = getData().getTraceRecord().getTracepointId();
// We could rely on the TraceRecordSelectedChangedEvent to update all the views, but this
// would require a lot of changes.
// Notice that looking at a new trace record should behave in the same manner
// as when the debugger suspends during normal execution; therefore we can simply
// trigger an MIStoppedEvent, as if reported by GDB. Note that we do this already for
// cases where GDB is missing such a event (like older versions of GDB when using a CLI command)
IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class);
if (procService == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Could not find necessary services", null)); //$NON-NLS-1$
rm.done();
return;
}
final MIResultRecord rr = getData().getMIOutput().getMIResultRecord();
if (rr == null) {
assert false;
rm.done();
return;
}
// First find the process we are using.
ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(context, ICommandControlDMContext.class);
procService.getProcessesBeingDebugged(
controlDmc,
new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
assert getData() != null;
assert getData().length == 1;
if (getData() == null || getData().length < 1) {
rm.done();
return;
}
// Choose the first process for now, until gdb can tell
// us which process the trace record is associated with.
// Or maybe GDB already tells us by only reporting a single
// process?
IContainerDMContext processContainerDmc = (IContainerDMContext)(getData()[0]);
// Now find the proper thread. We must do this here because in post-mortem debugging
// we cannot rely on MIRunControl using 'thread', as it will fail
IMIProcesses procService = getServicesTracker().getService(IMIProcesses.class);
if (procService == null) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Could not find necessary services", null)); //$NON-NLS-1$
rm.done();
return;
}
procService.getProcessesBeingDebugged(
processContainerDmc,
new DataRequestMonitor<IDMContext[]>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
assert getData() != null;
assert getData().length == 1;
if (getData() == null || getData().length < 1) {
rm.done();
return;
}
IExecutionDMContext execDmc = (IExecutionDMContext)getData()[0];
MIEvent<?> event = MITracepointSelectedEvent.parse(execDmc, rr.getToken(), rr.getMIResults());
getSession().dispatchEvent(event, getProperties());
rm.done();
}
});
}
});
}
});
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid trace record context.", null)); //$NON-NLS-1$
rm.done();
}
}
private void stopVisualizingTraceData(final ITraceTargetDMContext context, final RequestMonitor rm) {
if (fIsTracingCurrentlySupported == false) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Tracing not supported", null)); //$NON-NLS-1$
rm.done();
return;
}
if (fBackend.getSessionType() == SessionType.CORE) {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Cannot stop visualizing for a post mortem session", null)); //$NON-NLS-1$
rm.done();
return;
}
fConnection.queueCommand(
fCommandFactory.createMITraceFindNone(context),
new DataRequestMonitor<MITraceFindInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
assert getData().isFound() == false;
fCurrentRecordDmc = null;
// This event will indicate to the other services that we are no longer visualizing trace data.
ITraceRecordDMContext invalidDmc = new InvalidTraceRecordDMContext(getSession(), context);
getSession().dispatchEvent(new TraceRecordSelectedChangedEvent(invalidDmc), getProperties());
rm.done();
return;
}
});
}
public void getTraceRecordData(final ITraceRecordDMContext context, final DataRequestMonitor<ITraceRecordDMData> rm) {
if (context instanceof MITraceRecordDMContext) {
RequestMonitor tdumpRm = new RequestMonitor(ImmediateExecutor.getInstance(), rm) {
@Override
protected void handleSuccess() {
fConnection.queueCommand(
fCommandFactory.createCLITraceDump(context),
new DataRequestMonitor<CLITraceDumpInfo>(getExecutor(), rm) {
@Override
protected void handleSuccess() {
TraceRecordDMData data = new TraceRecordDMData(
getData().getContent(),
getData().getTracepointNumber(),
getData().getFrameNumber(),
getData().getTimestamp()
);
rm.setData(data);
rm.done();
}
});
}
};
// If we are pointing to the right context, we can do the tdump right away,
// if not, we should first select the record.
// This is because 'tdump' does not take any parameters to specify
// which record we want to dump.
if (fCurrentRecordDmc.equals(context)) {
tdumpRm.done();
} else {
selectTraceRecord(context, tdumpRm);
}
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid trace record context.", null)); //$NON-NLS-1$
rm.done();
}
}
public void flushCache(IDMContext context) {
fTraceCache.reset(context);
}
}