/******************************************************************************* * Copyright (c) 2008, 2010 Wind River Systems 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.cdt.examples.dsf.pda.service; import java.util.Hashtable; import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; 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.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.ICachingService; import org.eclipse.cdt.dsf.debug.service.IRunControl; import org.eclipse.cdt.dsf.debug.service.IStack; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason; import org.eclipse.cdt.dsf.debug.service.command.CommandCache; import org.eclipse.cdt.dsf.service.AbstractDsfService; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAFrame; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAFrameCommand; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAFrameCommandResult; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStackCommand; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStackCommandResult; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStackDepthCommand; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAStackDepthCommandResult; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; /** * Service for retrieving PDA debugger stack data. * <p> * This service depends on the {@link PDACommandControl} service and the * {@link IRunControl} service. These services must be initialized before * this service is initialized. * </p> */ public class PDAStack extends AbstractDsfService implements IStack, ICachingService { /** * PDA stack frame contains only the stack frame level. It is only * used as an index into the frame data returned by the PDA debugger. */ @Immutable private static class FrameDMContext extends AbstractDMContext implements IFrameDMContext { final private int fLevel; FrameDMContext(String sessionId, PDAThreadDMContext execDmc, int level) { super(sessionId, new IDMContext[] { execDmc }); fLevel = level; } public int getLevel() { return fLevel; } @Override public boolean equals(Object other) { return super.baseEquals(other) && ((FrameDMContext)other).fLevel == fLevel; } @Override public int hashCode() { return super.baseHashCode() ^ fLevel; } @Override public String toString() { return baseToString() + ".frame[" + fLevel + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } } /** * Frame data based on the PDAFrame object returned by the PDA debugger. */ @Immutable private static class FrameDMData implements IFrameDMData { final private PDAFrame fFrame; FrameDMData(PDAFrame frame) { fFrame = frame; } public String getFile() { return fFrame.fFilePath.lastSegment(); } public String getFunction() { return fFrame.fFunction; } public int getLine() { return fFrame.fLine + 1; } public int getColumn() { return 0; } public IAddress getAddress() { return null; } public String getModule() { return "";//$NON-NLS-1$ } } /** * Context representing a variable in a given stack frame. */ @Immutable private static class VariableDMContext extends AbstractDMContext implements IVariableDMContext { final private String fVariable; VariableDMContext(String sessionId, FrameDMContext frameCtx, String variable) { super(sessionId, new IDMContext[] { frameCtx }); fVariable = variable; } String getVariable() { return fVariable; } @Override public boolean equals(Object other) { return super.baseEquals(other) && ((VariableDMContext)other).fVariable.equals(fVariable); } @Override public int hashCode() { return super.baseHashCode() + fVariable.hashCode(); } @Override public String toString() { return baseToString() + ".variable(" + fVariable + ")"; //$NON-NLS-1$ //$NON-NLS-2$ } } /** * PDA variable data, only supports returning the variable name. */ @Immutable private static class VariableDMData implements IVariableDMData { final private String fVariable; VariableDMData(String variable) { fVariable = variable; } public String getName() { return fVariable; } public String getValue() { return null; } } // Services that this service depends on. private PDACommandControl fCommandControl; private IRunControl fRunControl; // Command cache private CommandCache fCommandCache; public PDAStack(DsfSession session) { super(session); } @Override protected BundleContext getBundleContext() { return PDAPlugin.getBundleContext(); } @Override public void initialize(final RequestMonitor rm) { super.initialize( new RequestMonitor(getExecutor(), rm) { @Override protected void handleSuccess() { doInitialize(rm); }}); } private void doInitialize(final RequestMonitor rm) { // Initialize service references that stack service depends on fCommandControl = getServicesTracker().getService(PDACommandControl.class); fRunControl = getServicesTracker().getService(IRunControl.class); // Create the commands cache fCommandCache = new CommandCache(getSession(), fCommandControl); fCommandCache.setContextAvailable(fCommandControl.getContext(), true); // Register to listen for run control events, to clear cache accordingly. getSession().addServiceEventListener(this, null); // Register stack service with OSGi register(new String[]{IStack.class.getName(), PDAStack.class.getName()}, new Hashtable<String,String>()); rm.done(); } @Override public void shutdown(final RequestMonitor rm) { getSession().removeServiceEventListener(this); fCommandCache.reset(); super.shutdown(rm); } public void getArguments(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm) { PDAPlugin.failRequest(rm, IDsfStatusConstants.NOT_SUPPORTED, "PDA debugger does not support function arguments."); } public void getFrameData(final IFrameDMContext frameCtx, final DataRequestMonitor<IFrameDMData> rm) { final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(frameCtx, PDAThreadDMContext.class); if (threadCtx == null) { rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + frameCtx, null)); rm.done(); return; } getStackDepth( threadCtx, -1, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override protected void handleSuccess() { // PDAFrame array is ordered highest to lowest. We need to // calculate the index based on frame level. int frameNum = getData() - frameCtx.getLevel() - 1; if (frameNum < 0) { PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Invalid frame level " + frameCtx); return; } // Execute the PDA stack command, or retrieve the result from cache if already available. fCommandCache.execute( new PDAFrameCommand(threadCtx, frameNum), new DataRequestMonitor<PDAFrameCommandResult>(getExecutor(), rm) { @Override protected void handleSuccess() { // Create the frame data object based on the corresponding PDAFrame rm.setData(new FrameDMData(getData().fFrame)); rm.done(); } }); } }); } public void getFrames(IDMContext context, final DataRequestMonitor<IFrameDMContext[]> rm) { // Can only create stack frames for an execution context as a parent, // however the argument context is a generic context type, so it could // be an execution context, a frame, a variable, etc. Search the // hierarchy of the argument context to find the execution one. final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(context, PDAThreadDMContext.class); if (threadCtx == null) { rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + context, null)); rm.done(); return; } // Execute the stack command and create the corresponding frame contexts. getStackDepth( context, -1, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override protected void handleSuccess() { IFrameDMContext[] frameCtxs = new IFrameDMContext[getData()]; for (int i = 0; i < getData(); i++) { frameCtxs[i] = new FrameDMContext(getSession().getId(), threadCtx, i); } rm.setData(frameCtxs); rm.done(); } }); } public void getFrames(IDMContext context, final int startIndex, final int endIndex, final DataRequestMonitor<IFrameDMContext[]> rm) { // Validate index range. assert startIndex >=0 && (endIndex < 0 || startIndex <= endIndex); final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(context, PDAThreadDMContext.class); if (threadCtx == null) { rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + context, null)); rm.done(); return; } // Execute the stack command and create the corresponding frame contexts. getStackDepth( context, -1, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override protected void handleSuccess() { int numFrames = endIndex < 0 ? (getData() - startIndex) : Math.min(endIndex + 1, getData()) - startIndex; IFrameDMContext[] frameCtxs = new IFrameDMContext[numFrames]; for (int i = 0; i < numFrames; i++) { frameCtxs[i] = new FrameDMContext(getSession().getId(), threadCtx, startIndex + i); } rm.setData(frameCtxs); rm.done(); } }); } public void getLocals(IFrameDMContext context, final DataRequestMonitor<IVariableDMContext[]> rm) { if (!(context instanceof FrameDMContext)) { rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + context, null)); rm.done(); return; } final FrameDMContext frameCtx = (FrameDMContext)context; final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(frameCtx, PDAThreadDMContext.class); if (threadCtx == null) { rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + frameCtx, null)); rm.done(); return; } fCommandCache.execute( new PDAStackCommand(threadCtx), new DataRequestMonitor<PDAStackCommandResult>(getExecutor(), rm) { @Override protected void handleSuccess() { // Find the correct PDAFrame int frameId = getData().fFrames.length - frameCtx.getLevel() - 1; if (frameId < 0) { PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Invalid frame level " + frameCtx); return; } PDAFrame pdaFrame = getData().fFrames[frameId]; // Create variable contexts for all variables in frame. IVariableDMContext[] variableCtxs = new IVariableDMContext[pdaFrame.fVariables.length]; for (int i = 0; i < pdaFrame.fVariables.length; i++) { variableCtxs[i] = new VariableDMContext(getSession().getId(), frameCtx, pdaFrame.fVariables[i]); } rm.setData(variableCtxs); rm.done(); } }); } public void getStackDepth(IDMContext context, final int maxDepth, final DataRequestMonitor<Integer> rm) { final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(context, PDAThreadDMContext.class); if (threadCtx == null) { rm.setStatus(new Status(IStatus.ERROR, PDAPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context" + context, null)); rm.done(); return; } // Execute stack command and return the data's size. fCommandCache.execute( new PDAStackDepthCommand(threadCtx), new DataRequestMonitor<PDAStackDepthCommandResult>(getExecutor(), rm) { @Override protected void handleSuccess() { int depth= getData().fDepth; if (maxDepth > 0 && maxDepth < depth) { depth = maxDepth; } rm.setData(depth); rm.done(); } }); } public void getTopFrame(IDMContext context, final DataRequestMonitor<IFrameDMContext> rm) { // Can only create stack frames for an execution context as a parent, // however the argument context is a generic context type, so it could // be an execution context, a frame, a variable, etc. Search the // hierarchy of the argument context to find the execution one. final PDAThreadDMContext execCtx = DMContexts.getAncestorOfType(context, PDAThreadDMContext.class); if (execCtx == null) { PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Invalid context " + context); return; } // Since the frame context only contain the level, there's no need to // call the PDA debugger. Simply create a context for level 0. rm.setData(new FrameDMContext(getSession().getId(), execCtx, 0)); rm.done(); } public void getVariableData(IVariableDMContext variableCtx, DataRequestMonitor<IVariableDMData> rm) { if ( !(variableCtx instanceof VariableDMContext) ) { PDAPlugin.failRequest(rm, IDsfStatusConstants.INVALID_HANDLE, "Invalid context " + variableCtx); return; } // The variable data doen't contain a value. So there's no need to // go to the back end to retrieve it. String variable = ((VariableDMContext)variableCtx).getVariable(); rm.setData(new VariableDMData(variable)); rm.done(); } public boolean isStackAvailable(IDMContext context) { // Stack is available if the program is suspended or stepping. IExecutionDMContext execCtx = DMContexts.getAncestorOfType(context, IExecutionDMContext.class); return execCtx != null && (fRunControl.isSuspended(execCtx) || (fRunControl.isStepping(execCtx))); } /** * Returns a frame context for the given thread and level; */ public IFrameDMContext getFrameDMContext(PDAThreadDMContext thread, int level) { return new FrameDMContext(getSession().getId(), thread, level); } @DsfServiceEventHandler public void eventDispatched(IResumedDMEvent e) { // Mark the cache as not available, so that stack commands will // fail. Also reset the cache unless it was a step command. fCommandCache.setContextAvailable(e.getDMContext(), false); if (!e.getReason().equals(StateChangeReason.STEP)) { fCommandCache.reset(e.getDMContext()); } } @DsfServiceEventHandler public void eventDispatched(ISuspendedDMEvent e) { // Enable sending commands to target and clear the cache. fCommandCache.setContextAvailable(e.getDMContext(), true); fCommandCache.reset(e.getDMContext()); } public void flushCache(IDMContext context) { fCommandCache.reset(context); } }