/******************************************************************************* * Copyright (c) 2006, 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 * Ericsson - Modified for handling of multiple execution contexts * Axel Mueller - Bug 306555 - Add support for cast to type / view as array (IExpressions2) * Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121) * Marc Khouzam (Ericsson) - Added support for expression aliases for return values of functions (bug 341731) * Abeer Bagul (Tensilica) - Extra partition created for arrays of length 20000 or greater (Bug 443687) *******************************************************************************/ package org.eclipse.cdt.dsf.mi.service; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; 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.IExpressions2; import org.eclipse.cdt.dsf.debug.service.IExpressions3; import org.eclipse.cdt.dsf.debug.service.IFormattedValues; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent; import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; import org.eclipse.cdt.dsf.debug.service.IMemorySpaces; import org.eclipse.cdt.dsf.debug.service.IMemorySpaces.DecodeResult; import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; 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.debug.service.command.ICommandControlService; import org.eclipse.cdt.dsf.gdb.GDBTypeParser.GDBType; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordSelectedChangedDMEvent; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetAttributes; import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount; import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildren; import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetValue; import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetVar; import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent; import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo; import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame; import org.eclipse.cdt.dsf.service.AbstractDsfService; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfServicesTracker; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.cdt.utils.Addr32; import org.eclipse.cdt.utils.Addr64; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; /** * This class implements a debugger expression evaluator as a DSF service. The * primary interface that clients of this class should use is IExpressions. * * This class used to be name ExpressionService in the 1.1 release. * * @since 2.0 */ public class MIExpressions extends AbstractDsfService implements IMIExpressions, ICachingService { private static final int PARTITION_LENGTH = 100; /** * A format that gives more details about an expression and supports pretty-printing * provided by the backend. * * @since 3.0 */ public static final String DETAILS_FORMAT = "Details"; //$NON-NLS-1$ /* The order given here is the order that will be used by DSF in the Details Pane */ private static final String[] FORMATS_SUPPORTED = new String[] { DETAILS_FORMAT, IFormattedValues.NATURAL_FORMAT, IFormattedValues.DECIMAL_FORMAT, IFormattedValues.HEX_FORMAT, IFormattedValues.BINARY_FORMAT, IFormattedValues.OCTAL_FORMAT }; /** * This class represents the two expressions that characterize an Expression Context. */ public static class ExpressionInfo { private final String fullExpression; private final String relativeExpression; private boolean isDynamic = false; private ExpressionInfo parent; private int indexInParent = -1; private int childCountLimit = IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED; public ExpressionInfo(String full, String relative) { fullExpression = full; relativeExpression = relative; } /** * @since 4.0 */ public ExpressionInfo(String full, String relative, boolean isDynamic, ExpressionInfo parent, int indexInParent) { fullExpression = full; relativeExpression = relative; this.isDynamic = isDynamic; this.parent = parent; this.indexInParent = indexInParent; } public String getFullExpr() { return fullExpression; } public String getRelExpr() { return relativeExpression; } @Override public boolean equals(Object other) { if (other instanceof ExpressionInfo) { if (fullExpression == null ? ((ExpressionInfo) other).fullExpression == null : fullExpression.equals(((ExpressionInfo) other).fullExpression)) { if (relativeExpression == null ? ((ExpressionInfo) other).relativeExpression == null : relativeExpression.equals(((ExpressionInfo) other).relativeExpression)) { // The other members don't play any role for equality. return true; } } } return false; } @Override public int hashCode() { return (fullExpression == null ? 0 : fullExpression.hashCode()) ^ (relativeExpression == null ? 0 : relativeExpression.hashCode()); // The other members don't play any role for equality. } @Override public String toString() { return "[" + fullExpression +", " + relativeExpression + ", isDynamic=" + isDynamic + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ } /** * @return The parent expression info, if existing. * @since 4.0 */ public ExpressionInfo getParent() { return parent; } /** * @return The index in the child array of the parent. Only valid if * {@link #getParent()} returns not null. * @since 4.0 */ public int getIndexInParentExpression() { return indexInParent; } /** * @return Whether the corresponding variable object is dynamic, * i.e. it's value and children are provided by a pretty printer. * @since 4.0 */ public boolean isDynamic() { return isDynamic; } /** * @return Whether the expression info has any ancestor that is dynamic. * @since 4.0 */ public boolean hasDynamicAncestor() { for (ExpressionInfo parent = getParent(); parent != null; parent = parent.getParent()) { if (parent.isDynamic()) { return true; } } return false; } /** * @param isDynamic * Whether the value and children of this expression is * currently provided by a pretty printer or not. * @since 4.0 */ public void setDynamic(boolean isDynamic) { this.isDynamic = isDynamic; } /** * @param parent The new parent expression info. * @since 4.0 */ public void setParent(ExpressionInfo parent) { this.parent = parent; } /** * @param index The index in the children array of the parent. * @since 4.0 */ public void setIndexInParent(int index) { this.indexInParent = index; } /** * @return The current limit on the number of children to be fetched. * @since 4.0 */ public int getChildCountLimit() { return childCountLimit; } /** * @param newLimit * The new limit on the number of children to be fetched. * @since 4.0 */ public void setChildCountLimit(int newLimit) { this.childCountLimit = newLimit; } /** * @return if this expression is part of the memory space or not. * If it not part of the memory space, it won't have an address. * @since 4.3 */ public boolean inMemory() { // Registers and convenience variables which both start with $ // are not part of memory. We care about the top-most parent // as it is the only one that can be a register or convenience var. if (getParent() == null) { if (getRelExpr().startsWith("$")) { //$NON-NLS-1$ return false; } return true; } return getParent().inMemory(); } } /** * This class represents an expression. * @noextend This class is not intended to be subclassed by clients. * @since 4.3 */ public static class MIExpressionDMC extends AbstractDMContext implements IExpressionDMContext { /** * This field holds an expression to be evaluated. */ private ExpressionInfo exprInfo; /** * ExpressionDMC Constructor for expression to be evaluated in context of * a stack frame. * * @param sessionId * The session ID in which this context is created. * @param expression * The expression to be described by this ExpressionDMC * @param relExpr * The relative expression if this expression was created as a child * @param frameCtx * The parent stack frame context for this ExpressionDMC. */ public MIExpressionDMC(String sessionId, String expression, String relExpr, IFrameDMContext frameCtx) { this(sessionId, expression, relExpr, (IDMContext)frameCtx); } /** * ExpressionDMC Constructor for expression to be evaluated in context of * an thread. * * @param sessionId * The session ID in which this context is created. * @param expression * The expression to be described by this ExpressionDMC * @param relExpr * The relative expression if this expression was created as a child * @param execCtx * The parent thread context for this ExpressionDMC. */ public MIExpressionDMC(String sessionId, String expression, String relExpr, IMIExecutionDMContext execCtx) { this(sessionId, expression, relExpr, (IDMContext)execCtx); } /** * ExpressionDMC Constructor for expression to be evaluated in context of * a memory space. * * @param sessionId * The session ID in which this context is created. * @param expression * The expression to be described by this ExpressionDMC * @param relExpr * The relative expression if this expression was created as a child * @param memoryCtx * The parent memory space context for this ExpressionDMC. */ public MIExpressionDMC(String sessionId, String expression, String relExpr, IMemoryDMContext memoryCtx) { this(sessionId, expression, relExpr, (IDMContext)memoryCtx); } private MIExpressionDMC(String sessionId, String expr, String relExpr, IDMContext parent) { this(sessionId, new ExpressionInfo(expr, relExpr), parent); } /** * ExpressionDMC Constructor for expression to be evaluated in context * of a stack frame. * * @param sessionId * The session ID in which this context is created. * @param info * The expression info that this expression is to use. * @param frameCtx * The parent stack frame context for this ExpressionDMC. * * @since 4.0 */ public MIExpressionDMC(String sessionId, ExpressionInfo info, IFrameDMContext frameCtx) { this(sessionId, info, (IDMContext)frameCtx); } /** * @since 4.3 */ public MIExpressionDMC(String sessionId, ExpressionInfo info, IDMContext parent) { super(sessionId, new IDMContext[] { parent }); exprInfo = info; } /** * @return True if the two objects are equal, false otherwise. */ @Override public boolean equals(Object other) { return super.baseEquals(other) && exprInfo.equals(((MIExpressionDMC)other).exprInfo); } /** * * @return The hash code of this ExpressionDMC object. */ @Override public int hashCode() { return super.baseHashCode() + exprInfo.hashCode(); } /** * * @return A string representation of this ExpressionDMC (including the * expression to which it is bound). */ @Override public String toString() { return baseToString() + ".expr" + exprInfo.toString(); //$NON-NLS-1$ } /** * @return The full expression string represented by this ExpressionDMC */ @Override public String getExpression() { return exprInfo.getFullExpr(); } /** * @return The relative expression string represented by this ExpressionDMC */ public String getRelativeExpression() { return exprInfo.getRelExpr(); } /** * @return Get the expression info for this context. * @since 4.0 */ public ExpressionInfo getExpressionInfo() { return exprInfo; } /** * @param info * * @since 4.0 */ public void setExpressionInfo(ExpressionInfo info) { assert (this.exprInfo.getFullExpr().equals(info.getFullExpr())); assert (this.exprInfo.getRelExpr().equals(info.getRelExpr())); this.exprInfo = info; } } protected static class InvalidContextExpressionDMC extends AbstractDMContext implements IExpressionDMContext { private final String expression; public InvalidContextExpressionDMC(String sessionId, String expr, IDMContext parent) { super(sessionId, new IDMContext[] { parent }); expression = expr; } @Override public boolean equals(Object other) { return super.baseEquals(other) && (expression == null ? ((InvalidContextExpressionDMC) other).getExpression() == null : expression.equals(((InvalidContextExpressionDMC) other).getExpression())); } @Override public int hashCode() { return expression == null ? super.baseHashCode() : super.baseHashCode() ^ expression.hashCode(); } @Override public String toString() { return baseToString() + ".invalid_expr[" + expression + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } @Override public String getExpression() { return expression; } } /** * @since 4.1 */ protected static class IndexedPartitionDMC extends MIExpressionDMC implements IIndexedPartitionDMContext { final private MIExpressionDMC fParentExpression; private final int fIndex; private final int fLength; /** * @deprecated This method does not keep track of casted expressions. * It has been replaced by the constructor that takes an MIExpressionDMC * as a parameter. */ @Deprecated public IndexedPartitionDMC( String sessionId, ExpressionInfo parentInfo, IFrameDMContext frameCtx, int index, int length) { this(new MIExpressionDMC(sessionId, parentInfo, frameCtx), frameCtx, index, length); } /** * @since 4.2 */ public IndexedPartitionDMC( MIExpressionDMC parentExpr, int index, int length) { this(parentExpr, getParentDmc(parentExpr), index, length); } /** * @param parentExpr The expression of the array. This can be a casted expression. * This is not the parent that will be used in the context hierarchy, as we chose * not to stack up partitions. * @param parentDmc The frame or thread context that will be used as a parent in the context hierarchy. */ private IndexedPartitionDMC( MIExpressionDMC parentExpr, IDMContext parentDmc, int index, int length) { super(parentExpr.getSessionId(), createExpressionInfo(parentExpr.getExpressionInfo(), index, length), parentDmc); fIndex = index; fLength = length; fParentExpression = parentExpr; } /** * Find the frame context that will be the parent of this partition in the context hierarchy. * Not to be confused with the original parent array that contains the partition. That parent * can be obtained using getParentExpressionContext() */ private static IDMContext getParentDmc(MIExpressionDMC parentExpr) { IFrameDMContext frameDmc = DMContexts.getAncestorOfType(parentExpr, IFrameDMContext.class); if (frameDmc != null) { return frameDmc; } IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(parentExpr, IMIExecutionDMContext.class); if (execCtx != null) { // If we have a thread context but not a frame context, we give the user // the expression as per the top-most frame of the specified thread. // To do this, we create our own frame context. DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(), parentExpr.getSessionId()); MIStack stackService = tracker.getService(MIStack.class); tracker.dispose(); if (stackService != null) { return stackService.createFrameDMContext(execCtx, 0); } } return parentExpr; } public ExpressionInfo getParentInfo() { return fParentExpression.getExpressionInfo(); } /* (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.service.IExpressions4.IIndexedPartitionDMContext#getParentExpression() */ @Override public String getParentExpression() { return getParentExpressionContext().getExpression(); } /** * Get the context of the parent array. This can be used to know if the * parent array is a casted expression. * @since 4.2 */ public MIExpressionDMC getParentExpressionContext() { return fParentExpression; } @Override public int getIndex() { return fIndex; } @Override public int getLength() { return fLength; } @Override public boolean equals(Object other) { return super.baseEquals(other) && ((IndexedPartitionDMC) other).getParentExpressionContext().equals(getParentExpressionContext()) && ((IndexedPartitionDMC) other).getIndex() == getIndex() && ((IndexedPartitionDMC) other).getLength() == getLength(); } @Override public int hashCode() { return super.baseHashCode() + 17*getIndex() + 31*getLength(); } @Override public String toString() { return String.format( "%s.expr[%s][%d-%d]", baseToString(), getParentExpression(), getIndex(), getIndex() + getLength() - 1); //$NON-NLS-1$ } private static ExpressionInfo createExpressionInfo(ExpressionInfo parentInfo, int index, int length) { String expression = String.format( "*((%s)+%d)@%d", //$NON-NLS-1$ parentInfo.getFullExpr(), Integer.valueOf(index), Integer.valueOf(length)); return new ExpressionInfo(expression, expression); } } /** * Contains the address of an expression as well as the size of its type. */ protected static class ExpressionDMAddress implements IExpressionDMAddress { IAddress fAddr; int fSize; String fMemSpace = ""; //$NON-NLS-1$ public ExpressionDMAddress(IAddress addr, int size) { fAddr = addr; fSize = size; } public ExpressionDMAddress(String addrStr, int size) { fSize = size; // We must count the "0x" and that // is why we compare with 10 characters // instead of 8 if (addrStr.length() <= 10) { fAddr = new Addr32(addrStr); } else { fAddr = new Addr64(addrStr); } } /** * @since 5.0 */ public ExpressionDMAddress(String addrStr, int size, String memSpace) { this(addrStr, size); fMemSpace = memSpace; } @Override public IAddress getAddress() { return fAddr; } @Override public int getSize() { return fSize; } /** * @since 5.0 */ @Override public String getMemorySpaceID() { return fMemSpace; } @Override public boolean equals(Object other) { if (other instanceof ExpressionDMAddress) { ExpressionDMAddress otherAddr = (ExpressionDMAddress) other; boolean sameAddr = fAddr == null ? otherAddr.getAddress() == null : fAddr.equals(otherAddr.getAddress()); boolean sameMemSpace = fMemSpace == null ? otherAddr.getMemorySpaceID() == null : fMemSpace.equals(otherAddr.getMemorySpaceID()); return (fSize == otherAddr.getSize()) && sameAddr && sameMemSpace; } return false; } @Override public int hashCode() { return (fAddr == null ? 0 :fAddr.hashCode()) + fSize; } @Override public String toString() { return (fAddr == null ? "null" : "(" + fAddr.toHexAddressString()) + ", " + fSize + ")"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ } } /** * If an expressions doesn't have an address, or it cannot be determined, * use this class. * @since 4.0 */ protected class InvalidDMAddress implements IExpressionDMLocation { @Override public IAddress getAddress() { return IExpressions.IExpressionDMLocation.INVALID_ADDRESS; } @Override public int getSize() { return 0; } @Override public String getLocation() { return ""; //$NON-NLS-1$ } } /** * This class represents the static data referenced by an instance of ExpressionDMC, * such as its type and number of children; it does not contain the value or format * of the expression. */ protected static class ExpressionDMData implements IExpressionDMDataExtension { // This is the relative expression, such as the name of a field within a structure, // in contrast to the fully-qualified expression contained in the ExpressionDMC, // which refers to the full name, including parent structure. private final String relativeExpression; private final String exprType; /** * A hint at the number of children. * In the case of C++ complex structures, this number will not be the * actual number of children. This is because GDB considers * 'private/protected/public' as an actual level of children, but * we do not. This number is meant to be used to know if the expression * has children at all. */ private final int numChildrenHint; private final boolean editable; private final BasicType fBasicType; /** * ExpressionDMData constructor. */ public ExpressionDMData(String expr, String type, int num, boolean edit) { this (expr, type, num, edit, null); } /** * ExpressionDMData constructor. * @since 3.0 */ public ExpressionDMData(String expr, String type, int num, boolean edit, BasicType basicType) { relativeExpression = expr; exprType = type; numChildrenHint = num; editable = edit; fBasicType = basicType; } @Override public BasicType getBasicType() { return fBasicType; } @Override public String getEncoding() { return null; } @Override public Map<String, Integer> getEnumerations() { return new HashMap<String, Integer>(); } @Override public String getName() { return relativeExpression; } @Override public IRegisterDMContext getRegister() { return null; } // See class VariableVMNode for an example of usage of this method public String getStringValue() { return null; } @Override public String getTypeId() { return null; } @Override public String getTypeName() { return exprType; } /** * This method only returns a 'hint' to the number of children. * In the case of C++ complex structures, this number will not be the * actual number of children. This is because GDB considers * 'private/protected/public' as an actual level of children, but * we do not. * * This method can be used reliably to know if the expression * does have children or not. However, for this particular use, * the new {@link IExpressionDMDataExtension#hasChildren()} method should be used instead. * * To get the correct number of children of an expression, a call * to {@link IExpressions#getSubExpressionCount} should be used. * * @deprecated */ @Deprecated public int getNumChildren() { return numChildrenHint; } public boolean isEditable() { return editable; } /** * @since 4.0 */ @Override public boolean hasChildren() { return numChildrenHint > 0; } @Override public boolean equals(Object other) { if (other instanceof ExpressionDMData) { ExpressionDMData otherData = (ExpressionDMData) other; return (numChildrenHint == otherData.numChildrenHint) && (getTypeName() == null ? otherData.getTypeName() == null : getTypeName().equals(otherData.getTypeName())) && (getName() == null ? otherData.getName() == null : getName().equals(otherData.getName())); } return false; } @Override public int hashCode() { return relativeExpression == null ? 0 : relativeExpression.hashCode() + exprType == null ? 0 : exprType.hashCode() + numChildrenHint; } @Override public String toString() { return "relExpr=" + relativeExpression + ", type=" + exprType + ", numchildren=" + numChildrenHint; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ } } /** * Event generated every time an expression is changed by the ExpressionService. * * A client wishing to receive such events has to register as a service * event listener and implement the corresponding eventDispatched method. * * E.g.: * * getSession().addServiceEventListener(listenerObject, null); * * @DsfServiceEventHandler * public void eventDispatched(ExpressionChangedEvent e) { * IExpressionDMContext context = e.getDMContext(); * // do something... * } */ protected static class ExpressionChangedEvent extends AbstractDMEvent<IExpressionDMContext> implements IExpressionChangedDMEvent { public ExpressionChangedEvent(IExpressionDMContext context) { super(context); } } /** * Keeps track of aliases for return values of methods. */ private class ReturnValueAliasing { /** * Map of expression to alias. The expression is the name of the convenience variable * storing the return value, e.g., $1 -> "foo() returned" * This map allows to quickly find the alias to be used for return value variables. */ private Map<String, String> fExpressionAliasesMap = new HashMap<String, String>(); /** * Map of thread to aliases expression list. This map allows to know which aliases are related * to a thread of execution. This is important to allow us to delete aliases when a * thread exits. Note that we need a list because we keep all previous aliases until * the thread exits. */ private Map<IMIExecutionDMContext, List<String>> fThreadToAliasedExpressionsMap = new HashMap<IMIExecutionDMContext, List<String>>(); /** * Map of thread to the name of the method the thread last stopped in. * This allows us to create the alias based on the method the thread was in * before it returned out of the method. */ private Map<IMIExecutionDMContext, String> fThreadToTopMethodName = new HashMap<IMIExecutionDMContext, String>(); /** * Create an alias for expr with respect to threadDmc. * The alias is created based on where threadDmc was previously stopped. */ public void createAlias(IMIExecutionDMContext threadDmc, String expr) { String alias = expr; String methodName = fThreadToTopMethodName.get(threadDmc); if (methodName != null) { alias = String.format(Messages.MIExpressions_ReturnValueAlias, methodName + "()"); //$NON-NLS-1$ } fExpressionAliasesMap.put(expr, alias); List<String> aliasedExprList = fThreadToAliasedExpressionsMap.get(threadDmc); if (aliasedExprList == null) { aliasedExprList = new ArrayList<String>(); fThreadToAliasedExpressionsMap.put(threadDmc, aliasedExprList); } aliasedExprList.add(expr); } /** * Clear all information related to a particular thread of execution. */ public void clearThread(IMIExecutionDMContext threadDmc) { fThreadToTopMethodName.remove(threadDmc); clearAliases(threadDmc); } /** * Clear all aliased expressions related to a particular thread of execution. * It is good to keep the aliases around as long as the thread is alive; * even if we won't show the return value automatically, the user * could add the expression in the expression view, and the alias * would then be used. */ public void clearAliases(IMIExecutionDMContext threadDmc) { List<String> aliasedExprList = fThreadToAliasedExpressionsMap.remove(threadDmc); if (aliasedExprList != null) { for (String expr : aliasedExprList) { fExpressionAliasesMap.remove(expr); } } } /** * Update the method name of the last location where threadDmc was stopped. */ public void updateStoppedLocation(IMIExecutionDMContext threadDmc, String methodName) { fThreadToTopMethodName.put(threadDmc, methodName); } /** * @return The alias for 'expr' if there is one. null if there * is no alias for that expression. */ public String getAlias(String expr) { String alias = fExpressionAliasesMap.get(expr); if (alias == null) { // Check if the expression contains the string that must be aliased. // E.g., $1[0], *$2 // If it does, just replace that string within the expression to // create the full alias for (Entry<String, String> entry : fExpressionAliasesMap.entrySet()) { int index = expr.indexOf(entry.getKey()); if (index != -1) { // Found the string! Now replace it with our alias. // We put it between () to make things clearer to the user. // Note that there can only be one string contained // in the expression, so once we found it, we are done. alias = expr.substring(0, index) + "(" + entry.getValue() + ")" + //$NON-NLS-1$ //$NON-NLS-2$ expr.substring(index + entry.getKey().length()); break; } } } return alias; } } /** Structure to keep track of aliases for method return values. */ private ReturnValueAliasing fReturnValueAliases = new ReturnValueAliasing(); /** * @since 4.3 */ protected CommandCache fExpressionCache; private CommandFactory fCommandFactory; private MIVariableManager varManager; /** * Indicates that we are currently visualizing trace data. * In this case, some errors should not be reported. */ private boolean fTraceVisualization; private IMemorySpaces fMemorySpaceService; public MIExpressions(DsfSession session) { 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 ImmediateRequestMonitor(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 to receive service events for this session. getSession().addServiceEventListener(this, null); // Register this service, but only if we don't already have an // IExpression service present. This allows another expression // service to be used, while delegating calls to this service. if (getServicesTracker().getService(IExpressions.class) == null) { register(new String[] { IExpressions.class.getName(), IExpressions2.class.getName(), IExpressions3.class.getName(), IMIExpressions.class.getName(), MIExpressions.class.getName() }, new Hashtable<String, String>()); } // Create the expressionService-specific CommandControl which is our // variable object manager. // It will deal with the meta-commands, before sending real MI commands // to the back-end, through the MICommandControl service // It must be created after the ExpressionService is registered // since it will need to find it. varManager = createMIVariableManager(); // Create the meta command cache which will use the variable manager // to actually send MI commands to the back-end fExpressionCache = new CommandCache(getSession(), varManager); ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class); fExpressionCache.setContextAvailable(commandControl.getContext(), true); fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); fMemorySpaceService = getServicesTracker().getService(IMemorySpaces.class); requestMonitor.done(); } /** * Creates the MI variable manager to be used by this expression service. * Overriding classes may override to provide a custom services tracker. * * @since 3.0 */ protected MIVariableManager createMIVariableManager() { return new MIVariableManager(getSession(), getServicesTracker()); } /** * 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. */ @Override public void shutdown(RequestMonitor requestMonitor) { unregister(); varManager.dispose(); getSession().removeServiceEventListener(this); super.shutdown(requestMonitor); } /** * @return The bundle context of the plug-in to which this service belongs. */ @Override protected BundleContext getBundleContext() { return GdbPlugin.getBundleContext(); } /** * Create an expression context with the same full and relative expression */ @Override public IExpressionDMContext createExpression(IDMContext ctx, String expression) { return createExpression(ctx, expression, expression); } /** * Create an expression context. */ public IExpressionDMContext createExpression(IDMContext ctx, String expression, String relExpr) { return createExpression(ctx, new ExpressionInfo(expression, relExpr)); } /** * Create an expression context from a given expression info. * @since 4.0 */ private IExpressionDMContext createExpression(IDMContext ctx, ExpressionInfo info) { String expression = info.getFullExpr(); IFrameDMContext frameDmc = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class); if (frameDmc != null) { return new MIExpressionDMC(getSession().getId(), info, frameDmc); } IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class); if (execCtx != null) { // If we have a thread context but not a frame context, we give the user // the expression as per the top-most frame of the specified thread. // To do this, we create our own frame context. MIStack stackService = getServicesTracker().getService(MIStack.class); if (stackService != null) { frameDmc = stackService.createFrameDMContext(execCtx, 0); return new MIExpressionDMC(getSession().getId(), info, frameDmc); } return new InvalidContextExpressionDMC(getSession().getId(), expression, execCtx); } IMemoryDMContext memoryCtx = DMContexts.getAncestorOfType(ctx, IMemoryDMContext.class); if (memoryCtx != null) { return new MIExpressionDMC(getSession().getId(), info, memoryCtx); } // Don't care about the relative expression at this point return new InvalidContextExpressionDMC(getSession().getId(), expression, ctx); } /** * @see IFormattedValues.getFormattedValueContext(IFormattedDataDMContext, String) * * @param dmc * The context describing the data for which we want to create * a Formatted context. * @param formatId * The format that will be used to create the Formatted context * * @return A FormattedValueDMContext that can be used to obtain the value * of an expression in a specific format. */ @Override public FormattedValueDMContext getFormattedValueContext( IFormattedDataDMContext dmc, String formatId) { return new FormattedValueDMContext(this, dmc, formatId); } /** * @see IFormattedValues.getAvailableFormats(IFormattedDataDMContext, DataRequestMonitor) * * @param dmc * The context describing the data for which we want to know * which formats are available. * @param rm * The data request monitor for this asynchronous operation. * */ @Override public void getAvailableFormats(IFormattedDataDMContext dmc, final DataRequestMonitor<String[]> rm) { rm.setData(FORMATS_SUPPORTED); rm.done(); } /** * Obtains the static data of an expression represented * by an ExpressionDMC object (<tt>dmc</tt>). * * @param dmc * The ExpressionDMC for the expression to be evaluated. * @param rm * The data request monitor that will contain the requested data */ @Override public void getExpressionData( final IExpressionDMContext dmc, final DataRequestMonitor<IExpressionDMData> rm) { if (dmc instanceof MIExpressionDMC) { fExpressionCache.execute( new ExprMetaGetVar(dmc), new DataRequestMonitor<ExprMetaGetVarInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { IExpressionDMData.BasicType basicType = getBasicType(getData()); String relativeExpr = getData().getExpr(); String alias = fReturnValueAliases.getAlias(relativeExpr); if (alias != null) { relativeExpr = alias; } rm.setData(new ExpressionDMData( relativeExpr, getData().getType(), getData().getNumChildren(), getData().getEditable(), basicType)); rm.done(); } }); } else if (dmc instanceof InvalidContextExpressionDMC) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ rm.done(); } } /** * @since 4.7 */ protected IExpressionDMData.BasicType getBasicType(ExprMetaGetVarInfo varInfo) { IExpressionDMData.BasicType basicType = null; GDBType gdbType = varInfo.getGDBType(); if (gdbType != null) { switch (gdbType.getType()) { case GDBType.ARRAY: basicType = IExpressionDMData.BasicType.array; break; case GDBType.FUNCTION: basicType = IExpressionDMData.BasicType.function; break; case GDBType.POINTER: case GDBType.REFERENCE: basicType = IExpressionDMData.BasicType.pointer; break; case GDBType.GENERIC: default: // The interesting question is not hasChildren, // but canHaveChildren. E.g. an empty // collection still is a composite. if (varInfo.hasChildren() || varInfo.getCollectionHint()) { basicType = IExpressionDMData.BasicType.composite; } else { basicType = IExpressionDMData.BasicType.basic; } break; } } return basicType; } /** * Obtains the address of an expression and the size of its type. * * @param dmc * The ExpressionDMC for the expression. * @param rm * The data request monitor that will contain the requested data */ @Override public void getExpressionAddressData( final IExpressionDMContext dmc, final DataRequestMonitor<IExpressionDMAddress> rm) { if (dmc instanceof MIExpressionDMC) { MIExpressionDMC miDMC = (MIExpressionDMC) dmc; if (miDMC.getExpressionInfo().hasDynamicAncestor() || !miDMC.getExpressionInfo().inMemory()) { // For children of dynamic varobjs, there is no full expression that gdb // could evaluate in order to provide address and size. // Also, if an expression is not in memory, such as a register // or a GDB convenience variable, there is no address to return rm.setData(new InvalidDMAddress()); rm.done(); return; } } // First create an address expression and a size expression // to be used in back-end calls final IExpressionDMContext addressDmc = createExpression( dmc, "&(" + dmc.getExpression() + ")" );//$NON-NLS-1$//$NON-NLS-2$ final IExpressionDMContext sizeDmc = createExpression( dmc, "sizeof(" + dmc.getExpression() + ")" ); //$NON-NLS-1$//$NON-NLS-2$ if (addressDmc instanceof InvalidContextExpressionDMC || sizeDmc instanceof InvalidContextExpressionDMC) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { fExpressionCache.execute( fCommandFactory.createMIDataEvaluateExpression(addressDmc), new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { String tmpAddrStr = getData().getValue(); DecodeResult memSpaceParsed = null; if (fMemorySpaceService != null) { try { memSpaceParsed = fMemorySpaceService .decodeAddress(tmpAddrStr); } catch (CoreException e1) { // No memory space id found } } String tMemSpace = ""; //$NON-NLS-1$ if (memSpaceParsed != null) { tmpAddrStr = memSpaceParsed.getExpression(); tMemSpace = memSpaceParsed.getMemorySpaceId(); } final String memSpaceId = tMemSpace; // Deal with addresses of contents of a char* which is in // the form of "0x12345678 \"This is a string\"" int split = tmpAddrStr.indexOf(' '); if (split != -1) tmpAddrStr = tmpAddrStr.substring(0, split); final String addrStr = tmpAddrStr; fExpressionCache.execute( fCommandFactory.createMIDataEvaluateExpression(sizeDmc), new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { try { int size = Integer.parseInt(getData().getValue()); rm.setData(new ExpressionDMAddress(addrStr, size, memSpaceId)); } catch (NumberFormatException e) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Unexpected size format from backend: " + getData().getValue(), null)); //$NON-NLS-1$ } rm.done(); } }); } }); } } /** * Obtains the value of an expression in a specific format. * * @param dmc * The context for the format of the value requested and * for the expression to be evaluated. The expression context * should be a parent of the FormattedValueDMContext. * @param rm * The data request monitor that will contain the requested data */ @Override public void getFormattedExpressionValue( final FormattedValueDMContext dmc, final DataRequestMonitor<FormattedValueDMData> rm) { // We need to make sure the FormattedValueDMContext also holds an ExpressionContext, // or else this method cannot do its work. // Note that we look for MIExpressionDMC and not IExpressionDMC, because // looking for IExpressionDMC could yield InvalidContextExpressionDMC which is still // not what we need. MIExpressionDMC exprDmc = DMContexts.getAncestorOfType(dmc, MIExpressionDMC.class); if (exprDmc == null ) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { if (DETAILS_FORMAT.equals(dmc.getFormatID())) { if (exprDmc.getExpressionInfo().hasDynamicAncestor()) { // -data-evaluate-expression does not work for children of // dynamic varobjs, since there is no full expression // that gdb could evaluate. rm.setData(new FormattedValueDMData(Messages.MIExpressions_NotAvailableBecauseChildOfDynamicVarobj)); rm.done(); } else { // This format is obtained through a different GDB command. // It yields more details than the variableObject output. // Starting with GDB 7.0, this format automatically supports pretty-printing, as long as // GDB has been configured to support it. fExpressionCache.execute( fCommandFactory.createMIDataEvaluateExpression(exprDmc), new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { rm.setData(new FormattedValueDMData(getData().getValue())); rm.done(); } @Override protected void handleError() { if (fTraceVisualization) { rm.setData(new FormattedValueDMData("")); //$NON-NLS-1$ rm.done(); } else { super.handleError(); } } }); } } else { fExpressionCache.execute( new ExprMetaGetValue(dmc), new DataRequestMonitor<ExprMetaGetValueInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { rm.setData(new FormattedValueDMData(getData().getValue())); rm.done(); } }); } } } /* Not implemented * * (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.service.IExpressions#getBaseExpressions(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) */ @Override public void getBaseExpressions(IExpressionDMContext exprContext, DataRequestMonitor<IExpressionDMContext[]> rm) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$ rm.done(); } /** * Retrieves the children expressions of the specified expression * * @param dmc * The context for the expression for which the children * should be retrieved. * @param rm * The data request monitor that will contain the requested data */ @Override public void getSubExpressions(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMContext[]> rm) { getSubExpressions(dmc, -1, -1, rm); } /** * Retrieves a range of children expressions of the specified expression * * @param exprCtx * The context for the expression for which the children * should be retrieved. * @param startIndex * The starting index within the list of all children of the parent * expression. Must be a positive integer. * @param length * The length or number of elements of the range requested. * Must be a positive integer. * @param rm * The data request monitor that will contain the requested data */ @Override public void getSubExpressions(final IExpressionDMContext exprCtx, final int startIndex, final int length, final DataRequestMonitor<IExpressionDMContext[]> rm) { if (exprCtx instanceof IndexedPartitionDMC) { getIndexedPartitionChildren((IndexedPartitionDMC)exprCtx, startIndex, length, rm); } else if (exprCtx instanceof MIExpressionDMC) { getRealSubExpressionCount( exprCtx, IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED, new DataRequestMonitor<Integer>(getExecutor(), rm) { /* (non-Javadoc) * @see org.eclipse.cdt.dsf.concurrent.RequestMonitor#handleSuccess() */ @Override protected void handleSuccess() { final int realNumChildren = getData().intValue(); if (realNumChildren == 0) { rm.setData(new IExpressionDMContext[0]); rm.done(); return; } if (realNumChildren <= getArrayPartitionLength()) { getRealSubExpressions(exprCtx, startIndex, length, rm); } else { getExpressionData( exprCtx, new DataRequestMonitor<IExpressionDMData>(ImmediateExecutor.getInstance(), rm) { @Override protected void handleSuccess() { if (IExpressionDMData.BasicType.array.equals(getData().getBasicType())) { rm.setData(getTopLevelIndexedPartitions((MIExpressionDMC)exprCtx, realNumChildren, startIndex, length )); rm.done(); } else { getRealSubExpressions(exprCtx, startIndex, length, rm); } } }); } } }); } else if (exprCtx instanceof InvalidContextExpressionDMC) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ rm.done(); } } /** * @since 4.0 */ @Override public void safeToAskForAllSubExpressions(IExpressionDMContext dmc, final DataRequestMonitor<Boolean> rm) { if (dmc instanceof MIExpressionDMC) { fExpressionCache.execute( new ExprMetaGetVar(dmc), new DataRequestMonitor<ExprMetaGetVarInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { boolean safe = getData().isSafeToAskForAllChildren(); rm.setData(safe); rm.done(); } }); } else if (dmc instanceof InvalidContextExpressionDMC) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ rm.done(); } } /** * @since 4.0 */ @Override public void getSubExpressionCount(final IExpressionDMContext dmc, final int numChildLimit, final DataRequestMonitor<Integer> rm) { if (dmc instanceof MIExpressionDMC) { if (dmc instanceof IndexedPartitionDMC) { int length = ((IndexedPartitionDMC)dmc).getLength(); rm.setData(computeNumberOfChildren(length)); rm.done(); } else { getRealSubExpressionCount( dmc, numChildLimit, new DataRequestMonitor<Integer>(getExecutor(), rm) { @Override protected void handleSuccess() { final int realNum = getData().intValue(); if (realNum <= getArrayPartitionLength()) { rm.setData(Integer.valueOf(realNum)); rm.done(); } else { getExpressionData( dmc, new DataRequestMonitor<IExpressionDMData>(ImmediateExecutor.getInstance(), rm) { @Override protected void handleSuccess() { if (IExpressionDMData.BasicType.array.equals(getData().getBasicType())) { rm.setData(computeNumberOfChildren(realNum)); } else { rm.setData(Integer.valueOf(realNum)); } rm.done(); } }); } } }); } } else if (dmc instanceof InvalidContextExpressionDMC) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ rm.done(); } } /** * Retrieves the count of children expressions of the specified expression * * @param dmc * The context for the expression for which the children count * should be retrieved. * @param rm * The data request monitor that will contain the requested data */ @Override public void getSubExpressionCount(IExpressionDMContext dmc, final DataRequestMonitor<Integer> rm) { getSubExpressionCount(dmc, IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED, rm); } /** * This method indicates if an expression can be written to. * * @param dmc The data model context representing an expression. * * @param rm Data Request monitor containing True if this expression's value can be edited. False otherwise. */ @Override public void canWriteExpression(IExpressionDMContext dmc, final DataRequestMonitor<Boolean> rm) { if (dmc instanceof MIExpressionDMC) { fExpressionCache.execute( new ExprMetaGetAttributes(dmc), new DataRequestMonitor<ExprMetaGetAttributesInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { rm.setData(getData().getEditable()); rm.done(); } }); } else if (dmc instanceof InvalidContextExpressionDMC) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ rm.done(); } } /** * Changes the value of the specified expression based on the new value and format. * * @param dmc * The context for the expression for which the value * should be changed. * @param expressionValue * The new value for the specified expression * @param formatId * The format in which the value is specified * @param rm * The request monitor that will indicate the completion of the operation */ @Override public void writeExpression(final IExpressionDMContext dmc, String expressionValue, String formatId, final RequestMonitor rm) { if (dmc instanceof MIExpressionDMC) { // This command must not be cached, since it changes the state of the back-end. // We must send it directly to the variable manager varManager.writeValue( dmc, expressionValue, formatId, new RequestMonitor(getExecutor(), rm) { @Override protected void handleSuccess() { // A value has changed, we should remove any references to that // value in our cache. Since we don't have such granularity, // we must clear the entire cache. // We cannot use the context to do a more-specific reset, because // the same global variable can be set with different contexts fExpressionCache.reset(); // Issue event that the expression has changed getSession().dispatchEvent(new ExpressionChangedEvent(dmc), getProperties()); rm.done(); } }); } else if (dmc instanceof InvalidContextExpressionDMC) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ rm.done(); } } @DsfServiceEventHandler public void eventDispatched(IResumedDMEvent e) { fExpressionCache.setContextAvailable(e.getDMContext(), false); if (e.getReason() != StateChangeReason.STEP) { fExpressionCache.reset(); } } @DsfServiceEventHandler public void eventDispatched(ISuspendedDMEvent e) { fExpressionCache.setContextAvailable(e.getDMContext(), true); fExpressionCache.reset(); handleReturnValueAliasing(e); } private void handleReturnValueAliasing(ISuspendedDMEvent e) { // Process MIStoppedEvent from within the ISuspendedDMEvent // to avoid any race conditions where the actual MIStoppedEvent // can arrive faster that a preceding IResumedDMEvent if (e instanceof IMIDMEvent) { Object miEvent = ((IMIDMEvent)e).getMIEvent(); if (miEvent instanceof MIStoppedEvent) { IMIExecutionDMContext stoppedEventThread = null; if (e instanceof IContainerSuspendedDMEvent) { // All-stop mode IExecutionDMContext[] triggerContexts = ((IContainerSuspendedDMEvent)e).getTriggeringContexts(); if (triggerContexts.length != 0 && triggerContexts[0] instanceof IMIExecutionDMContext) { stoppedEventThread = (IMIExecutionDMContext)triggerContexts[0]; } } else { // Non-stop mode IDMContext dmc = e.getDMContext(); if (dmc instanceof IMIExecutionDMContext) { stoppedEventThread = (IMIExecutionDMContext)dmc; } } if (stoppedEventThread != null) { if (miEvent instanceof MIFunctionFinishedEvent) { // When getting an MIFunctionFinishedEvent we must set // a proper alias for the convenience variable String resultVar = ((MIFunctionFinishedEvent)miEvent).getGDBResultVar(); if (resultVar != null && !resultVar.isEmpty()) { fReturnValueAliases.createAlias(stoppedEventThread, resultVar); } } // Keep track of the latest method the thread is stopped in. // Must do this after creating any alias, or else we will overwrite // the previous function name, which we need for the alias MIFrame frame = ((MIStoppedEvent)miEvent).getFrame(); if (frame != null) { fReturnValueAliases.updateStoppedLocation(stoppedEventThread, frame.getFunction()); } } } } } @DsfServiceEventHandler public void eventDispatched(IMemoryChangedEvent e) { fExpressionCache.reset(); // MIVariableManager separately traps this event } /** @since 3.0 */ @DsfServiceEventHandler public void eventDispatched(ITraceRecordSelectedChangedDMEvent e) { if (e.isVisualizationModeEnabled()) { fTraceVisualization = true; } else { fTraceVisualization = false; } } /** * @nooverride This method is not intended to be re-implemented or extended by clients. * @noreference This method is not intended to be referenced by clients. */ @DsfServiceEventHandler public void eventDispatched(IExitedDMEvent e) { IDMContext ctx = e.getDMContext(); if (ctx instanceof IMIExecutionDMContext) { // When a thread exits, clear the alias structure for that // thread to avoid leaks fReturnValueAliases.clearThread((IMIExecutionDMContext)ctx); } } /** * {@inheritDoc} * @since 1.1 */ @Override public void flushCache(IDMContext context) { fExpressionCache.reset(context); // We must also mark all variable objects as out-of-date // to refresh them as well varManager.markAllOutOfDate(); } /** * A casted or array-displayed expression. * @since 3.0 */ protected class CastedExpressionDMC extends MIExpressionDMC implements ICastedExpressionDMContext { private final CastInfo fCastInfo; public CastedExpressionDMC(MIExpressionDMC exprDMC, String castExpression, CastInfo castInfo) { super(getSession().getId(), castExpression, exprDMC.getRelativeExpression(), exprDMC); fCastInfo = castInfo; } /* (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext#getCastInfo() */ @Override public CastInfo getCastInfo() { return fCastInfo; } /** * @return True if the two objects are equal, false otherwise. */ @Override public boolean equals(Object other) { return super.equals(other) && fCastInfo.equals(((CastedExpressionDMC) other).fCastInfo); } } /* (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.service.IExpressions2#createCastedExpression(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext) */ /** @since 3.0 */ @Override public ICastedExpressionDMContext createCastedExpression(IExpressionDMContext exprDMC, CastInfo castInfo) { if (exprDMC instanceof MIExpressionDMC && castInfo != null) { String castType = castInfo.getTypeString(); String castExpression = exprDMC.getExpression(); int castingLength = castInfo.getArrayCount(); int castingIndex = castInfo.getArrayStartIndex(); // cast to type if (castType != null && !castType.isEmpty()) { StringBuilder buffer = new StringBuilder(); buffer.append('(').append(castType).append(')'); buffer.append('(').append(castExpression).append(')'); castExpression = buffer.toString(); } // cast to array (can be in addition to cast to type) if (castingLength > 0) { StringBuilder buffer = new StringBuilder(); buffer.append("*("); //$NON-NLS-1$ buffer.append('(').append(castExpression).append(')'); buffer.append('+').append(castingIndex).append(')'); buffer.append('@').append(castingLength); castExpression = buffer.toString(); } // Surround the entire casted expression with parenthesis in case we are // dealing with an array. Arrays must be parenthesized before they are // subscripted. Note that we can be casting to an array or displaying // as an array, so we must do this all the time. castExpression = String.format("(%s)", castExpression); //$NON-NLS-1$ return new CastedExpressionDMC((MIExpressionDMC) exprDMC, castExpression, castInfo); } else { assert false; return null; } } /* (non-Javadoc) * @see org.eclipse.cdt.dsf.debug.service.IExpressions3#getExpressionDataExtension(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) */ /** @since 4.0 */ @Override public void getExpressionDataExtension(IExpressionDMContext dmc, final DataRequestMonitor<IExpressionDMDataExtension> rm) { getExpressionData(dmc, new DataRequestMonitor<IExpressionDMData>(getExecutor(), rm) { @Override protected void handleSuccess() { rm.setData((IExpressionDMDataExtension)getData()); super.handleSuccess(); } }); } private IndexedPartitionDMC[] getTopLevelIndexedPartitions( MIExpressionDMC exprCtx, int realNumChildren, int startIndex, int length) { int numChildren = computeNumberOfChildren(realNumChildren); if (startIndex >= numChildren) return new IndexedPartitionDMC[0]; int startIndex1 = (startIndex < 0) ? 0 : startIndex; int length1 = (length < 0) ? numChildren - startIndex1 : Math.min(length, numChildren - startIndex1); IndexedPartitionDMC[] children = new IndexedPartitionDMC[numChildren]; int index = 0; // If the parent array is a casted expression it could have a different // start index. We want the partition to start at the right index, not always 0 // if (exprCtx instanceof ICastedExpressionDMContext) { // index = ((ICastedExpressionDMContext)exprCtx).getCastInfo().getArrayStartIndex(); // } for(int i = 0; i < children.length; ++i) { int partLength = computePartitionLength(realNumChildren, i); children[i] = createIndexedPartition( exprCtx, index, partLength); index += partLength; } return Arrays.copyOfRange(children, startIndex1, startIndex1 + length1 ); } private void getIndexedPartitionChildren( final IndexedPartitionDMC partDmc, final int startIndex, final int length, final DataRequestMonitor<IExpressionDMContext[]> rm) { final int startIndex1 = (startIndex < 0) ? 0 : startIndex; final int length1 = (length < 0) ? Integer.MAX_VALUE : length; final int partStartIndex = partDmc.getIndex(); final int partLength = partDmc.getLength(); if (partLength > getArrayPartitionLength()) { // create subpartitions int numChildren = computeNumberOfChildren(partLength); if (startIndex1 >= numChildren) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, "Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$ rm.done(); return; } int numPart = Math.min(numChildren, length1); IndexedPartitionDMC[] children = new IndexedPartitionDMC[numPart]; int index = partStartIndex; for (int i = 0; i < startIndex1; ++i) index += computePartitionLength(partLength, i); for (int i = 0; i < children.length; ++i) { int childPartLength = computePartitionLength(partLength, i + startIndex1); children[i] = createIndexedPartition( partDmc.getParentExpressionContext(), index, childPartLength); index += childPartLength; } rm.setData(children); rm.done(); } else { // this is the last partition level, create "real" children if (startIndex1 > partLength) { rm.setData(new IExpressionDMContext[0]); rm.done(); } else { getRealSubExpressions( partDmc.getParentExpressionContext(), partStartIndex + startIndex1, Math.min(length1, partLength - startIndex1), rm); } } } void getRealSubExpressions( final IExpressionDMContext exprCtx, int startIndex, int length, final DataRequestMonitor<IExpressionDMContext[]> rm) { ExprMetaGetChildren getChildren = (startIndex < 0 || length < 0) ? new ExprMetaGetChildren(exprCtx) : new ExprMetaGetChildren(exprCtx, startIndex + length); final int startIndex1 = (startIndex < 0) ? 0 : startIndex; final int length1 = (length < 0) ? Integer.MAX_VALUE : length; fExpressionCache.execute( getChildren, new DataRequestMonitor<ExprMetaGetChildrenInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { ExpressionInfo[] childrenExpr = getData().getChildrenExpressions(); if (startIndex1 >= childrenExpr.length) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED, "Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$ rm.done(); return; } int numChildren = childrenExpr.length - startIndex1; numChildren = Math.min(length1, numChildren); IExpressionDMContext[] childrenArray = new IExpressionDMContext[numChildren]; for (int i=0; i < numChildren; i++) { childrenArray[i] = createExpression(exprCtx.getParents()[0], childrenExpr[startIndex1 + i]); } rm.setData(childrenArray); rm.done(); } }); } /** * Returns the number of "real" children if it is less or equal to the partition size, * otherwise returns the number of partitions. */ private int computeNumberOfChildren(int realNumberOfChildren) { int childNum = realNumberOfChildren; int partLength = getArrayPartitionLength(); int maxPartitionLength = 1; while (childNum > partLength) { childNum /= partLength; maxPartitionLength *= partLength; } if (childNum*maxPartitionLength < realNumberOfChildren) ++childNum; return childNum; } private int computePartitionLength(int realNumberOfChildren, int index) { int childNum = realNumberOfChildren; int depth = 0; int partLength = getArrayPartitionLength(); int length = partLength; while (childNum > partLength) { childNum /= partLength; if (depth > 0) length *= partLength; ++depth; } int diff = realNumberOfChildren - length*index; return ( diff > length ) ? length : diff ; } private IndexedPartitionDMC createIndexedPartition(MIExpressionDMC parentExpr, int index, int length) { return new IndexedPartitionDMC(parentExpr, index, length); } private void getRealSubExpressionCount(IExpressionDMContext dmc, int numChildLimit, final DataRequestMonitor<Integer> rm) { if (dmc instanceof MIExpressionDMC) { fExpressionCache.execute( new ExprMetaGetChildCount(dmc, numChildLimit), new DataRequestMonitor<ExprMetaGetChildCountInfo>(getExecutor(), rm) { @Override protected void handleSuccess() { rm.setData(getData().getChildNum()); rm.done(); } }); } else if (dmc instanceof InvalidContextExpressionDMC) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid context for evaluating expressions.", null)); //$NON-NLS-1$ rm.done(); } else { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", null)); //$NON-NLS-1$ rm.done(); } } private int getArrayPartitionLength() { // Replace this in case we or the platform decide to add a user preference. // See org.eclipse.debug.internal.ui.model.elements.VariableContentProvider. return PARTITION_LENGTH; } }