/******************************************************************************* * Copyright (c) 2008, 2016 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 java.util.Map; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; 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.debug.service.ICachingService; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; 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.IStack.IFrameDMContext; 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.PDAChildrenCommand; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDACommandResult; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAListResult; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDASetVarCommand; import org.eclipse.cdt.examples.dsf.pda.service.commands.PDAVarCommand; import org.osgi.framework.BundleContext; /** * */ public class PDAExpressions extends AbstractDsfService implements ICachingService, IExpressions { private static final String FORMAT_MY_FORMAT = "My format"; @Immutable private static class ExpressionDMContext extends AbstractDMContext implements IExpressionDMContext { final private String fExpression; ExpressionDMContext(String sessionId, IFrameDMContext frameDmc, String expressin) { super(sessionId, new IDMContext[] { frameDmc }); fExpression = expressin; } @Override public String getExpression() { return fExpression; } @Override public boolean equals(Object other) { return super.baseEquals(other) && ((ExpressionDMContext)other).fExpression.equals(fExpression); } @Override public int hashCode() { return super.baseHashCode() + fExpression.hashCode(); } @Override public String toString() { return baseToString() + ".expression(" + fExpression + ")"; } } /** * PDA expressions are simply variables. Only the variable name * is relevant for its data. */ @Immutable private static class ExpressionDMData implements IExpressionDMData { final private String fExpression; public ExpressionDMData(String expression) { fExpression = expression; } @Override public BasicType getBasicType() { return BasicType.basic; } @Override public String getEncoding() { return null; } @Override public Map<String, Integer> getEnumerations() { return null; } @Override public String getName() { return fExpression; } @Override public IRegisterDMContext getRegister() { return null; } @Override public String getTypeId() { return null; } @Override public String getTypeName() { return null; } } // @see #createExpression() @Immutable private static class InvalidExpressionDMContext extends AbstractDMContext implements IExpressionDMContext { final private String fExpression; public InvalidExpressionDMContext(String sessionId, IDMContext parent, String expr) { super(sessionId, new IDMContext[] { parent }); fExpression = expr; } @Override public boolean equals(Object other) { if (other == null) return false; return super.baseEquals(other) && fExpression == null ? ((InvalidExpressionDMContext) other).getExpression() == null : fExpression.equals(((InvalidExpressionDMContext) other).getExpression()); } @Override public int hashCode() { return fExpression == null ? super.baseHashCode() : super.baseHashCode() ^ fExpression.hashCode(); } @Override public String toString() { return baseToString() + ".invalid_expr[" + fExpression + "]"; } @Override public String getExpression() { return fExpression; } } @Immutable private static class ExpressionChangedDMEvent extends AbstractDMEvent<IExpressionDMContext> implements IExpressionChangedDMEvent { ExpressionChangedDMEvent(IExpressionDMContext expression) { super(expression); } } private PDACommandControl fCommandControl; private PDAStack fStack; private CommandCache fCommandCache; public PDAExpressions(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) { fCommandControl = getServicesTracker().getService(PDACommandControl.class); fStack = getServicesTracker().getService(PDAStack.class); fCommandCache = new CommandCache(getSession(), fCommandControl); fCommandCache.setContextAvailable(fCommandControl.getContext(), true); getSession().addServiceEventListener(this, null); register(new String[]{IExpressions.class.getName(), PDAExpressions.class.getName()}, new Hashtable<String,String>()); rm.done(); } @Override public void shutdown(final RequestMonitor rm) { getSession().removeServiceEventListener(this); fCommandCache.reset(); super.shutdown(rm); } @Override public void canWriteExpression(IExpressionDMContext expressionContext, DataRequestMonitor<Boolean> rm) { rm.setData(true); rm.done(); } @Override public IExpressionDMContext createExpression(IDMContext ctx, String expression) { // Create an expression based on the given context and string expression. PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(ctx, PDAThreadDMContext.class); if (threadCtx != null) { // The PDA debugger can only evaluate variables as expressions and only // in context of a frame, so if a frame is not given, create a top-level frame. IFrameDMContext frameCtx = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class); if (frameCtx == null) { frameCtx = fStack.getFrameDMContext(threadCtx, 0); } return new ExpressionDMContext(getSession().getId(), frameCtx, expression); } // If the thread cannot be found in context, return an "invalid" // expression context, because a null return value is not allowed. // Evaluating an invalid expression context will always yield an // error. return new InvalidExpressionDMContext(getSession().getId(), ctx, expression); } @Override public void getBaseExpressions(IExpressionDMContext exprContext, DataRequestMonitor<IExpressionDMContext[]> rm) { PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Not supported"); } @Override public void getExpressionAddressData(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMAddress> rm) { PDAPlugin.failRequest(rm, NOT_SUPPORTED, "Not supported"); } @Override public void getExpressionData(final IExpressionDMContext exprCtx, final DataRequestMonitor<IExpressionDMData> rm) { // Since expression data doesn't contain any more information than the // context, it doesn't require any debugger commmands. if (exprCtx instanceof ExpressionDMContext) { rm.setData(new ExpressionDMData(exprCtx.getExpression())); rm.done(); } else { PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid expression context " + exprCtx); } } @Override public void getSubExpressionCount(final IExpressionDMContext exprCtx, final DataRequestMonitor<Integer> rm) { if (exprCtx instanceof ExpressionDMContext) { final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class); final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); // First retrieve the stack depth, needed to properly calculate // the frame index that is used by the PDAVarCommand. fStack.getStackDepth( frameCtx, 0, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override protected void handleSuccess() { // Calculate the frame index. int frameId = getData() - frameCtx.getLevel() - 1; // Send the command to evaluate the variable. fCommandCache.execute( new PDAChildrenCommand(threadCtx, frameId, exprCtx.getExpression()), new DataRequestMonitor<PDAListResult>(getExecutor(), rm) { @Override protected void handleSuccess() { rm.setData(getData().fValues.length); rm.done(); } }); } }); } else { PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); } } @Override public void getSubExpressions(IExpressionDMContext exprCtx, DataRequestMonitor<IExpressionDMContext[]> rm) { getSubExpressions(exprCtx, -1, -1, rm); } @Override public void getSubExpressions(final IExpressionDMContext exprCtx, final int startIndexArg, final int lengthArg, final DataRequestMonitor<IExpressionDMContext[]> rm) { if (exprCtx instanceof ExpressionDMContext) { final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class); final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); // First retrieve the stack depth, needed to properly calculate // the frame index that is used by the PDAVarCommand. fStack.getStackDepth( frameCtx, 0, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override protected void handleSuccess() { // Calculate the frame index. int frameId = getData() - frameCtx.getLevel() - 1; // Send the command to evaluate the variable. fCommandCache.execute( new PDAChildrenCommand(threadCtx, frameId, exprCtx.getExpression()), new DataRequestMonitor<PDAListResult>(getExecutor(), rm) { @Override protected void handleSuccess() { int start = startIndexArg > 0 ? startIndexArg : 0; int end = lengthArg > 0 ? (start + lengthArg) : getData().fValues.length; IExpressionDMContext[] contexts = new IExpressionDMContext[end - start]; for (int i = start; i < end && i < getData().fValues.length; i++) { contexts[i - start] = new ExpressionDMContext( getSession().getId(), frameCtx, getData().fValues[i]); } rm.setData(contexts); rm.done(); } }); } }); } else { PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid context"); } } @Override public void getAvailableFormats(IFormattedDataDMContext dmc, final DataRequestMonitor<String[]> rm) { getFormattedExpressionValue( new FormattedValueDMContext(this, dmc, NATURAL_FORMAT), new DataRequestMonitor<FormattedValueDMData>(getExecutor(), rm) { @Override protected void handleSuccess() { try { Integer.parseInt(getData().getFormattedValue()); rm.setData(new String[] { DECIMAL_FORMAT, HEX_FORMAT, OCTAL_FORMAT, BINARY_FORMAT, FORMAT_MY_FORMAT }); rm.done(); } catch (NumberFormatException e) { rm.setData(new String[] { STRING_FORMAT }); rm.done(); } } @Override protected void handleErrorOrWarning() { rm.setData(new String[] { STRING_FORMAT }); rm.done(); } }); } @Override public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext exprCtx, String formatId) { // Creates a context that can be used to retrieve a formatted value. return new FormattedValueDMContext(this, exprCtx, formatId); } @Override public void getFormattedExpressionValue(FormattedValueDMContext formattedCtx, final DataRequestMonitor<FormattedValueDMData> rm) { final String formatId = formattedCtx.getFormatID(); final ExpressionDMContext exprCtx = DMContexts.getAncestorOfType(formattedCtx, ExpressionDMContext.class); if (exprCtx != null) { final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class); final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); // First retrieve the stack depth, needed to properly calculate // the frame index that is used by the PDAVarCommand. fStack.getStackDepth( frameCtx, 0, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override protected void handleSuccess() { // Calculate the frame index. int frameId = getData() - frameCtx.getLevel() - 1; // Send the command to evaluate the variable. fCommandCache.execute( new PDAVarCommand(threadCtx, frameId, exprCtx.getExpression()), new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { @Override protected void handleSuccess() { if (NATURAL_FORMAT.equals(formatId) || STRING_FORMAT.equals(formatId)) { rm.setData(new FormattedValueDMData(getData().fResponseText)); rm.done(); } else { try { int intResult = Integer.parseInt(getData().fResponseText); String formattedResult = ""; if (HEX_FORMAT.equals(formatId)) { formattedResult = Integer.toHexString(intResult); StringBuilder prefix = new StringBuilder("0x"); for (int i = 0; i < 8 - formattedResult.length(); i++) { prefix.append('0'); } prefix.append(formattedResult); formattedResult = prefix.toString(); } else if (OCTAL_FORMAT.equals(formatId)) { formattedResult = Integer.toOctalString(intResult); StringBuilder prefix = new StringBuilder("0c"); for (int i = 0; i < 16 - formattedResult.length(); i++) { prefix.append('0'); } prefix.append(formattedResult); formattedResult = prefix.toString(); } else if (BINARY_FORMAT.equals(formatId)) { formattedResult = Integer.toBinaryString(intResult); StringBuilder prefix = new StringBuilder("0b"); for (int i = 0; i < 32 - formattedResult.length(); i++) { prefix.append('0'); } prefix.append(formattedResult); formattedResult = prefix.toString(); } else if (DECIMAL_FORMAT.equals(formatId)) { formattedResult = Integer.toString(intResult); } else if (FORMAT_MY_FORMAT.equals(formatId)) { formattedResult = "This value is in my format"; } else { PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid format " + formatId); } rm.setData(new FormattedValueDMData(formattedResult)); rm.done(); } catch (NumberFormatException e) { PDAPlugin.failRequest(rm, REQUEST_FAILED, "Cannot format value"); } } } }); } }); } else { PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid expression context " + formattedCtx); } } @Override public void writeExpression(final IExpressionDMContext exprCtx, final String exprValue, String formatId, final RequestMonitor rm) { writeExpression(exprCtx, exprValue, formatId, true, rm); } /** * Method to write an expression, with an additional parameter to suppress * issuing of the expression changed event. * @see IExpressions#writeExpression(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, String, String, RequestMonitor) */ public void writeExpression(final IExpressionDMContext exprCtx, String formattedExprValue, String formatId, final boolean sendEvent, final RequestMonitor rm) { String value = null; try { if (HEX_FORMAT.equals(formatId)) { if (formattedExprValue.startsWith("0x")) formattedExprValue = formattedExprValue.substring(2); value = Integer.toString( Integer.parseInt(formattedExprValue, 16) ); } else if (DECIMAL_FORMAT.equals(formatId)) { value = Integer.toString( Integer.parseInt(formattedExprValue, 10) ); } else if (OCTAL_FORMAT.equals(formatId)) { if (formattedExprValue.startsWith("0c")) formattedExprValue = formattedExprValue.substring(2); value = Integer.toString( Integer.parseInt(formattedExprValue, 8) ); } else if (BINARY_FORMAT.equals(formatId)) { if (formattedExprValue.startsWith("0b")) formattedExprValue = formattedExprValue.substring(2); value = Integer.toString( Integer.parseInt(formattedExprValue, 2) ); } } catch (NumberFormatException e) { PDAPlugin.failRequest(rm, INVALID_HANDLE, "Value not formatted properly"); return; } final String exprValue = value != null ? value : formattedExprValue; if (exprCtx instanceof ExpressionDMContext) { final PDAThreadDMContext threadCtx = DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class); final IFrameDMContext frameCtx = DMContexts.getAncestorOfType(exprCtx, IFrameDMContext.class); // Similarly to retrieving the variable, retrieve the // stack depth first. fStack.getStackDepth( frameCtx, 0, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override protected void handleSuccess() { // Calculate the frame index. int frameId = getData() - frameCtx.getLevel() - 1; // Send the "write" command to PDA debugger fCommandCache.execute( new PDASetVarCommand( threadCtx, frameId, exprCtx.getExpression(), exprValue), new DataRequestMonitor<PDACommandResult>(getExecutor(), rm) { @Override protected void handleSuccess() { if (sendEvent) { getSession().dispatchEvent(new ExpressionChangedDMEvent(exprCtx), getProperties()); } // An expression changed, clear the cache corresponding to // this event. Since the var evaluate commands, use the thread // context, we have to clear all the cache entries for that thread. fCommandCache.reset(DMContexts.getAncestorOfType(exprCtx, PDAThreadDMContext.class)); rm.done(); } }); } }); } else { PDAPlugin.failRequest(rm, INVALID_HANDLE, "Invalid expression context " + exprCtx); } } @DsfServiceEventHandler public void eventDispatched(IResumedDMEvent e) { // Mark the cache as not available, so that data retrieval 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()); } @DsfServiceEventHandler public void eventDispatched(ExpressionChangedDMEvent e) { // An expression changed, clear the cache corresponding to // this event. Since the var evaluate commands, use the thread // context, we have to clear all the cache entries for that thread. fCommandCache.reset(DMContexts.getAncestorOfType(e.getDMContext(), PDAThreadDMContext.class)); } @Override public void flushCache(IDMContext context) { fCommandCache.reset(context); } }