--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="lib" path="lib/truezip-6.jar"/>
+ <classpathentry kind="lib" path="lib/org.eclipse.cdt.debug.core_7.1.0.201109151620.jar"/>
+ <classpathentry kind="lib" path="lib/org.eclipse.cdt.launch_7.0.0.201109151620.jar"/>
+ <classpathentry kind="lib" path="lib/org.eclipse.cdt.scripting_1.0.0.201109151658.jar"/>
+ <classpathentry kind="lib" path="lib/org.eclipse.cdt.debug.edc.tcf.extension_2.0.0.201109151658.jar"/>
+ <classpathentry kind="lib" path="lib/org.eclipse.tm.tcf.core_0.4.1.201109151255.jar"/>
+ <classpathentry kind="lib" path="lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.cdt.debug.edc</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+#Fri Sep 19 19:14:41 KST 2014
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Edc
+Bundle-SymbolicName: org.eclipse.cdt.debug.edc
+Bundle-Version: 1.0.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Require-Bundle: org.eclipse.core.jobs;bundle-version="3.5.100",
+ org.eclipse.equinox.common;bundle-version="3.6.0",
+ org.eclipse.core.resources;bundle-version="3.7.100",
+ org.eclipse.equinox.preferences;bundle-version="3.4.1",
+ org.eclipse.osgi;bundle-version="3.7.1",
+ org.eclipse.debug.core;bundle-version="3.7.0",
+ org.eclipse.core.runtime;bundle-version="3.7.0",
+ org.apache.commons.codec;bundle-version="1.3.0",
+ org.eclipse.core.variables;bundle-version="3.2.500",
+ org.eclipse.cdt.core;bundle-version="5.3.2"
+Export-Package: org.eclipse.cdt.debug.edc.internal.symbols,
+ org.eclipse.cdt.debug.edc.internal.symbols.dwarf,
+ org.eclipse.cdt.debug.edc.internal.symbols.files,
+ org.eclipse.cdt.debug.edc.symbols
+Bundle-ClassPath: .,
+ lib/org.eclipse.cdt.debug.core_7.1.0.201109151620.jar,
+ lib/org.eclipse.cdt.debug.edc.tcf.extension_2.0.0.201109151658.jar,
+ lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar,
+ lib/org.eclipse.cdt.launch_7.0.0.201109151620.jar,
+ lib/org.eclipse.cdt.scripting_1.0.0.201109151658.jar,
+ lib/org.eclipse.tm.tcf.core_0.4.1.201109151255.jar,
+ lib/truezip-6.jar
--- /dev/null
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ lib/org.eclipse.cdt.debug.core_7.1.0.201109151620.jar,\
+ lib/org.eclipse.cdt.debug.edc.tcf.extension_2.0.0.201109151658.jar,\
+ lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar,\
+ lib/org.eclipse.cdt.launch_7.0.0.201109151620.jar,\
+ lib/org.eclipse.cdt.scripting_1.0.0.201109151658.jar,\
+ lib/org.eclipse.tm.tcf.core_0.4.1.201109151255.jar,\
+ lib/truezip-6.jar
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Expression evaluator that computes jump-to address for a control-change
+ * instruction such as jump and call instruction. To evaluate an address
+ * expression, accessing registers and/or memory is required. And DSF services
+ * are invoked for those access.<br>
+ * <br>
+ * As the address expression is usually produced by disassembler, this evaluator
+ * implementation should stay in sync with corresponding disassembler
+ * implementation.
+ */
+public interface IAddressExpressionEvaluator {
+
+ /**
+ * Evaluate a expression synchronously.<br>
+ * <br>
+ * This method should be called only when control is at the instruction. As
+ * DSF services will be called, this method should also be called in DSF
+ * dispatch thread.
+ *
+ * @param context
+ * Execution DMC.
+ * @param expression
+ * the address expression from a control-change instruction. This
+ * expression is usually produced by disassembler.
+ * @param regService
+ * EDC version of DSF IRegisters service for register access.
+ * @param memService
+ * EDC version of DSF IMemory service for memory access.
+ * @return address computed.
+ * @throws CoreException
+ * on any error.
+ */
+ IAddress evaluate(IExecutionDMContext context, String expression, IRegisters regService, IMemory memService)
+ throws CoreException;
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc;
+
+public interface IEDCConstants {
+
+ /**
+ * This is the TCF peer attribute used to differentiate between available
+ * TCF peers distributed with stock CDT (e.g., windows, linux-x86).
+ */
+ final static String PEER_ATTR_DEBUG_SUPPORT = "DebugSupport";
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc;
+
+public interface IJumpToAddress {
+
+ /**
+ * Whether this address is the sole destination address after executing the
+ * current instruction. E.g. it's true for unconditional jump and subroutine
+ * call, but false for condition jump.
+ *
+ * @return
+ */
+ public boolean isSoleDestination();
+
+ /**
+ * Whether the jump-to address is a subroutine address (namely whether the
+ * current instruction is a subroutine call instruction.
+ *
+ * @return
+ */
+ public boolean isSubroutineAddress();
+
+ /**
+ * Is the address an immediate value (no calculation is needed) ?
+ *
+ * @return
+ */
+ public boolean isImmediate();
+
+ /**
+ * Get the address.
+ *
+ * @return IAddress object for immediate address, or an expression string
+ * indicating how to calculate the actual address.
+ */
+ public Object getValue();
+
+}
\ No newline at end of file
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc;
+
+import java.nio.Buffer;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * This is an subset of the ByteBuffer interface used for read-only
+ * scanning of a buffer. Its implementation is optimized for streaming access
+ * (i.e. not random access) and for making cheap sub-buffers.
+ */
+public interface IStreamBuffer {
+ /**
+ * @see ByteBuffer#capacity()
+ */
+ public long capacity();
+
+ /**
+ * @see ByteBuffer#hasRemaining()
+ */
+ public boolean hasRemaining();
+
+ /**
+ * @see Buffer#remaining()
+ */
+ public long remaining();
+
+ /**
+ * @see Buffer#position()
+ */
+ public long position();
+
+ /**
+ * @see Buffer#position(int)
+ */
+ public IStreamBuffer position(long newPosition);
+
+ /**
+ * Skip ahead in the buffer.
+ *
+ * @param amount the number of bytes to skip ahead
+ * @return this buffer
+ * @since 2.0
+ */
+ public IStreamBuffer skip(long amount);
+
+ /**
+ * @see ByteBuffer#get()
+ * @throws BufferUnderflowException
+ */
+ public abstract byte get();
+
+ /**
+ * @see ByteBuffer#get(byte[], int, int)
+ * @throws BufferUnderflowException
+ */
+ public IStreamBuffer get(byte[] dst, int offset, int length);
+
+ /**
+ * @see ByteBuffer#get(byte[])
+ * @throws BufferUnderflowException
+ */
+ public IStreamBuffer get(byte[] dst);
+
+ /**
+ * @see ByteBuffer#getChar()
+ * @throws BufferUnderflowException
+ */
+ public abstract char getChar();
+
+ /**
+ * @see ByteBuffer#getShort()
+ * @throws BufferUnderflowException
+ */
+ public abstract short getShort();
+
+
+ /**
+ * @see ByteBuffer#getInt()
+ * @throws BufferUnderflowException
+ */
+ public abstract int getInt();
+
+ /**
+ * @see ByteBuffer#getLong()
+ * @throws BufferUnderflowException
+ */
+ public abstract long getLong();
+
+ /**
+ * Wrap a portion of the buffer. This is a cheap operation whose
+ * returned buffer maintains references to the receiver but has an
+ * independent position.
+ * @param size the size to wrap, starting from the current {@link #position()}
+ */
+ IStreamBuffer wrapSubsection(long size);
+
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.tm.tcf.protocol.IPeer;
+
+/**
+ * An implementation of this interface is provided by a tcgAgentLauncher
+ * extension. It's a way to advertise a TCF agent that may not yet be running
+ * and to provide a means to programatically launch it (or at least put up some
+ * GUI that informs the user how to manually launch it, in the case of an agent
+ * hosted remotely). Agents can advertise themselves once they are running via
+ * TCF UDP Discovery, but we need a way for a debugger to discover and launch
+ * agents that are not yet running. This interface assumes the agent hosts a
+ * single peer. An agent that hosts multiple peers can be described using
+ * multiple launchers with common launch logic.
+ */
+public interface ITCFAgentLauncher {
+
+ /**
+ * Gets the user friendly name of the peer this agent hosts. Same as calling
+ * getAttributes().get(IPeer#ATTR_NAME)
+ *
+ * @return the name of the peer
+ */
+ String getPeerName();
+
+ /**
+ * Get the names of the services the peer implements.
+ *
+ * @return list of service names, may be empty
+ */
+ List<String> getServiceNames();
+
+ /**
+ * Get the attributes of the peer this agent hosts.
+ *
+ * @return the peer's attributes; at least the {@link IPeer#ATTR_NAME} will
+ * be present
+ */
+ Map<String, String> getPeerAttributes();
+
+ /**
+ * Tell whether the agent can be launched. This is mainly used to
+ * avoid considering the launcher for situations where it will never
+ * work (e.g., wrong OS host). {@link #launch()} can, of course,
+ * fail for other reasons.
+ * @return true if launching is possible.
+ */
+ boolean isLaunchable();
+
+ /**
+ * Launches the agent, if it's not already running
+ *
+ * @throws Exception
+ * on any error.
+ */
+ void launch() throws Exception;
+
+
+ /**
+ * Shuts down the agent if it was launched
+ *
+ * @throws Exception
+ * on any error
+ */
+ void shutdown() throws Exception;
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc;
+
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IPeer;
+
+/**
+ * This listener on {@link ITCFServiceManager} allows a client to track the
+ * state of peers and channels managed by EDC.
+ * @since 2.0
+ */
+public interface ITCFConnectionListener {
+
+ /**
+ * Called when ITCFServiceManager opens a channel on a peer.
+ * This is called on the TCF dispatch thread.
+ * */
+ void peerChannelOpened(IPeer peer, IChannel channel);
+
+ /**
+ * Called when a channel was closed for a peer.
+ * This is called on the TCF dispatch thread.
+ */
+ void peerChannelClosed(IPeer peer, IChannel channel, Throwable error);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IPeer;
+
+/**
+ * This interface provides access to TCF services. It abstracts out the details
+ * of which agent provides the services, launching the agent if necessary, etc.
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITCFServiceManager {
+
+ /**
+ * Gets the channel used to talk to a peer. If no channel is open yet it
+ * will return null.
+ *
+ * @param peer
+ * the peer
+ * @return the channel used to communicate to the specified peer
+ */
+
+ public IChannel getChannelForPeer(IPeer peer);
+
+ /**
+ * Find an open channel or create a new one and return it. This blocks,
+ * and should not be called from the TCF dispatch thread.
+ * @param peer the peer
+ * @return the channel used to communicate to the specified peer, in open state
+ * @since 2.0
+ */
+ public IChannel findOrCreateChannelForPeer(IPeer peer) throws CoreException;
+
+ /**
+ * Add a listener. Duplicate listeners are ignored.
+ * @param listener
+ * @since 2.0
+ */
+ public void addConnectionListener(ITCFConnectionListener listener);
+ /**
+ * Remove a listener. Nonexistent listeners are ignored.
+ * @param listener
+ * @since 2.0
+ */
+ public void removeConnectionListener(ITCFConnectionListener listener);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.utils.Addr64;
+
+/**
+ * Representing the address to which control-change instruction (e.g. a jump
+ * instruction or subroutine call instruction) may jump to. <br>
+ * Contrast to: the address of the instruction right after this instruction in
+ * memory.<br>
+ * <br>
+ * Object of such class is supposed to be produced by disassembler and consumed
+ * by debugger for tasks such as stepping and stack crawling.<br>
+ * <br>
+ * For some jump or call instructions, the jump-to-address is an absolute
+ * immediate address encoded in the instruction, while for some instructions it
+ * is an indirect address whose value has to be computed by reading certain
+ * registers or memory when control is at the instruction. The computation
+ * detail is target processor dependent.
+ */
+public class JumpToAddress implements IJumpToAddress {
+ public static final String EXPRESSION_RETURN_NEAR = "ret-near";
+ public static final String EXPRESSION_RETURN_FAR = "ret-far";
+ /**
+ * @since 2.0
+ */
+ public static final String EXPRESSION_LR = "lr";
+
+ private final IAddress address;
+ private final String expression;
+ private final boolean isSoleDestination;
+ private final boolean isSubroutineAddress;
+
+ public JumpToAddress(IAddress address, boolean isSoleDestination, boolean isSubroutineAddress) {
+ this.address = address;
+ this.expression = null;
+ this.isSoleDestination = isSoleDestination;
+ this.isSubroutineAddress = isSubroutineAddress;
+ }
+
+ public JumpToAddress(long address, boolean isSoleDestination, boolean isSubroutineAddress) {
+ this(new Addr64(Long.toString(address)), isSoleDestination, isSubroutineAddress);
+ }
+
+ public JumpToAddress(String expression, boolean isSoleDestination, boolean isSubroutineAddress) {
+ this.address = null;
+ this.expression = expression;
+ this.isSoleDestination = isSoleDestination;
+ this.isSubroutineAddress = isSubroutineAddress;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IJumpToAddress#isSoleDestination()
+ */
+ public boolean isSoleDestination() {
+ return isSoleDestination;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IJumpToAddress#isSubroutineAddress()
+ */
+ public boolean isSubroutineAddress() {
+ return isSubroutineAddress;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IJumpToAddress#isImmediate()
+ */
+ public boolean isImmediate() {
+ return address != null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IJumpToAddress#getValue()
+ */
+ public Object getValue() {
+ if (address != null)
+ return address;
+ else {
+ assert expression != null;
+ return expression;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((address == null) ? 0 : address.hashCode());
+ result = prime * result + ((expression == null) ? 0 : expression.hashCode());
+ result = prime * result + (isSoleDestination ? 1231 : 1237);
+ result = prime * result + (isSubroutineAddress ? 1231 : 1237);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ JumpToAddress other = (JumpToAddress) obj;
+ if (address == null) {
+ if (other.address != null)
+ return false;
+ } else if (!address.equals(other.address))
+ return false;
+ if (expression == null) {
+ if (other.expression != null)
+ return false;
+ } else if (!expression.equals(other.expression))
+ return false;
+ if (isSoleDestination != other.isSoleDestination)
+ return false;
+ if (isSubroutineAddress != other.isSubroutineAddress)
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "JumpToAddress [address="
+ + ((address != null) ? address.toHexAddressString() : "null")
+ + ", expression=" + expression
+ + ", isSoleDestination=" + isSoleDestination
+ + ", isSubroutineAddress=" + isSubroutineAddress + "]";
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.symbols.IBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.model.MemoryByte;
+
+public class MemoryUtils {
+
+ public static final int LITTLE_ENDIAN = 0;
+ public static final int BIG_ENDIAN = 1;
+ public static final int ENDIANESS_UNKNOWN = 2;
+
+ /**
+ * Pad byte array with 0's or 1's when the byte array's length is shorter
+ * than what's expected by the conversion functions.
+ *
+ * @param array
+ * @param size
+ * @param endianess
+ * @param isSigned
+ * @return an array of bytes
+ */
+ protected static byte[] fillArray(byte[] array, int size, int endianess, boolean isSigned) {
+
+ byte[] temp = new byte[size];
+
+ // either 0 fill or sign extend
+ byte fillByte = 0;
+ if (isSigned && ((array[array.length - 1] & 0x80) != 0))
+ fillByte = (byte) 0xff;
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+
+ for (int i = 0; i < array.length; i++) {
+ temp[i] = array[i];
+ }
+
+ // fill up the rest of the array
+ for (int i = array.length; i < size; i++) {
+ temp[i] = fillByte;
+ }
+
+ array = temp;
+ return array;
+ }
+
+ for (int i = 0; i < size - array.length; i++) {
+ temp[i] = fillByte;
+ }
+
+ int j = 0;
+ // fill up the rest of the array
+ for (int i = size - array.length; i < size; i++) {
+ temp[i] = array[j];
+ j++;
+ }
+
+ array = temp;
+ return array;
+ }
+
+ /**
+ * Convert byte array to unsigned long.
+ *
+ * @param data
+ * @param endianess
+ * @return result of the conversion in long
+ */
+ static public BigInteger convertByteArrayToUnsignedLong(MemoryByte[] data, int endianess) {
+ byte[] array = new byte[data.length];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = data[i].getValue();
+ }
+
+ if (array.length < 8) {
+ array = fillArray(array, 8, endianess, false);
+ }
+
+ BigInteger value = new BigInteger("0"); //$NON-NLS-1$
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int i = 0; i < 8; i++) {
+ byte[] temp = new byte[1];
+ temp[0] = array[i];
+ BigInteger b = new BigInteger(temp);
+ b = b.and(new BigInteger("ff", 16)); //$NON-NLS-1$
+ b = b.shiftLeft(i * 8);
+ value = value.or(b);
+ }
+ } else {
+ for (int i = 0; i < 8; i++) {
+ byte[] temp = new byte[1];
+ temp[0] = array[i];
+ BigInteger b = new BigInteger(temp);
+ b = b.and(new BigInteger("ff", 16)); //$NON-NLS-1$
+ b = b.shiftLeft((7 - i) * 8);
+ value = value.or(b);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Convert byte array to signed long.
+ *
+ * @param data
+ * @param endianess
+ * @return result of the conversion in long
+ */
+ static public long convertByteArrayToLong(MemoryByte[] data, int endianess) {
+ byte[] array = new byte[data.length];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = data[i].getValue();
+ }
+
+ if (array.length < 8) {
+ array = fillArray(array, 8, endianess, true);
+ }
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ long value = 0;
+ for (int i = 0; i < 8; i++) {
+ long b = array[i];
+ b &= 0xff;
+ value |= (b << (i * 8));
+ }
+ return value;
+ }
+ long value = 0;
+ for (int i = 0; i < 8; i++) {
+ long b = array[i];
+ b &= 0xff;
+ value |= (b << ((7 - i) * 8));
+ }
+
+ return value;
+ }
+
+ static public BigInteger convertByteArrayToSignedBigInt(byte[] array, int endianess) {
+ if (array.length < 16) {
+ array = fillArray(array, 16, endianess, false);
+ }
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ // reverse bytes
+ byte[] holder = new byte[16];
+ int j = 15;
+ for (int i = 0; i < 16; i++, j--) {
+ holder[i] = array[j];
+ }
+
+ // create BigInteger
+ BigInteger value = new BigInteger(holder);
+ return value;
+ }
+ BigInteger value = new BigInteger(array);
+ return value;
+ }
+
+ static public BigInteger convertByteArrayToSignedBigInt(byte[] array, int endianess, int arraySize) {
+ if (array.length < arraySize) {
+ array = fillArray(array, arraySize, endianess, true);
+ }
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ // reverse bytes
+ byte[] holder = new byte[arraySize];
+ int j = arraySize - 1;
+ for (int i = 0; i < arraySize; i++, j--) {
+ holder[i] = array[j];
+ }
+
+ // create BigInteger
+ BigInteger value = new BigInteger(holder);
+ return value;
+ }
+ BigInteger value = new BigInteger(array);
+ return value;
+ }
+
+ static public BigInteger convertByteArrayToUnsignedBigInt(byte[] array, int endianess) {
+ if (array.length < 16) {
+ array = fillArray(array, 16, endianess, false);
+ }
+
+ BigInteger value = new BigInteger("0"); //$NON-NLS-1$
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int i = 0; i < 16; i++) {
+ byte[] temp = new byte[1];
+ temp[0] = array[i];
+ BigInteger b = new BigInteger(temp);
+ b = b.and(new BigInteger("ff", 16)); //$NON-NLS-1$
+ b = b.shiftLeft(i * 8);
+ value = value.or(b);
+ }
+ } else {
+ for (int i = 0; i < 16; i++) {
+ byte[] temp = new byte[1];
+ temp[0] = array[i];
+ BigInteger b = new BigInteger(temp);
+ b = b.and(new BigInteger("ff", 16)); //$NON-NLS-1$
+ b = b.shiftLeft((15 - i) * 8);
+ value = value.or(b);
+ }
+ }
+ return value;
+ }
+
+ static public BigInteger convertByteArrayToUnsignedBigInt(byte[] array, int endianess, int arraySize) {
+ if (array.length < arraySize) {
+ array = fillArray(array, arraySize, endianess, false);
+ }
+
+ BigInteger value = new BigInteger("0"); //$NON-NLS-1$
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int i = 0; i < arraySize; i++) {
+ byte[] temp = new byte[1];
+ temp[0] = array[i];
+ BigInteger b = new BigInteger(temp);
+ b = b.and(new BigInteger("ff", 16)); //$NON-NLS-1$
+ b = b.shiftLeft(i * 8);
+ value = value.or(b);
+ }
+ } else {
+ for (int i = 0; i < arraySize; i++) {
+ byte[] temp = new byte[1];
+ temp[0] = array[i];
+ BigInteger b = new BigInteger(temp);
+ b = b.and(new BigInteger("ff", 16)); //$NON-NLS-1$
+ b = b.shiftLeft((arraySize - 1 - i) * 8);
+ value = value.or(b);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Convert byte array to integer.
+ *
+ * @param array
+ * @param endianess
+ * @return result of the conversion in int
+ */
+ static public int convertByteArrayToInt(MemoryByte[] data, int endianess) {
+ byte[] array = new byte[data.length];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = data[i].getValue();
+ }
+
+ if (array.length < 4) {
+ array = fillArray(array, 4, endianess, true);
+ }
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ int value = 0;
+ for (int i = 0; i < 4; i++) {
+ int b = array[i];
+ b &= 0xff;
+ value |= (b << (i * 8));
+ }
+ return value;
+ }
+ int value = 0;
+ for (int i = 0; i < 4; i++) {
+ int b = array[i];
+ b &= 0xff;
+ value |= (b << ((3 - i) * 8));
+ }
+
+ return value;
+ }
+
+ /**
+ * Convert byte array to short.
+ *
+ * @param array
+ * @param endianess
+ * @return result of teh conversion in short
+ */
+ static public short convertByteArrayToShort(MemoryByte[] data, int endianess) {
+ byte[] array = new byte[data.length];
+ for (int i = 0; i < array.length; i++) {
+ array[i] = data[i].getValue();
+ }
+ if (array.length < 2) {
+ array = fillArray(array, 2, endianess, true);
+ }
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ short value = 0;
+ for (int i = 0; i < 2; i++) {
+ short b = array[i];
+ b &= 0xff;
+ value |= (b << (i * 8));
+ }
+ return value;
+ }
+ short value = 0;
+ for (int i = 0; i < 2; i++) {
+ short b = array[i];
+ b &= 0xff;
+ value |= (b << ((1 - i) * 8));
+ }
+ return value;
+ }
+
+ /**
+ * Convert big integer to byte array.
+ *
+ * @param i
+ * @param endianess
+ * @return result of the conversion in raw byte array
+ */
+ static public byte[] convertBigIntegerToByteArray(BigInteger i, int endianess) {
+ byte buf[] = new byte[16];
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int j = 0; j < 16; j++) {
+ BigInteger x = i.shiftRight(j * 8);
+ buf[j] = x.byteValue();
+ }
+ return buf;
+ }
+ for (int j = 15; j >= 0; j--) {
+ BigInteger x = i.shiftRight((15 - j) * 8);
+ buf[j] = x.byteValue();
+ }
+ return buf;
+ }
+
+ static public byte[] convertSignedBigIntToByteArray(BigInteger i, int endianess, int arraySize) {
+ byte buf[] = new byte[arraySize];
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int j = 0; j < arraySize; j++) {
+ BigInteger x = i.shiftRight(j * 8);
+ buf[j] = x.byteValue();
+ }
+ return buf;
+ }
+ for (int j = arraySize - 1; j >= 0; j--) {
+ BigInteger x = i.shiftRight((arraySize - 1 - j) * 8);
+ buf[j] = x.byteValue();
+ }
+ return buf;
+ }
+
+ /**
+ * Convert big integer to byte array.
+ *
+ * @param i
+ * @param endianess
+ * @return result of the conversion in raw byte array
+ */
+ static public byte[] convertUnsignedBigIntegerToByteArray(BigInteger i, int endianess) {
+ byte buf[] = new byte[32];
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int j = 0; j < 32; j++) {
+ BigInteger x = i.shiftRight(j * 8);
+ buf[j] = x.byteValue();
+ }
+ return buf;
+ }
+ for (int j = 31; j >= 0; j--) {
+ BigInteger x = i.shiftRight((31 - j) * 8);
+ buf[j] = x.byteValue();
+ }
+ return buf;
+ }
+
+ static public byte[] convertUnsignedBigIntToByteArray(BigInteger i, int endianess, int arraySize) {
+ byte buf[] = new byte[arraySize * 2];
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int j = 0; j < arraySize * 2; j++) {
+ BigInteger x = i.shiftRight(j * 8);
+ buf[j] = x.byteValue();
+ }
+ return buf;
+ }
+ for (int j = (arraySize * 2) - 1; j >= 0; j--) {
+ BigInteger x = i.shiftRight(((arraySize * 2) - 1 - j) * 8);
+ buf[j] = x.byteValue();
+ }
+ return buf;
+ }
+
+ /**
+ * Convert long to byte array.
+ *
+ * @param i
+ * @param endianess
+ * @return result of the conversion in raw byte array
+ */
+ static public byte[] convertLongToByteArray(long i, int endianess) {
+ byte buf[] = new byte[8];
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int j = 0; j < 8; j++) {
+ buf[j] = new Long(i >> j * 8).byteValue();
+ }
+ return buf;
+ }
+ for (int j = 7; j >= 0; j--) {
+ buf[j] = new Long(i >> (7 - j) * 8).byteValue();
+ }
+ return buf;
+ }
+
+ /**
+ * Convert integer to byte array.
+ *
+ * @param i
+ * @param endianess
+ * @return result of the conversion in raw byte array
+ */
+ static public byte[] convertIntToByteArray(int i, int endianess) {
+ byte buf[] = new byte[4];
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (int j = 0; j < 4; j++) {
+ buf[j] = new Integer(i >> j * 8).byteValue();
+ }
+ return buf;
+ }
+ for (int j = 3; j >= 0; j--) {
+ buf[j] = new Integer(i >> (3 - j) * 8).byteValue();
+ }
+ return buf;
+ }
+
+ /**
+ * Convert short to byte array.
+ *
+ * @param i
+ * @param endianess
+ * @return result of the conversion in raw byte array
+ */
+ static public byte[] convertShortToByteArray(short i, int endianess) {
+ byte buf[] = new byte[2];
+
+ if (endianess == MemoryUtils.LITTLE_ENDIAN) {
+ for (short j = 0; j < 2; j++) {
+ buf[j] = new Integer(i >> j * 8).byteValue();
+ }
+ return buf;
+ }
+ for (short j = 1; j >= 0; j--) {
+ buf[j] = new Integer(i >> (1 - j) * 8).byteValue();
+ }
+ return buf;
+ }
+
+ /**
+ * byte array to Hex string helper replaces the Integer.toHexString() which
+ * can't convert byte values properly (always pads with FFFFFF)
+ */
+ static public String convertByteArrayToHexString(byte[] byteArray) {
+ StringBuffer strBuffer = new StringBuffer();
+ char charArray[];
+
+ for (byte element : byteArray) {
+ charArray = MemoryUtils.convertByteToCharArray(element);
+ strBuffer.append(charArray);
+ }
+
+ return strBuffer.toString();
+ }
+
+ static public char[] convertByteToCharArray(byte aByte) {
+ char charArray[] = new char[2];
+ int val = aByte;
+ if (val < 0)
+ val += 256;
+ charArray[0] = Character.forDigit(val / 16, 16);
+ charArray[1] = Character.forDigit(val % 16, 16);
+
+ return charArray;
+ }
+
+ /**
+ * Convert raw memory data to byte array
+ *
+ * @param str
+ * @param numBytes
+ * @param numCharsPerByte
+ * - number of characters per byte of data
+ * @return an array of byte, converted from a hex string
+ * @throws NumberFormatException
+ */
+ public static byte[] convertHexStringToByteArray(String str, int numBytes, int numCharsPerByte)
+ throws NumberFormatException {
+ if (str.length() == 0)
+ return null;
+
+ StringBuffer buf = new StringBuffer(str);
+
+ // pad string with zeros
+ int requiredPadding = numBytes * numCharsPerByte - str.length();
+ while (requiredPadding > 0) {
+ buf.insert(0, "0"); //$NON-NLS-1$
+ requiredPadding--;
+ }
+
+ byte[] bytes = new byte[numBytes];
+ str = buf.toString();
+
+ // set data in memory
+ for (int i = 0; i < bytes.length; i++) {
+ // convert string to byte
+ String oneByte = str.substring(i * 2, i * 2 + 2);
+
+ Integer number = Integer.valueOf(oneByte, 16);
+ if (number.compareTo(Integer.valueOf(Byte.toString(Byte.MAX_VALUE))) > 0) {
+ int temp = number.intValue();
+ temp = temp - 256;
+
+ String tempStr = Integer.toString(temp);
+
+ Byte myByte = Byte.valueOf(tempStr);
+ bytes[i] = myByte.byteValue();
+ } else {
+ Byte myByte = Byte.valueOf(oneByte, 16);
+ bytes[i] = myByte.byteValue();
+ }
+ }
+
+ return bytes;
+ }
+
+ public static String convertMemoryBytesToHexString(MemoryByte[] bytes) {
+ StringBuffer strBuffer = new StringBuffer();
+ char charArray[];
+
+ for (MemoryByte b : bytes) {
+ charArray = MemoryUtils.convertByteToCharArray(b.getValue());
+ strBuffer.append(charArray);
+ }
+
+ return strBuffer.toString();
+ }
+
+ /**
+ * Convert raw memory data to byte array
+ *
+ * @param str
+ * @param numBytes
+ * @param numCharsPerByte
+ * - number of characters per byte of data
+ * @return an array of byte, converted from a hex string
+ * @throws NumberFormatException
+ */
+ public static MemoryByte[] convertHexStringToMemoryBytes(String str, int numBytes, int numCharsPerByte)
+ throws NumberFormatException {
+ if (str.length() == 0)
+ return null;
+
+ StringBuffer buf = new StringBuffer(str);
+
+ // pad string with zeros
+ int requiredPadding = numBytes * numCharsPerByte - str.length();
+ while (requiredPadding > 0) {
+ buf.insert(0, "0"); //$NON-NLS-1$
+ requiredPadding--;
+ }
+
+ MemoryByte[] bytes = new MemoryByte[numBytes];
+ str = buf.toString();
+
+ // set data in memory
+ for (int i = 0; i < bytes.length; i++) {
+ // convert string to byte
+ String oneByte = str.substring(i * 2, i * 2 + 2);
+
+ Integer number = Integer.valueOf(oneByte, 16);
+ if (number.compareTo(Integer.valueOf(Byte.toString(Byte.MAX_VALUE))) > 0) {
+ int temp = number.intValue();
+ temp = temp - 256;
+
+ String tempStr = Integer.toString(temp);
+
+ Byte myByte = Byte.valueOf(tempStr);
+ bytes[i] = new MemoryByte(myByte.byteValue());
+ } else {
+ Byte myByte = Byte.valueOf(oneByte, 16);
+ bytes[i] = new MemoryByte(myByte.byteValue());
+ }
+ }
+
+ return bytes;
+ }
+
+ public static class TypeCharacteristics {
+ public int basicType = IBasicType.t_unspecified;
+ public boolean isSigned = false;
+ public boolean isShort = false;
+ public boolean isLong = false;
+ public boolean isLongLong = false;
+ public boolean isComplex = false;
+
+ public TypeCharacteristics(IType varType) {
+ if (varType instanceof IBasicType) {
+ IBasicType type = (IBasicType) varType;
+ basicType = type.getBaseType();
+ isSigned = type.isSigned();
+ isShort = type.isShort();
+ isLong = type.isLong();
+
+ if (varType instanceof ICPPBasicType) {
+ ICPPBasicType cppType = (ICPPBasicType) varType;
+ isLongLong = cppType.isLongLong();
+ isComplex = cppType.isComplex();
+ }
+ } else if (varType instanceof IPointerType) {
+ // treat pointer as an unsigned int
+ basicType = IBasicType.t_int;
+ } else if (varType instanceof IEnumerator){
+ // treat enumerator as a signed int
+ basicType = IBasicType.t_int;
+ isSigned = true;
+ } else {
+ // treat unknown type as an unsigned int
+ basicType = IBasicType.t_int;
+ }
+ }
+ }
+
+ public static BigInteger convertValueToMemory(IType varType, Number value) throws CoreException {
+ BigInteger result = null;
+ if (varType == null)
+ throw EDCDebugger.newCoreException("Unknown type");
+ int varSize = varType.getByteSize();
+ if (varSize <= 0)
+ throw EDCDebugger.newCoreException("Type has no size");
+
+ TypeCharacteristics characteristics = new TypeCharacteristics(varType);
+
+ // all other locations
+ switch (characteristics.basicType) {
+ case IBasicType.t_float:
+ case IBasicType.t_double:
+ if (varSize == 4) {
+ result = BigInteger.valueOf(Float.floatToIntBits(value.floatValue()));
+ } else if (varSize == 8) {
+ result = BigInteger.valueOf(Double.doubleToLongBits(value.doubleValue()));
+ }
+ break;
+
+ case ICPPBasicType.t_bool:
+ case ICPPBasicType.t_wchar_t:
+ case IBasicType.t_char:
+ case IBasicType.t_int:
+ case IBasicType.t_void:
+ if (characteristics.isSigned) {
+ // as needed, mask the value and sign-extend
+ if (varSize == 4) {
+ result = BigInteger.valueOf(value.intValue());
+ } else if (varSize == 2) {
+ int intResult = value.intValue() & 0xffff;
+ if ((intResult & 0x00008000) != 0)
+ intResult |= 0xffff0000;
+ result = BigInteger.valueOf(intResult);
+ } else if (varSize == 1) {
+ int intResult = value.intValue() & 0xff;
+ if ((intResult & 0x00000080) != 0)
+ intResult |= 0xffffff00;
+ result = BigInteger.valueOf(intResult);
+ } else {
+ // assume an 8-byte long is the default
+ result = BigInteger.valueOf(value.longValue());
+ }
+ } else {
+ if (varSize == 4) {
+ result = BigInteger.valueOf(value.longValue() & 0xffffffffL); // keep it unsigned
+ } else if (varSize == 2) {
+ result = BigInteger.valueOf(value.intValue() & 0xffff);
+ } else if (varSize == 1) {
+ result = BigInteger.valueOf(value.intValue() & 0xff);
+ } else {
+ // assume an 8-byte long is the default
+ result = BigInteger.valueOf(value.longValue());
+ }
+ }
+ break;
+
+ case IBasicType.t_unspecified:
+ default:
+ assert false;
+ throw EDCDebugger.newCoreException("Unhandled type");
+ }
+ return result;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * This class performs error logging. The error can be logged, shown or both. It
+ * is important to log errors to clearly identify the plug-in as the source of
+ * problems.
+ * <p>
+ * The goals of the logging and the showing are different:
+ * <p>
+ * A logged message should retain enough information so it's not useless, while
+ * not having empty top-level messages or unnecessary levels of nesting.
+ * <p>
+ * A reported message, which is meant to be more user-friendly, does not show a
+ * stack trace or exception names, so it should not expose the StatusDialog's
+ * weaknesses of reporting empty messages, empty details, or details which are
+ * the same as the main message.
+ */
+public abstract class MessageLogger {
+
+ public static final String MISSING_MESSAGE_PLACEHOLDER = "Internal error";
+ public static final String MULTIPLE_MESSAGE_PLACEHOLDER = "Multiple problems have occurred";
+
+ public interface Listener {
+ void statusLogged(IStatus status);
+ }
+
+ private static List<Listener> listeners = new ArrayList<Listener>();
+
+ public MessageLogger() {
+ }
+
+ /**
+ * Add listener to logged errors for all instances of ErrorLogger.
+ *
+ * @param listener
+ * listener, duplicates ignored
+ */
+ public static synchronized void addListener(Listener listener) {
+ if (!listeners.contains(listener))
+ listeners.add(listener);
+ }
+
+ /**
+ * Remove listener for logged errors for all instances of ErrorLogger.
+ *
+ * @param listener
+ * listener,missing ignored
+ */
+ public static synchronized void removeListener(Listener listener) {
+ listeners.remove(listener);
+ }
+
+ protected static void fireListener(IStatus status) {
+ Listener[] array;
+ synchronized (listeners) {
+ array = listeners.toArray(new Listener[listeners.size()]);
+ }
+ for (Listener listener : array) {
+ listener.statusLogged(status);
+ }
+ }
+
+ /**
+ * Log a finalized status message.
+ *
+ * @param status
+ */
+ protected void doLog(IStatus status) {
+ if (getPlugin() != null) {
+ getPlugin().getLog().log(status);
+ } else {
+ System.err.println(status.toString());
+ if (status.getException() != null)
+ status.getException().printStackTrace(System.err);
+ }
+
+ fireListener(status);
+ }
+
+ /**
+ * Create a status with as much information as possible at the top level
+ * (IStatus#getMessage), reading the message from the exception or a nested
+ * exception if possible. Favor a CoreException's IStatus if the mainMessage
+ * is null.
+ *
+ * @param severity
+ * @param mainMessage
+ * @param exception
+ * @return new or existing IStatus
+ * @since 2.0
+ */
+ public IStatus createStatus(int severity, String mainMessage, Throwable exception) {
+ String pluginID = getPluginID();
+ IStatus status;
+
+ // use an available status if possible
+ if (isEmpty(mainMessage) && exception instanceof CoreException) {
+ status = ((CoreException) exception).getStatus();
+ exception = null;
+
+ // the CoreException message is just the status message
+ if (!isEmpty(status.getMessage())) {
+ if (!isRealMultiStatus(status))
+ return status;
+ mainMessage = MULTIPLE_MESSAGE_PLACEHOLDER;
+ }
+
+ if (status.getException() != null && status.getException().getMessage() != null) {
+ // make a new status -- the current one isn't very useful
+ mainMessage = status.getException().getMessage();
+ exception = status.getException();
+ } else {
+ return status;
+ }
+ }
+
+ // peek to see if the exception is a simple wrapper
+ if (isEmpty(mainMessage)) {
+ Throwable base = exception;
+ while (base != null && isEmpty(base.getMessage()))
+ base = base.getCause();
+ if (base != null && !isEmpty(base.getMessage()))
+ mainMessage = base.getMessage();
+ else
+ mainMessage = MISSING_MESSAGE_PLACEHOLDER;
+ }
+
+ status = new Status(severity, pluginID, mainMessage, exception);
+ return status;
+ }
+
+ private static boolean isEmpty(String text) {
+ return text == null || text.length() == 0;
+ }
+
+ /**
+ * Log a status message.
+ *
+ * @param status
+ */
+ public void log(IStatus status) {
+ if (status.getSeverity() == IStatus.CANCEL)
+ return;
+ if (isEmpty(status.getMessage()) && !isRealMultiStatus(status))
+ status = createStatus(status.getSeverity(), null, status.getException());
+ doLog(status);
+ }
+
+ /**
+ * @param status
+ * @return
+ */
+ private boolean isRealMultiStatus(IStatus status) {
+ return status.isMultiStatus() && ((MultiStatus) status).getChildren().length > 0;
+ }
+
+ /**
+ * Log a message
+ *
+ * @param severity
+ * one of {@value IStatus#ERROR} {@value IStatus#WARNING}
+ * {@value IStatus#INFO}
+ * @param mainMessage
+ * the error message (may be <code>null</code> to use the
+ * exception message)
+ * @param exception
+ * the exception caused by the error (may be <code>null</code>)
+ */
+ public void log(int severity, String mainMessage, Throwable exception) {
+ IStatus status = createStatus(severity, mainMessage, exception);
+ log(status);
+ }
+
+ /**
+ * Log an error.
+ *
+ * @param mainMessage
+ * the error message (may be <code>null</code> to use the
+ * exception message)
+ * @param exception
+ * the exception caused by the error (may be <code>null</code>)
+ */
+ public void logError(String mainMessage, Throwable t) {
+ log(IStatus.ERROR, mainMessage, t);
+ }
+
+ /**
+ * Log an exception.
+ *
+ * @param exception
+ * the exception caused by the error (may be <code>null</code>)
+ * @since 2.0
+ */
+ public void logException(Throwable t) {
+ log(IStatus.ERROR, null, t);
+ }
+
+ public abstract Plugin getPlugin();
+
+ public abstract String getPluginID();
+
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.datamodel.IDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+
+/**
+ * Base class for ACPM objects that cache information obtained from
+ * {@link IFormattedValues}
+ *
+ * @param <V>
+ * the specific type of information requested
+ * @since 2.0
+ */
+public abstract class AbstractFormattedValuesRequestCache<V> extends BaseRequestCache<V> {
+
+ /** Constructor */
+ public AbstractFormattedValuesRequestCache(IFormattedValues service, IDMContext ctx) {
+ super(service, ctx);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.acmp.BaseRequestCache#resumedEventHandler(org.eclipse.cdt.dsf.datamodel.IDMEvent)
+ */
+ @DsfServiceEventHandler
+ public void resumedEventHandler(IDMEvent<?> e) {
+ // TODO: for now, we defer to the default behavior of losing confidence
+ // in our data on *any* debug event. We should be more discriminating,
+ // but this will be tricky from here since technically we're dealing
+ // with a generic IFormattedValues object. Could be a register, an
+ // expression, or something else. I don't know that we can cleanly
+ // determine what events will invalidate out data.
+ super.resumedEventHandler(e);
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.datamodel.IDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+
+/**
+ * Base class for ACPM objects that cache information obtained from
+ * {@link IRegisters}
+ *
+ * @param <V>
+ * the specific type of information requested
+ * @since 2.0
+ */
+public abstract class AbstractRegisterRequestCache<V> extends BaseRequestCache<V> {
+
+ /** Constructor */
+ public AbstractRegisterRequestCache(IRegisters service, IDMContext ctx) {
+ super(service, ctx);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.acmp.BaseRequestCache#resumedEventHandler(org.eclipse.cdt.dsf.datamodel.IDMEvent)
+ */
+ @DsfServiceEventHandler
+ public void resumedEventHandler(IDMEvent<?> e) {
+ // By default, don't invalidate our data on any event. Our derivatives
+ // store information *about* the registers, not the contents of
+ // registers. The former typically does not change over the course
+ // of a debug session
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext;
+
+/**
+ * Cached result of {@link IFormattedValues#getAvailableFormats(IFormattedDataDMContext, DataRequestMonitor)}
+ *
+ * @param <V>
+ * the specific type of information requested
+ * @since 2.0
+ */
+public class AvailableFormatsRequestCache extends AbstractFormattedValuesRequestCache<String[]> {
+
+ /** Constructor */
+ public AvailableFormatsRequestCache(IFormattedValues service, IDMContext ctx) {
+ super(service, ctx);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.RequestCache#retrieve(org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ protected void retrieve(final DataRequestMonitor<String[]> rm) {
+ ((IFormattedValues)fService).getAvailableFormats((IFormattedDataDMContext)fCtx, new DataRequestMonitor<String[]>(fService.getExecutor(), rm) {
+ public void handleSuccess() {
+ rm.setData(getData());
+ rm.done();
+ }
+ });
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import java.util.List;
+
+import org.eclipse.cdt.dsf.concurrent.ImmediateInDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.RangeCache;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.datamodel.IDMEvent;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.IDsfService;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Base EDC class for ACPM range-based caches. See {@link RangeCache}.
+ * @since 2.0
+ */
+public abstract class BaseRangeCache<V> extends RangeCache<V> implements ICacheEntry {
+
+ /** See {@link #getService()} */
+ protected final IDsfService fService;
+
+ /** See {@link #getContext()} */
+ protected final IDMContext fCtx;
+
+ /** See {@link #getMetaData()} */
+ private Object fMetaData;
+
+ /**
+ * Constructor
+ *
+ * @param service
+ * The DSF service that will provide the data
+ * @param ctx
+ * The primary context the data is for
+ */
+ public BaseRangeCache(IDsfService service, IDMContext ctx) {
+ super(new ImmediateInDsfExecutor(service.getExecutor()));
+ fService = service;
+ fCtx = ctx;
+ }
+
+ /** Returns the DSF service that provides the data */
+ public IDsfService getService() {
+ return fService;
+ }
+
+ /**
+ * Returns the primary context the data is for. Additional qualifications
+ * may be held by derivative classes
+ */
+ public IDMContext getContext() {
+ return fCtx;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.acmp.ICacheEntry#setMetaData(java.lang.Object)
+ */
+ public void setMetaData(Object data) {
+ fMetaData = data;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.acmp.ICacheEntry#getMetaData()
+ */
+ public Object getMetaData() {
+ return fMetaData;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.AbstractCache#reset()
+ */
+ protected void reset() {
+ super.reset();
+
+ // Once we're invalid, there's no point in listening for events; we
+ // don't support going back to the valid state via a notification--too
+ // much work. A fresh asynchronous query is the only way to get back to
+ // the valid state
+ fService.getSession().removeServiceEventListener(this);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.RequestCache#set(java.lang.Object, org.eclipse.core.runtime.IStatus)
+ */
+ protected void set(long offset, int count, List<V> data, IStatus status) {
+ super.set(offset, count, data, status);
+
+ // Now that we are valid, listen for debug events to find out when we
+ // need to move ourselves to the invalid state
+ fService.getSession().addServiceEventListener(this, null);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.acmp.BaseRequestCache#dispose()
+ */
+ public void dispose() {
+ reset();
+ }
+
+ @DsfServiceEventHandler
+ public void resumedEventHandler(IDMEvent<?> e) {
+ // We are extremely sensitive. *Any* debug event will cause us to lose
+ // confidence in our data. We should be more discriminating and react to
+ // specific events that we know may affect the data. E.g., there is no
+ // need to invalidate ourselves if a breakpoint creation event occurs.
+ reset();
+ }
+
+ public RequestMonitor getRequestMonitor() {
+ return null; // TODO: how to handle this???
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import org.eclipse.cdt.dsf.concurrent.ImmediateInDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestCache;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.datamodel.IDMEvent;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.IDsfService;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * Base EDC class for ACPM caches. See {@link RequestCache}
+ * @since 2.0
+ */
+public abstract class BaseRequestCache<V> extends RequestCache<V> implements ICacheEntry {
+
+ /** See {@link #getService()} */
+ protected final IDsfService fService;
+
+ /** See {@link #getContext()} */
+ protected final IDMContext fCtx;
+
+ /** See {@link #getMetaData()} */
+ private Object fMetaData;
+
+ /**
+ * Constructor
+ *
+ * @param service
+ * The DSF service that will provide the data
+ * @param ctx
+ * The primary context the data is for
+ */
+ public BaseRequestCache(IDsfService service, IDMContext ctx) {
+ super(new ImmediateInDsfExecutor(service.getExecutor()));
+ fService = service;
+ fCtx = ctx;
+ }
+
+ /** Returns the DSF service that provides the data */
+ public IDsfService getService() {
+ return fService;
+ }
+
+ /**
+ * Returns the primary context the data is for. Additional qualifications
+ * may be held by derivative classes
+ */
+ public IDMContext getContext() {
+ return fCtx;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.acmp.ICacheEntry#setMetaData(java.lang.Object)
+ */
+ public void setMetaData(Object data) {
+ fMetaData = data;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.acmp.ICacheEntry#getMetaData()
+ */
+ public Object getMetaData() {
+ return fMetaData;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.acmp.ICacheEntry#dispose()
+ */
+ public void dispose() {
+ if (isValid()) {
+ reset();
+ }
+ }
+
+ @DsfServiceEventHandler
+ public void resumedEventHandler(IDMEvent<?> e) {
+ // The default behavior is to be extremely sensitive. *Any* debug event
+ // will cause us to lose confidence in our data. Subclasses should be
+ // more discriminating and react to specific events that we know may
+ // affect the data. E.g., there is no need to invalidate ourselves if a
+ // breakpoint creation event occurs.
+ if (isValid()) {
+ reset();
+ }
+ }
+
+ protected void set(V data, IStatus status) {
+ super.set(data, status);
+
+ // Now that we are valid, listen for debug events to find out when we
+ // need to move ourselves to the invalid state
+ fService.getSession().addServiceEventListener(this, null);
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.AbstractCache#reset()
+ */
+ protected void reset() {
+ super.reset();
+
+ // Once we're invalid, there's no point in listening for events; we
+ // don't support going back to the valid state via a notification--too
+ // much work. A fresh asynchronous query is the only way to get back to
+ // the valid state
+ fService.getSession().removeServiceEventListener(this);
+ }
+
+ public RequestMonitor getRequestMonitor() {
+ return fRm;
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData;
+
+/**
+ * An ACPM cache for {@link IFormattedValues#getFormattedExpressionValue(FormattedValueDMContext, DataRequestMonitor)}
+ * @since 2.0
+ */
+public class FormatedExpressionValueRequestCache extends AbstractFormattedValuesRequestCache<FormattedValueDMData> {
+
+ public FormatedExpressionValueRequestCache(IFormattedValues service, FormattedValueDMContext ctx) {
+ super(service, ctx);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.RequestCache#retrieve(org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ protected void retrieve(final DataRequestMonitor<FormattedValueDMData> rm) {
+
+ ((IFormattedValues)fService).getFormattedExpressionValue((FormattedValueDMContext)fCtx, new DataRequestMonitor<FormattedValueDMData>(fService.getExecutor(), rm) {
+ public void handleSuccess() {
+ rm.setData(getData());
+ rm.done();
+ }
+ });
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
+
+/**
+ * An ACPM cache for {@link IRegisters#getRegisters(IDMContext, DataRequestMonitor)
+ * @since 2.0
+ */
+public class GetRegistersRequestCache extends AbstractRegisterRequestCache<IRegisterDMContext[]> {
+
+ public GetRegistersRequestCache(IRegisters service, IDMContext ctx) {
+ super(service, ctx);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.RequestCache#retrieve(org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ protected void retrieve(DataRequestMonitor<IRegisterDMContext[]> rm) {
+ ((IRegisters)fService).getRegisters(fCtx, rm);
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+
+/**
+ * EDC ACPM cache objects must implement this interface in order to be
+ * manageable by an ICacheManager. Some cache manager implementations need to
+ * attach meta data to the cache objects in order to track them and decide which
+ * ones should be discarded first.
+ *
+ * @since 2.0
+ */
+public interface ICacheEntry {
+ /**
+ * Called by a cache manager to attach meta data for tracking purposes
+ *
+ * @param data
+ * the meta data
+ */
+ void setMetaData(Object data);
+
+ /**
+ * Called by a cache manager to retrieve the data it has previously attached
+ * via {@link #setMetaData(Object)}
+ *
+ * @return the meta data
+ */
+ Object getMetaData();
+
+ /**
+ * Called by a cache manager when it discards a cache object in order to
+ * make room for more. The implementation should clear any data it has been
+ * storing from its source.
+ */
+ void dispose();
+
+ /**
+ * Returns the RM the cache object passes to its derivative's retrieval
+ * method. Cache objects that are containers for other cache objects can
+ * return null. They are responsible for managing the lifetime of their own
+ * cache objects. We manage the lifetime of the collection as a whole.
+ */
+ RequestMonitor getRequestMonitor();
+
+
+ /**
+ * Returns the primary context the cache object is associated with
+ */
+ IDMContext getContext();
+}
+
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.datamodel.IDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.debug.core.model.MemoryByte;
+
+/**
+ * An ACPM cache for
+ * {@link IMemory#getMemory(IMemoryDMContext, IAddress, long, int, int, DataRequestMonitor)}
+ *
+ * @since 2.0
+ */
+public class MemoryRangeCache extends BaseRangeCache<MemoryByte> {
+
+ /** See {@link #getAddress()} */
+ final private IAddress fAddress;
+
+ /** See {@link #getWordSize()} */
+ final private int fWordSize;
+
+ public MemoryRangeCache(IMemory service, IDMContext ctx, IAddress address, int wordSize) {
+ super(service, ctx);
+ fAddress = address;
+ fWordSize = wordSize;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.RangeCache#retrieve(long, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ @Override
+ protected void retrieve(long offset, int count, final DataRequestMonitor<List<MemoryByte>> rm) {
+ ((IMemory)fService).getMemory((IMemoryDMContext)fCtx, fAddress, offset, fWordSize, count, new DataRequestMonitor<MemoryByte[]>(fService.getExecutor(), rm) {
+ public void handleSuccess() {
+ rm.setData(Arrays.asList(getData()));
+ rm.done();
+ }
+ });
+ }
+
+ @DsfServiceEventHandler
+ public void resumedEventHandler(IDMEvent<?> e) {
+ // TODO: be more discriminating instead instead of calling our super
+ // class for any event
+ super.resumedEventHandler(e);
+ }
+
+ /** The base address range requests are relative to */
+ public IAddress getAddress() {
+ return fAddress;
+ }
+
+ /** The the size, in bytes, of an addressable item */
+ public int getWordSize() {
+ return fWordSize;
+ }
+
+
+ public boolean wasCanceled() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext;
+
+/**
+ * An ACPM cache for
+ * {@link IRegisters#getRegisters(IDMContext, DataRequestMonitor)}
+ *
+ * @since 2.0
+ */
+public class RegisterGroupsRequestCache extends AbstractRegisterRequestCache<IRegisterGroupDMContext[]> {
+
+ public RegisterGroupsRequestCache(IRegisters service, IDMContext ctx) {
+ super(service, ctx);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.RequestCache#retrieve(org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ protected void retrieve(DataRequestMonitor<IRegisterGroupDMContext[]> rm) {
+ ((IRegisters)fService).getRegisterGroups(fCtx, rm);
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.acpm;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.services.DMContext;
+import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext;
+
+/**
+ * An ACPM cache of the collection of all available registers for the given
+ * context, indexed by name. The name is provided by TCF via the property
+ * {@link org.eclipse.tm.tcf.services.IRegisters#PROP_NAME}
+ *
+ * @since 2.0
+ */
+public class RegistersByNameRequestCache extends AbstractRegisterRequestCache< Map<String, IRegisterDMContext> > {
+
+ public RegistersByNameRequestCache(IRegisters service, IDMContext ctx) {
+ super(service, ctx);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.RequestCache#retrieve(org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ protected void retrieve(final DataRequestMonitor< Map<String, IRegisterDMContext> > rm) {
+ final Map<String, IRegisterDMContext> registerMap = new HashMap<String, IRegisterDMContext>();
+ ((IRegisters)fService).getRegisterGroups(fCtx, new DataRequestMonitor<IRegisterGroupDMContext[]>(fService.getExecutor(), rm) {
+ public void handleSuccess() {
+ final List<IRegisterDMContext> registers = new ArrayList<IRegisterDMContext>();
+ final IRegisterGroupDMContext[] groups = getData();
+ final CountingRequestMonitor crm = new CountingRequestMonitor(fService.getExecutor(), rm) {
+ public void handleSuccess() {
+ for (IRegisterDMContext register : registers) {
+ String regname = (String)((DMContext)register).getProperties().get(org.eclipse.tm.tcf.services.IRegisters.PROP_NAME);
+ registerMap.put(regname, register);
+ }
+ rm.setData(registerMap);
+ rm.done();
+ }
+ };
+ crm.setDoneCount(groups.length);
+ for (IRegisterGroupDMContext group : groups) {
+ ((IRegisters)fService).getRegisters(group, new DataRequestMonitor<IRegisterDMContext[]>(fService.getExecutor(), crm) {
+ public void handleSuccess() {
+ IRegisterDMContext[] regs = getData();
+ for (IRegisterDMContext reg : regs) {
+ registers.add(reg);
+ }
+ crm.done();
+ }
+ });
+ }
+ }
+ });
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Abstract disassembler that implements the platform neutral methods.
+ */
+public abstract class AbstractDisassembler implements IDisassembler {
+
+ private final ITargetEnvironment targetEnvironment;
+
+ /**
+ * Don't allow constructor without passing ITargetEnvironment
+ * @see org.eclipse.cdt.debug.edc.AbstractDisassembler#disassemblerAbstractDisassembler(ITargetEnvironment)
+ */
+ @SuppressWarnings("unused")
+ private AbstractDisassembler() { targetEnvironment = null; }
+
+ /**
+ * Since a disassembler is generally created within the implementor of
+ * {@link ITargetEnvironment#getDisassembler()}, this constructor together
+ * with the private default constructor forces the implementation to pass
+ * it's own "this" pointer for use in later disassembler processing.
+ * @since 2.0
+ */
+ protected AbstractDisassembler(ITargetEnvironment env) {
+ targetEnvironment = env;
+ }
+
+ /**
+ * Returns the ITargetEnvironment used by the disassembler.
+ * @since 2.0
+ */
+ protected ITargetEnvironment getTargetEnvironment() {
+ return targetEnvironment;
+ }
+
+ /**
+ * Translates the raw memory in the buffer into a list of disassembled instructions.
+ *
+ * @param startAddress the start address
+ * @param endAddress the end address
+ * @param codeBuffer the code buffer
+ * @param options the options, often target environment specific, that are used for disassembly
+ * @param dmc the disassembly context
+ * @return the list of disassembled instructions
+ * @throws CoreException the core exception
+ * @since 2.0
+ */
+ public List<IDisassembledInstruction> disassembleInstructions(IAddress startAddress, IAddress endAddress,
+ ByteBuffer codeBuffer, Map<String, Object> options, IDisassemblyDMContext dmc)
+ throws CoreException {
+ IDisassembledInstruction inst = null;
+ ArrayList<IDisassembledInstruction> result = new ArrayList<IDisassembledInstruction>();
+ IAddress address = startAddress;
+
+ while (codeBuffer.hasRemaining() && address.compareTo(endAddress) < 0) {
+ try {
+ inst = disassembleOneInstruction(address, codeBuffer, options, dmc);
+ result.add(inst);
+
+ // next instruction address
+ // Note at this point the current position of code buffer should
+ // point to the next instruction.
+ address = address.add(inst.getSize());
+ } catch (CodeBufferUnderflowException e) {
+ if (result.size() == 0) {
+ if (options.containsKey(IDisassemblerOptions.ADDRESS_IS_PC)) {
+ throwAnnotated(e, address);
+ }
+ throw e;
+ }
+ break;
+ } catch (CoreException e) {
+ throwAnnotated(e, address);
+ }
+ }
+
+ return result;
+ }
+
+ private void throwAnnotated(CoreException e, IAddress address) throws CoreException {
+ throw EDCDebugger.newCoreException("Fail to disassemble instruction at "
+ + address.toHexAddressString() + "\nCause: " + e.getLocalizedMessage(), e);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.cdt.core.IAddress;
+
+public class AssemblyFormatter {
+
+ private static final int ADDRESS_COLUMN_SIZE = 12;
+ private static final int CODE_BYTE_COLUMN_SIZE = 32;
+ protected static final int INST_NAME_COLUMN_SIZE = 10;
+ public static final char OPEN = '(';
+ public static final char CLOSE = ')';
+ public static final char SEPARATOR = ',';
+ public static final char SCALE = ',';
+ public static final char PREFIX_IMMEDIATE = '$';
+ public static final char PREFIX_REGISTER = '%';
+
+ public static String toHexString(IAddress addr, boolean show0x) {
+ // this gives "800000"
+ String ret = Integer.toHexString(addr.getValue().intValue());
+ if (show0x)
+ ret = "0x" + ret;
+
+ return ret;
+ }
+
+ /**
+ * get hexString representation of a byte data. <br>
+ * E.g. 0x12 => "12" or "0x12" <br>
+ * 2 = > "02" or "0x02".
+ *
+ * @param b
+ * @param show0x
+ * @return
+ */
+ public static String toHexString(byte b, boolean show0x) {
+ String s = Integer.toHexString(b & 0xff);
+ if (s.length() == 1)
+ // padding
+ s = "0" + s;
+ if (show0x)
+ s = "0x" + s;
+
+ return s;
+ }
+
+ public static String toHexString(long i, boolean show0x) {
+ String ret = Long.toHexString(i);
+ if (show0x)
+ ret = "0x" + ret;
+
+ return ret;
+ }
+ public static String toHexString(int i, boolean show0x) {
+ String ret = Integer.toHexString(i);
+ if (show0x)
+ ret = "0x" + ret;
+
+ return ret;
+ }
+
+ public static String toHexString(short i, boolean show0x) {
+ String ret = Integer.toHexString(i & 0xffff);
+ if (show0x)
+ ret = "0x" + ret;
+
+ return ret;
+ }
+
+ public static String formatFarPointer(short segment, int offset) {
+ return "$" + toHexString(segment, true) + ",$" + toHexString(offset, true);
+ }
+
+ /**
+ * Format address for displaying in format column.
+ */
+ public static String formatForAddressColumn(IAddress addr) {
+ String addrStr = toHexString(addr, false);
+
+ // Padding space at the end.
+ StringBuffer buf = new StringBuffer(addrStr).append(':');
+ for (int i = 0; i < ADDRESS_COLUMN_SIZE - addrStr.length(); i++)
+ buf.append(' ');
+
+ return buf.toString();
+ }
+
+ /**
+ * Format bytes for displaying in byte column.
+ *
+ * @param codeBuffer
+ * - code byte buffer.
+ * @param startPosition
+ * - position of the first byte in the code buffer.
+ * @param length
+ * - number of bytes to display.
+ * @return
+ */
+ public static String formatForByteColumn(ByteBuffer codeBuffer, int startPosition,
+ int length) {
+ StringBuffer tmp = new StringBuffer();
+
+ codeBuffer.position(startPosition);
+ for (int i = 0; i < length; i++) {
+ byte b = codeBuffer.get();
+ tmp.append(toHexString(b, false)).append(' ');
+ }
+
+ int cnt = tmp.length();
+
+ // padding
+ for (int i = 0; i < CODE_BYTE_COLUMN_SIZE - cnt; i++)
+ tmp.append(' ');
+
+ return tmp.toString();
+ }
+
+ public static String formatForCode(IAddress addr) {
+ return toHexString(addr, true);
+ }
+
+ public static String formatImmediate(long imm) {
+ /* Not signed. */
+ return PREFIX_IMMEDIATE + toHexString(imm, true);
+ }
+
+ public static String formatImmediate(int imm) {
+ /* Not signed. */
+ return PREFIX_IMMEDIATE + toHexString(imm, true);
+ }
+
+ public static String formatDisplacement(int displacement) {
+ if (displacement < 0)
+ return "-" + toHexString(-displacement, true); // negative hex
+ else
+ return toHexString(displacement, true); // signed hex
+ }
+
+ public static String formatOffset(int offset) {
+ // Offset is never negative.
+ return toHexString(offset, true); // signed hex
+ }
+
+ public static String formatRegister(String regName, boolean fIndirectAddressing) {
+ String ret = "";
+ if (fIndirectAddressing)
+ ret += OPEN;
+
+ ret += PREFIX_REGISTER + regName;
+
+ if (fIndirectAddressing)
+ ret += CLOSE;
+
+ return ret;
+ }
+
+ public static String instructionNameSizeSuffix(int size) {
+ return size == 8 ? "b" : (size == 16 ? "w" : "l");
+ }
+
+ public AssemblyFormatter() {
+ super();
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * Exists to help identify the precise CoreException being thrown by
+ * instruction parsers so that the disassemblers can perform error
+ * recovery more gracefully. <br>Extends CoreException so that
+ * existing handlers can catch it without having to be modified.
+ * @since 2.0
+ */
+public class CodeBufferUnderflowException extends CoreException {
+ private static final long serialVersionUID = 2725920360107613447L;
+
+ /**
+ * Exists to help identify the precise CoreException being
+ * thrown by instruction parsers so that the disassemblers
+ * can perform error recovery more gracefully.
+ * @param t the original thrown object, for reference as necessary
+ */
+ public CodeBufferUnderflowException(Throwable t) {
+ super(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID,
+ " end of code buffer reached.", t));
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IJumpToAddress;
+
+/**
+ * Class describing an instruction output from a disassembler.
+ */
+public class DisassembledInstruction implements IDisassembledInstruction {
+ // Address of the instruction
+ private IAddress address;
+ // size of instruction in 8-bit bytes
+ private int size;
+ // mnemonics, including instruction name & arguments. May include
+ // address and raw bytes, depending on disassembler options.
+ private String mnemonics;
+ // jump-to-address for a control-change instruction (branch, call, ret,
+ // etc.).
+ // Null for the other instructions.
+ private IJumpToAddress jumpToAddress;
+
+ public DisassembledInstruction() {
+ address = null;
+ size = 0;
+ mnemonics = null;
+ jumpToAddress = null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.disassembler.IDisassembledInstruction#getAddress()
+ */
+ public IAddress getAddress() {
+ return address;
+ }
+
+ public void setAddress(IAddress address) {
+ this.address = address;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.disassembler.IDisassembledInstruction#isValid()
+ */
+ public boolean isValid() {
+ return size > 0;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.disassembler.IDisassembledInstruction#getSize()
+ */
+ public int getSize() {
+ return size;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.disassembler.IDisassembledInstruction#getMnemonics()
+ */
+ public String getMnemonics() {
+ return mnemonics;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.disassembler.IDisassembledInstruction#getJumpToAddress()
+ */
+ public IJumpToAddress getJumpToAddress() {
+ return jumpToAddress;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public void setMnemonics(String mnemonics) {
+ this.mnemonics = mnemonics;
+ }
+
+ public void setJumpToAddress(IJumpToAddress jta) {
+ this.jumpToAddress = jta;
+ }
+
+ @Override
+ public String toString() {
+ return "(length: " + size + ") " + Integer.toHexString(address.getValue().intValue()) + ": " + mnemonics
+ + (jumpToAddress != null ? " [BranchAddress: " + jumpToAddress.toString() + "]" : "");
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import java.math.BigInteger;
+import java.util.StringTokenizer;
+
+import org.eclipse.cdt.dsf.debug.service.AbstractInstruction;
+import org.eclipse.cdt.dsf.debug.service.IInstruction;
+
+public class EDCInstruction extends AbstractInstruction {
+
+ private final IDisassembledInstruction instruction;
+ private String functionName;
+ private int offset;
+
+ public EDCInstruction(IDisassembledInstruction instruction) {
+ this.instruction = instruction;
+ functionName = null;
+ offset = 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.cdt.dsf.debug.service.IInstruction#getAdress()
+ */
+ public BigInteger getAdress() {
+ return instruction.getAddress().getValue();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.cdt.dsf.debug.service.IInstruction#getArgs()
+ */
+ public String getArgs() {
+ // It's assumed the disassembler does not put extras like address
+ // and raw bytes in the output.
+ String ret = null;
+ String asm = instruction.getMnemonics();
+ StringTokenizer tzer = new StringTokenizer(asm);
+ if (tzer.countTokens() == 1) { // no arguments
+ return ret;
+ } else {
+ tzer.nextToken(); // skip the instruction name
+ ret = tzer.nextToken();
+ while (tzer.hasMoreTokens())
+ ret += " " + tzer.nextToken();
+ }
+
+ return ret;
+ }
+
+ /**
+ * Sets the function name, if any, for this instruction..
+ *
+ * @param functionName the new function name
+ * @since 2.0
+ */
+ public void setFunctionName(String functionName) {
+ this.functionName = functionName;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.cdt.dsf.debug.service.IInstruction#getFuntionName()
+ */
+ public String getFuntionName() {
+ return functionName;
+ }
+ /**
+ * not a true override; the name is "misspelled" in {@link IInstruction}
+ * @since 2.0
+ * @see org.eclipse.cdt.dsf.debug.service.IInstruction#getFuntionName()
+ */
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.cdt.dsf.debug.service.IInstruction#getInstruction()
+ */
+ public String getInstruction() {
+ // Hmm, this actually needs the whole instruction.
+ return instruction.getMnemonics();
+ }
+
+ /**
+ * Sets the offset for this instruction.
+ *
+ * @param offset the new offset
+ * @since 2.0
+ */
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.cdt.dsf.debug.service.IInstruction#getOffset()
+ */
+ public long getOffset() {
+ return offset;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.cdt.dsf.debug.service.IInstruction#getOpcode()
+ */
+ public String getOpcode() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Needed for DisassemblyBackendDsf#insertDisassembly() ;
+ * for HEAD see the first reference; for CDT_7_0, see the second reference
+ * @see org.eclipse.cdt.dsf.debug.service.IInstruction#getSize()
+ * @see org.eclipse.cdt.dsf.debug.internal.provisional.service.IInstructionWithSize#getSize()
+ * @since 2.0
+ */
+ public Integer getSize() {
+ return instruction.getSize();
+ }
+
+ @Override
+ public String toString() {
+ return instruction.toString();
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext;
+import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+
+/**
+ * Information for an instruction, line, or block that can be used
+ * when constructing EDCInstruction objects to collect and pass to
+ * DisassemblyBackendDsf for display in the disassembly view.
+ * @author kirk.beitz@nokia.com
+ * @since 2.0
+ */
+public class EDCInstructionFunctionInfo {
+ private final String functionName;
+ private final IAddress functionStartAddress;
+
+ /**
+ * simple constructor
+ * @param name function for the block this object represents
+ * @param address beginning of the function
+ */
+ public EDCInstructionFunctionInfo(String name, IAddress address) {
+ functionName = name;
+ functionStartAddress = address;
+ }
+
+ /**
+ * constructor for a block whose function may not be known
+ * @param module container of the item for which to find function/address info
+ * @param line source location from LNT for which to find function/address info
+ */
+ public EDCInstructionFunctionInfo(IEDCModuleDMContext module, ILineEntry line) {
+ String name = null;
+ IAddress start = null;
+ IEDCSymbolReader reader = module.getSymbolReader();
+ if (reader != null) {
+ IScope scope = reader.getModuleScope().getScopeAtAddress(line.getLowAddress());
+ while (scope != null && !(scope instanceof IFunctionScope)) {
+ scope = scope.getParent();
+ }
+
+ if (scope != null) {
+ name = scope.getName();
+ start = ((IFunctionScope)scope).getLowAddress();
+ }
+ }
+ functionName = name;
+ functionStartAddress = start;
+ }
+
+ /**
+ * @return function name for block, null if never established
+ */
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ /**
+ * @return starting address of function, null if never established
+ */
+ public IAddress getFunctionStartAddress() {
+ return functionStartAddress;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import org.eclipse.cdt.dsf.debug.service.IInstruction;
+import org.eclipse.cdt.dsf.debug.service.IMixedInstruction;
+
+public class EDCMixedInstruction implements IMixedInstruction {
+
+ private final String fileName;
+ private final int lineNumber;
+ private final IInstruction[] instructions;
+
+ public EDCMixedInstruction(String fileName, int lineNumber, IInstruction[] instructions) {
+ this.fileName = fileName;
+ this.lineNumber = lineNumber;
+ this.instructions = instructions;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.cdt.dsf.debug.service.IMixedInstruction#getFileName()
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.cdt.dsf.debug.service.IMixedInstruction#getInstructions()
+ */
+ public IInstruction[] getInstructions() {
+ return instructions;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.cdt.dsf.debug.service.IMixedInstruction#getLineNumber()
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("Source File: ").append(fileName).append("\n");
+ buf.append(" line #").append(lineNumber).append(":\n");
+ for (IInstruction i : instructions)
+ buf.append("\t").append(i).append("\n");
+
+ return buf.toString();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IJumpToAddress;
+import org.eclipse.cdt.debug.edc.JumpToAddress;
+
+public interface IDisassembledInstruction {
+
+ public IAddress getAddress();
+
+ public boolean isValid();
+
+ public int getSize();
+
+ public String getMnemonics();
+
+ /**
+ * Get {@link JumpToAddress} of the instruction. Return null for non
+ * control-change instruction.
+ *
+ * @return
+ */
+ public IJumpToAddress getJumpToAddress();
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.disassembler;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Generic interface for disassemblers.
+ *
+ */
+public interface IDisassembler {
+
+ /**
+ * A known static value for assembly instruction parsers to use to
+ * indicate an instruction that cannot be parsed, and against which
+ * abstract/generic members of Disassembly services can compare.
+ * @since 2.0
+ */
+ public static final String INVALID_OPCODE = "invalid opcode";
+
+ /**
+ * Disassembler options that are common to all targets. Different targets
+ * may have its own disassembler options.
+ */
+ public static interface IDisassemblerOptions {
+ /*
+ * Option key names.
+ */
+ public final static String GET_BRANCH_ADDRESS = "GetBranchAddress";
+ public final static String GET_MNEMONICS = "GetMnemonics";
+
+ // Following are sub-options when GetMnemonics is true.
+ //
+ /**
+ * Show address of the instruction in disassembler output.
+ */
+ public final static String MNEMONICS_SHOW_ADDRESS = "ShowAddresses";
+ /**
+ * Show original bytes of the instruction in disassembler output.
+ */
+ public final static String MNEMONICS_SHOW_BYTES = "ShowBytes";
+ /**
+ * Show symbol in the address in disassembler output.
+ */
+ public final static String MNEMONICS_SHOW_SYMBOL = "ShowSymbol";
+
+ /**
+ * Indicates that the address being disassembled is the PC
+ * @since 2.0
+ */
+ public static final String ADDRESS_IS_PC = "AddressIsPC";
+ }
+
+ /**
+ * Disassemble one instruction at the beginning of the given byte array.
+ *
+ * @param address
+ * address of the code bytes
+ * @param code_bytes
+ * memory bytes containing instructions.
+ * @param options
+ * disassembler options.
+ * @param dmc
+ * for context specific needs of the implementor (may be null)
+ * @return a {@link IDisassembledInstruction} object, null if no valid
+ * instruction at the beginning of the code_bytes.
+ * @throws CoreException
+ * @since 2.0
+ */
+ public IDisassembledInstruction disassembleOneInstruction(IAddress address, ByteBuffer code_bytes,
+ Map<String, Object> options, IDisassemblyDMContext dmc) throws CoreException;
+
+ /**
+ * Disassemble a block of memory.
+ *
+ * @param start_address
+ * address of the first byte in the "code_bytes"
+ * @param end_address
+ * address of the byte after the last byte that is disassembled.
+ * @param code_bytes
+ * memory bytes
+ * @param options
+ * disassembler options.
+ * @param dmc
+ * for context specific needs of the implementor (may be null)
+ *
+ * @return a list of {@link IDisassembledInstruction} objects.
+ * @throws CoreException
+ * @since 2.0
+ */
+ public List<IDisassembledInstruction> disassembleInstructions(IAddress start_address, IAddress end_address,
+ ByteBuffer code_bytes, Map<String, Object> options, IDisassemblyDMContext dmc) throws CoreException;
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.cdt.debug.edc.services.IEDCExpression;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.dsf.debug.service.IExpressions;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * An abstract class for formatting composite types.
+ */
+public abstract class AbstractCompositeFormatProvider extends AbstractVariableConverter implements ITypeContentProvider {
+
+ /**
+ * The Class NameToFieldPath.
+ */
+ protected static class NameToFieldPath {
+
+ /** The name. */
+ private String name;
+
+ /** The field path. */
+ private String fieldPath;
+
+ /**
+ * Instantiates a new name to field path.
+ *
+ * @param name the name
+ * @param fieldPath the field path
+ */
+ public NameToFieldPath(String name, String fieldPath) {
+ this.name = name;
+ this.fieldPath = fieldPath;
+ }
+
+ /**
+ * Gets the name.
+ *
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets the field path.
+ *
+ * @return the field path
+ */
+ public String getFieldPath() {
+ return fieldPath;
+ }
+ }
+
+ /** The name-to-field paths. */
+ private final NameToFieldPath[] nameToFieldPaths;
+
+ /**
+ * Instantiates a new abstract composite format provider.
+ *
+ * @param type the type
+ * @param forDetails is this used for the details pane
+ * @param nameToFieldPaths the name to field paths
+ */
+ public AbstractCompositeFormatProvider(IType type, boolean forDetails, List<NameToFieldPath> nameToFieldPaths) {
+ super(type, forDetails);
+ this.nameToFieldPaths = nameToFieldPaths.toArray(new NameToFieldPath[nameToFieldPaths.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.formatter.ITypeContentProvider#getChildIterator(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext)
+ */
+ public Iterator<IExpressionDMContext> getChildIterator(IExpressionDMContext variable) throws CoreException {
+ List<IExpressionDMContext> childExpressions = getChildren(variable);
+ return childExpressions.iterator();
+ }
+
+ /**
+ * Gets the child count.
+ *
+ * @param variable the variable
+ * @return the child count
+ * @throws CoreException the core exception
+ * @since 2.0
+ */
+ public int getChildCount(IExpressionDMContext variable) throws CoreException {
+ List<IExpressionDMContext> childExpressions = getChildren(variable);
+ return childExpressions.size();
+ }
+
+ /**
+ * Gets the children.
+ *
+ * @param variable the variable
+ * @return the children
+ * @throws CoreException the core exception
+ */
+ protected List<IExpressionDMContext> getChildren(IExpressionDMContext variable) throws CoreException {
+ List<IExpressionDMContext> childExpressions = new ArrayList<IExpressionDMContext>();
+ for (NameToFieldPath nameToFieldPath : nameToFieldPaths) {
+ IExpressionDMContext createSubExpression =
+ FormatUtils.createSubExpression(variable, nameToFieldPath.getName(),
+ FormatUtils.getFieldAccessor(type) + nameToFieldPath.getFieldPath());
+ if (createSubExpression != null) {
+ childExpressions.add(createSubExpression);
+ }
+ }
+ // now add all unmapped children
+ List<IExpressionDMContext> allChildren = FormatUtils.getAllChildExpressions(variable);
+ for (IExpressionDMContext child : allChildren) {
+ String name = ((IEDCExpression) child).getName();
+ if (!hasFieldPath(name))
+ childExpressions.add(child);
+ }
+ return childExpressions;
+ }
+
+ /**
+ * Checks for field path.
+ *
+ * @param fieldPath the field path
+ * @return true, if successful
+ */
+ private boolean hasFieldPath(String fieldPath) {
+ for (NameToFieldPath nameToFieldPath : nameToFieldPaths) {
+ if (nameToFieldPath.getFieldPath().equals(fieldPath))
+ return true;
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.formatter.AbstractVariableConverter#getDetailsValue(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext)
+ */
+ protected String getDetailsValue(IExpressionDMContext variable) throws CoreException {
+ return getValueString(variable);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.formatter.AbstractVariableConverter#getSummaryValue(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext)
+ */
+ protected String getSummaryValue(IExpressionDMContext variable) throws CoreException {
+ return getValueString(variable);
+ }
+
+ /**
+ * Gets the value string.
+ *
+ * @param variable the variable
+ * @return the value string
+ * @throws CoreException the core exception
+ */
+ protected String getValueString(IExpressionDMContext variable) throws CoreException {
+ IExpressions expressions = ((IEDCExpression) variable).getExpressionsService();
+ if (expressions == null)
+ return ""; //$NON-NLS-1$
+
+ StringBuilder sb = new StringBuilder();
+ List<IExpressionDMContext> children = getChildren(variable);
+ int i = 0;
+ for (IExpressionDMContext child : children) {
+ IEDCExpression childExpression = (IEDCExpression) child;
+ if (i < nameToFieldPaths.length)
+ sb.append(nameToFieldPaths[i].getName());
+ else
+ sb.append(childExpression.getName());
+ sb.append("="); //$NON-NLS-1$
+ sb.append(FormatUtils.getVariableValue(childExpression));
+ sb.append(" "); //$NON-NLS-1$
+ i++;
+ }
+ return sb.toString();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.services.IEDCExpression;
+import org.eclipse.cdt.debug.edc.symbols.IMemoryVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+
+/**
+ * Base class for string formatters.
+ */
+public abstract class AbstractStringFormatter implements IVariableFormatProvider {
+
+ /**
+ * Converter handles char* and wchar_t* strings terminated by a null character.
+ */
+ public class DefaultNullTerminatedStringConverter extends AbstractVariableConverter {
+ public DefaultNullTerminatedStringConverter(IType type, boolean forDetails) {
+ super(type, forDetails);
+ }
+
+
+ @Override
+ protected String getDetailsValue(IExpressionDMContext variable) throws CoreException {
+ return getValueString(variable);
+ }
+
+ @Override
+ protected String getSummaryValue(IExpressionDMContext variable) throws CoreException {
+ return getValueString(variable);
+ }
+
+ private String getValueString(IExpressionDMContext variable) throws CoreException {
+ IEDCExpression expressionDMC = (IEDCExpression) variable;
+ expressionDMC.evaluateExpression();
+ IType baseType = TypeUtils.getBaseType(expressionDMC.getEvaluatedType());
+ int size = baseType.getByteSize();
+
+ if (expressionDMC.getEvaluationError() != null)
+ return expressionDMC.getEvaluationError().getMessage();
+
+ // pointer living at null is not valid (e.g. inside a struct pointing to null)
+ IVariableLocation exprLoc = expressionDMC.getEvaluatedLocation();
+ if (exprLoc instanceof IMemoryVariableLocation && ((IMemoryVariableLocation) exprLoc).getAddress().isZero())
+ return "0";
+
+ int maximumLength = getMaximumLength();
+ // limit to express char[] size if given
+ if (expressionDMC.getEvaluatedType() instanceof IArrayType) {
+ long boundLength = ((IArrayType)expressionDMC.getEvaluatedType()).getBound(0).getBoundCount();
+ if (boundLength > 0) {
+ maximumLength = (int) Math.min(boundLength, maximumLength);
+ }
+ }
+
+ Number value = expressionDMC.getEvaluatedValue();
+ if (value == null) {
+ if (expressionDMC.getEvaluatedValueString() != null) {
+ // already a string
+ // TODO: proper formatting
+ return '"' + expressionDMC.getEvaluatedValueString() + '"';
+ }
+ return null;
+ }
+
+ IAddress address = FormatUtils.getPointerValue(value);
+ if (address == null) {
+ return value.toString(); // dunno
+ }
+
+ // null pointer
+ if (address.isZero())
+ return "0";
+
+ String formattedString =
+ FormatUtils.getFormattedNullTermString(variable, address, size, maximumLength);
+ return quoteString(formattedString);
+ }
+
+ private String quoteString(String str) {
+ StringBuilder sb = new StringBuilder("\"");
+ sb.append(str);
+ sb.append('\"');
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Override to change the maximum string length to format.
+ * @return size in characters
+ */
+ protected int getMaximumLength() {
+ IEclipsePreferences scope = InstanceScope.INSTANCE.getNode("org.eclipse.debug.ui");
+ return scope.getInt("org.eclipse.debug.ui.max_detail_length", 256);
+ }
+
+ public ITypeContentProvider getTypeContentProvider(IType type) {
+ return null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Abstract class implementing IVariableValueConverter with summary and detail variants
+ */
+public abstract class AbstractVariableConverter implements IVariableValueConverter {
+
+ protected IType type;
+ private boolean forDetails;
+ private int curValueLength;
+
+ public AbstractVariableConverter(IType type, boolean forDetails) {
+ this.forDetails = forDetails;
+ this.type = type;
+ }
+
+ protected abstract String getDetailsValue(IExpressionDMContext variable) throws CoreException;
+
+ protected abstract String getSummaryValue(IExpressionDMContext variable) throws CoreException;
+
+ public String getValue(IExpressionDMContext variable) throws CoreException {
+ if (forDetails)
+ return getDetailsValue(variable);
+ return getSummaryValue(variable);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setCurValueLength(int curValueLength) {
+ this.curValueLength = curValueLength;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public int getCurValueLength() {
+ return curValueLength;
+ }
+
+ public boolean canEditValue() {
+ return false; // read-only implementation
+ }
+
+ public String getEditableValue(IExpressionDMContext variable) throws CoreException {
+ return getValue(variable); // default - use same value
+ }
+
+ public void setValue(IExpressionDMContext variable, String newValue) {
+ // read-only implementation
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import java.util.List;
+
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.IArrayDimensionType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.services.IEDCExpression;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.dsf.debug.service.IExpressions;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public class DefaultArrayFormatter implements IVariableFormatProvider {
+
+ public class DefaultArrayConverter extends AbstractVariableConverter {
+
+ private static final int STOP_LENGTH = 300; // stop adding items when string is this length
+
+ public DefaultArrayConverter(IType type, boolean forDetails) {
+ super(type, forDetails);
+ }
+
+ @Override
+ protected String getSummaryValue(IExpressionDMContext variable) throws CoreException {
+ IExpressions expressions = ((IEDCExpression) variable).getExpressionsService();
+ if (expressions == null)
+ return ""; //$NON-NLS-1$
+ StringBuilder sb = new StringBuilder("[");
+ List<IExpressionDMContext> children = FormatUtils.getAllChildExpressions(variable);
+ boolean skip = true;
+ for (IExpressionDMContext child : children) {
+ if (skip)
+ skip = false;
+ else
+ sb.append(", ");
+ String customString = getCustomValueString(child, getCurValueLength() + sb.length());
+ if (customString != null) {
+ sb.append('{');
+ sb.append(customString);
+ sb.append('}');
+ }
+ else {
+ IEDCExpression childExpression = (IEDCExpression) child;
+ sb.append(FormatUtils.getVariableValue(childExpression));
+ }
+ if (getCurValueLength() + sb.length() > STOP_LENGTH) {
+ if (!children.get(children.size() - 1).equals(child))
+ sb.append(", ...");
+ break;
+ }
+ }
+ return sb.append(']').toString();
+ }
+
+ private String getCustomValueString(IExpressionDMContext variable, int curValueLength) throws CoreException {
+ IVariableValueConverter converter = FormatUtils.getCustomValueConverter(variable);
+ if (converter != null) {
+ if (converter instanceof AbstractVariableConverter)
+ ((AbstractVariableConverter) converter).setCurValueLength(curValueLength);
+ return converter.getValue(variable);
+ }
+ return null;
+ }
+
+ @Override
+ protected String getDetailsValue(IExpressionDMContext variable) throws CoreException {
+ return getSummaryValue(variable);
+ }
+
+ }
+
+ public static boolean handlesType(IType type) {
+ IType unqualifiedType = FormatUtils.getUnqualifiedTypeRemovePointers(type);
+ return unqualifiedType instanceof IArrayType
+ || unqualifiedType instanceof IArrayDimensionType;
+ }
+
+ public ITypeContentProvider getTypeContentProvider(IType type) {
+ return null;
+ }
+
+ public IVariableValueConverter getDetailValueConverter(IType type) {
+ if (handlesType(type))
+ return new DefaultArrayConverter(type, true);
+ return null;
+ }
+
+ public IVariableValueConverter getVariableValueConverter(IType type) {
+ if (handlesType(type))
+ return new DefaultArrayConverter(type, false);
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.ConstType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IReferenceType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ITypedef;
+import org.eclipse.cdt.debug.edc.internal.symbols.VolatileType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * Handle char* and wchar_t* strings (ignoring const, volatile, and typedefs to char type).
+ */
+public class DefaultCStringFormatter extends AbstractStringFormatter {
+
+ public static boolean handlesType(IType type) {
+ if (type instanceof ICPPBasicType) {
+ ICPPBasicType basicType = (ICPPBasicType) type;
+ // NOTE: we may have neither signed nor unsigned set on a char*, and this is a "classic" string.
+ // We would like to explicitly ignore "unsigned char*", since that's normally a byte buffer --
+ // but RVCT incorrectly sets this flag for some instances of "char*" in source.
+ boolean isCharString = /*!basicType.isUnsigned() &&*/ basicType.getBaseType() == ICPPBasicType.t_char;
+ boolean isWcharTString = basicType.getBaseType() == ICPPBasicType.t_wchar_t || basicType.getName().equals("wchar_t");
+ return isCharString || isWcharTString;
+ }
+ return false;
+ }
+
+ public ITypeContentProvider getTypeContentProvider(IType type) {
+ return null;
+ }
+
+ public IVariableValueConverter getDetailValueConverter(IType type) {
+ IType basicType = getBasePointedType(type);
+ if (handlesType(basicType))
+ return new DefaultNullTerminatedStringConverter(type, true);
+
+ return null;
+ }
+
+ public IVariableValueConverter getVariableValueConverter(IType type) {
+ IType basicType = getBasePointedType(type);
+ if (handlesType(basicType))
+ return new DefaultNullTerminatedStringConverter(type, false);
+
+ return null;
+ }
+
+
+ /**
+ * Get the basic unit type pointed to by a string (reducing, e.g.,
+ * "const char *const" to "char"). Also, handle typedefs (e.g. "gchar") and
+ * references (char &) to char.
+ * @param type
+ * @return basic unit type or <code>null</code> if not a pointer to basic type
+ */
+ protected IType getBasePointedType(IType type) {
+ // remove upper qualifiers
+ while (type instanceof ConstType || type instanceof VolatileType || type instanceof ITypedef || type instanceof IReferenceType)
+ type = type.getType();
+
+ // make sure it's a single pointer or a single-dimension array
+ if (type instanceof IPointerType)
+ type = type.getType();
+ else if (type instanceof IArrayType && ((IArrayType) type).getBoundsCount() == 1)
+ type = type.getType();
+ else
+ return null;
+
+ // remove inner qualifiers
+ while (type instanceof ConstType || type instanceof VolatileType || type instanceof ITypedef || type instanceof IReferenceType)
+ type = type.getType();
+ if (!(type instanceof ICPPBasicType))
+ return null;
+ return type;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import java.util.List;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
+import org.eclipse.cdt.debug.edc.services.IEDCExpression;
+import org.eclipse.cdt.debug.edc.symbols.IMemoryVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.dsf.debug.service.IExpressions;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public class DefaultCompositeFormatter implements IVariableFormatProvider {
+
+ public class DefaultCompositeConverter extends AbstractVariableConverter {
+
+ private static final int MAX_DEPTH = 1; // stop recursing at this depth
+ private static final int STOP_LENGTH = 300; // stop adding items when string is this length
+
+ public DefaultCompositeConverter(IType type, boolean forDetails) {
+ super(type, forDetails);
+ }
+
+ @Override
+ protected String getSummaryValue(IExpressionDMContext variable) throws CoreException {
+ StringBuilder sb = new StringBuilder();
+ addVariableFields(null, sb, variable, 0);
+ if (sb.length() == 0) {
+ // if debug information does not include child information,
+ // return the already evaluated value
+ if (variable instanceof IEDCExpression) {
+ IEDCExpression variableEDMC = (IEDCExpression)variable;
+ return variableEDMC.getEvaluatedValueString();
+ }
+ }
+ return sb.toString();
+ }
+
+ private boolean hasNullLocation(IExpressionDMContext variable) throws CoreException {
+ if (variable instanceof IEDCExpression) {
+ FormatUtils.evaluateExpression((IEDCExpression) variable);
+ IVariableLocation loc = ((IEDCExpression) variable).getEvaluatedLocation();
+ if (loc instanceof IMemoryVariableLocation) {
+ if (((IMemoryVariableLocation) loc).getAddress().isZero()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void addVariableFields(String prefix, StringBuilder sb, IExpressionDMContext variable, int curDepth) throws CoreException {
+ // if at null, don't try
+ if (hasNullLocation(variable))
+ return;
+
+ if (prefix == null)
+ prefix = ""; //$NON-NLS-1$
+ List<IExpressionDMContext> childContexts = FormatUtils.getAllChildExpressions(variable);
+ for (IExpressionDMContext child : childContexts) {
+ IEDCExpression childExpression = (IEDCExpression) child;
+
+ // if any child is at null, likely the struct is at null or crosses null, and is bad news
+ if (hasNullLocation(childExpression)) {
+ continue;
+ }
+
+ IVariableValueConverter customConverter =
+ FormatUtils.getCustomValueConverter(child);
+ if (customConverter != null &&
+ !(customConverter instanceof DefaultCompositeConverter)) {
+ sb.append(prefix);
+ sb.append(childExpression.getName());
+ sb.append("="); //$NON-NLS-1$
+ // for default array converter strings, don't surround in extra brackets
+ boolean isDefaultArrayConverter =
+ customConverter instanceof DefaultArrayFormatter.DefaultArrayConverter;
+ if (!isDefaultArrayConverter)
+ sb.append("{");
+ if (customConverter instanceof AbstractVariableConverter)
+ ((AbstractVariableConverter) customConverter).setCurValueLength(getCurValueLength() + sb.length());
+ sb.append(customConverter.getValue(child));
+ if (!isDefaultArrayConverter)
+ sb.append("}");
+ sb.append(" ");
+ }
+ else {
+ IType evaluatedType = childExpression.getEvaluatedType();
+ IType unqualifiedType = FormatUtils.getUnqualifiedTypeRemovePointers(evaluatedType);
+ if (unqualifiedType instanceof ICompositeType) {
+ unqualifiedType = TypeUtils.getStrippedType(evaluatedType);
+ StringBuilder childPrefixSB = new StringBuilder(prefix);
+ String name = childExpression.getName();
+ if (name.startsWith("*")) {
+ childPrefixSB.append('(');
+ childPrefixSB.append(name);
+ childPrefixSB.append(')');
+ } else
+ childPrefixSB.append(name);
+ childPrefixSB.append(FormatUtils.getFieldAccessor(unqualifiedType));
+ if (curDepth < MAX_DEPTH)
+ addVariableFields(childPrefixSB.toString(), sb, child, ++curDepth);
+ else {
+ addSimpleChild(prefix, sb, childExpression);
+ }
+ }
+ else {
+ addSimpleChild(prefix, sb, childExpression);
+ }
+ }
+
+ if (getCurValueLength() + sb.length() > STOP_LENGTH) {
+ if (!childContexts.get(childContexts.size() - 1).equals(child))
+ sb.append("... ");
+ break;
+ }
+ }
+ }
+
+ private void addSimpleChild(String prefix, StringBuilder sb, IEDCExpression childExpression) {
+ IExpressions expressions = childExpression.getExpressionsService();
+ if (expressions == null)
+ return;
+
+ sb.append(prefix);
+ sb.append(childExpression.getName());
+ sb.append("="); //$NON-NLS-1$
+ sb.append(FormatUtils.getVariableValue(childExpression));
+ sb.append(" "); //$NON-NLS-1$
+ }
+
+ @Override
+ protected String getDetailsValue(IExpressionDMContext variable) throws CoreException {
+ return getSummaryValue(variable);
+ }
+ }
+
+ public static boolean handlesType(IType type) {
+ IType unqualifiedType = FormatUtils.getUnqualifiedTypeRemovePointers(type);
+ return unqualifiedType instanceof ICompositeType;
+ }
+
+ public ITypeContentProvider getTypeContentProvider(IType type) {
+ return null;
+ }
+
+ public IVariableValueConverter getDetailValueConverter(IType type) {
+ if (handlesType(type))
+ return new DefaultCompositeConverter(type, true);
+
+ return null;
+ }
+
+ public IVariableValueConverter getVariableValueConverter(IType type) {
+ if (handlesType(type))
+ return new DefaultCompositeConverter(type, false);
+
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import org.eclipse.osgi.util.NLS;
+
+public class EDCFormatterMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.edc.formatter.EDCFormatterMessages"; //$NON-NLS-1$
+ public static String FormatUtils_CannotReadMemory;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, EDCFormatterMessages.class);
+ }
+
+ private EDCFormatterMessages() {
+ }
+}
--- /dev/null
+###############################################################################
+# Copyright (c) 2010 Nokia 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:
+# Nokia - Initial API and implementation
+###############################################################################
+FormatUtils_CannotReadMemory=Cannot read memory at 0x{0}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import java.math.BigInteger;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.IArrayDimensionType;
+import org.eclipse.cdt.debug.edc.internal.formatter.FormatExtensionManager;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Expressions;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Memory;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IInheritance;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ITypedef;
+import org.eclipse.cdt.debug.edc.services.IEDCExpression;
+import org.eclipse.cdt.debug.edc.services.IEDCMemory;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.dsf.debug.service.IExpressions;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData;
+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.debug.core.model.MemoryByte;
+
+/**
+ * Utilities for generating formatters
+ *
+ * Use of non-api IType in this class is provisional. IType will later move to a public package.
+ */
+public class FormatUtils {
+
+ /** The Constant CLASS. */
+ private final static String CLASS = "class " ; //$NON-NLS-1$
+
+ /** The Constant STRUCT. */
+ private final static String STRUCT = "struct "; //$NON-NLS-1$
+
+ /**
+ * Check type by name.
+ *
+ * @param type the type
+ * @param baseName the base name
+ * @return true, if successful
+ */
+ public static boolean checkTypeByName(IType type, String baseName) {
+ if (type == null)
+ return false;
+ // we want to preserve typedefs to determine whether this is a type we support with a formatter
+ IType baseType = TypeUtils.getBaseTypePreservingTypedef(type);
+
+ // check for someone making a typedef of what we're looking for
+ while (baseType != null && baseType instanceof ITypedef) {
+ if (baseType.getName().equals(baseName))
+ return true;
+ baseType = TypeUtils.getBaseTypePreservingTypedef(baseType.getType());
+ }
+
+ if (baseType == null)
+ return false;
+
+ return checkName(baseType.getName(), baseName);
+ }
+
+ /**
+ * Check name.
+ *
+ * @param typeName the type name
+ * @param baseName the base name
+ * @return true, if successful
+ */
+ public static boolean checkName(String typeName, String baseName) {
+ String checkName = typeName;
+ if (typeName.startsWith(CLASS))
+ checkName = typeName.substring(CLASS.length()).trim();
+ else if (typeName.startsWith(STRUCT))
+ checkName = typeName.substring(STRUCT.length()).trim();
+ return checkName.equals(baseName);
+ }
+
+
+ /**
+ * Check if the name of a class/struct, or one of the classes/structs it
+ * derives from, matches a given name.
+ *
+ * @param type type of class/struct
+ * @param name type name to match against
+ * @return true if class/struct or inherited class/struct matches name,
+ * or null if no match
+ */
+ public static boolean checkClassOrInheritanceByName(IType type, String name) {
+ // strip off typedefs and type qualifiers, to look for classes and structs
+ type = TypeUtils.getBaseType(type);
+
+ if (!(type instanceof ICompositeType))
+ return false;
+
+ ICompositeType composite = (ICompositeType)type;
+
+ String baseName = composite.getBaseName();
+
+ if (baseName.equals(name))
+ return true;
+
+ // if base name ends with a template size (e.g., "<15>"),
+ // match ignoring the value in the braces
+ if (baseName.indexOf('<') != -1) //$NON-NLS-1$
+ if (baseName.matches(name + "<.*>$")) //$NON-NLS-1$
+ return true;
+
+ // check classes and structs it derives from
+ for (IInheritance inheritance : composite.getInheritances()) {
+ if (checkClassOrInheritanceByName(inheritance.getType(), name))
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Creates the sub expression.
+ *
+ * @param variable the variable
+ * @param name the name
+ * @param subExpressionStr the sub expression str
+ * @return the IExpressionDMContext for the sub expression.
+ */
+ public static IExpressionDMContext createSubExpression(IExpressionDMContext variable, String name, String subExpressionStr) {
+ IEDCExpression parentExpr = (IEDCExpression) variable;
+ IExpressions expressions = parentExpr.getExpressionsService();
+ if (expressions == null)
+ return null;
+ String expressionStr = parentExpr.getExpression() + subExpressionStr;
+ IEDCExpression subExpression = (IEDCExpression) expressions.createExpression(parentExpr, expressionStr);
+ subExpression.setName(name);
+ return subExpression;
+ }
+
+ /**
+ * Gets the formatted string.
+ *
+ * @param variable the variable
+ * @param address the address
+ * @param length the length
+ * @param charSize the char size
+ * @return the formatted string
+ * @throws CoreException the core exception
+ */
+ public static String getFormattedString(IExpressionDMContext variable, IAddress address, int length, int charSize)
+ throws CoreException {
+ IEDCExpression expression = (IEDCExpression) variable;
+ StackFrameDMC frame = (StackFrameDMC) expression.getFrame();
+ IEDCMemory memory = frame.getEDCServicesTracker().getService(Memory.class);
+
+ StringBuilder sb = new StringBuilder();
+ ArrayList<MemoryByte> buffer = new ArrayList<MemoryByte>();
+ IStatus status = memory.getMemory(frame.getExecutionDMC(), address, buffer, length * charSize, 1);
+ if (status.isOK()) {
+ for (int i = 0; i < length * charSize; i++) {
+ // make sure each byte is okay
+ if (!buffer.get(i).isReadable())
+ throw EDCDebugger.newCoreException(
+ MessageFormat.format(EDCFormatterMessages.FormatUtils_CannotReadMemory,
+ address.add(i).getValue().toString(16)));
+
+ char c = (char) (buffer.get(i).getValue() & 0xff);
+ if (charSize > 1) {
+ char c2 = (char) (buffer.get(++i).getValue() << 8);
+ c |= c2;
+ }
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Gets the formatted null term string.
+ *
+ * @param variable the variable
+ * @param address the address
+ * @param charSize the char size
+ * @param maximumLength the maximum length
+ * @return the formatted null term string
+ * @throws CoreException the core exception
+ */
+ public static String getFormattedNullTermString(IExpressionDMContext variable,
+ IAddress address, int charSize,
+ int maximumLength) throws CoreException {
+ IEDCExpression expression = (IEDCExpression) variable;
+ StackFrameDMC frame = (StackFrameDMC) expression.getFrame();
+ IEDCMemory memory = frame.getEDCServicesTracker().getService(Memory.class);
+
+ StringBuilder sb = new StringBuilder();
+ ArrayList<MemoryByte> buffer = new ArrayList<MemoryByte>(64);// typical size of cache block
+ if (maximumLength == 0)
+ maximumLength = 16384; // somewhat arbitrary; if the user really wants more, the value can always be set higher
+ OUTER:while (maximumLength > 0) {
+ int amount = Math.min(maximumLength, 64);// typical size of cache block
+ IStatus status = memory.getMemory(frame.getExecutionDMC(), address, buffer, amount, charSize);
+ if (status.isOK()) {
+ // make sure each byte is okay
+ for (int i = 0; i < buffer.size() && maximumLength > 0; ++i, --maximumLength) {
+ if (!buffer.get(i).isReadable())
+ {
+ if (i == 0) // partial memory read success
+ throw EDCDebugger.newCoreException(
+ MessageFormat.format(EDCFormatterMessages.FormatUtils_CannotReadMemory,
+ address.add(i).getValue().toString(16)));
+ maximumLength = 0;
+ break OUTER;
+ }
+ char c = (char) buffer.get(i).getValue();
+ if (charSize > 1) {
+ char c2 = (char) (buffer.get(++i).getValue() << 8);
+ c |= c2;
+ }
+ if (c == '\0')
+ break OUTER;
+ sb.append(c);
+ address = address.add(charSize);
+ }
+ } else if (amount > 1) {
+ maximumLength = Math.min(maximumLength, 64) / 2;
+ } else {
+ // Error in reading memory, bail out. If we got more than one character,
+ // use ellipsis, else fail.
+ if (sb.length() == 0)
+ throw EDCDebugger.newCoreException(
+ MessageFormat.format(EDCFormatterMessages.FormatUtils_CannotReadMemory,
+ address.getValue().toString(16)));
+ maximumLength = 0;
+ break;
+ }
+ buffer.clear();
+ }
+ if (maximumLength <= 0)
+ sb.append("..."); //$NON-NLS-1$
+
+ return sb.toString();
+ }
+
+ /**
+ * Find in collection by name.
+ *
+ * @param collection the collection
+ * @param name the name
+ * @return the i expression dm context
+ */
+ public static IExpressionDMContext findInCollectionByName(Collection<IExpressionDMContext> collection, String name) {
+ for (IExpressionDMContext context : collection) {
+ if (((IEDCExpression) context).getName().equals(name))
+ return context;
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the all child expressions.
+ *
+ * @param variable the variable
+ * @return the all child expressions
+ */
+ public static List<IExpressionDMContext> getAllChildExpressions(IExpressionDMContext variable) {
+
+ IEDCExpression variableDMC = (IEDCExpression) variable;
+ Expressions expressions = (Expressions) variableDMC.getExpressionsService();
+ if (expressions == null)
+ return Collections.emptyList();
+
+ List<IExpressionDMContext> kids = Arrays.<IExpressionDMContext>asList(
+ expressions.getLogicalSubExpressions(variableDMC));
+ return kids;
+ }
+
+ /**
+ * Gets the field accessor.
+ *
+ * @param type the type
+ * @return the field accessor
+ */
+ public static String getFieldAccessor(IType type) {
+ if (type instanceof IPointerType)
+ return "->"; //$NON-NLS-1$
+ return "."; //$NON-NLS-1$
+ }
+
+ /**
+ * Gets the member value.
+ *
+ * @param variable the variable
+ * @param type the type
+ * @param memberName the member name
+ * @return the member value
+ */
+ public static String getMemberValue(IExpressionDMContext variable, IType type, String memberName) {
+ return getMemberValue(variable, type, memberName, IExpressions.NATURAL_FORMAT);
+ }
+
+ /**
+ * Gets the member value.
+ *
+ * @param variable the variable
+ * @param type the type
+ * @param memberName the member name
+ * @param format the format
+ * @return the member value
+ */
+ public static String getMemberValue(IExpressionDMContext variable, IType type, String memberName, String format) {
+ IExpressions expressions = ((IEDCExpression)variable).getExpressionsService();
+ if (expressions == null)
+ return ""; //$NON-NLS-1$
+ IEDCExpression expression =
+ (IEDCExpression) expressions.createExpression(variable, variable.getExpression()
+ + FormatUtils.getFieldAccessor(type) + memberName);
+ FormattedValueDMContext fvc = expressions.getFormattedValueContext(expression, format);
+ return expression.getFormattedValue(fvc).getFormattedValue();
+ }
+
+ /**
+ * Gets the variable value.
+ *
+ * @param variable the variable
+ * @return the variable value
+ * @since 2.0
+ */
+ public static String getVariableValue(IExpressionDMContext variable) {
+ return getVariableValue(variable, IExpressions.NATURAL_FORMAT);
+ }
+
+ /**
+ * Gets the variable value.
+ *
+ * @param variable the variable
+ * @param format the format
+ * @return the variable value
+ * @since 2.0
+ */
+ public static String getVariableValue(IExpressionDMContext variable, String format) {
+ IExpressions expressions = ((IEDCExpression)variable).getExpressionsService();
+ FormattedValueDMContext fvc =
+ expressions.getFormattedValueContext(variable, format);
+ FormattedValueDMData formattedValue = ((IEDCExpression) variable).getFormattedValue(fvc);
+ return formattedValue.getFormattedValue();
+ }
+
+ /**
+ * Gets the unqualified type remove pointers.
+ *
+ * @param type the type
+ * @return the unqualified type remove pointers
+ */
+ public static IType getUnqualifiedTypeRemovePointers(IType type) {
+ IType unqualifiedType = TypeUtils.getStrippedType(type);
+ while (unqualifiedType instanceof IPointerType)
+ unqualifiedType = TypeUtils.getStrippedType(unqualifiedType.getType());
+ return unqualifiedType;
+ }
+
+ /**
+ * Gets the custom value converter.
+ *
+ * @param variable the variable
+ * @return the custom value converter
+ */
+ public static IVariableValueConverter getCustomValueConverter(IExpressionDMContext variable) {
+ IEDCExpression variableDMC = (IEDCExpression) variable;
+ variableDMC.evaluateExpression();
+ IType type = TypeUtils.getUnRefStrippedType(variableDMC.getEvaluatedType());
+ if (type instanceof IArrayDimensionType)
+ type = ((IArrayDimensionType)type).getArrayType();
+ return FormatExtensionManager.instance().getVariableValueConverter(type);
+ }
+
+ /**
+ * Get an address from an expression representing a pointer.
+ * @param value the evaluated value of an IEDCExpression
+ * @return the pointer address or <code>null</code>
+ */
+ public static IAddress getPointerValue(Number value) {
+ IAddress address = null;
+
+ if (value instanceof BigInteger) {
+ address = new Addr64((BigInteger) value);
+ } else {
+ address = new Addr32(value.longValue());
+ }
+ return address;
+ }
+
+ /**
+ * Gets the template type name.
+ *
+ * @param typeName the type name
+ * @param type the type
+ * @return the template type name
+ * @since 2.0
+ */
+ public static String getTemplateTypeName(String typeName, IType type) {
+ // TODO Fix this when type gives template information Bug 11443
+
+ ICompositeType composite = (ICompositeType) TypeUtils.getBaseType(type);
+ String baseName = composite.getBaseName();
+
+ Matcher m = Pattern.compile(typeName + "<(.+)>").matcher(baseName);
+ if (m.matches())
+ return m.group(1);
+
+ // check classes and structs it derives from
+ for (IInheritance inheritance : composite.getInheritances()) {
+ String templateTypeName = getTemplateTypeName(typeName, inheritance.getType());
+ if (templateTypeName != null)
+ return templateTypeName;
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the formatted value.
+ *
+ * @param variable the variable
+ * @return the formatted value
+ * @throws CoreException the core exception
+ * @since 2.0
+ */
+ public static String getFormattedValue(IExpressionDMContext variable) throws CoreException {
+ IVariableValueConverter valueConverter = getCustomValueConverter(variable);
+ if (valueConverter != null) {
+ return valueConverter.getValue(variable);
+ }
+ else
+ return getVariableValue(variable);
+ }
+
+ /**
+ * Gets the max number of children.
+ *
+ * @return the max number of children
+ * @since 2.0
+ */
+ public static int getMaxNumberOfChildren() {
+ return 200; // this seems like a good default
+ }
+
+ /**
+ * Evaluates the expression and throws a CoreException if there is an evaluation error.
+ *
+ * @param expression the expression
+ * @throws CoreException the core exception
+ * @since 2.0
+ */
+ public static void evaluateExpression(IEDCExpression expression) throws CoreException {
+ expression.evaluateExpression();
+ IStatus status = expression.getEvaluationError();
+ if ((status != null && !status.isOK()) || expression.getEvaluatedValue() == null) {
+ Throwable t = status != null ? status.getException() : null;
+ throw EDCDebugger.newDebugException("Error evaluating expression: " + expression.getExpression(), t);
+ }
+
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import java.util.Iterator;
+
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+
+
+/**
+ * An interface describing structure for a type
+ */
+public interface ITypeContentProvider {
+
+ /**
+ * Return an iterator starting at a start index generating the
+ * list of IExpressionDMContext for each of the direct children
+ * of the current object.
+ * @param variable IExpressionDMContext
+ * @return Iterator<IExpressionDMContext>
+ * @throws CoreException on errors
+ */
+ Iterator<IExpressionDMContext> getChildIterator(IExpressionDMContext variable) throws CoreException;
+
+ /**
+ * Return the number of children
+ * @param variable IExpressionDMContext
+ * @return int
+ * @throws CoreException on errors
+ * @since 2.0
+ */
+ int getChildCount(IExpressionDMContext variable) throws CoreException;
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * Interface for an extension providing custom formatting for variables
+ */
+public interface IVariableFormatProvider {
+ /**
+ * An optional structure to use for this type
+ * @param type IType
+ * @return ITypeContentProvider
+ */
+ ITypeContentProvider getTypeContentProvider(IType type);
+
+ /**
+ * An optional summary value to display in the value column for the current object.
+ * @param type IType
+ * @return IVariableValueConverter
+ */
+ IVariableValueConverter getVariableValueConverter(IType type);
+
+ /**
+ * An optional string to display in the detail pane when the variable is selected
+ * @param type IType
+ * @return IVariableValueConverter
+ */
+ IVariableValueConverter getDetailValueConverter(IType type);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.formatter;
+
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+
+/**
+ * Interface for converting displayed values and optionally editing converted value
+ */
+public interface IVariableValueConverter {
+ /**
+ * Return the formatted value.
+ * @param variable IExpressionDMContext
+ * @return String
+ * @throws CoreException any error on getting the value.
+ */
+ String getValue(IExpressionDMContext variable) throws CoreException;
+
+ /**
+ * Whether the value is editable.
+ * If false, {@link #setValue(String)} and {@link #getEditableValue(IExpressionDMContext)} may fail.
+ * @return boolean
+ */
+ boolean canEditValue();
+
+ /**
+ * Return the formatted value for editing.
+ * @param variable IExpressionDMContext
+ * @return String
+ * @throws CoreException any error on getting the value.
+ */
+ String getEditableValue(IExpressionDMContext variable) throws CoreException;
+
+ /**
+ * The value entered by the user to change the variable.
+ * @param variable IExpressionDMContext
+ * @param newValue String
+ * @throws CoreException any error on setting the value.
+ */
+ void setValue(IExpressionDMContext variable, String newValue) throws CoreException;
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+
+/**
+ * This implementation of IStreamBuffer works on an existing ByteBuffer.
+ */
+public class ByteBufferStreamBuffer extends StreamBufferBase {
+
+ private ByteBuffer buffer;
+
+
+ /**
+ * Wrap in-memory content.
+ * @param content
+ * @param order
+ */
+ public ByteBufferStreamBuffer(ByteBuffer buffer) throws IOException {
+ super(buffer.order(), 0, buffer.capacity());
+ this.buffer = buffer;
+ }
+ public ByteBufferStreamBuffer(ByteBuffer buffer, long position, long size) {
+ super(buffer.order(), position, size);
+ this.buffer = buffer;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.StreamBufferBase#fetchPage(byte[], int, int)
+ */
+ @Override
+ protected void fetchPage(byte[] buffer, long sourceOffset, int count) {
+ if (sourceOffset > Integer.MAX_VALUE)
+ throw new BufferUnderflowException();
+ this.buffer.position((int) sourceOffset);
+ this.buffer.get(buffer, 0, count);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.StreamBufferBase#createSubBuffer(long, long)
+ */
+ @Override
+ protected IStreamBuffer createSubBuffer(long offset, long size) {
+ return new ByteBufferStreamBuffer(buffer, offset, size);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import org.eclipse.cdt.scripting.ScriptingPlugin;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+
+@SuppressWarnings("restriction")
+public class EDCApplication implements IApplication {
+
+ boolean running;
+
+ public Object start(IApplicationContext context) throws Exception {
+
+ running = true;
+
+ IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(ScriptingPlugin.PLUGIN_ID);
+ prefs.putBoolean(ScriptingPlugin.SCRIPTING_ENABLED, true);
+
+ prefs = InstanceScope.INSTANCE.getNode(DebugPlugin.getUniqueIdentifier());
+ prefs.putBoolean(IInternalDebugCoreConstants.PREF_ENABLE_STATUS_HANDLERS, false);
+
+ ScriptingPlugin.getBundleContext();
+
+ while (running)
+ {
+ Thread.sleep(1000);
+ }
+
+ return null;
+ }
+
+ public void stop() {
+ running = false;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import org.eclipse.cdt.debug.edc.internal.formatter.FormatExtensionManager;
+import org.eclipse.cdt.debug.edc.internal.snapshot.Album;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+
+public class EDCDebugPreferenceInitializer extends
+ AbstractPreferenceInitializer {
+
+ @Override
+ public void initializeDefaultPreferences() {
+ IEclipsePreferences node = DefaultScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
+ node.putInt(Album.PREF_VARIABLE_CAPTURE_DEPTH, 5);
+ node.put(Album.PREF_CREATION_CONTROL, Album.CREATE_MANUAL);
+ node.putBoolean(FormatExtensionManager.VARIABLE_FORMATS_ENABLED, FormatExtensionManager.VARIABLE_FORMATS_ENABLED_DEFAULT);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import org.eclipse.cdt.debug.edc.ITCFServiceManager;
+import org.eclipse.cdt.debug.edc.MessageLogger;
+import org.eclipse.cdt.debug.edc.tcf.extension.services.ILogging;
+import org.eclipse.cdt.debug.edc.tcf.extension.services.ISettings;
+import org.eclipse.cdt.debug.edc.tcf.extension.services.LoggingProxy;
+import org.eclipse.cdt.debug.edc.tcf.extension.services.SettingsProxy;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.osgi.service.debug.DebugOptions;
+import org.eclipse.osgi.service.debug.DebugTrace;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.protocol.Protocol.ChannelOpenListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+
+public class EDCDebugger extends Plugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.cdt.debug.edc"; //$NON-NLS-1$
+
+ // The shared instance
+ private static EDCDebugger plugin;
+
+ /** Platform facility used to trace. Lock {@link #traceLock} before accessing. */
+ private volatile DebugTrace trace;
+
+ /** Serializes access to {@link #trace} */
+ private final String traceLock = new String("trace lock");
+
+ private ITCFServiceManager tcfServiceManager;
+
+ private PersistentCache cache;
+
+ /** This plugin, once activated */
+ private BundleContext context;
+
+ /**
+ * The constructor
+ */
+ public EDCDebugger() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.Plugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ this.context = context;
+ plugin = this;
+
+ // Validate our plugin ID constant
+ if (!getBundle().getSymbolicName().equals(PLUGIN_ID)) {
+ throw new IllegalStateException("PLUGIN_ID constant is not correct"); //$NON-NLS-1$
+ }
+
+ EDCTrace.init();
+
+ installChannelListener();
+ }
+
+ private void installChannelListener() {
+
+ Protocol.invokeLater(new Runnable() {
+
+ public void run() {
+ Protocol.addChannelOpenListener(new ChannelOpenListener() {
+
+ public void onChannelOpen(IChannel channel) {
+ // logging service
+ if (channel.getRemoteService(ILogging.NAME) != null)
+ channel.setServiceProxy(ILogging.class, new LoggingProxy(channel));
+ // settings service
+ if (channel.getRemoteService(ISettings.NAME) != null)
+ channel.setServiceProxy(ISettings.class, new SettingsProxy(channel));
+ //
+ }
+ });
+ };
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ if (cache != null)
+ cache.flushAll();
+ plugin = null;
+ if (tcfServiceManager != null)
+ ((TCFServiceManager) tcfServiceManager).shutdown();
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static EDCDebugger getDefault() {
+ return plugin;
+ }
+
+ public static BundleContext getBundleContext() {
+ return getDefault().getBundle().getBundleContext();
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public DebugTrace getTrace() {
+ synchronized (traceLock) {
+ if (trace == null) {
+ if (context == null) {
+ return null; // Sorry, can't help. Bundle hasn't been activated yet
+ }
+
+ ServiceTracker tracker = new ServiceTracker(context, DebugOptions.class.getName(), null);
+ tracker.open();
+ DebugOptions debugOptions = (DebugOptions)tracker.getService();
+ if (debugOptions != null) {
+ trace = debugOptions.newDebugTrace(getBundle().getSymbolicName());
+ }
+ tracker.close();
+ }
+ }
+ return trace;
+ }
+
+ public ITCFServiceManager getServiceManager() {
+ if (tcfServiceManager == null) {
+ tcfServiceManager = new TCFServiceManager();
+ }
+ return tcfServiceManager;
+ }
+
+ /**
+ * Utility method for creating a CoreException object with this EDC plugin
+ * ID.
+ *
+ * @param msg
+ * - error message.
+ * @param e
+ * - cause exception, can be null.
+ * @return a {@link CoreException} object.
+ */
+ public static CoreException newCoreException(String msg, Throwable t) {
+ if ((msg == null || msg.length() == 0) && t instanceof CoreException)
+ return new CoreException(((CoreException) t).getStatus());
+ else
+ return new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, msg, t));
+ }
+
+ /**
+ * Utility method for creating a CoreException object with this EDC plugin
+ * ID.
+ *
+ * @param msg
+ * - error message.
+ * @return a {@link CoreException} object.
+ */
+ public static CoreException newCoreException(String msg) {
+ return new CoreException(new Status(IStatus.ERROR, PLUGIN_ID, msg));
+ }
+
+ /**
+ * Utility method for creating a DebugException object with this EDC plugin
+ * ID.
+ *
+ * @param msg
+ * - error message.
+ * @param e
+ * - cause exception, can be null.
+ * @return a {@link DebugException} object.
+ */
+ public static DebugException newDebugException(String msg, Throwable t) {
+ if ((msg == null || msg.length() == 0) && t instanceof CoreException)
+ return new DebugException(((CoreException) t).getStatus());
+ else
+ return new DebugException(new Status(IStatus.ERROR, PLUGIN_ID, msg, t));
+ }
+
+ /**
+ * Utility method for creating a DebugException object with this EDC plugin
+ * ID.
+ *
+ * @param msg
+ * - error message.
+ * @return a {@link DebugException} object.
+ */
+ public static DebugException newDebugException(String msg) {
+ return new DebugException(new Status(IStatus.ERROR, PLUGIN_ID, msg));
+ }
+
+ public static MessageLogger getMessageLogger() {
+ return new MessageLogger() {
+ @Override
+ public String getPluginID() {
+ return PLUGIN_ID;
+ }
+
+ @Override
+ public Plugin getPlugin() {
+ return plugin;
+ }
+ };
+ }
+
+ /**
+ * Returns the unique identifier of this plugin.
+ */
+ public static String getUniqueIdentifier() {
+ return PLUGIN_ID;
+ }
+
+ public static IStatus dsfRequestFailedStatus(String message, Throwable exception) {
+ return new Status(IStatus.ERROR, PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, message, exception);
+ }
+
+ public PersistentCache getCache() {
+ if (cache == null) {
+ cache = new PersistentCache(getStateLocation().append("cached_data"));
+ }
+ return cache;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ * Freescale Semiconductor - Refactoring and improvements
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.osgi.service.debug.DebugTrace;
+
+/**
+ * Tracing of EDC code based on standard tracing mechanism in eclipse;
+ * <br>see
+ * <a href=http://wiki.eclipse.org/FAQ_How_do_I_use_the_platform_debug_tracing_facility%3F>
+ * How do I use the Platform debug tracing facility?</a>
+ */
+public class EDCTrace {
+
+ // The various tracing options. DEBUG_TRACE is a master shut-on/off valve
+ public static final String DEBUG_TRACE = "/debug";
+ public static final String RUN_CONTROL_TRACE = "/debug/runControl";
+ public static final String STACK_TRACE = "/debug/stack";
+ public static final String EXPRESSION_PARSE_TRACE = "/debug/expressionParse";
+ public static final String SYMBOL_READER_TRACE = "/debug/symbolReader";
+ public static final String SYMBOL_READER_VERBOSE_TRACE = "/debug/symbolReader/verbose";
+ public static final String VARIABLE_VALUE_TRACE = "/debug/variableValue";
+ public static final String BREAKPOINTS_TRACE = "/debug/breakpoints";
+ public static final String MEMORY_TRACE = "/debug/memory";
+ public static final String ACPM_TRACE = "/debug/acpm";
+ public static final String PERSISTENT_CACHE_TRACE = "/debug/persistentCache";
+
+ // In order to minimize trace overhead when tracing is off, we check these
+ // "globals". They are set at plugin initialization time. Note that they do
+ // not preclude dynamic toggling of the trace options. Toggling would
+ // require dedicated GUI in any case. We would just have to have a pref
+ // change listener that toggles the values of these fields (note that they
+ // are not 'final').
+ public static boolean DEBUG_TRACE_ON;
+ public static boolean RUN_CONTROL_TRACE_ON;
+ public static boolean STACK_TRACE_ON;
+ public static boolean EXPRESSION_PARSE_TRACE_ON;
+ public static boolean SYMBOL_READER_TRACE_ON;
+ public static boolean SYMBOL_READER_VERBOSE_TRACE_ON;
+ public static boolean VARIABLE_VALUE_TRACE_ON;
+ public static boolean BREAKPOINTS_TRACE_ON;
+ public static boolean MEMORY_TRACE_ON;
+ public static boolean ACPM_TRACE_ON;
+ public static boolean PERSISTENT_CACHE_TRACE_ON;
+
+ /**
+ * Returns whether the specific tracing option is on. The answer is based on
+ * the real-time state of options as managed by the platform, whereas our
+ * XXXXX_ON static fields provide the answer based on the state of the
+ * options at plugin initialization time. Since we currently provide the
+ * user no way to toggle the options after launching Eclipse, use of this
+ * method is a heavy and unnecessary alternative to just checking the static
+ * field--thus the private visibility.
+ */
+ private static boolean isOn(String option) {
+ return "true".equals(Platform.getDebugOption(EDCDebugger.PLUGIN_ID + option));
+ }
+
+ /**
+ * Sets up static booleans at plugin startup time for efficient trace checks.
+ */
+ public static void init() {
+ if ("true".equals(Platform.getDebugOption(EDCDebugger.PLUGIN_ID + "/debug"))) { //$NON-NLS-1$//$NON-NLS-2$
+ DEBUG_TRACE_ON = true;
+ RUN_CONTROL_TRACE_ON = isOn(EDCTrace.RUN_CONTROL_TRACE);
+ STACK_TRACE_ON = isOn(EDCTrace.STACK_TRACE);
+ EXPRESSION_PARSE_TRACE_ON = isOn(EDCTrace.EXPRESSION_PARSE_TRACE);
+ SYMBOL_READER_TRACE_ON = isOn(EDCTrace.SYMBOL_READER_TRACE);
+ SYMBOL_READER_VERBOSE_TRACE_ON = SYMBOL_READER_TRACE_ON && isOn(EDCTrace.SYMBOL_READER_VERBOSE_TRACE);
+ VARIABLE_VALUE_TRACE_ON = isOn(EDCTrace.VARIABLE_VALUE_TRACE);
+ BREAKPOINTS_TRACE_ON = isOn(EDCTrace.BREAKPOINTS_TRACE);
+ MEMORY_TRACE_ON = isOn(EDCTrace.MEMORY_TRACE);
+ ACPM_TRACE_ON = isOn(EDCTrace.ACPM_TRACE);
+ PERSISTENT_CACHE_TRACE_ON = isOn(EDCTrace.PERSISTENT_CACHE_TRACE);
+ }
+ }
+
+ static class NullDebugTrace implements DebugTrace {
+ public void trace(String option, String message) {}
+ public void trace(String option, String message, Throwable error) {}
+ public void traceDumpStack(String option) {}
+ public void traceEntry(String option) {}
+ public void traceEntry(String option, Object methodArgument) {}
+ public void traceEntry(String option, Object[] methodArguments) {}
+ public void traceExit(String option) {}
+ public void traceExit(String option, Object result) {}
+ };
+
+ private static DebugTrace sTrace;
+
+ public static String fixArg(Object argument) {
+ if (argument == null || sTrace instanceof NullDebugTrace)
+ return null;
+ return argument.toString().replaceAll("\\{", "[").replaceAll("\\}", "]");
+ }
+
+ public static String[] fixArgs(Object[] arguments) {
+ if (arguments == null || sTrace instanceof NullDebugTrace)
+ return null;
+ String[] args = new String[arguments.length];
+ for (int i = 0; i < arguments.length; i++) {
+ args[i] = fixArg(arguments[i]);
+ }
+ return args;
+ }
+
+ public static DebugTrace getTrace() {
+ if (sTrace == null) {
+ EDCDebugger activator = EDCDebugger.getDefault();
+ if (activator != null) {
+ sTrace = activator.getTrace();
+ }
+ else
+ sTrace = new NullDebugTrace();
+ }
+ return sTrace;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.eclipse.cdt.debug.core.executables.Executable;
+import org.eclipse.cdt.debug.core.executables.ExecutablesManager;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
+import org.eclipse.debug.core.sourcelookup.containers.AbstractSourceContainer;
+import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
+
+public class ExecutablesSourceContainer extends AbstractSourceContainer {
+
+ public static final String TYPE_ID = EDCDebugger.getUniqueIdentifier() + ".containerType.executables"; //$NON-NLS-1$
+
+ public Object[] findSourceElements(String name) throws CoreException {
+ IPath path = PathUtils.findExistingPathIfCaseSensitive(PathUtils.createPath(name));
+ // Now looking for the file in executable view.
+ //
+ // Between the SDK and target, the exact directory and file capitalization may differ.
+ //
+ // Inject required initial slash so we can confidently use String#endsWith() without
+ // matching, e.g. "/path/to/program.exe" with "ram.exe".
+ //
+ String slashAndLowerFileName = File.separator + path.lastSegment().toLowerCase();
+ String absoluteLowerPath = path.makeAbsolute().toOSString().toLowerCase();
+
+ // Note the 'wait=true' argument. We can wait now that this job does not lock the UI.
+ Collection<Executable> executables = ExecutablesManager.getExecutablesManager().getExecutables(true);
+ for (Executable e : executables) {
+ String p = e.getPath().makeAbsolute().toOSString().toLowerCase();
+ if (p.endsWith(absoluteLowerPath) || // stricter match first
+ p.endsWith(slashAndLowerFileName)) // then only check by name
+ {
+ return new LocalFileStorage[] { new LocalFileStorage(e.getPath().toFile()) };
+ }
+ }
+ return new Object[]{};
+ }
+
+ public String getName() {
+ return "Executables";
+ }
+
+ public ISourceContainerType getType() {
+ return getSourceContainerType( TYPE_ID );
+ }
+
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteOrder;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+
+/**
+ * This implementation of IStreamBuffer works on file content.
+ */
+public class FileStreamBuffer extends StreamBufferBase {
+ private final boolean DEBUG = false;
+ private RandomAccessFile file;
+
+
+ /**
+ * Wrap in-memory content.
+ * @param content
+ * @param order
+ */
+ public FileStreamBuffer(RandomAccessFile file, ByteOrder order) throws IOException {
+ super(order, 0, file.length());
+ this.file = file;
+ }
+ public FileStreamBuffer(RandomAccessFile file, ByteOrder order, long position, long size) {
+ super(order, position, size);
+ this.file = file;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.StreamBufferBase#fetchPage(byte[], int, int)
+ */
+ @Override
+ protected void fetchPage(byte[] buffer, long sourceOffset, int count) {
+ try {
+ if (DEBUG) System.out.print("Reading "+ sourceOffset + " x "+ count + "... ");
+ file.seek(sourceOffset);
+ file.read(buffer, 0, count);
+ if (DEBUG) System.out.println("done");
+ } catch (IOException e) {
+ BufferUnderflowException be = new BufferUnderflowException();
+ be.initCause(e);
+ throw be;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.StreamBufferBase#createSubBuffer(long, long)
+ */
+ @Override
+ protected IStreamBuffer createSubBuffer(long offset, long size) {
+ return new FileStreamBuffer(file, order, offset, size);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+
+/**
+ * Utilities used for portability between hosts.
+ */
+public class HostOS {
+ /** Is the host Windows? */
+ public static boolean IS_WIN32 = File.separatorChar == '\\';
+ /** Is the host some Unix variant? */
+ public static boolean IS_UNIX = File.separatorChar == '/';
+ /** Executable file extension */
+ public static final String EXE_EXT = IS_WIN32 ? ".exe" : "";
+
+ /**
+ * Ensure that the executable name mentioned is canonical for the machine.
+ * This only affects Windows, currently, ensuring that an ".exe" is attached.
+ * @param executablePath
+ * @return updated path
+ */
+ public static String canonicalizeExecutableName(String executable) {
+ if (IS_WIN32) {
+ IPath executablePath = new Path(executable);
+ String ext = executablePath.getFileExtension();
+ if (ext == null) {
+ executable += EXE_EXT;
+ }
+ }
+ return executable;
+ }
+
+ /**
+ * Scan the PATH variable and see if the given binary is visible on
+ * the PATH that will be used at runtime (with the default environment and overrides).
+ * @param pathValue the expected Path
+ * @param program
+ * @return IPath if program is on PATH, else <code>null</code>
+ */
+ public static IPath findProgramOnPath(String program, String pathValue) {
+
+ // be sure proper path/extension are present
+ program = HostOS.canonicalizeExecutableName(program);
+
+ IPath path = null;
+
+ IPath[] pathEntries = PathUtils.getPathEntries(pathValue);
+ for (IPath pathEntry : pathEntries) {
+ IPath testPath = pathEntry.append(program);
+ if (testPath.toFile().exists()) {
+ path = testPath;
+ break;
+ }
+ }
+
+ return path;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal;
+
+import org.eclipse.cdt.debug.edc.services.IEDCExecutionDMC;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.debug.core.model.MemoryByte;
+
+/**
+ * @author Administrator
+ * @since 2.0
+ *
+ */
+public interface IMemoryAccess {
+
+ /**
+ * Retrieves the memory address values as shown at the Hex Pane Memory
+ * View. Every cell has 4 bytes.
+ * @param contextId string representing the context of for the memory
+ * @param memoryAddress The memory address to get its values.
+ * @param length the amount of memory to retrieve
+ * @return An array of memory bytes starting from the memory address given.
+ * @throws Exception Any exception is propagated to the caller.
+ */
+ public MemoryByte[] getMemoryValues(final DsfSession session,
+ final IEDCExecutionDMC exe_dmc, final String memoryAddress,
+ final int length)
+ throws Exception;
+
+ /**
+ * Changes the memory value for the given memory address for the length of the array
+ * @param contextId string representing the context of for the memory
+ * @param memoryAddress The memory address which content will be changed.
+ * @param newMemoryValue The new value of the memory address content.
+ * @return True if the change was successful. False if the value couldn't be changed.
+ * @throws Exception Any exception will be propagated to the caller.
+ */
+ public boolean changeMemoryValue(final DsfSession session,
+ final IEDCExecutionDMC exe_dmc, final String memoryAddress,
+ final byte[] newMemoryValue)
+ throws Exception;
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.nio.ByteOrder;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+
+/**
+ * This implementation of IStreamBuffer works on memory content.
+ */
+public class MemoryStreamBuffer extends StreamBufferBase {
+
+ private byte[] content;
+
+
+ /**
+ * Wrap in-memory content.
+ * @param content
+ * @param order
+ */
+ public MemoryStreamBuffer(byte[] content, ByteOrder order) {
+ super(order, 0, content.length);
+ this.content = content;
+ }
+ public MemoryStreamBuffer(byte[] content, ByteOrder order, long position, long size) {
+ super(order, position, size);
+ this.content = content;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.StreamBufferBase#fetchPage(byte[], int, int)
+ */
+ @Override
+ protected void fetchPage(byte[] buffer, long sourceOffset, int count) {
+ System.arraycopy(content, (int) sourceOffset, buffer, 0, count);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.StreamBufferBase#createSubBuffer(long, long)
+ */
+ @Override
+ protected IStreamBuffer createSubBuffer(long offset, long size) {
+ return new MemoryStreamBuffer(content, order, offset, size);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
+
+public class NumberFormatUtils {
+
+ private static final String HEX_PREFIX = "0x"; //$NON-NLS-1$
+
+ private static final String OCTAL_PREFIX = "0"; //$NON-NLS-1$
+
+ private static final String BINARY_PREFIX = "0b"; //$NON-NLS-1$
+
+ private static final String SINGLE_QUOTE = "'"; //$NON-NLS-1$
+
+ private static final String DECIMAL_SUFFIX = " (Decimal)"; //$NON-NLS-1$
+
+ static public String toHexString(Number number) {
+ String str = null;
+ if (number instanceof Integer)
+ str = Integer.toHexString((Integer) number);
+ else if (number instanceof Long)
+ str = Long.toHexString((Long) number);
+ else if (number instanceof BigInteger)
+ str = ((BigInteger) number).toString(16);
+ else if (number instanceof Float)
+ str = Float.toHexString((Float) number);
+ else if (number instanceof Double)
+ str = Double.toHexString((Double) number);
+ if (str != null && !str.startsWith(HEX_PREFIX))
+ return HEX_PREFIX + str;
+ return str;
+ }
+
+ static public String toOctalString(Number number) {
+ String str = null;
+ if (number instanceof Integer)
+ str = Integer.toOctalString((Integer) number);
+ else if (number instanceof Long)
+ str = Long.toOctalString((Long) number);
+ else if (number instanceof BigInteger)
+ str = ((BigInteger) number).toString(8);
+ if (str != null && !str.startsWith(OCTAL_PREFIX))
+ str = OCTAL_PREFIX + str;
+ if (str == null && (number instanceof Float || number instanceof Double))
+ str = number.toString() + DECIMAL_SUFFIX;
+ return str;
+ }
+
+ static public String asBinary(Number number) {
+ String str = null;
+ if (number instanceof Integer)
+ str = Integer.toBinaryString((Integer) number);
+ else if (number instanceof Long)
+ str = Long.toBinaryString((Long) number);
+ else if (number instanceof BigInteger)
+ str = ((BigInteger) number).toString(2);
+ if (str != null && !str.startsWith(BINARY_PREFIX))
+ str = BINARY_PREFIX + str;
+ if (str == null && (number instanceof Float || number instanceof Double))
+ str = number.toString() + DECIMAL_SUFFIX;
+ return str;
+ }
+
+ static public String toCharString(Number number, IType valueType) {
+ int intValue = number.intValue();
+ String charVal = null;
+ if (intValue < 128) {
+ switch ((char) intValue) {
+ case 0:
+ charVal = ("\\0"); //$NON-NLS-1$
+ break;
+ case '\b':
+ charVal = ("\\b"); //$NON-NLS-1$
+ break;
+ case '\f':
+ charVal = ("\\f"); //$NON-NLS-1$
+ break;
+ case '\n':
+ charVal = ("\\n"); //$NON-NLS-1$
+ break;
+ case '\r':
+ charVal = ("\\r"); //$NON-NLS-1$
+ break;
+ case '\t':
+ charVal = ("\\t"); //$NON-NLS-1$
+ break;
+ case '\'':
+ charVal = ("\\'"); //$NON-NLS-1$
+ break;
+ case '\"':
+ charVal = ("\\\""); //$NON-NLS-1$
+ break;
+ case '\\':
+ charVal = ("\\\\"); //$NON-NLS-1$
+ break;
+ case 0xb:
+ charVal = ("\\v"); //$NON-NLS-1$
+ break;
+ }
+ }
+
+ // Show the numeric value (decimal for char, since it's short, and hex for wchar_t)
+ // then the character value. Note that at the system font may not be able to show
+ // all characters in the variables/expressions view, which is why we show the
+ // more meaningful numeric value before the possibly "boxy" character representation.
+ //
+ // Also, we assume wchar_t == Unicode.
+ boolean isWchart = (valueType instanceof ICPPBasicType
+ && ((ICPPBasicType) valueType).getBaseType() == ICPPBasicType.t_wchar_t)
+ || valueType.getName().equals("wchar_t"); //$NON-NLS-1$
+
+ StringBuilder info = new StringBuilder();
+
+ if (isWchart) {
+ info.append(HEX_PREFIX);
+ if (valueType.getByteSize() == 2)
+ info.append(String.format("%04X", intValue)); //$NON-NLS-1$
+ else
+ info.append(String.format("%08X", intValue)); //$NON-NLS-1$
+ info.append(" (L"); //$NON-NLS-1$
+ } else {
+ info.append("" + intValue); //$NON-NLS-1$
+ info.append(" ("); //$NON-NLS-1$
+ }
+
+ if (charVal == null) {
+ // treat chars as unsigned for getting the char representation
+ String fmt = "\\U%08X"; //$NON-NLS-1$
+ switch (valueType.getByteSize()) {
+ case 1:
+ fmt = "\\%03o"; //$NON-NLS-1$
+ intValue &= 0xff; break;
+ case 2:
+ fmt = "\\u%04X"; //$NON-NLS-1$
+ intValue &= 0xffff; break;
+ case 4:
+ // note: may still be too large to be legal
+ fmt = "\\U%08X"; //$NON-NLS-1$
+ intValue &= 0xffffffff; break;
+ }
+
+ boolean gotRepr = false;
+ try {
+ if (!Character.isISOControl(intValue)) {
+ char[] chars = Character.toChars(intValue);
+ info.append(asStringQuoted(new String(chars)));
+ gotRepr = true;
+ }
+ } catch (IllegalArgumentException e) {
+ // some character values are negative or outside the UCS range;
+ // these throw exceptions
+ }
+ if (!gotRepr) {
+ info.append(asStringQuoted(String.format(fmt, intValue)));
+ }
+ } else {
+ info.append(asStringQuoted(charVal));
+ }
+ info.append(')');
+
+ return info.toString();
+ }
+
+ static public String asStringQuoted(String val) {
+ StringBuilder sb = new StringBuilder(SINGLE_QUOTE);
+ sb.append(val);
+ sb.append(SINGLE_QUOTE);
+ return sb.toString();
+ }
+
+ static public BigInteger parseIntegerByFormat(String expressionValue, String formatId) {
+ int radix = 10;
+ if (IFormattedValues.HEX_FORMAT.equals(formatId)) {
+ if (expressionValue.startsWith(HEX_PREFIX))
+ expressionValue = expressionValue.substring(HEX_PREFIX.length());
+ radix = 16;
+ } else if (IFormattedValues.OCTAL_FORMAT.equals(formatId)) {
+ if (expressionValue.startsWith(OCTAL_PREFIX))
+ expressionValue = expressionValue.substring(OCTAL_PREFIX.length());
+ radix = 8;
+ } else if (IFormattedValues.BINARY_FORMAT.equals(formatId)) {
+ if (expressionValue.startsWith(BINARY_PREFIX))
+ expressionValue = expressionValue.substring(BINARY_PREFIX.length());
+ radix = 2;
+ } else if (IFormattedValues.NATURAL_FORMAT.equals(formatId)) {
+ if (expressionValue.startsWith(BINARY_PREFIX)) {
+ expressionValue = expressionValue.substring(BINARY_PREFIX.length());
+ radix = 2;
+ } else if (expressionValue.startsWith(OCTAL_PREFIX)) {
+ expressionValue = expressionValue.substring(OCTAL_PREFIX.length());
+ radix = 8;
+ } else if (expressionValue.startsWith(HEX_PREFIX)) {
+ expressionValue = expressionValue.substring(HEX_PREFIX.length());
+ radix = 16;
+ }
+ // else, decimal
+ }
+ try {
+ return new BigInteger(expressionValue, radix);
+ } catch (NumberFormatException e) {
+ // just return null
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+/**
+ * These utilities handle some common portability issues when dealing with
+ * (absolute) paths which may be in a format intended for another operating system.
+ * It also handles shortcomings in the org.eclipse.core.runtime.Path
+ * implementation, which is not able to construct a meaningful path from
+ * a Win32 path outside of Windows.
+ */
+public class PathUtils {
+
+ /**
+ * Convert a variable constructed blindly for a Win32 environment into
+ * Unix-like syntax. This is typically used for PATH or lists
+ * of paths where ';' is the entry separator and '\' is the
+ * path component separator.
+ * <p>
+ * NOTE: we assume that the entries in the
+ * path list are already legal Unix paths, but just with the
+ * wrong slash.
+ * @param env
+ * @return converted string
+ */
+ public static String convertPathListToUnix(String env) {
+ if (env == null) return null;
+ env = env.replaceAll(";", ":"); // entry separators
+ env = env.replaceAll("\\\\", "/"); // path separators
+ return env;
+ }
+
+ /**
+ * Convert a path constructed blindly for a Win32 environment into
+ * Unix-like syntax. <p>
+ * NOTE: we assume that the path is already a legal Unix path,
+ * but just with the wrong slash.
+ * @param file
+ * @return converted string
+ */
+ public static String convertPathToUnix(String file) {
+ if (file == null) return null;
+ // handle Windows slashes and canonicalize
+ file = file.replaceAll("\\\\", "/");
+ return file;
+ }
+
+ /**
+ * Convert a path which may be in Windows or Unix format to Windows format.
+ * NOTE: we assume that the path is already a legal path,
+ * but just with the wrong slash.
+ * @param file
+ * @return converted string
+ */
+ public static String convertPathToWindows(String file) {
+ if (file == null) return null;
+ file = file.replaceAll("/", "\\\\");
+ return file;
+ }
+
+ /**
+ * Convert a path which may be in Windows or Unix format to Windows format.
+ * NOTE: we assume that the path is already a legal path,
+ * but just with the wrong slash.
+ * @param file
+ * @return converted string
+ */
+ public static String convertPathToWindows(IPath path) {
+ return convertPathToWindows(path.toPortableString());
+ }
+
+ /**
+ * Convert a path which may be in the opposite slash format to the local slash format.
+ * NOTE: we assume that the path is already a legal path,
+ * but just with the wrong slash.
+ * @param file
+ * @return converted string
+ */
+ public static String convertPathToNative(String path) {
+ if (path == null) return null;
+ if (HostOS.IS_UNIX)
+ return path.replaceAll("\\\\", "/");
+ else
+ return path.replaceAll("/", "\\\\");
+ }
+
+ /**
+ * Create an IPath from a string which may be a Win32 path. <p>
+ * <p>
+ * ("new Path(...)" won't work in Unix when using a Win32 path: the backslash
+ * separator and the device notation are completely munged.)
+ * @param path
+ * @return converted string
+ */
+ public static IPath createPath(String path) {
+ if (path == null) return null;
+ boolean hasWindowsSlashes = path.contains("\\");
+ if (hasWindowsSlashes) {
+ // handle Windows slashes and canonicalize
+ path = path.replaceAll("\\\\", "/");
+ }
+
+ // also check for device or UNC
+ int idx = path.indexOf(":");
+ if (idx > 0) {
+ String device = path.substring(0, idx + 1);
+ path = path.substring(idx + 1);
+ return new Path(path).setDevice(device);
+ }
+ else {
+ // Cygwin or UNC path
+ if (path.startsWith("//") && !hasWindowsSlashes) {
+ String network;
+ idx = path.indexOf("/", 2);
+ if (idx > 0) {
+ network = path.substring(0, idx);
+ path = path.substring(idx);
+ } else {
+ network = path;
+ path = "";
+ }
+ return new Path(network, path).makeUNC(true);
+ }
+ }
+
+ // fallthrough
+ return new Path(path);
+ }
+
+ /**
+ * Get the PATH entries from the given path environment value or the
+ * system environment.
+ * @param pathValue the expected PATH/Path value, or <code>null</code> for the system value
+ * @return array of IPath, never <code>null</code>
+ */
+ public static IPath[] getPathEntries(String pathValue) {
+ String pathVar = null;
+ if (pathValue != null) {
+ pathVar = pathValue;
+ } else {
+ if (HostOS.IS_WIN32) {
+ // canonical name, plus fallback below
+ pathVar = System.getenv("Path"); //$NON-NLS-1$
+ }
+ if (pathVar == null) {
+ pathVar = System.getenv("PATH"); //$NON-NLS-1$
+ }
+ }
+
+ if (pathVar == null)
+ pathVar = "";
+
+ String pathSeparator = System.getProperty("path.separator");
+ String[] pathEntries = pathVar.split(pathSeparator);
+ IPath[] paths = new IPath[pathEntries.length];
+ for (int i = 0; i < pathEntries.length; i++) {
+ paths[i] = new Path(pathEntries[i]);
+ }
+ return paths;
+ }
+
+ /**
+ * If the filesystem is case sensitive, locate the file on the filesystem
+ * on the given path, by ignoring case sensitivity differences.
+ * This is needed on case-preserving but not case-insensitive filesystems.
+ * @param path
+ * @return path pointing to existing file (possibly with different case in components) or
+ * original path if there is no match
+ */
+ public static IPath findExistingPathIfCaseSensitive(IPath path) {
+ // case is insensitive already
+ if (HostOS.IS_WIN32)
+ return path;
+
+ if (path == null || !path.isAbsolute())
+ return path;
+
+ File pathFile = path.toFile();
+ if (pathFile.exists()) {
+ try {
+ return new Path(pathFile.getCanonicalPath());
+ } catch (IOException e) {
+ // should not happen
+ return path;
+ }
+ }
+
+
+ // start with the assumption that the path is mostly correct except for the
+ // last N segments.
+ IPath goodPath = Path.ROOT;
+ if (path.getDevice() != null)
+ goodPath = goodPath.setDevice(path.getDevice());
+
+ // if bad drive or no root (?!), just skip
+ if (!goodPath.toFile().exists())
+ return path;
+
+ for (int seg = path.segmentCount(); seg > 0; seg--) {
+ final IPath prefix = path.uptoSegment(seg - 1);
+
+ if (prefix.toFile().exists()) {
+ goodPath = prefix;
+ break;
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.append(goodPath.addTrailingSeparator().toOSString());
+
+ boolean failedLookup = false;
+
+ for (int seg = goodPath.segmentCount(); seg < path.segmentCount(); seg++) {
+ final String segment = path.segment(seg);
+
+ final String[] matches = { segment };
+
+ if (!failedLookup) {
+ File dir = new File(builder.toString());
+ if (!new File(dir, matches[0]).exists()) {
+ // component has wrong case; find the first one matching case-insensitively
+ String[] names = dir.list(new FilenameFilter() {
+
+ public boolean accept(File dir, String name) {
+ if (name.equalsIgnoreCase(segment)) {
+ matches[0] = name;
+ return true;
+ }
+ return false;
+ }
+ });
+
+ if (names.length == 0) {
+ // no matches! the rest of the path won't match either
+ failedLookup = true;
+ }
+ }
+ }
+ builder.append(matches[0]);
+ builder.append('/');
+ }
+
+ if (!path.hasTrailingSeparator() && builder.length() > 0 && builder.charAt(builder.length() - 1) == '/') {
+ builder.setLength(builder.length() - 1);
+ }
+ return new Path(builder.toString());
+ }
+
+ public static boolean isCaseSensitive() {
+ // Is the underlying file system case sensitive?
+ // This can actually be complex to determine and can even vary by volume
+ // but this is an OK general test for now.
+ if (HostOS.IS_UNIX)
+ return true;
+ return false;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IPath;
+
+public class PersistentCache {
+
+ private class CacheEntry {
+
+ private String identifier;
+ private long freshness;
+ private Serializable data;
+ private IPath location;
+
+ public CacheEntry(String identifier, Serializable data, long freshness) {
+ this.identifier = identifier;
+ this.freshness = freshness;
+ this.data = data;
+ this.location = getDefaultLocation().append(Integer.toString(identifier.hashCode())).addFileExtension("txt");;
+ }
+
+ public CacheEntry(ObjectInputStream ois) throws Exception {
+ this.identifier = (String) ois.readObject();
+ this.freshness = (Long) ois.readObject();
+ this.data = (Serializable) ois.readObject();
+ this.location = getDefaultLocation().append(Integer.toString(identifier.hashCode())).addFileExtension("txt");;
+ }
+
+ public IPath getLocation() {
+ return location;
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T getData(Class<T> expectedClass) {
+ if (expectedClass.isInstance(data))
+ return (T) data;
+ else
+ return null;
+ }
+
+ private long getFreshness() {
+ return freshness;
+ }
+
+ private void flush() throws Exception {
+ File cacheFile = getLocation().toFile();
+ if (!cacheFile.exists())
+ {
+ cacheFile.getParentFile().mkdirs();
+ cacheFile.createNewFile();
+ }
+ FileOutputStream fos = new FileOutputStream(cacheFile);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(identifier);
+ oos.writeObject(freshness);
+ oos.writeObject(data);
+ fos.close();
+ if (EDCTrace.PERSISTENT_CACHE_TRACE_ON) {
+ EDCTrace.getTrace().trace(null, "Cache flush: " + identifier + " data: " + data);
+ }
+ }
+
+ public void delete() {
+ File cacheFile = getLocation().toFile();
+ if (cacheFile.exists())
+ {
+ cacheFile.delete();
+ }
+ }
+
+ }
+
+ private Map<String, CacheEntry> caches = Collections.synchronizedMap(new HashMap<String, CacheEntry>());
+ private IPath defaultLocation;
+
+ public PersistentCache(IPath defaultLocation) {
+ this.defaultLocation = defaultLocation;
+ }
+
+ public CacheEntry getCache(String identifier){
+ CacheEntry result = caches.get(identifier);
+ return result;
+ }
+
+ public boolean hasCachedData(String cacheIdentifier) {
+ return caches.containsKey(cacheIdentifier);
+ }
+
+ synchronized public <T> T getCachedData(String cacheIdentifier, T expectedClass, long freshness) {
+ @SuppressWarnings("unchecked")
+ T result = (T) getCachedData(cacheIdentifier, expectedClass.getClass(), freshness);
+ if (result == null)
+ {
+ putCachedData(cacheIdentifier, (Serializable) expectedClass, freshness);
+ result = expectedClass;
+ }
+ return result;
+ }
+
+ synchronized public <T> T getCachedData(String cacheIdentifier, Class<T> expectedClass, long freshness) {
+ // freshness = 0;
+ CacheEntry cache = caches.get(cacheIdentifier);
+
+ if (cache == null)
+ {
+ cache = loadCachedData(getDefaultLocation(), cacheIdentifier);
+ if (cache != null)
+ caches.put(cacheIdentifier, cache);
+ }
+
+ if (cache != null)
+ {
+ long cachedFreshness = cache.getFreshness();
+ T result = cache.getData(expectedClass);
+ if (cachedFreshness == freshness && result != null)
+ {
+ if (EDCTrace.PERSISTENT_CACHE_TRACE_ON) {
+ EDCTrace.getTrace().trace(null, "Cache get data: " + cacheIdentifier + " data: " + result);
+ }
+ return result;
+ }
+ else
+ {
+ if (EDCTrace.PERSISTENT_CACHE_TRACE_ON) {
+ EDCTrace.getTrace().trace(null, "Stale data. cachedFreshness: " + cachedFreshness + " freshness: " + result + " cache: " + cache);
+ }
+ caches.remove(cache);
+ cache.delete();
+ }
+ }
+
+ return null;
+ }
+
+ private CacheEntry loadCachedData(IPath location, String cacheIdentifier) {
+ IPath flushPath = location.append(Integer.toString(cacheIdentifier.hashCode())).addFileExtension("txt");
+
+ if (flushPath.toFile().exists())
+ {
+ try {
+ final ClassLoader classLoader = EDCDebugger.getDefault().getClass().getClassLoader();
+ FileInputStream fis = new FileInputStream(flushPath.toFile());
+ ObjectInputStream ois = new ObjectInputStream(fis) {
+
+ @Override
+ protected Class<?> resolveClass(ObjectStreamClass desc)
+ throws IOException, ClassNotFoundException {
+ String name = desc.getName();
+ try {
+ return classLoader.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ return super.resolveClass(desc);
+ }
+ }};
+ return new CacheEntry(ois);
+ } catch (Exception e) {}
+ }
+
+ return null;
+ }
+
+ public void putCachedData(String cacheIdentifier, Serializable data, long freshness)
+ {
+ CacheEntry cache = new CacheEntry(cacheIdentifier, data, freshness);
+ caches.put(cacheIdentifier, cache);
+ if (EDCTrace.PERSISTENT_CACHE_TRACE_ON) {
+ EDCTrace.getTrace().trace(null, "Cache put data: " + cacheIdentifier + " data: " + data);
+ }
+ }
+
+ public void flushAll() {
+ Collection<CacheEntry> allCaches = caches.values();
+ for (CacheEntry entry : allCaches) {
+ try {
+ entry.flush();
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logException(e);
+ }
+ }
+ caches.clear();
+ }
+
+ public IPath getDefaultLocation() {
+ return defaultLocation;
+ }
+
+ public void setDefaultLocation(IPath defaultLocation) {
+ this.defaultLocation = defaultLocation;
+ }
+
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteOrder;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+
+/**
+ *
+ */
+public abstract class StreamBufferBase implements IStreamBuffer {
+ /* must be a power of 2 */
+ public static final int BUFFER_SIZE = 4096;
+
+ protected ByteOrder order;
+
+ // absolute
+ private long position;
+ // absolute
+ private long sourceCapacity;
+
+ private byte[] buffer;
+ // absolute source position in buffer[0]
+ private long sourceOffset;
+ // absolute source position in buffer[buffer.length]
+ private long sourceLimit;
+
+ // offset from source to position
+ private final long baseOffset;
+
+ /**
+ * Create a buffer over some source content
+ * @param order native byte order of content
+ * @param baseOffset base offset from source to this buffer
+ * @param capacity total size of the source (from baseOffset)
+ */
+ public StreamBufferBase(ByteOrder order, long baseOffset, long capacity) {
+ this.order = order;
+ this.baseOffset = baseOffset;
+ this.position = 0;
+ this.sourceCapacity = capacity;
+
+ this.buffer = new byte[BUFFER_SIZE];
+ this.sourceOffset = 0;
+ this.sourceLimit = 0;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " pos="+position() + " of "+ capacity() + " base="+ baseOffset; //$NON-NLS-N$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Fetch a page of content from the buffer.
+ * @param buffer the buffer
+ * @param sourceOffset absolute offset in original content
+ * @throws BufferUnderflowException
+ */
+ protected abstract void fetchPage(byte[] buffer, long sourceOffset, int count);
+
+ protected abstract IStreamBuffer createSubBuffer(long offset, long size);
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#wrapSubsection(int)
+ */
+ public IStreamBuffer wrapSubsection(long size) {
+ long availableSize = capacity() - position();
+ if (availableSize < size)
+ size = availableSize;
+ return createSubBuffer(position() + baseOffset, size);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#capacity()
+ */
+ public long capacity() {
+ return sourceCapacity;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#hasRemaining()
+ */
+ public boolean hasRemaining() {
+ return position < sourceCapacity;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#remaining()
+ */
+ public long remaining() {
+ return sourceCapacity - position;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#position()
+ */
+ public long position() {
+ return position;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#position(int)
+ */
+ public IStreamBuffer position(long newPosition) {
+ if (newPosition < 0 || newPosition > sourceCapacity)
+ throw new IllegalArgumentException(newPosition + " not in 0.."+ sourceCapacity);
+
+ this.position = newPosition;
+ return this;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#get(byte[], int, int)
+ */
+ public IStreamBuffer get(byte[] dst, int offset, int length) {
+ // read page-by-page if possible
+ while (length > 0) {
+ if (needFetch())
+ refetch();
+
+ int left = (int) Math.min(sourceLimit - position, length);
+ if (left > 0) {
+ System.arraycopy(buffer, (int) (position - sourceOffset), dst, offset, left);
+ offset += left;
+ position += left;
+ length -= left;
+ } else {
+ break;
+ }
+ }
+ return this;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#get(byte[])
+ */
+ public IStreamBuffer get(byte[] dst) {
+ return get(dst, 0, dst.length);
+ }
+
+ /**
+ * Fill memory buffer from source
+ */
+ protected void refetch() {
+ long newSourceOffset = position - (position & buffer.length - 1);
+ if (newSourceOffset < 0)
+ throw new BufferUnderflowException();
+ if (newSourceOffset >= sourceCapacity)
+ throw new BufferUnderflowException();
+
+ int toFetch = (int) Math.min(sourceCapacity - newSourceOffset, buffer.length);
+ fetchPage(buffer, newSourceOffset + baseOffset, toFetch);
+ sourceOffset = newSourceOffset;
+ sourceLimit = sourceOffset + toFetch;
+ }
+
+ protected final boolean needFetch() {
+ return (position < sourceOffset || position >= sourceLimit);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#get()
+ */
+ public byte get() {
+ if (needFetch())
+ refetch();
+
+ if (position < sourceCapacity)
+ return buffer[(int)((position++) - sourceOffset)];
+ else
+ throw new BufferUnderflowException();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#getChar()
+ */
+ public char getChar() {
+ return (char) getShort();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#getShort()
+ */
+ public short getShort() {
+ int a = get() & 0xff;
+ int b = get() & 0xff;
+ if (order == ByteOrder.LITTLE_ENDIAN)
+ return (short) (a | (b << 8));
+ else
+ return (short) (b | (a << 8));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#getInt()
+ */
+ public int getInt() {
+ int a = getShort() & 0xffff;
+ int b = getShort() & 0xffff;
+ if (order == ByteOrder.LITTLE_ENDIAN)
+ return a | (b << 16);
+ else
+ return b | (a << 16);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#getLong()
+ */
+ public long getLong() {
+ long a = getInt();
+ long b = getInt();
+ if (order == ByteOrder.LITTLE_ENDIAN)
+ return a | (b << 32);
+ else
+ return b | (a << 32);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.IStreamBuffer#skip(long)
+ */
+ public IStreamBuffer skip(long amount) {
+ return position(position() + amount);
+ }
+
+ public ByteOrder getOrder() {
+ return order;
+ }
+ public void setOrder(ByteOrder order) {
+ this.order = order;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.cdt.debug.edc.ITCFAgentLauncher;
+import org.eclipse.cdt.debug.edc.ITCFConnectionListener;
+import org.eclipse.cdt.debug.edc.ITCFServiceManager;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.tm.tcf.core.AbstractPeer;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IChannel.IChannelListener;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.tcf.protocol.IService;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.ILocator;
+import org.eclipse.tm.tcf.util.TCFTask;
+
+/**
+ * Utility class that provides access to TCF agents and services. It abstracts
+ * out the details of which agent provides the services, launching the agent if
+ * necessary, etc.
+ */
+public class TCFServiceManager implements ITCFServiceManager {
+
+ /**
+ * The IP addresses of the local machine. Typically, there's at least two
+ * (the loopback address is one of them), but there can be more if there are
+ * multiple network adapters (physical or virtual).
+ *
+ * <p>
+ * TODO: if you look at the TCF Java reference implementation, it updates
+ * its list every so often, as a system's network configuration can change
+ * during the life of a process. We should probably do that, too, though
+ * it's clearly an edge case.
+ */
+ private static List<String> localIPAddresses;
+
+ private List<ITCFAgentLauncher> tcfAgentLaunchers;
+
+ private static final String EXTENSION_POINT_NAME = "tcfAgentLauncher";
+
+ private List<ITCFAgentLauncher> launchedtcfAgentLaunchers;
+
+ private ListenerList peerChannelListeners = new ListenerList();
+
+ static {
+ // Record local host IP addresses--not only numeric IP addresses but
+ // also hostnames if available.
+ try {
+ localIPAddresses = getLocalIPAddresses();
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError("Problem getting local IP addresses", e); //$NON-NLS-1$
+ }
+ }
+
+ public TCFServiceManager() {
+ // load TCFAgentLauncher extensions
+ tcfAgentLaunchers = new ArrayList<ITCFAgentLauncher>();
+ launchedtcfAgentLaunchers = new ArrayList<ITCFAgentLauncher>();
+
+ IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
+ IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(EDCDebugger.PLUGIN_ID, EXTENSION_POINT_NAME);
+ IExtension[] extensions = extensionPoint.getExtensions();
+
+ for (IExtension extension : extensions) {
+ IConfigurationElement[] elements = extension.getConfigurationElements();
+ IConfigurationElement element = elements[0];
+
+ boolean failed = false;
+ CoreException exc = null;
+ try {
+ Object extObject = element.createExecutableExtension("class"); //$NON-NLS-1$
+ if (extObject instanceof ITCFAgentLauncher) {
+ tcfAgentLaunchers.add((ITCFAgentLauncher) extObject);
+ } else {
+ failed = true;
+ }
+ } catch (CoreException e) {
+ failed = true;
+ exc = e;
+ }
+
+ if (failed) {
+ EDCDebugger.getMessageLogger().logError(
+ "Unable to load " + EXTENSION_POINT_NAME + " extension from " + extension.getContributor().getName(), exc);
+ }
+ }
+
+ }
+
+ /**
+ * Returns true if <i>all</i> the attributes in [attributesToMatch] appear
+ * identically in [attributes] (keys and respective values). Basically, is
+ * [attributesToMatch] a subset of [attributes]?
+ */
+ public static boolean matchesAllAttributes(Map<String, String> attributes, Map<String, String> attributesToMatch) {
+ if (attributesToMatch.isEmpty())
+ return false;
+
+ for (String key : attributesToMatch.keySet()) {
+ if (!attributes.containsKey(key)) {
+ return false;
+ }
+ if (!attributesToMatch.get(key).equals(attributes.get(key))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if the given TCF peer is the LocalPeer defined in TCF. As that
+ * LocalPeer is not public, we check by its internal ID. It may not be
+ * forward compatible, but is there a better way ?
+ *
+ * @param p
+ * @return
+ */
+ public static boolean isInternalLocalPeer(IPeer p) {
+ assert Protocol.isDispatchThread();
+ return p.getID().equals("TCFLocal");
+ }
+
+ /**
+ * Find any registered TCF agent-launchers that will (should) produce a peer
+ * with the given attributes and that exposes the given service. The
+ * agent-launchers are registered through an EDC extension point.
+ *
+ * @param serviceName
+ * the required service
+ * @param attributesToMatch
+ * the required peer attributes
+ * @return zero or more agent-launchers that fit the bill
+ */
+ public ITCFAgentLauncher[] findSuitableAgentLaunchers(final String serviceName, final Map<String, String> attributesToMatch, boolean localAgentsOnly) {
+ List<String> registeredPeerLabels = new ArrayList<String>();
+ List<ITCFAgentLauncher> registeredAgents = new ArrayList<ITCFAgentLauncher>();
+
+ // Find registered agents that meets our need and which can be launched.
+
+ for (ITCFAgentLauncher descriptor : tcfAgentLaunchers) {
+ if (descriptor.getServiceNames().contains(serviceName)
+ && matchesAllAttributes(descriptor.getPeerAttributes(), attributesToMatch)
+ && descriptor.isLaunchable()) {
+ registeredPeerLabels.add(descriptor.getPeerName() + " (local registered non-started)");
+ registeredAgents.add(descriptor);
+ }
+ }
+ return registeredAgents.toArray(new ITCFAgentLauncher[registeredAgents.size()]);
+ }
+
+ public IPeer[] getRunningPeers(final String serviceName, final Map<String, String> attributesToMatch, final boolean localAgentsOnly) throws CoreException {
+ // first find running peers with matching attributes
+ final List<IPeer> runningCandidates1 = new ArrayList<IPeer>();
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ // This collection is only changed in TCF dispatcher thread.
+ // So don't worry about race condition.
+ Collection<IPeer> peers = Protocol.getLocator().getPeers().values();
+
+ for (IPeer p : peers) {
+ // Don't bother with internal local peer.
+ if (isInternalLocalPeer(p))
+ continue;
+
+ if (matchesAllAttributes(p.getAttributes(), attributesToMatch)) {
+ runningCandidates1.add(p);
+ }
+ }
+ }
+ });
+
+ // Now search the running candidates for the one that offers the
+ // required service.
+
+ final List<IPeer> runningCandidates2 = new ArrayList<IPeer>();
+ final List<String> runningLocalAgentPorts = new ArrayList<String>();
+
+ for (final IPeer peer : runningCandidates1) {
+
+ // wait up to 3 seconds for the asynchronous task.
+ TCFTask<Object> task = new TCFTask<Object>(3000) {
+ public void run() {
+ final boolean isLocalAgent = isInLocalAgent(peer);
+
+ /*
+ * If host has multiple IP addresses (e.g. 127.0.0.1 &
+ * 192.168.0.5), a local agent instance may be running on
+ * each of the addresses (see AgentServerTCP for more) but
+ * listening on the same port. In such case, we don't want
+ * to ask user to choose between those for local debug (it's
+ * annoying). So we'll just use first of them for local
+ * debug. Also note that different types of agents should
+ * not listen to the same port.
+ */
+ if (isLocalAgent) {
+ String port = peer.getAttributes().get(IPeer.ATTR_IP_PORT);
+ if (port != null) { // TCP/IP peer
+ if (runningLocalAgentPorts.contains(port)) {
+ // a local agent on the same port already exists (it
+ // must be of the same agent type), skip this one.
+ done(this);
+ return;
+ }
+ else
+ runningLocalAgentPorts.add(port);
+ }
+ }
+
+ IChannel ch = getChannelForPeer(peer);
+ if (ch != null) {
+ assert (ch.getState() == IChannel.STATE_OPEN);
+ if (null != ch.getRemoteService(serviceName)) {
+ // If the peer is on a local host, add it. If the
+ // peer is on another host, then whether we add
+ // it or not depends on the caller's wishes.
+ if (isLocalAgent || !localAgentsOnly)
+ runningCandidates2.add(peer);
+ }
+ done(this);
+ } else {
+ final IChannel channel = peer.openChannel();
+
+ IChannel.IChannelListener listener = new IChannel.IChannelListener() {
+ public void onChannelOpened() {
+ if (null != channel.getRemoteService(serviceName)) {
+ // If the peer is on this machine, add it. If the
+ // peer is on another machine, then whether we add
+ // it or not depends on the caller's wishes.
+ if (isLocalAgent || !localAgentsOnly)
+ runningCandidates2.add(peer);
+ }
+
+ fireConnectionOpened(peer, channel);
+
+ done(this); // argument is do-not-care
+ }
+
+ public void onChannelClosed(Throwable error) {
+ fireConnectionClosed(peer, channel, error);
+ channel.removeChannelListener(this);
+ }
+
+ public void congestionLevel(int level) {
+ }
+ };
+
+ channel.addChannelListener(listener);
+ }
+ }
+ };
+
+ try {
+ task.get();
+ } catch (Exception e) {
+ // Failed to find nor open channel to the peer, it must be a
+ // stale peer (a peer that dies but not removed from the TCF
+ // framework. See
+ // rg.eclipse.tm.internal.tcf.services.local.LocatorService.refresh_timer()).
+ // Dispose it so that it won't get in the way
+ // when we try to auto-launch the agent again.
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ try {
+ ((AbstractPeer) peer).dispose();
+ } catch (AssertionError e) {
+ // we were wrong; it is disposed
+ }
+ }
+ });
+ }
+ }
+
+ return runningCandidates2.toArray(new IPeer[runningCandidates2.size()]);
+ }
+
+ /**
+ * Determines whether the given peer is running in a local agent. We compare
+ * the IP address of the peer against the list of IP addresses for this
+ * machine (typically, there are at least two: the loopback address and the
+ * physical NIC).
+ */
+ public static boolean isInLocalAgent(IPeer peer) {
+ assert Protocol.isDispatchThread();
+ String ipHost = peer.getAttributes().get(IPeer.ATTR_IP_HOST);
+ return localIPAddresses.contains(ipHost);
+ }
+
+ public IChannel findOrCreateChannelForPeer(final IPeer peer) throws CoreException {
+ IChannel channel = null;
+
+ // First check if there is existing open channel to the peer.
+ //
+ channel = getChannelForPeer(peer);
+ if (channel != null)
+ return channel;
+
+ // Then try to open a channel to the peer.
+ //
+ /*
+ * Following will cause deadlock if called in TCF dispatcher thread as
+ * it will wait for an TCF even to finish in the dispatcher thread.
+ */
+ assert (!Protocol.isDispatchThread());
+
+ final WaitForResult<IChannel> waitForChannel = new WaitForResult<IChannel>();
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ try {
+ final IChannel newChannel = peer.openChannel();
+ newChannel.addChannelListener(new IChannelListener() {
+
+ public void onChannelOpened() {
+ waitForChannel.setData(newChannel);
+
+ fireConnectionOpened(peer, newChannel);
+ }
+
+ public void onChannelClosed(Throwable error) {
+ waitForChannel.handleException(error);
+ fireConnectionClosed(peer, newChannel, error);
+ newChannel.removeChannelListener(this);
+ }
+
+ public void congestionLevel(int level) {
+ }
+ });
+ }
+ catch (Throwable exc) {
+ waitForChannel.handleException(exc);
+ }
+ }
+ });
+
+ try {
+ channel = waitForChannel.get(15, TimeUnit.SECONDS);
+ } catch (ExecutionException e) {
+ throw EDCDebugger.newCoreException("Failed to open channel for " + peer.getID(), e);
+
+ } catch (Exception e) {
+ throw EDCDebugger.newCoreException("Time out getting open channel for peer.", e);
+ }
+
+ return channel;
+ }
+
+ /**
+ * Find existing open channel for the given peer.
+ *
+ * @param peer
+ * @return null if not found.
+ */
+ public IChannel getChannelForPeer(final IPeer peer) {
+
+ final IChannel[] ret = { null };
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ String peerName = peer.getName();
+ String peerID = peer.getID();
+
+ IChannel[] channels = Protocol.getOpenChannels();
+ for (IChannel channel : channels) {
+ IPeer remotePeer = channel.getRemotePeer();
+ if (remotePeer.getName().equals(peerName) && remotePeer.getID().equals(peerID)) {
+ ret[0] = channel;
+ return;
+ }
+ }
+ }
+ });
+
+ return ret[0];
+ }
+
+ /**
+ * Gets the service from the given TCF peer.
+ *
+ * @param peer
+ * TCF peer.
+ * @param serviceName
+ * the name of the service
+ * @return IService if the peer offers that service, null otherwise.
+ * @throws CoreException on error
+ */
+ public IService getPeerService(final IPeer peer, final String serviceName) throws CoreException {
+ final WaitForResult<IService> waitForService = new WaitForResult<IService>();
+
+ final IChannel channel = findOrCreateChannelForPeer(peer);
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ try {
+ IService service = channel.getRemoteService(serviceName);
+ if (service == null) {
+ // If the service is unavailable, set a dummy service
+ // object so the delegating thread doesn't end up
+ // pointlessly waiting
+ service = new IService() {
+ public String getName() {
+ return null;
+ }
+ };
+ }
+ waitForService.setData(service);
+ } catch (Exception e) {
+ waitForService.handleException(e);
+ }
+ }
+ });
+
+ try {
+ IService service = waitForService.get();
+ // check for the dummy service object
+ return (service.getName() == null) ? null : service;
+
+ } catch (Exception e) {
+ throw EDCDebugger.newCoreException("Fail to get TCF service [" + serviceName + "] from peer.", e);
+ }
+ }
+
+ /**
+ * Invokes an agent-launcher and waits (a while) for an agent to be
+ * discovered that meets the given peer attributes
+ *
+ * @param descriptor
+ * @return
+ * @throws CoreException
+ */
+ public IPeer launchAgent(final ITCFAgentLauncher descriptor, final Map<String, String> peerAttrs) throws CoreException {
+ final WaitForResult<IPeer> waitForPeer = new WaitForResult<IPeer>() {
+ };
+
+ final ILocator.LocatorListener listener = new ILocator.LocatorListener() {
+
+ public void peerRemoved(String id) {
+ }
+
+ public void peerHeartBeat(String id) {
+ }
+
+ public void peerChanged(IPeer peer) {
+ }
+
+ public void peerAdded(IPeer peer) {
+ if (matchesAllAttributes(peer.getAttributes(), peerAttrs)) {
+ waitForPeer.setData(peer);
+ }
+ }
+ };
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ // register ourselves as a listener
+ Protocol.getLocator().addListener(listener);
+ }
+ });
+
+ // launch the agent
+
+ IPeer launchedPeer = null;
+ try {
+ // Launch the agent (if it's not already running)
+ try {
+ descriptor.launch();
+ } catch (Exception e) {
+ throw EDCDebugger.newCoreException(MessageFormat.format("Failed to launch the TCF agent that hosts peer \"{0}\". Cause: {1}",
+ descriptor.getPeerName(), e.getLocalizedMessage()), e);
+ }
+
+ // Wait for the Locator listener we registered above to be notified
+ // of the existence of the peer we're interested in
+ try {
+ launchedPeer = waitForPeer.get();
+ } catch (Exception e) {
+ if (e.getCause() instanceof TimeoutException) {
+ throw EDCDebugger.newCoreException(MessageFormat.format("Timed out waiting for the launched TCF agent to make peer \"{0}\" available.",
+ descriptor.getPeerName()), null);
+ }
+ else {
+ throw EDCDebugger.newCoreException(MessageFormat.format("Error waiting for the launched TCF agent to make peer \"{0}\" available. Cause: {1}",
+ descriptor.getPeerName(), e.getLocalizedMessage()), e);
+ }
+ }
+ launchedtcfAgentLaunchers.add(descriptor);
+ }
+ finally {
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ // unregister our listener
+ Protocol.getLocator().removeListener(listener);
+ }
+ });
+ }
+
+
+ return launchedPeer;
+ }
+
+ private static List<String> getLocalIPAddresses() throws CoreException {
+ List<String> ret = new ArrayList<String>();
+
+ Enumeration<NetworkInterface> e;
+ try {
+ e = NetworkInterface.getNetworkInterfaces();
+ } catch (SocketException e1) {
+ throw EDCDebugger.newCoreException("Host is required to connect to a network but it isn't.");
+ }
+
+ while (e.hasMoreElements()) {
+ NetworkInterface f = e.nextElement();
+ Enumeration<InetAddress> n = f.getInetAddresses();
+ while (n.hasMoreElements()) {
+ InetAddress addr = n.nextElement();
+ ret.add(addr.getHostAddress());
+ }
+ }
+
+ // Support agents who use hostnames instead of numeric IP addresses
+ try {
+ InetAddress localHost = InetAddress.getLocalHost();
+ if (localHost != null) {
+ ret.add(localHost.getHostName());
+ ret.add(localHost.getCanonicalHostName());
+ }
+ } catch (UnknownHostException exc) {
+ EDCDebugger.getMessageLogger().logError("", exc);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Shutdown.
+ */
+ public void shutdown() {
+ // shutdown all agents that were launched by this manager
+ for (ITCFAgentLauncher desc : launchedtcfAgentLaunchers) {
+ try {
+ desc.shutdown();
+ } catch (Exception e) {
+ }
+ }
+ launchedtcfAgentLaunchers.clear();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.ITCFServiceManager#addChannelPeerListener(org.eclipse.cdt.debug.edc.ITCFConnectionListener)
+ */
+ public void addConnectionListener(ITCFConnectionListener listener) {
+ peerChannelListeners.add(listener);
+ }
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.ITCFServiceManager#removeChannelPeerListener(org.eclipse.cdt.debug.edc.ITCFConnectionListener)
+ */
+ public void removeConnectionListener(ITCFConnectionListener listener) {
+ peerChannelListeners.remove(listener);
+ }
+
+ protected void fireConnectionOpened(final IPeer peer, final IChannel channel) {
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ for (Object o : peerChannelListeners.getListeners()) {
+ try {
+ ((ITCFConnectionListener) o).peerChannelOpened(peer, channel);
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError("Exception thrown from connection listener", t);
+ }
+ }
+ }
+ });
+ }
+
+ protected void fireConnectionClosed(final IPeer peer, final IChannel channel, final Throwable exception) {
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ for (Object o : peerChannelListeners.getListeners()) {
+ try {
+ ((ITCFConnectionListener) o).peerChannelClosed(peer, channel, exception);
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError("Exception thrown from connection listener", t);
+ }
+ }
+ }
+ });
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class WaitForResult<V> implements Future<V> {
+
+ public static final long DEFAULT_WAIT_TIMEOUT_SECONDS = 10;
+ public static final long WAIT_INTERVAL_MILLIS = 50;
+ private boolean running;
+ private boolean canceled;
+ private boolean done;
+ private V data;
+ private volatile Throwable exception;
+
+ /* (non-Javadoc)
+ * @see java.util.concurrent.Future#cancel(boolean)
+ */
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ if (!running)
+ return false;
+ else {
+ canceled = true;
+ return true;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.concurrent.Future#get()
+ */
+ public V get() throws InterruptedException, ExecutionException {
+ try {
+ return get(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ throw new ExecutionException(e);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)
+ */
+ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+
+ long limitMillis = System.currentTimeMillis() + unit.toMillis(timeout);
+ running = true;
+ while (!canceled && (exception == null) && !hasResult()) {
+ Thread.sleep(WAIT_INTERVAL_MILLIS);
+ if (System.currentTimeMillis() > limitMillis)
+ throw new TimeoutException();
+ }
+ done = true;
+ running = false;
+
+ if (exception != null)
+ throw new ExecutionException(exception);
+
+ return data;
+ }
+
+ public void setData(V data) {
+ this.data = data;
+ }
+
+ public V getData() {
+ return data;
+ }
+
+ public boolean hasResult() {
+ return getData() != null;
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.concurrent.Future#isCancelled()
+ */
+ public boolean isCancelled() {
+ return canceled;
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.concurrent.Future#isDone()
+ */
+ public boolean isDone() {
+ return done;
+ }
+
+ public void handleException(Throwable e) {
+ this.exception = e;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import de.schlichtherle.io.ArchiveDetector;
+import de.schlichtherle.io.ArchiveException;
+import de.schlichtherle.io.DefaultArchiveDetector;
+import de.schlichtherle.io.File;
+import de.schlichtherle.io.FileInputStream;
+
+
+/**
+ * Provides a convenience wrapper around TrueZip and java.util.zip for read/write access to zip archives.
+ * The API under java.util.zip does not provide ability to update individual entries in archives
+ * and hence can be cumbersome and slow to use so TrueZip is added to ease this pain.
+ * <p>
+ * This wrapper also encapsulates the use of de.schlichtherle.io.File and only takes java.io.File
+ * as explicit arguments. When differentiating the two, only java.io.File must be explicit.
+ * <p>
+ * For more information see https://truezip.dev.java.net/
+ *
+ */
+public class ZipFileUtils {
+
+ /**
+ * Delete a file from an archive
+ * @param fileName - File name relative path in the archive
+ * @param zipFile - The zip file on disk
+ * @param extensions - File extension of the zip format archive
+ * @return true on success
+ */
+ public static boolean deleteFileFromZip(String fileName, java.io.File zipFile, String[] extensions){
+ String archiveFileName = zipFile + File.separator + fileName;
+
+ ArchiveDetector detector = getArchiveDetector(extensions);
+
+ File file = null;
+ if (detector != null){
+ file = new File(archiveFileName, detector);
+ } else {
+ file = new File(archiveFileName);
+ }
+
+ boolean success = file.delete();
+
+ unmount();
+
+ return success;
+ }
+
+ /**
+ * Delete a file from an archive
+ * @param fileName - File name relative path in the archive
+ * @param zipFile - File extension of the zip format archive
+ * @return true on success
+ *
+ * @see {@link deleteFileFromZip(String fileName, java.io.File zipFile, String[] extensions)}
+ */
+ public static boolean deleteFileFromZip(String fileName, java.io.File zipFile) {
+ return deleteFileFromZip(fileName, zipFile, null);
+ }
+
+ /**
+ * Copies a source file into a specified zip file. If the file exists it will be overwritten.
+ * @param src - The originating source to be copied
+ * @param zipFile - The destination for src
+ * @param extensions - File extension of the zip archive
+ * @return true on success
+ */
+ public static boolean addFileToZip(java.io.File src, java.io.File zipFile, String[] extensions){
+
+ boolean success = false;
+ ArchiveDetector detector = getArchiveDetector(extensions);
+
+ File toBeAddedFile = null;
+
+ if (detector != null){
+ toBeAddedFile = new File(src, detector);
+ success = toBeAddedFile.archiveCopyTo(new File(zipFile, toBeAddedFile.getName(), detector));
+ } else {
+ toBeAddedFile = new File(src);
+ success = toBeAddedFile.archiveCopyTo(new File(zipFile, toBeAddedFile.getName()));
+ }
+
+ unmount();
+
+ return success;
+ }
+
+ /**
+ * Copies a source file into a specified zip file. If the file exists it will be overwritten.
+ * @param src - The originating source to be copied
+ * @param zipFile - The destination for src
+ * @return true on success
+ *
+ * @see {@link addFileToZip(java.io.File src, java.io.File zipFile, String[] extensions)}
+ */
+ public static boolean addFileToZip(java.io.File srcFile, java.io.File zipFile) {
+ return addFileToZip(srcFile, zipFile, null);
+ }
+
+ /**
+ * Copies source file(s) into a specified zip file. If the file exists it will be overwritten.
+ * @param src - The originating sources to be copied
+ * @param zipFile - The destination for src
+ * @param extensions - File extension of the zip archive
+ * @return true on success
+ */
+ public static boolean addFilesToZip(java.io.File[] src, java.io.File zipFile, String[] extensions){
+
+ boolean success = false;
+ ArchiveDetector detector = getArchiveDetector(extensions);
+
+ for (java.io.File currSrcFile : src) {
+
+ try {
+
+ File toBeAddedFile = null;
+ if (detector != null) {
+
+ toBeAddedFile = new File(currSrcFile.getCanonicalFile(),
+ detector);
+
+ success = toBeAddedFile.archiveCopyTo(new File(zipFile,
+ toBeAddedFile.getName(), detector));
+ } else {
+ toBeAddedFile = new File(currSrcFile.getCanonicalFile());
+ success = toBeAddedFile.archiveCopyTo(new File(zipFile,
+ toBeAddedFile.getName()));
+ }
+
+ unmount();
+
+ } catch (ArchiveException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return success;
+ }
+
+ /**
+ * Copies source file(s) into a specified zip file. If the file exists it will be overwritten.
+ * @param src - The originating source to be copied
+ * @param zipFile - The destination for src
+ * @return true on success
+ *
+ * @see ZipFileUtils#addFilesToZip(java.io.File[], java.io.File, String[])
+ */
+ public static boolean addFilesToZip(java.io.File[] src, java.io.File zipFile) {
+ return addFilesToZip(src, zipFile, null);
+ }
+
+ /**
+ * TrueZip detects archive types by file extension and only has built in support for known types.
+ * If other file extensions are used that TrueZip does not have in it's default configuration
+ * it will complain that it does not recognize the archive type. Passing an array of extensions will
+ * allow TrueZip to recognize any arbitrary file extension as a zip archive. This only works for zip archvies.
+ *
+ * @param extensions - array of extension TrueZip should recognize as zip files.
+ * @return ArchiveDetector
+ */
+ private static ArchiveDetector getArchiveDetector(String[] extensions){
+ List<Object> zipFileExtensions = new ArrayList<Object>();
+ ArchiveDetector detector = null;
+ if (extensions != null && extensions.length > 0){
+ for (String ext : extensions){
+ zipFileExtensions.add(ext);
+ zipFileExtensions.add(new de.schlichtherle.io.archive.zip.ZipDriver());
+ }
+
+ detector = new DefaultArchiveDetector(ArchiveDetector.NULL,
+ zipFileExtensions.toArray());
+ }
+
+ return detector;
+ }
+
+ /**
+ *
+ * @param zipFileName
+ * @return
+ * @throws IOException
+ *
+ */
+ public static List<ZipEntry> listZipContents(String zipFileName) throws IOException{
+ List<ZipEntry> zipContents = new ArrayList<ZipEntry>();
+
+ ZipFile zipFile = new ZipFile(zipFileName);
+ Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
+ while (zipEntries.hasMoreElements()) {
+ zipContents.add(zipEntries.nextElement());
+ }
+
+ return zipContents;
+ }
+
+ /**
+ * Get a java.io.BufferedInputStream for reading 'src' from the specified 'zipFile'. Clients should make sure to call {@link ZipFileUtils#unmount()} when reading is complete.
+ * @param zipFile - Archive to read
+ * @param src - File to open for reading in the zipFile
+ * @param extensions - Extensions for zip file used if not standard
+ * @return
+ */
+ public static java.io.BufferedInputStream openFile(java.io.File zipFile, String src, String[] extensions){
+ ArchiveDetector detector = getArchiveDetector(extensions);
+ String archiveFileName = zipFile + File.separator + src;
+ try {
+ File.setDefaultArchiveDetector(detector);
+ FileInputStream fs = new FileInputStream(archiveFileName);
+ java.io.BufferedInputStream stream = new java.io.BufferedInputStream(fs);
+ return stream;
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * Close all input and output streams.
+ * Equivalent to {@link File#umount(boolean, boolean, boolean, boolean)
+ */
+ public static void unmount(){
+ try {
+ File.umount();
+ } catch (ArchiveException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static boolean createNewZip(java.io.File zipFileToCreate) {
+ boolean success = false;
+ if (zipFileToCreate.exists()){
+ return true;
+ }
+
+ if (!zipFileToCreate.getParentFile().exists()){
+ zipFileToCreate.mkdirs();
+ }
+
+ File f = new File(zipFileToCreate);
+
+ try {
+ success = f.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ unmount();
+ }
+
+ return success;
+ }
+
+ /**
+ * Only unzips files in zip file not directories
+ *
+ * @param zipped
+ * file
+ * @param destPath
+ * Destination path
+ * @return Files that were unzipped
+ */
+ public static List<File> unzipFiles(java.io.File zippedfile, String destPath, IProgressMonitor monitor)
+ throws FileNotFoundException, IOException {
+ ZipFile zipFile = new ZipFile(zippedfile);
+
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ List<File> outputFiles = new ArrayList<File>();
+ File destinationFile = new File(destPath);
+ if (!destinationFile.exists()) {
+ destinationFile.mkdirs();
+ }
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ File outputFile = new File(destinationFile, entry.getName());
+ if (entry.isDirectory() && !outputFile.exists()) {
+ outputFile.mkdirs();
+ continue;
+ }
+
+ if (!outputFile.getParentFile().exists()) {
+ outputFile.getParentFile().mkdirs();
+ }
+
+ java.io.InputStream inputStream = zipFile.getInputStream(entry);
+ java.io.FileOutputStream outStream = new java.io.FileOutputStream(outputFile);
+ copyByteStream(inputStream, outStream);
+
+ outputFiles.add(outputFile);
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+ outStream.close();
+ inputStream.close();
+ }
+ zipFile.close();
+ return outputFiles;
+ }
+
+ public static void copyByteStream(java.io.InputStream in, java.io.OutputStream out) throws IOException {
+ if (in != null && out != null) {
+ java.io.BufferedInputStream inBuffered = new java.io.BufferedInputStream(in);
+
+ int bufferSize = 1000;
+ byte[] buffer = new byte[bufferSize];
+
+ int readCount;
+
+ java.io.BufferedOutputStream fout = new java.io.BufferedOutputStream(out);
+
+ while ((readCount = inBuffered.read(buffer)) != -1) {
+ if (readCount < bufferSize) {
+ fout.write(buffer, 0, readCount);
+ } else {
+ fout.write(buffer);
+ }
+ }
+ fout.flush();
+ fout.close();
+ in.close();
+ }
+ }
+
+
+
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine;
+
+import org.eclipse.osgi.util.NLS;
+
+public class ASTEvalMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages"; //$NON-NLS-1$
+
+ public static String DivideByZero;
+ public static String UnhandledTypeCode;
+ public static String UnhandledSize;
+ public static String UnsupportedStringOperation;
+
+ public static String ASTEvaluationEngine_DidNotDetectType;
+
+ public static String ASTInstructionCompiler_InvalidNumber;
+
+ public static String ArraySubscript_ArrayHasNoBounds;
+ public static String ArraySubscript_CannotIndirectTemporary;
+ public static String ArraySubscript_ErrorDereferencingArray;
+ public static String ArraySubscript_MustSubscriptArray;
+ public static String ArraySubscript_ReadingPastEndOfString;
+ public static String ArraySubscript_SubscriptMustBeInteger;
+
+ public static String EvaluateID_CannotResolveName;
+ public static String EvaluateID_NameHasNoLocation;
+ public static String EvaluateID_VariableNotFound;
+
+ public static String FieldReference_InvalidPointerDeref;
+ public static String FieldReference_InvalidDotDeref;
+ public static String FieldReference_InvalidMember;
+ public static String FieldReference_AmbiguousMember;
+ public static String FieldReference_CannotDereferenceType;
+ public static String FieldReference_UnhandledOperandSize;
+
+ public static String GetValue_TypePromotionError;
+
+ public static String Instruction_CannotUseCompositeType;
+ public static String Instruction_EmptyStack;
+ public static String Instruction_UnhandledTypeCombination;
+
+ public static String OperandValue_CannotGetAddress;
+ public static String OperandValue_CannotReadUnspecifiedType;
+ public static String OperandValue_CannotReadVoid;
+ public static String OperandValue_UnhandledType;
+ public static String OperandValue_VariableNoAddress;
+
+ public static String OperatorAddrOf_RequiresVariable;
+ public static String OperatorAddrOf_NoRegister;
+ public static String OperatorAddrOf_NoBitField;
+
+ public static String OperatorCast_CannotCastString;
+
+ public static String OperatorIndirection_RequiresPointer;
+ public static String OperatorIndirection_NoBitField;
+ public static String OperatorIndirection_NoFunction;
+ public static String OperatorIndirection_UnhandledType;
+
+ public static String OperatorMinus_NonPtrMinusPtr;
+ public static String OperatorPlus_PtrPlusPtr;
+
+ public static String VariableWithValue_CannotLocateVariable;
+ public static String VariableWithValue_NoTwelveByteLongDouble;
+ public static String VariableWithValue_UnhandledType;
+ public static String VariableWithValue_UnknownLocation;
+ public static String VariableWithValue_VariableHasNoType;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, ASTEvalMessages.class);
+ }
+
+ private ASTEvalMessages() {
+ }
+}
--- /dev/null
+###############################################################################
+# Copyright (c) 2010 Nokia 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:
+# Nokia - Initial API and implementation
+###############################################################################
+DivideByZero=divide by zero
+ASTEvaluationEngine_DidNotDetectType=did not detect type
+ASTInstructionCompiler_InvalidNumber=invalid number format
+ArraySubscript_ArrayHasNoBounds=array type has no bounds
+ArraySubscript_CannotIndirectTemporary=cannot indirect temporary
+ArraySubscript_ErrorDereferencingArray=error dereferencing array
+ArraySubscript_MustSubscriptArray=only arrays or pointers may be subscripted
+ArraySubscript_ReadingPastEndOfString=reading past end of string
+ArraySubscript_SubscriptMustBeInteger=subscript not an integral type
+UnhandledSize=unhandled size {0}
+EvaluateID_CannotResolveName=cannot resolve {0}
+EvaluateID_NameHasNoLocation=no location found for {0}
+EvaluateID_VariableNotFound=''{0}'' not found
+FieldReference_InvalidPointerDeref=left operand of '->' not a pointer to a class, struct, or union
+FieldReference_InvalidDotDeref=left operand of '.' not a class, struct, or union, or reference to one
+FieldReference_InvalidMember=''{0}'' is an invalid class, struct, or union member
+FieldReference_AmbiguousMember=''{0}'' matches 2 or more members (possibly inherited) of ''{1}''
+FieldReference_CannotDereferenceType=cannot dereference this type
+FieldReference_UnhandledOperandSize=unhandled operand size
+GetValue_TypePromotionError=internal error: type promotion failure
+Instruction_CannotUseCompositeType=cannot use composite type in expression
+Instruction_EmptyStack=empty stack
+Instruction_UnhandledTypeCombination=unhandled type combination {0} and {1}
+UnhandledTypeCode=unhandled type code
+UnsupportedStringOperation=operation not supported on strings
+OperandValue_CannotGetAddress=cannot get address of {0}
+OperandValue_CannotReadUnspecifiedType=cannot read from unspecified type
+OperandValue_CannotReadVoid=cannot read from void
+OperandValue_UnhandledType=Unhandled type
+OperandValue_VariableNoAddress=variable has no address
+OperatorAddrOf_RequiresVariable=unary '&' requires a memory location
+OperatorAddrOf_NoRegister=cannot get memory address of the variable because it is in register
+OperatorAddrOf_NoBitField=cannot get memory address of a bit-field
+OperatorCast_CannotCastString=cannot cast string literal
+OperatorIndirection_RequiresPointer=unary '*' requires a pointer
+OperatorIndirection_NoBitField=unary '*' cannot be applied to a bit-field
+OperatorIndirection_NoFunction=cannot dereference a function pointer
+OperatorIndirection_UnhandledType=unhandled type {0}
+OperatorMinus_NonPtrMinusPtr=subtracting pointer from non-pointer
+OperatorPlus_PtrPlusPtr=adding two pointers
+VariableWithValue_CannotLocateVariable=Cannot locate variable
+VariableWithValue_NoTwelveByteLongDouble=12-byte long double not implemented
+VariableWithValue_UnhandledType=Unhandled type
+VariableWithValue_UnknownLocation=unknown location
+VariableWithValue_VariableHasNoType=Variable has no type
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.core.dom.ast.ASTVisitor;
+import org.eclipse.cdt.core.dom.ast.IASTProblem;
+import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
+import org.eclipse.cdt.core.dom.ast.IASTTypeId;
+import org.eclipse.cdt.core.dom.parser.ISourceCodeParser;
+import org.eclipse.cdt.core.dom.parser.c.GCCScannerExtensionConfiguration;
+import org.eclipse.cdt.core.dom.parser.cpp.GPPParserExtensionConfiguration;
+import org.eclipse.cdt.core.parser.FileContent;
+import org.eclipse.cdt.core.parser.IScanner;
+import org.eclipse.cdt.core.parser.IScannerInfo;
+import org.eclipse.cdt.core.parser.IncludeFileContentProvider;
+import org.eclipse.cdt.core.parser.NullLogService;
+import org.eclipse.cdt.core.parser.ParserLanguage;
+import org.eclipse.cdt.core.parser.ParserMode;
+import org.eclipse.cdt.core.parser.ScannerInfo;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.InstructionSequence;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.Interpreter;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.symbols.TypeEngine;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.internal.core.dom.parser.cpp.GNUCPPSourceParser;
+import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor;
+import org.eclipse.core.runtime.CoreException;
+
+@SuppressWarnings("restriction")
+public class ASTEvaluationEngine {
+
+ public static final String UNKNOWN_TYPE = "<UNKNOWN>"; //$NON-NLS-1$
+ private final EDCServicesTracker tracker;
+ private final IDMContext context;
+ private final TypeEngine typeEngine;
+
+ private static final Map<String, InstructionSequence> compiledExpressionsCache =
+ Collections.synchronizedMap(new HashMap<String, InstructionSequence>());
+
+ /**
+ * @param context
+ * @param tracker
+ *
+ */
+ public ASTEvaluationEngine(EDCServicesTracker tracker, IDMContext context, TypeEngine typeEngine) {
+ this.tracker = tracker;
+ this.context = context;
+ this.typeEngine = typeEngine;
+ }
+
+ public InstructionSequence getCompiledExpression(String expression) throws CoreException {
+
+ // the creation and parsing of the AST can get expensive so we cache it for
+ // the given expression
+ InstructionSequence instructions = compiledExpressionsCache.get(expression);
+ if (instructions == null) {
+ FileContent reader = FileContent.create("<edc-expression>", ("void* dummy_func() { return " + //$NON-NLS-1$ //$NON-NLS-2$
+ expression + " ; }").toCharArray()); //$NON-NLS-1$
+ IScannerInfo scannerInfo = new ScannerInfo(); // creates an empty scanner info
+ IScanner scanner = new CPreprocessor(reader, scannerInfo, ParserLanguage.CPP, new NullLogService(), GCCScannerExtensionConfiguration.getInstance(), IncludeFileContentProvider.getEmptyFilesProvider());
+ ISourceCodeParser parser = new GNUCPPSourceParser(scanner, ParserMode.COMPLETE_PARSE, new NullLogService(), GPPParserExtensionConfiguration.getInstance(), null);
+ IASTTranslationUnit ast = parser.parse();
+
+ ASTInstructionCompiler visitor = new ASTInstructionCompiler(expression);
+ ast.accept(visitor);
+
+ if (visitor.hasErrors())
+ throw EDCDebugger.newCoreException(visitor.getErrorMessage());
+
+ instructions = visitor.getInstructions();
+
+ // Remove NoOps
+ instructions.removeNoOps();
+
+ compiledExpressionsCache.put(expression, instructions);
+ }
+
+
+ // make a copy of the cached generic instruction sequence since we'll make
+ // context specific changes below (reduceCasts)
+ InstructionSequence sequence = new InstructionSequence(instructions);
+
+ // Reduce (possibly internally generated) cast expressions to avoid
+ // taking the address of a register or bitfield.
+ sequence.reduceCasts(typeEngine);
+
+ return sequence;
+
+ }
+
+ public Interpreter evaluateCompiledExpression(InstructionSequence expression) throws CoreException {
+ Interpreter interpreter = new Interpreter(tracker, context, typeEngine, expression);
+ interpreter.execute();
+ return interpreter;
+ }
+
+ /**
+ * Get the type engine
+ * @return
+ */
+ public TypeEngine getTypeEngine() {
+ return typeEngine;
+ }
+
+
+ static private class ASTTypeVisitor extends ASTVisitor {
+ private IASTTypeId theType;
+ private String errorMessage;
+
+ {
+ shouldVisitTypeIds = true;
+ shouldVisitProblems = true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTTypeId)
+ */
+ @Override
+ public int visit(IASTTypeId typeId) {
+ theType = typeId;
+ return PROCESS_ABORT;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTProblem)
+ */
+ @Override
+ public int visit(IASTProblem problem) {
+ errorMessage = problem.getMessage();
+ return PROCESS_ABORT;
+ }
+ }
+ /**
+ * Parse the given type string and get the AST tree for it
+ * @param type
+ * @return IASTTypeId instance
+ * @throws CoreException
+ */
+ public IASTTypeId getCompiledType(String type) throws CoreException {
+
+ FileContent reader = FileContent.create("<edc-expression>", ("void* dummy_func() { typeof(" + //$NON-NLS-1$ //$NON-NLS-2$
+ type + ") x; }").toCharArray()); //$NON-NLS-1$
+ IScannerInfo scannerInfo = new ScannerInfo(); // creates an empty scanner info
+ IScanner scanner = new CPreprocessor(reader, scannerInfo, ParserLanguage.CPP, new NullLogService(), GCCScannerExtensionConfiguration.getInstance(), IncludeFileContentProvider.getEmptyFilesProvider());
+ ISourceCodeParser parser = new GNUCPPSourceParser(scanner, ParserMode.COMPLETE_PARSE, new NullLogService(), GPPParserExtensionConfiguration.getInstance(), null);
+ IASTTranslationUnit ast = parser.parse();
+
+ ASTTypeVisitor visitor = new ASTTypeVisitor();
+ ast.accept(visitor);
+ if (visitor.errorMessage != null)
+ throw EDCDebugger.newCoreException(visitor.errorMessage);
+ if (visitor.theType == null)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.ASTEvaluationEngine_DidNotDetectType);
+
+ return visitor.theType;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine;
+
+import java.util.Stack;
+
+import org.eclipse.cdt.core.dom.ast.ASTVisitor;
+import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
+import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression;
+import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
+import org.eclipse.cdt.core.dom.ast.IASTCastExpression;
+import org.eclipse.cdt.core.dom.ast.IASTComment;
+import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
+import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
+import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
+import org.eclipse.cdt.core.dom.ast.IASTExpression;
+import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
+import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
+import org.eclipse.cdt.core.dom.ast.IASTInitializer;
+import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
+import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
+import org.eclipse.cdt.core.dom.ast.IASTProblem;
+import org.eclipse.cdt.core.dom.ast.IASTStatement;
+import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
+import org.eclipse.cdt.core.dom.ast.IASTTypeId;
+import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
+import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCastExpression;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.ArraySubscript;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.CompoundInstruction;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.EvaluateID;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.FieldReference;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.Instruction;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.InstructionSequence;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.NoOp;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorAddrOf;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorBinaryAnd;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorBinaryOr;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorBinaryXor;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorBitwiseNot;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorCast;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorCastValue;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorDivide;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorEquals;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorGreaterEqual;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorGreaterThan;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorIndirection;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorLessEqual;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorLessThan;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorLogicalAnd;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorLogicalNot;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorLogicalOr;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorMinus;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorModulo;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorMultiply;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorNotEquals;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorPlus;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorShiftLeft;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorShiftRight;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorUnaryMinus;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperatorUnaryPlus;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.PushBoolean;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.PushChar;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.PushDouble;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.PushFloat;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.PushLongOrBigInteger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.PushString;
+import org.eclipse.cdt.debug.edc.symbols.TypeEngine;
+import org.eclipse.cdt.internal.core.dom.parser.ASTAmbiguousNode;
+
+@SuppressWarnings("restriction")
+public class ASTInstructionCompiler extends ASTVisitor {
+
+ private final Stack<Instruction> stack;
+ private final InstructionSequence instructions;
+ private int counter;
+ private String errorMessage;
+ private boolean active = true;
+
+ public ASTInstructionCompiler( String snippet) {
+ super(true);
+ stack = new Stack<Instruction>();
+ instructions = new InstructionSequence(snippet);
+ }
+
+ private void push(Instruction i) {
+ stack.push(i);
+ }
+
+ private Instruction pop() {
+ return stack.pop();
+ }
+
+ /**
+ * Returns the instruction sequence generated by this AST instruction
+ * compiler
+ */
+ public InstructionSequence getInstructions() {
+ return instructions;
+ }
+
+ public boolean hasErrors() {
+ return errorMessage != null;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ private boolean isActive() {
+ return active;
+ }
+
+ private void storeInstruction() {
+ Instruction instruction = pop();
+ counter++;
+ if (instruction instanceof CompoundInstruction) {
+ ((CompoundInstruction) instruction).setEnd(counter);
+ }
+ instructions.add(instruction);
+ }
+
+ @Override
+ public int leave(IASTArrayModifier arrayModifier) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(arrayModifier)); }
+ return super.leave(arrayModifier);
+ }
+
+ @SuppressWarnings("deprecation") // we're simply wrapping a deprecated method
+ @Override
+ public int leave(IASTComment comment) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(comment)); }
+ return super.leave(comment);
+ }
+
+ @Override
+ public int leave(IASTDeclaration declaration) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(declaration)); }
+ return super.leave(declaration);
+ }
+
+ @Override
+ public int leave(IASTDeclarator declarator) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(declarator)); }
+ return super.leave(declarator);
+ }
+
+ @Override
+ public int leave(IASTDeclSpecifier declSpec) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(declSpec)); }
+ return super.leave(declSpec);
+ }
+
+ @Override
+ public int leave(IASTEnumerator enumerator) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(enumerator)); }
+ return super.leave(enumerator);
+ }
+
+ @Override
+ public int leave(IASTExpression expression) {
+ if (!isActive() || hasErrors())
+ return PROCESS_CONTINUE;
+ storeInstruction();
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(expression)); }
+ return super.leave(expression);
+ }
+
+ @Override
+ public int leave(IASTInitializer initializer) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(initializer)); }
+ return super.leave(initializer);
+ }
+
+ @Override
+ public int leave(IASTName name) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(name)); }
+ return super.leave(name);
+ }
+
+ @Override
+ public int leave(IASTParameterDeclaration parameterDeclaration) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(parameterDeclaration)); }
+ return super.leave(parameterDeclaration);
+ }
+
+ @Override
+ public int leave(IASTPointerOperator ptrOperator) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(ptrOperator)); }
+ return super.leave(ptrOperator);
+ }
+
+ @Override
+ public int leave(IASTProblem problem) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(problem)); }
+ return super.leave(problem);
+ }
+
+ @Override
+ public int leave(IASTStatement statement) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(statement)); }
+ return super.leave(statement);
+ }
+
+ @Override
+ public int leave(IASTTranslationUnit tu) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(tu)); }
+ return super.leave(tu);
+ }
+
+ @Override
+ public int leave(IASTTypeId typeId) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(typeId)); }
+ return super.leave(typeId);
+ }
+
+ @Override
+ public int visit(IASTArrayModifier arrayModifier) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(arrayModifier)); }
+ return super.visit(arrayModifier);
+ }
+
+ @SuppressWarnings("deprecation") // we're simply wrapping a deprecated method
+ @Override
+ public int visit(IASTComment comment) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(comment)); }
+ return super.visit(comment);
+ }
+
+ @Override
+ public int visit(IASTDeclaration declaration) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(declaration)); }
+ return super.visit(declaration);
+ }
+
+ @Override
+ public int visit(IASTDeclarator declarator) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(declarator)); }
+ return super.visit(declarator);
+ }
+
+ @Override
+ public int visit(IASTDeclSpecifier declSpec) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(declSpec)); }
+ return super.visit(declSpec);
+ }
+
+ @Override
+ public int visit(IASTEnumerator enumerator) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(enumerator)); }
+ return super.visit(enumerator);
+ }
+
+ @Override
+ public int visit(IASTInitializer initializer) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(initializer)); }
+ return super.visit(initializer);
+ }
+
+ @Override
+ public int visit(IASTParameterDeclaration parameterDeclaration) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg("visit: " + parameterDeclaration.getClass().getSimpleName())); } //$NON-NLS-1$
+ return super.visit(parameterDeclaration);
+ }
+
+ @Override
+ public int visit(IASTPointerOperator ptrOperator) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(ptrOperator)); }
+ return super.visit(ptrOperator);
+ }
+
+ @Override
+ public int visit(IASTStatement statement) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(statement)); }
+ return super.visit(statement);
+ }
+
+ @Override
+ public int visit(IASTTranslationUnit tu) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(tu)); }
+ return super.visit(tu);
+ }
+
+ @Override
+ public int visit(IASTTypeId typeId) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(typeId)); }
+ return super.visit(typeId);
+ }
+
+ @Override
+ public int visit(ASTAmbiguousNode astAmbiguousNode) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(astAmbiguousNode)); }
+ return super.visit(astAmbiguousNode);
+ }
+
+ @Override
+ public int visit(IASTName name) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(name)); }
+ return super.visit(name);
+ }
+
+ @Override
+ public int visit(IASTProblem problem) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(problem)); }
+ return super.visit(problem);
+ }
+
+ @Override
+ public int visit(IASTExpression expression) {
+ if (EDCTrace.EXPRESSION_PARSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(expression)); }
+
+ if (expression instanceof IASTLiteralExpression) {
+ visitLiteralExpression((IASTLiteralExpression) expression);
+ } else if (expression instanceof IASTBinaryExpression) {
+ visitBinaryExpression((IASTBinaryExpression) expression);
+ } else if (expression instanceof IASTUnaryExpression) {
+ visitUnaryExpression((IASTUnaryExpression) expression);
+ } else if (expression instanceof IASTIdExpression) {
+ visitIDExpression((IASTIdExpression) expression);
+ } else if (expression instanceof IASTArraySubscriptExpression) {
+ visitArraySubscriptExpression((IASTArraySubscriptExpression) expression);
+ } else if (expression instanceof IASTFieldReference) {
+ visitFieldReference((IASTFieldReference) expression);
+ } else if (expression instanceof IASTCastExpression) {
+ visitCastExpression((IASTCastExpression) expression);
+ } else
+ push(new NoOp(counter));
+
+ return super.visit(expression);
+ }
+
+ private void visitIDExpression(IASTIdExpression expression) {
+ push(new EvaluateID(expression));
+ }
+
+ private void visitArraySubscriptExpression(IASTArraySubscriptExpression expression) {
+ push(new ArraySubscript(counter));
+ }
+
+ private void visitFieldReference(IASTFieldReference expression) {
+ push(new FieldReference(expression, counter));
+ }
+
+ private void visitCastExpression(IASTCastExpression expression) {
+ if (expression.getOperator() == ICPPASTCastExpression.op_reinterpret_cast)
+ push(new OperatorCastValue(counter, expression));
+ else
+ push(new OperatorCast(counter, expression));
+ }
+
+ private void visitLiteralExpression(IASTLiteralExpression expression) {
+ int kind = expression.getKind();
+ String value = new String(expression.getValue());
+ switch (kind) {
+ case IASTLiteralExpression.lk_integer_constant:
+ try {
+ push(new PushLongOrBigInteger(value));
+ } catch (NumberFormatException nfe) {
+ errorMessage = ASTEvalMessages.ASTInstructionCompiler_InvalidNumber;
+ }
+ break;
+ case IASTLiteralExpression.lk_float_constant:
+ // check for explicitly float constant
+ try {
+ if (value.toUpperCase().endsWith("F")) //$NON-NLS-1$
+ push(new PushFloat(value));
+ else
+ push(new PushDouble(value));
+ } catch (NumberFormatException nfe) {
+ errorMessage = ASTEvalMessages.ASTInstructionCompiler_InvalidNumber;
+ }
+ break;
+ case IASTLiteralExpression.lk_char_constant:
+ try {
+ push(new PushChar(value));
+ } catch (NumberFormatException nfe) {
+ errorMessage = ASTEvalMessages.ASTInstructionCompiler_InvalidNumber;
+ }
+ break;
+ case IASTLiteralExpression.lk_string_literal:
+ push(new PushString(value));
+ break;
+ case IASTLiteralExpression.lk_false:
+ push(new PushBoolean(false));
+ break;
+ case IASTLiteralExpression.lk_true:
+ push(new PushBoolean(true));
+ break;
+ case IASTLiteralExpression.lk_this:
+ push(new EvaluateID("this")); //$NON-NLS-1$
+ break;
+ default:
+ push(new NoOp(counter));
+ }
+ }
+
+ private void visitBinaryExpression(IASTBinaryExpression expression) {
+ int op = expression.getOperator();
+
+ switch (op) {
+ case IASTBinaryExpression.op_binaryAnd:
+ push(new OperatorBinaryAnd(counter));
+ break;
+
+ case IASTBinaryExpression.op_binaryOr:
+ push(new OperatorBinaryOr(counter));
+ break;
+
+ case IASTBinaryExpression.op_binaryXor:
+ push(new OperatorBinaryXor(counter));
+ break;
+
+ case IASTBinaryExpression.op_plus:
+ push(new OperatorPlus(counter));
+ break;
+
+ case IASTBinaryExpression.op_minus:
+ push(new OperatorMinus(counter));
+ break;
+
+ case IASTBinaryExpression.op_multiply:
+ push(new OperatorMultiply(counter));
+ break;
+
+ case IASTBinaryExpression.op_divide:
+ push(new OperatorDivide(counter));
+ break;
+
+ case IASTBinaryExpression.op_modulo:
+ push(new OperatorModulo(counter));
+ break;
+
+ case IASTBinaryExpression.op_shiftLeft:
+ push(new OperatorShiftLeft(counter));
+ break;
+
+ case IASTBinaryExpression.op_shiftRight:
+ push(new OperatorShiftRight(counter));
+ break;
+
+ case IASTBinaryExpression.op_equals:
+ push(new OperatorEquals(counter));
+ break;
+
+ case IASTBinaryExpression.op_notequals:
+ push(new OperatorNotEquals(counter));
+ break;
+
+ case IASTBinaryExpression.op_greaterEqual:
+ push(new OperatorGreaterEqual(counter));
+ break;
+
+ case IASTBinaryExpression.op_greaterThan:
+ push(new OperatorGreaterThan(counter));
+ break;
+
+ case IASTBinaryExpression.op_lessEqual:
+ push(new OperatorLessEqual(counter));
+ break;
+
+ case IASTBinaryExpression.op_lessThan:
+ push(new OperatorLessThan(counter));
+ break;
+
+ case IASTBinaryExpression.op_logicalAnd:
+ push(new OperatorLogicalAnd(counter));
+ break;
+
+ case IASTBinaryExpression.op_logicalOr:
+ push(new OperatorLogicalOr(counter));
+ break;
+
+ default:
+ push(new NoOp(counter));
+ }
+ }
+
+ private void visitUnaryExpression(IASTUnaryExpression expression) {
+ int op = expression.getOperator();
+
+ switch (op) {
+ case IASTUnaryExpression.op_minus:
+ push(new OperatorUnaryMinus(counter));
+ break;
+
+ case IASTUnaryExpression.op_not:
+ push(new OperatorLogicalNot(counter));
+ break;
+
+ case IASTUnaryExpression.op_plus:
+ push(new OperatorUnaryPlus(counter));
+ break;
+
+ case IASTUnaryExpression.op_tilde:
+ push(new OperatorBitwiseNot(counter));
+ break;
+
+ case IASTUnaryExpression.op_star:
+ push(new OperatorIndirection(counter));
+ break;
+
+ case IASTUnaryExpression.op_amper:
+ push(new OperatorAddrOf(counter));
+ break;
+
+ default:
+ push(new NoOp(counter));
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.Type;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+
+// Internal expression type to hold all dimensions of a multidimensional array except the smallest.
+// E.g., for "int a[6][7][8];", this type might hold info about "a[2]" or "a[2][4]", but not "a[2][4][3]".
+public class ArrayDimensionType extends Type implements IArrayDimensionType {
+
+ private final OperandValue value; // needed for scope,
+ // frame, services
+ // tracker, etc.
+ private final IArrayType arrayType;
+ private IVariableLocation location;
+ private int dimensionCount; // number of dimensions processed so far
+
+ public ArrayDimensionType(String name, OperandValue value, IArrayType arrayType, IVariableLocation location) {
+ super(name, null, 0, null);
+ this.value = value;
+ this.arrayType = arrayType;
+ this.location = location;
+ this.dimensionCount = 1;
+ }
+
+ public OperandValue getOperandValue() {
+ return this.value;
+ }
+
+ public IArrayType getArrayType() {
+ return this.arrayType;
+ }
+
+ public IVariableLocation getLocation() {
+ return this.location;
+ }
+
+ public int getDimensionCount() {
+ return this.dimensionCount;
+ }
+
+ public void addDimension(long subscript, long increase) {
+ this.name += "[" + subscript + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ this.location = location.addOffset(increase);
+ this.dimensionCount++;
+ }
+
+ @Override
+ public IType getType() {
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+import java.text.MessageFormat;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.IAggregate;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IEnumeration;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ITypedef;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.debug.edc.symbols.VariableLocationFactory;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Array subscript instruction
+ */
+public class ArraySubscript extends CompoundInstruction {
+
+ /**
+ * Constructor for array subscript instruction
+ *
+ * @param expression
+ * - array subscript expression
+ * @param start
+ * - instruction start
+ */
+ public ArraySubscript(int start) {
+ super(start);
+ }
+
+ /**
+ * Resolve an array subscript expression
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue subscriptOperand = popValue();
+ OperandValue variableOperand = popValue();
+
+ long subscript = 0;
+
+ if (subscriptOperand.isFloating())
+ throw EDCDebugger.newCoreException(ASTEvalMessages.ArraySubscript_SubscriptMustBeInteger);
+
+ subscript = subscriptOperand.getLongValue();
+
+ IType variableType = TypeUtils.getStrippedType(variableOperand.getValueType());
+
+ IArrayType arrayType;
+ IVariableLocation location = null;
+ IType arrayElementType;
+ int byteSize;
+
+ if (variableType instanceof IArrayDimensionType)
+ arrayElementType = TypeUtils.getStrippedType(((IArrayDimensionType) variableType).getArrayType().getType());
+ else
+ arrayElementType = TypeUtils.getStrippedType(variableType.getType());
+ if (arrayElementType == null)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.ArraySubscript_MustSubscriptArray);
+
+ if (variableType instanceof IArrayType)
+ byteSize = variableType.getByteSize();
+ else
+ byteSize = arrayElementType.getByteSize();
+
+
+ IVariableLocation varLocation = variableOperand.getValueLocation();
+ if (varLocation == null) {
+ // may be a string...
+ String stringValue = variableOperand.getStringValue();
+ if (stringValue != null) {
+ if (subscript < stringValue.length()) {
+ pushNewValue(arrayElementType, (int) stringValue.charAt((int) subscript));
+ } else if (subscript == stringValue.length()) {
+ pushNewValue(arrayElementType, 0);
+ } else {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.ArraySubscript_ReadingPastEndOfString);
+ }
+ return;
+ }
+ throw EDCDebugger.newCoreException(ASTEvalMessages.ArraySubscript_CannotIndirectTemporary);
+ }
+
+ // If the variable type is just a pointer, then add the pointer base type's size
+ //
+ // *(ptr+element)
+ if (variableType instanceof IPointerType) {
+ IPointerType pointerType = (IPointerType) variableType;
+
+ // dereference ptr
+ BigInteger ptrValue = varLocation.readValue(pointerType.getByteSize());
+
+ // point into array
+ location = VariableLocationFactory.createMemoryVariableLocation(
+ fInterpreter.getServicesTracker(), fInterpreter.getContext(),
+ ptrValue.add(BigInteger.valueOf(byteSize * subscript)));
+
+ // dereference to fetch offset
+ OperandValue op = new OperandValue(pointerType.getType());
+
+ op.setValueLocation(VariableLocationFactory.createMemoryVariableLocation(
+ fInterpreter.getServicesTracker(), fInterpreter.getContext(),
+ location.getAddress().getValue()));
+
+ // read actual value
+ Number newValue = op.getValueByType(op.getValueType(), op.getValueLocation());
+ op.setValue(newValue);
+ push(op);
+
+ return;
+
+ }
+
+ // if the variable is an IArrayType, there are two cases:
+ // we're accessing a single element of a one dimensional array
+ // we're accessing an entire dimension of a multidimensional array
+ if (variableType instanceof IArrayType) {
+
+ arrayType = (IArrayType) variableType;
+
+ if (arrayType.getBoundsCount() == 0) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.ArraySubscript_ArrayHasNoBounds);
+ }
+
+ // find the location of indexed 1st dimension element, or of entire
+ // dimension
+ location = varLocation.addOffset(arrayType.getBounds()[0].getElementCount() * byteSize
+ * subscript);
+
+ if (arrayType.getBoundsCount() == 1) {
+ // we're accessing a single element of a one dimensional array
+ pushArrayElement(variableOperand, location, arrayElementType);
+ } else {
+ String name = variableOperand.getValueType().getName() + "[" + subscript + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ ArrayDimensionType arrayDimensionType = new ArrayDimensionType(name, variableOperand, arrayType,
+ location);
+ OperandValue opValue = new OperandValue(arrayDimensionType);
+
+ opValue.setAddressValue(location);
+ opValue.setValueLocation(location);
+ push(opValue);
+ }
+ return;
+ }
+
+ if (!(variableType instanceof IArrayDimensionType)) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.ArraySubscript_MustSubscriptArray);
+ }
+
+ // if the variable is an ArrayDimensionType, there are two cases:
+ // we're accessing a single element of a multidimensional array
+ // we're accessing another entire dimension of a multidimensional array
+ ArrayDimensionType arrayDimension = (ArrayDimensionType) variableType;
+ arrayType = arrayDimension.getArrayType();
+ arrayElementType = TypeUtils.getStrippedType(arrayType.getType());
+
+ byteSize = arrayElementType.getByteSize();
+ if (arrayElementType instanceof ITypedef) {
+ byteSize = TypeUtils.getStrippedType(arrayElementType.getType()).getByteSize();
+ }
+
+ arrayDimension.addDimension(subscript, arrayType.getBound(arrayDimension.getDimensionCount()).getElementCount()
+ * byteSize * subscript);
+ location = arrayDimension.getLocation();
+
+ if (arrayDimension.getDimensionCount() >= arrayType.getBoundsCount()) {
+ // we're accessing a single element of a multidimensional array
+ pushArrayElement(arrayDimension.getOperandValue(), location, arrayElementType);
+ } else {
+ // we're accessing another entire dimension of a multidimensional
+ // array
+ variableOperand.setAddressValue(location);
+ variableOperand.setValueLocation(location);
+ push(variableOperand);
+ }
+ }
+
+ private void pushArrayElement(OperandValue originalVariableValue, IVariableLocation location, IType arrayElementType) throws CoreException {
+ OperandValue varValue = new OperandValue(arrayElementType);
+
+ varValue.setValueLocation(location);
+
+ // for a lvalues (base arithmetic types, enums, and pointers), read the
+ // value and cast it to the right type
+ if (arrayElementType instanceof IBasicType || arrayElementType instanceof IEnumeration
+ || arrayElementType instanceof IPointerType) {
+ int byteSize = arrayElementType.getByteSize();
+
+ // TODO support 12-byte long double
+ if (byteSize != 1 && byteSize != 2 && byteSize != 4 && byteSize != 8) {
+ throw EDCDebugger.newCoreException(MessageFormat.format(ASTEvalMessages.UnhandledSize, byteSize));
+ }
+
+ // read the value pointed to
+ Number newValue = originalVariableValue.getValueByType(arrayElementType, location);
+ varValue.setValue(newValue);
+ push(varValue);
+
+ } else if (arrayElementType instanceof IAggregate) {
+ // for aggregates, the address of the aggregate is the value
+ // returned
+ varValue.setAddressValue(location);
+ push(varValue);
+
+ } else {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.ArraySubscript_ErrorDereferencingArray);
+ }
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary logical operator, such as "<"
+ */
+public abstract class BinaryLogicalOperator extends CompoundInstruction {
+
+ /**
+ * Constructor for a binary logical operator, such as "<"
+ *
+ * @param resultId - for assignment, variable ID of the result
+ * @param isAssignmentOperator - whether the result is assigned
+ * @param start - instruction start
+ */
+ protected BinaryLogicalOperator(int resultId, boolean isAssignmentOperator, int start) {
+ super(start);
+ }
+
+ /**
+ * Resolve a binary logical operator, such as "<"
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue right = popValue();
+ OperandValue left = popValue();
+
+ right = convertForPromotion(right);
+ left = convertForPromotion(left);
+
+ int promotedType = getJavaBinaryPromotionType(right, left);
+ IType type = fInterpreter.getTypeEngine().getBooleanType(1);
+
+ switch (promotedType) {
+ case T_String:
+ pushNewValue(type, getStringResult(GetValue.getStringValue(left), GetValue.getStringValue(right)));
+ break;
+ case T_double:
+ pushNewValue(type, getDoubleResult(GetValue.getDoubleValue(left), GetValue.getDoubleValue(right)));
+ break;
+ case T_float:
+ pushNewValue(type, getFloatResult(GetValue.getFloatValue(left), GetValue.getFloatValue(right)));
+ break;
+ case T_long:
+ pushNewValue(type, getLongResult(GetValue.getLongValue(left), GetValue.getLongValue(right)));
+ break;
+ case T_int:
+ pushNewValue(type, getIntResult(GetValue.getIntValue(left), GetValue.getIntValue(right)));
+ break;
+ case T_boolean:
+ pushNewValue(type, getBooleanResult(GetValue.getBooleanValue(left), GetValue.getBooleanValue(right)));
+ break;
+ case T_BigInt:
+ pushNewValue(type, getBigIntegerResult(GetValue.getBigIntegerValue(left), GetValue.getBigIntegerValue(right)));
+ break;
+ }
+ }
+
+ /**
+ * Get boolean result of applying a binary logical operation to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract boolean getIntResult(int leftOperand, int rightOperand) throws CoreException;
+
+ /**
+ * Get boolean result of applying a binary logical operation to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract boolean getLongResult(long leftOperand, long rightOperand) throws CoreException;
+
+ /**
+ * Get boolean result of applying a binary logical operation to two
+ * BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand)
+ throws CoreException;
+
+ /**
+ * Get boolean result of applying a binary logical operation to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract boolean getFloatResult(float leftOperand, float rightOperand);
+
+ /**
+ * Get boolean result of applying a binary logical operation to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract boolean getDoubleResult(double leftOperand, double rightOperand);
+
+ /**
+ * Get boolean result of applying a binary logical operation to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract boolean getBooleanResult(boolean leftOperand, boolean rightOperand);
+
+ /**
+ * Get boolean result of applying a binary logical operation to two strings
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract boolean getStringResult(String leftOperand, String rightOperand) throws CoreException;
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary arithmetic operator, such as "/"
+ */
+public abstract class BinaryOperator extends CompoundInstruction {
+
+ /**
+ * Constructor for a binary arithmetic operator, such as "/"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected BinaryOperator(int resultId, boolean isAssignmentOperator, int start) {
+ super(start);
+ }
+
+ /**
+ * Resolve a binary arithmetic operator, such as "/"
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue right = popValue();
+ OperandValue left = popValue();
+
+ right = convertForPromotion(right);
+ left = convertForPromotion(left);
+
+ if (customHandleOperation(fInterpreter, left, right))
+ return;
+
+ int resultType = getJavaBinaryPromotionType(right, left);
+ IType type;
+ if (resultType == T_String)
+ type = left.getValueType();
+ else
+ type = getBinaryPromotionType(right, left);
+
+ // non-logical operations on booleans are int results
+ if ((type instanceof ICPPBasicType) && ((ICPPBasicType) type).getBaseType() == ICPPBasicType.t_bool) {
+ type = fInterpreter.getTypeEngine().getIntegerTypeFor(TypeUtils.BASIC_TYPE_INT, true);
+ }
+
+ switch (resultType) {
+ case T_String:
+ pushNewValue(type, getStringResult(GetValue.getStringValue(left), GetValue.getStringValue(right)));
+ break;
+ case T_double:
+ pushNewValue(type, getDoubleResult(GetValue.getDoubleValue(left), GetValue.getDoubleValue(right)));
+ break;
+ case T_float:
+ pushNewValue(type, getFloatResult(GetValue.getFloatValue(left), GetValue.getFloatValue(right)));
+ break;
+ case T_long:
+ pushNewValue(type, getLongResult(GetValue.getLongValue(left), GetValue.getLongValue(right)));
+ break;
+ case T_int:
+ pushNewValue(type, getIntResult(GetValue.getIntValue(left), GetValue.getIntValue(right)));
+ break;
+ case T_boolean:
+ pushNewValue(type, getBooleanResult(GetValue.getBooleanValue(left), GetValue.getBooleanValue(right)));
+ break;
+ case T_BigInt:
+ pushNewValue(type, getBigIntegerResult(GetValue.getBigIntegerValue(left), GetValue.getBigIntegerValue(right), 8));
+ break;
+ default:
+ throw EDCDebugger.newCoreException(ASTEvalMessages.UnhandledTypeCode + resultType);
+ }
+ }
+
+ /**
+ * Handle type operation in a non-standard way
+ * @param fInterpreter
+ * @param left
+ * @param right
+ * @return true if handled
+ */
+ protected boolean customHandleOperation(Interpreter fInterpreter, OperandValue left, OperandValue right) throws CoreException {
+ return false;
+ }
+
+ /**
+ * Get int result of applying a binary operation to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return int result of the operation if possible, or an operation-specific
+ * default
+ * @throws CoreException
+ */
+ protected abstract int getIntResult(int leftOperand, int rightOperand) throws CoreException;
+
+ /**
+ * Get long result of applying a binary operation to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return long result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract long getLongResult(long leftOperand, long rightOperand) throws CoreException;
+
+ /**
+ * Get BigInteger result of applying a binary operation to two longs
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return BigInteger result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException;
+
+ /**
+ * Get float result of applying a binary operation to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return float result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract float getFloatResult(float leftOperand, float rightOperand);
+
+ /**
+ * Get double result of applying a binary operation to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return double result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract double getDoubleResult(double leftOperand, double rightOperand);
+
+ /**
+ * Get boolean result of applying a binary operation to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract boolean getBooleanResult(boolean leftOperand, boolean rightOperand);
+
+ /**
+ * Get string result of applying a binary operation to two strings.
+ * Default implementation throws.
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return string result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected String getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.UnsupportedStringOperation);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+public abstract class CompoundInstruction extends Instruction {
+
+ private int size;
+
+ /**
+ * Constructor for a compound instruction
+ *
+ * @param start
+ * - instruction start
+ */
+ protected CompoundInstruction(int start) {
+ size = -start;
+ }
+
+ /**
+ * Set compound instruction end
+ *
+ * @param end
+ * - compound instruction end
+ */
+ public void setEnd(int end) {
+ size += end;
+ }
+
+ /**
+ * Get compound instruction size
+ *
+ * @return compound instruction size
+ */
+ @Override
+ public int getSize() {
+ return size;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.text.MessageFormat;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Symbols;
+import org.eclipse.cdt.debug.edc.internal.symbols.InvalidVariableLocation;
+import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCModules;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.services.Stack.EnumeratorDMC;
+import org.eclipse.cdt.debug.edc.services.Stack.IVariableEnumeratorContext;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.services.Stack.VariableDMC;
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public class EvaluateID extends SimpleInstruction {
+
+ private String name;
+ private final ICPPASTQualifiedName qualifiedName;
+
+ /**
+ * Constructor for ID (number + variable name) evaluate instruction
+ *
+ * @param idExpression
+ */
+ public EvaluateID(IASTIdExpression idExpression) {
+ IASTName lookupName;
+
+ if (idExpression.getName() instanceof ICPPASTQualifiedName) {
+ // the name has the form namespace::...::variable
+ qualifiedName = (ICPPASTQualifiedName) idExpression.getName();
+ lookupName = qualifiedName.getLastName();
+ } else {
+ lookupName = idExpression.getName();
+ qualifiedName = null;
+ }
+
+ name = new String(lookupName.getLookupKey());
+ }
+
+ /**
+ * Constructor for lookup of a specific literal name
+ * (presumably a local, like "this")
+ *
+ * @param name the literal name
+ */
+ public EvaluateID(String name) {
+ this.name = name;
+ this.qualifiedName = null;
+ }
+
+ /**
+ * Resolve a variable ID
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+
+ IDMContext context = getContext();
+
+ if (!(context instanceof StackFrameDMC))
+ throw EDCDebugger.newCoreException(MessageFormat.format(ASTEvalMessages.EvaluateID_CannotResolveName, name));
+
+ StackFrameDMC frame = (StackFrameDMC) context;
+ EDCServicesTracker servicesTracker = frame.getEDCServicesTracker();
+ IEDCModules modules = servicesTracker.getService(IEDCModules.class);
+
+ // check by name for a variable or enumerator
+ IVariableEnumeratorContext variableOrEnumerator = frame.findVariableOrEnumeratorByName(name, qualifiedName != null ? qualifiedName.getRawSignature() : null, false);
+ VariableDMC variable = variableOrEnumerator instanceof VariableDMC ?
+ (VariableDMC)variableOrEnumerator : null;
+ EnumeratorDMC enumerator = variableOrEnumerator instanceof EnumeratorDMC ?
+ (EnumeratorDMC)variableOrEnumerator : null;
+
+ // This may be called on debugger shutdown, in which case the "modules"
+ // service may have been shutdown.
+ if (variable != null && modules != null) {
+ IVariableLocation valueLocation = null;
+ ILocationProvider provider = variable.getVariable().getLocationProvider();
+ IEDCModuleDMContext module = frame.getModule();
+ if (module != null && provider != null) {
+ valueLocation = provider.getLocation(servicesTracker, frame, module.toLinkAddress(frame.getInstructionPtrAddress()),
+ TypeUtils.isConstType(variable.getVariable().getType()));
+ }
+ if (valueLocation == null) {
+ // unhandled
+ valueLocation = new InvalidVariableLocation(MessageFormat.format(ASTEvalMessages.EvaluateID_NameHasNoLocation, variable.getName()));
+ }
+ // create a VariableWithValue and push on the stack
+ VariableWithValue varWval = new VariableWithValue(servicesTracker, frame, variable.getVariable());
+ varWval.setValueLocation(valueLocation);
+ push(varWval);
+ return;
+ }
+
+ if (enumerator != null) {
+ // TODO: map IEnumerator to an IEnumeration and use the real type
+ pushNewValue(fInterpreter.getTypeEngine().getIntegerTypeOfSize(4, true),
+ enumerator.getEnumerator().getValue());
+ return;
+ }
+
+ // match against function names visible in the module
+ Symbols symbolsService = servicesTracker.getService(Symbols.class);
+ if (symbolsService != null) {
+ IEDCModuleDMContext module = frame.getModule();
+ if (module != null) {
+ String searchName = name;
+ if (qualifiedName != null)
+ searchName = qualifiedName.getRawSignature();
+ List<IAddress> addresses = symbolsService.getFunctionAddress(module, searchName);
+ if (addresses.size() > 0) {
+ pushNewValue(fInterpreter.getTypeEngine().getIntegerTypeOfSize(4, false),
+ addresses.get(0).getValue().longValue());
+ return;
+ }
+ // show the whole qualified name in the exception message
+ name = searchName;
+ }
+ }
+
+ // did not find a variable, enumerator, or function to match the expression
+ throw EDCDebugger.newCoreException(
+ MessageFormat.format(ASTEvalMessages.EvaluateID_VariableNotFound, name));
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.text.MessageFormat;
+
+import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.IAggregate;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IEnumeration;
+import org.eclipse.cdt.debug.edc.internal.symbols.IField;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IReferenceType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.debug.edc.symbols.VariableLocationFactory;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Field reference instruction, such as "." or "->"
+ */
+public class FieldReference extends CompoundInstruction {
+
+ private final IASTFieldReference refExpression;
+
+ /**
+ * Constructor for field reference instruction
+ *
+ * @param expression
+ * - field reference expression
+ * @param start
+ * - instruction start
+ */
+ public FieldReference(IASTFieldReference expression, int start) {
+ super(start);
+ this.refExpression = expression;
+ }
+
+ /**
+ * Resolve a field reference operator, such as "." or "->"
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ // pop the structure variable at the start of the field references
+ OperandValue operand = popValue();
+
+ if (operand == null)
+ return;
+
+ IType variableType = TypeUtils.getStrippedType(operand.getValueType());
+
+ IVariableLocation location = null;
+ boolean referenceType = variableType instanceof IReferenceType;
+
+ if (refExpression.isPointerDereference()) {
+ // '->' operator requires a pointer type
+ boolean validPointerType = variableType instanceof IPointerType;
+
+ if (!validPointerType) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.FieldReference_InvalidPointerDeref);
+ }
+
+ IPointerType pointer = (IPointerType) variableType;
+
+ IType pointedTo = pointer.getType();
+ variableType = TypeUtils.getStrippedType(pointedTo);
+ } else if (referenceType) {
+ // '.' may be used with a reference "&" type
+ IReferenceType pointer = (IReferenceType) variableType;
+
+ IType pointedTo = pointer.getType();
+ variableType = TypeUtils.getStrippedType(pointedTo);
+ }
+
+ if (!TypeUtils.isCompositeType(variableType)) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.FieldReference_InvalidDotDeref);
+ }
+
+ // get the field/member
+ ICompositeType compositeType = (ICompositeType) variableType;
+ String fieldName = refExpression.getFieldName().toString();
+ IField[] fields = compositeType.findFields(fieldName);
+
+ if (fields == null) {
+ throw EDCDebugger.newCoreException(
+ MessageFormat.format(ASTEvalMessages.FieldReference_InvalidMember, fieldName));
+ }
+
+ if (fields.length > 1) {
+ throw EDCDebugger.newCoreException(
+ MessageFormat.format(ASTEvalMessages.FieldReference_AmbiguousMember, fieldName,
+ operand.getValueType().getName()));
+ }
+
+ // type and address of the field
+ IField field = fields[0];
+ IType typeOfField = field.getType();
+
+ if ( refExpression.isPointerDereference()
+ || (!refExpression.isPointerDereference() && referenceType)) {
+ // pointer with '->' operator, or reference with '.'
+ location = VariableLocationFactory.createMemoryVariableLocation(
+ fInterpreter.getServicesTracker(), fInterpreter.getContext(),
+ operand.getValue());
+ } else {
+ // '.' operator
+ location = operand.getValueLocation();
+ }
+
+ location = location.addOffset(field.getFieldOffset());
+
+ OperandValue varValue = new OperandValue(typeOfField, field.getBitSize() > 0);
+
+ typeOfField = TypeUtils.getStrippedType(typeOfField);
+
+ // for lvalues (base arithmetic types, enums, and pointers), read the
+ // value and cast it to the right type
+ if ( typeOfField instanceof ICPPBasicType || typeOfField instanceof IPointerType
+ || typeOfField instanceof IEnumeration || typeOfField instanceof IReferenceType) {
+ int byteSize = typeOfField.getByteSize();
+
+ // TODO support 12-byte long double
+ if (byteSize != 1 && byteSize != 2 && byteSize != 4 && byteSize != 8 &&
+ !(typeOfField instanceof IPointerType && byteSize == 0)) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.FieldReference_UnhandledOperandSize + byteSize);
+ }
+
+ // read the value pointed to
+ Number newValue = varValue.getValueByType(typeOfField, location);
+
+ // if this is a bit-field, then mask and/or extend the value
+ // appropriately
+ // Note: only unnamed bit-fields have a 0 bit size, so a named field
+ // with a 0 bit size is not a bit-field
+ if (field.getBitSize() > 0) {
+ int bitSize = field.getBitSize();
+ int bitOffset = field.getBitOffset();
+ boolean isSignedInt = false;
+
+ if (typeOfField instanceof ICPPBasicType)
+ isSignedInt = ((ICPPBasicType) typeOfField).isSigned();
+ else if (typeOfField instanceof IEnumeration)
+ isSignedInt = true;
+
+ newValue = TypeUtils.extractBitField(newValue, byteSize, bitSize, bitOffset, isSignedInt);
+ }
+ varValue.setValue(newValue);
+ varValue.setValueLocation(location);
+
+ } else if (typeOfField instanceof IAggregate) {
+ // for aggregates, the address of the aggregate is the value
+ // returned
+ varValue.setAddressValue(location);
+ varValue.setValueLocation(location);
+
+ } else {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.FieldReference_CannotDereferenceType);
+ }
+
+ push(varValue);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Get the correct type of value from an object, converting if needed.
+ * <p>
+ * All of these expect to be called with values no larger than their types (e.g. by {@link Instruction#convertForPromotion(Object)})
+ * so we throw exceptions if not.
+ */
+public class GetValue {
+
+ private static CoreException badType() {
+ return EDCDebugger.newCoreException(ASTEvalMessages.GetValue_TypePromotionError);
+ }
+
+ /**
+ * Get the boolean value of an object
+ *
+ * @param value
+ * - possibly Boolean object
+ * @return boolean value of param, or false if param is not a Boolean object
+ */
+ public static boolean getBooleanValue(OperandValue op) throws CoreException {
+ Number value = op.getValue();
+ if (value instanceof BigInteger)
+ return ((BigInteger) value).signum() != 0 ? true : false;
+ return value.longValue() != 0;
+ }
+
+ /**
+ * Get the integer value of an object
+ *
+ * @param value
+ * - possibly Integer, Short, or Byte object
+ * @return integer value of param, or 0 if param is not an integer object
+ */
+ public static int getIntValue(OperandValue op) throws CoreException {
+ Number value = op.getValue();
+ if (value instanceof Integer)
+ return (Integer) value;
+ if (value instanceof Short)
+ return new Integer((Short) value);
+ if (value instanceof Byte)
+ return new Integer((Byte) value);
+ throw badType();
+ }
+
+ /**
+ * Get the long value of an object
+ *
+ * @param value value with Long, Integer, Short, or Byte value
+ * @return long value of param, or 0 if param is not an integral object
+ */
+ public static long getLongValue(OperandValue op) throws CoreException {
+ Number value = op.getValue();
+ if (value instanceof Long)
+ return (Long) value;
+ if (value instanceof Integer)
+ return new Long((Integer) value);
+ if (value instanceof Short)
+ return new Long((Short) value);
+ if (value instanceof Byte)
+ return new Long((Byte) value);
+ throw badType();
+ }
+
+ /**
+ * Get the BigInteger value of an object
+ *
+ * @param value value with possibly BigInteger, Long, Integer, Short, Byte, or
+ * Character object
+ * @return BigInteger value of param, or 0 if param is not an integral
+ * object
+ */
+ public static BigInteger getBigIntegerValue(OperandValue op) throws CoreException {
+ Number value = op.getValue();
+ if (value instanceof BigInteger)
+ return (BigInteger) value;
+ if (value instanceof Long)
+ return new BigInteger(((Long) value).toString());
+ if (value instanceof Integer)
+ return new BigInteger(((Integer) value).toString());
+ if (value instanceof Short)
+ return new BigInteger(((Short) value).toString());
+ if (value instanceof Byte)
+ return new BigInteger(new byte[] { (Byte) value });
+ //if (value instanceof Character)
+ // return new BigInteger(new byte[] { (byte) Character.getNumericValue((Character) value) });
+ throw badType();
+ }
+
+ /**
+ * Get the float value of an object
+ *
+ * @param value with possibly Float or integral (e.g., Long) object
+ * @return float value of param, or 0 if param is not a Float or integral
+ * object
+ */
+ public static float getFloatValue(OperandValue op) throws CoreException {
+ Number value = op.getValue();
+ if (value instanceof Float)
+ return (Float) value;
+ if (value instanceof Long)
+ return new Float((Long) value);
+ if (value instanceof Integer)
+ return new Float((Integer) value);
+ if (value instanceof Short)
+ return new Float((Short) value);
+ if (value instanceof Byte)
+ return new Float((Byte) value);
+ if (value instanceof BigInteger)
+ return new Float(((BigInteger) value).floatValue());
+ throw badType();
+ }
+
+ /**
+ * Get the double value of an object
+ *
+ * @param value
+ * - possibly float (e.g., Double) or integral (e.g., Long)
+ * object
+ * @return double value of param, or 0 if param is not a float or integral
+ * object
+ */
+ public static double getDoubleValue(OperandValue op) throws CoreException {
+ Number value = op.getValue();
+ if (value instanceof Double)
+ return (Double) value;
+ if (value instanceof Float)
+ return new Double((Float) value);
+ if (value instanceof Long)
+ return new Double((Long) value);
+ if (value instanceof Integer)
+ return new Double((Integer) value);
+ if (value instanceof Short)
+ return new Double((Short) value);
+ if (value instanceof Byte)
+ return new Double((Byte) value);
+ if (value instanceof BigInteger)
+ return new Double(((BigInteger) value).doubleValue());
+ throw badType();
+ }
+
+ /**
+ * Get the string value of an object
+ *
+ * @param value
+ * - String or Character object
+ * @return string value of String param, or quoted string for Character
+ * param
+ */
+ public static String getStringValue(OperandValue value) throws CoreException {
+ if (value.getStringValue() != null)
+ return value.getStringValue();
+ return "\"" + (char) (value.getValue().longValue()) + "\""; //$NON-NLS-1$ //$NON-NLS-2$
+ //throw badType();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.IAggregate;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+
+public interface IArrayDimensionType extends IAggregate {
+
+ public IArrayType getArrayType();
+
+ public IVariableLocation getLocation();
+
+ public int getDimensionCount();
+
+ public void addDimension(long subscript, long increase);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+/**
+ * Invalid expression
+ */
+public interface IInvalidExpression {
+
+ /**
+ * Get message telling why the expression is invalid
+ *
+ * @return messsage
+ */
+ public String getMessage();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+import java.text.MessageFormat;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IEnumeration;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public abstract class Instruction {
+
+ protected static BigInteger Mask8Bytes = new BigInteger("ffffffffffffffff", 16); //$NON-NLS-1$
+
+ protected Interpreter fInterpreter;
+
+ /**
+ * Get instruction size
+ *
+ * return instruction size
+ */
+ public abstract int getSize();
+
+ /**
+ * Set instruction's fInterpreter
+ *
+ * @param interpreter
+ */
+ public void setInterpreter(Interpreter interpreter) {
+ fInterpreter = interpreter;
+ }
+
+ /**
+ * Stop the instruction fInterpreter
+ */
+ public void stop() {
+ fInterpreter.stop();
+ }
+
+ /**
+ * Execute the instruction
+ * @throws CoreException
+ */
+ public abstract void execute() throws CoreException;
+
+ /**
+ * Get the instruction's context
+ *
+ * @return instruction fInterpreter context
+ */
+ protected IDMContext getContext() {
+ return fInterpreter.getContext();
+ }
+
+ /**
+ * Jump to an instruction fInterpreter offset
+ *
+ * @param offset
+ * - fInterpreter offset
+ */
+ protected void jump(int offset) {
+ fInterpreter.jump(offset);
+ }
+
+ /**
+ * Push an object on the instruction stack
+ *
+ * @param op
+ */
+ protected OperandValue push(OperandValue op) {
+ fInterpreter.push(op);
+ return op;
+ }
+
+ /**
+ * Pop an object off the instruction stack
+ *
+ * @return object on the top of the stack
+ */
+ protected OperandValue pop() {
+ return fInterpreter.pop();
+ }
+
+ /**
+ * Pop a value from the instruction stack
+ *
+ * @return current top of stack, if the stack is not empty, or
+ * <code>null</code> otherwise
+ * @throws CoreException
+ */
+ protected OperandValue popValue() throws CoreException {
+ OperandValue value = null;
+ if (!fInterpreter.isEmpty())
+ value = fInterpreter.pop();
+ if (value == null)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.Instruction_EmptyStack);
+ return value;
+ }
+
+ /**
+ * Push a boolean on the instruction stack
+ *
+ * @param value
+ * - boolean value
+ */
+ protected OperandValue pushNewValue(IType type, boolean value) {
+ OperandValue op = new OperandValue(value ? 1 : 0, type);
+ fInterpreter.push(op);
+ return op;
+ }
+
+
+ /**
+ * Push a number on the instruction stack
+ *
+ * @param value
+ * - number value
+ */
+ protected OperandValue pushNewValue(IType type, Number value) {
+ OperandValue op = new OperandValue(value, type);
+ fInterpreter.push(op);
+ return op;
+ }
+
+ /**
+ * Push a string on the instruction stack
+ *
+ * @param value
+ * - string value
+ */
+ protected OperandValue pushNewValue(IType type, String value) {
+ OperandValue op = new OperandValue(value, type);
+ fInterpreter.push(op);
+ return op;
+ }
+
+ /**
+ * Convert operands to types expected by getBinaryPromotionType() (e.g.,
+ * VariableWithValue to its underlying Long)
+ *
+ * @param operand
+ * - original operand
+ * @return result operand type
+ * @throws CoreException if value cannot be fetched
+ */
+ protected OperandValue convertForPromotion(OperandValue operand) throws CoreException {
+ IType type = getBasicType(operand.getValueType());
+ if (type instanceof ICompositeType) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.Instruction_CannotUseCompositeType);
+ }
+ if (type instanceof IArrayType && operand.getValueLocation() != null) {
+ // take address as value
+ return new OperandValue(operand.getValueLocationAddress(), fInterpreter.getTypeEngine().getPointerSizeType());
+ }
+ return operand;
+ }
+
+ /**
+ * Get result binary operation type given types of the left and right
+ * operands, according to Java rules
+ *
+ * @param left
+ * - left operand
+ * @param right
+ * - right operand
+ * @return result T_ type
+ * @throws CoreException
+ */
+ public int getJavaBinaryPromotionType(OperandValue left, OperandValue right) throws CoreException {
+ int leftType = getValueType(left);
+ int rightType = getValueType(right);
+ return fTypeTable[leftType][rightType];
+ }
+
+ /**
+ * Get result binary operation type given types of the left and right
+ * operands
+ *
+ * @param left
+ * - left operand T_ type
+ * @param right
+ * - right operand T_type
+ * @return result T_ type
+ */
+ public int getBinaryPromotionType(int left, int right) {
+ return fTypeTable[left][right];
+ }
+
+ /**
+ * Get T_ type for a base Java type (e.g., Character or Boolean).
+ * <p>
+ * This differs from the actual C type in that we pick the Java
+ * type that has the same size as the C type. I.e., "char" of size 1
+ * becomes T_byte and "wchar_t" of size 4 becomes T_int.
+ *
+ * @param value
+ * - base Java type object
+ * @return corresponding T_ type, or T_undefined if value is not a base type
+ * @throws CoreException
+ */
+ public int getValueType(OperandValue op) throws CoreException {
+ Number value = op.getValue();
+ if (value == null) {
+ if (op.getStringValue() != null)
+ return T_String;
+ }
+ // respect Java types first, since C types can alias (e.g. int == long)
+ if (value instanceof Integer)
+ return T_int;
+ if (value instanceof Short)
+ return T_short;
+ if (value instanceof Byte)
+ return T_byte;
+ if (value instanceof Long)
+ return T_long;
+ if (value instanceof Float)
+ return T_float;
+ if (value instanceof Double)
+ return T_double;
+ if (value instanceof BigInteger)
+ return T_BigInt;
+
+ return getJavaValueType(op.getValueType());
+ }
+
+ /**
+ * Get T_ type corresponding to the given C type. This is used for promotion.
+ * Note: in this interpretation, long long maps to T_BigInt.
+ *
+ * @param type the basic type
+ * @return corresponding T_ type, or T_undefined if value is not a base type
+ * @throws CoreException
+ */
+ public int getCValueType(OperandValue op) throws CoreException {
+ if (op.getStringValue() != null) {
+ return T_String;
+ }
+
+ IType type_ = getBasicType(op.getValueType());
+
+ if (!(type_ instanceof ICPPBasicType))
+ return T_undefined;
+
+ ICPPBasicType type = (ICPPBasicType) type_;
+
+ switch (type.getBaseType()) {
+ case ICPPBasicType.t_bool:
+ return T_boolean;
+ case ICPPBasicType.t_char:
+ return T_char;
+ case ICPPBasicType.t_float:
+ return T_float;
+ case ICPPBasicType.t_double:
+ if (type.isLong())
+ assert(false); // TODO; need long double type
+ return T_double;
+ case ICPPBasicType.t_int:
+ if (type.isLongLong())
+ return T_BigInt;
+ if (type.isLong())
+ return T_long;
+ if (type.isShort())
+ return T_short;
+ return T_int;
+ case ICPPBasicType.t_unspecified:
+ return T_undefined;
+ case ICPPBasicType.t_void:
+ return T_void;
+ }
+ return T_undefined;
+ }
+
+ /**
+ * Get result binary operation type given types of the left and right
+ * operands. This uses the C rules for type promotion.
+ *
+ * @param left
+ * - left type
+ * @param right
+ * - right type
+ * @return result type or <code>null</code> if an undefined promotion
+ */
+ public IType getBinaryPromotionType(OperandValue leftOp, OperandValue rightOp) throws CoreException {
+
+ int leftType = getCValueType(leftOp);
+ int rightType = getCValueType(rightOp);
+ int promoted = getBinaryPromotionType(leftType, rightType);
+
+ if (promoted == T_null || promoted == T_undefined || promoted == T_void)
+ throw EDCDebugger.newCoreException(MessageFormat.format(ASTEvalMessages.Instruction_UnhandledTypeCombination,
+ leftOp.getValueType().getName(), rightOp.getValueType().getName()));
+
+ // promoted type loses qualifier bits and enum-ness
+ if (leftType == promoted)
+ return getBasicType(leftOp.getValueType());
+ if (rightType == promoted)
+ return getBasicType(rightOp.getValueType());
+
+ // we're here because the promoted type is bigger than either of the
+ // incoming types (e.g. short + float -> double)
+
+ boolean isSigned = true;
+
+ switch (promoted) {
+ case T_char:
+ return fInterpreter.getTypeEngine().getIntegerTypeFor(TypeUtils.BASIC_TYPE_CHAR, isSigned);
+ case T_short:
+ return fInterpreter.getTypeEngine().getIntegerTypeFor(TypeUtils.BASIC_TYPE_SHORT, isSigned);
+ case T_int:
+ return fInterpreter.getTypeEngine().getIntegerTypeFor(TypeUtils.BASIC_TYPE_INT, isSigned);
+ case T_long:
+ return fInterpreter.getTypeEngine().getIntegerTypeFor(TypeUtils.BASIC_TYPE_LONG, isSigned);
+ case T_float:
+ return fInterpreter.getTypeEngine().getFloatTypeOfSize(TypeUtils.BASIC_TYPE_FLOAT);
+ case T_double:
+ return fInterpreter.getTypeEngine().getFloatTypeOfSize(TypeUtils.BASIC_TYPE_DOUBLE);
+ // TODO: long double
+
+ case T_byte: // should not happen
+ case T_null: // should not happen
+ case T_Object: // should not happen
+ case T_String: // should not happen
+ default:
+ assert(false);
+ throw EDCDebugger.newCoreException(ASTEvalMessages.UnhandledTypeCode + promoted);
+ }
+ }
+
+ /**
+ * Get the stripped down basic type that promotion rules work with.
+ * Ignore const/volatile/... qualifiers, demote enums to ints, etc.
+ * @param type
+ * @return adjusted type
+ */
+ private IType getBasicType(IType type) {
+ type = TypeUtils.getStrippedType(type);
+ if (type instanceof IEnumeration) {
+ // discover the appropriate integer to hold this
+ int byteSize = ((IEnumeration) type).getByteSize();
+ type = fInterpreter.getTypeEngine().getIntegerTypeOfSize(byteSize, true);
+ }
+ return type;
+ }
+
+ /**
+ * Get T_ type for a base Java type that can hold a given type
+ * (e.g., Character or Boolean).
+ * <p>
+ * This differs from the actual C type in that we pick the Java
+ * type that has a compatible size as the C type. I.e., "char" of size 1
+ * becomes T_byte and "wchar_t" of size 4 becomes T_int. (An actual
+ * target may have larger or smaller primitive types than Java.)
+ * <p>
+ * Note: when we do unsigned math, we go up one type size in order to handle
+ * unsigned math properly.
+ *
+ * @param value
+ * - base Java type object
+ * @return corresponding T_ type, T_string, or T_undefined if value is not a base or string type
+ */
+ public int getJavaValueType(IType type_) {
+ type_ = getBasicType(type_);
+ if (!(type_ instanceof ICPPBasicType)) {
+ if (type_ instanceof IArrayType && type_.getType() instanceof ICPPBasicType
+ && ((((ICPPBasicType) type_.getType()).getBaseType() == ICPPBasicType.t_char
+ || ((ICPPBasicType) type_.getType()).getBaseType() == ICPPBasicType.t_wchar_t))) {
+ return T_String;
+ }
+ return T_undefined;
+ }
+
+ ICPPBasicType type = (ICPPBasicType) type_;
+
+ switch (type.getBaseType()) {
+ case ICPPBasicType.t_bool:
+ return T_boolean;
+ case ICPPBasicType.t_char:
+ return T_char;
+ case ICPPBasicType.t_float:
+ return T_float;
+ case ICPPBasicType.t_double:
+ if (type.isLong())
+ assert(false); // TODO
+ return T_double;
+ case ICPPBasicType.t_unspecified:
+ return T_undefined;
+ case ICPPBasicType.t_void:
+ return T_void;
+ case ICPPBasicType.t_int:
+ if (type.isLongLong()) {
+ if (type.getByteSize() > 8 || (type.getByteSize() == 8 && type.isUnsigned()))
+ return T_BigInt; // java long cannot handle unsigned 8-byte math
+ else
+ return T_long;
+ }
+ if (type.isLong()) {
+ if (type.getByteSize() > 8 || (type.getByteSize() == 8 && type.isUnsigned()))
+ return T_BigInt; // java long cannot handle unsigned 8-byte math
+ else
+ return T_long;
+ }
+ switch (type.getByteSize()) {
+ case 1:
+ if (type.isUnsigned())
+ return T_short;
+ else
+ return T_byte;
+ case 2:
+ if (type.isUnsigned())
+ return T_int;
+ else
+ return T_short;
+ case 4:
+ if (type.isUnsigned())
+ return T_int;
+ else
+ return T_short;
+ }
+ if (type.isLong()) {
+ if (type.isUnsigned() && type.getByteSize() > 8)
+ return T_BigInt;
+ else
+ return T_long;
+ }
+
+
+ }
+ return T_undefined;
+ }
+
+ static public final int T_undefined = 0;
+ static public final int T_Object = 1;
+ static public final int T_char = 2;
+ static public final int T_byte = 3;
+ static public final int T_short = 4;
+ static public final int T_boolean = 5;
+ static public final int T_void = 6;
+ static public final int T_long = 7;
+ static public final int T_double = 8;
+ static public final int T_float = 9;
+ static public final int T_int = 10;
+ static public final int T_String = 11;
+ static public final int T_null = 12;
+ static public final int T_BigInt = 13;
+
+ private static final int[][] fTypeTable = {
+ // undefined object char byte short boolean void
+ /* undefined */ { T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined},
+ // undefined object char byte short boolean void
+ /* object */ { T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_undefined, T_undefined, T_undefined, T_undefined, T_String, T_undefined, T_undefined },
+ // undefined object char byte short boolean void
+ /* char */ { T_undefined, T_undefined, T_int, T_int, T_int, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_long, T_double, T_float, T_int, T_String, T_undefined, T_BigInt },
+ // undefined object char byte short boolean void
+ /* byte */ { T_undefined, T_undefined, T_int, T_int, T_int, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_long, T_double, T_float, T_int, T_String, T_undefined, T_BigInt },
+ // undefined object char byte short boolean void
+ /* short */ { T_undefined, T_undefined, T_int, T_int, T_int, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_long, T_double, T_float, T_int, T_String, T_undefined, T_BigInt },
+ // undefined object char byte short boolean void
+ /* boolean */ { T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_boolean, T_undefined,
+ // long double float int String null BigInteger
+ T_undefined, T_undefined, T_undefined, T_undefined, T_String, T_undefined, T_undefined },
+ // undefined object char byte short boolean void
+ /* void */ { T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined },
+ // undefined object char byte short boolean void
+ /* long */ { T_undefined, T_undefined, T_long, T_long, T_long, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_long, T_double, T_float, T_long, T_String, T_undefined, T_BigInt },
+ // undefined object char byte short boolean void
+ /* double */ { T_undefined, T_undefined, T_double, T_double, T_double, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_double, T_double, T_double, T_double, T_String, T_undefined, T_double },
+ // undefined object char byte short boolean void
+ /* float */ { T_undefined, T_undefined, T_float, T_float, T_float, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_float, T_double, T_float, T_float, T_String, T_undefined, T_float },
+ // undefined object char byte short boolean void
+ /* int */ { T_undefined, T_undefined, T_int, T_int, T_int, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_long, T_double, T_float, T_int, T_String, T_undefined, T_BigInt },
+ // undefined object char byte short boolean void
+ /* String */ { T_undefined, T_String, T_String, T_String, T_String, T_String, T_undefined,
+ // long double float int String null BigInteger
+ T_String, T_String, T_String, T_String, T_String, T_String, T_undefined },
+ // undefined object char byte short boolean void
+ /* null */ { T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_undefined, T_undefined, T_undefined, T_undefined, T_String, T_undefined, T_undefined },
+ // undefined object char byte short boolean void
+ /* BigInteger */{ T_undefined, T_undefined, T_BigInt, T_BigInt, T_BigInt, T_undefined, T_undefined,
+ // long double float int String null BigInteger
+ T_BigInt, T_double, T_float, T_BigInt, T_undefined, T_undefined, T_BigInt },
+ };
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeEngine;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Instruction sequence
+ */
+public class InstructionSequence {
+
+ private List<Instruction> fInstructions;
+ /**
+ * A collection of error messages (<code>String</code>) that occurred while
+ * creating this expression
+ */
+ private List<String> fErrors;
+ private String fSnippet;
+ private CoreException fException;
+
+ /**
+ * Constructor for an instruction sequence
+ *
+ * @param snippet
+ * - expression the instruction sequence represents
+ */
+ public InstructionSequence(String snippet) {
+ fInstructions = new ArrayList<Instruction>(10);
+ fErrors = new ArrayList<String>();
+ fSnippet = snippet;
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param original - the instance to copy
+ */
+ public InstructionSequence(InstructionSequence original) {
+ fInstructions = new ArrayList<Instruction>(Arrays.asList(original.getInstructions()));
+ fErrors = new ArrayList<String>(Arrays.asList(original.getErrorMessages()));
+ fSnippet = original.getSnippet();
+ fException = original.getException();
+ }
+
+ /**
+ * Get the runtime exception that occurred while evaluating this expression
+ *
+ * @param runtime
+ * exception, or <code>null</code> if no exception occurred
+ */
+ public CoreException getException() {
+ return fException;
+ }
+
+ /**
+ * @see ICompiledExpression#getSnippet()
+ */
+ public String getSnippet() {
+ return fSnippet;
+ }
+
+ /**
+ * Adds the given error to the list of errors that occurred while compiling
+ * this instruction sequence
+ */
+ public void addError(String error) {
+ fErrors.add(error);
+ }
+
+ /**
+ * @see ICompiledExpression#hasErrors()
+ */
+ public boolean hasErrors() {
+ return !fErrors.isEmpty();
+ }
+
+ /**
+ * @see org.eclipse.jdt.debug.eval.ICompiledExpression#getErrorMessages()
+ */
+ public String[] getErrorMessages() {
+ return fErrors.toArray(new String[fErrors.size()]);
+ }
+
+ /**
+ * Get the array of instructions
+ *
+ * return array of instructions, or an empty array
+ */
+ public Instruction[] getInstructions() {
+ int size = fInstructions.size();
+ Instruction[] instructions = new Instruction[size];
+ if (size > 0) {
+ fInstructions.toArray(instructions);
+ }
+ return instructions;
+ }
+
+ /**
+ * Get the instruction at the given address
+ *
+ * @param address
+ * - address of instruction
+ * @return instruction at the address
+ */
+ public Instruction getInstruction(int address) {
+ return fInstructions.get(address);
+ }
+
+ /**
+ * Add the given instruction to the end of the list
+ *
+ * @param instruction
+ * - instruction to add
+ */
+ public void add(Instruction instruction) {
+ fInstructions.add(instruction);
+ }
+
+ /**
+ * Get the index of an instruction
+ *
+ * @param instruction
+ * - instruction to find
+ * @return index of instruction, or -1 if the instruction does not exist
+ */
+ public int indexOf(Instruction instruction) {
+ return fInstructions.indexOf(instruction);
+ }
+
+ /**
+ * Tell whether the instruction sequence is empty
+ *
+ * @return true if the instruction sequence is empty, and false otherwise
+ */
+ public boolean isEmpty() {
+ return fInstructions.isEmpty();
+ }
+
+ /**
+ * Insert the instruction at the given index. If the index is less than 0 or
+ * greater than the current instruction count, the instruction is added at
+ * the end of the sequence.
+ *
+ * Instructs the instructions to update their program counters.
+ */
+ public void insert(Instruction instruction, int index) {
+ fInstructions.add(index, instruction);
+ }
+
+ /**
+ * Get the instruction at the given address
+ *
+ * @param address
+ * - instruction address
+ * @return instruction at the given address
+ */
+ public Instruction get(int address) {
+ return fInstructions.get(address);
+ }
+
+ /**
+ * Get the index of the last instruction in the sequence
+ *
+ * @return size of the instruction sequence - 1
+ */
+ public int getEnd() {
+ return fInstructions.size() - 1;
+ }
+
+ /**
+ * Remove no-ops from the instruction list
+ */
+ public void removeNoOps() {
+ for (Iterator<Instruction> iter = fInstructions.iterator(); iter.hasNext(); ) {
+ if (iter.next() instanceof NoOp) {
+ iter.remove();
+ }
+ }
+ }
+
+ /**
+ * Fixup instructions like: *(<type>*)&<something>
+ *<p>
+ * We want to avoid getting the address of a register or bitfield and
+ * causing an error, so just make a synthetic "cast value" operator here.
+ * <p>
+ * If there is a concern about incorrectly avoiding an error here --
+ * "if the variable is in a register, then this should fail" -- I argue that
+ * the user doesn't necessarily know it's in a register at compile time, and
+ * we should try harder to allow evaluating such an expression. Asking him to
+ * modify and rebuild his code so the variable will be in memory is a bit draconian.
+ * @param typeEngine
+ */
+ public void reduceCasts(TypeEngine typeEngine) {
+ boolean anyChanges = false;
+ for (int i = 0; i < fInstructions.size(); i++) {
+ Instruction inst = fInstructions.get(i);
+ if ((inst instanceof OperatorAddrOf) && i + 1 < fInstructions.size()) {
+ Instruction inst2 = fInstructions.get(i + 1);
+ if (inst2 instanceof OperatorCast && i + 2 < fInstructions.size()) {
+ Instruction inst3 = fInstructions.get(i + 2);
+ if (inst3 instanceof OperatorIndirection) {
+ // see if it's a pointer or array
+ IType castType = null;
+ try {
+ castType = TypeUtils.getStrippedType(((OperatorCast) inst2).getCastType(typeEngine));
+ } catch (CoreException e) {
+ // ignore
+ continue;
+ }
+ if (castType instanceof IPointerType || castType instanceof IArrayType) {
+ OperatorCastValue cv = new OperatorCastValue(inst.getSize(), castType.getType());
+ fInstructions.set(i, cv);
+ fInstructions.set(i + 1, new NoOp(inst2.getSize()));
+ fInstructions.set(i + 2, new NoOp(inst3.getSize()));
+ anyChanges = true;
+ }
+ }
+ } else if (inst2 instanceof OperatorCastValue) {
+ // see if it's a pointer or array
+ IType castType;
+ try {
+ castType = TypeUtils.getStrippedType(((OperatorCastValue)inst2).getCastType(typeEngine));
+ } catch (CoreException e) {
+ // ignore
+ continue;
+ }
+ if (castType instanceof IPointerType || castType instanceof IArrayType) {
+ OperatorCastValue cv = new OperatorCastValue(inst.getSize(), castType.getType());
+ fInstructions.set(i, cv);
+ fInstructions.set(i + 1, new NoOp(inst2.getSize()));
+ anyChanges = true;
+ }
+ }
+ }
+ }
+ if (anyChanges) {
+ removeNoOps();
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.util.EmptyStackException;
+import java.util.Stack;
+
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.symbols.TypeEngine;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public class Interpreter {
+ private final Instruction[] instructions;
+ private int instructionCounter;
+ private final IDMContext context;
+ private Stack<OperandValue> stack;
+ private OperandValue lastValue;
+
+ private boolean fStopped = false;
+ private final EDCServicesTracker tracker;
+ private final TypeEngine typeEngine;
+
+ /**
+ * Constructor for fInterpreter
+ * @param context
+ * @param instructionSequence
+ * - instruction sequence to execute
+ * @param context
+ * - instruction context
+ */
+ public Interpreter(EDCServicesTracker tracker, IDMContext context,
+ TypeEngine typeEngine,
+ InstructionSequence instructionSequence) {
+ this.tracker = tracker;
+ this.context = context;
+ this.typeEngine = typeEngine;
+ this.instructions = instructionSequence.getInstructions();
+ }
+
+ public EDCServicesTracker getServicesTracker() {
+ return tracker;
+ }
+
+ /**
+ * Execute an instruction sequence
+ *
+ * @throws CoreException
+ */
+ public void execute() throws CoreException {
+ reset();
+ while (instructionCounter < instructions.length && !fStopped) {
+ Instruction instruction = instructions[instructionCounter++];
+ synchronized(instruction)
+ {
+ Interpreter old = instruction.fInterpreter;
+ instruction.setInterpreter(this);
+ instruction.execute();
+ instruction.setInterpreter(old);
+ }
+ }
+ }
+
+ /**
+ * Stop the fInterpreter
+ */
+ public void stop() {
+ fStopped = true;
+ }
+
+ /**
+ * Reset the fInterpreter
+ */
+ private void reset() {
+ stack = new Stack<OperandValue>();
+ instructionCounter = 0;
+ }
+
+ /**
+ * Jump to a relative instruction counter offset
+ *
+ * @param offset
+ * - offset from the current instruction counter
+ */
+ public void jump(int offset) {
+ instructionCounter += offset;
+ }
+
+ /**
+ * Push an object on the stack. Disables garbage collection for any interim
+ * object pushed onto the stack. Objects are released after the evaluation
+ * completes.
+ *
+ * @param object
+ */
+ public void push(OperandValue object) {
+ stack.push(object);
+ }
+
+ /**
+ * Tell whether the stack is empty
+ *
+ * @return true if the stack is empty, and false otherwise
+ */
+ public boolean isEmpty() {
+ return stack.isEmpty();
+ }
+
+ /**
+ * Peek at the top object of the stack
+ *
+ * @return object on the top of the stack
+ */
+ public OperandValue peek() {
+ return stack.peek();
+ }
+
+ /**
+ * Pop an object off of the stack
+ *
+ * @return object on the top of the stack
+ */
+ public OperandValue pop() throws EmptyStackException {
+ return stack.pop();
+ }
+
+ /**
+ * Get the context for the fInterpreter
+ *
+ * @return fInterpreter context
+ */
+ public IDMContext getContext() {
+ return context;
+ }
+
+ /**
+ * Get current instruction result
+ *
+ * @return current top of stack, or the last stack value if the stack is
+ * <code>null</code> or empty
+ */
+ public OperandValue getResult() {
+ if (stack == null || stack.isEmpty()) {
+ return lastValue;
+ }
+ OperandValue top = stack.peek();
+ return top;
+ }
+
+ public TypeEngine getTypeEngine() {
+ return typeEngine;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * No-op instruction
+ */
+public class NoOp extends CompoundInstruction {
+
+ /**
+ * Constructor for no-op instruction
+ *
+ * @param start
+ * - instruction start
+ */
+ public NoOp(int start) {
+ super(start);
+ }
+
+ /**
+ * Resolve a no-op instruction
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+import java.text.MessageFormat;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.IAggregate;
+import org.eclipse.cdt.debug.edc.internal.symbols.IBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IEnumeration;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IQualifierType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IReferenceType;
+import org.eclipse.cdt.debug.edc.internal.symbols.TypedefType;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.debug.edc.symbols.VariableLocationFactory;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This is the basic unit of work when evaluating expressions. It carries
+ * a type and a number. Subclasses like {@link VariableWithValue} may hold
+ * additional information, like a specific variable and a location.
+ */
+public class OperandValue {
+
+ protected IVariableLocation valueLocation;
+
+ protected Number value;
+ protected String stringValue;
+ protected IType type;
+ protected boolean isBitField;
+
+
+ public OperandValue(IType type) {
+ this(type, false);
+ }
+
+ public OperandValue(IType type, boolean isBitField) {
+ this.type = type;
+ this.isBitField = isBitField;
+ }
+
+ // operand order switched so we don't accidentally invoke isBitField variant with Boolean
+ public OperandValue(Number value, IType type) {
+ this.type = type;
+ this.value = value;
+ }
+
+ public OperandValue(String string, IType type) {
+ this(type, false);
+ this.stringValue = string;
+ }
+
+ /**
+ * @return the type
+ */
+ public IType getValueType() {
+ return type;
+ }
+
+ public IVariableLocation getValueLocation() {
+ return valueLocation;
+ }
+
+ public void setValueLocation(IVariableLocation valueLocation) {
+ this.valueLocation = valueLocation;
+ }
+
+ public Number getValueLocationAddress() throws CoreException {
+ IVariableLocation location = getValueLocation();
+ if (location == null)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperandValue_VariableNoAddress);
+ return getLocationAddress(location);
+ }
+
+ public Number getValueByType(IType varType, IVariableLocation location)
+ throws CoreException {
+ Number result;
+
+ if (varType != null) {
+ if (varType instanceof ICPPBasicType)
+ result = getBasicTypeValue(varType, location);
+ else if (varType instanceof IPointerType)
+ result = getBasicTypeValue(varType, location);
+ else if (varType instanceof IReferenceType) {
+ result = getBasicTypeValue(varType, location);
+
+ // use result as the location of the referenced variable's value
+ if (location != null && location.getContext() != null && location.getServicesTracker() != null) {
+ IDMContext context = location.getContext();
+ EDCServicesTracker servicesTracker = location.getServicesTracker();
+ IType pointedTo = TypeUtils.getStrippedType(varType).getType();
+ if (pointedTo instanceof ICPPBasicType || pointedTo instanceof IPointerType ||
+ pointedTo instanceof IEnumeration) {
+ IVariableLocation newLocation = VariableLocationFactory.createMemoryVariableLocation(servicesTracker, context, result);
+ setValueLocation(newLocation);
+ result = getBasicTypeValue(pointedTo, newLocation);
+ }
+ }
+ } else if (varType instanceof IAggregate)
+ result = getAggregateTypeValue((IAggregate) varType, location);
+ else if (varType instanceof IQualifierType || varType instanceof TypedefType)
+ result = getValueByType(varType.getType(), location);
+ else if (varType instanceof IEnumeration)
+ result = getBasicTypeValue(varType, location);
+ else {
+ assert false;
+ throw EDCDebugger.newCoreException(ASTEvalMessages.VariableWithValue_UnhandledType + varType.getName());
+ }
+ } else {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.VariableWithValue_VariableHasNoType);
+ }
+ return result;
+ }
+
+ public Number getValue() throws CoreException {
+ return value;
+ }
+
+ public void setValue(Number value) {
+ this.value = value;
+ }
+
+ public String getStringValue() {
+ return stringValue;
+ }
+
+ public void setStringValue(String stringValue) {
+ this.stringValue = stringValue;
+ }
+
+ public void setAddressValue(IVariableLocation location) throws CoreException {
+ setValue(getLocationAddress(location));
+ }
+
+ protected Number getLocationAddress(IVariableLocation location) throws CoreException {
+ IAddress addr = location.getAddress();
+ if (addr == null)
+ throw EDCDebugger.newCoreException(
+ MessageFormat.format(ASTEvalMessages.OperandValue_CannotGetAddress, location.getLocationName()));
+ return addr.getValue();
+ }
+
+ private Number getAggregateTypeValue(IAggregate varType, IVariableLocation location)
+ throws CoreException {
+ // assumes that an array, class, struct, or union is always located in
+ // memory, not in a register
+ return getLocationAddress(location);
+ }
+
+ private Number getBasicTypeValue(IType varType, IVariableLocation location)
+ throws CoreException {
+ if (EDCTrace.VARIABLE_VALUE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { varType, location })); }
+ Number result = null;
+ try {
+ result = doGetBasicTypeValue(varType, location);
+ return result;
+ } finally {
+ if (EDCTrace.VARIABLE_VALUE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(result)); }
+ }
+ }
+
+ private Number doGetBasicTypeValue(IType varType, IVariableLocation location) throws CoreException {
+
+ Number result = null;
+ int varSize = varType.getByteSize();
+
+ // get characteristics of the type
+ int basicType = IBasicType.t_unspecified;
+ boolean isSigned = false;
+ boolean isShort = false;
+ boolean isLong = false;
+ boolean isLongLong = false;
+ boolean isComplex = false;
+
+ if (varType instanceof IBasicType) {
+ IBasicType type = (IBasicType) varType;
+ basicType = type.getBaseType();
+
+ if (basicType == ICPPBasicType.t_void)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperandValue_CannotReadVoid);
+ if (basicType == ICPPBasicType.t_unspecified)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperandValue_CannotReadUnspecifiedType);
+
+ isSigned = type.isSigned();
+ isShort = type.isShort();
+ isLong = type.isLong();
+
+ if (varType instanceof ICPPBasicType) {
+ ICPPBasicType cppType = (ICPPBasicType) varType;
+ isLongLong = cppType.isLongLong();
+ isComplex = cppType.isComplex();
+ }
+ } else if (varType instanceof IPointerType) {
+ // treat pointer as an unsigned int
+ basicType = IBasicType.t_int;
+ } else if (varType instanceof IEnumerator){
+ // treat enumerator as a signed int
+ basicType = IBasicType.t_int;
+ isSigned = true;
+ } else {
+ // treat unknown type as an unsigned int
+ basicType = IBasicType.t_int;
+ isSigned = false;
+ }
+
+ // if variable's size is 0, use its default size
+ if (varSize == 0) {
+ varSize = getDefaultSize(varType, basicType, isShort, isLong, isLongLong, isComplex);
+ }
+
+ // all other locations
+ IVariableLocation varLocation = location;
+ BigInteger varValue = null;
+
+ if (varLocation != null) {
+ varValue = varLocation.readValue(varSize);
+ } else {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.VariableWithValue_UnknownLocation);
+ }
+
+ switch (basicType) {
+ case IBasicType.t_float:
+ case IBasicType.t_double:
+ if (varSize == 4) {
+ result = Float.intBitsToFloat(varValue.intValue());
+ } else if (varSize == 8) {
+ result = Double.longBitsToDouble(varValue.longValue());
+ } else {
+ // TODO: support 12-byte long double read from register
+ throw EDCDebugger.newCoreException(ASTEvalMessages.VariableWithValue_NoTwelveByteLongDouble);
+ }
+ break;
+
+ case ICPPBasicType.t_bool:
+ case ICPPBasicType.t_wchar_t:
+ case IBasicType.t_char:
+ case IBasicType.t_int:
+ case IBasicType.t_void:
+ if (isSigned) {
+ // as needed, mask the value and sign-extend
+ if (varSize == 4) {
+ result = new Integer(varValue.intValue());
+ } else if (varSize == 2) {
+ int intResult = varValue.intValue() & 0xffff;
+ if ((intResult & 0x00008000) != 0)
+ intResult |= 0xffff0000;
+ result = new Integer(intResult);
+ } else if (varSize == 1) {
+ int intResult = varValue.intValue() & 0xff;
+ if ((intResult & 0x00000080) != 0)
+ intResult |= 0xffffff00;
+ result = new Integer(intResult);
+ } else {
+ // assume an 8-byte long is the default
+ result = new Long(varValue.longValue());
+ }
+ } else {
+ if (varSize == 4) {
+ result = new Long(varValue.longValue() & 0xffffffffL); // keep it unsigned
+ } else if (varSize == 2) {
+ result = new Integer(varValue.intValue() & 0xffff);
+ } else if (varSize == 1) {
+ result = new Integer(varValue.intValue() & 0xff);
+ } else {
+ // assume an 8-byte long is the default
+ result = new Long(varValue.longValue());
+ }
+ }
+ break;
+
+ case IBasicType.t_unspecified:
+ default:
+ assert false;
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperandValue_UnhandledType);
+ }
+ return result;
+ }
+
+ private int getDefaultSize(IType varType, int basicType, boolean isShort,
+ boolean isLong, boolean isLongLong, boolean isComplex) {
+ int varSize = 0;
+
+ // debug info should never claim something is zero size any more
+ /*
+ Object retrievedSize = null;
+ ITargetEnvironment targetEnvironmentService = servicesTracker.getService(ITargetEnvironment.class);
+ if (targetEnvironmentService != null) {
+ Map<Integer, Integer> sizes = targetEnvironmentService.getBasicTypeSizes();
+
+ switch(basicType) {
+ case IBasicType.t_char:
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_CHAR);
+ break;
+ case IBasicType.t_int:
+ if (isShort) {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_SHORT);
+ } else if (isLongLong) {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_LONG_LONG);
+ } else if (isLong) {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_LONG);
+ } else {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_INT);
+ }
+ break;
+ case IBasicType.t_float:
+ if (isComplex) {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_FLOAT_COMPLEX);
+ } else {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_FLOAT);
+ }
+ break;
+ case IBasicType.t_double:
+ if (isComplex) {
+ if (isLong) {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_LONG_DOUBLE_COMPLEX);
+ } else {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_DOUBLE_COMPLEX);
+ }
+ } else {
+ if (isLong) {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_LONG_DOUBLE);
+ } else {
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_DOUBLE);
+ }
+ }
+ break;
+ case ICPPBasicType.t_bool:
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_BOOL);
+ break;
+ case ICPPBasicType.t_wchar_t:
+ retrievedSize = sizes.get(TypeUtils.BASIC_TYPE_WCHAR_T);
+ break;
+ default:
+ retrievedSize = 1;
+ }
+ }
+ if (retrievedSize != null)
+ varSize = (Integer) retrievedSize;
+ */
+
+ varSize = 1;
+ return varSize;
+ }
+
+ /**
+ * Get the operand value as a long
+ * @return long
+ * @throws CoreException
+ */
+ public long getLongValue() throws CoreException {
+ return getValue().longValue();
+ }
+
+ /**
+ * @return
+ */
+ public boolean isFloating() {
+ return value instanceof Float || value instanceof Double;
+ }
+
+
+ public boolean isBitField() {
+ return isBitField;
+ }
+
+ /**
+ * Get the value as a BigInteger
+ * @return value or 0 if not a number
+ */
+ public BigInteger getBigIntValue() throws CoreException {
+ BigInteger bigIntVal;
+ if (getValue() == null)
+ return BigInteger.ZERO;
+ if (value instanceof BigInteger)
+ bigIntVal = (BigInteger) value;
+ else
+ bigIntVal = BigInteger.valueOf(value.longValue());
+ return bigIntVal;
+ }
+
+ public OperandValue copyWithType(IType otherType) {
+ OperandValue value = new OperandValue(otherType);
+ value.value = this.value;
+ value.stringValue = this.stringValue;
+ value.valueLocation = this.valueLocation;
+ return value;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.PointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.RegisterVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Unary address of operation "&"
+ */
+public class OperatorAddrOf extends CompoundInstruction {
+
+ /**
+ * Constructor for address of operation "&"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorAddrOf(int start) {
+ super(start);
+ }
+
+ /**
+ * Resolve an address of operation "&"
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue operand = popValue();
+
+ // only allow address of an lvalue
+ if (operand.getValueLocation() == null) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorAddrOf_RequiresVariable);
+ }
+
+ IType subType = operand.getValueType();
+
+ // do not allow a variable that is in a register
+ if (operand.getValueLocation() instanceof RegisterVariableLocation) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorAddrOf_NoRegister);
+ }
+
+ // do not allow a bit-field
+ if (operand.isBitField()) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorAddrOf_NoBitField);
+ }
+
+ PointerType pointer = new PointerType();
+ pointer.setType(subType);
+
+ OperandValue addr = new OperandValue(pointer);
+ addr.setValueLocation(operand.getValueLocation());
+ addr.setValue(operand.getValueLocationAddress());
+ push(addr);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary AND operation "&"
+ */
+public class OperatorBinaryAnd extends BinaryOperator {
+
+ /**
+ * Constructor for a binary AND operation "&"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorBinaryAnd(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary AND operation "&"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorBinaryAnd(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get int result of applying binary AND "&" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> & <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand & rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary AND "&" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> & <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand & rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary AND "&" to two BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> & <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ return leftOperand.and(rightOperand);
+ }
+
+ /**
+ * Get float result of applying binary AND "&" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return 0
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get double result of applying binary AND "&" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return 0
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get boolean result of applying binary AND "&" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>leftOperand</code> & <code>rightOperand</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return leftOperand & rightOperand;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary OR operation "|"
+ */
+public class OperatorBinaryOr extends BinaryOperator {
+
+ /**
+ * Constructor for a binary OR operation "|"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorBinaryOr(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary OR operation "|"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorBinaryOr(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get int result of applying binary OR "|" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> | <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand | rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary OR "|" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> | <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand | rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary OR "|" to two BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> | <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ return leftOperand.or(rightOperand);
+ }
+
+ /**
+ * Get float result of applying binary OR "|" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return 0
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get double result of applying binary OR "|" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return 0
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get boolean result of applying binary OR "|" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>leftOperand</code> | <code>rightOperand</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return leftOperand | rightOperand;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary XOR operation "^"
+ */
+public class OperatorBinaryXor extends BinaryOperator {
+
+ /**
+ * Constructor for a binary XOR operation "^"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorBinaryXor(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary XOR operation "^"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorBinaryXor(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get int result of applying binary XOR "^" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> ^ <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand ^ rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary XOR "^" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> ^ <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand ^ rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary XOR "^" to two BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> ^ <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ return leftOperand.xor(rightOperand);
+ }
+
+ /**
+ * Get float result of applying binary XOR "^" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return 0
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get double result of applying binary XOR "^" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return 0
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get boolean result of applying binary XOR "^" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>leftOperand</code> ^ <code>rightOperand</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return leftOperand ^ rightOperand;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Unary bit-wise NOT operation "~"
+ */
+public class OperatorBitwiseNot extends UnaryOperator {
+
+ /**
+ * Constructor for a unary bit-wise NOT operation "~"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorBitwiseNot(int start) {
+ super(start);
+ }
+
+ /**
+ * Constructor for a unary bit-wise NOT operation "~"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ public OperatorBitwiseNot(int resultId, boolean isAssignmentOperator, int start) {
+ super(start);
+ }
+
+ /**
+ * Get int result of applying unary bit-wise NOT "~" to an int
+ *
+ * @param operand
+ * - int operand
+ * @return ~<code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int operand) throws CoreException {
+ return ~operand;
+ }
+
+ /**
+ * Get long result of applying unary bit-wise NOT "~" to a long
+ *
+ * @param operand
+ * - long operand
+ * @return ~<code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long operand) throws CoreException {
+ return ~operand;
+ }
+
+ /**
+ * Get BigInteger result of applying unary bit-wise NOT "~" to a BigInteger
+ *
+ * @param operand
+ * - BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return ~<code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger operand, int length) throws CoreException {
+ return operand.not();
+ }
+
+ /**
+ * Get float result of applying unary bit-wise NOT "~" to a float
+ *
+ * @param operand
+ * - float operand
+ * @return 0
+ */
+ @Override
+ protected float getFloatResult(float operand) {
+ return 0;
+ }
+
+ /**
+ * Get double result of applying unary bit-wise NOT "~" to a double
+ *
+ * @param operand
+ * - double operand
+ * @return 0
+ */
+ @Override
+ protected double getDoubleResult(double operand) {
+ return 0;
+ }
+
+ /**
+ * Get boolean result of applying unary bit-wise NOT "~" to a boolean
+ *
+ * @param operand
+ * - boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean operand) {
+ return false;
+ }
+
+ /**
+ * Get string result of applying unary bit-wise NOT "~" to a string
+ *
+ * @param operand
+ * - string operand
+ * @return <code>null</code>
+ * @throws CoreException
+ */
+ @Override
+ protected String getStringResult(String operand) throws CoreException {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.UnsupportedStringOperation);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.core.dom.ast.IASTCastExpression;
+import org.eclipse.cdt.core.dom.ast.IASTTypeId;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeEngine;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ *
+ */
+public class OperatorCast extends CompoundInstruction {
+
+ private final IASTCastExpression castExpr;
+ private IType castType;
+
+ public OperatorCast(int start, IASTCastExpression castExpr) {
+ super(start);
+ this.castExpr = castExpr;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.Instruction#execute()
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue value = fInterpreter.pop();
+ if (value.getStringValue() != null)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorCast_CannotCastString);
+
+ castType = getCastType(fInterpreter.getTypeEngine());
+
+ IVariableLocation location = value.getValueLocation();
+
+ OperandValue castValue = new OperandValue(castType);
+ Number origValue = value.getValue();
+ Number castedValue = origValue;
+
+ if (castType instanceof ICPPBasicType) {
+ ICPPBasicType cppType = (ICPPBasicType) castType;
+ // when casting to primtive integral type, reduce or zero/sign extend
+ if (cppType.getBaseType() == ICPPBasicType.t_char ||
+ cppType.getBaseType() == ICPPBasicType.t_int ||
+ cppType.getBaseType() == ICPPBasicType.t_wchar_t ||
+ cppType.getBaseType() == ICPPBasicType.t_bool) {
+ switch (cppType.getByteSize()) {
+ case 1:
+ if (cppType.isSigned())
+ castedValue = origValue.byteValue();
+ else
+ castedValue = origValue.byteValue() & 0xff;
+ break;
+ case 2:
+ if (cppType.isSigned())
+ castedValue = origValue.shortValue();
+ else
+ castedValue = origValue.shortValue() & 0xfffff;
+ break;
+ case 4:
+ if (cppType.isSigned())
+ castedValue = origValue.intValue();
+ else
+ castedValue = origValue.intValue() & 0xffffffffL;
+ break;
+ case 8:
+ if (cppType.isSigned())
+ castedValue = BigInteger.valueOf(origValue.longValue());
+ else
+ castedValue = BigInteger.valueOf(origValue.longValue()).and(Mask8Bytes);
+ break;
+ }
+ } else if (cppType.getBaseType() == ICPPBasicType.t_float ||
+ cppType.getBaseType() == ICPPBasicType.t_double) {
+ // and be sure integers promoted to floats, if needed
+ switch (cppType.getByteSize()) {
+ case 4:
+ castedValue = Float.valueOf(origValue.longValue());
+ break;
+ case 8:
+ case 12:
+ castedValue = Double.valueOf(origValue.longValue());
+ break;
+ }
+ }
+ }
+
+ castValue.setValue(castedValue);
+ castValue.setValueLocation(location);
+
+ fInterpreter.push(castValue);
+ }
+
+ /**
+ * @return
+ * @throws CoreException
+ */
+ public IType getCastType(TypeEngine typeEngine) throws CoreException {
+ if (castType == null) {
+ IASTTypeId typeId = castExpr.getTypeId();
+ castType = typeEngine.getTypeForTypeId(typeId);
+ }
+ return castType;
+ }
+
+ public IASTCastExpression getCastExpr() {
+ return castExpr;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.cdt.core.dom.ast.IASTCastExpression;
+import org.eclipse.cdt.core.dom.ast.IASTTypeId;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.ValueVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeEngine;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This operator directly casts the value of an expression without going through
+ * "*(type*)&expr".
+ */
+public class OperatorCastValue extends CompoundInstruction {
+ private IASTCastExpression castExpr;
+ private IType castType;
+
+ public OperatorCastValue(int start, IType castType) {
+ super(start);
+ this.castType = castType;
+ }
+
+ public OperatorCastValue(int start, IASTCastExpression castExpr) {
+ super(start);
+ this.castExpr = castExpr;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.Instruction#execute()
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue value = fInterpreter.pop();
+ if (value.getStringValue() != null)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorCast_CannotCastString);
+
+ castType = getCastType(fInterpreter.getTypeEngine());
+
+ IVariableLocation location = value.getValueLocation();
+ OperandValue castValue = new OperandValue(castType);
+
+ if (location == null)
+ location = new ValueVariableLocation(value.getBigIntValue());
+
+ Number castedValue = castValue.getValueByType(castType, location);
+
+ castValue.setValue(castedValue);
+ castValue.setValueLocation(location);
+
+ fInterpreter.push(castValue);
+ }
+
+ public IType getCastType(TypeEngine typeEngine) throws CoreException {
+ if (castType == null) {
+ IASTTypeId typeId = castExpr.getTypeId();
+ castType = typeEngine.getTypeForTypeId(typeId);
+ }
+ return castType;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary divide operation "/"
+ */
+public class OperatorDivide extends BinaryOperator {
+
+ /**
+ * Constructor for a binary divide operation "/"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorDivide(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary divide operation "/"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorDivide(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get int result of applying binary divide "/" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> / <code>rightOperand</code>
+ * @throws CoreException
+ * if <code>rightOperand</code> is 0
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ if (rightOperand == 0)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.DivideByZero);
+ else
+ return leftOperand / rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary divide "/" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> / <code>rightOperand</code>
+ * @throws CoreException
+ * if <code>rightOperand</code> is 0
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ if (rightOperand == 0)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.DivideByZero);
+ else
+ return leftOperand / rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary divide "/" to two BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> / <code>rightOperand</code>
+ * @throws CoreException
+ * if <code>rightOperand</code> is 0
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ if (rightOperand.equals(BigInteger.ZERO))
+ throw EDCDebugger.newCoreException(ASTEvalMessages.DivideByZero);
+ else
+ return leftOperand.divide(rightOperand);
+ }
+
+ /**
+ * Get float result of applying binary divide "/" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return <code>leftOperand</code> / <code>rightOperand</code>
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand / rightOperand;
+ }
+
+ /**
+ * Get double result of applying binary divide "/" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return <code>leftOperand</code> / <code>rightOperand</code>
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand / rightOperand;
+ }
+
+ /**
+ * Get boolean result of applying binary divide "/" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary equals operation "=="
+ */
+public class OperatorEquals extends BinaryLogicalOperator {
+
+ /**
+ * Constructor for a binary equals operation "=="
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorEquals(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary equals operation "=="
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorEquals(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get boolean result of comparing two ints with equals "=="
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return true if <code>leftOperand</code> == <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand == rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two longs with equals "=="
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return true if <code>leftOperand</code> == <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand == rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two BigIntegers with equals "=="
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @return true if <code>leftOperand</code> == <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand) throws CoreException {
+ return leftOperand.equals(rightOperand);
+ }
+
+ /**
+ * Get boolean result of comparing two floats with equals "=="
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return true if <code>leftOperand</code> == <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand == rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two doubles with equals "=="
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return true if <code>leftOperand</code> == <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand == rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two booleans with equals "=="
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return true if <code>leftOperand</code> == <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return leftOperand == rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two strings with equals "=="
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return true if <code>leftOperand</code> == <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ return leftOperand.equals(rightOperand);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary greater or equal operation ">="
+ */
+public class OperatorGreaterEqual extends BinaryLogicalOperator {
+
+ /**
+ * Constructor for a binary greater or equal operation ">="
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorGreaterEqual(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary greater or equal operation ">="
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorGreaterEqual(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get boolean result of comparing two ints with greater or equal ">="
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return true if <code>leftOperand</code> >= <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand >= rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two longs with greater or equal ">="
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return true if <code>leftOperand</code> >= <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand >= rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two BigIntegers with greater or equal
+ * ">="
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @return true if <code>leftOperand</code> >= <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand) throws CoreException {
+ return leftOperand.compareTo(rightOperand) >= 0;
+ }
+
+ /**
+ * Get boolean result of comparing two floats with greater or equal ">="
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return true if <code>leftOperand</code> >= <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand >= rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two doubles with greater or equal ">="
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return true if <code>leftOperand</code> >= <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand >= rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two booleans with greater or equal ">="
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+
+ /**
+ * Get boolean result of comparing two strings with greater or equal ">="
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return true if <code>leftOperand</code> >= <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ return leftOperand.compareTo(rightOperand) >= 0;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary greater than operation ">"
+ */
+public class OperatorGreaterThan extends BinaryLogicalOperator {
+
+ /**
+ * Constructor for a binary greater than operation ">"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorGreaterThan(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary greater than operation ">"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorGreaterThan(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get boolean result of comparing two ints with greater than ">"
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return true if <code>leftOperand</code> > <code>rightOperand</code>, and
+ * false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand > rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two longs with greater than ">"
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return true if <code>leftOperand</code> > <code>rightOperand</code>, and
+ * false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand > rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two BigIntegers with greater than ">"
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @return true if <code>leftOperand</code> > <code>rightOperand</code>, and
+ * false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand) throws CoreException {
+ return leftOperand.compareTo(rightOperand) > 0;
+ }
+
+ /**
+ * Get boolean result of comparing two floats with greater than ">"
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return true if <code>leftOperand</code> > <code>rightOperand</code>, and
+ * false otherwise
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand > rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two doubles with greater than ">"
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return true if <code>leftOperand</code> > <code>rightOperand</code>, and
+ * false otherwise
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand > rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two booleans with greater than ">"
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+
+ /**
+ * Get boolean result of comparing two longs with greater than ">"
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return true if <code>leftOperand</code> > <code>rightOperand</code>, and
+ * false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ return leftOperand.compareTo(rightOperand) > 0;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.text.MessageFormat;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.IAggregate;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IEnumeration;
+import org.eclipse.cdt.debug.edc.internal.symbols.IField;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISubroutineType;
+import org.eclipse.cdt.debug.edc.symbols.IMemoryVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.debug.edc.symbols.VariableLocationFactory;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Unary indirection operation "*"
+ */
+public class OperatorIndirection extends CompoundInstruction {
+
+ /**
+ * Constructor for a unary indirection operation "*"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorIndirection(int start) {
+ super(start);
+ }
+
+ /**
+ * Resolve a unary indirection expression
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue operand = popValue();
+
+ IType opType = TypeUtils.getUnRefStrippedType(operand.getValueType());
+
+ if (operand.getStringValue() != null) {
+ // read first char of a string constant
+ pushNewValue(opType.getType(), (int) operand.getStringValue().charAt(0));
+ return;
+ }
+
+ if (!TypeUtils.isPointerType(opType)) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorIndirection_RequiresPointer);
+ }
+
+ IPointerType pointer = (IPointerType) opType;
+ IType pointedTo = pointer.getType();
+ IType unqualifiedPointedTo = TypeUtils.getStrippedType(pointedTo);
+
+ // do not allow a pointer to a bit-field
+ if ((unqualifiedPointedTo instanceof IField) && (((IField) unqualifiedPointedTo).getBitSize() != 0)) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorIndirection_NoBitField);
+ }
+
+ OperandValue opValue = new OperandValue(unqualifiedPointedTo);
+
+ // for a lvalues (base arithmetic types, enums, and pointers), read the
+ // value and cast it to the right type
+ IMemoryVariableLocation location = VariableLocationFactory.createMemoryVariableLocation(
+ fInterpreter.getServicesTracker(), fInterpreter.getContext(),
+ operand.getValue());
+
+ if (unqualifiedPointedTo instanceof ICPPBasicType || unqualifiedPointedTo instanceof IPointerType
+ || unqualifiedPointedTo instanceof IEnumeration) {
+ int byteSize = unqualifiedPointedTo.getByteSize();
+
+ // treat ICPPBasicType of byte size 0 as a void pointer (size 4)
+ if (unqualifiedPointedTo instanceof ICPPBasicType && byteSize == 0)
+ byteSize = 4;
+
+ if (byteSize != 1 && byteSize != 2 && byteSize != 4 && byteSize != 8) {
+ throw EDCDebugger.newCoreException(MessageFormat.format(ASTEvalMessages.UnhandledSize, byteSize));
+ }
+
+ // read the value pointed to
+ Number newValue = operand.getValueByType(unqualifiedPointedTo, location);
+ opValue.setValue(newValue);
+ opValue.setValueLocation(location);
+ push(opValue);
+
+ } else if (unqualifiedPointedTo instanceof IAggregate) {
+ // for aggregates, the address of the aggregate is the value
+ // returned
+ opValue.setAddressValue(location);
+ opValue.setValueLocation(location);
+ push(opValue);
+
+ } else {
+ if (unqualifiedPointedTo instanceof ISubroutineType)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorIndirection_NoFunction);
+ else
+ throw EDCDebugger.newCoreException(MessageFormat.format(ASTEvalMessages.OperatorIndirection_UnhandledType,
+ unqualifiedPointedTo != null ? unqualifiedPointedTo.getName() : "null"));
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary less or equal operation "<="
+ */
+public class OperatorLessEqual extends BinaryLogicalOperator {
+
+ /**
+ * Constructor for a binary less or equal operation "<="
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorLessEqual(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary less or equal operation "<="
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorLessEqual(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get boolean result of comparing two ints with less or equal "<="
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return true if <code>leftOperand</code> <= <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand <= rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two longs with less or equal "<="
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return true if <code>leftOperand</code> <= <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand <= rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two BigIntegers with less or equal "<="
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @return true if <code>leftOperand</code> <= <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand) throws CoreException {
+ return leftOperand.compareTo(rightOperand) <= 0;
+ }
+
+ /**
+ * Get boolean result of comparing two floats with less or equal "<="
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return true if <code>leftOperand</code> <= <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand <= rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two doubles with less or equal "<="
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return true if <code>leftOperand</code> <= <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand <= rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two booleans with less or equal "<="
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+
+ /**
+ * Get boolean result of comparing two strings with less or equal "<="
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return true if <code>leftOperand</code> <= <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ return leftOperand.compareTo(rightOperand) <= 0;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary less than operation "<"
+ */
+public class OperatorLessThan extends BinaryLogicalOperator {
+
+/**
+ * Constructor for a binary less than operation "<"
+ *
+ * @param start - instruction start
+ */
+ public OperatorLessThan(int start) {
+ this(0, false, start);
+ }
+
+/**
+ * Constructor for a binary less than operation "<"
+ *
+ * @param resultId - for assignment, variable ID of the result
+ * @param isAssignmentOperator - whether the result is assigned
+ * @param start - instruction start
+ */
+ protected OperatorLessThan(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+/**
+ * Get boolean result of comparing two ints with less than "<"
+ *
+ * @param leftOperand - left int operand
+ * @param rightOperand - right int operand
+ * @return true if <code>leftOperand</code> < <code>rightOperand</code>, and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand < rightOperand;
+ }
+
+/**
+ * Get boolean result of comparing two longs with less than "<"
+ *
+ * @param leftOperand - left long operand
+ * @param rightOperand - right long operand
+ * @return true if <code>leftOperand</code> < <code>rightOperand</code>, and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand < rightOperand;
+ }
+
+/**
+ * Get boolean result of comparing two BigIntegers with less than "<"
+ *
+ * @param leftOperand - left BigInteger operand
+ * @param rightOperand - right BigInteger operand
+ * @return true if <code>leftOperand</code> < <code>rightOperand</code>, and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand) throws CoreException {
+ return leftOperand.compareTo(rightOperand) < 0;
+ }
+
+/**
+ * Get boolean result of comparing two floats with less than "<"
+ *
+ * @param leftOperand - left float operand
+ * @param rightOperand - right float operand
+ * @return true if <code>leftOperand</code> < <code>rightOperand</code>, and false otherwise
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand < rightOperand;
+ }
+
+/**
+ * Get boolean result of comparing two doubles with less than "<"
+ *
+ * @param leftOperand - left double operand
+ * @param rightOperand - right double operand
+ * @return true if <code>leftOperand</code> < <code>rightOperand</code>, and false otherwise
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand < rightOperand;
+ }
+
+/**
+ * Get boolean result of comparing two longs with less than "<"
+ *
+ * @param leftOperand - left long operand
+ * @param rightOperand - right long operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+
+/**
+ * Get boolean result of comparing two strings with less than "<"
+ *
+ * @param leftOperand - left string operand
+ * @param rightOperand - right string operand
+ * @return true if <code>leftOperand</code> < <code>rightOperand</code>, and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ return leftOperand.compareTo(rightOperand) < 0;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary logical AND operation "&&"
+ */
+public class OperatorLogicalAnd extends BinaryLogicalOperator {
+
+ /**
+ * Constructor for a binary logical AND operation "&&"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorLogicalAnd(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary logical AND operation "&&"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorLogicalAnd(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get boolean result of comparing two ints with logical AND "&&"
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ // as bools
+ return (leftOperand != 0) && (rightOperand != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two longs with logical AND "&&"
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ // as bools
+ return (leftOperand != 0) && (rightOperand != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two BigIntegers with logical AND "&&"
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand) throws CoreException {
+ // as bools
+ return (leftOperand.signum() != 0) && (rightOperand.signum() != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two floats with logical AND "&&"
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand, float rightOperand) {
+ // as bools
+ return (leftOperand != 0) && (rightOperand != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two doubles with logical AND "&&"
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand, double rightOperand) {
+ // as bools
+ return (leftOperand != 0) && (rightOperand != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two booleans with logical AND "&&"
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return true if <code>leftOperand</code> && <code>rightOperand</code> is
+ * true, and false otherwise
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return leftOperand && rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two strings with logical AND "&&"
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ return true;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Unary logical NOT operation "!"
+ */
+public class OperatorLogicalNot extends UnaryLogicalOperator {
+
+ /**
+ * Constructor for a unary logical NOT operation "!"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorLogicalNot(int start) {
+ super(start);
+ }
+
+ /**
+ * Get boolean result of applying logical NOT "!" to an int
+ *
+ * @param operand
+ * - int operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand) throws CoreException {
+ // as bool
+ return leftOperand == 0;
+ }
+
+ /**
+ * Get boolean result of applying logical NOT "!" to a long
+ *
+ * @param operand
+ * - long operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand) throws CoreException {
+ // as bool
+ return leftOperand == 0;
+ }
+
+ /**
+ * Get boolean result of applying logical NOT "!" to a BigInteger
+ *
+ * @param operand
+ * - BigInteger operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand) throws CoreException {
+ // as bool
+ return leftOperand.signum() == 0;
+ }
+
+ /**
+ * Get boolean result of applying logical NOT "!" to a float
+ *
+ * @param operand
+ * - float operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand) {
+ // as bool
+ return leftOperand == 0;
+ }
+
+ /**
+ * Get boolean result of applying logical NOT "!" to a double
+ *
+ * @param operand
+ * - double operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand) {
+ // as bool
+ return leftOperand == 0;
+ }
+
+ /**
+ * Get boolean result of applying logical NOT "!" to a boolean
+ *
+ * @param operand
+ * - boolean operand
+ * @return !<code>operand</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand) {
+ return !leftOperand;
+ }
+
+ /**
+ * Get boolean result of applying logical NOT "!" to a string
+ *
+ * @param operand
+ * - string operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand) throws CoreException {
+ // not of address
+ return false;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary logical OR operation "||"
+ */
+public class OperatorLogicalOr extends BinaryLogicalOperator {
+
+ /**
+ * Constructor for a binary logical OR operation "||"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorLogicalOr(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary logical OR operation "||"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorLogicalOr(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get boolean result of comparing two ints with logical OR "||"
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ // as bools
+ return (leftOperand != 0) || (rightOperand != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two longs with logical OR "||"
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ // as bools
+ return (leftOperand != 0) || (rightOperand != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two BigIntegers with logical OR "||"
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand) throws CoreException {
+ // as bools
+ return (leftOperand.signum() != 0) || (rightOperand.signum() != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two floats with logical OR "||"
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand, float rightOperand) {
+ // as bools
+ return (leftOperand != 0) || (rightOperand != 0);
+
+ }
+
+ /**
+ * Get boolean result of comparing two doubles with logical OR "||"
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand, double rightOperand) {
+ // as bools
+ return (leftOperand != 0) || (rightOperand != 0);
+ }
+
+ /**
+ * Get boolean result of comparing two booleans with logical OR "||"
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return true if <code>leftOperand</code> || <code>rightOperand</code> is
+ * true, and false otherwise
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return leftOperand || rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two strings with logical OR "||"
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return <code>false</code>
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ // address or
+ return true;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary minus operation "-"
+ */
+public class OperatorMinus extends BinaryOperator {
+
+ /**
+ * Constructor for a binary minus operation "-"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorMinus(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary minus operation "-"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorMinus(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.BinaryOperator#customHandleOperation(org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperandValue, org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperandValue)
+ */
+ @Override
+ protected boolean customHandleOperation(Interpreter fInterpreter,
+ OperandValue left, OperandValue right) throws CoreException {
+ IType rightType = null;
+ IType leftType = null;
+
+ boolean isLeftPointer = false;
+ leftType = TypeUtils.getStrippedType(left.getValueType());
+ isLeftPointer = leftType instanceof IPointerType || leftType instanceof IArrayType;
+
+ boolean isRightPointer = false;
+ rightType = TypeUtils.getStrippedType(right.getValueType());
+ isRightPointer = rightType instanceof IPointerType || rightType instanceof IArrayType;
+
+ // no pointer operands
+ if (!isLeftPointer && !isRightPointer) {
+ return false;
+ }
+
+ // only pointer is on the right
+ if (!isLeftPointer && isRightPointer) {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorMinus_NonPtrMinusPtr);
+ }
+
+ // ignore strings
+ if (getValueType(left) == T_String && getValueType(right) == T_String)
+ return false;
+
+ BigInteger bigIntAddress = left.getBigIntValue();
+
+ BigInteger aggregateSize;
+ if (leftType instanceof IPointerType)
+ aggregateSize = BigInteger.valueOf(leftType.getByteSize());
+ else
+ aggregateSize = BigInteger.valueOf(TypeUtils.getStrippedType(leftType.getType())
+ .getByteSize());
+
+ if (!isRightPointer) {
+ right = convertForPromotion(right);
+ }
+
+ BigInteger subtractAmount = right.getBigIntValue();
+
+ if (!isRightPointer)
+ subtractAmount = subtractAmount.multiply(aggregateSize);
+
+ bigIntAddress = bigIntAddress.subtract(subtractAmount);
+
+ // if both are pointers, subtract, then divide by size of what's pointed
+ // to
+ if (isRightPointer && aggregateSize.longValue() != 0)
+ bigIntAddress = bigIntAddress.divide(aggregateSize);
+
+ if (isRightPointer)
+ pushNewValue(left.getValueType(), bigIntAddress);
+ else
+ pushNewValue(fInterpreter.getTypeEngine().getPointerSizeType(), bigIntAddress);
+
+ return true;
+ }
+
+ /**
+ * Get int result of applying binary minus "-" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> - <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand - rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary minus "-" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> - <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand - rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary minus "-" to two BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> - <code>rightOperand</code>, truncated
+ * to 8 bytes
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ return leftOperand.subtract(rightOperand).and(Instruction.Mask8Bytes);
+ }
+
+ /**
+ * Get float result of applying binary minus "-" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return <code>leftOperand</code> - <code>rightOperand</code>
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand - rightOperand;
+ }
+
+ /**
+ * Get double result of applying binary minus "-" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return <code>leftOperand</code> - <code>rightOperand</code>
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand - rightOperand;
+ }
+
+ /**
+ * Get boolean result of applying binary minus "-" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary modulo operation "%"
+ */
+public class OperatorModulo extends BinaryOperator {
+
+ /**
+ * Constructor for a binary modulo operation "%"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorModulo(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary modulo operation "%"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorModulo(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get int result of applying binary modulo "%" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> % <code>rightOperand</code>
+ * @throws CoreException
+ * if <code>rightOperand</code> is 0
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ if (rightOperand == 0)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.DivideByZero);
+ else
+ return leftOperand % rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary modulo "%" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> % <code>rightOperand</code>
+ * @throws CoreException
+ * if <code>rightOperand</code> is 0
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ if (rightOperand == 0)
+ throw EDCDebugger.newCoreException(ASTEvalMessages.DivideByZero);
+ else
+ return leftOperand % rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary modulo "%" to two BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> % <code>rightOperand</code>
+ * @throws CoreException
+ * if <code>rightOperand</code> is 0
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ if (rightOperand.equals(BigInteger.ZERO))
+ throw EDCDebugger.newCoreException(ASTEvalMessages.DivideByZero);
+ else
+ return leftOperand.mod(rightOperand);
+ }
+
+ /**
+ * Get float result of applying binary modulo "%" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return 0
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand % rightOperand;
+ }
+
+ /**
+ * Get double result of applying binary modulo "%" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return 0
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand % rightOperand;
+ }
+
+ /**
+ * Get boolean result of applying binary modulo "%" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary multiply operation "*"
+ */
+public class OperatorMultiply extends BinaryOperator {
+
+ /**
+ * Constructor for a binary multiply operation "*"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorMultiply(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary multiply operation "*"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorMultiply(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get int result of applying binary multiply "*" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> * <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand * rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary multiply "*" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> * <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand * rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary multiply "*" to two BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> * <code>rightOperand</code>, truncated
+ * to 8 bytes
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ return leftOperand.multiply(rightOperand).and(Instruction.Mask8Bytes);
+ }
+
+ /**
+ * Get float result of applying binary multiply "*" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return <code>leftOperand</code> * <code>rightOperand</code>
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand * rightOperand;
+ }
+
+ /**
+ * Get double result of applying binary multiply "*" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return <code>leftOperand</code> * <code>rightOperand</code>
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand * rightOperand;
+ }
+
+ /**
+ * Get boolean result of applying binary multiply "*" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary not equals operation "!="
+ */
+public class OperatorNotEquals extends BinaryLogicalOperator {
+
+ /**
+ * Constructor for a binary not equals operation "!="
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorNotEquals(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary not equals operation "!="
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorNotEquals(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get boolean result of comparing two ints with not equals "!="
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return true if <code>leftOperand</code> != <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand != rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two longs with not equals "!="
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return true if <code>leftOperand</code> != <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand != rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two BigIntegers with not equals "!="
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @return true if <code>leftOperand</code> != <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand) throws CoreException {
+ return !leftOperand.equals(rightOperand);
+ }
+
+ /**
+ * Get boolean result of comparing two floats with not equals "!="
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return true if <code>leftOperand</code> != <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand != rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two doubles with not equals "!="
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return true if <code>leftOperand</code> != <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand != rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two booleans with not equals "!="
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return true if <code>leftOperand</code> != <code>rightOperand</code>,
+ * and false otherwise
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return leftOperand != rightOperand;
+ }
+
+ /**
+ * Get boolean result of comparing two strings with not equals "!="
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return true if <code>leftOperand</code> != <code>rightOperand</code>,
+ * and false otherwise
+ * @throws CoreException
+ */
+ @Override
+ protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ return !leftOperand.equals(rightOperand);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary add operation "+"
+ */
+public class OperatorPlus extends BinaryOperator {
+
+ /**
+ * Constructor for a binary add operation "+"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorPlus(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary add operation "+"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorPlus(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.BinaryOperator#customHandleOperation(org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperandValue, org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperandValue)
+ */
+ @Override
+ protected boolean customHandleOperation(Interpreter fInterpreter,
+ OperandValue left, OperandValue right) throws CoreException {
+ IType rightType = null;
+ IType leftType = null;
+
+ boolean isLeftPointer = false;
+ leftType = TypeUtils.getStrippedType(left.getValueType());
+ isLeftPointer = leftType instanceof IPointerType || leftType instanceof IArrayType;
+
+ boolean isRightPointer = false;
+ rightType = TypeUtils.getStrippedType(right.getValueType());
+ isRightPointer = rightType instanceof IPointerType || rightType instanceof IArrayType;
+
+ // zero pointer operands
+ if (!isLeftPointer && !isRightPointer) {
+ return false;
+ }
+
+ // two pointer operands
+ if (isLeftPointer && isRightPointer) {
+ // allow for strings...
+ if (getValueType(left) == T_String && getValueType(right) == T_String)
+ return false;
+ throw EDCDebugger.newCoreException(ASTEvalMessages.OperatorPlus_PtrPlusPtr);
+ }
+
+ // get the non-pointer on the left
+ if (isRightPointer) {
+ OperandValue temp = right;
+ right = left;
+ left = temp;
+ temp = right;
+ right = left;
+ left = temp;
+ IType tempType = rightType;
+ rightType = leftType;
+ leftType = tempType;
+ }
+
+ // convert the left address to BigInteger
+ BigInteger bigIntAddress = left.getBigIntValue();
+
+ BigInteger aggregateSize = BigInteger.ZERO;
+ if (leftType instanceof IPointerType)
+ aggregateSize = BigInteger.valueOf(leftType.getByteSize());
+ else
+ aggregateSize = BigInteger.valueOf(TypeUtils.getStrippedType(leftType.getType())
+ .getByteSize());
+ BigInteger addAmount = aggregateSize;
+
+ right = convertForPromotion(right);
+
+ addAmount = addAmount.multiply(right.getBigIntValue());
+
+ bigIntAddress = bigIntAddress.add(addAmount);
+
+ pushNewValue(left.getValueType(), bigIntAddress);
+
+ return true;
+ }
+
+ /**
+ * Get int result of applying binary plus "+" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> + <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand + rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary plus "+" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> + <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand + rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary plus "+" to two BigIntegers
+ *
+ * @param leftOperand
+ * - BigInteger long operand
+ * @param rightOperand
+ * - BigInteger long operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> + <code>rightOperand</code>, truncated
+ * to 8 bytes
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ return leftOperand.add(rightOperand).and(Instruction.Mask8Bytes);
+ }
+
+ /**
+ * Get float result of applying binary plus "+" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return <code>leftOperand</code> + <code>rightOperand</code>
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return leftOperand + rightOperand;
+ }
+
+ /**
+ * Get double result of applying binary plus "+" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return <code>leftOperand</code> + <code>rightOperand</code>
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return leftOperand + rightOperand;
+ }
+
+ /**
+ * Get boolean result of applying binary plus "+" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+
+ /**
+ * Get string result of applying binary plus "+" to two strings
+ *
+ * @param leftOperand
+ * - left string operand
+ * @param rightOperand
+ * - right string operand
+ * @return <code>leftOperand</code> + <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected String getStringResult(String leftOperand, String rightOperand) throws CoreException {
+ return leftOperand + rightOperand;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary shift left operation "<<"
+ */
+public class OperatorShiftLeft extends BinaryOperator {
+
+ /**
+ * Constructor for a binary shift left operation "<<"
+ *
+ * @param start - instruction start
+ */
+ public OperatorShiftLeft(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary shift left operation "<<"
+ *
+ * @param resultId - for assignment, variable ID of the result
+ * @param isAssignmentOperator - whether the result is assigned
+ * @param start - instruction start
+ */
+ protected OperatorShiftLeft(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get int result of applying binary shift left "<<" to two ints
+ *
+ * @param leftOperand - left int operand
+ * @param rightOperand - right int operand
+ * @return <code>leftOperand</code> << <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand << rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary shift left "<<" to two longs
+ *
+ * @param leftOperand - left long operand
+ * @param rightOperand - right long operand
+ * @return <code>leftOperand</code> << <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand << rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary shift left "<<" to two BigIntegers
+ *
+ * @param leftOperand - left BigInteger operand
+ * @param rightOperand - right BigInteger operand
+ * @param length - length in bytes of result
+ * @return <code>leftOperand</code> << <code>rightOperand</code>, truncated to 8 bytes
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ return leftOperand.shiftLeft(rightOperand.intValue()).and(Instruction.Mask8Bytes);
+ }
+
+ /**
+ * Get float result of applying binary shift left "<<" to two floats
+ *
+ * @param leftOperand - left float operand
+ * @param rightOperand - right float operand
+ * @return 0
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get double result of applying binary shift left "<<" to two doubles
+ *
+ * @param leftOperand - left double operand
+ * @param rightOperand - right double operand
+ * @return 0
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get boolean result of applying binary shift left "<<" to two booleans
+ *
+ * @param leftOperand - left boolean operand
+ * @param rightOperand - right boolean operand
+ * @return boolean
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Binary shift right operation ">>"
+ */
+public class OperatorShiftRight extends BinaryOperator {
+
+ /**
+ * Constructor for a binary shift right operation ">>"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorShiftRight(int start) {
+ this(0, false, start);
+ }
+
+ /**
+ * Constructor for a binary shift right operation ">>"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ protected OperatorShiftRight(int resultId, boolean isAssignmentOperator, int start) {
+ super(resultId, isAssignmentOperator, start);
+ }
+
+ /**
+ * Get int result of applying binary shift right ">>" to two ints
+ *
+ * @param leftOperand
+ * - left int operand
+ * @param rightOperand
+ * - right int operand
+ * @return <code>leftOperand</code> >> <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int leftOperand, int rightOperand) throws CoreException {
+ return leftOperand >> rightOperand;
+ }
+
+ /**
+ * Get long result of applying binary shift right ">>" to two longs
+ *
+ * @param leftOperand
+ * - left long operand
+ * @param rightOperand
+ * - right long operand
+ * @return <code>leftOperand</code> >> <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long leftOperand, long rightOperand) throws CoreException {
+ return leftOperand >> rightOperand;
+ }
+
+ /**
+ * Get BigInteger result of applying binary shift right ">>" to two
+ * BigIntegers
+ *
+ * @param leftOperand
+ * - left BigInteger operand
+ * @param rightOperand
+ * - right BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>leftOperand</code> >> <code>rightOperand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger leftOperand, BigInteger rightOperand, int length)
+ throws CoreException {
+ return leftOperand.shiftRight(rightOperand.intValue());
+ }
+
+ /**
+ * Get float result of applying binary shift right ">>" to two floats
+ *
+ * @param leftOperand
+ * - left float operand
+ * @param rightOperand
+ * - right float operand
+ * @return 0
+ */
+ @Override
+ protected float getFloatResult(float leftOperand, float rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get double result of applying binary shift right ">>" to two doubles
+ *
+ * @param leftOperand
+ * - left double operand
+ * @param rightOperand
+ * - right double operand
+ * @return 0
+ */
+ @Override
+ protected double getDoubleResult(double leftOperand, double rightOperand) {
+ return 0;
+ }
+
+ /**
+ * Get boolean result of applying binary shift right ">>" to two booleans
+ *
+ * @param leftOperand
+ * - left boolean operand
+ * @param rightOperand
+ * - right boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) {
+ return false;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Unary minus operation "-"
+ */
+public class OperatorUnaryMinus extends UnaryOperator {
+
+ /**
+ * Constructor for a unary minus operation "-"
+ *
+ * @param start
+ * - instruction start
+ */
+ public OperatorUnaryMinus(int start) {
+ super(start);
+ }
+
+ /**
+ * Constructor for a unary minus operation "-"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ public OperatorUnaryMinus(int resultId, boolean isAssignmentOperator, int start) {
+ super(start);
+ }
+
+ /**
+ * Get int result of applying unary minus "-" to an int
+ *
+ * @param operand
+ * - int operand
+ * @return -<code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int operand) throws CoreException {
+ return -operand;
+ }
+
+ /**
+ * Get long result of applying unary minus "-" to a long
+ *
+ * @param operand
+ * - long operand
+ * @return -<code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long operand) throws CoreException {
+ return -operand;
+ }
+
+ /**
+ * Get BigInteger result of applying unary minus "-" to a BigInteger
+ *
+ * @param operand
+ * - BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return -<code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger operand, int length) throws CoreException {
+ return operand.negate();
+ }
+
+ /**
+ * Get float result of applying unary minus "-" to a float
+ *
+ * @param operand
+ * - float operand
+ * @return -<code>operand</code>
+ */
+ @Override
+ protected float getFloatResult(float operand) {
+ return -operand;
+ }
+
+ /**
+ * Get double result of applying unary minus "-" to a double
+ *
+ * @param operand
+ * - double operand
+ * @return -<code>operand</code>
+ */
+ @Override
+ protected double getDoubleResult(double operand) {
+ return -operand;
+ }
+
+ /**
+ * Get boolean result of applying unary minus "-" to a boolean
+ *
+ * @param operand
+ * - boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean operand) {
+ return false;
+ }
+
+ /**
+ * Get string result of applying unary minus "-" to a string
+ *
+ * @param operand
+ * - string operand
+ * @return <code>null</code>
+ * @throws CoreException
+ */
+ @Override
+ protected String getStringResult(String operand) throws CoreException {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.UnsupportedStringOperation);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Unary plus operation "+"
+ */
+public class OperatorUnaryPlus extends UnaryOperator {
+
+ /**
+ * Constructor for unary plus operator "+"
+ *
+ * @param start
+ */
+ public OperatorUnaryPlus(int start) {
+ super(start);
+ }
+
+ /**
+ * Constructor for a unary plus operation "+"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ public OperatorUnaryPlus(int resultId, boolean isAssignmentOperator, int start) {
+ super(start);
+ }
+
+ /**
+ * Get int result of applying unary plus "+" to an int
+ *
+ * @param operand
+ * - int operand
+ * @return <code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected int getIntResult(int operand) throws CoreException {
+ return operand;
+ }
+
+ /**
+ * Get long result of applying unary plus "+" to a long
+ *
+ * @param operand
+ * - long operand
+ * @return <code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected long getLongResult(long operand) throws CoreException {
+ return operand;
+ }
+
+ /**
+ * Get BigInteger result of applying unary plus "+" to a BigInteger
+ *
+ * @param operand
+ * - BigInteger operand
+ * @param length
+ * - length in bytes of result
+ * @return <code>operand</code>
+ * @throws CoreException
+ */
+ @Override
+ protected BigInteger getBigIntegerResult(BigInteger operand, int length) throws CoreException {
+ return operand;
+ }
+
+ /**
+ * Get float result of applying unary plus "+" to a float
+ *
+ * @param operand
+ * - float operand
+ * @return <code>operand</code>
+ */
+ @Override
+ protected float getFloatResult(float operand) {
+ return operand;
+ }
+
+ /**
+ * Get double result of applying unary plus "+" to a double
+ *
+ * @param operand
+ * - double operand
+ * @return <code>operand</code>
+ */
+ @Override
+ protected double getDoubleResult(double operand) {
+ return operand;
+ }
+
+ /**
+ * Get boolean result of applying unary plus "+" to a boolean
+ *
+ * @param operand
+ * - boolean operand
+ * @return <code>false</code>
+ */
+ @Override
+ protected boolean getBooleanResult(boolean operand) {
+ return false;
+ }
+
+ /**
+ * Get string result of applying unary plus "+" to a string
+ *
+ * @param operand
+ * - string operand
+ * @return <code>null</code>
+ * @throws CoreException
+ */
+ @Override
+ protected String getStringResult(String operand) throws CoreException {
+ throw EDCDebugger.newCoreException(ASTEvalMessages.UnsupportedStringOperation);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Push a boolean on the instruction stack
+ */
+public class PushBoolean extends SimpleInstruction {
+ private boolean fValue;
+
+ /**
+ * Constructor for pushing a boolean on the stack
+ *
+ * @param value
+ * - boolean value
+ */
+ public PushBoolean(boolean value) {
+ fValue = value;
+ }
+
+ /**
+ * Execute pushing a boolean on the stack
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() {
+ pushNewValue(fInterpreter.getTypeEngine().getBooleanType(4), fValue);
+ }
+
+ /**
+ * Show a boolean value as a string
+ *
+ * @return string version of a boolean
+ */
+ @Override
+ public String toString() {
+ return Boolean.toString(fValue);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Push a character on the instruction stack
+ *
+ * Note: if the character does not fit in a byte, it is represented by the low 4 bytes of a Java long */
+public class PushChar extends SimpleInstruction {
+
+ static private final char BELL = '\u0007';
+ static private final char VERTICAL_TAB = '\u000B';
+
+ // character value
+ private long fValue;
+ // is wchar_t?
+ private boolean isWide;
+ // if true, the value is multiple characters (e.g. 'AB' or 'CWIE')
+ private boolean multiChar;
+
+ /**
+ * Constructor for pushing a char on the stack
+ *
+ * @param value
+ * - char value
+ */
+ public PushChar(char value) {
+ fValue = (short) value;
+ }
+
+ /**
+ * Constructor for pushing a char on the stack
+ *
+ * @param value
+ * - string value of form 'X' to convert to char X
+ * @throws NumberFormatException
+ */
+ public PushChar(String value) throws NumberFormatException {
+ parseCharValue(value);
+ }
+
+ /**
+ * Execute pushing a char on the stack
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() {
+ if (multiChar) {
+ pushNewValue(fInterpreter.getTypeEngine().getIntegerTypeOfSize(4, false), fValue);
+ return;
+ }
+
+ if (!isWide) {
+ // TODO: truncate to size of this type
+ pushNewValue(fInterpreter.getTypeEngine().getCharacterType(
+ fInterpreter.getTypeEngine().getTypeSize(TypeUtils.BASIC_TYPE_CHAR)), fValue);
+ } else {
+ pushNewValue(fInterpreter.getTypeEngine().getCharacterType(
+ fInterpreter.getTypeEngine().getTypeSize(TypeUtils.BASIC_TYPE_WCHAR_T)), fValue);
+ }
+ }
+
+ /**
+ * Show a char value as a string
+ *
+ * @return string version of a char
+ */
+ @Override
+ public String toString() {
+ if (fValue < 65536)
+ return "" + ((char) fValue); //$NON-NLS-1$
+ char[] surrogate = { (char)(fValue >> 16), (char)(fValue & 0xffff) };
+ return new String(surrogate);
+ }
+
+ /**
+ * Convert string value of form 'X' to char X. This may be either a single
+ * character (char or wchar_t) or a multi-character constant, which is treated
+ * as an integer.
+ *
+ * @param value
+ * - string of form 'X'
+ * @throws NumberFormatException
+ */
+ private void parseCharValue(String value) throws NumberFormatException {
+ if (value.startsWith("L")) { //$NON-NLS-1$
+ isWide = true;
+ value = value.substring(1);
+ }
+ if (value.length() < 3 || value.charAt(0) != '\'' || !value.endsWith("'")) //$NON-NLS-1$
+ throw new NumberFormatException();
+
+ value = value.substring(1, value.length() - 1);
+
+ if (value.startsWith("\\u")) { //$NON-NLS-1$
+ // hex representation
+ if (value.length() < 3)
+ throw new NumberFormatException();
+
+ fValue = Long.parseLong(value.substring(2), 16);
+ return;
+ }
+
+ // escape character
+ if (value.startsWith("\\")) { //$NON-NLS-1$
+ if (value.length() < 2)
+ throw new NumberFormatException();
+
+ if (value.charAt(1) >= '0' && value.charAt(1) <= '7') {
+ // octal representation
+ if (value.length() > 4)
+ throw new NumberFormatException();
+
+ fValue = Long.parseLong(value.substring(1), 8);
+ return;
+ }
+
+ if (value.length() > 2)
+ throw new NumberFormatException();
+
+ switch (value.charAt(1)) {
+ case 'n':
+ fValue = '\n'; break;
+ case 't':
+ fValue = '\t'; break;
+ case 'v':
+ fValue = VERTICAL_TAB; break;
+ case 'b':
+ fValue = '\b'; break;
+ case 'r':
+ fValue = '\r'; break;
+ case 'f':
+ fValue = '\f'; break;
+ case 'a':
+ fValue = BELL; break;
+ case '\\':
+ fValue = '\\'; break;
+ case '?':
+ fValue = '?'; break;
+ case '\'':
+ fValue = '\''; break;
+ case '"':
+ fValue = '"'; break;
+ default:
+ fValue = value.charAt(1); break;
+ }
+ return;
+ }
+
+ multiChar = (value.length() > 1);
+
+ fValue = 0;
+ for (int i = 0; i < value.length(); i++) {
+ fValue = (fValue << 8) + value.charAt(i);
+ }
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Push a double value on the instruction stack
+ */
+public class PushDouble extends SimpleInstruction {
+
+ private double fValue;
+ private boolean isLong;
+
+ /**
+ * Constructor for pushing a double value on the stack
+ *
+ * @param value
+ * - double value
+ */
+ public PushDouble(double value) {
+ fValue = value;
+ }
+
+ /**
+ * Constructor for pushing a double value on the stack
+ *
+ * @param value
+ * - string version of a double
+ * @throws NumberFormatException
+ */
+ public PushDouble(String value) throws NumberFormatException {
+ if (value.toLowerCase().endsWith("l")) { //$NON-NLS-1$
+ isLong = true;
+ value = value.substring(0, value.length() - 1);
+ }
+ fValue = Double.valueOf(value).doubleValue();
+ }
+
+ /**
+ * Execute pushing a double value on the stack
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() {
+ int size;
+ if (isLong) {
+ size = fInterpreter.getTypeEngine().getTypeSize(TypeUtils.BASIC_TYPE_LONG_DOUBLE);
+ pushNewValue(fInterpreter.getTypeEngine().getBasicType(ICPPBasicType.t_double, ICPPBasicType.IS_LONG, size), fValue);
+ }
+ else {
+ size = fInterpreter.getTypeEngine().getTypeSize(TypeUtils.BASIC_TYPE_DOUBLE);
+ pushNewValue(fInterpreter.getTypeEngine().getBasicType(ICPPBasicType.t_double, 0, size), fValue);
+ }
+
+ }
+
+ /**
+ * Show a double value as a string
+ *
+ * @return string version of a double
+ */
+ @Override
+ public String toString() {
+ return Double.toString(fValue);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Push a float on the instruction stack
+ */
+public class PushFloat extends SimpleInstruction {
+
+ private float fValue;
+
+ /**
+ * Constructor for pushing a float on the stack
+ *
+ * @param value
+ * - float value
+ */
+ public PushFloat(float value) {
+ fValue = value;
+ }
+
+ /**
+ * Constructor for pushing a float on the stack
+ *
+ * @param value
+ * - string version of a float
+ * @throws NumberFormatException
+ */
+ public PushFloat(String value) throws NumberFormatException {
+ fValue = Float.valueOf(value).floatValue();
+ }
+
+ /**
+ * Execute pushing a float on the stack
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() {
+ pushNewValue(fInterpreter.getTypeEngine().getFloatTypeOfSize(4), fValue);
+ }
+
+ /**
+ * Show a float value as a string
+ *
+ * @return string version of a float
+ */
+ @Override
+ public String toString() {
+ return Float.toString(fValue);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Push a long on the instruction stack
+ */
+public class PushLongOrBigInteger extends SimpleInstruction {
+
+ // if true, suffix had 'u'
+ private boolean isUnsigned;
+ // if true, suffix had 'l' (but not 'll')
+ private boolean isLong;
+ // if true, suffix had 'll' (but not 'l')
+ private boolean isLongLong;
+
+ // if true, the original number was a hex or octal string
+ private boolean isHexOrOctal;
+
+ // if not null, a big integer
+ private BigInteger fBigInteger;
+ // otherwise, this is the value
+ private long fLong;
+ private IType type;
+
+ /**
+ * Constructor for pushing a long on the stack
+ *
+ * @param value
+ * - long value
+ */
+ public PushLongOrBigInteger(long value) {
+ isLong = true;
+ fLong = value;
+ }
+
+ /**
+ * Constructor for pushing a long on the stack
+ *
+ * @param value
+ * - string version of a long
+ * @throws NumberFormatException
+ */
+ public PushLongOrBigInteger(String value) throws NumberFormatException {
+ parseLongOrBigInteger(value);
+ }
+
+ /**
+ */
+ public long getLong() {
+ return fLong;
+ }
+
+ /**
+ * Execute pushing a long on the stack
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() {
+ Number number = fBigInteger;
+ if (number == null)
+ number = fLong;
+
+ // TODO: Handle 8-byte long and >8-byte long long
+
+ int intSize = fInterpreter.getTypeEngine().getTypeSize(TypeUtils.BASIC_TYPE_INT);
+ int longSize = fInterpreter.getTypeEngine().getTypeSize(TypeUtils.BASIC_TYPE_LONG);
+ int longLongSize = fInterpreter.getTypeEngine().getTypeSize(TypeUtils.BASIC_TYPE_LONG_LONG);
+
+ // promote constant type based on the size of C/C++ int, long, and long long types.
+ // this cannot be done at parse time because the type engine is not known then
+ correctIntegerType(intSize * 8, longSize * 8, longLongSize * 8);
+
+ if (type == null) {
+ int flags = isLongLong ? ICPPBasicType.IS_LONG_LONG : isLong ? ICPPBasicType.IS_LONG : 0;
+
+ if (isUnsigned)
+ flags |= ICPPBasicType.IS_UNSIGNED;
+ else
+ flags |= ICPPBasicType.IS_SIGNED;
+
+ int size;
+ if (isLongLong)
+ size = longLongSize;
+ else if (isLong)
+ size = longSize;
+ else
+ size = intSize;
+
+ type = fInterpreter.getTypeEngine().getBasicType(ICPPBasicType.t_int, flags, size);
+ }
+
+ pushNewValue(type, number);
+ }
+
+ /**
+ * Fix type of constant based on C++ promotion rules, which depend on the size of
+ * int, long, and long long types.
+ *
+ * @param intBitSize
+ * @param longBitSize
+ * @param longLongBitSize
+ */
+ private void correctIntegerType(int intBitSize, int longBitSize, int longLongBitSize) {
+ if ( fBigInteger == null
+ // check if it fits in signed long (but maybe not an unsigned int)
+ && ( (fLong >= 0 && (fLong >> (intBitSize - 1)) == 0)
+ || (fLong < 0 && (fLong >> (intBitSize - 1)) < -1)))
+ return;
+
+ // at execute() time, isUnsigned means explicit unsigned, isLong means explicit long,
+ // and isLongLong means explicit long long
+ if (fBigInteger == null) {
+ // it fits in a 64-bit signed Java long
+ if (isUnsigned) {
+ // explicit unsigned decimal, hex, or octal constant
+ if (isLong) {
+ // explicit unsigned long may change to unsigned long long
+ if ((fLong >> longBitSize) > 0) {
+ isLong = false;
+ isLongLong = true;
+ }
+ } else if (!isLongLong) {
+ // not declared long long - may be int, long, or long long
+ if ((fLong >> (intBitSize - 1)) <= 1) {
+ // fits in an unsigned int
+ } else if ((fLong >> (longBitSize - 1)) <= 1) {
+ // fits in an unsigned long
+ isLong = true;
+ } else {
+ // fits in an unsigned longLong
+ isLongLong = true;
+ }
+ }
+ } else {
+ // signed decimal, hex, or octal constant
+ if (!isHexOrOctal) {
+ // decimal constant
+ if (!isLong && !isLongLong){
+ // signed decimal constant, not declared long or long long
+ if ( (fLong >= 0 && (fLong >> (longBitSize - 1)) == 0)
+ || (fLong < 0 && (fLong >> (longBitSize - 1)) < -1)) {
+ // fits in signed long
+ isLong = true;
+ } else {
+ // fits in signed long long
+ isLongLong = true;
+ }
+ } else if (isLong) {
+ // explicit long may change to long long
+ if (!( (fLong >= 0 && (fLong >> (longLongBitSize - 1)) == 0)
+ || (fLong < 0 && (fLong >> (longLongBitSize - 1)) < -1))) {
+ isLong = false;
+ isLongLong = true;
+ }
+ }
+ } else {
+ // hex or octal constant
+ if (!isLong && !isLongLong){
+ // signed hex or octal constant, not declared long or long long
+ // may be int, unsigned int, long, unsigned long, long long, or unsigned long long
+ if (fLong >> (intBitSize - 1) == 1) {
+ // fits in unsigned int
+ isUnsigned = true;
+ } else if ( (fLong >= 0 && (fLong >> (longBitSize - 1)) == 0)
+ || (fLong < 0 && (fLong >> (longBitSize - 1)) < -1)) {
+ // fits in signed long
+ isLong = true;
+ } else if (fLong >> (longBitSize - 1) == 1) {
+ // fits in unsigned long
+ isLong = true;
+ isUnsigned = true;
+ } else if ( (fLong >= 0 && (fLong >> (longLongBitSize - 1)) == 0)
+ || (fLong < 0 && (fLong >> (longLongBitSize - 1)) < -1)) {
+ // fits in signed long long
+ isLongLong = true;
+ } else {
+ // fits in unsigned long long
+ isLongLong = true;
+ isUnsigned = true;
+ }
+ } else if (isLong) {
+ // explicit long hex or octal may change to unsigned long, long long, or unsigned long long
+ if ( (fLong >= 0 && (fLong >> (longBitSize - 1)) == 0)
+ || (fLong < 0 && (fLong >> (longBitSize - 1)) < -1)) {
+ // fits in signed long
+ } else if (fLong >> (longBitSize - 1) == 1) {
+ // fits in unsigned long
+ isUnsigned = true;
+ } else if ( (fLong >= 0 && (fLong >> (longLongBitSize - 1)) == 0)
+ || (fLong < 0 && (fLong >> (longLongBitSize - 1)) < -1)) {
+ // fits in signed long long
+ isLong = false;
+ isLongLong = true;
+ } else {
+ // fits in unsigned long long
+ isLong = false;
+ isLongLong = true;
+ isUnsigned = true;
+ }
+ } else {
+ // explicit long long may change to unsigned long long
+ if (fLong >> (longLongBitSize - 1) > 0)
+ isUnsigned = true;
+ }
+ }
+ }
+ } else {
+ // BigInteger, which does not fit in a Java long
+ isLongLong = true;
+ isLong = false;
+ if (!isUnsigned) {
+ // signed decimal, hex, or octal long long constant
+ if (isHexOrOctal) {
+ // for hex or octal constant, long long may change to unsigned long long
+ if (fBigInteger.bitLength() > (longLongBitSize - 1))
+ isUnsigned = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Show a long or BigInteger value as a string
+ *
+ * @return string version of a long or BigInteger
+ */
+ @Override
+ public String toString() {
+ if (isLong)
+ return Long.toString(fLong);
+ else
+ return fBigInteger.toString();
+ }
+
+ /**
+ * Convert a string to a long or BigInteger
+ *
+ * @param value
+ * - string version of a long
+ * @throws NumberFormatException
+ */
+ public void parseLongOrBigInteger(String value) throws NumberFormatException {
+ isUnsigned = false;
+ isHexOrOctal = false;
+
+ String val = value.toLowerCase();
+ int length = value.length();
+
+ if (val.endsWith("ull") || val.endsWith("llu")) { //$NON-NLS-1$ //$NON-NLS-2$
+ isUnsigned = true;
+ isLongLong = true;
+ isLong = false;
+ val = val.substring(0, val.length() - 3);
+ length -= 3;
+ } else if (val.endsWith("ll")) { //$NON-NLS-1$
+ isLong = false;
+ isLongLong = true;
+ val = val.substring(0, val.length() - 2);
+ length -= 2;
+ } else if (val.endsWith("ul") || val.endsWith("lu")) { //$NON-NLS-1$ //$NON-NLS-2$
+ isUnsigned = true;
+ isLong = true;
+ val = val.substring(0, val.length() - 2);
+ length -= 2;
+ } else if (val.endsWith("l")) { //$NON-NLS-1$
+ isLong = true;
+ val = val.substring(0, val.length() - 1);
+ length -= 1;
+ } else if (val.endsWith("u")) { //$NON-NLS-1$
+ isUnsigned = true;
+ val = val.substring(0, val.length() - 1);
+ length -= 1;
+ }
+
+ // if conversion to BigInteger fails, the string is invalid
+ // if after BigInteger conversion, Long conversion fails, the value is
+ // too large or small
+ if (val.startsWith("0x")) { //$NON-NLS-1$
+ isHexOrOctal = true;
+ try {
+ fLong = Long.valueOf(val.substring(2), 16);
+ } catch (NumberFormatException nfe) {
+ fBigInteger = new BigInteger(val.substring(2), 16);
+ fBigInteger.and(Instruction.Mask8Bytes);
+ return;
+ }
+ length -= 2;
+ } else if (length > 1 && val.startsWith("0")) { //$NON-NLS-1$
+ isHexOrOctal = true;
+
+ try {
+ fLong = Long.valueOf(val.substring(1), 8);
+ } catch (NumberFormatException nfe) {
+ fBigInteger = new BigInteger(val.substring(1), 8);
+ fBigInteger.and(Instruction.Mask8Bytes);
+ return;
+ }
+ length -= 1;
+ } else {
+
+ try {
+ fLong = Long.valueOf(val);
+ } catch (NumberFormatException nfe) {
+ fBigInteger = new BigInteger(val);
+ fBigInteger.and(Instruction.Mask8Bytes);
+ return;
+ }
+ }
+
+ if (length == 0) {
+ fLong = 0;
+ return;
+ }
+
+ if (isUnsigned) {
+ if (fBigInteger != null) {
+ if (fBigInteger.bitLength() < 64) {
+ // unsigned will fit in a Java long
+ fLong = fBigInteger.longValue();
+ fBigInteger = null;
+ return;
+ }
+ // keep it a BigInteger
+ // TODO: Allow bigger than 8 bytes
+ fBigInteger.and(Instruction.Mask8Bytes);
+ }
+ }
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Push a string on the instruction stack
+ */
+public class PushString extends SimpleInstruction {
+
+ private String fValue;
+ private boolean isWide;
+ private IType stringType;
+
+ /**
+ * Constructor for pushing a string on the stack
+ *
+ * @param value
+ * - string value in format "..." or L"..."
+ */
+ public PushString(String value) {
+ if (value.startsWith("L")) { //$NON-NLS-1$
+ isWide = true;
+ value = value.substring(1);
+ }
+
+ fValue = value.substring(1, value.length() - 1);
+ }
+
+ /**
+ * Execute pushing a string on the stack
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() {
+ if (stringType == null) {
+ int size = fInterpreter.getTypeEngine().getTypeSize(isWide ? TypeUtils.BASIC_TYPE_WCHAR_T : TypeUtils.BASIC_TYPE_CHAR);
+ IType charType = fInterpreter.getTypeEngine().getBasicType(ICPPBasicType.t_char, 0, size);
+ stringType = fInterpreter.getTypeEngine().getCharArrayType(charType, fValue.length() + 1);
+ }
+ pushNewValue(stringType, fValue);
+ }
+
+ /**
+ * Show a string value
+ *
+ * @return string
+ */
+ @Override
+ public String toString() {
+ return fValue;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+public abstract class SimpleInstruction extends Instruction {
+
+ /**
+ * Constructor for a simple instruction
+ */
+ protected SimpleInstruction() {
+ super();
+ }
+
+ /**
+ * Get simple instruction size
+ *
+ * @return 1
+ */
+ @Override
+ public int getSize() {
+ return 1;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Unary logical operator, such as "!"
+ */
+public abstract class UnaryLogicalOperator extends CompoundInstruction {
+
+ /**
+ * Constructor for a unary logical operator, such as "!"
+ *
+ * @param start
+ * - instruction start
+ */
+ public UnaryLogicalOperator(int start) {
+ super(start);
+ }
+
+ /**
+ * Constructor for a unary logical operator, such as "!"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ public UnaryLogicalOperator(int resultId, boolean isAssignmentOperator, int start) {
+ super(start);
+ }
+
+ /**
+ * Resolve a unary logical expression
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue operand = popValue();
+
+ operand = convertForPromotion(operand);
+
+ // change chars/shorts to int, etc.
+ int resultType = getJavaBinaryPromotionType(operand, operand);
+ IType type = fInterpreter.getTypeEngine().getBooleanType(1);
+
+ switch (resultType) {
+ case T_String:
+ pushNewValue(type, getStringResult(GetValue.getStringValue(operand)));
+ break;
+ case T_double:
+ pushNewValue(type, getDoubleResult(GetValue.getDoubleValue(operand)));
+ break;
+ case T_float:
+ pushNewValue(type, getFloatResult(GetValue.getFloatValue(operand)));
+ break;
+ case T_long:
+ pushNewValue(type, getLongResult(GetValue.getLongValue(operand)));
+ break;
+ case T_int:
+ pushNewValue(type, getIntResult(GetValue.getIntValue(operand)));
+ break;
+ case T_boolean:
+ pushNewValue(type, getBooleanResult(GetValue.getBooleanValue(operand)));
+ break;
+ case T_BigInt:
+ pushNewValue(type, getBigIntegerResult(GetValue.getBigIntegerValue(operand)));
+ break;
+ default:
+ throw EDCDebugger.newCoreException(ASTEvalMessages.UnhandledTypeCode + resultType);
+
+ }
+ }
+
+ /**
+ * Get boolean result of applying a unary logical operation to an int
+ *
+ * @param operand
+ * - int operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract boolean getIntResult(int operand) throws CoreException;
+
+ /**
+ * Get boolean result of applying a unary logical operation to a long
+ *
+ * @param operand
+ * - long operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract boolean getLongResult(long operand) throws CoreException;
+
+ /**
+ * Get boolean result of applying a unary logical operation to a BigInteger
+ *
+ * @param operand
+ * - BigInteger operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract boolean getBigIntegerResult(BigInteger operand) throws CoreException;
+
+ /**
+ * Get boolean result of applying a unary logical operation to a float
+ *
+ * @param operand
+ * - float operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract boolean getFloatResult(float operand);
+
+ /**
+ * Get boolean result of applying a unary logical operation to a double
+ *
+ * @param operand
+ * - double operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract boolean getDoubleResult(double operand);
+
+ /**
+ * Get boolean result of applying a unary logical operation to a boolean
+ *
+ * @param operand
+ * - boolean operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract boolean getBooleanResult(boolean operand);
+
+ /**
+ * Get boolean result of applying a unary logical operation to a string
+ *
+ * @param operand
+ * - string operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract boolean getStringResult(String operand) throws CoreException;
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+/*
+ * Unary arithmetic operator, such as "~"
+ */
+public abstract class UnaryOperator extends CompoundInstruction {
+
+ /**
+ * Constructor for a unary arithmetic operator, such as "~"
+ *
+ * @param start
+ * - instruction start
+ */
+ public UnaryOperator(int start) {
+ super(start);
+ }
+
+ /**
+ * Constructor for a unary arithmetic operator, such as "~"
+ *
+ * @param resultId
+ * - for assignment, variable ID of the result
+ * @param isAssignmentOperator
+ * - whether the result is assigned
+ * @param start
+ * - instruction start
+ */
+ public UnaryOperator(int resultId, boolean isAssignmentOperator, int start) {
+ super(start);
+ }
+
+ /**
+ * Resolve a unary arithmetic expression
+ *
+ * @throws CoreException
+ */
+ @Override
+ public void execute() throws CoreException {
+ OperandValue operand = popValue();
+
+ operand = convertForPromotion(operand);
+
+ // change chars/shorts to int, etc.
+
+ int resultType = getJavaBinaryPromotionType(operand, operand);
+ IType type = getBinaryPromotionType(operand, operand);
+
+ // non-logical operations on booleans are int results
+ if ((type instanceof ICPPBasicType) && ((ICPPBasicType) type).getBaseType() == ICPPBasicType.t_bool) {
+ type = fInterpreter.getTypeEngine().getIntegerTypeFor(TypeUtils.BASIC_TYPE_INT, true);
+ }
+
+ switch (resultType) {
+ case T_String:
+ pushNewValue(type, getStringResult(GetValue.getStringValue(operand)));
+ break;
+ case T_double:
+ pushNewValue(type, getDoubleResult(GetValue.getDoubleValue(operand)));
+ break;
+ case T_float:
+ pushNewValue(type, getFloatResult(GetValue.getFloatValue(operand)));
+ break;
+ case T_long:
+ pushNewValue(type, getLongResult(GetValue.getLongValue(operand)));
+ break;
+ case T_int:
+ pushNewValue(type, getIntResult(GetValue.getIntValue(operand)));
+ break;
+ case T_boolean:
+ pushNewValue(type, getBooleanResult(GetValue.getBooleanValue(operand)));
+ break;
+ default:
+ throw EDCDebugger.newCoreException(ASTEvalMessages.UnhandledTypeCode + resultType);
+ }
+ }
+
+ /**
+ * Get int result of applying a unary operation to an int
+ *
+ * @param operand
+ * - int operand
+ * @return int result of the operation if possible, or an operation-specific
+ * default
+ * @throws CoreException
+ */
+ protected abstract int getIntResult(int operand) throws CoreException;
+
+ /**
+ * Get long result of applying a unary operation to a long
+ *
+ * @param operand
+ * - long operand
+ * @return long result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract long getLongResult(long operand) throws CoreException;
+
+ /**
+ * Get BigInteger result of applying a unary operation to a BigInteger
+ *
+ * @param operand
+ * - long operand
+ * @param length
+ * - length in bytes of the result
+ * @return long result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract BigInteger getBigIntegerResult(BigInteger operand, int length) throws CoreException;
+
+ /**
+ * Get float result of applying a unary operation to a float
+ *
+ * @param operand
+ * - float operand
+ * @return float result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract float getFloatResult(float operand);
+
+ /**
+ * Get double result of applying a unary operation to a double
+ *
+ * @param operand
+ * - double operand
+ * @return double result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract double getDoubleResult(double operand);
+
+ /**
+ * Get boolean result of applying a unary operation to a boolean
+ *
+ * @param operand
+ * - boolean operand
+ * @return boolean result of the operation if possible, or an
+ * operation-specific default
+ */
+ protected abstract boolean getBooleanResult(boolean operand);
+
+ /**
+ * Get string result of applying a unary operation to a string
+ *
+ * @param operand
+ * - string operand
+ * @return string result of the operation if possible, or an
+ * operation-specific default
+ * @throws CoreException
+ */
+ protected abstract String getStringResult(String operand) throws CoreException;
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions;
+
+
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvalMessages;
+import org.eclipse.cdt.debug.edc.internal.symbols.InvalidVariableLocation;
+import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.core.runtime.CoreException;
+
+public class VariableWithValue extends OperandValue {
+ final IVariable variable;
+ private final EDCServicesTracker servicesTracker;
+ private final StackFrameDMC frame;
+
+ public VariableWithValue(EDCServicesTracker servicesTracker, StackFrameDMC frame, IVariable variable) {
+ this(servicesTracker, frame, variable, false);
+ }
+
+ public VariableWithValue(EDCServicesTracker servicesTracker, StackFrameDMC frame, IVariable variable,
+ boolean isBitField) {
+ super(variable.getType(), isBitField);
+ this.servicesTracker = servicesTracker;
+ this.frame = frame;
+ this.variable = variable;
+ }
+
+ public VariableWithValue(EDCServicesTracker servicesTracker, StackFrameDMC frame, IVariable variable, IType otherType) {
+ super(otherType, false);
+ this.servicesTracker = servicesTracker;
+ this.frame = frame;
+ this.variable = variable;
+ }
+ /**
+ * @return the servicesTracker
+ */
+ public EDCServicesTracker getServicesTracker() {
+ return servicesTracker;
+ }
+ /**
+ * @return the frame
+ */
+ public StackFrameDMC getFrame() {
+ return frame;
+ }
+ public IVariable getVariable() {
+ return variable;
+ }
+
+ public Number getValue() throws CoreException {
+ if (value == null) {
+ IVariableLocation location = getValueLocation();
+ IType varType = type;
+ if (varType != null) {
+ value = getValueByType(varType, location);
+ } else {
+ assert false;
+ }
+ }
+ return value;
+ }
+
+ public IVariableLocation getValueLocation() {
+ if (valueLocation == null) {
+ ILocationProvider provider = variable.getLocationProvider();
+ if (provider == null) {
+ // ERROR
+ valueLocation = new InvalidVariableLocation(ASTEvalMessages.VariableWithValue_CannotLocateVariable);
+ return valueLocation;
+ }
+ IEDCModuleDMContext module = frame.getModule();
+ valueLocation = provider.getLocation(servicesTracker, frame, module.toLinkAddress(frame.getInstructionPtrAddress()),
+ TypeUtils.isConstType(variable.getType()));
+ if (valueLocation == null) {
+ // unhandled
+ valueLocation = new InvalidVariableLocation(ASTEvalMessages.VariableWithValue_CannotLocateVariable);
+ }
+ }
+ return valueLocation;
+ }
+
+ public OperandValue copyWithType(IType otherType) {
+ OperandValue value = new VariableWithValue(servicesTracker, frame, variable, otherType);
+ value.stringValue = this.stringValue;
+ value.valueLocation = this.valueLocation;
+ return value;
+ }
+
+ public void setType(IType type) {
+ this.type = type;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.formatter;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.cdt.debug.edc.formatter.ITypeContentProvider;
+import org.eclipse.cdt.debug.edc.formatter.IVariableFormatProvider;
+import org.eclipse.cdt.debug.edc.formatter.IVariableValueConverter;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+
+/**
+ * Manages format extensions
+ */
+public class FormatExtensionManager implements IVariableFormatManager {
+
+ // Preferences
+ public static final String VARIABLE_FORMATS_ENABLED = "variable_formats_enabled";
+ public static final boolean VARIABLE_FORMATS_ENABLED_DEFAULT = true;
+
+ /**
+ * A chooser implementation that always defers to any formatter that is not one of the default formatters
+ */
+ private static final class DefaultFormatProviderChooser implements IVariableFormatProviderChooser {
+ private static final String DEFAULT_COMPOSITE =
+ "org.eclipse.cdt.debug.edc.formatter.default.composite"; //$NON-NLS-1$
+ private static final String DEFAULT_ARRAY =
+ "org.eclipse.cdt.debug.edc.formatter.default.array"; //$NON-NLS-1$
+
+ public String chooseDetailValueConverter(IType type, Collection<String> ids) {
+ return chooseAnyBeforeDefault(ids);
+ }
+
+ public String chooseTypeContentProvider(IType type, Collection<String> ids) {
+ return chooseAnyBeforeDefault(ids);
+ }
+
+ public String chooseVariableValueConverter(IType type, Collection<String> ids) {
+ return chooseAnyBeforeDefault(ids);
+ }
+
+ private String chooseAnyBeforeDefault(Collection<String> ids) {
+ if (ids.size() > 1) {
+ for (String id : ids) {
+ if (!id.equals(DEFAULT_COMPOSITE) && !id.equals(DEFAULT_ARRAY))
+ return id;
+ }
+ }
+ else if (ids.size() == 1)
+ return ids.iterator().next();
+
+ return null;
+ }
+ }
+
+ public class FormatProviderExtension {
+ private String label;
+ private IVariableFormatProvider formatProvider;
+
+ public FormatProviderExtension(String label, IVariableFormatProvider formatProvider) {
+ this.label = label;
+ this.formatProvider = formatProvider;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public IVariableFormatProvider getFormatProvider() {
+ return formatProvider;
+ }
+ }
+
+ private static IVariableFormatManager instance =
+ new FormatExtensionManager(new DefaultFormatProviderChooser());
+
+ private Map<String, FormatProviderExtension> formatProviders;
+ private IVariableFormatProviderChooser chooser;
+ private boolean enabled = FormatExtensionManager.VARIABLE_FORMATS_ENABLED_DEFAULT;
+
+ public static IVariableFormatManager instance() {
+ return instance;
+ }
+
+ private FormatExtensionManager(IVariableFormatProviderChooser chooser) {
+ readProviders();
+ setFormatProviderChooser(chooser);
+ IEclipsePreferences scope = InstanceScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
+ enabled = scope.getBoolean(VARIABLE_FORMATS_ENABLED, FormatExtensionManager.VARIABLE_FORMATS_ENABLED_DEFAULT);
+ }
+
+ private void readProviders() {
+ if (formatProviders != null)
+ return;
+ IConfigurationElement[] elements =
+ Platform.getExtensionRegistry().getConfigurationElementsFor(EDCDebugger.PLUGIN_ID + ".variableFormatProvider"); //$NON-NLS-1$
+ for (IConfigurationElement element : elements) {
+ try {
+ String id = element.getAttribute("id"); //$NON-NLS-1$
+ String label = element.getAttribute("label"); //$NON-NLS-1$
+ IVariableFormatProvider formatProvider =
+ (IVariableFormatProvider) element.createExecutableExtension("class"); //$NON-NLS-1$
+ if (formatProviders == null)
+ formatProviders = new HashMap<String, FormatProviderExtension>();
+ formatProviders.put(id, new FormatProviderExtension(label, formatProvider));
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError("Could not create formatting extension", e);
+ }
+ }
+ }
+
+ private interface Getter <T> {
+ T get(IVariableFormatProvider fp, IType type);
+ String choose(IType type, Collection<String> ids);
+ }
+
+ private<T> T getProvider(IType type, Getter<T> getter) {
+ if (!enabled)
+ return null;
+ Map<String, T> providers = new HashMap<String, T>();
+ for (Entry<String, FormatProviderExtension> entry : formatProviders.entrySet()) {
+ IVariableFormatProvider fp = entry.getValue().getFormatProvider();
+ T t = getter.get(fp, type);
+ if (t != null)
+ providers.put(entry.getKey(), t);
+ }
+ String id = getter.choose(type, providers.keySet());
+ return providers.get(id);
+ }
+
+
+ public ITypeContentProvider getTypeContentProvider(IType type) {
+ return getProvider(type, new Getter<ITypeContentProvider>() {
+ public ITypeContentProvider get(IVariableFormatProvider fp, IType type) {
+ return fp.getTypeContentProvider(type);
+ }
+ public String choose(IType type, Collection<String> ids) {
+ return chooser.chooseTypeContentProvider(type, ids);
+ }
+ });
+ }
+
+ public IVariableValueConverter getVariableValueConverter(IType type) {
+ return getProvider(type, new Getter<IVariableValueConverter>() {
+ public IVariableValueConverter get(IVariableFormatProvider fp, IType type) {
+ return fp.getVariableValueConverter(type);
+ }
+ public String choose(IType type, Collection<String> ids) {
+ return chooser.chooseVariableValueConverter(type, ids);
+ }
+ });
+ }
+
+ public IVariableValueConverter getDetailValueConverter(IType type) {
+ return getProvider(type, new Getter<IVariableValueConverter>() {
+ public IVariableValueConverter get(IVariableFormatProvider fp, IType type) {
+ return fp.getDetailValueConverter(type);
+ }
+ public String choose(IType type, Collection<String> ids) {
+ return chooser.chooseDetailValueConverter(type, ids);
+ }
+ });
+ }
+
+ public void setFormatProviderChooser(IVariableFormatProviderChooser chooser) {
+ this.chooser = chooser;
+ }
+
+ public String[] getVariableFormatProviderIds() {
+ Set<String> keySet = formatProviders.keySet();
+ return keySet.toArray(new String[keySet.size()]);
+ }
+
+ public String getFormatProviderLabel(String id) {
+ return formatProviders.get(id).getLabel();
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.formatter;
+
+import org.eclipse.cdt.debug.edc.formatter.IVariableFormatProvider;
+
+/**
+ * An interface to the variable format provider manager singleton
+ */
+public interface IVariableFormatManager extends IVariableFormatProvider {
+
+ void setFormatProviderChooser(IVariableFormatProviderChooser chooser);
+
+ String[] getVariableFormatProviderIds();
+
+ String getFormatProviderLabel(String id);
+
+ void setEnabled(boolean enabled);
+
+ boolean isEnabled();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.formatter;
+
+import java.util.Collection;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * An object that allows choosing between format providers
+ */
+public interface IVariableFormatProviderChooser {
+
+ String chooseTypeContentProvider(IType type, Collection<String> ids);
+
+ String chooseVariableValueConverter(IType type, Collection<String> ids);
+
+ String chooseDetailValueConverter(IType type, Collection<String> ids);
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.launch;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
+import org.eclipse.cdt.dsf.service.AbstractDsfService;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.osgi.framework.BundleContext;
+
+/**
+ * ISourceLookup service implementation based on the CDT CSourceLookupDirector.
+ * Supports multiple source lookup directors.
+ */
+public class CSourceLookup extends AbstractDsfService implements ISourceLookup {
+
+ private final Map<ISourceLookupDMContext, List<CSourceLookupDirector>> directors = new HashMap<ISourceLookupDMContext, List<CSourceLookupDirector>>();
+
+ public CSourceLookup(DsfSession session) {
+ super(session);
+ }
+
+ @Override
+ protected BundleContext getBundleContext() {
+ return EDCDebugger.getBundleContext();
+ }
+
+ public void addSourceLookupDirector(ISourceLookupDMContext ctx, CSourceLookupDirector director) {
+ List<CSourceLookupDirector> directorsInContext = directors.get(ctx);
+ if (directorsInContext == null)
+ directorsInContext = new ArrayList<CSourceLookupDirector>();
+ directorsInContext.add(director);
+ directors.put(ctx, directorsInContext);
+ }
+
+ public CSourceLookupDirector[] getSourceLookupDirectors(ISourceLookupDMContext ctx) {
+ List<CSourceLookupDirector> directorList = directors.get(ctx);
+ return directorList.toArray(new CSourceLookupDirector[directorList.size()]);
+ }
+
+ @Override
+ public void initialize(final RequestMonitor requestMonitor) {
+ super.initialize(new RequestMonitor(getExecutor(), requestMonitor) {
+ @Override
+ protected void handleSuccess() {
+ doInitialize(requestMonitor);
+ }
+ });
+ }
+
+ private void doInitialize(final RequestMonitor requestMonitor) {
+ // Register this service
+ register(new String[] { CSourceLookup.class.getName(), ISourceLookup.class.getName() },
+ new Hashtable<String, String>());
+
+ requestMonitor.done();
+ }
+
+ @Override
+ public void shutdown(final RequestMonitor requestMonitor) {
+ unregister();
+ super.shutdown(requestMonitor);
+ }
+
+ public void getDebuggerPath(ISourceLookupDMContext sourceLookupCtx, Object source,
+ final DataRequestMonitor<String> rm) {
+ if (!(source instanceof String)) {
+ // In future if needed other elements such as URIs could be
+ // supported.
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED,
+ "Only string source element is supported", null)); //$NON-NLS-1$);
+ rm.done();
+ return;
+ }
+ final String sourceString = (String) source;
+
+ if (!directors.containsKey(sourceLookupCtx)) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE,
+ "No source director configured for given context", null)); //$NON-NLS-1$);
+ rm.done();
+ return;
+ }
+ final CSourceLookupDirector[] director = getSourceLookupDirectors(sourceLookupCtx);
+
+ new Job("Lookup Debugger Path") { //$NON-NLS-1$
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+
+ rm.setData(sourceString);
+
+ for (CSourceLookupDirector cSourceLookupDirector : director) {
+ IPath debuggerPath = cSourceLookupDirector.getCompilationPath(sourceString);
+ if (debuggerPath != null) {
+ rm.setData(debuggerPath.toString());
+ break;
+ }
+ }
+ rm.done();
+
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+
+ }
+
+ public void getSource(ISourceLookupDMContext sourceLookupCtx, final String debuggerPath,
+ final DataRequestMonitor<Object> rm) {
+ if (!directors.containsKey(sourceLookupCtx)) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE,
+ "No source director configured for given context", null)); //$NON-NLS-1$);
+ rm.done();
+ return;
+ }
+ final CSourceLookupDirector[] director = getSourceLookupDirectors(sourceLookupCtx);
+
+ new Job("Lookup Source") { //$NON-NLS-1$
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ Object[] sources;
+ try {
+ for (CSourceLookupDirector cSourceLookupDirector : director) {
+ sources = cSourceLookupDirector.findSourceElements(debuggerPath);
+ if (sources == null || sources.length == 0) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID,
+ IDsfStatusConstants.REQUEST_FAILED, "No sources found", null)); //$NON-NLS-1$);
+ } else {
+ rm.setData(sources[0]);
+ break;
+ }
+ }
+ } catch (CoreException e) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED,
+ "Source lookup failed", e)); //$NON-NLS-1$);
+ } finally {
+ rm.done();
+ }
+
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.launch;
+
+import org.eclipse.cdt.debug.edc.internal.services.dsf.BreakpointAttributeTranslator;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Breakpoints;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.INoop;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Snapshots;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Symbols;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.Sequence;
+import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2;
+import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
+import org.eclipse.cdt.dsf.debug.service.IDisassembly;
+import org.eclipse.cdt.dsf.debug.service.IExpressions;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+import org.eclipse.cdt.dsf.debug.service.IModules;
+import org.eclipse.cdt.dsf.debug.service.IProcesses;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+import org.eclipse.cdt.dsf.debug.service.IRunControl;
+import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
+import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext;
+import org.eclipse.cdt.dsf.debug.service.IStack;
+import org.eclipse.cdt.dsf.debug.service.ISymbols;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class ServicesLaunchSequence extends Sequence {
+
+ Step[] fSteps = new Step[] {
+ /*
+ * create this service as the first one as it's needed when
+ * constructing/initializing other services.
+ */
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(ITargetEnvironment.class, session, launch).initialize(
+ requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(IProcesses.class, session).initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ runControlService = (RunControl) launch.getServiceFactory().createService(IRunControl.class, session);
+ runControlService.initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(IMemory.class, session).initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ Modules modules = (Modules) launch.getServiceFactory().createService(IModules.class, session);
+ modules.initialize(requestMonitor);
+ modules.setSourceLocator(launch.getExecutableLocator());
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(IStack.class, session).initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ Symbols symbols = (Symbols) launch.getServiceFactory().createService(ISymbols.class, session);
+ symbols.initialize(requestMonitor);
+ symbols.setSourceLocator(launch.getSourceLocator());
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(IExpressions.class, session).initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ sourceLookup = (CSourceLookup) launch.getServiceFactory().createService(ISourceLookup.class, session);
+ sourceLookup.initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ ISourceLookupDMContext sourceLookupDmc = runControlService.getRootDMC();
+ sourceLookup.addSourceLookupDirector(sourceLookupDmc, (CSourceLookupDirector) launch.getSourceLocator());
+ requestMonitor.done();
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ Breakpoints breakpoints = (Breakpoints) launch.getServiceFactory().createService(IBreakpoints.class,
+ session);
+ breakpoints.initialize(new RequestMonitor(getExecutor(), requestMonitor));
+ breakpoints.setSourceLocator(launch.getSourceLocator());
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ final BreakpointsMediator2 bpmService = new BreakpointsMediator2(session, new BreakpointAttributeTranslator(
+ session));
+ bpmService.initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(IRegisters.class, session).initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(IDisassembly.class, session).initialize(requestMonitor);
+ }
+ },
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(Snapshots.class, session).initialize(
+ requestMonitor);
+ }
+ },
+
+ // Used for testing
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ launch.getServiceFactory().createService(INoop.class, session).initialize(requestMonitor);
+ }
+ }};
+
+ DsfSession session;
+ EDCLaunch launch;
+ RunControl runControlService;
+ CSourceLookup sourceLookup;
+
+ public ServicesLaunchSequence(DsfSession session, EDCLaunch launch, IProgressMonitor pm) {
+ super(session.getExecutor(), pm, "Initializing debugger services", "Aborting debugger services initialization");
+ this.session = session;
+ this.launch = launch;
+ }
+
+ @Override
+ public Step[] getSteps() {
+ return fSteps;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.launch;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.INoop;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Snapshots;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Symbols;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.Sequence;
+import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2;
+import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
+import org.eclipse.cdt.dsf.debug.service.IDisassembly;
+import org.eclipse.cdt.dsf.debug.service.IExpressions;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+import org.eclipse.cdt.dsf.debug.service.IModules;
+import org.eclipse.cdt.dsf.debug.service.IProcesses;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+import org.eclipse.cdt.dsf.debug.service.IRunControl;
+import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
+import org.eclipse.cdt.dsf.debug.service.IStack;
+import org.eclipse.cdt.dsf.service.DsfServicesTracker;
+import org.eclipse.cdt.dsf.service.IDsfService;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+public class ShutdownSequence extends Sequence {
+
+ String sessionId;
+
+ String applicationName;
+
+ String debugModelId;
+
+ DsfServicesTracker tracker;
+
+ public ShutdownSequence(DsfExecutor executor, String sessionId, RequestMonitor requestMonitor) {
+ super(executor, requestMonitor);
+ this.sessionId = sessionId;
+ }
+
+ @Override
+ public Step[] getSteps() {
+ return steps;
+ }
+
+ private final Step[] steps = new Step[] {
+
+ new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ assert EDCDebugger.getDefault().getBundle().getBundleContext() != null;
+ tracker = new DsfServicesTracker(EDCDebugger.getDefault().getBundle().getBundleContext(), sessionId);
+ requestMonitor.done();
+ }
+
+ @Override
+ public void rollBack(RequestMonitor requestMonitor) {
+ tracker.dispose();
+ tracker = null;
+ requestMonitor.done();
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(Snapshots.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IDisassembly.class, requestMonitor);
+ }
+ }, new Step() {
+ // Call this to make sure breakpoints are removed.
+ // Do this before we shutdown other services to ensure
+ // breakpoints can be removed.
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(BreakpointsMediator2.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IRegisters.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IBreakpoints.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(ISourceLookup.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IExpressions.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IStack.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IModules.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IMemory.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IRunControl.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(IProcesses.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(ITargetEnvironment.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(Symbols.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ shutdownService(INoop.class, requestMonitor);
+ }
+ }, new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ tracker.dispose();
+ tracker = null;
+ requestMonitor.done();
+ }
+ } };
+
+ private <V> void shutdownService(Class<V> clazz, final RequestMonitor requestMonitor) {
+ IDsfService service = (IDsfService) tracker.getService(clazz);
+ if (service != null) {
+ service.shutdown(new RequestMonitor(getExecutor(), requestMonitor) {
+ @Override
+ protected void handleCompleted() {
+ if (!isSuccess()) {
+
+ }
+ requestMonitor.done();
+ }
+ });
+ } else {
+ requestMonitor
+ .setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "AbstractEDCService '" + clazz.getName() + "' not found.", null)); //$NON-NLS-1$//$NON-NLS-2$
+ requestMonitor.done();
+ }
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.internal.launch;
+
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.ISourceLocator;
+
+public class SnapshotLaunch extends EDCLaunch {
+
+ public SnapshotLaunch(ILaunchConfiguration launchConfiguration, String mode,
+ ISourceLocator locator, String ownerID) {
+ super(launchConfiguration, mode, locator, ownerID);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.launch;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.snapshot.Album;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunchDelegate;
+import org.eclipse.cdt.debug.edc.launch.IEDCLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.Sequence;
+import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+
+public class SnapshotLaunchDelegate extends EDCLaunchDelegate {
+
+ public final static String SNAPSHOT_DEBUG_MODEL_ID = "org.eclipse.cdt.debug.edc.internal.snapshot"; //$NON-NLS-1$
+ private Album album;
+ private ILaunchConfiguration proxyLaunchConfig;
+
+ @Override
+ public boolean preLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor)
+ throws CoreException {
+ String albumLocation = configuration.getAttribute(IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE, "");
+ IPath albumPath = new Path(albumLocation);
+ album = Album.getAlbumByLocation(albumPath);
+ if (album == null || !album.isLoaded()) {
+ if (album == null) {
+ album = new Album();
+ }
+ album.setLocation(albumPath);
+ try {
+ album.loadAlbum(false);
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ return false;
+ }
+ }
+ ILaunchManager lm = DebugPlugin.getDefault().getLaunchManager();
+ String launchID = album.getLaunchTypeID();
+ ILaunchConfigurationType launchType = lm.getLaunchConfigurationType(launchID);
+ if (launchType == null) {
+ throw new CoreException(new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, "Can't launch this snapshot because the required debugger is not installed: " + launchID));
+ }
+ proxyLaunchConfig = findExistingLaunchForAlbum(albumLocation);
+ if (proxyLaunchConfig == null) {
+ String lcName = lm.generateLaunchConfigurationName(album.getDisplayName());
+ ILaunchConfigurationWorkingCopy proxyLaunchConfigWC = launchType.newInstance(null, lcName);
+
+ proxyLaunchConfigWC.setAttributes(album.getLaunchProperties());
+ proxyLaunchConfigWC.setAttribute(IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE, configuration
+ .getAttribute(IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE, ""));
+ proxyLaunchConfig = proxyLaunchConfigWC.doSave();
+ }
+
+ Job launchJob = new Job("Launching " + configuration.getName()) {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ proxyLaunchConfig.launch(ILaunchManager.DEBUG_MODE, new NullProgressMonitor(), false, true);
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ return Status.CANCEL_STATUS;
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ launchJob.schedule();
+ return false;
+ }
+
+ private ILaunchConfiguration findExistingLaunchForAlbum(String albumLocation) {
+ ILaunchManager lm = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType launchType = lm.getLaunchConfigurationType(album.getLaunchTypeID());
+
+ try {
+ ILaunchConfiguration[] configurations = lm.getLaunchConfigurations(launchType);
+ for (ILaunchConfiguration configuration : configurations) {
+ if (albumLocation.equals(configuration.getAttribute(IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE,
+ "")))
+ return configuration;
+ }
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ return null;
+ }
+
+ @Override
+ public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)
+ throws CoreException {
+ proxyLaunchConfig.launch(mode, new NullProgressMonitor(), false, true);
+ }
+
+ @Override
+ public String getDebugModelID() {
+ return SNAPSHOT_DEBUG_MODEL_ID;
+ }
+
+ @Override
+ protected Sequence getLiveLaunchSequence(DsfExecutor executor, EDCLaunch launch, IProgressMonitor pm) {
+ return null;
+ }
+
+ @Override
+ protected IDsfDebugServicesFactory newServiceFactory() {
+ return null;
+ }
+
+ @Override
+ protected String getPluginID() {
+ return EDCDebugger.getUniqueIdentifier();
+ }
+
+ @Override
+ protected boolean isSameTarget(EDCLaunch existingLaunch,
+ ILaunchConfiguration configuration, String mode) {
+ return false;
+ }
+
+ @Override
+ public EDCLaunch createLaunch(ILaunchConfiguration configuration,
+ String mode) {
+ return new SnapshotLaunch(configuration, mode, null, getDebugModelID());
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.core.model.ICBreakpoint;
+import org.eclipse.cdt.debug.core.model.ICLineBreakpoint;
+import org.eclipse.cdt.debug.core.model.ICWatchpoint;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.ModuleDMC;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider.ILineAddresses;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.DMContexts;
+import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2;
+import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2.BreakpointEventType;
+import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2.ITargetBreakpointInfo;
+import org.eclipse.cdt.dsf.debug.service.IBreakpointAttributeTranslator2;
+import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointDMContext;
+import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
+import org.eclipse.cdt.dsf.service.DsfServicesTracker;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.model.IBreakpoint;
+
+public class BreakpointAttributeTranslator implements IBreakpointAttributeTranslator2 {
+
+ private DsfServicesTracker dsfServicesTracker;
+ private DsfSession dsfSession;
+ private ITargetEnvironment targetEnvService;
+
+ public BreakpointAttributeTranslator(DsfSession dsfSession) {
+ super();
+ this.dsfSession = dsfSession;
+
+ dsfServicesTracker = new DsfServicesTracker(EDCDebugger.getDefault().getBundle().getBundleContext(), dsfSession.getId());
+ targetEnvService = dsfServicesTracker.getService(ITargetEnvironment.class);
+ assert targetEnvService != null;
+ }
+
+ public boolean canUpdateAttributes(IBreakpointDMContext bp, Map<String, Object> delta) {
+ /*
+ * This method decides whether we need to re-install the breakpoint
+ * based on the attributes change (refer to caller in
+ * BreakpointsMediator). For EDC, following changed attributes justify
+ * re-installation.
+ */
+ // Check if there is any modified attribute
+ if (delta == null || delta.size() == 0)
+ return true;
+
+ // Check the "critical" attributes
+ // TODO: threadID change
+ if (delta.containsKey(IMarker.LINE_NUMBER) // Line number
+ || delta.containsKey(IBreakpoint.ENABLED) // EDC don't handle enable/disable. TODO: ask ITargetEnvironment service if it can handle it.
+ || delta.containsKey(ICLineBreakpoint.FUNCTION) // Function name
+ || delta.containsKey(ICLineBreakpoint.ADDRESS) // Absolute address
+ || delta.containsKey(ICWatchpoint.EXPRESSION) // Watchpoint expression
+ || delta.containsKey(ICWatchpoint.READ) // Watchpoint type
+ || delta.containsKey(ICWatchpoint.WRITE)) { // Watchpoint type
+ return false;
+ }
+
+ // for other attrs (ICBreakpoint.INSTALL_COUNT, ICBreakpoint.IGNORE_COUNT,
+ // ICBreakpoint.CONDITION, etc), we can update by just copying.
+ return true;
+ }
+
+ public void dispose() {
+ if (dsfServicesTracker != null)
+ dsfServicesTracker.dispose();
+ dsfSession = null;
+ }
+
+ public List<Map<String, Object>> getBreakpointAttributes(IBreakpoint bp, boolean bpManagerEnabled)
+ throws CoreException {
+ // The breakpoint mediator allows for multiple target-side breakpoints
+ // to be created for each IDE breakpoint. But the API is not good enough.
+
+ // obsolete
+ List<Map<String, Object>> retVal = new ArrayList<Map<String, Object>>(1);
+ return retVal;
+ }
+
+ public void initialize(BreakpointsMediator2 mediator) {
+
+ }
+
+ public boolean supportsBreakpoint(IBreakpoint bp) {
+ // We support only CDT breakpoints.
+ return bp instanceof ICBreakpoint;
+ }
+
+ public void updateBreakpointStatus(IBreakpoint bp) {
+ // obsolet, do nothing.
+ }
+
+ public void updateBreakpointsStatus(Map<IBreakpoint, Map<IBreakpointsTargetDMContext, ITargetBreakpointInfo[]>> bpsInfo,
+ BreakpointEventType eventType) {
+ for (IBreakpoint bp : bpsInfo.keySet()) {
+ if (! (bp instanceof ICBreakpoint)) // not C breakpoints, bail out.
+ return;
+
+ final ICBreakpoint icbp = (ICBreakpoint) bp;
+
+ Map<IBreakpointsTargetDMContext, ITargetBreakpointInfo[]> targetBpPerContext = bpsInfo.get(bp);
+
+ switch (eventType) {
+ case ADDED: {
+ int installCountTotal = 0;
+ for (ITargetBreakpointInfo[] tbpInfos : targetBpPerContext.values()) {
+ // For each BpTargetDMContext, we increment the installCount for each
+ // target BP that has been successfully installed.
+ int installCountPerContext = 0;
+ for (ITargetBreakpointInfo tbp : tbpInfos) {
+ if (tbp.getTargetBreakpoint() != null)
+ installCountPerContext++;
+ }
+ installCountTotal += installCountPerContext;
+ }
+
+ for (int i=0; i < installCountTotal; i++)
+ try {
+ // this will eventually carried out in a workbench runnable.
+ icbp.incrementInstallCount();
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ }
+ break;
+ }
+ case MODIFIED:
+ break;
+
+ case REMOVED: {
+ int removeCountTotal = 0;
+ for (ITargetBreakpointInfo[] tbpInfos : targetBpPerContext.values()) {
+ // For each BpTargetDMContext, we decrement the installCount for each
+ // target BP that we tried to remove, even if the removal failed. That's
+ // because I've not seen a way to tell platform that removal fails
+ // and the BP should be kept in UI.
+ removeCountTotal += tbpInfos.length;
+ }
+
+ for (int i=0; i < removeCountTotal; i++)
+ try {
+ if (icbp.isRegistered()) // not deleted in UI
+ icbp.decrementInstallCount();
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public Map<String, Object> convertAttributes(Map<String, Object> platformBPAttrDelta) {
+ // For EDC, we don't need any conversion yet....11/08/09.
+ return new HashMap<String, Object>(platformBPAttrDelta);
+ }
+
+ public void resolveBreakpoint(IBreakpointsTargetDMContext context, IBreakpoint breakpoint,
+ final Map<String, Object> attributes, final DataRequestMonitor<List<Map<String, Object>>> drm) {
+
+ final List<Map<String, Object>> targetBPAttrs = new ArrayList<Map<String, Object>>(1);
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null,
+ "Resolving breakpoint " + EDCTrace.fixArg(breakpoint) + " in context " + EDCTrace.fixArg(context)); }
+
+ if (dsfSession == null) {
+ // already disposed
+ drm.setData(targetBPAttrs);
+ drm.done();
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) {EDCTrace.getTrace().traceExit(null, "null session");}
+ return;
+ }
+
+ Map<String, Object> oneBPAttr;
+
+ final ModuleDMC module = (ModuleDMC) context;
+
+ String bpType = (String)attributes.get(Breakpoints.BREAKPOINT_SUBTYPE);
+
+ if (bpType.equals(Breakpoints.ADDRESS_BREAKPOINT)) {
+ String addr = (String)attributes.get(ICLineBreakpoint.ADDRESS);
+ // This is hex string with "0x".
+ assert addr != null;
+
+ oneBPAttr = new HashMap<String, Object>(attributes);
+ String s = addr.toLowerCase().startsWith("0x") ? addr.substring(2) : addr;
+ oneBPAttr.put(Breakpoints.RUNTIME_ADDRESS, s);
+ targetBPAttrs.add(oneBPAttr);
+
+ drm.setData(targetBPAttrs);
+ drm.done();
+ }
+ else if (bpType.equals(Breakpoints.FUNCTION_BREAKPOINT)) {
+ String function = (String) attributes.get(ICLineBreakpoint.FUNCTION);
+ assert (function != null && function.length() > 0);
+
+ // the point is a symbol
+ Symbols symService = dsfServicesTracker.getService(Symbols.class);
+ List<IAddress> addrs = symService.getFunctionAddress(module, function);
+ for (IAddress a : addrs) {
+ oneBPAttr = new HashMap<String, Object>(attributes);
+ oneBPAttr.put(Breakpoints.RUNTIME_ADDRESS, a.toString(16));
+
+ targetBPAttrs.add(oneBPAttr);
+ }
+
+ drm.setData(targetBPAttrs);
+ drm.done();
+ }
+ else {
+ assert bpType.equals(Breakpoints.LINE_BREAKPOINT);
+
+ final String bpFile = (String) attributes.get(ICBreakpoint.SOURCE_HANDLE);
+ final Integer line = (Integer) attributes.get(IMarker.LINE_NUMBER);
+
+ final IExecutionDMContext exe_dmc = DMContexts.getAncestorOfType(context, IExecutionDMContext.class);
+
+ final ICBreakpoint icBP = (ICBreakpoint)breakpoint;
+
+ assert exe_dmc != null : "ExecutionDMContext is unknown in resolveBreakpoint().";
+
+ Modules modulesService = dsfServicesTracker.getService(Modules.class);
+ ISymbolDMContext sym_dmc = DMContexts.getAncestorOfType(context, ISymbolDMContext.class);
+
+ String compileFile = EDCLaunch.getLaunchForSession(dsfSession.getId()).getCompilationPath(bpFile);
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null,
+ "BP file: " + bpFile + " Compile file: " + compileFile); }
+
+ /*
+ * Look for code lines within five lines above and below the line in
+ * question as we don't want to move a breakpoint too far.
+ */
+ modulesService.findClosestLineWithCode(sym_dmc, compileFile, line, 5,
+ new DataRequestMonitor<ILineAddresses>(dsfSession.getExecutor(), drm) {
+
+ @Override
+ protected void handleCompleted() {
+ if (! isSuccess()) {
+ drm.setStatus(getStatus());
+ drm.done();
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null,
+ "findClosestLineWithCode failed: " + drm.getStatus()); }
+ return;
+ }
+
+ ILineAddresses codeLine = getData();
+
+ /*
+ * there could be multiple address ranges for the same
+ * source line. e.g. for templates or inlined functions. if
+ * so, we need only set a breakpoint on the first location
+ */
+ IAddress[] addresses = codeLine.getAddress();
+ if (addresses.length > 0) {
+ IAddress address = addresses[0];
+ for (int i = 1; i < addresses.length; i++)
+ if (addresses[i].getValue().longValue() < address.getValue().longValue())
+ address = addresses[i];
+ Map<String, Object> targetAttr = new HashMap<String, Object>(attributes);
+ targetAttr.put(Breakpoints.RUNTIME_ADDRESS, address.toString(16));
+ targetBPAttrs.add(targetAttr);
+ }
+
+ drm.setData(targetBPAttrs);
+
+ int actualCodeLine = codeLine.getLineNumber();
+
+ if (actualCodeLine == line)
+ drm.done();
+ else {
+ // breakpoint is resolved to a different line (the closest code line).
+ // If there is no user breakpoint at that line, we move the breakpoint there.
+ // Otherwise just mark this breakpoint as unresolved.
+ //
+ final int newLine = actualCodeLine;
+
+ /**
+ * Move the breakpoint to the actual code line.
+ *
+ * Should we run following code in another thread ? Seems yes according to comment in
+ * BreakpointsMediator2.startTrackingBreakpoints(). But that way we'll run into this
+ * problem:
+ * 11 // blank line
+ * 12 // blank line
+ * 13 i = 2;
+ * set bp at line 11 & 12, start debugger, we'll get two resolved breakpoints on line 13
+ * (check in Breakpoints view).
+ *
+ * To fix that issue, I just run this in DSF executor thread. I don't see any problem
+ * in my test......... 01/03/11
+ */
+ if (null == findUserBreakpointAt(bpFile, newLine)) {
+ // After we change the line number attribute, a breakpoint-change
+ // notification will come from platform through BreakpointsMediator2,
+ // resulting in installation of the changed bp and removal of the
+ // original bp.
+ try {
+ icBP.getMarker().setAttribute(IMarker.LINE_NUMBER, newLine);
+ } catch (CoreException e) {
+ // When will this happen ? ignore.
+ }
+
+ // At this point the "drm" contains a valid list of "targetBPAttrs", namely
+ // we treat this BP as resolved. This is needed for such moved-BP to work
+ // on debugger start.
+ drm.done();
+ }
+ else {
+ targetBPAttrs.clear(); // mark the BP as unresolved by clearing the list.
+ drm.done();
+ }
+ }
+ }
+ });
+ }
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) {EDCTrace.getTrace().traceExit(null);}
+ }
+
+ public Map<String, Object> getAllBreakpointAttributes(IBreakpoint platformBP, boolean bpManagerEnabled)
+ throws CoreException {
+ // Check that the marker exists and retrieve its attributes.
+ // Due to accepted race conditions, the breakpoint marker may become
+ // null while this method is being invoked. In this case throw an exception
+ // and let the caller handle it.
+ IMarker marker = platformBP.getMarker();
+ if (marker == null || !marker.exists()) {
+ throw new DebugException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ "Breakpoint marker does not exist", null));
+ }
+ // Suppress cast warning: platform is still on Java 1.3
+ Map<String, Object> platformBpAttrs = marker.getAttributes();
+
+ // Just make a copy of the platform attributes.
+ // Add conversion or addition when needed.
+ Map<String, Object> attrs = new HashMap<String, Object>(platformBpAttrs);
+
+ if (platformBP instanceof ICWatchpoint) {
+ attrs.put(Breakpoints.BREAKPOINT_TYPE, Breakpoints.WATCHPOINT);
+ /*
+ * Related Attributes attributes.get(ICWatchpoint.EXPRESSION));
+ * attributes.get(ICWatchpoint.READ));
+ * attributes.get(ICWatchpoint.WRITE));
+ */
+ } else if (platformBP instanceof ICLineBreakpoint) {
+ attrs.put(Breakpoints.BREAKPOINT_TYPE, Breakpoints.BREAKPOINT);
+
+ String file = (String) attrs.get(ICBreakpoint.SOURCE_HANDLE);
+ String address = (String) attrs.get(ICLineBreakpoint.ADDRESS);
+ String function = (String) attrs.get(ICLineBreakpoint.FUNCTION);
+ Integer line = (Integer) attrs.get(IMarker.LINE_NUMBER);
+
+ if (address != null && address.length() > 0)
+ attrs.put(Breakpoints.BREAKPOINT_SUBTYPE, Breakpoints.ADDRESS_BREAKPOINT);
+ else if (function != null && function.length() > 0)
+ attrs.put(Breakpoints.BREAKPOINT_SUBTYPE, Breakpoints.FUNCTION_BREAKPOINT);
+ else {
+ assert file != null && file.length() > 0 && line != null;
+ attrs.put(Breakpoints.BREAKPOINT_SUBTYPE, Breakpoints.LINE_BREAKPOINT);
+ }
+ /*
+ * Related attributes: String
+ * attributes.get(ICBreakpoint.SOURCE_HANDLE)); Int
+ * attributes.get(IMarker.LINE_NUMBER)); String
+ * attributes.get(ICLineBreakpoint.FUNCTION)); String
+ * attributes.get(ICLineBreakpoint.ADDRESS));
+ */
+ } else {
+ // catchpoint?
+ }
+
+ /*
+ * Common fields attributes.get(ICBreakpoint.CONDITION));
+ * attributes.get(ICBreakpoint.IGNORE_COUNT));
+ * attributes.get(ICBreakpoint.INSTALL_COUNT));
+ * attributes.get(ICBreakpoint.ENABLED));
+ * attributes.get(ATTR_THREAD_ID)); // TODO: check: gdb specific ?
+ */
+
+ // If the breakpoint manager is disabled, override the enabled
+ // attribute.
+ if (!bpManagerEnabled) {
+ attrs.put(IBreakpoint.ENABLED, false);
+ }
+
+ return attrs;
+ }
+
+ public boolean canUpdateAttributes(IBreakpoint bp, IBreakpointsTargetDMContext context, Map<String, Object> attrDelta) {
+ // no special handling needed for EDC yet.
+ return canUpdateAttributes(null, attrDelta);
+ }
+
+ /**
+ * Find the CDT line breakpoint that exists at the given line of the
+ * given file.
+ *
+ * @param bpFile
+ * @param bpLine
+ * @return IBreakpoint if found, null otherwise.
+ */
+ static private IBreakpoint findUserBreakpointAt(
+ String bpFile, int bpLine) {
+ IBreakpoint[] platformBPs = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints();
+ for (IBreakpoint pbp : platformBPs) {
+ if (pbp instanceof ICLineBreakpoint) {
+ // Check that the marker exists and retrieve its attributes.
+ // Due to accepted race conditions, the breakpoint marker may become
+ // null while this method is being invoked. In this case throw an exception
+ // and let the caller handle it.
+ IMarker marker = pbp.getMarker();
+ if (marker == null || !marker.exists())
+ continue;
+
+ // Suppress cast warning: platform is still on Java 1.3
+ try {
+ Map<String, Object> attrs = marker.getAttributes();
+
+ String file = (String) attrs.get(ICBreakpoint.SOURCE_HANDLE);
+ Integer line = (Integer) attrs.get(IMarker.LINE_NUMBER);
+
+ if (bpFile.equals(file) && bpLine == line)
+ return pbp;
+ }
+ catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+
+ return null;
+ }
+}
+
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.IllegalFormatException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.core.model.ICBreakpoint;
+import org.eclipse.cdt.debug.core.model.ICLineBreakpoint;
+import org.eclipse.cdt.debug.core.model.ICWatchpoint;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.ModuleDMC;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.ModuleLoadedEvent;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.ModuleUnloadedEvent;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExecutionDMC;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.debug.edc.services.DMContext;
+import org.eclipse.cdt.debug.edc.services.IDSFServiceUsingTCF;
+import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCExpression;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.debug.edc.services.Stack;
+import org.eclipse.cdt.debug.edc.tcf.extension.ProtocolConstants.IModuleProperty;
+import org.eclipse.cdt.debug.internal.core.breakpoints.BreakpointProblems;
+import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
+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.AbstractDMEvent;
+import org.eclipse.cdt.dsf.datamodel.DMContexts;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2;
+import org.eclipse.cdt.dsf.debug.service.IBreakpointAttributeTranslator;
+import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData;
+import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules.ModuleLoadedDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IModules.ModuleUnloadedDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.Addr32;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.model.MemoryByte;
+import org.eclipse.tm.tcf.protocol.IService;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IBreakpoints.DoneCommand;
+
+public class Breakpoints extends AbstractEDCService implements IBreakpoints, IDSFServiceUsingTCF {
+
+ /**
+ * Breakpoint attributes markers used in the map parameters of
+ * insert/updateBreakpoint(). All are optional with the possible exception
+ * of TYPE. It is the responsibility of the
+ * {@link IBreakpointAttributeTranslator} to ensure that the set of
+ * attributes provided is sufficient to create/update a valid breakpoint on
+ * the back-end.
+ */
+ public static final String PREFIX = "org.eclipse.cdt.debug.edc.breakpoint"; //$NON-NLS-1$
+
+ // Our own attribute keys.
+ //
+ /**
+ * Breakpoint type: value is string.
+ */
+ public static final String BREAKPOINT_TYPE = PREFIX + ".type"; //$NON-NLS-1$
+ // type values:
+ public static final String BREAKPOINT = "breakpoint"; //$NON-NLS-1$
+ public static final String WATCHPOINT = "watchpoint"; //$NON-NLS-1$
+ public static final String CATCHPOINT = "catchpoint"; //$NON-NLS-1$
+
+ /**
+ * breakponint sub-type: value is string.
+ */
+ public static final String BREAKPOINT_SUBTYPE = PREFIX + ".subtype"; //$NON-NLS-1$
+ // sub-type values:
+ public static final String LINE_BREAKPOINT = "line_bp"; //$NON-NLS-1$
+ public static final String FUNCTION_BREAKPOINT = "function_bp"; //$NON-NLS-1$
+ public static final String ADDRESS_BREAKPOINT = "address_bp"; //$NON-NLS-1$
+
+ /**
+ * breakpoint runtime address: value is hex string with no preceding "0x".
+ */
+ public static final String RUNTIME_ADDRESS = PREFIX + ".runtime_addr";
+
+ // Error messages
+ static final String NULL_STRING = ""; //$NON-NLS-1$
+ static final String UNKNOWN_EXECUTION_CONTEXT = "Unknown execution context"; //$NON-NLS-1$
+ static final String UNKNOWN_BREAKPOINT_CONTEXT = "Unknown breakpoint context"; //$NON-NLS-1$
+ static final String UNKNOWN_BREAKPOINT_TYPE = "Unknown breakpoint type"; //$NON-NLS-1$
+ static final String UNKNOWN_BREAKPOINT = "Unknown breakpoint"; //$NON-NLS-1$
+ static final String BREAKPOINT_INSERTION_FAILURE = "Breakpoint insertion failure"; //$NON-NLS-1$
+ static final String WATCHPOINT_INSERTION_FAILURE = "Watchpoint insertion failure"; //$NON-NLS-1$
+ static final String INVALID_CONDITION = "Invalid condition"; //$NON-NLS-1$
+
+ // User breakpoints (those from the IDE) currently installed.
+ private final Map<IBreakpointDMContext, BreakpointDMData> userBreakpoints = new HashMap<IBreakpointDMContext, BreakpointDMData>();
+
+ /**
+ * Internal temporary breakpoints set by debugger for stepping.
+ */
+ private final List<BreakpointDMData> tempBreakpoints = new ArrayList<BreakpointDMData>();
+
+ private org.eclipse.tm.tcf.services.IBreakpoints tcfBreakpointService;
+
+ // Module in which startup breakpoint is installed for the debug session.
+ private Map<ILaunchConfiguration, ModuleDMC> startupBreakpointModule = new HashMap<ILaunchConfiguration, ModuleDMC>();
+
+ private ISourceLocator sourceLocator;
+
+ private Map<ICBreakpoint, IMarker> fBreakpointMarkers = new HashMap<ICBreakpoint, IMarker>();
+
+ static private long nextBreakpointID = 1;
+
+ // /////////////////////////////////////////////////////////////////////////
+ // Breakpoint Events
+ // /////////////////////////////////////////////////////////////////////////
+
+ public class BreakpointsChangedEvent extends AbstractDMEvent<IBreakpointsTargetDMContext> implements
+ IBreakpointsChangedEvent {
+ private IBreakpointDMContext[] eventBreakpoints;
+
+ public BreakpointsChangedEvent(IBreakpointDMContext bp) {
+ super(DMContexts.getAncestorOfType(bp, IBreakpointsTargetDMContext.class));
+ eventBreakpoints = new IBreakpointDMContext[] { bp };
+ }
+
+ public IBreakpointDMContext[] getBreakpoints() {
+ return eventBreakpoints;
+ }
+ }
+
+ public class BreakpointAddedEvent extends BreakpointsChangedEvent implements IBreakpointsAddedEvent {
+ public BreakpointAddedEvent(IBreakpointDMContext context) {
+ super(context);
+ }
+ }
+
+ public class BreakpointUpdatedEvent extends BreakpointsChangedEvent implements IBreakpointsUpdatedEvent {
+ public BreakpointUpdatedEvent(IBreakpointDMContext context) {
+ super(context);
+ }
+ }
+
+ public class BreakpointRemovedEvent extends BreakpointsChangedEvent implements IBreakpointsRemovedEvent {
+ public BreakpointRemovedEvent(IBreakpointDMContext context) {
+ super(context);
+ }
+ }
+
+ // /////////////////////////////////////////////////////////////////////////
+ // IBreakpointDMContext
+ // /////////////////////////////////////////////////////////////////////////
+ @Immutable
+ public static final class BreakpointDMContext extends DMContext implements IBreakpointDMContext {
+ public BreakpointDMContext(String sessionID, IDMContext[] parents, long id) {
+ super(sessionID, parents, Long.toString(id));
+ }
+
+ @Override
+ public String toString() {
+ return "BreakpointDMContext [id=" + getID() + "]";
+ }
+ }
+
+ public class BreakpointDMData implements IBreakpointDMData {
+
+ private final long id; // internal ID.
+ private final IBreakpointDMContext context;
+ private final IAddress[] addresses;
+ private final byte[] originalInstruction;
+ private Map<String, Object> properties;
+ private int hitCount;
+
+ public BreakpointDMData(long id, IBreakpointDMContext context, IAddress[] addresses,
+ Map<String, Object> properties) {
+ super();
+ this.id = id;
+ this.context = context;
+ this.addresses = addresses;
+ this.originalInstruction = null;
+ this.properties = new HashMap<String, Object>(properties); // make a copy
+ }
+
+ public BreakpointDMData(long id, IBreakpointDMContext context, IAddress[] addresses,
+ byte[] fOriginalInstruction, Map<String, Object> properties) {
+ super();
+ this.id = id;
+ this.context = context;
+ this.addresses = addresses;
+ this.originalInstruction = fOriginalInstruction;
+ this.properties = new HashMap<String, Object>(properties);
+ }
+
+ public IAddress[] getAddresses() {
+ return addresses;
+ }
+
+ public String getBreakpointType() {
+ return (String) properties.get(BREAKPOINT_TYPE);
+ }
+
+ public String getCondition() {
+ return (String) properties.get(ICBreakpoint.CONDITION);
+ }
+
+ public String getExpression() {
+ return (String) properties.get(ICWatchpoint.EXPRESSION);
+ }
+
+ public String getFileName() {
+ return (String) properties.get(ICBreakpoint.SOURCE_HANDLE);
+ }
+
+ public String getFunctionName() {
+ return (String) properties.get(ICLineBreakpoint.FUNCTION);
+ }
+
+ public int getIgnoreCount() {
+ return (Integer) properties.get(ICBreakpoint.IGNORE_COUNT);
+ }
+
+ public int getLineNumber() {
+ return (Integer) properties.get(IMarker.LINE_NUMBER);
+ }
+
+ public boolean isEnabled() {
+ return (Boolean) properties.get(IBreakpoint.ENABLED);
+ }
+
+ public long getID() {
+ return id;
+ }
+
+ public byte[] getOriginalInstruction() {
+ return originalInstruction;
+ }
+
+ /**
+ * @return reference to properties map of the bp.
+ */
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public IBreakpointDMContext getContext() {
+ return context;
+ }
+
+ public void setProperties(Map<String, Object> props) {
+ properties = new HashMap<String, Object>(props);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + getOuterType().hashCode();
+ result = prime * result + (int) (id ^ (id >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BreakpointDMData other = (BreakpointDMData) obj;
+ if (!getOuterType().equals(other.getOuterType()))
+ return false;
+ if (id != other.id)
+ return false;
+ return true;
+ }
+
+ private Breakpoints getOuterType() {
+ return Breakpoints.this;
+ }
+
+ @Override
+ public String toString() {
+ String s = getFileName();
+ if (s == null) // address breakpoint
+ s = getAddresses()[0].toHexAddressString();
+ else {
+ if (getFunctionName() != null)
+ s += ": " + getFunctionName();
+ else
+ s += ":line " + getLineNumber();
+ }
+ return "Breakpoint@" + s;
+ }
+
+ public void incrementHitCount() {
+ hitCount++;
+ }
+
+ public int getHitCount()
+ {
+ return hitCount;
+ }
+ }
+
+ public Breakpoints(DsfSession session) {
+ super(session, new String[] { IBreakpoints.class.getName(), Breakpoints.class.getName() });
+ }
+
+ @Override
+ public void initialize(final RequestMonitor rm) {
+ super.initialize(new RequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ // Register as event listener.
+ getSession().addServiceEventListener(Breakpoints.this, null);
+ rm.done();
+ }
+ });
+ }
+
+ public void getBreakpointDMData(IBreakpointDMContext dmc, DataRequestMonitor<IBreakpointDMData> drm) {
+ if (!userBreakpoints.containsKey(dmc)) {
+ drm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, UNKNOWN_BREAKPOINT));
+ } else
+ drm.setData(userBreakpoints.get(dmc));
+
+ drm.done();
+ }
+
+ public void getBreakpoints(IBreakpointsTargetDMContext context, DataRequestMonitor<IBreakpointDMContext[]> drm) {
+ Set<IBreakpointDMContext> breakpointIDs = userBreakpoints.keySet();
+ drm.setData(breakpointIDs.toArray(new IBreakpointDMContext[breakpointIDs.size()]));
+ drm.done();
+ }
+
+ /**
+ * Find breakpoint, either user-set or debugger internal temporary one, at
+ * the given address.
+ *
+ * @param addr
+ * - absolute runtime address.
+ * @return null if not found.
+ */
+ public BreakpointDMData findBreakpoint(IAddress addr) {
+ BreakpointDMData bp = findUserBreakpoint(addr);
+ if (bp == null)
+ bp = findTempBreakpoint(addr);
+ return bp;
+ }
+
+ /**
+ * Find user breakpoint at the given address.
+ *
+ * @param addr
+ * - absolute runtime address.
+ * @return null if not found.
+ */
+ public BreakpointDMData findUserBreakpoint(IAddress addr) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, "Find user breakpoint at " + addr.toHexAddressString()); }
+
+ for (BreakpointDMData bp : userBreakpoints.values())
+ if (bp.getAddresses()[0].equals(addr)) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(bp.toString())); }
+ return bp;
+ }
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) {EDCTrace.getTrace().traceExit(null, "not found.");}
+ return null;
+ }
+
+ /**
+ * Find a temporary breakpoint at the given address.
+ *
+ * @param addr
+ * - absolute runtime address.
+ * @return null if not found.
+ */
+ public BreakpointDMData findTempBreakpoint(IAddress addr) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, "Find temp breakpoint at " + addr.toHexAddressString()); }
+
+ for (BreakpointDMData bp : tempBreakpoints) {
+ if (bp.getAddresses()[0].equals(addr)) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(bp.toString())); }
+ return bp;
+ }
+ }
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null, "not found."); }
+ return null;
+ }
+
+ /**
+ * Remove software breakpoints inserted in memory by debugger from the given
+ * memory buffer starting from given address.
+ *
+ * @param startAddr
+ * start address of the memory data.
+ * @param memBuffer
+ * a buffer containing data from memory. Its content will be
+ * changed by this method if a breakpoint falls in the address
+ * range.
+ */
+ public void removeBreakpointFromMemoryBuffer(IAddress startAddr, MemoryByte[] memBuffer) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, "remove bp in memory area:" + startAddr.toHexAddressString() + "," + memBuffer.length); }
+
+ // If the breakpoint is actually set by TCF agent, we have to assume
+ // that the TCF agent would do this breakpoint removing for us as
+ // we have no idea how the agent set the breakpoint (e.g. is it software
+ // breakpoint ? if yes, what breakpoint instruction is used ?)
+ //
+ if (usesTCFBreakpointService())
+ return;
+
+ for (BreakpointDMData edcBp : userBreakpoints.values()) {
+ // TODO: bail out if the bp is not software breakpoint.
+
+ IAddress bpAddr = edcBp.getAddresses()[0];
+ int bpOffset = (int) startAddr.distanceTo(bpAddr).longValue();
+ if (bpOffset >= 0 && bpOffset < memBuffer.length) {
+ // the breakpoint falls in the buffer. Restore the original
+ // instruction.
+ byte[] orgInst = edcBp.getOriginalInstruction();
+ for (int i = 0; i < orgInst.length && i + bpOffset < memBuffer.length; i++) {
+ memBuffer[bpOffset + i].setValue(orgInst[i]);
+ }
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null, "breakpoint removed at offset " + bpOffset); }
+ }
+ }
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ public void insertBreakpoint(IBreakpointsTargetDMContext context, Map<String, Object> attributes,
+ DataRequestMonitor<IBreakpointDMContext> drm) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { attributes })); }
+
+ // Validate the context
+ if (context == null) {
+ drm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_EXECUTION_CONTEXT,
+ null));
+ drm.done();
+ return;
+ }
+
+ // Validate the breakpoint type
+ String type = (String) attributes.get(BREAKPOINT_TYPE);
+ if (type == null) {
+ drm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE,
+ null));
+ drm.done();
+ return;
+ }
+
+ // And go...
+ if (type.equals(BREAKPOINT)) {
+ addBreakpoint(context, attributes, drm);
+ } else if (type.equals(WATCHPOINT)) {
+ drm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED,
+ "Watchpoint is not supported yet.", null));
+ drm.done();
+ } else {
+ drm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, UNKNOWN_BREAKPOINT_TYPE,
+ null));
+ drm.done();
+ }
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * Set one target breakpoint.
+ *
+ * @param context
+ * @param attributes
+ * attributes for the target breakpoint. For EDC, it must contain
+ * the RUNTIME_ADDRESS attribute.
+ * @param drm
+ */
+ private void addBreakpoint(final IBreakpointsTargetDMContext context, final Map<String, Object> attributes,
+ final DataRequestMonitor<IBreakpointDMContext> drm) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { attributes })); }
+
+ IExecutionDMContext exe_dmc = DMContexts.getAncestorOfType(context, IExecutionDMContext.class);
+ String bpAddr = (String)attributes.get(RUNTIME_ADDRESS);
+
+ assert exe_dmc != null : "ExecutionDMContext is unknown in addBreakpoint().";
+ assert bpAddr != null;
+
+ createBreakpoint(exe_dmc, new Addr64(bpAddr, 16), attributes, new DataRequestMonitor<BreakpointDMData>(
+ getExecutor(), drm) {
+
+ @Override
+ protected void handleSuccess() {
+ final BreakpointDMData bpd = getData();
+
+ enableBreakpoint(bpd, new RequestMonitor(getExecutor(), drm) {
+
+ @Override
+ protected void handleSuccess() {
+ IBreakpointDMContext bp_dmc = bpd.getContext();
+ drm.setData(bp_dmc);
+
+ // Remember this in our global list.
+ userBreakpoints.put(bp_dmc, bpd);
+
+ drm.done();
+ }
+ });
+ }
+ });
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ public ISourceLocator getSourceLocator() {
+ return sourceLocator;
+ }
+
+ public void setSourceLocator(ISourceLocator sourceLocator) {
+ this.sourceLocator = sourceLocator;
+ }
+
+ /**
+ * Set temporary breakpoint at given address. This is for cases such as
+ * stepping and initial startup breakpoint (aka entry breakpoint).<br>
+ * If a user or temporary breakpoint already exists at the address, no
+ * temporary breakpoint will be set.
+ *
+ * @param context
+ * @param address
+ * @param rm
+ */
+ public void setTempBreakpoint(final IExecutionDMContext context, final IAddress address, final RequestMonitor rm) {
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, "set temp breakpoint at " + address.toHexAddressString()); }
+
+ // If a breakpoint (user-set or temp) exists at the address, we are
+ // done.
+ if (findBreakpoint(address) != null) {
+ rm.done();
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null, "A breakpoint exists at " + address.toHexAddressString()); }
+ return;
+ }
+
+ createBreakpoint(context, address, new HashMap<String, Object>(), new DataRequestMonitor<BreakpointDMData>(
+ getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ final BreakpointDMData bp_data = getData();
+
+ enableBreakpoint(bp_data, new RequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ // Remember this in our list.
+ tempBreakpoints.add(bp_data);
+ rm.done();
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg("A temp breakpoint successfully set at " + address.toHexAddressString())); }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Remove all temporary breakpoints set so far.
+ *
+ * @param rm
+ */
+ public void removeAllTempBreakpoints(final RequestMonitor rm) {
+
+ int numTempBps = tempBreakpoints.size();
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, "remove " + numTempBps + " temp breakpoint" + ((numTempBps == 1)?"":"s") + "."); }
+
+ if (numTempBps == 0) {
+ rm.done();
+ return;
+ }
+
+ CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleCompleted() {
+ if (getStatus().isOK()) {
+ tempBreakpoints.clear();
+ }
+ super.handleCompleted();
+ }
+ };
+
+ crm.setDoneCount(tempBreakpoints.size());
+
+ for (BreakpointDMData bp : tempBreakpoints)
+ disableBreakpoint(bp, crm);
+ }
+
+ private void createBreakpoint(final IExecutionDMContext exeDMC, final IAddress address, final Map<String, Object> props,
+ final DataRequestMonitor<BreakpointDMData> drm) {
+
+ asyncExec(new Runnable() {
+ public void run() {
+ final long id = getNewBreakpointID();
+ final IBreakpointDMContext bp_dmc = new BreakpointDMContext(getSession().getId(), new IDMContext[] { exeDMC },
+ id);
+ final IAddress[] bp_addrs = new IAddress[] { address };
+ final Map<String, Object> properties = new HashMap<String, Object>(props);
+
+ if (usesTCFBreakpointService()) {
+ properties.put(org.eclipse.tm.tcf.services.IBreakpoints.PROP_ID, Long.toString(id));
+ properties.put(org.eclipse.tm.tcf.services.IBreakpoints.PROP_ENABLED, true);
+ properties.put(org.eclipse.tm.tcf.services.IBreakpoints.PROP_TYPE,
+ org.eclipse.tm.tcf.services.IBreakpoints.TYPE_AUTO);
+ properties.put(org.eclipse.tm.tcf.services.IBreakpoints.PROP_LOCATION, address.toString());
+
+
+ // Pass "contexts" for which the BP is supposed to work.
+ // NOTE: EDC extension to TCF:
+ // TCF only define "IBreakpoints.PROP_CONTEXTIDS" as a array of IDs.
+ // In EDC, it's required the first element in the array be IBreakpointsTargetDMContext
+ // which is usually (but not always) a process. This makes EDC backward compatible with
+ // some existing TCF agents.
+ IBreakpointsTargetDMContext bpTargetDMC = DMContexts.getAncestorOfType(exeDMC, IBreakpointsTargetDMContext.class);
+ // pass "exeDMC" as a context if it's different from bpTargetDMC, say, if "exeDMC" is a thread and
+ // the "bpTargetDMC" is the owner process. This allows for thread-specific BP if agent supports it.
+ String[] contexts;
+ if (! exeDMC.equals(bpTargetDMC))
+ contexts = new String[] {((IEDCDMContext)bpTargetDMC).getID(), ((IEDCDMContext)exeDMC).getID()};
+ else
+ contexts = new String[] {((IEDCDMContext)bpTargetDMC).getID()};
+ properties.put(org.eclipse.tm.tcf.services.IBreakpoints.PROP_CONTEXTIDS, contexts);
+
+ getTargetEnvironmentService().updateBreakpointProperties(exeDMC, address, properties);
+
+ drm.setData(new BreakpointDMData(id, bp_dmc, bp_addrs, properties));
+ drm.done();
+ } else { // generic software breakpoint
+ final byte[] bpInstruction = getTargetEnvironmentService().getBreakpointInstruction(exeDMC, address);
+ final int inst_size = bpInstruction.length;
+
+ Memory memoryService = getService(Memory.class);
+ IMemoryDMContext mem_dmc = DMContexts.getAncestorOfType(exeDMC, IMemoryDMContext.class);
+
+ memoryService.getMemory(mem_dmc, address, 0, 1, inst_size, new DataRequestMonitor<MemoryByte[]>(
+ getExecutor(), drm) {
+ @Override
+ protected void handleSuccess() {
+ MemoryByte[] org_inst = getData();
+ final byte[] org_inst_bytes = new byte[org_inst.length];
+ for (int i = 0; i < org_inst.length; i++) {
+ // make sure each byte is okay
+ if (!org_inst[i].isReadable()) {
+ drm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED,
+ ("Cannot read memory at 0x" + address.add(i).getValue().toString(16)),
+ null));
+ drm.done();
+ return;
+ }
+ org_inst_bytes[i] = org_inst[i].getValue();
+ }
+
+ drm.setData(new BreakpointDMData(id, bp_dmc, bp_addrs, org_inst_bytes, properties));
+ drm.done();
+ }
+ });
+ }
+ }
+
+ }, drm);
+
+ }
+
+ /**
+ * Install the breakpoint in the target process.
+ *
+ * @param bp
+ * @param rm
+ */
+ public void enableBreakpoint(final BreakpointDMData bp, final RequestMonitor rm) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(bp)); }
+
+ if (usesTCFBreakpointService()) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ tcfBreakpointService.add(bp.getProperties(), new DoneCommand() {
+
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null) {
+ // Make sure "done()" is called for the rm.
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED,
+ "TCF agent fails to install " + bp + " because:\n"
+ + error.getLocalizedMessage(), error));
+ rm.done();
+ } else {
+ getSession().dispatchEvent(new BreakpointAddedEvent(bp.getContext()),
+ new Hashtable<String, Object>(bp.getProperties()));
+
+ rm.done();
+ }
+ }
+ });
+ }
+ });
+ } else {
+ IAddress bp_addr = bp.getAddresses()[0];
+ IExecutionDMContext exe_dmc = DMContexts.getAncestorOfType(bp.getContext(), IExecutionDMContext.class);
+ byte[] bpInstruction = getTargetEnvironmentService().getBreakpointInstruction(exe_dmc, bp_addr);
+
+ Memory memoryService = getService(Memory.class);
+ IMemoryDMContext mem_dmc = DMContexts.getAncestorOfType(bp.getContext(), IMemoryDMContext.class);
+
+ memoryService.setMemory(mem_dmc, bp_addr, 0, 1, bpInstruction.length, bpInstruction, rm);
+ getSession().dispatchEvent(new BreakpointAddedEvent(bp.getContext()),
+ new Hashtable<String, Object>(bp.getProperties()));
+ }
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ private synchronized long getNewBreakpointID() {
+ return nextBreakpointID++;
+ }
+
+ public void removeBreakpoint(final IBreakpointDMContext dmc, RequestMonitor rm) {
+ // Remove user breakpoint.
+ //
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { dmc })); }
+
+ if (!(dmc instanceof BreakpointDMContext)) {
+ // not our breakpoint, should not happen
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, "Unrecognized breakpoint context."));
+ rm.done();
+ return;
+ }
+
+ if (!userBreakpoints.containsKey(dmc)) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, UNKNOWN_BREAKPOINT));
+ rm.done();
+ return;
+ }
+
+ disableBreakpoint(userBreakpoints.get(dmc), new RequestMonitor(getExecutor(), rm) {
+
+ @Override
+ protected void handleSuccess() {
+ // Remove it from our record.
+ userBreakpoints.remove(dmc);
+
+ super.handleSuccess();
+ }
+ });
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * Remove the breakpoint from the target process.
+ *
+ * @param bp
+ * @param rm
+ */
+ public void disableBreakpoint(final BreakpointDMData bp, final RequestMonitor rm) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { bp })); }
+
+ if (!usesTCFBreakpointService()) {
+ final Memory memoryService = getService(Memory.class);
+ IMemoryDMContext mem_dmc = DMContexts.getAncestorOfType(bp.getContext(), IMemoryDMContext.class);
+ byte[] orgInst = bp.getOriginalInstruction();
+ memoryService.setMemory(mem_dmc, bp.getAddresses()[0], 0, 1, orgInst.length, orgInst, rm);
+ } else {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ Map<String, Object> properties = bp.getProperties();
+ String id = (String) properties.get(org.eclipse.tm.tcf.services.IBreakpoints.PROP_ID);
+ tcfBreakpointService.remove(new String[] { id }, new DoneCommand() {
+
+ public void doneCommand(IToken token, Exception error) {
+ rm.done();
+ }
+ });
+ }
+ });
+ }
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ public void updateBreakpoint(IBreakpointDMContext dmc, Map<String, Object> delta, RequestMonitor rm) {
+ /*
+ * For EDC, we don't need to do any update on non-significant attribute
+ * change, e.g. change of Install_count, ignore_count. For significant
+ * change, the breakpoint will just be re-installed.
+ * See canUpdateAttributes().
+ */
+ BreakpointDMData bp = userBreakpoints.get(dmc);
+ if (bp == null)
+ assert false : "Fail to find BreakpointDMData linked with the IBreakpointDMContext:" + dmc;
+ else {
+ Map<String, Object> existingProps = bp.getProperties();
+ for (String key : delta.keySet())
+ existingProps.put(key, delta.get(key));
+ }
+ rm.done();
+ }
+
+ public boolean usesTCFBreakpointService() {
+ return tcfBreakpointService != null;
+ }
+
+ public void tcfServiceReady(IService service) {
+ tcfBreakpointService = (org.eclipse.tm.tcf.services.IBreakpoints) service;
+ }
+
+ @DsfServiceEventHandler
+ public void eventHandler_installBreakpointsForModule(ModuleLoadedDMEvent e) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { e })); }
+
+ // A new module (including main exe) is loaded. Install breakpoints for
+ // it.
+ ModuleLoadedEvent event = (ModuleLoadedEvent) e;
+ final IExecutionDMContext executionDMC = event.getExecutionDMC();
+ final ModuleDMC module = (ModuleDMC) e.getLoadedModuleContext();
+ BreakpointsMediator2 bm = getService(BreakpointsMediator2.class);
+ if (bm == null) {
+ EDCDebugger.getMessageLogger().logError("Fail to get BreakpointsMediator service to install breakpoints for loaded module "+module, null);
+ assert false;
+ return;
+ }
+
+ final boolean requireResume = requireResume(module);
+
+ IBreakpointsTargetDMContext bt_dmc = DMContexts.getAncestorOfType(module, IBreakpointsTargetDMContext.class);
+ bm.startTrackingBreakpoints(bt_dmc, new RequestMonitor(getExecutor(), null) {
+
+ @Override
+ protected void handleCompleted() {
+ if (!isSuccess()) {
+ // do we want to display a dialog for user ?
+ // No, as it's expected not all breakpoints can be resolved
+ // in the module.
+
+ // Form readable message and log it.
+ IStatus status = getStatus();
+ String msg = MessageFormat.format(
+ "Failed to install some breakpoints in the module [{0}]. Errors: \n", module.getName());
+ if (status.isMultiStatus()) {
+ for (IStatus s : ((MultiStatus) status).getChildren())
+ msg += s.getMessage() + "\n";
+ } else
+ msg += status.getMessage();
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null, msg); }
+ }
+
+ // We should do these regardless of whether installing
+ // breakpoints succeeded or not.
+ setStartupBreakpoint(module, new RequestMonitor(getExecutor(), null) {
+
+ @Override
+ protected void handleCompleted() {
+ // do this regardless of status of installing entry
+ // breakpoint
+ // as it's expected the startup bp not resolvable in all
+ // modules.
+ //
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null, "resume process after module load event ..."); }
+ if (requireResume)
+ ((ExecutionDMC) executionDMC).resume(new RequestMonitor(getExecutor(), null));
+ }
+ });
+ }
+ });
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * Check if resume is required after handling load/unload of the given module.
+ *
+ * @param module
+ * @return
+ */
+ private boolean requireResume(ModuleDMC module) {
+ boolean requireResume = true;
+ Object propvalue = module.getProperties().get(IModuleProperty.PROP_RESUME);
+ if (propvalue != null)
+ if (propvalue instanceof Boolean)
+ requireResume = (Boolean) propvalue;
+
+ return requireResume;
+ }
+
+ /**
+ * Set breakpoint at startup point specified by user.
+ *
+ * @param module
+ * @param rm
+ */
+ protected void setStartupBreakpoint(ModuleDMC module, RequestMonitor rm) {
+ EDCLaunch launch = EDCLaunch.getLaunchForSession(getSession().getId());
+ ILaunchConfiguration launchConfig = launch.getLaunchConfiguration();
+ if (startupBreakpointModule.get(launchConfig) != null) {
+ // already set in a module for this launch configuration, no need to try it for any other module
+ rm.done();
+ return;
+ }
+
+ String startupStopAt = launch.getStartupStopAtPoint();
+ // Is this even requested by user ?
+ if (startupStopAt == null) {
+ rm.done();
+ return;
+ }
+
+ // Ask target environment whether it wants us to try installing
+ // startup breakpoint in the module.
+ ITargetEnvironment te = getTargetEnvironmentService();
+ if (! te.needStartupBreakpointInExecutable(module.getName())) {
+ rm.done();
+ return;
+ }
+
+ IAddress iaddr = null;
+ long address = 0;
+
+ // Check if the point is absolute runtime address.
+ //
+ try {
+ // first check if it's decimal number
+ address = Long.parseLong(startupStopAt);
+ } catch (NumberFormatException e) {
+ // then check if it's hex
+ if (startupStopAt.toLowerCase().startsWith("0x")) {
+ try {
+ address = Long.parseLong(startupStopAt.substring(2), 16);
+ } catch (IllegalFormatException e1) {
+ // ignore
+ }
+ }
+ }
+
+ if (address != 0) {
+ iaddr = new Addr32(address);
+
+ // Assume it is a link-time address first. Run-time addresses are not predictable across launches.
+ IAddress runAddr = module.toRuntimeAddress(iaddr);
+ if (module.containsAddress(runAddr)) {
+ iaddr = runAddr;
+ } else {
+ // Try for a runtime address.
+ if (!module.containsAddress(iaddr)) {
+ // address not in the module, don't bother.
+ // This is to ensure the address breakpoint is installed
+ // after the container module is loaded.
+ iaddr = null;
+ }
+ }
+ } else {
+ // the point is a symbol
+ Symbols symService = getService(Symbols.class);
+ List<IAddress> addrs = symService.getFunctionAddress(module, startupStopAt);
+
+ if (addrs.size() > 0)
+ // just choose the first one
+ iaddr = addrs.get(0);
+ }
+
+ if (iaddr == null) {
+ EDCDebugger.getMessageLogger().logError(
+ "Could not resolve startup breakpoint: "+ startupStopAt, null);
+ rm.done();
+ } else {
+ // The breakpoint is resolved in the module.
+ startupBreakpointModule.put(launchConfig, module);
+
+ IExecutionDMContext exe_dmc = DMContexts.getAncestorOfType(module, IExecutionDMContext.class);
+ setTempBreakpoint(exe_dmc, iaddr, rm);
+ }
+ }
+
+ @DsfServiceEventHandler
+ public void eventHandler_uninstallBreakpointsForModule(ModuleUnloadedDMEvent e) {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { e.getClass().getName(), e })); }
+
+ // An existing module (including main exe) is unloaded. Uninstall
+ // breakpoints for it.
+ ModuleUnloadedEvent event = (ModuleUnloadedEvent) e;
+ final ExecutionDMC executionDMC = (ExecutionDMC)event.getExecutionDMC();
+ final ModuleDMC module = (ModuleDMC) e.getUnloadedModuleContext();
+
+ /*
+ * If module containing startup break is unloaded, mark the startup
+ * break as gone so that we can reinstall it the next time the module is
+ * loaded again in the same debug session.
+ */
+ if (startupBreakpointModule != null &&
+ startupBreakpointModule.equals(module))
+ startupBreakpointModule = null;
+
+ final boolean requireResume = requireResume(module);
+
+ BreakpointsMediator2 bm = getService(BreakpointsMediator2.class);
+ IBreakpointsTargetDMContext bt_dmc = DMContexts.getAncestorOfType(e.getUnloadedModuleContext(),
+ IBreakpointsTargetDMContext.class);
+ bm.stopTrackingBreakpoints(bt_dmc, new RequestMonitor(getExecutor(), null) {
+ @Override
+ protected void handleFailure() {
+ // super will just log the error.
+ super.handleFailure();
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null, "uninstalling breakpoints failed"); }
+ }
+
+ @Override
+ protected void handleSuccess() {
+ super.handleSuccess();
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null, "breakpoints uninstalled and resume process..."); }
+ if (requireResume)
+ executionDMC.resume(new RequestMonitor(getExecutor(), null));
+ }
+ });
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ protected void addBreakpointProblemMarker(final ICBreakpoint breakpoint, final String description, final int severity) {
+ if (! (breakpoint instanceof ICLineBreakpoint))
+ return;
+
+ new Job("Add Breakpoint Problem Marker") { //$NON-NLS-1$
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ // If we have already have a problem marker on this breakpoint
+ // we should remove it first.
+ IMarker marker = fBreakpointMarkers.remove(breakpoint);
+ if (marker != null) {
+ try {
+ marker.delete();
+ } catch (CoreException e) {
+ }
+ }
+
+ ICLineBreakpoint lineBreakpoint = (ICLineBreakpoint) breakpoint;
+ try {
+ // Locate the workspace resource via the breakpoint marker
+ IMarker breakpoint_marker = lineBreakpoint.getMarker();
+ IResource resource = breakpoint_marker.getResource();
+
+ // Add a problem marker to the resource
+ IMarker problem_marker = resource.createMarker(BreakpointProblems.BREAKPOINT_PROBLEM_MARKER_ID);
+ int line_number = lineBreakpoint.getLineNumber();
+ problem_marker.setAttribute(IMarker.LOCATION, String.valueOf(line_number));
+ problem_marker.setAttribute(IMarker.MESSAGE, description);
+ problem_marker.setAttribute(IMarker.SEVERITY, severity);
+ problem_marker.setAttribute(IMarker.LINE_NUMBER, line_number);
+
+ // And save the baby
+ fBreakpointMarkers.put(breakpoint, problem_marker);
+ } catch (CoreException e) {
+ }
+
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+
+ protected void removeBreakpointProblemMarker(final ICBreakpoint breakpoint) {
+
+ final IMarker marker = fBreakpointMarkers.remove(breakpoint);
+ if (marker == null)
+ return;
+
+ new Job("Remove Breakpoint Problem Marker") { //$NON-NLS-1$
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ marker.delete();
+ } catch (CoreException e) {
+ }
+
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+
+ /**
+ * Evaluate condition of given breakpoint, if any.
+ *
+ * @param context
+ * execution context in which to evaluate the condition.
+ * @param bp
+ * the breakpoint.
+ * @param drm
+ * DataRequestMonitor that contains result indicating whether
+ * to stop execution of debugged program. The result value is
+ * true if <br>
+ * 1. the breakpoint has no condition, or <br>
+ * 2. the breakpoint condition is invalid in syntax, or <br>
+ * 3. the breakpoint condition cannot be resolved, or <br>
+ * 4. the breakpoint condition is true.<br>
+ * Otherwise the result in the drm is false.
+ *
+ */
+ public void evaluateBreakpointCondition(IExecutionDMContext context, final BreakpointDMData bp, final DataRequestMonitor<Boolean> drm) {
+ final String expr = bp.getCondition();
+ if (expr == null || expr.length() == 0) {
+ bp.incrementHitCount();
+ drm.setData(bp.getHitCount() > bp.getIgnoreCount());
+ drm.done();
+ return;
+ }
+
+ Stack stackService = getService(Stack.class);
+
+ stackService.getTopFrame(context, new DataRequestMonitor<IFrameDMContext>(getExecutor(), drm) {
+
+ @Override
+ protected void handleCompleted() {
+ if (!isSuccess()) { // fail to get frame, namely cannot
+ // evaluate the condition
+ bp.incrementHitCount();
+ drm.setData(bp.getHitCount() > bp.getIgnoreCount());
+ drm.done();
+ } else {
+ Expressions exprService = getService(Expressions.class);
+ IEDCExpression expression = (IEDCExpression) exprService.createExpression(getData(), expr);
+ FormattedValueDMContext fvc = exprService.getFormattedValueContext(expression,
+ IFormattedValues.NATURAL_FORMAT);
+ FormattedValueDMData value = expression.getFormattedValue(fvc);
+ /*
+ * honor the breakpoint if the condition is true or
+ * invalid.
+ */
+ String vstr = value.getFormattedValue();
+ if (! vstr.equals("true") && ! vstr.equals("false")) //$NON-NLS-1$ //$NON-NLS-2$
+ reportBreakpointProblem(bp.getContext(), "Breakpoint condition failed to resolve to boolean: " + vstr);
+ else // remove any problem marker
+ reportBreakpointProblem(bp.getContext(), "");
+
+ if (!vstr.equals("false"))
+ {
+ bp.incrementHitCount();
+ drm.setData(bp.getHitCount() > bp.getIgnoreCount());
+ }
+ else
+ drm.setData(false); //$NON-NLS-1$
+
+ drm.done();
+ }
+ }
+ });
+ }
+
+ /**
+ * Report breakpoint problem in breakpoint marker.
+ *
+ * @param targetBP
+ * @param description - empty string indicates removing problem marker.
+ */
+ protected void reportBreakpointProblem(IBreakpointDMContext targetBP, String description) {
+ BreakpointsMediator2 bmService = getService(BreakpointsMediator2.class);
+ if (bmService == null) {
+ assert false;
+ return;
+ }
+ IBreakpoint platformBP = bmService.getPlatformBreakpoint(null, targetBP);
+ if (platformBP == null)
+ return;
+
+ if (description.length() > 0)
+ addBreakpointProblemMarker((ICBreakpoint)platformBP, description, IMarker.SEVERITY_WARNING);
+ else
+ removeBreakpointProblemMarker((ICBreakpoint)platformBP);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import org.eclipse.osgi.util.NLS;
+
+public class EDCServicesMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.edc.internal.services.dsf.EDCServicesMessages"; //$NON-NLS-1$
+
+ public static String Expressions_CannotCastOutsideFrame;
+
+ public static String Expressions_CannotModifyCompositeValue;
+
+ public static String Expressions_CannotParseExpression;
+
+ public static String Expressions_ErrorInVariableFormatter;
+
+ public static String Expressions_ExpressionNoLocation;
+
+ public static String Expressions_SyntaxError;
+
+ public static String Disassembly_CannotReadMemoryAt;
+
+ public static String Disassembly_NoDisassemblerYet;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, EDCServicesMessages.class);
+ }
+
+ private EDCServicesMessages() {
+ }
+}
--- /dev/null
+###############################################################################
+# Copyright (c) 2009, 2010 Nokia 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:
+# Nokia - Initial API and implementation
+###############################################################################
+Expressions_CannotCastOutsideFrame=cannot cast expressions outside stack frame
+Expressions_CannotModifyCompositeValue=Cannot modify the value of a composite type
+Expressions_CannotParseExpression=cannot parse expression
+Expressions_SyntaxError=syntax error
+Expressions_ErrorInVariableFormatter=Error in variable formatter:
+Expressions_ExpressionNoLocation=expression has no location
+Disassembly_CannotReadMemoryAt=Cannot read memory at {0} (length {1})
+Disassembly_NoDisassemblerYet=No disassembler is available yet.
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.core.dom.ast.IASTTypeId;
+import org.eclipse.cdt.core.dom.ast.IBasicType;
+import org.eclipse.cdt.debug.edc.MemoryUtils;
+import org.eclipse.cdt.debug.edc.formatter.ITypeContentProvider;
+import org.eclipse.cdt.debug.edc.formatter.IVariableValueConverter;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.NumberFormatUtils;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.ASTEvaluationEngine;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.IArrayDimensionType;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.InstructionSequence;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.Interpreter;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.OperandValue;
+import org.eclipse.cdt.debug.edc.internal.formatter.FormatExtensionManager;
+import org.eclipse.cdt.debug.edc.internal.symbols.IAggregate;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IEnumeration;
+import org.eclipse.cdt.debug.edc.internal.symbols.IField;
+import org.eclipse.cdt.debug.edc.internal.symbols.IInheritance;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IReferenceType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISubroutineType;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.debug.edc.services.DMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCExpression;
+import org.eclipse.cdt.debug.edc.services.IEDCExpressions;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+import org.eclipse.cdt.debug.edc.symbols.IInvalidVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.Immutable;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
+import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
+import org.eclipse.cdt.dsf.datamodel.DMContexts;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.IExpressions;
+import org.eclipse.cdt.dsf.debug.service.IExpressions2;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
+import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+public class Expressions extends AbstractEDCService implements IEDCExpressions {
+
+ public abstract class BaseEDCExpressionDMC extends DMContext implements IEDCExpression {
+ protected String expression;
+ private InstructionSequence parsedExpression;
+ private final ASTEvaluationEngine engine;
+ private final StackFrameDMC frame;
+ protected Number value;
+ protected IStatus valueError;
+ private IVariableLocation valueLocation;
+ private IType valueType;
+ private boolean hasChildren = false;
+ private String valueString;
+
+ public BaseEDCExpressionDMC(IDMContext parent, String expression, String name) {
+ super(Expressions.this, new IDMContext[] { parent }, name, ((IEDCDMContext)parent).getID() + "." + name); //$NON-NLS-1$
+ this.expression = expression;
+ this.frame = DMContexts.getAncestorOfType(parent, StackFrameDMC.class);
+ engine = new ASTEvaluationEngine(getEDCServicesTracker(), frame, frame.getTypeEngine());
+ }
+
+ public BaseEDCExpressionDMC(IDMContext parent, String expression) {
+ this(parent, expression, expression);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.DMContext#toString()
+ */
+ @Override
+ public String toString() {
+ return getExpression();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getFrame()
+ */
+ public IFrameDMContext getFrame() {
+ return frame;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getExpression()
+ */
+ public String getExpression() {
+ return expression;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#evaluateExpression()
+ */
+ public synchronized void evaluateExpression() {
+ if (value != null || valueError != null)
+ return;
+
+ String expression = getExpression();
+
+ if (parsedExpression == null) {
+ try {
+ parsedExpression = engine.getCompiledExpression(expression);
+ } catch (CoreException e) {
+ value = null;
+ valueError = e.getStatus();
+ valueLocation = null;
+ valueType = null;
+ return;
+ }
+ }
+
+ if (parsedExpression.getInstructions().length == 0) {
+ value = null;
+ valueError = new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID,
+ EDCServicesMessages.Expressions_SyntaxError);
+ valueLocation = null;
+ valueType = null;
+ return;
+ }
+
+ Interpreter interpreter;
+ try {
+ interpreter = engine.evaluateCompiledExpression(parsedExpression);
+ } catch (CoreException e) {
+ value = null;
+ valueError = e.getStatus();
+ valueLocation = null;
+ valueType = null;
+ return;
+ }
+
+ OperandValue variableValue = interpreter.getResult();
+ if (variableValue == null) {
+ value = null;
+ valueError = null;
+ valueLocation = null;
+ valueType = null;
+ return;
+ }
+
+ valueLocation = variableValue.getValueLocation();
+ valueType = variableValue.getValueType();
+ try {
+ value = variableValue.getValue();
+ valueString = variableValue.getStringValue();
+ } catch (CoreException e1) {
+ value = null;
+ valueError = e1.getStatus();
+ return;
+ }
+
+ // for a structured type or array, return the location and note
+ // that it has children
+ if (valueType instanceof IAggregate && valueLocation != null) {
+ // TODO
+ try {
+ value = variableValue.getValueLocationAddress();
+ } catch (CoreException e) {
+ value = null;
+ valueError = e.getStatus();
+ }
+ if (!(value instanceof IInvalidVariableLocation))
+ hasChildren = true;
+ }
+
+ // if the location evaluates to NotLive, the types and values do
+ // not matter
+ if (valueLocation instanceof IInvalidVariableLocation) {
+ value = null;
+ valueError = new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID,
+ ((IInvalidVariableLocation) valueLocation).getMessage());
+ valueLocation = null; //$NON-NLS-1$
+ return;
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getFormattedValue(org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext)
+ */
+ public FormattedValueDMData getFormattedValue(FormattedValueDMContext dmc) {
+ if (EDCTrace.VARIABLE_VALUE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(dmc)); }
+ evaluateExpression();
+ String result = ""; //$NON-NLS-1$
+
+ if (valueError != null) {
+ result = valueError.getMessage();
+ } else if (value != null) {
+ result = value.toString();
+
+ IType unqualifiedType = TypeUtils.getUnRefStrippedType(valueType);
+
+ String temp = null;
+ String formatID = dmc.getFormatID();
+
+ // the non-natural formats have expected representations in other
+ // parts of DSF, so be strict about what we return
+ if (formatID.equals(IFormattedValues.HEX_FORMAT)) {
+ temp = NumberFormatUtils.toHexString(value);
+ } else if (formatID.equals(IFormattedValues.OCTAL_FORMAT)) {
+ temp = NumberFormatUtils.toOctalString(value);
+ } else if (formatID.equals(IFormattedValues.BINARY_FORMAT)) {
+ temp = NumberFormatUtils.asBinary(value);
+ } else if (formatID.equals(IFormattedValues.NATURAL_FORMAT)) {
+ // convert non-integer types to original representation
+ if (unqualifiedType instanceof ICPPBasicType) {
+ ICPPBasicType basicType = (ICPPBasicType) unqualifiedType;
+ switch (basicType.getBaseType()) {
+ case ICPPBasicType.t_char:
+ temp = NumberFormatUtils.toCharString(value, valueType);
+ break;
+ case ICPPBasicType.t_wchar_t:
+ temp = NumberFormatUtils.toCharString(value, valueType);
+ break;
+ case ICPPBasicType.t_bool:
+ temp = Boolean.toString(value.longValue() != 0);
+ break;
+ default:
+ // account for other debug formats
+ if (basicType.getName().equals("wchar_t")) { //$NON-NLS-1$
+ temp = NumberFormatUtils.toCharString(value, valueType);
+ }
+ break;
+ }
+ } else if (unqualifiedType instanceof IAggregate || unqualifiedType instanceof IPointerType) {
+ // show addresses for aggregates and pointers as hex in natural format
+ temp = NumberFormatUtils.toHexString(value);
+ }
+
+ // TODO: add type suffix if the value cannot fit in
+ // the ordinary range of the base type.
+ // E.g., for an unsigned int, 0xFFFFFFFF should usually be 0xFFFFFFFFU,
+ // and for a long double, 1.E1000 should be 1.E1000L.
+ /*
+ // apply required integer and float suffixes
+ IType unqualifiedType = TypeUtils.getStrippedType(valueType);
+ if (unqualifiedType instanceof ICPPBasicType) {
+ ICPPBasicType basicType = (ICPPBasicType) unqualifiedType;
+
+ if (basicType.getBaseType() == ICPPBasicType.t_float) {
+ //result += "F"; // no
+ } else if (basicType.getBaseType() == ICPPBasicType.t_double) {
+ if (basicType.isLong() AND actual value does not fit in a double)
+ result += "L";
+ } else if (basicType.getBaseType() == ICPPBasicType.t_int) {
+ if (basicType.isUnsigned() AND actual value does not fit in a signed int)
+ result += "U";
+ if (basicType.isLongLong() AND actual value does not fit in a signed int)
+ result += "LL";
+ else if (basicType.isLong() AND actual value does not fit in a signed int)
+ result += "L";
+ }
+ }
+ */
+
+ // for an enumerator, return the name, if any
+ if (unqualifiedType instanceof IEnumeration) {
+ long enumeratorValue = value.longValue();
+
+ IEnumerator enumerator = ((IEnumeration) unqualifiedType).getEnumeratorByValue(enumeratorValue);
+ if (enumerator != null) {
+ if (temp == null)
+ temp = result;
+
+ temp = enumerator.getName() + " [" + temp + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+ if (temp != null)
+ result = temp;
+
+ // otherwise, leave value as is
+
+
+ }
+ if (EDCTrace.VARIABLE_VALUE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(result)); }
+ return new FormattedValueDMData(result);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getValueLocation()
+ */
+ public IVariableLocation getValueLocation() {
+ evaluateExpression();
+ return getEvaluatedLocation();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.IEDCExpression#getEvaluationError()
+ */
+ public IStatus getEvaluationError() {
+ return valueError;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getEvaluatedValue()
+ */
+ public Number getEvaluatedValue() {
+ return value;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.IEDCExpression#getEvaluatedValueString()
+ */
+ public String getEvaluatedValueString() {
+ if (valueError != null)
+ return valueError.getMessage();
+
+ if (valueString != null)
+ return valueString;
+
+ valueString = value != null ? value.toString() : ""; //$NON-NLS-1$
+ return valueString;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.IEDCExpression#setEvaluatedValueString(java.lang.String)
+ */
+ public void setEvaluatedValueString(String string) {
+ this.valueString = string;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#setEvaluatedValue(java.lang.Object)
+ */
+ public void setEvaluatedValue(Number value) {
+ this.value = value;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getEvaluatedLocation()
+ */
+ public IVariableLocation getEvaluatedLocation() {
+ return valueLocation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getEvaluatedType()
+ */
+ public IType getEvaluatedType() {
+ return valueType;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getTypeName()
+ */
+ public String getTypeName() {
+ evaluateExpression();
+ if (valueType == null)
+ if (valueError != null)
+ return ""; //$NON-NLS-1$
+ else
+ return ASTEvaluationEngine.UNKNOWN_TYPE;
+ return engine.getTypeEngine().getTypeName(valueType);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#hasChildren()
+ */
+ public boolean hasChildren() {
+ return this.hasChildren;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getService()
+ */
+ public Expressions getExpressionsService() {
+ return Expressions.this;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.IEDCExpression#getExecutor()
+ */
+ public Executor getExecutor() {
+ return getSession().getExecutor();
+ }
+
+ }
+
+ /** A basic expression. */
+ private class ExpressionDMC extends BaseEDCExpressionDMC {
+
+ public ExpressionDMC(IDMContext parent, String expression) {
+ super(parent, expression);
+ }
+
+
+ public ExpressionDMC(IDMContext parent, String expression, String name) {
+ super(parent, expression, name);
+ }
+
+ /**
+ * There is no casting on a vanilla expression.
+ * @return <code>null</code>
+ */
+ public CastInfo getCastInfo() {
+ return null;
+ }
+
+
+ }
+
+ /** A casted or array-displayed expression. */
+ private class CastedExpressionDMC extends BaseEDCExpressionDMC implements ICastedExpressionDMContext {
+
+ private final CastInfo castInfo;
+ /** if non-null, interpret result as this type rather than the raw expression's type */
+ private IType castType = null;
+ private IStatus castError;
+
+ public CastedExpressionDMC(IEDCExpression exprDMC, String expression, String name, CastInfo castInfo) {
+ super(exprDMC, name);
+ this.castInfo = castInfo;
+
+ String castType = castInfo.getTypeString();
+
+ String castExpression = expression;
+
+ // If changing type, assume it's reinterpret_cast<>.
+ // Once we support RTTI, this should be dynamic_cast<> when casting
+ // class pointers to class pointers.
+ if (castType != null) {
+ if (castInfo.getArrayCount() > 0) {
+ castType += "[]"; //$NON-NLS-1$
+ // Force non-pointer expressions to be pointers.
+ exprDMC.evaluateExpression();
+ IType exprType = TypeUtils.getStrippedType(exprDMC.getEvaluatedType());
+ if (exprType != null) {
+ if (!(exprType instanceof IPointerType || exprType instanceof IArrayType)) {
+ expression = "&" + expression; //$NON-NLS-1$
+ }
+ }
+ }
+ castExpression = "reinterpret_cast<" + castType +">(" + expression + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ } else if (castInfo.getArrayCount() > 0) {
+ // For arrays, be sure the OperatorSubscript accepts the base type.
+ // Force non-pointer expressions to be pointers.
+ exprDMC.evaluateExpression();
+ IType exprType = TypeUtils.getStrippedType(exprDMC.getEvaluatedType());
+ if (exprType != null) {
+ if (!(exprType instanceof IPointerType || exprType instanceof IArrayType)) {
+ // cast to pointer if not already one (cast to array is not valid C/C++ but we support it)
+ castExpression = "(" + exprDMC.getTypeName() + "[])&" + expression; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+ this.expression = castExpression;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#evaluateExpression()
+ */
+ public void evaluateExpression() {
+ if (castError != null) {
+ return;
+ }
+
+ super.evaluateExpression();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCExpression#getEvaluatedType()
+ */
+ public IType getEvaluatedType() {
+ if (castType != null)
+ return castType;
+ return super.getEvaluatedType();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext#getCastInfo()
+ */
+ public CastInfo getCastInfo() {
+ return castInfo;
+ }
+ }
+
+ public class ExpressionData implements IExpressionDMData {
+
+ private final IEDCExpression dmc;
+ private String typeName = "";
+
+ public ExpressionData(IEDCExpression dmc) {
+ this.dmc = dmc;
+ if (dmc != null)
+ this.typeName = dmc.getTypeName();
+ }
+
+ public BasicType getBasicType() {
+ BasicType basicType = BasicType.unknown;
+ if (dmc == null)
+ return basicType;
+
+ IType type = dmc.getEvaluatedType();
+ type = TypeUtils.getStrippedType(type);
+ if (type instanceof IArrayType) {
+ basicType = BasicType.array;
+ }
+ else if (type instanceof IBasicType) {
+ basicType = BasicType.basic;
+ }
+ else if (type instanceof ICompositeType) {
+ basicType = BasicType.composite;
+ }
+ else if (type instanceof IEnumeration) {
+ basicType = BasicType.enumeration;
+ }
+ else if (type instanceof IPointerType) {
+ basicType = BasicType.pointer;
+ }
+ else if (type instanceof ISubroutineType) {
+ basicType = BasicType.function;
+ }
+ return basicType;
+ }
+
+ public String getEncoding() {
+ return null;
+ }
+
+ public Map<String, Integer> getEnumerations() {
+ return null;
+ }
+
+ public String getName() {
+ if (dmc != null)
+ return dmc.getName();
+ else
+ return ""; //$NON-NLS-1$
+ }
+
+ public IRegisterDMContext getRegister() {
+ return null;
+ }
+
+ public String getTypeId() {
+ return TYPEID_INTEGER;
+ }
+
+ public String getTypeName() {
+ return typeName;
+ }
+
+ }
+
+ 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$
+ }
+
+ public String getExpression() {
+ return expression;
+ }
+ }
+
+ public class ExpressionDMAddress implements IExpressionDMLocation {
+
+ private final IVariableLocation valueLocation;
+
+ public ExpressionDMAddress(IExpressionDMContext exprContext) {
+ if (exprContext instanceof IEDCExpression)
+ valueLocation = ((IEDCExpression) exprContext).getValueLocation();
+ else
+ valueLocation = null;
+ }
+
+ public IAddress getAddress() {
+ if (valueLocation != null) {
+ IAddress address = valueLocation.getAddress();
+ if (address != null)
+ return address;
+ }
+ return new Addr64(BigInteger.ZERO);
+ }
+
+ public int getSize() {
+ return 4;
+ }
+
+ public String getLocation() {
+ if (valueLocation instanceof IInvalidVariableLocation) {
+ return ((IInvalidVariableLocation)valueLocation).getMessage();
+ }
+ if (valueLocation == null)
+ return ""; //$NON-NLS-1$
+ return valueLocation.getLocationName();
+ }
+
+ }
+
+ public Expressions(DsfSession session) {
+ super(session, new String[] { IExpressions.class.getName(), Expressions.class.getName(), IExpressions2.class.getName() });
+ }
+
+ public boolean canWriteExpression(IEDCExpression expressionDMC) {
+ EDCLaunch launch = EDCLaunch.getLaunchForSession(getSession().getId());
+ if (launch.isSnapshotLaunch())
+ return false;
+ IVariableValueConverter converter = getCustomValueConverter(expressionDMC);
+ if (converter != null)
+ return converter.canEditValue();
+
+ return !isComposite(expressionDMC);
+ }
+
+ public void canWriteExpression(IExpressionDMContext exprContext, DataRequestMonitor<Boolean> rm) {
+ IEDCExpression expressionDMC = (IEDCExpression) exprContext;
+ rm.setData(canWriteExpression(expressionDMC));
+ rm.done();
+ }
+
+ private boolean isComposite(IEDCExpression expressionDMC) {
+ IType exprType = TypeUtils.getStrippedType(expressionDMC.getEvaluatedType());
+ return exprType instanceof ICompositeType;
+ }
+
+ public IExpressionDMContext createExpression(IDMContext context, String expression) {
+ StackFrameDMC frameDmc = DMContexts.getAncestorOfType(context, StackFrameDMC.class);
+
+ if (frameDmc != null) {
+ return new ExpressionDMC(frameDmc, expression);
+ }
+ return new InvalidContextExpressionDMC(getSession().getId(), expression, context);
+ }
+
+ class CastInfoCachedData {
+
+ private CastInfo info;
+
+ private IType type;
+ private IStatus error;
+ private StackFrameDMC frameDmc;
+
+ public CastInfoCachedData(ExpressionDMC exprDMC, CastInfo info) {
+ this.info = info;
+ this.frameDmc = DMContexts.getAncestorOfType(exprDMC, StackFrameDMC.class);
+ }
+
+ public String getTypeString() {
+ return info.getTypeString();
+ }
+
+ public int getArrayStartIndex() {
+ return info.getArrayStartIndex();
+ }
+
+ public int getArrayCount() {
+ return info.getArrayCount();
+ }
+
+ /**
+ * Get the compiled type
+ * @return the type
+ */
+ public IType getType() {
+ if (info.getTypeString() == null)
+ return null;
+
+ if (type == null && error == null) {
+ if (frameDmc != null) {
+ ASTEvaluationEngine engine = new ASTEvaluationEngine(getEDCServicesTracker(), frameDmc, frameDmc.getTypeEngine());
+ try {
+ IASTTypeId typeId = engine.getCompiledType(info.getTypeString());
+ type = engine.getTypeEngine().getTypeForTypeId(typeId);
+ } catch (CoreException e) {
+ error = e.getStatus();
+ }
+ } else {
+ error = EDCDebugger.dsfRequestFailedStatus(EDCServicesMessages.Expressions_CannotCastOutsideFrame, null);
+ }
+ }
+ return type;
+ }
+
+ /**
+ * @return the error
+ */
+ public IStatus getError() {
+ if (type == null && error == null) {
+ getType();
+ }
+ return error;
+ }
+ }
+
+ /* (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)
+ */
+ public ICastedExpressionDMContext createCastedExpression(IExpressionDMContext exprDMC,
+ CastInfo castInfo) {
+
+ // then apply the casting stuff
+ if (exprDMC instanceof IEDCExpression) {
+ CastedExpressionDMC castedDMC = new CastedExpressionDMC((IEDCExpression) exprDMC,
+ exprDMC.getExpression(), ((IEDCExpression) exprDMC).getName(), castInfo);
+ return castedDMC;
+ } else {
+ assert false;
+ return null;
+ }
+ }
+
+ /*
+ public void createCastedExpression(IDMContext context, String expression,
+ ICastedExpressionDMContext castDMC, IArrayCastedExpressionDMContext arrayCastDMC,
+ DataRequestMonitor<IExpressionDMContext> rm) {
+
+ // create an ordinary expression...
+ IExpressionDMContext exprDMC = createExpression(context, expression);
+
+ // then apply the casting stuff
+ if (exprDMC instanceof ExpressionDMC
+ && (castDMC == null || castDMC instanceof CastedExpressionDMContext)
+ && (arrayCastDMC == null || arrayCastDMC instanceof ArrayCastedExpressionDMContext)) {
+ ExpressionDMC expressionDMC = ((ExpressionDMC) exprDMC);
+ if (castDMC != null)
+ expressionDMC.setCastToType((CastedExpressionDMContext) castDMC);
+ if (arrayCastDMC != null)
+ expressionDMC.setArrayCast((ArrayCastedExpressionDMContext) arrayCastDMC);
+ rm.setData(expressionDMC);
+ rm.done();
+ } else {
+ assert false;
+ rm.setStatus(EDCDebugger.dsfRequestFailedStatus("unexpected cast information", null));
+ rm.done();
+ }
+ }
+ */
+
+ public void getBaseExpressions(IExpressionDMContext exprContext, DataRequestMonitor<IExpressionDMContext[]> rm) {
+ rm.setData(new IEDCExpression[0]);
+ rm.done();
+ }
+
+ public void getExpressionAddressData(final IExpressionDMContext exprContext, final DataRequestMonitor<IExpressionDMAddress> rm) {
+ asyncExec(new Runnable() {
+ public void run() {
+ if (exprContext instanceof IEDCExpression)
+ rm.setData(new ExpressionDMAddress(exprContext));
+ else
+ rm.setData(new ExpressionDMAddress(null));
+ rm.done();
+ }
+ }, rm);
+ }
+
+ public void getExpressionData(final IExpressionDMContext exprContext, final DataRequestMonitor<IExpressionDMData> rm) {
+ asyncExec(new Runnable() {
+ public void run() {
+ if (exprContext instanceof IEDCExpression)
+ rm.setData(new ExpressionData((IEDCExpression) exprContext));
+ else
+ rm.setData(new ExpressionData(null));
+ rm.done();
+ }
+ }, rm);
+ }
+
+ public void getSubExpressionCount(final IExpressionDMContext exprContext, final DataRequestMonitor<Integer> rm) {
+ asyncExec(new Runnable() {
+ public void run() {
+ // handle array casts
+ CastInfo cast = null;
+ if (exprContext instanceof IEDCExpression && (cast = ((IEDCExpression) exprContext).getCastInfo()) != null) {
+ if (cast.getArrayCount() > 0) {
+ if (((IEDCExpression)exprContext).getEvaluationError() != null) {
+ rm.setData(0);
+ rm.done();
+ return;
+ }
+ rm.setData(cast.getArrayCount());
+ rm.done();
+ return;
+ }
+ }
+
+ if (!(exprContext instanceof IEDCExpression)) {
+ rm.setData(0);
+ rm.done();
+ return;
+ }
+
+ IEDCExpression expr = (IEDCExpression) exprContext;
+
+ // if expression has no evaluated value, then it has not yet been evaluated
+ if (expr.getEvaluatedValue() == null && expr.getEvaluatedValueString() != null) {
+ expr.evaluateExpression();
+ }
+
+ IType exprType = TypeUtils.getStrippedType(expr.getEvaluatedType());
+
+ // to expand it, it must either be a pointer, a reference to an aggregate,
+ // or an aggregate
+ boolean pointerType = exprType instanceof IPointerType;
+ boolean referenceType = exprType instanceof IReferenceType;
+ IType pointedTo = null;
+ if (referenceType)
+ pointedTo = TypeUtils.getStrippedType(((IReferenceType) exprType).getType());
+
+ if (!(exprType instanceof IAggregate) && !pointerType &&
+ !(referenceType && (pointedTo instanceof IAggregate || pointedTo instanceof IPointerType))) {
+ rm.setData(0);
+ rm.done();
+ return;
+ }
+
+ ITypeContentProvider customProvider =
+ FormatExtensionManager.instance().getTypeContentProvider(exprType);
+ if (customProvider != null) {
+ try {
+ rm.setData(customProvider.getChildCount(expr));
+ rm.done();
+ return;
+ } catch (Throwable e) {
+ }
+ }
+
+ // TODO: maybe cache these subexpressions; they are just requested again in #getSubExpressions()
+ getSubExpressions(exprContext, new DataRequestMonitor<IExpressions.IExpressionDMContext[]>(
+ getExecutor(), rm) {
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.RequestMonitor#handleSuccess()
+ */
+ @Override
+ protected void handleSuccess() {
+ rm.setData(getData().length);
+ rm.done();
+ }
+ });
+ }
+ }, rm);
+ }
+
+ public void getSubExpressions(final IExpressionDMContext exprContext, final DataRequestMonitor<IExpressionDMContext[]> rm) {
+ asyncExec(new Runnable() {
+ public void run() {
+ if (!(exprContext instanceof IEDCExpression) || ((IEDCExpression) exprContext).getFrame() == null) {
+ rm.setData(new IEDCExpression[0]);
+ rm.done();
+ return;
+ }
+
+ IEDCExpression expr = (IEDCExpression) exprContext;
+
+ // if expression has no evaluated value, then it has not yet been evaluated
+ if (expr.getEvaluatedValue() == null && expr.getEvaluatedValueString() != null) {
+ expr.evaluateExpression();
+ }
+
+ StackFrameDMC frame = (StackFrameDMC) expr.getFrame();
+ IType exprType = TypeUtils.getStrippedType(expr.getEvaluatedType());
+
+ // if casted to an array, convert thusly
+ CastInfo castInfo = expr.getCastInfo();
+ if (castInfo != null && castInfo.getArrayCount() > 0) {
+ try {
+ exprType = frame.getTypeEngine().convertToArrayType(exprType, castInfo.getArrayCount());
+ } catch (CoreException e) {
+ rm.setStatus(e.getStatus());
+ rm.done();
+ return;
+ }
+ }
+
+
+ // to expand it, it must either be a pointer, a reference to an aggregate,
+ // or an aggregate
+ boolean pointerType = exprType instanceof IPointerType;
+ boolean referenceType = exprType instanceof IReferenceType;
+ IType pointedTo = null;
+ if (referenceType) {
+ pointedTo = TypeUtils.getStrippedType(((IReferenceType) exprType).getType());
+ exprType = pointedTo;
+ }
+
+ if (!(exprType instanceof IAggregate) && !pointerType &&
+ !(referenceType && (pointedTo instanceof IAggregate || pointedTo instanceof IPointerType))) {
+ rm.setData(new IEDCExpression[0]);
+ rm.done();
+ return;
+ }
+
+ ITypeContentProvider customProvider =
+ FormatExtensionManager.instance().getTypeContentProvider(exprType);
+ if (customProvider != null) {
+ getSubExpressions(expr, frame, exprType, customProvider, rm);
+ }
+ else
+ getSubExpressions(expr, rm);
+ }
+ }, rm);
+ }
+
+ public void getSubExpressions(final IExpressionDMContext exprContext, final int startIndex_, final int length_,
+ final DataRequestMonitor<IExpressionDMContext[]> rm) {
+ asyncExec(new Runnable() {
+ public void run() {
+ getSubExpressions(exprContext, new DataRequestMonitor<IExpressionDMContext[]>(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ IExpressionDMContext[] allExprs = getData();
+ if (startIndex_ == 0 && length_ >= allExprs.length) {
+ rm.setData(allExprs);
+ rm.done();
+ } else {
+ int startIndex = startIndex_, length = length_;
+ if (startIndex > allExprs.length) {
+ startIndex = allExprs.length;
+ length = 0;
+ } else if (startIndex + length > allExprs.length) {
+ length = allExprs.length - startIndex;
+ if (length < 0)
+ length = 0;
+ }
+
+ IExpressionDMContext[] result = new IExpressionDMContext[length];
+ System.arraycopy(allExprs, startIndex, result, 0, length);
+ rm.setData(result);
+ rm.done();
+ }
+ }
+ });
+ }
+ }, rm);
+ }
+
+ private void getSubExpressions(final IEDCExpression expr, final StackFrameDMC frame,
+ final IType exprType, final ITypeContentProvider customProvider,
+ final DataRequestMonitor<IExpressionDMContext[]> rm) {
+
+ List<IExpressionDMContext> children = new ArrayList<IExpressionDMContext>();
+ Iterator<IExpressionDMContext> childIterator;
+ try {
+ childIterator = customProvider.getChildIterator(expr);
+ while (childIterator.hasNext() && !rm.isCanceled()) {
+ children.add(childIterator.next());
+ }
+ rm.setData(children.toArray(new IExpressionDMContext[children.size()]));
+ rm.done();
+ } catch (CoreException e) {
+ // Checked exception. But we don't want to pass the error up as it
+ // would make the variable (say, a structure) not expandable on UI.
+ // Just resort to the normal formatting.
+ getSubExpressions(expr, rm);
+ } catch (Throwable e) {
+ // unexpected error. log it.
+ EDCDebugger.getMessageLogger().logError(
+ EDCServicesMessages.Expressions_ErrorInVariableFormatter + customProvider.getClass().getName(), e);
+
+ // default to normal formatting
+ getSubExpressions(expr, rm);
+ }
+ }
+
+ private void getSubExpressions(final IEDCExpression expr,
+ final DataRequestMonitor<IExpressionDMContext[]> rm) {
+ rm.setData(getLogicalSubExpressions(expr));
+ rm.done();
+ }
+
+ /**
+ * Get the logical subexpressions for the given expression context. We want
+ * to skip unnecessary nodes, e.g., a pointer to a composite, and directly
+ * show the object contents.
+ * @param expr the expression from which to start
+ * @return array of children
+ */
+ public IEDCExpression[] getLogicalSubExpressions(IEDCExpression expr) {
+
+ IType exprType = TypeUtils.getUnRefStrippedType(expr.getEvaluatedType());
+
+ // cast to array?
+ CastInfo castInfo = expr.getCastInfo();
+ if (castInfo != null && castInfo.getArrayCount() > 0) {
+
+ String exprName = expr.getExpression();
+
+ // in case of casts, need to resolve that before dereferencing, so be safe
+ if (exprName.contains("(")) //$NON-NLS-1$
+ exprName = '(' + exprName + ')';
+
+ long lowerBound = castInfo.getArrayStartIndex();
+ long count = castInfo.getArrayCount();
+
+ List<IEDCExpression> arrayChildren = new ArrayList<IEDCExpression>();
+ for (int i = 0; i < count; i++) {
+ String arrayElement = "[" + (i + lowerBound) + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ IEDCExpression newExpr = new ExpressionDMC(expr.getFrame(), (exprName + arrayElement),
+ expr.getName() + arrayElement);
+ IEDCExpression exprChild = newExpr; //$NON-NLS-1$ //$NON-NLS-2$
+ if (exprChild != null) {
+ arrayChildren.add(exprChild);
+ }
+ }
+
+ return arrayChildren.toArray(new IEDCExpression[arrayChildren.size()]);
+ }
+
+ if (exprType instanceof IPointerType) {
+ // automatically dereference a pointer
+ String exprName = expr.getExpression();
+ IType typePointedTo = TypeUtils.getStrippedType(exprType.getType());
+
+ // If expression name already starts with "&" (e.g. "&struct"), indirect it first
+ boolean indirected = false;
+
+ IEDCExpression exprChild;
+
+ if (exprName.startsWith("&")) { //$NON-NLS-1$
+ exprName = exprName.substring(1);
+ IEDCExpression newExpr = new ExpressionDMC(expr.getFrame(), exprName);
+ exprChild = newExpr;
+ indirected = true;
+ }
+ else {
+ // avoid dereferencing void pointer
+ if (typePointedTo instanceof ICPPBasicType
+ && ((ICPPBasicType) typePointedTo).getBaseType() == ICPPBasicType.t_void) {
+ return new IEDCExpression[0];
+ }
+
+ // do not dereference null either
+ if (expr.getEvaluatedValue() != null && expr.getEvaluatedValue().intValue() == 0)
+ return new IEDCExpression[0];
+ IEDCExpression newExpr = new ExpressionDMC(expr.getFrame(), ("*" + exprName), "*" + expr.getName()); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // a pointer type has one child
+ exprChild = newExpr; //$NON-NLS-1$
+ }
+
+ return doGetLogicalSubExpressions(exprChild, typePointedTo, indirected);
+ }
+ else if (exprType instanceof IReferenceType) {
+ // and bypass a reference
+
+ IType typePointedTo = TypeUtils.getStrippedType(exprType.getType());
+ return doGetLogicalSubExpressions(expr, typePointedTo, false);
+ }else {
+ // normal aggregate, just do it
+ return doGetLogicalSubExpressions(expr, exprType, false);
+ }
+
+ }
+
+ /**
+ * Get the logical subexpressions for the given expression context and string
+ * @param expr the expression from which to start
+ * @param exprType the type in which to consider the expression
+ * @param indirected if true, the expression was already indirected, as opposed to what the expression says
+ * @return
+ */
+ private IEDCExpression[] doGetLogicalSubExpressions(IEDCExpression expr, IType exprType, boolean indirected) {
+ ArrayList<IEDCExpression> exprList = new ArrayList<IEDCExpression>();
+ IEDCExpression exprChild;
+
+ String expression = expr.getExpression();
+
+ // in case of casts, need to resolve that before dereferencing, so be safe
+ if (expression.contains("(")) //$NON-NLS-1$
+ expression = '(' + expression + ')';
+
+ /*
+ // cast to array?
+ CastInfo castInfo = expr.getCastInfo();
+ if (castInfo != null && castInfo.getArrayCount() > 0) {
+ long lowerBound = castInfo.getArrayStartIndex();
+ long count = castInfo.getArrayCount();
+ for (int i = 0; i < count; i++) {
+ exprChild = createDerivedExpression(expr, exprName + "[" + (i + lowerBound) + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (exprChild != null) {
+ exprList.add(exprChild);
+ }
+ }
+
+ }
+ else*/ if (exprType instanceof ICompositeType) {
+ // an artifact of following a pointer to a structure is that the
+ // name starts with '*'
+ if (expression.startsWith("*")) { //$NON-NLS-1$
+ if (expression.startsWith("**"))
+ expression = "(" + expression.substring(1) + ")->"; //$NON-NLS-1$
+ else
+ expression = expression.substring(1) + "->"; //$NON-NLS-1$
+ } else {
+ expression = expression + '.'; //$NON-NLS-1$
+ }
+
+ // for each field, evaluate an expression, then shorten the name
+ ICompositeType compositeType = (ICompositeType) exprType;
+
+ for (IField field : compositeType.getFields()) {
+ String fieldName = field.getName();
+ if (fieldName.length() == 0) {
+ // This makes an invalid expression
+ // The debug info provider should have filtered out or renamed such fields
+ assert false;
+ continue;
+ }
+ exprChild = new ExpressionDMC(expr.getFrame(), expression + fieldName, fieldName);
+ if (exprChild != null) {
+ exprList.add(exprChild);
+ }
+ }
+
+ for (IInheritance inherited : compositeType.getInheritances()) {
+ String inheritedName = inherited.getName();
+ if (inheritedName.length() == 0) {
+ // This makes an invalid expression
+ // The debug info provider should have filtered out or renamed such fields
+ assert false; // couldn't this be the case for an anonymous member, like a union?
+ } else if (!inheritedName.contains("<")) {
+ exprChild = new ExpressionDMC(expr.getFrame(), expression + inheritedName, inheritedName);
+ if (exprChild != null) {
+ exprList.add(exprChild);
+ }
+ } else {
+ IType inheritedType = inherited.getType();
+ if (inheritedType instanceof ICompositeType) {
+ for (IField field : ((ICompositeType)inheritedType).getFields()) {
+ String fieldName = field.getName();
+ if (fieldName.length() == 0) {
+ // This makes an invalid expression
+ // The debug info provider should have filtered out or renamed such fields
+ assert false;
+ continue;
+ }
+ exprChild = new ExpressionDMC(expr.getFrame(), expression + fieldName, fieldName);
+ if (exprChild != null) {
+ exprList.add(exprChild);
+ }
+ }
+ }
+ }
+ }
+
+ }
+ else if (exprType instanceof IArrayType) {
+ IArrayType arrayType = (IArrayType) exprType;
+
+ if (arrayType.getBoundsCount() > 0) {
+ long lowerBound = expr.getCastInfo() != null && expr.getCastInfo().getArrayCount() > 0
+ ? expr.getCastInfo().getArrayStartIndex() : 0;
+ long upperBound = arrayType.getBound(0).getBoundCount();
+ if (upperBound == 0)
+ upperBound = 1;
+ for (int i = 0; i < upperBound; i++) {
+ String arrayElementName = "[" + (i + lowerBound) + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ IEDCExpression newExpr = new ExpressionDMC(expr.getFrame(), expression + arrayElementName,
+ expr.getName() + arrayElementName);
+ exprChild = newExpr;
+ if (exprChild != null) {
+ exprList.add(exprChild);
+ }
+ }
+ }
+ }
+ else if (exprType instanceof IArrayDimensionType) {
+ IArrayDimensionType arrayDimensionType = (IArrayDimensionType) exprType;
+ IArrayType arrayType = arrayDimensionType.getArrayType();
+
+ if (arrayType.getBoundsCount() > arrayDimensionType.getDimensionCount()) {
+ long lowerBound = expr.getCastInfo() != null && expr.getCastInfo().getArrayCount() > 0
+ ? expr.getCastInfo().getArrayStartIndex() : 0;
+ long upperBound = arrayType.getBound(arrayDimensionType.getDimensionCount()).getBoundCount();
+ for (int i = 0; i < upperBound; i++) {
+ String arrayElement = "[" + (i + lowerBound) + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ IEDCExpression newExpr = new ExpressionDMC(expr.getFrame(), expression + arrayElement,
+ expr.getName() + arrayElement);
+ exprChild = newExpr;
+ if (exprChild != null) {
+ exprList.add(exprChild);
+ }
+ }
+ }
+ }
+ else {
+ // nothing interesting
+ exprList.add(expr);
+ }
+
+ return exprList.toArray(new IEDCExpression[exprList.size()]);
+ }
+
+ @Immutable
+ private static class ExpressionChangedDMEvent extends AbstractDMEvent<IExpressionDMContext> implements IExpressionChangedDMEvent {
+ ExpressionChangedDMEvent(IExpressionDMContext expression) {
+ super(expression);
+ }
+ }
+
+ public void writeExpression(final IExpressionDMContext exprContext, final String expressionValue, final String formatId, final RequestMonitor rm) {
+
+ asyncExec(new Runnable() {
+ public void run() {
+ IEDCExpression expressionDMC = (IEDCExpression) exprContext;
+ if (isComposite(expressionDMC)) {
+ rm.setStatus(EDCDebugger.dsfRequestFailedStatus(EDCServicesMessages.Expressions_CannotModifyCompositeValue, null));
+ rm.done();
+ return;
+ }
+
+ IType exprType = TypeUtils.getStrippedType(expressionDMC.getEvaluatedType());
+
+ // first try to get value by format as BigInteger
+ Number number = NumberFormatUtils.parseIntegerByFormat(expressionValue, formatId);
+ if (number == null) {
+ IEDCExpression temp = (IEDCExpression) createExpression(expressionDMC.getFrame(), expressionValue);
+ temp.evaluateExpression();
+ number = temp.getEvaluatedValue();
+
+ if (number == null) {
+ rm.setStatus(EDCDebugger.dsfRequestFailedStatus(EDCServicesMessages.Expressions_CannotParseExpression, null));
+ rm.done();
+ return;
+ }
+ }
+
+ BigInteger value = null;
+ try {
+ value = MemoryUtils.convertValueToMemory(exprType, number);
+ } catch (CoreException e) {
+ rm.setStatus(e.getStatus());
+ rm.done();
+ return;
+ }
+
+ IVariableLocation variableLocation = expressionDMC.getValueLocation();
+ if (variableLocation == null) {
+ rm.setStatus(EDCDebugger.dsfRequestFailedStatus(EDCServicesMessages.Expressions_ExpressionNoLocation, null));
+ rm.done();
+ return;
+ }
+
+ try {
+ variableLocation.writeValue(exprType.getByteSize(), value);
+ getSession().dispatchEvent(new ExpressionChangedDMEvent(exprContext), getProperties());
+ } catch (CoreException e) {
+ rm.setStatus(e.getStatus());
+ }
+
+ rm.done();
+ }
+ }, rm);
+
+ }
+
+ public void getAvailableFormats(IFormattedDataDMContext formattedDataContext, DataRequestMonitor<String[]> rm) {
+ rm.setData(new String[] { IFormattedValues.NATURAL_FORMAT, IFormattedValues.DECIMAL_FORMAT,
+ IFormattedValues.HEX_FORMAT, IFormattedValues.OCTAL_FORMAT, IFormattedValues.BINARY_FORMAT });
+ rm.done();
+ }
+
+ public void getFormattedExpressionValue(final FormattedValueDMContext formattedDataContext,
+ final DataRequestMonitor<FormattedValueDMData> rm) {
+ asyncExec(new Runnable() {
+ public void run() {
+ try {
+ rm.setData(getFormattedExpressionValue(formattedDataContext));
+ rm.done();
+ } catch (CoreException ce) {
+ rm.setStatus(ce.getStatus());
+ rm.done();
+ return;
+ }
+ }
+ }, rm);
+ }
+
+ public String getExpressionValueString(IExpressionDMContext expression, String format) throws CoreException {
+ FormattedValueDMContext formattedDataContext = getFormattedValueContext(expression, format);
+ FormattedValueDMData formattedValue = getFormattedExpressionValue(formattedDataContext);
+
+ return formattedValue != null ? formattedValue.getFormattedValue() : "";
+ }
+
+ public FormattedValueDMData getFormattedExpressionValue(FormattedValueDMContext formattedDataContext) throws CoreException {
+ IDMContext idmContext = formattedDataContext.getParents()[0];
+ FormattedValueDMData formattedValue = null;
+ IEDCExpression exprDMC = null;
+
+ if (idmContext instanceof IEDCExpression) {
+ exprDMC = (IEDCExpression) formattedDataContext.getParents()[0];
+
+ exprDMC.evaluateExpression();
+
+ if (exprDMC != null && exprDMC.getEvaluationError() != null) {
+ throw new CoreException(exprDMC.getEvaluationError());
+ }
+
+ formattedValue = exprDMC.getFormattedValue(formattedDataContext); // must call this to get type
+
+ if (formattedDataContext.getFormatID().equals(IFormattedValues.NATURAL_FORMAT))
+ {
+ IVariableValueConverter customConverter = getCustomValueConverter(exprDMC);
+ if (customConverter != null) {
+ FormattedValueDMData customFormattedValue = null;
+ try {
+ customFormattedValue = new FormattedValueDMData(customConverter.getValue(exprDMC));
+ formattedValue = customFormattedValue;
+ }
+ catch (Throwable t) {
+ // CoreExeception will just propagate out, so this is for
+ // other unexpected errors, usually bug in the formatter. Log it
+ // so that user will be able to see and report the bug.
+ // Meanwhile, default to normal formatting so that user won't see
+ // such error in Variable UI.
+ EDCDebugger.getMessageLogger().logError(
+ EDCServicesMessages.Expressions_ErrorInVariableFormatter + customConverter.getClass().getName(), t);
+ }
+ }
+ }
+ } else
+ formattedValue = new FormattedValueDMData(""); //$NON-NLS-1$
+
+ return formattedValue;
+ }
+
+ private IVariableValueConverter getCustomValueConverter(IEDCExpression exprDMC) {
+ IType exprType = TypeUtils.getUnRefStrippedType(exprDMC.getEvaluatedType());
+ return FormatExtensionManager.instance().getVariableValueConverter(exprType);
+ }
+
+ public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext formattedDataContext,
+ String formatId) {
+ return new FormattedValueDMContext(this, formattedDataContext, formatId);
+ }
+
+ public void getModelData(IDMContext context, DataRequestMonitor<?> rm) {
+ }
+
+ public String getExpressionValue(IExpressionDMContext expression)
+ {
+ final StringBuffer holder = new StringBuffer();
+ FormattedValueDMContext formattedValueContext = getFormattedValueContext(expression, IFormattedValues.NATURAL_FORMAT);
+ getFormattedExpressionValue(formattedValueContext, new DataRequestMonitor<FormattedValueDMData>(ImmediateExecutor.getInstance(), null) {
+ @Override
+ protected void handleSuccess() {
+ holder.append(this.getData().getFormattedValue());
+ }
+
+ @Override
+ protected void handleFailure() {
+ // RequestMonitor would by default log any error if it's not explicitly
+ // handled. But we don't want to log those expected errors (checked exceptions)
+ // in such case as creating snapshot. Hence this dummy handler...02/17/10
+
+ // DO nothing.
+ }
+
+
+ });
+ return holder.toString();
+ }
+
+ public void loadExpressionValues(IExpressionDMContext expression, int depth)
+ {
+ loadExpressionValues(expression, new Integer[] {depth});
+ }
+
+ private void loadExpressionValues(IExpressionDMContext expression, final Integer[] depth)
+ {
+ getExpressionValue(expression);
+ if (depth[0] > 0)
+ {
+ getSubExpressions(expression, new DataRequestMonitor<IExpressions.IExpressionDMContext[]>(ImmediateExecutor.getInstance(), null) {
+
+ @Override
+ protected void handleSuccess() {
+ depth[0] = depth[0] - 1;
+ IExpressions.IExpressionDMContext[] subExpressions = getData();
+ for (IExpressionDMContext iExpressionDMContext : subExpressions) {
+ loadExpressionValues(iExpressionDMContext, depth);
+ }
+ }});
+ }
+ }
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import org.eclipse.cdt.debug.edc.services.IEDCService;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+
+/**
+ * A dummy DSF service used for testing.
+ */
+public interface INoop extends IEDCService {
+ /** Simply sticks a boolean in the given RM */
+ void noop(IDMContext whatever, DataRequestMonitor<Boolean> rm);
+
+ /**
+ * Simply sticks a boolean in the given RM after 10 seconds have elapsed.
+ * The sleep is not done on the DSF executor thread, of course
+ */
+ void longNoop(IDMContext whatever, DataRequestMonitor<Boolean> rm);
+
+ /**
+ * This service simply loops for [duration] seconds, asking for the service
+ * tracker every second and trying to use it to get a service. It's used in
+ * a shutdown test to validate that threads in the EDC thread pool are given
+ * a chance to complete before the DSF session moves forward with its
+ * shutdown. If session shutdown didn't wait, then this service would end up
+ * encountering either a null pointer exception or an exception due to the
+ * attempted use of a disposed tracker.
+ *
+ * @param duration
+ * the number of seconds to loop
+ * @param rm
+ * not used but included for consistency
+ */
+ void longNoopUsingServiceTracker(int duration, RequestMonitor rm);
+}
\ No newline at end of file
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.EDCAddressRange;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider;
+import org.eclipse.cdt.debug.edc.symbols.IModuleLineEntryProvider;
+import org.eclipse.cdt.dsf.debug.service.IModules.AddressRange;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Utilities for mapping source to addresses and vice versa.
+ */
+public class LineEntryMapper {
+
+ private static final int toEOF = -1;
+
+ /**
+ * Get the link-time address ranges starting at the given source location.
+ * There may be more than one, e.g., in template expansions, static functions
+ * declared in headers, inlined functions, etc. Or, of course, someone may
+ * declare multiple functions on one line.
+ * @path sourceFile the absolute path to the source file
+ * @param line the line number (1-based)
+ * @return the unmodifiable list of link-time addresses which start at the given line, possibly empty,
+ * will be null if the source file is not used by this module.
+ */
+ public static Collection<AddressRange> getAddressRangesAtSource(
+ IModuleLineEntryProvider moduleLineEntryProvider,
+ IPath sourceFile,
+ int line) {
+ Collection<AddressRange> range = getAddressRangesAtSource(moduleLineEntryProvider, sourceFile, line, line);
+ if (range != null && range.isEmpty()) {
+ range = getAddressRangesAtSource(moduleLineEntryProvider, sourceFile, line, toEOF);
+ }
+ return range;
+ }
+
+ /**
+ * The public version of this function acts as a wrapper, allowing the original algorithm
+ * to try all fileProviders first, and if that fails, to try to find the next line.
+ * The use case in the original case is to be precise about inline/template situations.
+ * The use case in the new version of the call is to set a breakpoint on first available line
+ * in case the user has chosen to set a breakpoint on a line without LNT information.
+ * @path sourceFile the absolute path to the source file
+ * @param startLine the line number (1-based)
+ * @param endLine the line number (1-based)
+ * @return the unmodifiable list of link-time addresses which start at the given line, possibly empty,
+ * will be null if the source file is not used by this module.
+ */
+ private static Collection<AddressRange> getAddressRangesAtSource(
+ IModuleLineEntryProvider moduleLineEntryProvider,
+ IPath sourceFile,
+ int startLine,
+ int endLine) {
+ Collection<? extends ILineEntryProvider> fileProviders =
+ moduleLineEntryProvider.getLineEntryProvidersForFile(sourceFile);
+ if (fileProviders.isEmpty())
+ return null;
+
+ int lastColumn = -1;
+ IPath lastFile = null;
+ int bestLine = endLine;
+
+ List<EDCAddressRange> addrRanges = null;
+ for (ILineEntryProvider fileProvider : fileProviders) {
+
+ Collection<ILineEntry> entries = fileProvider.getLineEntriesForLines(sourceFile, startLine, endLine);
+ if (addrRanges == null && !entries.isEmpty())
+ addrRanges = new ArrayList<EDCAddressRange>();
+
+ for (ILineEntry entry : entries) {
+
+ int entryLine = entry.getLineNumber();
+
+ if (entryLine < bestLine && addrRanges != null) {
+ addrRanges.clear();
+ bestLine = entryLine;
+ }
+ else if (bestLine == toEOF) {
+ bestLine = entryLine;
+ }
+ else if (entryLine > bestLine) {
+ break; // assume entries sorted; go onto the next fileProvider
+ }
+ // else (entryLine == bestLine) // implied
+
+ /*
+ * when there is more than one line mapping for the source line,
+ * see if it makes sense to merge the line entries into the same
+ * address range, or keep different address ranges. examples of
+ * when this might happen are when there are multiple logical
+ * code segments for the same source line, but in different
+ * columns. in this case it makes sense to merge these into one
+ * address range. for templates and inline functions however,
+ * the column will be the same. for these cases it makes sense
+ * to keep the address ranges separate.
+ */
+ IPath entryPath = entry.getFilePath();
+ int entryColumn = entry.getColumnNumber();
+ if (addrRanges != null)
+ {
+ if (addrRanges.isEmpty() || !entryPath.equals(lastFile) || lastColumn == entryColumn) {
+ addrRanges.add(new EDCAddressRange(entry.getLowAddress(), entry.getHighAddress()));
+ } else {
+ EDCAddressRange range = addrRanges.remove(addrRanges.size() - 1);
+ range.setEndAddress(entry.getHighAddress());
+ addrRanges.add(range);
+ }
+ }
+ lastColumn = entryColumn;
+ lastFile = entryPath;
+ }
+ }
+
+ if (addrRanges == null)
+ return Collections.emptyList();
+
+ List<? extends AddressRange> returnRanges = addrRanges;
+ return Collections.unmodifiableCollection(returnRanges);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.debug.edc.services.IDSFServiceUsingTCF;
+import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCExecutionDMC;
+import org.eclipse.cdt.debug.edc.services.IEDCMemory;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+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.IExpressions.IExpressionChangedDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+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.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.model.MemoryByte;
+import org.eclipse.tm.tcf.protocol.IService;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class Memory extends AbstractEDCService implements IEDCMemory, ICachingService, ISnapshotContributor,
+ IDSFServiceUsingTCF {
+
+ private org.eclipse.tm.tcf.services.IMemory tcfMemoryService;
+ private Map<String, MemoryCache> memoryCaches;
+ private long tcfTimeout;
+
+ private class MemoryChangedEvent extends AbstractDMEvent<IMemoryDMContext> implements IMemoryChangedEvent {
+ IAddress[] addresses;
+
+ public MemoryChangedEvent(IMemoryDMContext context, IAddress[] addresses) {
+ super(context);
+ this.addresses = addresses;
+ }
+
+ public IAddress[] getAddresses() {
+ return addresses;
+ }
+ }
+
+ public Memory(DsfSession session) {
+ super(session, new String[] { IEDCMemory.class.getName(), IMemory.class.getName(), Memory.class.getName(),
+ ISnapshotContributor.class.getName() });
+ setTCFTimeout(15 * 1000); // Fifteen seconds
+ }
+
+ private MemoryCache getMemoryCache(IMemoryDMContext memoryDMC) {
+ assert memoryDMC instanceof IEDCDMContext;
+ MemoryCache cache = memoryCaches.get(((IEDCDMContext) memoryDMC).getID());
+ if (cache == null) {
+ cache = new MemoryCache(getTargetEnvironmentService().getMemoryCacheMinimumBlockSize());
+ memoryCaches.put(((IEDCDMContext) memoryDMC).getID(), cache);
+ }
+ return cache;
+ }
+
+ @Override
+ protected void doInitialize(RequestMonitor requestMonitor) {
+ super.doInitialize(requestMonitor);
+
+ // create memory cache
+ memoryCaches = new HashMap<String, MemoryCache>();
+
+ getSession().addServiceEventListener(this, null);
+ }
+
+ public MemoryByte[] getMemory(final IMemoryDMContext context, final IAddress address, final long offset,
+ final int word_size, final int count) throws CoreException {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), offset, word_size, count })); }
+
+ // Validate the context
+ if (context == null) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
+ "Unknown context type", null)); //$NON-NLS-1$);
+ }
+
+ // Validate the word size
+ if (word_size < 1) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED,
+ "Word size not supported (< 1)", null)); //$NON-NLS-1$
+ }
+
+ // Validate the byte count
+ if (count < 0) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "Invalid word count (< 0)", null)); //$NON-NLS-1$
+ }
+
+ // everything OK
+
+ // NOTE: We normalize word_size and count to read 1-byte words for this implementation
+ MemoryByte[] memoryBytes = getMemoryCache(context).getMemory(tcfMemoryService, context, address.add(offset), 1, count * word_size,
+ getTCFTimeout());
+ // hide breakpoints inserted in the memory by debugger
+ Breakpoints bpService = getService(Breakpoints.class);
+ bpService.removeBreakpointFromMemoryBuffer(address.add(offset), memoryBytes);
+ if (RunControl.timeStepping())
+ System.out.println("Time since stepping start: " +
+ ((System.currentTimeMillis() - RunControl.getSteppingStartTime()) / 1000.0));
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ return memoryBytes;
+ }
+
+ public IStatus getMemory(IEDCExecutionDMC context, IAddress address,
+ ArrayList<MemoryByte> memBuffer, int count, int word_size) {
+ try {
+ MemoryByte[] memArray = getMemory(context, address, 0, count, word_size);
+ memBuffer.addAll(Arrays.asList(memArray));
+ } catch (CoreException e) {
+ return new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
+ "Error reading memory from: " + address.toHexAddressString(), null);
+ }
+ return Status.OK_STATUS;
+ }
+
+ public void flushCache(IDMContext context) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context })); }
+
+ if (isSnapshot())
+ return;
+
+ if (context == null) {
+ // reset every cache in each context
+ for (String key : memoryCaches.keySet()) {
+ memoryCaches.get(key).reset();
+ }
+ } else {
+ IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class);
+ if (memoryCaches.containsKey(((IEDCDMContext) memoryDMC).getID())) {
+ // We do not want to use the call to getMemoryCache() here.
+ // This is because:
+ // 1- if there is not an entry already , we do not want to
+ // automatically
+ // create one, just to call reset() on it.
+ // 2- if memoryDMC == null, we do not want to create a cache
+ // entry for which the key is 'null'
+ memoryCaches.get(((IEDCDMContext) memoryDMC).getID()).reset();
+ }
+ }
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ public IStatus setMemory(IMemoryDMContext context, IAddress address, int word_size, int count, byte[] buffer) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), count })); }
+
+ final IStatus[] ret = new IStatus[] { Status.OK_STATUS };
+
+ setMemory(context, address, 0, word_size, count, buffer, new RequestMonitor(ImmediateExecutor
+ .getInstance(), null) {
+
+ @Override
+ protected void handleFailure() {
+ ret[0] = getStatus();
+ }
+ });
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(ret[0])); }
+ return ret[0];
+ }
+
+ public void tcfServiceReady(IService service) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { service })); }
+ tcfMemoryService = (org.eclipse.tm.tcf.services.IMemory) service;
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(ISuspendedDMEvent e) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { e.getClass() })); }
+ flushCache(e.getDMContext());
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(IResumedDMEvent e) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { e.getClass() })); }
+ if (e.getReason() != StateChangeReason.STEP) {
+ flushCache(e.getDMContext());
+ }
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(IExpressionChangedDMEvent e) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { e.getClass() })); }
+
+ // Get the context and expression service handle
+ final IExpressionDMContext context = e.getDMContext();
+ IExpressions expressionService = getService(IExpressions.class);
+
+ // Get the variable information and update the corresponding memory
+ // locations
+ if (expressionService != null) {
+ expressionService.getExpressionAddressData(context, new DataRequestMonitor<IExpressionDMAddress>(
+ getExecutor(), null) {
+ @Override
+ protected void handleSuccess() {
+ // Figure out which memory area was modified
+ IExpressionDMAddress expression = getData();
+ final int count = expression.getSize();
+ Object expAddress = expression.getAddress();
+ final Addr64 address;
+ if (expAddress instanceof Addr64)
+ address = (Addr64) expAddress;
+ else if (expAddress instanceof IAddress)
+ address = new Addr64(((IAddress) expAddress).getValue());
+ else
+ return; // not a valid memory address
+
+ final IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class);
+ boolean modified = getMemoryCache(memoryDMC).refreshMemory(tcfMemoryService, memoryDMC, address, 0,
+ 1, count, new RequestMonitor(getExecutor(), null), getTCFTimeout());
+ if (modified) {
+ // we've modified cache - send an event
+ IAddress[] addresses = new IAddress[count];
+ for (int i = 0; i < count; i++) {
+ addresses[i] = address.add(i);
+ }
+ getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, addresses), getProperties());
+ }
+ }
+ });
+ }
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ private static final String MEMORY_CONTEXT = "memory_context";
+ private static final String MEMORY = "memory";
+ private static final String CONTEXT_ID = "context_id";
+
+ public void loadSnapshot(Element element) throws Exception {
+ memoryCaches = new HashMap<String, MemoryCache>();
+ NodeList contextElements = element.getElementsByTagName(MEMORY_CONTEXT);
+
+ int numContexts = contextElements.getLength();
+ for (int i = 0; i < numContexts; i++) {
+ Element contextElement = (Element) contextElements.item(i);
+ String contextID = contextElement.getAttribute(CONTEXT_ID);
+ MemoryCache cache = new MemoryCache(getTargetEnvironmentService().getMemoryCacheMinimumBlockSize());
+ cache.loadSnapshot(contextElement);
+ memoryCaches.put(contextID, cache);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor#takeShapshot(org.eclipse.cdt.debug.edc.snapshot.IAlbum, org.w3c.dom.Document, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor) {
+ Element memoryElement = document.createElement(MEMORY);
+ SubMonitor progress = SubMonitor.convert(monitor, memoryCaches.keySet().size() * 1000);
+ progress.subTask("Memory");
+ for (String key : memoryCaches.keySet()) {
+ MemoryCache cache = memoryCaches.get(key);
+ Element memoryCacheElement = document.createElement(MEMORY_CONTEXT);
+ memoryCacheElement.setAttribute(CONTEXT_ID, key);
+ memoryCacheElement.appendChild(cache.takeSnapshot(album, document, progress.newChild(1000)));
+ memoryElement.appendChild(memoryCacheElement);
+ }
+ return memoryElement;
+ }
+
+ public void setTCFTimeout(long msecs) {
+ tcfTimeout = msecs;
+ }
+
+ public long getTCFTimeout() {
+ return tcfTimeout;
+ }
+
+ // Implementation of org.eclipse.cdt.dsf.debug.service.IMemory
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IMemory#fillMemory(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext, org.eclipse.cdt.core.IAddress, long, int, int, byte[], org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ public void fillMemory(final IMemoryDMContext context, final IAddress address, final long offset, final int word_size, final int count,
+ final byte[] pattern, final RequestMonitor rm) {
+ asyncExec(new Runnable() {
+
+ public void run() {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), offset, word_size, count })); }
+
+ // Validate the context
+ if (context == null) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
+ "Unknown context type", null)); //$NON-NLS-1$;
+ rm.done();
+ return;
+ }
+
+ // Validate the word size
+ if (word_size < 1) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED,
+ "Word size not supported (< 1)", null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ // Validate the repeat count
+ if (count < 0) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "Invalid repeat count (< 0)", null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ // Validate the pattern
+ if (pattern.length < 1) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "Empty pattern", null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ // Create an aggregate buffer so we can write in 1 shot
+ final int length = pattern.length;
+ final byte[] buffer = new byte[count * length];
+ for (int i = 0; i < count; i++) {
+ System.arraycopy(pattern, 0, buffer, i * length, length);
+ }
+
+ // All is clear: go for it
+ // NOTE: We normalize word_size and count to read 1-byte words for this implementation
+ try {
+ getMemoryCache(context).setMemory(tcfMemoryService, context, address, offset, 1, count * length * word_size,
+ buffer, getTCFTimeout());
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ rm.setStatus(e.getStatus());
+ }
+ rm.done();
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ }, rm);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IMemory#getMemory(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext, org.eclipse.cdt.core.IAddress, long, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getMemory(final IMemoryDMContext context, final IAddress address, final long offset,
+ final int word_size, final int count, final DataRequestMonitor<MemoryByte[]> drm) {
+
+ asyncExec(new Runnable() {
+
+ public void run() {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), offset, word_size, count })); }
+ // NOTE: We normalize word_size and count to read 1-byte words for this implementation
+ try {
+ MemoryByte[] memoryBytes = getMemory(context, address, offset, word_size, count);
+ drm.setData(memoryBytes);
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ drm.setStatus(e.getStatus());
+ }
+ drm.done();
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ }, drm);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IMemory#setMemory(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext, org.eclipse.cdt.core.IAddress, long, int, int, byte[], org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ public void setMemory(final IMemoryDMContext context, final IAddress address, final long offset,
+ final int word_size, final int count, final byte[] buffer, final RequestMonitor rm) {
+
+ asyncExec(new Runnable() {
+
+ public void run() {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), offset, word_size, count })); }
+
+ // Validate the context
+ if (context == null) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
+ "Unknown context type", null)); //$NON-NLS-1$);
+ rm.done();
+ return;
+ }
+
+ // Validate the word size
+ // NOTE: We only accept 1 byte words for this implementation
+ if (word_size != 1) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED,
+ "Word size not supported (!= 1)", null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ // Validate the byte count
+ if (count < 0) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "Invalid word count (< 0)", null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ // Validate the buffer size
+ if (buffer.length < count) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "Buffer too short", null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+ // everything ok
+ // NOTE: We normalize word_size and count to read 1-byte words for this implementation
+ try {
+ getMemoryCache(context).setMemory(tcfMemoryService, context, address, offset, word_size, count, buffer, getTCFTimeout());
+ if (rm.isSuccess()) {
+ // we've modified memory - send an event
+ IAddress[] addresses = new IAddress[count];
+ for (int i = 0; i < count; i++) {
+ addresses[i] = address.add(offset + i);
+ }
+ getSession().dispatchEvent(new MemoryChangedEvent(context, addresses), getProperties());
+ }
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ rm.setStatus(e.getStatus());
+ }
+ rm.done();
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ }, rm);
+
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.core.IAddressFactory;
+import org.eclipse.cdt.debug.edc.MemoryUtils;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
+import org.eclipse.cdt.utils.Addr32Factory;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.cdt.utils.Addr64Factory;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.model.MemoryByte;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IMemory;
+import org.eclipse.tm.tcf.services.IMemory.DoneGetContext;
+import org.eclipse.tm.tcf.services.IMemory.DoneMemory;
+import org.eclipse.tm.tcf.services.IMemory.ErrorOffset;
+import org.eclipse.tm.tcf.services.IMemory.MemoryContext;
+import org.eclipse.tm.tcf.services.IMemory.MemoryError;
+import org.eclipse.tm.tcf.util.TCFTask;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * This is adapted from
+ * org.eclipse.cdt.dsf.mi.service.MIMemory.MIMemoryCache
+ *
+ */
+public class MemoryCache implements ISnapshotContributor {
+
+ // Timeout waiting for TCF agent reply.
+ final private int TIMEOUT = 6000; // milliseconds
+ private int minimumBlockSize = 0;
+ private final Map<String, MemoryContext> tcfMemoryContexts = Collections.synchronizedMap(new HashMap<String, MemoryContext>());
+ private final SortedMemoryBlockList memoryBlockList = new SortedMemoryBlockList();
+
+ /**
+ * @param minimumBlockSize minimum size of memory block to cache.
+ */
+ public MemoryCache(int minimumBlockSize) {
+ this.minimumBlockSize = minimumBlockSize;
+ }
+
+ public void reset() {
+ // clear the memory cache
+ synchronized (memoryBlockList)
+ {
+ memoryBlockList.clear();
+ }
+ }
+
+ /**
+ * This function walks the address-sorted memory block list to identify
+ * the 'missing' blocks (i.e. the holes) that need to be fetched on the target.
+ *
+ * The idea is fairly simple but an illustration could perhaps help.
+ * Assume the cache holds a number of cached memory blocks with gaps i.e.
+ * there is un-cached memory areas between blocks A, B and C:
+ *
+ * +---------+ +---------+ +---------+
+ * + A + + B + + C +
+ * +---------+ +---------+ +---------+
+ * : : : : : :
+ * [a] : : [b] : : [c] : : [d]
+ * : : : : : :
+ * [e---+--] : [f--+---------+--] : :
+ * [g---+---------+------+---------+------+---------+----]
+ * : : : : : :
+ * : [h] : : [i----+--] : :
+ *
+ *
+ * We have the following cases to consider.The requested block [a-i] either:
+ *
+ * [1] Fits entirely before A, in one of the gaps, or after C
+ * with no overlap and no contiguousness (e.g. [a], [b], [c] and [d])
+ * -> Add the requested block to the list of blocks to fetch
+ *
+ * [2] Starts before an existing block but overlaps part of it, possibly
+ * spilling in the gap following the cached block (e.g. [e], [f] and [g])
+ * -> Determine the length of the missing part (< count)
+ * -> Add a request to fill the gap before the existing block
+ * -> Update the requested block for the next iteration:
+ * - Start address to point just after the end of the cached block
+ * - Count reduced by cached block length (possibly becoming negative, e.g. [e])
+ * At this point, the updated requested block starts just beyond the cached block
+ * for the next iteration.
+ *
+ * [3] Starts at or into an existing block and overlaps part of it ([h] and [i])
+ * -> Update the requested block for the next iteration:
+ * - Start address to point just after the end of the cached block
+ * - Count reduced by length to end of cached block (possibly becoming negative, e.g. [h])
+ * At this point, the updated requested block starts just beyond the cached block
+ * for the next iteration.
+ *
+ * We iterate over the cached blocks list until there is no entry left or until
+ * the remaining requested block count is <= 0, meaning the result list contains
+ * only the sub-blocks needed to fill the gap(s), if any.
+ *
+ * (As is often the case, it takes much more typing to explain it than to just do it :-)
+ *
+ * What is missing is a parameter that indicates the minimal block size that is worth fetching.
+ * This is target-specific and straight in the realm of the coalescing function...
+ *
+ * @param reqBlockStart The address of the requested block
+ * @param count Its length
+ * @return A list of the sub-blocks to fetch in order to fill enough gaps in the memory cache
+ * to service the request
+ */
+ private LinkedList<MemoryBlock> getListOfMissingBlocks(IAddress reqBlockStart, int count) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { reqBlockStart.toHexAddressString(), count })); }
+ LinkedList<MemoryBlock> list = new LinkedList<MemoryBlock>();
+
+ synchronized (memoryBlockList)
+ {
+ ListIterator<MemoryBlock> it = memoryBlockList.listIterator();
+
+ // Look for holes in the list of memory blocks
+ while (it.hasNext() && count > 0) {
+ MemoryBlock cachedBlock = it.next();
+ IAddress cachedBlockStart = cachedBlock.fAddress;
+ IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength);
+
+ // Case where we miss a block before the cached block
+ if (reqBlockStart.distanceTo(cachedBlockStart).longValue() >= 0) {
+ int length = (int) Math.min(reqBlockStart.distanceTo(cachedBlockStart).longValue(), count);
+ // If both blocks start at the same location, no need to create
+ // a new cached block
+ if (length > 0) {
+ IAddress blockAddress;
+ if (reqBlockStart instanceof Addr64) {
+ IAddressFactory f = new Addr64Factory();
+ blockAddress = f.createAddress(reqBlockStart.getValue());
+ } else {
+ IAddressFactory f = new Addr32Factory();
+ blockAddress = f.createAddress(reqBlockStart.getValue());
+ }
+ MemoryBlock newBlock = new MemoryBlock(blockAddress, length, new MemoryByte[0]);
+ list.add(newBlock);
+ }
+ // Adjust request block start and length for the next iteration
+ reqBlockStart = cachedBlockEnd;
+ count -= length + cachedBlock.fLength;
+ }
+
+ // Case where the requested block starts somewhere in the cached
+ // block
+ else if (cachedBlockStart.distanceTo(reqBlockStart).longValue() > 0
+ && reqBlockStart.distanceTo(cachedBlockEnd).longValue() >= 0) {
+ // Start of the requested block already in cache
+ // Adjust request block start and length for the next iteration
+ count -= reqBlockStart.distanceTo(cachedBlockEnd).longValue();
+ reqBlockStart = cachedBlockEnd;
+ }
+ }
+
+ // Case where we miss a block at the end of the cache
+ if (count > 0) {
+ IAddress blockAddress;
+ if (reqBlockStart instanceof Addr64) {
+ IAddressFactory f = new Addr64Factory();
+ blockAddress = f.createAddress(reqBlockStart.getValue());
+ } else {
+ IAddressFactory f = new Addr32Factory();
+ blockAddress = f.createAddress(reqBlockStart.getValue());
+ }
+ MemoryBlock newBlock = new MemoryBlock(blockAddress, count, new MemoryByte[0]);
+ list.add(newBlock);
+ }
+ }
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(list)); }
+ return list;
+ }
+
+ /**
+ * This function walks the address-sorted memory block list to get the
+ * cached memory bytes (possibly from multiple contiguous blocks). This
+ * function is called *after* the missing blocks have been read from the
+ * back end i.e. the requested memory is all cached.
+ *
+ * Again, this is fairly simple. As we loop over the address-ordered list,
+ * There are really only 2 cases:
+ *
+ * [1] The requested block fits entirely in the cached block ([a] or [b])
+ * [2] The requested block starts in a cached block and ends in the
+ * following (contiguous) one ([c]) in which case it is treated as 2
+ * contiguous requests ([c'] and [c"])
+ *
+ * +--------------+--------------+ + A + B + +--------------+--------------+
+ * : [a----] : [b-----] : : : : : [c-----+------] : : [c'---]+[c"---] :
+ *
+ * @param reqBlockStart
+ * The address of the requested block
+ * @param count
+ * Its length
+ * @return The cached memory content
+ */
+ private MemoryByte[] getMemoryBlockFromCache(IAddress reqBlockStart, int count) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { reqBlockStart.toHexAddressString(), count })); }
+
+ MemoryByte[] resultBlock = new MemoryByte[count];
+
+ synchronized (memoryBlockList)
+ {
+ IAddress reqBlockEnd = reqBlockStart.add(count);
+ ListIterator<MemoryBlock> iter = memoryBlockList.listIterator();
+
+ while (iter.hasNext()) {
+ MemoryBlock cachedBlock = iter.next();
+ IAddress cachedBlockStart = cachedBlock.fAddress;
+ IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength);
+
+ // Case where the cached block overlaps completely the requested
+ // memory block
+ if (cachedBlockStart.distanceTo(reqBlockStart).longValue() >= 0
+ && reqBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) {
+ int pos = (int) cachedBlockStart.distanceTo(reqBlockStart).longValue();
+ System.arraycopy(cachedBlock.fBlock, pos, resultBlock, 0, count);
+ }
+
+ // Case where the beginning of the cached block is within the
+ // requested memory block
+ else if (reqBlockStart.distanceTo(cachedBlockStart).longValue() >= 0
+ && cachedBlockStart.distanceTo(reqBlockEnd).longValue() > 0) {
+ int pos = (int) reqBlockStart.distanceTo(cachedBlockStart).longValue();
+ int length = (int) Math.min(cachedBlock.fLength, count - pos);
+ System.arraycopy(cachedBlock.fBlock, 0, resultBlock, pos, length);
+ }
+
+ // Case where the end of the cached block is within the requested
+ // memory block
+ else if (cachedBlockStart.distanceTo(reqBlockStart).longValue() >= 0
+ && reqBlockStart.distanceTo(cachedBlockEnd).longValue() > 0) {
+ int pos = (int) cachedBlockStart.distanceTo(reqBlockStart).longValue();
+ int length = (int) Math.min(cachedBlock.fLength - pos, count);
+ System.arraycopy(cachedBlock.fBlock, pos, resultBlock, 0, length);
+ }
+ }
+ }
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArgs(resultBlock)); }
+ return resultBlock;
+ }
+
+ /**
+ * This function walks the address-sorted memory block list and updates the
+ * content with the actual memory just read from the target.
+ *
+ * @param modBlockStart
+ * @param count
+ * @param modBlock
+ */
+ private void updateMemoryCache(IAddress modBlockStart, int count, MemoryByte[] modBlock) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { modBlockStart.toHexAddressString(), count })); }
+
+ synchronized (memoryBlockList)
+ {
+ IAddress modBlockEnd = modBlockStart.add(count);
+ ListIterator<MemoryBlock> iter = memoryBlockList.listIterator();
+
+ while (iter.hasNext()) {
+ MemoryBlock cachedBlock = iter.next();
+ IAddress cachedBlockStart = cachedBlock.fAddress;
+ IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength);
+
+ // For now, we only bother to update bytes already cached.
+ // Note: In a better implementation (v1.1), we would augment
+ // the cache with the missing memory blocks since we went
+ // through the pains of reading them in the first place.
+ // (this is left as an exercise to the reader :-)
+
+ // Case where the modified block is completely included in the
+ // cached block
+ if (cachedBlockStart.distanceTo(modBlockStart).longValue() >= 0
+ && modBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) {
+ int pos = (int) cachedBlockStart.distanceTo(modBlockStart).longValue();
+ System.arraycopy(modBlock, 0, cachedBlock.fBlock, pos, count);
+ }
+
+ // Case where the beginning of the modified block is within the
+ // cached block
+ else if (cachedBlockStart.distanceTo(modBlockStart).longValue() >= 0
+ && modBlockStart.distanceTo(cachedBlockEnd).longValue() > 0) {
+ int pos = (int) cachedBlockStart.distanceTo(modBlockStart).longValue();
+ int length = (int) cachedBlockStart.distanceTo(modBlockEnd).longValue();
+ System.arraycopy(modBlock, 0, cachedBlock.fBlock, pos, length);
+ }
+
+ // Case where the end of the modified block is within the cached
+ // block
+ else if (cachedBlockStart.distanceTo(modBlockEnd).longValue() > 0
+ && modBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) {
+ int pos = (int) modBlockStart.distanceTo(cachedBlockStart).longValue();
+ int length = (int) cachedBlockStart.distanceTo(modBlockEnd).longValue();
+ System.arraycopy(modBlock, pos, cachedBlock.fBlock, 0, length);
+ }
+ }
+ }
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ return;
+ }
+
+ /**
+ * This function iterates through missing blocks (blocks not currently
+ * cached, but wanted) and reads from the target and creates new cached
+ * blocks.
+ *
+ * @param tcfMemoryService
+ * @param context
+ * @param address
+ * @param word_size
+ * @param count
+ * @param drm
+ * @param timeOutLimit
+ * @throws CoreException
+ */
+ public MemoryByte[] getMemory(final IMemory tcfMemoryService, final IMemoryDMContext context, final IAddress address,
+ final int word_size, final int count, long timeOutLimit) throws CoreException {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context, address.toHexAddressString(), word_size, count })); }
+
+ // determine number of read requests to issue
+ final LinkedList<MemoryBlock> missingBlocks = getListOfMissingBlocks(address, count);
+ final int numberOfRequests = missingBlocks.size();
+
+ if (numberOfRequests > 0 && tcfMemoryService == null) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, "Fail to read memory."));
+ }
+ // System.out.printf("MemoryCache.getMemory address=%x count=%d numberOfRequests=%d\n",
+ // address.getValue(), count, numberOfRequests);
+ for (int i = 0; i < numberOfRequests; i++) {
+ MemoryBlock block = missingBlocks.get(i);
+ IAddress blockAddress = block.fAddress;
+ int blockLength = (int) block.fLength;
+ if (blockLength < minimumBlockSize)
+ blockLength = minimumBlockSize;
+
+ MemoryByte[] result;
+ try {
+ result = readBlock(tcfMemoryService, context, blockAddress, word_size, blockLength, timeOutLimit);
+ MemoryBlock newBlock = new MemoryBlock(blockAddress, blockLength, result);
+ memoryBlockList.add(newBlock);
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "Fail to read memory.", e.getCause())); //$NON-NLS-1$
+ }
+ }
+ MemoryByte[] result = getMemoryBlockFromCache(address, count);
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ return result;
+ }
+
+ private MemoryContext getTCFMemoryContext(final IMemory tcfMemoryService, final String contextID, long timeOutLimit) throws IOException, InterruptedException, ExecutionException, TimeoutException {
+
+ MemoryContext ret = tcfMemoryContexts.get(contextID);
+ if (ret != null)
+ return ret;
+
+ final TCFTask<MemoryContext> tcfTask = new TCFTask<MemoryContext>(TIMEOUT) {
+
+ public void run() {
+ tcfMemoryService.getContext(contextID, new DoneGetContext() {
+
+ public void doneGetContext(IToken token, Exception error, MemoryContext context) {
+ if (error == null) {
+ done(context);
+ } else {
+ error(error);
+ }
+ }
+ });
+ }
+ };
+
+ ret = tcfTask.get(timeOutLimit, TimeUnit.MILLISECONDS);
+
+ if (ret != null)
+ tcfMemoryContexts.put(contextID, ret);
+
+ return ret;
+ }
+
+ /**
+ * This function does the actual reading from the target.
+ *
+ * @param tcfMemoryService
+ * @param context
+ * @param address
+ * @param word_size
+ * @param count
+ * @param timeOutLimit
+ * @return
+ * @throws IOException
+ * @throws TimeoutException
+ * @throws ExecutionException
+ * @throws InterruptedException
+ */
+ private MemoryByte[] readBlock(final IMemory tcfMemoryService, final IMemoryDMContext context,
+ final IAddress address, final int word_size, final int count, long timeOutLimit) throws IOException, InterruptedException, ExecutionException, TimeoutException {
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context, address.toHexAddressString(), word_size, count })); }
+
+ final MemoryContext tcfMC = getTCFMemoryContext(tcfMemoryService, ((IEDCDMContext)context).getID(), timeOutLimit);
+
+ MemoryByte[] result = null;
+
+ final TCFTask<MemoryByte[]> tcfTask = new TCFTask<MemoryByte[]>(TIMEOUT) {
+
+ public void run() {
+ Number tcfAddress = address.getValue();
+ final byte[] buffer = new byte[word_size * count];
+ tcfMC.get(tcfAddress, word_size, buffer, 0, count * word_size, 0, new DoneMemory() {
+
+ public void doneMemory(IToken token, MemoryError error) {
+ if (error == null) {
+ MemoryByte[] res = new MemoryByte[buffer.length];
+ for (int i = 0; i < buffer.length; i++) {
+ res[i] = new MemoryByte(buffer[i]);
+ }
+ done(res);
+ } else if (error instanceof IMemory.ErrorOffset) {
+ boolean someStatusKnown = false;
+ IMemory.ErrorOffset errorOffset = (ErrorOffset) error;
+ MemoryByte[] res = new MemoryByte[buffer.length];
+
+ // TODO: figure actual endianness (MemoryByte.BIG_ENDIAN) flag;
+ // we leave out the flag here which defaults to little-endian
+ for (int i = 0; i < buffer.length; i++) {
+ byte flags = MemoryByte.ENDIANESS_KNOWN | MemoryByte.READABLE | MemoryByte.WRITABLE;
+
+ int st = errorOffset.getStatus(i);
+ if ((st & IMemory.ErrorOffset.BYTE_UNKNOWN) != 0) {
+ flags = 0;
+ } else {
+ someStatusKnown = true;
+ if ((st & IMemory.ErrorOffset.BYTE_INVALID) != 0) {
+ flags &= ~(MemoryByte.READABLE + MemoryByte.WRITABLE);
+ } else {
+ if ((st & IMemory.ErrorOffset.BYTE_CANNOT_READ) != 0)
+ flags &= ~MemoryByte.READABLE;
+ if ((st & IMemory.ErrorOffset.BYTE_CANNOT_WRITE) != 0)
+ flags &= ~MemoryByte.WRITABLE;
+ }
+ }
+ res[i] = new MemoryByte(buffer[i], flags);
+ }
+ if (someStatusKnown)
+ done(res);
+ else
+ error(error);
+ } else {
+ error(error);
+ }
+ }
+ });
+ }
+ };
+
+ result = tcfTask.get(timeOutLimit, TimeUnit.MILLISECONDS);
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ return result;
+ }
+
+ /**
+ * This function writes a block of memory and then re-reads and updates any
+ * cached blocks.
+ *
+ * @param tcfMemoryService
+ * @param context
+ * @param address
+ * @param offset
+ * @param word_size
+ * @param count
+ * @param buffer
+ * @param rm
+ * @throws CoreException
+ */
+ public void setMemory(IMemory tcfMemoryService, IMemoryDMContext context, final IAddress address,
+ final long offset, final int word_size, final int count, byte[] buffer,
+ long timeOutLimit) throws CoreException {
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context, address.toHexAddressString(), offset, word_size, count })); }
+
+ try {
+ writeBlock(tcfMemoryService, context, address, offset, word_size, count, buffer, timeOutLimit);
+ if (blockIsCached(address.add(offset), word_size * count)) {
+ MemoryByte[] update = readBlock(tcfMemoryService, context, address.add(offset), word_size, count, timeOutLimit);
+ updateMemoryCache(address.add(offset), update.length, update);
+ }
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, "Fail to write memory."));
+ }
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * This function figures out if a block of memory is already cached.
+ *
+ * @param modBlockStart
+ * @param count
+ * @return
+ */
+ private boolean blockIsCached(IAddress modBlockStart, int count) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { modBlockStart.toHexAddressString(), count })); }
+ boolean cacheFound = false;
+
+ synchronized (memoryBlockList)
+ {
+ IAddress modBlockEnd = modBlockStart.add(count);
+ ListIterator<MemoryBlock> iter = memoryBlockList.listIterator();
+
+ while (iter.hasNext()) {
+ MemoryBlock cachedBlock = iter.next();
+ IAddress cachedBlockStart = cachedBlock.fAddress;
+ IAddress cachedBlockEnd = cachedBlock.fAddress.add(cachedBlock.fLength);
+
+ // For now, we only bother to update bytes already cached.
+ // Note: In a better implementation (v1.1), we would augment
+ // the cache with the missing memory blocks since we went
+ // through the pains of reading them in the first place.
+ // (this is left as an exercise to the reader :-)
+
+ // Case where the modified block is completely included in the
+ // cached block
+ if (cachedBlockStart.distanceTo(modBlockStart).longValue() >= 0
+ && modBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) {
+ cacheFound = true;
+ }
+
+ // Case where the beginning of the modified block is within the
+ // cached block
+ else if (cachedBlockStart.distanceTo(modBlockStart).longValue() >= 0
+ && modBlockStart.distanceTo(cachedBlockEnd).longValue() > 0) {
+ cacheFound = true;
+ }
+
+ // Case where the end of the modified block is within the cached
+ // block
+ else if (cachedBlockStart.distanceTo(modBlockEnd).longValue() > 0
+ && modBlockEnd.distanceTo(cachedBlockEnd).longValue() >= 0) {
+ cacheFound = true;
+ }
+ }
+ }
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(cacheFound)); }
+ return cacheFound;
+ }
+
+ /**
+ * This function writes a memory block to the target.
+ *
+ * @param tcfMemoryService
+ * @param context
+ * @param address
+ * @param offset
+ * @param word_size
+ * @param count
+ * @param buffer
+ * @param timeOutLimit
+ * @throws IOException
+ * @throws TimeoutException
+ * @throws ExecutionException
+ * @throws InterruptedException
+ */
+ private void writeBlock(final IMemory tcfMemoryService, final IMemoryDMContext context, final IAddress address,
+ final long offset, final int word_size, final int count, final byte[] buffer, long timeOutLimit) throws IOException, InterruptedException, ExecutionException, TimeoutException {
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context, address.toHexAddressString(), offset, word_size, count })); }
+
+ final TCFTask<MemoryByte[]> tcfTask = new TCFTask<MemoryByte[]>(TIMEOUT) {
+
+ public void run() {
+ final TCFTask<MemoryByte[]> task = this;
+ String memoryContextID = ((IEDCDMContext) context).getID();
+ tcfMemoryService.getContext(memoryContextID, new DoneGetContext() {
+
+ public void doneGetContext(IToken token, Exception error, MemoryContext context) {
+ if (error == null) {
+ Number tcfAddress = address.add(offset).getValue();
+ context.set(tcfAddress, word_size, buffer, 0, count * word_size, 0, new DoneMemory() {
+
+ public void doneMemory(IToken token, MemoryError error) {
+ if (error == null) {
+ task.done(null);
+ } else {
+ task.error(error);
+ }
+ }
+ });
+ } else {
+ task.error(error);
+ }
+ }
+ });
+ }
+ };
+
+ tcfTask.get(timeOutLimit, TimeUnit.MILLISECONDS);
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ private class MemoryBlock {
+ public MemoryBlock(IAddress fAddress, long fLength, MemoryByte[] fBlock) {
+ super();
+ this.fAddress = fAddress;
+ this.fLength = fLength;
+ this.fBlock = fBlock;
+ }
+
+ public IAddress fAddress;
+ public long fLength;
+ public MemoryByte[] fBlock;
+ }
+
+ @SuppressWarnings("serial")
+ private class SortedMemoryBlockList extends LinkedList<MemoryBlock> {
+ public SortedMemoryBlockList() {
+ super();
+ }
+
+ // Insert the block in the sorted linked list and merge contiguous
+ // blocks if necessary
+ @Override
+ synchronized public boolean add(MemoryBlock block) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { block.fAddress.toHexAddressString(), block.fLength })); }
+
+ // If the list is empty, just store the block
+ if (isEmpty()) {
+ addFirst(block);
+ return true;
+ }
+
+ // Insert the block at the correct location and then
+ // merge the blocks if possible
+ ListIterator<MemoryBlock> it = listIterator();
+ while (it.hasNext()) {
+ int index = it.nextIndex();
+ MemoryBlock item = it.next();
+ if (block.fAddress.compareTo(item.fAddress) < 0) {
+ add(index, block);
+ compact(index);
+ return true;
+ }
+ }
+
+ // Put at the end of the list and merge if necessary
+ addLast(block);
+ compact(size() - 1);
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ return true;
+ }
+
+ // Merge this block with its contiguous neighbors (if any)
+ // Note: Merge is not performed if resulting block size would exceed
+ // MAXINT
+ private void compact(int index) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { index })); }
+
+ MemoryBlock newBlock = get(index);
+
+ // Case where the block is to be merged with the previous block
+ if (index > 0) {
+ MemoryBlock prevBlock = get(index - 1);
+ IAddress endOfPreviousBlock = prevBlock.fAddress.add(prevBlock.fLength);
+ if (endOfPreviousBlock.distanceTo(newBlock.fAddress).longValue() == 0) {
+ long newLength = prevBlock.fLength + newBlock.fLength;
+ if (newLength <= Integer.MAX_VALUE) {
+ MemoryByte[] block = new MemoryByte[(int) newLength];
+ System.arraycopy(prevBlock.fBlock, 0, block, 0, (int) prevBlock.fLength);
+ System.arraycopy(newBlock.fBlock, 0, block, (int) prevBlock.fLength, (int) newBlock.fLength);
+ newBlock = new MemoryBlock(prevBlock.fAddress, newLength, block);
+ remove(index);
+ index -= 1;
+ set(index, newBlock);
+ }
+ }
+ }
+
+ // Case where the block is to be merged with the following block
+ int lastIndex = size() - 1;
+ if (index < lastIndex) {
+ MemoryBlock nextBlock = get(index + 1);
+ IAddress endOfNewBlock = newBlock.fAddress.add(newBlock.fLength);
+ if (endOfNewBlock.distanceTo(nextBlock.fAddress).longValue() == 0) {
+ long newLength = newBlock.fLength + nextBlock.fLength;
+ if (newLength <= Integer.MAX_VALUE) {
+ MemoryByte[] block = new MemoryByte[(int) newLength];
+ System.arraycopy(newBlock.fBlock, 0, block, 0, (int) newBlock.fLength);
+ System.arraycopy(nextBlock.fBlock, 0, block, (int) newBlock.fLength, (int) nextBlock.fLength);
+ newBlock = new MemoryBlock(newBlock.fAddress, newLength, block);
+ set(index, newBlock);
+ remove(index + 1);
+ }
+ }
+ }
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+ }
+
+ /**
+ * Refreshes cache blocks when memory has been changed through an event.
+ *
+ * @param tcfMemoryService
+ * @param context
+ * @param address
+ * @param offset
+ * @param word_size
+ * @param count
+ * @param rm
+ * @param timeOutLimit
+ * @return true = cache has been modified
+ */
+ public boolean refreshMemory(IMemory tcfMemoryService, IMemoryDMContext context, IAddress address, int offset,
+ int word_size, int count, RequestMonitor rm, long timeOutLimit) {
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context, address.toHexAddressString(), offset, count })); }
+
+ boolean modified = false;
+ // Check if we already cache part of this memory area (which means it
+ // is used by a memory service client that will have to be updated)
+ LinkedList<MemoryBlock> list = getListOfMissingBlocks(address, count);
+ int sizeToRead = 0;
+ for (MemoryBlock block : list) {
+ sizeToRead += block.fLength;
+ }
+
+ // If none of the requested memory is in cache, just get out
+ if (sizeToRead == count) {
+ rm.done();
+ return false;
+ }
+
+ try {
+ MemoryByte[] newBlock = readBlock(tcfMemoryService, context, address, word_size, count, timeOutLimit);
+ MemoryByte[] oldBlock = getMemoryBlockFromCache(address, count);
+ boolean blocksDiffer = false;
+ for (int i = 0; i < oldBlock.length; i++) {
+ if (oldBlock[i].getValue() != newBlock[i].getValue()) {
+ blocksDiffer = true;
+ break;
+ }
+ }
+ if (blocksDiffer) {
+ updateMemoryCache(address, count, newBlock);
+ modified = true;
+ }
+ } catch (Exception e) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "Error Writing Memory", e)); //$NON-NLS-1$
+ }
+ rm.done();
+
+ if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ return modified;
+ }
+
+ private static final String MEMORY_CACHE = "memory_cache";
+ private static final String MEMORY_BLOCK = "memory_block";
+ private static final String ATTR_ADDRESS = "address";
+ private static final String ATTR_LENGTH = "length";
+ private static final String ATTR_VALUE = "value";
+
+ public void loadSnapshot(Element element) throws Exception {
+ reset();
+ NodeList blockElements = element.getElementsByTagName(MEMORY_BLOCK);
+
+ int numBlocks = blockElements.getLength();
+ for (int i = 0; i < numBlocks; i++) {
+ Element blockElement = (Element) blockElements.item(i);
+ String blockAddress = blockElement.getAttribute(ATTR_ADDRESS);
+ String blockLength = blockElement.getAttribute(ATTR_LENGTH);
+ String blockValue = blockElement.getAttribute(ATTR_VALUE);
+ MemoryByte[] blockBytes = MemoryUtils.convertHexStringToMemoryBytes(blockValue, blockValue.length() / 2, 2);
+ MemoryBlock newBlock = new MemoryBlock(new Addr64(blockAddress), Long.parseLong(blockLength), blockBytes);
+ memoryBlockList.add(newBlock);
+ }
+ }
+
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor) {
+ synchronized (memoryBlockList)
+ {
+ SubMonitor progress = SubMonitor.convert(monitor, memoryBlockList.size());
+ progress.subTask("Memory");
+ Element memoryCacheElement = document.createElement(MEMORY_CACHE);
+ ListIterator<MemoryBlock> iter = memoryBlockList.listIterator();
+
+ while (iter.hasNext()) {
+ MemoryBlock block = iter.next();
+ Element blockElement = document.createElement(MEMORY_BLOCK);
+ blockElement.setAttribute(ATTR_ADDRESS, block.fAddress.toHexAddressString());
+ blockElement.setAttribute(ATTR_LENGTH, Long.toString(block.fLength));
+ blockElement.setAttribute(ATTR_VALUE, MemoryUtils.convertMemoryBytesToHexString(block.fBlock));
+ memoryCacheElement.appendChild(blockElement);
+ progress.worked(1);
+ }
+ return memoryCacheElement;
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.PathUtils;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExecutionDMC;
+import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotUtils;
+import org.eclipse.cdt.debug.edc.internal.symbols.IRuntimeSection;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
+import org.eclipse.cdt.debug.edc.internal.symbols.RuntimeSection;
+import org.eclipse.cdt.debug.edc.internal.symbols.Section;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.ExecutableSymbolicsReaderFactory;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.debug.edc.services.DMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCExecutionDMC;
+import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCModules;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
+import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider.ILineAddresses;
+import org.eclipse.cdt.debug.edc.tcf.extension.ProtocolConstants.IModuleProperty;
+import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+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.IBreakpoints.IBreakpointsTargetDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class Modules extends AbstractEDCService implements IModules, IEDCModules {
+
+ public static final String MODULE = "module";
+ public static final String SECTION = "section";
+
+ private static final String ADDRESS_RANGE_CACHE = "_address_range";
+ private static final String LINE_ADDRESSES_CACHE = "_line_addresses";
+ private static final String NO_FILE_CACHE = "_no_file";
+
+ /**
+ * Modules that are loaded for each ISymbolDMContext (process).
+ */
+ private final Map<String, List<ModuleDMC>> modules = Collections
+ .synchronizedMap(new HashMap<String, List<ModuleDMC>>());
+
+ private ISourceLocator sourceLocator;
+ private static int nextModuleID = 100;
+
+ public static class EDCAddressRange implements AddressRange, Serializable {
+
+ private static final long serialVersionUID = -6475152211053407789L;
+ private IAddress startAddr, endAddr;
+
+ public EDCAddressRange(IAddress start, IAddress end) {
+ startAddr = start;
+ endAddr = end;
+ }
+
+ public IAddress getEndAddress() {
+ return endAddr;
+ }
+
+ public void setEndAddress(IAddress address) {
+ endAddr = address;
+ }
+
+ public IAddress getStartAddress() {
+ return startAddr;
+ }
+
+ public void setStartAddress(IAddress address) {
+ startAddr = address;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormat.format("[{0},{1})", startAddr.toHexAddressString(), endAddr.toHexAddressString());
+ }
+
+ public boolean contains(IAddress address) {
+ return getStartAddress().compareTo(address) <= 0
+ && getEndAddress().compareTo(address) > 0;
+ }
+ }
+
+ public static class EDCLineAddresses implements ILineAddresses, Serializable {
+
+ private static final long serialVersionUID = 3263812332106024057L;
+
+ private int lineNumber;
+ private List<IAddress> addresses;
+
+ public EDCLineAddresses(int lineNumber, IAddress addr) {
+ super();
+ this.lineNumber = lineNumber;
+ addresses = new ArrayList<IAddress>();
+ addresses.add(addr);
+ }
+
+ public EDCLineAddresses(int lineNumber, List<IAddress> addrs) {
+ super();
+ this.lineNumber = lineNumber;
+ addresses = new ArrayList<IAddress>(addrs);
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public IAddress[] getAddress() {
+ return addresses.toArray(new IAddress[addresses.size()]);
+ }
+
+ /**
+ * add addresses mapped to the line.
+ * @param addr
+ */
+ public void addAddress(List<IAddress> addrs) {
+ addresses.addAll(addrs);
+ }
+
+ /**
+ * add addresses mapped to the line.
+ * @param addrs
+ */
+ public void addAddress(IAddress[] addrs) {
+ for (IAddress a : addrs)
+ addresses.add(a);
+ }
+
+ @Override
+ public String toString() {
+ String addrs = "";
+ for (IAddress a : addresses) {
+ addrs += a.toHexAddressString() + " ";
+ }
+ return "EDCLineAddresses [lineNumber=" + lineNumber
+ + ", addresses=(" + addrs + ")]";
+ }
+ }
+
+ public class ModuleDMC extends DMContext implements IEDCModuleDMContext, ISnapshotContributor,
+ // This means we'll install existing breakpoints
+ // for each newly loaded module
+ IBreakpointsTargetDMContext,
+ // This means calcAddressInfo() also applies to single module
+ // in addition to a process.
+ ISymbolDMContext {
+ private final ISymbolDMContext symbolContext;
+
+ private final IPath hostFilePath;
+ private IEDCSymbolReader symReader;
+ private final List<IRuntimeSection> runtimeSections = new ArrayList<IRuntimeSection>();
+
+ public ModuleDMC(ISymbolDMContext symbolContext, Map<String, Object> props) {
+ super(Modules.this, symbolContext == null ? new IDMContext[0] : new IDMContext[] { symbolContext }, Integer
+ .toString(getNextModuleID()), props);
+ this.symbolContext = symbolContext;
+
+ String filename = "";
+ if (props.containsKey(IModuleProperty.PROP_FILE))
+ filename = (String) props.get(IModuleProperty.PROP_FILE);
+
+ hostFilePath = locateModuleFileOnHost(filename);
+ }
+
+ public ISymbolDMContext getSymbolContext() {
+ return symbolContext;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext#getSymbolReader()
+ */
+ public IEDCSymbolReader getSymbolReader() {
+ return symReader;
+ }
+
+ public void loadSnapshot(Element element) throws Exception {
+ NodeList sectionElements = element.getElementsByTagName(SECTION);
+
+ int numSections = sectionElements.getLength();
+ for (int i = 0; i < numSections; i++) {
+ Element sectionElement = (Element) sectionElements.item(i);
+ Element propElement = (Element) sectionElement.getElementsByTagName(SnapshotUtils.PROPERTIES).item(0);
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ SnapshotUtils.initializeFromXML(propElement, properties);
+
+ IAddress linkAddress = new Addr64(sectionElement.getAttribute(ISection.PROPERTY_LINK_ADDRESS));
+ int sectionID = Integer.parseInt(sectionElement.getAttribute(ISection.PROPERTY_ID));
+ long size = Long.parseLong(sectionElement.getAttribute(ISection.PROPERTY_SIZE));
+
+ RuntimeSection section = new RuntimeSection(new Section(sectionID, size, linkAddress, properties));
+ section.relocate(new Addr64(sectionElement.getAttribute(IRuntimeSection.PROPERTY_RUNTIME_ADDRESS)));
+ runtimeSections.add(section);
+ }
+
+ initializeSymbolReader();
+ }
+
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor) {
+ SubMonitor progress = SubMonitor.convert(monitor, runtimeSections.size() + 1);
+ progress.subTask("Modules");
+ Element contextElement = document.createElement(MODULE);
+ contextElement.setAttribute(PROP_ID, this.getID());
+ Element propsElement = SnapshotUtils.makeXMLFromProperties(document, getProperties());
+ contextElement.appendChild(propsElement);
+
+ for (IRuntimeSection s : runtimeSections) {
+ Element sectionElement = document.createElement(SECTION);
+ sectionElement.setAttribute(ISection.PROPERTY_ID, Integer.toString(s.getId()));
+ sectionElement.setAttribute(ISection.PROPERTY_SIZE, Long.toString(s.getSize()));
+ sectionElement.setAttribute(ISection.PROPERTY_LINK_ADDRESS, s.getLinkAddress().toHexAddressString());
+ sectionElement.setAttribute(IRuntimeSection.PROPERTY_RUNTIME_ADDRESS, s.getRuntimeAddress()
+ .toHexAddressString());
+ propsElement = SnapshotUtils.makeXMLFromProperties(document, s.getProperties());
+ sectionElement.appendChild(propsElement);
+ contextElement.appendChild(sectionElement);
+ progress.worked(1);
+ }
+
+ if (!hostFilePath.isEmpty()) {
+ album.addFile(hostFilePath);
+ IPath possibleSymFile = ExecutableSymbolicsReaderFactory.findSymbolicsFile(hostFilePath);
+ if (possibleSymFile != null) {
+ album.addFile(possibleSymFile);
+ }
+ }
+ progress.worked(1);
+ return contextElement;
+ }
+
+ /**
+ * Relocate sections of the module. This should be called when the
+ * module is loaded.<br>
+ * <br>
+ * The relocation handling is target environment dependent.
+ * Implementation here has been tested for debug applications on
+ * Windows, Linux and Symbian. <br>
+ *
+ * @param props
+ * - runtime section properties from OS or from loader.
+ */
+ public void relocateSections(Map<String, Object> props) {
+
+ initializeSymbolReader();
+
+ if (symReader != null) {
+ for (ISection section: symReader.getSections())
+ {
+ runtimeSections.add(new RuntimeSection(section));
+ }
+ }
+
+ if (props.containsKey(IModuleProperty.PROP_IMAGE_BASE_ADDRESS)) {
+ // Windows module (PE file)
+ //
+
+ Object base = props.get(IModuleProperty.PROP_IMAGE_BASE_ADDRESS);
+ IAddress imageBaseAddr = null;
+ if (base != null) {
+ if (base instanceof Integer)
+ imageBaseAddr = new Addr64(base.toString());
+ else if (base instanceof Long)
+ imageBaseAddr = new Addr64(base.toString());
+ else if (base instanceof String) // the string should be hex
+ // string
+ imageBaseAddr = new Addr64((String) base, 16);
+ else
+ EDCDebugger.getMessageLogger().logError(
+ MessageFormat.format("Module property PROP_ADDRESS has invalid format {0}.", base
+ .getClass()), null);
+ }
+
+ Number size = 0;
+ if (props.containsKey(IModuleProperty.PROP_CODE_SIZE))
+ size = (Number) props.get(IModuleProperty.PROP_CODE_SIZE);
+
+ if (symReader != null) {
+ // relocate
+ //
+ IAddress linkBase = symReader.getBaseLinkAddress();
+ if (linkBase != null && !linkBase.equals(imageBaseAddr)) {
+ BigInteger offset = linkBase.distanceTo(imageBaseAddr);
+ for (IRuntimeSection s : runtimeSections) {
+ IAddress runtimeB = s.getLinkAddress().add(offset);
+ s.relocate(runtimeB);
+ }
+ }
+ } else { // fill in fake section data
+ Map<String, Object> pp = new HashMap<String, Object>();
+ pp.put(ISection.PROPERTY_NAME, ISection.NAME_TEXT);
+ runtimeSections.add(new RuntimeSection(new Section(0, size.longValue(), imageBaseAddr, pp)));
+ }
+ } else if (props.containsKey(IModuleProperty.PROP_CODE_ADDRESS)) {
+ // platforms other than Windows
+ //
+ Number codeAddr = null, dataAddr = null, bssAddr = null;
+ Number codeSize = null, dataSize = null, bssSize = null;
+
+ try {
+ codeAddr = (Number) props.get(IModuleProperty.PROP_CODE_ADDRESS);
+ dataAddr = (Number) props.get(IModuleProperty.PROP_DATA_ADDRESS);
+ bssAddr = (Number) props.get(IModuleProperty.PROP_BSS_ADDRESS);
+ codeSize = (Number) props.get(IModuleProperty.PROP_CODE_SIZE);
+ dataSize = (Number) props.get(IModuleProperty.PROP_DATA_SIZE);
+ bssSize = (Number) props.get(IModuleProperty.PROP_BSS_SIZE);
+ } catch (ClassCastException e) {
+ EDCDebugger.getMessageLogger().logError("Module property value has invalid format.", null);
+ }
+
+ if (symReader != null) {
+ // Relocate.
+ for (IRuntimeSection s : runtimeSections) {
+ if (s.getProperties().get(ISection.PROPERTY_NAME).equals(ISection.NAME_TEXT)
+ && codeAddr != null)
+ s.relocate(new Addr64(codeAddr.toString()));
+ else if (s.getProperties().get(ISection.PROPERTY_NAME).equals(ISection.NAME_DATA)
+ && dataAddr != null)
+ s.relocate(new Addr64(dataAddr.toString()));
+ else if (s.getProperties().get(ISection.PROPERTY_NAME).equals(ISection.NAME_BSS)
+ && bssAddr != null)
+ s.relocate(new Addr64(bssAddr.toString()));
+ }
+ } else {
+ // binary file not available.
+ // fill in our fake sections. If no section size available,
+ // don't bother.
+ //
+ Map<String, Object> pp = new HashMap<String, Object>();
+
+ if (codeAddr != null && codeSize != null) {
+ pp.put(ISection.PROPERTY_NAME, ISection.NAME_TEXT);
+ runtimeSections.add(new RuntimeSection(new Section(0, codeSize.intValue(), new Addr64(codeAddr.toString()), pp)));
+ }
+ if (dataAddr != null && dataSize != null) {
+ pp.clear();
+ pp.put(ISection.PROPERTY_NAME, ISection.NAME_DATA);
+ runtimeSections.add(new RuntimeSection(new Section(0, dataSize.intValue(), new Addr64(dataAddr.toString()), pp)));
+ }
+ if (bssAddr != null && bssSize != null) {
+ pp.clear();
+ pp.put(ISection.PROPERTY_NAME, ISection.NAME_BSS);
+ runtimeSections.add(new RuntimeSection(new Section(0, bssSize.intValue(), new Addr64(bssAddr.toString()), pp)));
+ }
+ }
+ } else {
+ // No runtime address info available from target environment.
+ // The runtime sections will just be the link-time sections.
+ //
+ // This works well for the case where no relocation is needed
+ // such as running the main executable (not DLLs nor shared
+ // libs)
+ // on Windows and Linux.
+ //
+ // However, this may also indicate an error that the debug agent
+ // (or even the target OS or loader) is not doing its job of
+ // telling us the runtime address info.
+ }
+ }
+
+ private void initializeSymbolReader() {
+ if (hostFilePath.toFile().exists()) {
+ symReader = Symbols.getSymbolReader(hostFilePath);
+ if (symReader == null)
+ EDCDebugger.getMessageLogger().log(IStatus.WARNING,
+ MessageFormat.format("''{0}'' has no recognized file format.",
+ hostFilePath), null);
+ else if (! symReader.hasRecognizedDebugInformation()) {
+ // Log as INFO, not ERROR.
+ EDCDebugger.getMessageLogger().log(IStatus.INFO,
+ MessageFormat.format("''{0}'' has no recognized symbolics.",
+ hostFilePath), null);
+ }
+ } else {
+ // Binary file not on host. Do we want to prompt user for one ?
+
+ // TODO: report this differently for the main executable vs. DLLs
+ EDCDebugger.getMessageLogger().log(IStatus.WARNING, MessageFormat
+ .format("Cannot debug ''{0}''; no match found on disk, through source lookup, or in Executables view",
+ hostFilePath), null);
+
+ }
+ }
+
+ /**
+ * Check if a given runtime address falls in this module
+ *
+ * @param absoluteAddr
+ * - absolute runtime address.
+ * @return
+ */
+ public boolean containsAddress(IAddress runtimeAddress) {
+ for (IRuntimeSection s : runtimeSections) {
+ long offset = s.getRuntimeAddress().distanceTo(runtimeAddress).longValue();
+ if (offset >= 0 && offset < s.getSize())
+ return true;
+ }
+
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext#toLinkAddress(org.eclipse.cdt.core.IAddress)
+ */
+ public IAddress toLinkAddress(IAddress runtimeAddress) {
+ IAddress ret = null;
+
+ for (IRuntimeSection s : runtimeSections) {
+ long offset = s.getRuntimeAddress().distanceTo(runtimeAddress).longValue();
+ if (offset >= 0 && offset < s.getSize()) {
+ return s.getLinkAddress().add(offset);
+ }
+ }
+
+ return ret;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCModuleDMContext#toRuntimeAddress(org.eclipse.cdt.core.IAddress)
+ */
+ public IAddress toRuntimeAddress(IAddress linkAddress) {
+ IAddress ret = null;
+
+ for (IRuntimeSection s : runtimeSections) {
+ long offset = s.getLinkAddress().distanceTo(linkAddress).longValue();
+ if (offset >= 0 && offset < s.getSize()) {
+ return s.getRuntimeAddress().add(offset);
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Get file name (without path) of the module.
+ *
+ * @return
+ */
+ public String getFile() {
+ return hostFilePath.lastSegment();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("\nModuleDMC [");
+ if (hostFilePath != null) {
+ builder.append("file=");
+ builder.append(hostFilePath.lastSegment());
+ builder.append(", ");
+ }
+
+ if (symbolContext != null) {
+ builder.append("owner=");
+ builder.append(symbolContext.toString());
+ }
+
+ for (IRuntimeSection s : runtimeSections) {
+ builder.append("\n");
+ builder.append(s);
+ }
+
+ builder.append("]");
+
+ return builder.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + getOuterType().hashCode();
+ result = prime * result + ((hostFilePath == null) ? 0 : hostFilePath.hashCode());
+ result = prime * result + ((symbolContext == null) ? 0 : symbolContext.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ModuleDMC other = (ModuleDMC) obj;
+ if (!getOuterType().equals(other.getOuterType()))
+ return false;
+ if (hostFilePath == null) {
+ if (other.hostFilePath != null)
+ return false;
+ } else if (!hostFilePath.equals(other.hostFilePath))
+ return false;
+ if (symbolContext == null) {
+ if (other.symbolContext != null)
+ return false;
+ } else if (!symbolContext.equals(other.symbolContext))
+ return false;
+ return true;
+ }
+
+ private IEDCModules getOuterType() {
+ return Modules.this;
+ }
+ }
+
+ static class ModuleDMData implements IModuleDMData {
+
+ private final Map<String, Object> properties;
+
+ public ModuleDMData(ModuleDMC dmc) {
+ properties = dmc.getProperties();
+ }
+
+ public String getFile() {
+ return (String) properties.get(IModuleProperty.PROP_FILE);
+ }
+
+ public String getName() {
+ return (String) properties.get(IEDCDMContext.PROP_NAME);
+ }
+
+ public long getTimeStamp() {
+ return 0;
+ // return (String) properties.get(IModuleProperty.PROP_TIME);
+ }
+
+ public String getBaseAddress() {
+ // return hex string representation.
+ //
+ Object baseAddress = properties.get(IModuleProperty.PROP_IMAGE_BASE_ADDRESS);
+ if (baseAddress == null)
+ baseAddress = properties.get(IModuleProperty.PROP_CODE_ADDRESS);
+
+ if (baseAddress != null)
+ return baseAddress.toString();
+ else
+ return "";
+ }
+
+ public String getToAddress() {
+ // TODO this should return the end address, e.g. base + size
+ return getBaseAddress();
+ }
+
+ public boolean isSymbolsLoaded() {
+ return false;
+ }
+
+ public long getSize() {
+ Number moduleSize = (Number) properties.get(IModuleProperty.PROP_CODE_SIZE);
+ if (moduleSize != null)
+ return moduleSize.longValue();
+ else
+ return 0;
+ }
+
+ }
+
+ public static class ModuleLoadedEvent extends AbstractDMEvent<ISymbolDMContext> implements ModuleLoadedDMEvent {
+
+ private final ModuleDMC module;
+ private final IExecutionDMContext executionDMC;
+
+ public ModuleLoadedEvent(ISymbolDMContext symbolContext, IExecutionDMContext executionDMC, ModuleDMC module) {
+ super(symbolContext);
+ this.module = module;
+ this.executionDMC = executionDMC;
+ }
+
+ public IExecutionDMContext getExecutionDMC() {
+ return executionDMC;
+ }
+
+ public IModuleDMContext getLoadedModuleContext() {
+ return module;
+ }
+
+ }
+
+ public static class ModuleUnloadedEvent extends AbstractDMEvent<ISymbolDMContext> implements ModuleUnloadedDMEvent {
+
+ private final ModuleDMC module;
+ private final IExecutionDMContext executionDMC;
+
+ public ModuleUnloadedEvent(ISymbolDMContext symbolContext, IExecutionDMContext executionDMC, ModuleDMC module) {
+ super(symbolContext);
+ this.module = module;
+ this.executionDMC = executionDMC;
+ }
+
+ public IExecutionDMContext getExecutionDMC() {
+ return executionDMC;
+ }
+
+ public IModuleDMContext getUnloadedModuleContext() {
+ return module;
+ }
+
+ }
+
+ public Modules(DsfSession session) {
+ super(session, new String[] { IModules.class.getName(), IEDCModules.class.getName(), Modules.class.getName() });
+ }
+
+ public void setSourceLocator(ISourceLocator sourceLocator) {
+ this.sourceLocator = sourceLocator;
+ }
+
+ public ISourceLocator getSourceLocator() {
+ return sourceLocator;
+ }
+
+ private void addModule(ModuleDMC module) {
+ ISymbolDMContext symContext = module.getSymbolContext();
+ if (symContext instanceof IEDCDMContext) {
+ String symContextID = ((IEDCDMContext) symContext).getID();
+ synchronized (modules) {
+ List<ModuleDMC> moduleList = modules.get(symContextID);
+ if (moduleList == null) {
+ moduleList = Collections.synchronizedList(new ArrayList<ModuleDMC>());
+ modules.put(symContextID, moduleList);
+ }
+ moduleList.add(module);
+ }
+ }
+ }
+
+ private void removeModule(ModuleDMC module) {
+ ISymbolDMContext symContext = module.getSymbolContext();
+ if (symContext instanceof IEDCDMContext) {
+ String symContextID = ((IEDCDMContext) symContext).getID();
+ synchronized (modules) {
+ List<ModuleDMC> moduleList = modules.get(symContextID);
+ if (moduleList != null) {
+ // other module attributes may not be passed during removal,
+ // so remove the module with the same name
+ for (ModuleDMC next : moduleList) {
+ if (next.getFile().equals(module.getFile())) {
+ moduleList.remove(next);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ /*
+ * The result AddressRange[] will contain absolute runtime addresses. And
+ * the "symCtx" can be a process or a module.
+ */
+ @SuppressWarnings("unchecked")
+ public void calcAddressInfo(ISymbolDMContext symCtx, String file, int line, int col,
+ DataRequestMonitor<AddressRange[]> rm) {
+ IModuleDMContext[] moduleList = null;
+
+ if (symCtx instanceof IEDCExecutionDMC) {
+ String symContextID = ((IEDCDMContext) symCtx).getID();
+ moduleList = getModulesForContext(symContextID);
+ } else if (symCtx instanceof IModuleDMContext) {
+ moduleList = new IModuleDMContext[1];
+ moduleList[0] = (IModuleDMContext) symCtx;
+ } else {
+ // should not happen
+ assert false : "Unknown ISymbolDMContext class.";
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, MessageFormat.format(
+ "Unknown class implementing ISymbolDMContext : {0}", symCtx.getClass().getName()), null));
+ rm.done();
+ return;
+ }
+
+ List<EDCAddressRange> addrRanges = new ArrayList<EDCAddressRange>(1);
+
+ for (IModuleDMContext module : moduleList) {
+ ModuleDMC mdmc = (ModuleDMC) module;
+ IEDCSymbolReader reader = mdmc.getSymbolReader();
+
+ if (reader != null) {
+
+ Collection<AddressRange> linkAddressRanges = null;
+ Map<String, Collection<AddressRange>> cachedRanges = new HashMap<String, Collection<AddressRange>>();
+ // Check the persistent cache
+ String cacheKey = reader.getSymbolFile().toOSString() + ADDRESS_RANGE_CACHE;
+ String noFileCacheKey = reader.getSymbolFile().toOSString() + NO_FILE_CACHE;
+ Set<String> noFileCachedData = EDCDebugger.getDefault().getCache().getCachedData(noFileCacheKey, Set.class, reader.getModificationDate());
+ if (noFileCachedData != null && noFileCachedData.contains(file))
+ continue; // We have already determined that this file is not used by this module, don't bother checking again.
+
+ Map<String, Collection<AddressRange>> cachedData = EDCDebugger.getDefault().getCache().getCachedData(cacheKey, Map.class, reader.getModificationDate());
+ if (cachedData != null)
+ {
+ cachedRanges = cachedData;
+ linkAddressRanges = cachedRanges.get(file + line);
+ }
+
+ if (linkAddressRanges == null)
+ {
+ linkAddressRanges = LineEntryMapper.getAddressRangesAtSource(
+ reader.getModuleScope().getModuleLineEntryProvider(),
+ PathUtils.createPath(file),
+ line);
+
+ if (linkAddressRanges == null)
+ { // If this file is not used by this module, cache it so we can avoid searching it again.
+ if (noFileCachedData == null)
+ noFileCachedData = new HashSet<String>();
+ noFileCachedData.add(file);
+ EDCDebugger.getDefault().getCache().putCachedData(noFileCacheKey, (Serializable) noFileCachedData, reader.getModificationDate());
+ continue;
+ }
+ cachedRanges.put(file + line, linkAddressRanges);
+ EDCDebugger.getDefault().getCache().putCachedData(cacheKey, (Serializable) cachedRanges, reader.getModificationDate());
+ }
+
+ // convert addresses to runtime ones.
+ for (AddressRange linkAddressRange : linkAddressRanges) {
+ EDCAddressRange addrRange = new EDCAddressRange(
+ mdmc.toRuntimeAddress(linkAddressRange.getStartAddress()),
+ mdmc.toRuntimeAddress(linkAddressRange.getEndAddress()));
+ addrRanges.add(addrRange);
+ }
+ }
+ }
+
+ if (addrRanges.size() > 0) {
+ AddressRange[] ar = addrRanges.toArray(new AddressRange[addrRanges.size()]);
+ rm.setData(ar);
+ } else {
+ /*
+ * we try to set the breakpoint for every module since we don't know
+ * which one the file is in. we report this error though if the file
+ * isn't in the module, and let the caller handle the error.
+ */
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, MessageFormat.format(
+ "Fail to find address for source line {0}: line# {1}", file, line), null));
+ }
+
+ rm.done();
+ }
+
+ public void calcLineInfo(ISymbolDMContext symCtx, IAddress address, DataRequestMonitor<LineInfo[]> rm) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * Given a source line (let's call it anchor), find the line closest to the
+ * anchor in the neighborhood (including the anchor itself) that has machine
+ * code. If the anchor itself has code, it's returned. Otherwise neighbor
+ * lines both above and below the anchor will be checked. If the closest
+ * line above the anchor and the closest line below the anchor have the same
+ * distance from the anchor, the one below will be selected.
+ *
+ * This is mainly used in setting breakpoint at anchor line.
+ *
+ * @param symCtx
+ * the symbol context in which to perform the lookup. It can be
+ * an execution context (e.g. a process), or a module (exe or
+ * dll) in a process.
+ * @param file
+ * the file that contains the source lines in question.
+ * @param anchor
+ * line number of the anchor source line.
+ * @param neighbor_limit
+ * specify the limit of the neighborhood: up to this number of
+ * lines above the anchor and up to this number of lines below
+ * the anchor will be checked if needed. But the check will never
+ * go beyond the source file. When the limit is zero, no neighbor
+ * lines will be checked. If the limit has value of -1, it means
+ * the actual limit is the source file.
+ * @param rm
+ * contains an object of {@link ILineAddresses} if the line with
+ * code is found. And addresses in it are runtime addresses. The
+ * RM will contain error status otherwise.
+ */
+ public void findClosestLineWithCode(ISymbolDMContext symCtx, String file, int anchor, int neighbor_limit,
+ DataRequestMonitor<ILineAddresses> rm) {
+ IModuleDMContext[] moduleList = null;
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceEntry(null,
+ "Find closest line with code. context: " + EDCTrace.fixArg(symCtx) + " file: " + file + " anchor: " + anchor + " limit: " + neighbor_limit); }
+
+ if (symCtx instanceof IEDCExecutionDMC) {
+ String symContextID = ((IEDCDMContext) symCtx).getID();
+ moduleList = getModulesForContext(symContextID);
+ } else if (symCtx instanceof IModuleDMContext) {
+ moduleList = new IModuleDMContext[1];
+ moduleList[0] = (IModuleDMContext) symCtx;
+ } else {
+ // should not happen
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, MessageFormat.format(
+ "Unknown class implementing ISymbolDMContext : {0}", symCtx.getClass().getName()), null));
+ rm.done();
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().traceExit(null,
+ rm.getStatus()); }
+ return;
+ }
+
+ EDCLineAddresses result = null;
+
+ for (IModuleDMContext module : moduleList) {
+ ModuleDMC mdmc = (ModuleDMC) module;
+ IEDCSymbolReader reader = mdmc.getSymbolReader();
+
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null,
+ "module: " + mdmc + " reader: " + reader); }
+
+ if (reader == null)
+ continue;
+
+ List<ILineAddresses> codeLines = null;
+
+ Map<String, List<ILineAddresses>> cache = new HashMap<String, List<ILineAddresses>>();
+ // Check the persistent cache
+ String cacheKey = reader.getSymbolFile().toOSString() + LINE_ADDRESSES_CACHE;
+ String noFileCacheKey = reader.getSymbolFile().toOSString() + NO_FILE_CACHE;
+ @SuppressWarnings("unchecked")
+ Set<String> noFileCachedData = EDCDebugger.getDefault().getCache().getCachedData(noFileCacheKey, Set.class, reader.getModificationDate());
+ if (noFileCachedData != null && noFileCachedData.contains(file))
+ {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null,
+ "Persistent cache says file not used by module"); }
+ continue; // We have already determined that this file is not used by this module, don't bother checking again.
+ }
+
+ @SuppressWarnings("unchecked")
+ Map<String, List<ILineAddresses>> cachedData = EDCDebugger.getDefault().getCache().getCachedData(cacheKey, Map.class, reader.getModificationDate());
+ if (cachedData != null)
+ {
+ cache = cachedData;
+ codeLines = cachedData.get(file + anchor);
+ }
+
+ if (codeLines == null) // cache missed
+ {
+ if (! reader.getModuleScope().getModuleLineEntryProvider().hasSourceFile(PathUtils.createPath(file)))
+ { // If this file is not used by this module, cache it so we can avoid searching it again.
+ if (noFileCachedData == null)
+ noFileCachedData = new HashSet<String>();
+ noFileCachedData.add(file);
+ EDCDebugger.getDefault().getCache().putCachedData(noFileCacheKey, (Serializable) noFileCachedData, reader.getModificationDate());
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null,
+ "File not used by module"); }
+ continue;
+ }
+
+ codeLines = reader.getModuleScope().getModuleLineEntryProvider().findClosestLineWithCode(
+ PathUtils.createPath(file), anchor, neighbor_limit);
+
+ if (codeLines == null)
+ {
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null,
+ "codeLines == null"); }
+ continue; // should not happen
+ }
+
+ // Cache code lines (with their link addresses), whether we find it or not.
+ cache.put(file + anchor, codeLines);
+ EDCDebugger.getDefault().getCache().putCachedData(cacheKey, (Serializable) cache, reader.getModificationDate());
+ if (EDCTrace.BREAKPOINTS_TRACE_ON) { EDCTrace.getTrace().trace(null,
+ "codeLines: " + codeLines); }
+ }
+
+ // convert addresses to runtime ones.
+ //
+ List<EDCLineAddresses> runtimeCLs = new ArrayList<Modules.EDCLineAddresses>(codeLines.size());
+ for (ILineAddresses cl : codeLines) {
+ List<IAddress> rt_addrs = new ArrayList<IAddress>(1);
+ for (IAddress a : cl.getAddress())
+ rt_addrs.add(mdmc.toRuntimeAddress(a));
+ runtimeCLs.add(new EDCLineAddresses(cl.getLineNumber(), rt_addrs));
+ }
+
+ for (ILineAddresses l : runtimeCLs)
+ result = selectCodeLine(result, l, anchor);
+ }
+
+ if (result != null) {
+ rm.setData(result);
+ } else {
+ /*
+ * we try to set the breakpoint for every module since we don't know
+ * which one the file is in. we report this error though if the file
+ * isn't in the module, and let the caller handle the error.
+ */
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, MessageFormat.format(
+ "Fail to find address sround source line {0}: line# {1}", file, anchor), null));
+ }
+
+ rm.done();
+ }
+
+ private EDCLineAddresses selectCodeLine(EDCLineAddresses prevChoice,
+ ILineAddresses newLine, int anchor) {
+
+ if (prevChoice == null)
+ prevChoice = (EDCLineAddresses)newLine;
+ else {
+ if (newLine.getLineNumber() == prevChoice.getLineNumber()) {
+ // merge the addresses. Same source line has different addresses in different module.
+ prevChoice.addAddress(newLine.getAddress());
+ }
+ else {
+ // code line is different for the anchor in different module
+ if (newLine.getLineNumber() == anchor)
+ // always honor anchor itself
+ prevChoice = (EDCLineAddresses)newLine;
+ else if (prevChoice.getLineNumber() != anchor) {
+ /*
+ * Two different code lines are found (from different
+ * modules or different CUs) and neither of them is anchor.
+ * Don't bother returning both of them as that would cause
+ * unnecessary complexity to breakpoint setting as it means
+ * moving breakpoint set on anchor line to two different
+ * lines. Just keep the one closer to anchor. And user will
+ * see the breakpoint works in one module (or CU) but not
+ * the other.
+ */
+ int new_distance = Math.abs(newLine.getLineNumber() - anchor);
+ int prev_distance = Math.abs(prevChoice.getLineNumber() - anchor);
+
+ if (new_distance < prev_distance)
+ prevChoice = (EDCLineAddresses)newLine;
+ else if (new_distance == prev_distance) {
+ // Same distance from anchor, choose the one below anchor
+ if (newLine.getLineNumber() > prevChoice.getLineNumber())
+ prevChoice = (EDCLineAddresses)newLine;
+ }
+ }
+ }
+ }
+
+ return prevChoice;
+ }
+
+ /**
+ * Get runtime addresses mapped to given source line in given run context.
+ *
+ * @param context
+ * @param sourceFile
+ * @param lineNumber
+ * @param drm If no address found, holds an empty list.
+ */
+ public void getLineAddress(IExecutionDMContext context,
+ String sourceFile, int lineNumber, final DataRequestMonitor<List<IAddress>> drm) {
+ final List<IAddress> addrs = new ArrayList<IAddress>(1);
+
+ final ExecutionDMC dmc = (ExecutionDMC) context;
+ if (dmc == null) {
+ drm.setData(addrs);
+ drm.done();
+ return;
+ }
+
+ ISymbolDMContext symCtx = DMContexts.getAncestorOfType(context, ISymbolDMContext.class);
+
+ sourceFile = EDCLaunch.getLaunchForSession(getSession().getId()).getCompilationPath(sourceFile);
+
+ calcAddressInfo(symCtx, sourceFile, lineNumber, 0,
+ new DataRequestMonitor<AddressRange[]>(getExecutor(), drm) {
+
+ @Override
+ protected void handleCompleted() {
+ if (! isSuccess()) {
+ drm.setStatus(getStatus());
+ drm.done();
+ return;
+ }
+
+ AddressRange[] addr_ranges = getData();
+
+ for (AddressRange range : addr_ranges) {
+ IAddress a = range.getStartAddress(); // this is runtime address
+ addrs.add(a);
+ }
+
+ drm.setData(addrs);
+ drm.done();
+ }
+ });
+ }
+
+ public void getModuleData(IModuleDMContext dmc, DataRequestMonitor<IModuleDMData> rm) {
+ rm.setData(new ModuleDMData((ModuleDMC) dmc));
+ rm.done();
+ }
+
+ public void getModules(ISymbolDMContext symCtx, DataRequestMonitor<IModuleDMContext[]> rm) {
+ String symContextID = ((IEDCDMContext) symCtx).getID();
+ IModuleDMContext[] moduleList = getModulesForContext(symContextID);
+ rm.setData(moduleList);
+ rm.done();
+ }
+
+ public IModuleDMContext[] getModulesForContext(String symContextID) {
+ synchronized (modules) {
+ List<ModuleDMC> moduleList = modules.get(symContextID);
+ if (moduleList == null)
+ return new IModuleDMContext[0];
+ else
+ return moduleList.toArray(new IModuleDMContext[moduleList.size()]);
+ }
+ }
+
+ private int getNextModuleID() {
+ return nextModuleID++;
+ }
+
+ public void moduleLoaded(ISymbolDMContext symbolContext, IExecutionDMContext executionDMC, Map<String, Object> moduleProps) {
+ ModuleDMC module = new ModuleDMC(symbolContext, moduleProps);
+ module.relocateSections(moduleProps);
+ addModule(module);
+ getSession().dispatchEvent(new ModuleLoadedEvent(symbolContext, executionDMC, module),
+ Modules.this.getProperties());
+ }
+
+ public void moduleUnloaded(ISymbolDMContext symbolContext, IExecutionDMContext executionDMC,
+ Map<String, Object> moduleProps) {
+ Object fileName = moduleProps.get(IEDCDMContext.PROP_NAME);
+ ModuleDMC module = getModuleByName(symbolContext, fileName);
+ if (module == null) {
+ EDCDebugger.getMessageLogger().logError("Unexpected unload of module: " + fileName, null);
+ return;
+ }
+ Object requireResumeValue = moduleProps.get("RequireResume");
+ if (requireResumeValue != null && requireResumeValue instanceof Boolean)
+ module.setProperty("RequireResume", requireResumeValue);
+ removeModule(module);
+ getSession().dispatchEvent(new ModuleUnloadedEvent(symbolContext, executionDMC, module),
+ Modules.this.getProperties());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCModules#getModuleByAddress(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress)
+ */
+ public ModuleDMC getModuleByAddress(ISymbolDMContext symCtx, IAddress instructionAddress) {
+ ModuleDMC bestMatch = null;
+ if (symCtx instanceof ModuleDMC) {
+ if (((ModuleDMC)symCtx).containsAddress(instructionAddress))
+ bestMatch = (ModuleDMC)symCtx;
+ }
+ else {
+ synchronized (modules) {
+ List<ModuleDMC> moduleList = modules.get(((IEDCDMContext) symCtx).getID());
+ if (moduleList != null) {
+ for (ModuleDMC moduleDMC : moduleList) {
+ if (moduleDMC.containsAddress(instructionAddress)) {
+ bestMatch = moduleDMC;
+ break;
+ }
+ }
+
+ if (bestMatch == null) {
+ // TODO: add a bogus wrap-all module ?
+ }
+ }
+ }
+ }
+ return bestMatch;
+ }
+
+ /**
+ * Find the host file that corresponds to a given module file whose name
+ * comes from target platform.
+ *
+ * @param originalPath
+ * path or filename from target platform.
+ * @return the path to an existing file on host, null otherwise.
+ */
+ public IPath locateModuleFileOnHost(String originalPath) {
+ if (originalPath == null || originalPath.length() == 0)
+ return Path.EMPTY;
+
+ // Canonicalize path for the host OS, in hopes of finding a match directly on the host,
+ // and for searching sources and executables below.
+ //
+ IPath path = PathUtils.findExistingPathIfCaseSensitive(PathUtils.createPath(originalPath));
+
+ // Try source locator, use the host-correct path.
+ //
+ Object sourceElement = null;
+ ISourceLocator locator = getSourceLocator();
+ if (locator != null) {
+ if (locator instanceof ICSourceLocator || locator instanceof CSourceLookupDirector) {
+ if (locator instanceof ICSourceLocator)
+ sourceElement = ((ICSourceLocator) locator).findSourceElement(path.toOSString());
+ else
+ sourceElement = ((CSourceLookupDirector) locator).getSourceElement(path.toOSString());
+ }
+ if (sourceElement != null) {
+ if (sourceElement instanceof LocalFileStorage) {
+ return new Path(((LocalFileStorage) sourceElement).getFile().getAbsolutePath());
+ }
+ }
+ }
+
+ return path;
+ }
+
+ public void loadModulesForContext(ISymbolDMContext context, Element element) throws Exception {
+
+ List<ModuleDMC> contextModules = Collections.synchronizedList(new ArrayList<ModuleDMC>());
+
+ NodeList moduleElements = element.getElementsByTagName(MODULE);
+
+ int numModules = moduleElements.getLength();
+ for (int i = 0; i < numModules; i++) {
+ Element moduleElement = (Element) moduleElements.item(i);
+ Element propElement = (Element) moduleElement.getElementsByTagName(SnapshotUtils.PROPERTIES).item(0);
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ SnapshotUtils.initializeFromXML(propElement, properties);
+
+ ModuleDMC module = new ModuleDMC(context, properties);
+ module.loadSnapshot(moduleElement);
+ contextModules.add(module);
+
+ }
+ modules.put(((IEDCDMContext) context).getID(), contextModules);
+
+ }
+
+ /**
+ * get module with given file name
+ *
+ * @param symCtx
+ * @param fileName
+ * executable name for module
+ * @return null if not found.
+ */
+ public ModuleDMC getModuleByName(ISymbolDMContext symCtx, Object fileName) {
+ ModuleDMC module = null;
+ synchronized (modules) {
+ List<ModuleDMC> moduleList = modules.get(((IEDCDMContext) symCtx).getID());
+ if (moduleList != null) {
+ for (ModuleDMC moduleDMC : moduleList) {
+ if ((moduleDMC.getName().compareToIgnoreCase((String) fileName)) == 0 ) {
+ module = moduleDMC;
+ break;
+ }
+ }
+ }
+ }
+ return module;
+ }
+
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * Implementation of the no-op service used for testing
+ *
+ */
+public class Noop extends AbstractEDCService implements INoop {
+
+ public Noop(DsfSession session) {
+ super(session, new String[] {INoop.class.getName()});
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.INoop#noop(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void noop(IDMContext ctx, DataRequestMonitor<Boolean> rm) {
+ rm.setData(true);
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.INoop#longNoop(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void longNoop(IDMContext whatever, final DataRequestMonitor<Boolean> rm) {
+ new Thread() {
+ public void run() {
+ try {
+ for (int i = 0; i < 100; i++) {
+ Thread.sleep(100);
+ if (rm.isCanceled()) {
+ rm.setStatus(Status.CANCEL_STATUS);
+ rm.done();
+ return;
+ }
+ }
+ } catch (InterruptedException e) {}
+ rm.setData(true);
+ rm.done();
+ }
+ }.start();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.INoop#longNoopUsingServiceTracker(int, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ public void longNoopUsingServiceTracker(int duration, RequestMonitor rm) {
+ for (int i = 0; i < duration; i++) {
+ // ask for any service; our own is fine
+ getService(INoop.class);
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExecutionDMC;
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.debug.edc.services.IDSFServiceUsingTCF;
+import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCExecutionDMC;
+import org.eclipse.cdt.debug.edc.tcf.extension.ProtocolConstants;
+import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
+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.DMContexts;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.BreakpointsMediator2;
+import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules;
+import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
+import org.eclipse.cdt.dsf.debug.service.IProcesses;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
+import org.eclipse.cdt.dsf.debug.service.command.IEventListener;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.DsfServicesTracker;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tm.tcf.protocol.IService;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IProcesses.ProcessContext;
+
+public class Processes extends AbstractEDCService implements IProcesses, IEventListener, IDSFServiceUsingTCF {
+
+ private org.eclipse.tm.tcf.services.IProcesses tcfProcessesService;
+
+ /*
+ * The data of a corresponding thread or process.
+ */
+ @Immutable
+ protected static class ExecutionDMData implements IThreadDMData {
+ String name = "unknown";
+ String id = "unknown";
+
+ public ExecutionDMData(ExecutionDMC dmc) {
+ id = dmc.getProperty(ProtocolConstants.PROP_OS_ID).toString();
+ name = (String) dmc.getProperty(IEDCDMContext.PROP_NAME);
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isDebuggerAttached() {
+ return true;
+ }
+ }
+
+ public Processes(DsfSession session) {
+ super(session, new String[] { IProcesses.class.getName(), Processes.class.getName() });
+ }
+
+ @Override
+ protected void doInitialize(RequestMonitor requestMonitor) {
+ super.doInitialize(requestMonitor);
+ getSession().addServiceEventListener(this, null);
+ }
+
+ public void attachDebuggerToProcess(IProcessDMContext procCtx, DataRequestMonitor<IDMContext> rm) {
+ rm.done();
+ }
+
+ public void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor<Boolean> rm) {
+ ExecutionDMC edcDMC = (ExecutionDMC) dmc;
+ rm.setData(edcDMC.canDetach());
+ rm.done();
+ }
+
+ public void canTerminate(IThreadDMContext thread, DataRequestMonitor<Boolean> rm) {
+ ExecutionDMC executionDmc = (ExecutionDMC) thread;
+ rm.setData(executionDmc.canTerminate());
+ rm.done();
+ }
+
+ public void debugNewProcess(IDMContext dmc, String file, Map<String, Object> attributes,
+ DataRequestMonitor<IDMContext> rm) {
+ rm.done();
+ }
+
+ /**
+ * Detach debugger from all processes in the debug session.
+ * @param rm
+ */
+ public void detachDebuggerFromSession(final RequestMonitor rm) {
+ RunControl rcService = getServicesTracker().getService(RunControl.class);
+ ExecutionDMC[] processes = rcService.getRootDMC().getChildren();
+
+ CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm);
+
+ crm.setDoneCount(processes.length);
+
+ for (ExecutionDMC p : processes)
+ detachDebuggerFromProcess(p, crm);
+ }
+
+ public void detachDebuggerFromProcess(final IDMContext exeDmc, final RequestMonitor rm) {
+ /*
+ * 1. Remove all breakpoints for all modules in the process.
+ * 2. Resume the process.
+ * 3. Detach the process from agent and host debugger.
+ */
+
+ // Make sure detach from the process, not just a thread.
+ final IProcessDMContext dmc = DMContexts.getAncestorOfType(exeDmc, IProcessDMContext.class);
+
+ final BreakpointsMediator2 bmService = getServicesTracker().getService(BreakpointsMediator2.class);
+ if (bmService == null) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Failed to get BreakpointsMediator2 service."));
+ rm.done();
+ return;
+ }
+ IModules modulesService = getServicesTracker().getService(IModules.class);
+ if (modulesService == null) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Failed to get Modules service."));
+ rm.done();
+ return;
+ }
+
+ ISymbolDMContext symCtx = DMContexts.getAncestorOfType(dmc, ISymbolDMContext.class);
+ modulesService.getModules(symCtx, new DataRequestMonitor<IModules.IModuleDMContext[]>(getExecutor(), rm) {
+
+ @Override
+ protected void handleCompleted() {
+ if (! isSuccess())
+ super.handleCompleted();
+ else {
+ IModuleDMContext[] processModules = getData();
+
+ CountingRequestMonitor bpRemovedCRM = new CountingRequestMonitor(getExecutor(), rm) {
+
+ @Override
+ protected void handleCompleted() {
+ if (! isSuccess()) {
+ super.handleCompleted();
+ return;
+ }
+
+ // Now resume the process
+ ((ExecutionDMC)dmc).resume(new RequestMonitor(getExecutor(), rm){
+
+ @Override
+ protected void handleCompleted() {
+ if (!isSuccess())
+ super.handleCompleted();
+ else {
+ doDetachDebugger((ExecutionDMC)dmc, rm);
+ }
+ }
+ });
+ }
+ };
+
+ int bpTargetsDMCCnt = 0;
+ for (IModuleDMContext m : processModules) {
+ if (m instanceof IBreakpointsTargetDMContext) {
+ // In EDC, each Module is a BpTargetsDMC.
+ bpTargetsDMCCnt++;
+ bmService.stopTrackingBreakpoints((IBreakpointsTargetDMContext)m, bpRemovedCRM);
+ }
+ }
+ assert bpTargetsDMCCnt > 0;
+
+ bpRemovedCRM.setDoneCount(bpTargetsDMCCnt);
+ }
+ }});
+ }
+
+ /**
+ * ask debugger to do final detach: forget the process.
+ *
+ * @param dmc
+ * @param rm
+ */
+ protected void doDetachDebugger(final ExecutionDMC dmc, final RequestMonitor rm) {
+ // First detach agent so that the program won't die when the agent dies.
+ // Then detach host debugger.
+ //
+ Protocol.invokeLater(new Runnable() {
+
+ public void run() {
+ tcfProcessesService.getContext(dmc.getID(), new org.eclipse.tm.tcf.services.IProcesses.DoneGetContext() {
+
+ public void doneGetContext(IToken token, Exception error,
+ ProcessContext context) {
+ if (error != null) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Fail to get TCF context for process: " + dmc.getID(), error));
+ rm.done();
+ }
+ else {
+ context.detach(new org.eclipse.tm.tcf.services.IProcesses.DoneCommand() {
+
+ public void doneCommand(IToken token, Exception error) {
+ if (error != null)
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Fail to detach process \"" + dmc.getID() + "\" from TCF agent.", error));
+ else {
+ // everything ok, now detach from host debugger,
+ // which will shutdown the debug session if the process
+ // is the last one.
+ dmc.detach();
+ }
+ rm.done();
+ }
+ });
+ }
+ }
+ });
+
+ }});
+ }
+
+ public void getDebuggingContext(IThreadDMContext dmc, DataRequestMonitor<IDMContext> rm) {
+ rm.done();
+ }
+
+ public void getExecutionData(IThreadDMContext dmc, DataRequestMonitor<IThreadDMData> rm) {
+ if (dmc instanceof IEDCExecutionDMC)
+ rm.setData(new ExecutionDMData((ExecutionDMC) dmc));
+ rm.done();
+ }
+
+ public void getProcessesBeingDebugged(IDMContext dmc, DataRequestMonitor<IDMContext[]> rm) {
+ rm.setData(new IDMContext[0]);
+ DsfServicesTracker tracker = getServicesTracker();
+ if (tracker != null) {
+ RunControl runcontrol = tracker.getService(RunControl.class);
+ if (runcontrol != null) {
+ IDMContext[] processes = runcontrol.getRootDMC().getChildren();
+ rm.setData(processes);
+ }
+ }
+ rm.done();
+ }
+
+ public void getRunningProcesses(IDMContext dmc, DataRequestMonitor<IProcessDMContext[]> rm) {
+ rm.done();
+ }
+
+ public void isDebugNewProcessSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm) {
+ rm.done();
+ }
+
+ public void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm) {
+ rm.done();
+ }
+
+ public void isRunNewProcessSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm) {
+ rm.done();
+ }
+
+ public void runNewProcess(IDMContext dmc, String file, Map<String, Object> attributes,
+ DataRequestMonitor<IProcessDMContext> rm) {
+ rm.done();
+ }
+
+ public void terminate(IThreadDMContext thread, RequestMonitor requestMonitor) {
+ ExecutionDMC executionDmc = (ExecutionDMC) thread;
+ executionDmc.terminate(requestMonitor);
+ }
+
+ public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) {
+ rm.done();
+ }
+
+ // Event handler when a thread or a threadGroup exits
+ @DsfServiceEventHandler
+ public void eventDispatched(IExitedDMEvent e) {
+ }
+
+ public void eventReceived(Object output) {
+ }
+
+ public void tcfServiceReady(IService service) {
+ assert service instanceof org.eclipse.tm.tcf.services.IProcesses;
+ tcfProcessesService = (org.eclipse.tm.tcf.services.IProcesses) service;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IAddressExpressionEvaluator;
+import org.eclipse.cdt.debug.edc.IJumpToAddress;
+import org.eclipse.cdt.debug.edc.JumpToAddress;
+import org.eclipse.cdt.debug.edc.disassembler.IDisassembledInstruction;
+import org.eclipse.cdt.debug.edc.disassembler.IDisassembler;
+import org.eclipse.cdt.debug.edc.disassembler.IDisassembler.IDisassemblerOptions;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.formatter.FormatExtensionManager;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Breakpoints.BreakpointDMData;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.EDCAddressRange;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.ModuleDMC;
+import org.eclipse.cdt.debug.edc.internal.snapshot.Album;
+import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotUtils;
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.debug.edc.services.DMContext;
+import org.eclipse.cdt.debug.edc.services.Disassembly;
+import org.eclipse.cdt.debug.edc.services.IDSFServiceUsingTCF;
+import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCExecutionDMC;
+import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCModules;
+import org.eclipse.cdt.debug.edc.services.IEDCSymbols;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.debug.edc.services.Registers;
+import org.eclipse.cdt.debug.edc.services.Registers.RegisterDMC;
+import org.eclipse.cdt.debug.edc.services.Registers.RegisterGroupDMC;
+import org.eclipse.cdt.debug.edc.services.Stack;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.services.Stack.VariableDMC;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
+import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.IModuleLineEntryProvider;
+import org.eclipse.cdt.debug.edc.tcf.extension.ProtocolConstants;
+import org.eclipse.cdt.debug.edc.tcf.extension.ProtocolConstants.IModuleProperty;
+import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
+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.AbstractDMEvent;
+import org.eclipse.cdt.dsf.datamodel.DMContexts;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.IBreakpoints.IBreakpointsTargetDMContext;
+import org.eclipse.cdt.dsf.debug.service.ICachingService;
+import org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
+import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
+import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
+import org.eclipse.cdt.dsf.debug.service.IRunControl;
+import org.eclipse.cdt.dsf.debug.service.IRunControl2;
+import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext;
+import org.eclipse.cdt.dsf.debug.service.IStack;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMContext;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.model.MemoryByte;
+import org.eclipse.tm.tcf.protocol.IService;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IRunControl.DoneCommand;
+import org.eclipse.tm.tcf.services.IRunControl.RunControlContext;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class RunControl extends AbstractEDCService implements IRunControl2, ICachingService, ISnapshotContributor,
+ IDSFServiceUsingTCF {
+
+ public static final String EXECUTION_CONTEXT = "execution_context";
+ public static final String EXECUTION_CONTEXT_REGISTERS = "execution_context_registers";
+ public static final String EXECUTION_CONTEXT_MODULES = "execution_context_modules";
+ public static final String EXECUTION_CONTEXT_FRAMES = "execution_context_frames";
+ /**
+ * Context property names. Properties that are optional but have default
+ * implicit values are indicated below
+ */
+ public static final String
+ PROP_PARENT_ID = "ParentID",
+ PROP_IS_CONTAINER = "IsContainer", // default = true
+ PROP_HAS_STATE = "HasState",
+ PROP_CAN_RESUME = "CanResume", // default = true
+ PROP_CAN_COUNT = "CanCount",
+ PROP_CAN_SUSPEND = "CanSuspend", // default = true
+ PROP_CAN_TERMINATE = "CanTerminate", // default = false
+ PROP_IS_SUSPENDED = "State", // default = false
+ PROP_MESSAGE = "Message",
+ PROP_SUSPEND_PC = "SuspendPC",
+ PROP_DISABLE_STEPPING = "DisableStepping";
+
+ /*
+ * See where this is used for more.
+ */
+ private static final int RESUME_NOTIFICATION_DELAY = 1000; // milliseconds
+
+ // Whether module is being loaded (if true) or unloaded (if false)
+
+ public abstract static class DMCSuspendedEvent extends AbstractDMEvent<IExecutionDMContext> {
+
+ private final StateChangeReason reason;
+ private final Map<String, Object> params;
+
+ public DMCSuspendedEvent(IExecutionDMContext dmc, StateChangeReason reason, Map<String, Object> params) {
+ super(dmc);
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { dmc, reason, params })); }
+ this.reason = reason;
+ this.params = params;
+ }
+
+ public StateChangeReason getReason() {
+ return reason;
+ }
+
+ public Map<String, Object> getParams() {
+ return params;
+ }
+
+ }
+
+ public static class SuspendedEvent extends DMCSuspendedEvent implements ISuspendedDMEvent {
+
+ public SuspendedEvent(IExecutionDMContext dmc,
+ StateChangeReason reason, Map<String, Object> params) {
+ super(dmc, reason, params);
+ }
+
+ }
+
+ public static class ContainerSuspendedEvent extends DMCSuspendedEvent implements IContainerSuspendedDMEvent {
+
+ public ContainerSuspendedEvent(IExecutionDMContext dmc,
+ StateChangeReason reason, Map<String, Object> params) {
+ super(dmc, reason, params);
+ }
+
+ public IExecutionDMContext[] getTriggeringContexts() {
+ return new IExecutionDMContext[]{getDMContext()};
+ }
+ }
+
+ public abstract static class DMCResumedEvent extends AbstractDMEvent<IExecutionDMContext> {
+
+ public DMCResumedEvent(IExecutionDMContext dmc) {
+ super(dmc);
+ }
+
+ public StateChangeReason getReason() {
+ return StateChangeReason.USER_REQUEST;
+ }
+ }
+
+ public static class ResumedEvent extends DMCResumedEvent implements IResumedDMEvent {
+
+ public ResumedEvent(IExecutionDMContext dmc) {
+ super(dmc);
+ }
+ }
+
+ public static class ContainerResumedEvent extends DMCResumedEvent implements IContainerResumedDMEvent {
+
+ public ContainerResumedEvent(IExecutionDMContext dmc) {
+ super(dmc);
+ }
+
+ public IExecutionDMContext[] getTriggeringContexts() {
+ return new IExecutionDMContext[]{getDMContext()};
+ }
+}
+
+ private static StateChangeReason toDsfStateChangeReason(String tcfReason) {
+ if (tcfReason == null)
+ return StateChangeReason.UNKNOWN;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_USER_REQUEST))
+ return StateChangeReason.USER_REQUEST;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_STEP))
+ return StateChangeReason.STEP;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_BREAKPOINT))
+ return StateChangeReason.BREAKPOINT;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_EXCEPTION))
+ return StateChangeReason.EXCEPTION;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_CONTAINER))
+ return StateChangeReason.CONTAINER;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_WATCHPOINT))
+ return StateChangeReason.WATCHPOINT;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_SIGNAL))
+ return StateChangeReason.SIGNAL;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_SHAREDLIB))
+ return StateChangeReason.SHAREDLIB;
+ if (tcfReason.equals(org.eclipse.tm.tcf.services.IRunControl.REASON_ERROR))
+ return StateChangeReason.ERROR;
+ return StateChangeReason.UNKNOWN;
+ }
+
+ @Immutable
+ private static class ExecutionData implements IExecutionDMData2 {
+ private final StateChangeReason reason;
+ private final String details;
+
+ ExecutionData(StateChangeReason reason, String details) {
+ this.reason = reason;
+ this.details = details;
+ }
+
+ public StateChangeReason getStateChangeReason() {
+ return reason;
+ }
+
+ public String getDetails() {
+ return details;
+ }
+ }
+
+ public abstract class ExecutionDMC extends DMContext implements IExecutionDMContext,
+ ISnapshotContributor, IEDCExecutionDMC {
+
+ private final List<ExecutionDMC> children = Collections.synchronizedList(new ArrayList<ExecutionDMC>());
+ private StateChangeReason stateChangeReason = StateChangeReason.UNKNOWN;
+ private String stateChangeDetails = null;
+ private final RunControlContext tcfContext;
+ private final ExecutionDMC parentExecutionDMC;
+ private String latestPC = null;
+ private RequestMonitor steppingRM = null;
+ private boolean isStepping = false;
+
+ // See where this is used for more.
+ private int countOfScheduledNotifications = 0 ;
+
+ /**
+ * Whether user chose to "terminate" or "disconnect" the context.
+ */
+ private boolean isTerminatingThanDisconnecting = false;
+
+ private List<EDCAddressRange> disabledRanges = Collections.synchronizedList(new ArrayList<EDCAddressRange>());
+ private boolean suspendEventsEnabled = true;
+
+ public ExecutionDMC(ExecutionDMC parent, Map<String, Object> props, RunControlContext tcfContext) {
+ super(RunControl.this, parent == null ? new IDMContext[0] : new IDMContext[] { parent }, props);
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { parent, properties })); }
+ this.parentExecutionDMC = parent;
+ this.tcfContext = tcfContext;
+ if (props != null) {
+ dmcsByID.put(getID(), this);
+ }
+ if (parent != null)
+ parent.addChild(this);
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ private void addChild(ExecutionDMC executionDMC) {
+ synchronized (children) {
+ children.add(executionDMC);
+ }
+ }
+
+ private void removeChild(IEDCExecutionDMC executionDMC) {
+ synchronized (children) {
+ children.remove(executionDMC);
+ }
+ }
+
+ public ExecutionDMC[] getChildren() {
+ synchronized (children) {
+ return children.toArray(new ExecutionDMC[children.size()]);
+ }
+ }
+
+ public boolean wantFocusInUI() {
+ Boolean wantFocus = (Boolean)properties.get(ProtocolConstants.PROP_WANT_FOCUS_IN_UI);
+ if (wantFocus == null)
+ wantFocus = true; // default if unknown (not set by debug agent).
+ return wantFocus;
+ }
+
+ public abstract ExecutionDMC contextAdded(Map<String, Object> properties, RunControlContext tcfContext);
+
+ public abstract boolean canDetach();
+
+ public abstract boolean canStep();
+
+ public void loadSnapshot(Element element) throws Exception {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(element)); }
+ NodeList ecElements = element.getElementsByTagName(EXECUTION_CONTEXT);
+ int numcontexts = ecElements.getLength();
+ for (int i = 0; i < numcontexts; i++) {
+ Element contextElement = (Element) ecElements.item(i);
+ if (contextElement.getParentNode().equals(element)) {
+ try {
+ Element propElement = (Element) contextElement.getElementsByTagName(SnapshotUtils.PROPERTIES)
+ .item(0);
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ SnapshotUtils.initializeFromXML(propElement, properties);
+ ExecutionDMC exeDMC = contextAdded(properties, null);
+ exeDMC.loadSnapshot(contextElement);
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor)throws Exception {
+ Element contextElement = document.createElement(EXECUTION_CONTEXT);
+ contextElement.setAttribute(PROP_ID, this.getID());
+
+ Element propsElement = SnapshotUtils.makeXMLFromProperties(document, getProperties());
+ contextElement.appendChild(propsElement);
+
+ ExecutionDMC[] dmcs = getChildren();
+ SubMonitor progress = SubMonitor.convert(monitor, dmcs.length * 1000);
+ progress.subTask(getName());
+
+ for (ExecutionDMC executionDMC : dmcs) {
+ Element dmcElement = executionDMC.takeSnapshot(album, document, progress.newChild(1000));
+ contextElement.appendChild(dmcElement);
+ }
+
+ return contextElement;
+ }
+
+ public boolean isSuspended() {
+ synchronized (properties) {
+ return RunControl.getProperty(properties, PROP_IS_SUSPENDED, false);
+ }
+ }
+
+ public StateChangeReason getStateChangeReason() {
+ return stateChangeReason;
+ }
+
+ public String getStateChangeDetails() {
+ return stateChangeDetails;
+ }
+
+ public void setIsSuspended(boolean isSuspended) {
+ synchronized (properties) {
+ properties.put(PROP_IS_SUSPENDED, isSuspended);
+ }
+ if (getParent() != null)
+ getParent().childIsSuspended(isSuspended);
+ }
+
+ private void childIsSuspended(boolean isSuspended) {
+ if (isSuspended) {
+ setIsSuspended(true);
+ } else {
+ boolean anySuspended = false;
+ for (ExecutionDMC childDMC : getChildren()) {
+ if (childDMC.isSuspended()) {
+ anySuspended = true;
+ break;
+ }
+ }
+ if (!anySuspended)
+ setIsSuspended(false);
+ }
+ }
+
+ protected void contextException(String msg) {
+ assert getExecutor().isInExecutorThread();
+
+ setIsSuspended(true);
+ synchronized (properties) {
+ properties.put(PROP_MESSAGE, msg);
+ }
+ stateChangeReason = StateChangeReason.EXCEPTION;
+ getSession().dispatchEvent(
+ createSuspendedEvent(StateChangeReason.EXCEPTION, new HashMap<String, Object>()),
+ RunControl.this.getProperties());
+ }
+
+ protected void contextSuspended(String pc, String reason, final Map<String, Object> params) {
+ assert getExecutor().isInExecutorThread();
+
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(new Object[] { pc, reason, params })); }
+ if (pc != null) {
+ // the PC from TCF agent is decimal string.
+ // convert it to hex string.
+ pc = Long.toHexString(Long.parseLong(pc));
+ }
+
+ latestPC = pc;
+
+ setIsSuspended(true);
+ synchronized (properties) {
+ properties.put(PROP_MESSAGE, reason);
+ properties.put(PROP_SUSPEND_PC, pc);
+ }
+ stateChangeReason = toDsfStateChangeReason(reason);
+
+ if (stateChangeReason == StateChangeReason.SHAREDLIB) {
+ handleModuleEvent(this, params);
+ } else {
+
+ properties.put(PROP_DISABLE_STEPPING, params.get(ProtocolConstants.PROP_DISABLE_STEPPING));
+ properties.put(ProtocolConstants.PROP_WANT_FOCUS_IN_UI, params.get(ProtocolConstants.PROP_WANT_FOCUS_IN_UI));
+
+ stateChangeDetails = (String) params.get(ProtocolConstants.PROP_SUSPEND_DETAIL);
+
+ // TODO This is not what the stateChangeDetails is for, we need an extended thread description
+ // and is "foreground" really the right term?
+
+ // Show the context is foreground one, if possible.
+ //
+ Boolean isForeground = (Boolean)params.get(ProtocolConstants.PROP_IS_FOREGROUND);
+ if (isForeground != null)
+ stateChangeDetails += isForeground ? " [foreground]" : "";
+
+ final ExecutionDMC dmc = this;
+
+ final DataRequestMonitor<Boolean> preprocessDrm = new DataRequestMonitor<Boolean>(getExecutor(), null) {
+ @Override
+ protected void handleCompleted() {
+ Boolean honorSuspend = getData();
+
+ if (honorSuspend!=null && honorSuspend) { // do suspend
+
+ // All the following must be done in DSF dispatch
+ // thread to ensure data integrity.
+
+ // Mark done of the single step RM, if any pending.
+ if (steppingRM != null) {
+ steppingRM.done();
+ steppingRM = null;
+ }
+
+ // Mark any stepping as done.
+ setStepping(false);
+
+ // Remove temporary breakpoints set by stepping.
+ // Note we don't want to do this on a sharedLibrary
+ // event as otherwise
+ // stepping will be screwed up by that event.
+ //
+ Breakpoints bpService = getService(Breakpoints.class);
+ bpService.removeAllTempBreakpoints(new RequestMonitor(getExecutor(), null));
+
+ // check to see if we are in a disabled range
+ IAddress pcAddress = new Addr64(dmc.getPC(), 16);
+ EDCAddressRange disabledRange = dmc.getDisabledRange(pcAddress);
+ if (disabledRange != null)
+ {
+ stepAddressRange(dmc, false, pcAddress, disabledRange.getEndAddress(), new RequestMonitor(getExecutor(), null));
+ }
+ else
+ {
+ // Only after completion of those preprocessing do
+ // we fire the event.
+ if (dmc.suspendEventsEnabled())
+ getSession().dispatchEvent(dmc.createSuspendedEvent(stateChangeReason, params),
+ RunControl.this.getProperties());
+ }
+ dmc.clearDisabledRanges();
+ } else {
+ // ignore suspend if, say, breakpoint condition is not met.
+ RunControl.this.resume(dmc, new RequestMonitor(getExecutor(), null));
+ }
+ }
+ };
+
+ preprocessOnSuspend(dmc, latestPC, preprocessDrm);
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ protected boolean suspendEventsEnabled() {
+ return suspendEventsEnabled ;
+ }
+
+ protected void setSuspendEventsEnabled(boolean enabled)
+ {
+ suspendEventsEnabled = enabled;
+ }
+
+ protected void clearDisabledRanges() {
+ disabledRanges.clear();
+ }
+
+ /**
+ * handle module load event and unload event. A module is an executable file
+ * or a library (e.g. DLL or shared lib).
+ *
+ * @param dmc
+ * @param moduleProperties
+ */
+ private void handleModuleEvent(final IEDCExecutionDMC dmc, final Map<String, Object> moduleProperties) {
+ // The following needs be done in DSF dispatch thread.
+ getSession().getExecutor().execute(new Runnable() {
+ public void run() {
+ // based on properties, either load or unload the module
+ boolean loaded = true;
+ Object loadedValue = moduleProperties.get(IModuleProperty.PROP_MODULE_LOADED);
+ if (loadedValue != null) {
+ if (loadedValue instanceof Boolean)
+ loaded = (Boolean) loadedValue;
+ }
+
+ if (loaded)
+ handleModuleLoadedEvent(dmc, moduleProperties);
+ else
+ handleModuleUnloadedEvent(dmc, moduleProperties);
+ }
+ });
+ }
+
+ public Boolean canTerminate() {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null); }
+ boolean result = false;
+ synchronized (properties) {
+ result = RunControl.getProperty(properties, PROP_CAN_TERMINATE, result);
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null, result); }
+ return result;
+ }
+
+ /**
+ * Resume the context.
+ *
+ * @param rm
+ * this is marked done as long as the resume command
+ * succeeds.
+ */
+ public boolean supportsStepMode(StepType type) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this)); }
+
+ int mode = 0;
+ switch (type) {
+ case STEP_OVER:
+ mode = org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER_RANGE;
+ break;
+ case STEP_INTO:
+ mode = org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO_RANGE;
+ break;
+ case STEP_RETURN:
+ mode = org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OUT;
+ break;
+ case INSTRUCTION_STEP_OVER:
+ mode = org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER;
+ break;
+ case INSTRUCTION_STEP_INTO:
+ mode = org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO;
+ break;
+ }
+
+ if (hasTCFContext())
+ return getTCFContext().canResume(mode);
+ else
+ return false;
+ }
+
+ /**
+ * Resume the context.
+ *
+ * @param rm
+ * this is marked done as long as the resume command
+ * succeeds.
+ */
+ public void resume(final RequestMonitor rm) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this)); }
+
+ flushCache(this);
+
+ if (hasTCFContext()) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ getTCFContext()
+ .resume(org.eclipse.tm.tcf.services.IRunControl.RM_RESUME,
+ 0, new DoneCommand() {
+
+ public void doneCommand(
+ IToken token,
+ final Exception error) {
+ getExecutor().execute(
+ new Runnable() {
+ public void run() {
+ if (error == null) {
+ contextResumed(true);
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) {
+ EDCTrace.getTrace().trace(null, "Resume command succeeded.");
+ }
+ } else {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) {
+ EDCTrace.getTrace().trace(null, "Resume command failed.");
+ }
+ rm.setStatus(new Status(
+ IStatus.ERROR,
+ EDCDebugger.PLUGIN_ID,
+ REQUEST_FAILED,
+ "Resume failed.",
+ null));
+ }
+ rm.done();
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * Resume the context but the request monitor is only marked done when
+ * the context is suspended. (vs. regular resume()). <br>
+ * Note this method does not wait for suspended-event.
+ *
+ * @param rm
+ */
+ protected void resumeForStepping(final RequestMonitor rm) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this)); }
+
+ setStepping(true);
+
+ flushCache(this);
+
+ if (hasTCFContext()) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ getTCFContext().resume(org.eclipse.tm.tcf.services.IRunControl.RM_RESUME,
+ 0, new DoneCommand() {
+
+ public void doneCommand(
+ IToken token,
+ final Exception error) {
+ // do this in DSF executor thread.
+ getExecutor().execute(
+ new Runnable() {
+ public void run() {
+ handleTCFResumeDoneForStepping(
+ "ResumeForStepping",
+ error,
+ rm);
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ private void handleTCFResumeDoneForStepping(String command, Exception tcfError, RequestMonitor rm) {
+ assert getExecutor().isInExecutorThread();
+
+ String msg = command;
+ if (tcfError == null) {
+ msg += " succeeded.";
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().trace(null, msg); }
+ contextResumed(false);
+
+ // we'll mark it as done when we get next
+ // suspend event.
+ assert steppingRM == null;
+ steppingRM = rm;
+ } else {
+ msg += " failed.";
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().trace(null, msg); }
+
+ setStepping(false);
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, msg, tcfError));
+ rm.done();
+ }
+ }
+
+ public void suspend(final RequestMonitor requestMonitor) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this)); }
+ if (isSnapshot())
+ {
+ Album.getAlbumBySession(getSession().getId()).stopPlayingSnapshots();
+ }
+ else
+ if (hasTCFContext()) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ getTCFContext().suspend(new DoneCommand() {
+
+ public void doneCommand(IToken token,
+ Exception error) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) {
+ EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this));
+ }
+ requestMonitor.done();
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) {
+ EDCTrace.getTrace().traceExit(null);
+ }
+ }
+ });
+ }
+ });
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ public void terminate(final RequestMonitor requestMonitor) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this)); }
+
+ isTerminatingThanDisconnecting = true;
+
+ if (hasTCFContext()) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ getTCFContext().terminate(new DoneCommand() {
+
+ public void doneCommand(IToken token, Exception error) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this)); }
+ if (error != null) {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID,
+ "terminate() failed.", error));
+ }
+
+ requestMonitor.done();
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+ });
+ }
+ });
+ } else {
+ // Snapshots, for e.g., don't have a TCF RunControlContext, so just remove all the contexts recursively
+ detachAllContexts();
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ protected ExecutionDMC getParent() {
+ return parentExecutionDMC;
+ }
+
+ /**
+ * get latest PC register value of the context.
+ *
+ * @return hex string of the PC value.
+ */
+ public String getPC() {
+ return latestPC;
+ }
+
+ /**
+ * Change cached PC value.
+ * This is only supposed to be used for move-to-line & resume-from-line commands.
+ *
+ * @param pc
+ */
+ private void setPC(String pc) {
+ latestPC = pc;
+ }
+
+ /**
+ * Detach debugger from this context and all its children.
+ */
+ public void detach(){
+ isTerminatingThanDisconnecting = false;
+ /**
+ * agent side detaching is invoked by Processes service.
+ * Here we just purge the context.
+ */
+ purgeFromDebugger();
+ }
+
+ /**
+ * Purge this context and all its children and grand-children
+ * from debugger UI and internal data cache.
+ */
+ public void purgeFromDebugger(){
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null); }
+
+ for (ExecutionDMC e : getChildren())
+ // recursively forget children first
+ e.purgeFromDebugger();
+
+ ExecutionDMC parent = getParent();
+ if (parent != null)
+ parent.removeChild(this);
+
+ getSession().dispatchEvent(new ExitedEvent(this, isTerminatingThanDisconnecting), RunControl.this.getProperties());
+
+ if (getRootDMC().getChildren().length == 0)
+ // no more contexts under debug, fire exitedEvent for the rootDMC which
+ // will trigger shutdown of the debug session.
+ // See EDCLaunch.eventDispatched(IExitedDMEvent e).
+ // Whether the root is terminated or disconnected depends on whether
+ // the last context is terminated or disconnected.
+ getSession().dispatchEvent(new ExitedEvent(getRootDMC(), isTerminatingThanDisconnecting), RunControl.this.getProperties());
+
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * Recursively marks all execution contexts as resumed
+ * @param dmc
+ */
+ public void resumeAll(){
+ contextResumed(true);
+ for (ExecutionDMC e : getChildren()){
+ e.resumeAll();
+ }
+ }
+
+ protected void contextResumed(boolean fireResumeEventNow) {
+ assert getExecutor().isInExecutorThread();
+
+ if (children.size() > 0) {
+ // If it has kids (e.g. a process has threads), only need
+ // to mark the kids as resumed.
+ for (ExecutionDMC e : children){
+ e.contextResumed(fireResumeEventNow);
+ }
+ return;
+ }
+
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { this, fireResumeEventNow })); }
+
+ setIsSuspended(false);
+
+ if (fireResumeEventNow)
+ getSession().dispatchEvent(this.createResumedEvent(), RunControl.this.getProperties());
+ else
+ scheduleResumeEvent();
+
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * Schedule a task to run after some time which will
+ * notify platform that the context is running.
+ */
+ private void scheduleResumeEvent() {
+ countOfScheduledNotifications++;
+
+ final ExecutionDMC dmc = this;
+
+ Runnable notifyPlatformTask = new Runnable() {
+ public void run() {
+ /*
+ * Notify platform the context is running.
+ *
+ * But don't do that if another such task is scheduled
+ * (namely current stepping is done within the RESUME_NOTIFICATION_DELAY and
+ * another stepping/resume is underway).
+ */
+ countOfScheduledNotifications--;
+ if (countOfScheduledNotifications == 0 && !isSuspended())
+ getSession().dispatchEvent(dmc.createResumedEvent(), RunControl.this.getProperties());
+ }};
+
+ getExecutor().schedule(notifyPlatformTask, RESUME_NOTIFICATION_DELAY, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Execute a single instruction. Note the "rm" is marked done() only
+ * when we get the suspend event, not when we successfully send the
+ * command to TCF agent.
+ *
+ * @param rm
+ */
+ protected void singleStep(final boolean stepInto, final RequestMonitor rm) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this.getName())); }
+
+ setStepping(true);
+
+ flushCache(this);
+
+ if (hasTCFContext())
+ {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ int mode = stepInto ? org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO
+ : org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER;
+ getTCFContext().resume(mode, 1, new DoneCommand() {
+ public void doneCommand(IToken token, final Exception error) {
+ // do this in DSF executor thread.
+ getExecutor().execute(new Runnable() {
+ public void run() {
+ handleTCFResumeDoneForStepping("SingleStep", error, rm);
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * Step out of the current function. Note the "rm" is marked done() only
+ * when we get the suspend event, not when we successfully send the
+ * command to TCF agent.
+ *
+ * @param rm
+ */
+ protected void stepOut(final RequestMonitor rm) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this.getName())); }
+
+ setStepping(true);
+
+ flushCache(this);
+
+ if (hasTCFContext()) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ getTCFContext()
+ .resume(org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OUT,
+ 0, new DoneCommand() {
+
+ public void doneCommand(
+ IToken token,
+ final Exception error) {
+ // do this in DSF executor thread.
+ getExecutor().execute(
+ new Runnable() {
+ public void run() {
+ handleTCFResumeDoneForStepping(
+ "StepOut",
+ error,
+ rm);
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ protected void stepRange(final boolean stepInto, final IAddress rangeStart, final IAddress rangeEnd,
+ final RequestMonitor rm) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(this.getName())); }
+
+ setStepping(true);
+
+ flushCache(this);
+
+ if (hasTCFContext()) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ int mode = stepInto ? org.eclipse.tm.tcf.services.IRunControl.RM_STEP_INTO_RANGE
+ : org.eclipse.tm.tcf.services.IRunControl.RM_STEP_OVER_RANGE;
+ Map<String, Object> params = new HashMap<String, Object>();
+ params.put("RANGE_START", rangeStart.getValue());
+ params.put("RANGE_END", rangeEnd.getValue());
+
+ getTCFContext().resume(mode, 0, params, new DoneCommand() {
+
+ public void doneCommand(IToken token,
+ final Exception error) {
+ // do this in DSF executor thread.
+ getExecutor().execute(new Runnable() {
+ public void run() {
+ handleTCFResumeDoneForStepping(
+ "StepRange", error, rm);
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+
+ /**
+ * set whether debugger is stepping in the context.
+ *
+ * @param isStepping
+ */
+ public void setStepping(boolean isStepping) {
+ this.isStepping = isStepping;
+ }
+
+ /**
+ * @return whether debugger is stepping the context.
+ */
+ public boolean isStepping() {
+ return isStepping;
+ }
+
+ public void addDisabledRange(IAddress lowAddress, IAddress highAddress) {
+ disabledRanges.add(new EDCAddressRange(lowAddress, highAddress));
+ }
+
+ public EDCAddressRange getDisabledRange(IAddress address) {
+ for (EDCAddressRange dRange : disabledRanges) {
+ if (dRange.contains(address))
+ return dRange;
+ }
+ return null;
+ }
+
+ protected DMCSuspendedEvent createSuspendedEvent(StateChangeReason reason, Map<String, Object> properties) {
+ return new SuspendedEvent(this, reason, properties);
+ }
+
+ protected DMCResumedEvent createResumedEvent() {
+ return new ResumedEvent(this);
+ }
+
+ public RunControlContext getTCFContext() {
+ return tcfContext;
+ }
+
+ public boolean hasTCFContext() {
+ return tcfContext != null;
+ }
+
+ }
+
+ public class ProcessExecutionDMC extends ExecutionDMC implements IContainerDMContext, IProcessDMContext,
+ ISymbolDMContext, IBreakpointsTargetDMContext, IDisassemblyDMContext {
+
+ public ProcessExecutionDMC(ExecutionDMC parent, Map<String, Object> properties, RunControlContext tcfContext) {
+ super(parent, properties, tcfContext);
+ }
+
+ @Override
+ public ExecutionDMC contextAdded(Map<String, Object> properties, RunControlContext tcfContext) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(properties)); }
+ ThreadExecutionDMC newDMC = new ThreadExecutionDMC(this, properties, tcfContext);
+ getSession().dispatchEvent(new StartedEvent(newDMC), RunControl.this.getProperties());
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(newDMC)); }
+ return newDMC;
+ }
+
+ public ISymbolDMContext getSymbolDMContext() {
+ return this;
+ }
+
+ @Override
+ public void loadSnapshot(Element element) throws Exception {
+ // load modules first, since this loads a stack which must consult modules and symbolics
+ Modules modulesService = getService(Modules.class);
+ modulesService.loadModulesForContext(this, element);
+ super.loadSnapshot(element);
+ }
+
+ @Override
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor)throws Exception {
+ SubMonitor progress = SubMonitor.convert(monitor, 1000);
+ progress.subTask(getName());
+ Element contextElement = super.takeSnapshot(album, document, progress.newChild(500));
+ Element modulesElement = document.createElement(EXECUTION_CONTEXT_MODULES);
+ Modules modulesService = getService(Modules.class);
+
+ IModuleDMContext[] modules = modulesService.getModulesForContext(this.getID());
+ SubMonitor modulesMonitor = progress.newChild(500);
+ modulesMonitor.setWorkRemaining(modules.length * 1000);
+ modulesMonitor.subTask("Modules");
+ for (IModuleDMContext moduleContext : modules) {
+ ModuleDMC moduleDMC = (ModuleDMC) moduleContext;
+ modulesElement.appendChild(moduleDMC.takeSnapshot(album, document, modulesMonitor.newChild(1000)));
+ }
+
+ contextElement.appendChild(modulesElement);
+ return contextElement;
+ }
+
+ @Override
+ public boolean canDetach() {
+ // Can detach from a process unless we're part of a snapshot.
+ return hasTCFContext();
+ }
+
+ @Override
+ public boolean canStep() {
+ // can't step a process.
+ return false;
+ }
+ }
+
+ public class ThreadExecutionDMC extends ExecutionDMC implements IThreadDMContext, IDisassemblyDMContext {
+
+ public ThreadExecutionDMC(ExecutionDMC parent, Map<String, Object> properties, RunControlContext tcfContext) {
+ super(parent, properties, tcfContext);
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) {
+ EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { parent, properties }));
+ EDCTrace.getTrace().traceExit(null);
+ }
+ }
+
+ public ISymbolDMContext getSymbolDMContext() {
+ return DMContexts.getAncestorOfType(this, ISymbolDMContext.class);
+ }
+
+ @Override
+ public void loadSnapshot(Element element) throws Exception {
+ super.loadSnapshot(element);
+ Registers regService = getService(Registers.class);
+ regService.loadGroupsForContext(this, element);
+
+ Stack stackService = getService(Stack.class);
+ NodeList frameElements = element.getElementsByTagName(EXECUTION_CONTEXT_FRAMES);
+ for (int i = 0; i < frameElements.getLength(); i++) {
+ Element frameElement = (Element) frameElements.item(i);
+ stackService.loadFramesForContext(this, frameElement);
+ }
+
+ getSession().dispatchEvent(
+ createSuspendedEvent(StateChangeReason.EXCEPTION, new HashMap<String, Object>()),
+ RunControl.this.getProperties());
+ }
+
+ @Override
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor)throws Exception {
+ SubMonitor progress = SubMonitor.convert(monitor, 1000);
+ progress.subTask(getName());
+ Element contextElement = super.takeSnapshot(album, document, progress.newChild(100));
+ Element registersElement = document.createElement(EXECUTION_CONTEXT_REGISTERS);
+ Registers regService = getService(Registers.class);
+
+ IRegisterGroupDMContext[] regGroups = regService.getGroupsForContext(this);
+ SubMonitor registerMonitor = progress.newChild(300);
+ registerMonitor.setWorkRemaining(regGroups.length * 1000);
+ registerMonitor.subTask("Registers");
+ for (IRegisterGroupDMContext registerGroupDMContext : regGroups) {
+ RegisterGroupDMC regDMC = (RegisterGroupDMC) registerGroupDMContext;
+ registersElement.appendChild(regDMC.takeSnapshot(album, document, registerMonitor.newChild(1000)));
+ }
+
+ contextElement.appendChild(registersElement);
+
+ Element framesElement = document.createElement(EXECUTION_CONTEXT_FRAMES);
+ Stack stackService = getService(Stack.class);
+ Expressions expressionsService = getService(Expressions.class);
+
+ IFrameDMContext[] frames = stackService.getFramesForDMC(this, 0, IStack.ALL_FRAMES);
+ SubMonitor framesMonitor = progress.newChild(600);
+ framesMonitor.setWorkRemaining(frames.length * 2000);
+ framesMonitor.subTask("Stack Frames");
+ for (IFrameDMContext frameDMContext : frames) {
+ StackFrameDMC frameDMC = (StackFrameDMC) frameDMContext;
+
+ // Get the local variables for each frame
+ IVariableDMContext[] variables = frameDMC.getLocals();
+ SubMonitor variablesMonitor = framesMonitor.newChild(1000);
+ variablesMonitor.setWorkRemaining(variables.length * 10);
+ variablesMonitor.subTask("Variables");
+ for (IVariableDMContext iVariableDMContext : variables) {
+ VariableDMC varDMC = (VariableDMC) iVariableDMContext;
+ IExpressionDMContext expression = expressionsService.createExpression(frameDMContext, varDMC.getName());
+ boolean wasEnabled = FormatExtensionManager.instance().isEnabled();
+ FormatExtensionManager.instance().setEnabled(true);
+ expressionsService.loadExpressionValues(expression, Album.getVariableCaptureDepth());
+ FormatExtensionManager.instance().setEnabled(wasEnabled);
+ variablesMonitor.worked(10);
+ variablesMonitor.subTask("Variables - " + varDMC.getName());
+ }
+
+ framesElement.appendChild(frameDMC.takeSnapshot(album, document, framesMonitor.newChild(1000)));
+ }
+ contextElement.appendChild(framesElement);
+
+ return contextElement;
+ }
+
+ @Override
+ public ExecutionDMC contextAdded(Map<String, Object> properties, RunControlContext tcfContext) {
+ assert (false);
+ return null;
+ }
+
+ @Override
+ public boolean canDetach() {
+ // Cannot detach from a thread.
+ return false;
+ }
+
+ @Override
+ public boolean canStep() {
+ if (isSuspended()) {
+ synchronized (properties) {
+ return !RunControl.getProperty(properties, PROP_DISABLE_STEPPING, false);
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Context representing a program running on a bare device without OS, which
+ * can also be the boot-up "process" of an OS.
+ * <p>
+ * It's like a thread context as it has its registers and stack frames, but
+ * also like a process as it has modules associated with it. Currently we
+ * set it as an IProcessDMContext so that it appears as a ContainerVMNode in
+ * debug view. See LaunchVMProvider for more. Also it's treated like a
+ * process in
+ * {@link Processes#getProcessesBeingDebugged(IDMContext, DataRequestMonitor)}
+ */
+ public class BareDeviceExecutionDMC extends ThreadExecutionDMC
+ implements IProcessDMContext, ISymbolDMContext, IBreakpointsTargetDMContext {
+
+ public BareDeviceExecutionDMC(ExecutionDMC parent,
+ Map<String, Object> properties, RunControlContext tcfContext) {
+ super(parent, properties, tcfContext);
+ assert !RunControl.getProperty(properties, PROP_IS_CONTAINER, true);
+ }
+
+ @Override
+ protected DMCSuspendedEvent createSuspendedEvent(StateChangeReason reason, Map<String, Object> properties) {
+ return new ContainerSuspendedEvent(this, reason, properties);
+ }
+
+ @Override
+ protected DMCResumedEvent createResumedEvent() {
+ return new ContainerResumedEvent(this);
+ }
+
+ @Override
+ public boolean canDetach() {
+ return true;
+ }
+
+ }
+
+ public class RootExecutionDMC extends ExecutionDMC implements ISourceLookupDMContext {
+
+ public RootExecutionDMC(Map<String, Object> props) {
+ super(null, props, null);
+ }
+
+ @Override
+ public ExecutionDMC contextAdded(Map<String, Object> properties, RunControlContext tcfContext) {
+ ExecutionDMC newDMC;
+ // If the new context being added under root is a container context,
+ // we treat it as a Process, otherwise a bare device program context.
+ //
+ if (RunControl.getProperty(properties, PROP_IS_CONTAINER, true))
+ newDMC = new ProcessExecutionDMC(this, properties, tcfContext);
+ else
+ newDMC = new BareDeviceExecutionDMC(this, properties, tcfContext);
+
+ getSession().dispatchEvent(new StartedEvent(newDMC), RunControl.this.getProperties());
+ return newDMC;
+ }
+
+ public ISymbolDMContext getSymbolDMContext() {
+ return null;
+ }
+
+ @Override
+ public boolean canDetach() {
+ return false;
+ }
+
+ @Override
+ public boolean canStep() {
+ return false;
+ }
+ }
+
+ private static final String EXECUTION_CONTEXTS = "execution_contexts";
+
+ private org.eclipse.tm.tcf.services.IRunControl tcfRunService;
+ private RootExecutionDMC rootExecutionDMC;
+ private final Map<String, ExecutionDMC> dmcsByID = new HashMap<String, ExecutionDMC>();
+
+ public RunControl(DsfSession session) {
+ super(session, new String[] {
+ IRunControl.class.getName(),
+ IRunControl2.class.getName(),
+ RunControl.class.getName(),
+ ISnapshotContributor.class.getName() });
+ initializeRootExecutionDMC();
+ }
+
+ private void initializeRootExecutionDMC() {
+ HashMap<String, Object> props = new HashMap<String, Object>();
+ props.put(IEDCDMContext.PROP_ID, "root");
+ rootExecutionDMC = new RootExecutionDMC(props);
+ }
+
+ public void canResume(IExecutionDMContext context, DataRequestMonitor<Boolean> rm) {
+ rm.setData(((ExecutionDMC) context).isSuspended() ? Boolean.TRUE : Boolean.FALSE);
+ rm.done();
+ }
+
+ public void canStep(IExecutionDMContext context, StepType stepType, DataRequestMonitor<Boolean> rm) {
+ rm.setData(((ExecutionDMC) context).canStep() ? Boolean.TRUE : Boolean.FALSE);
+ rm.done();
+ }
+
+ public void canSuspend(IExecutionDMContext context, DataRequestMonitor<Boolean> rm) {
+ if (isSnapshot())
+ rm.setData(Album.getAlbumBySession(getSession().getId()).isPlayingSnapshots());
+ else
+ rm.setData(((ExecutionDMC) context).isSuspended() ? Boolean.FALSE : Boolean.TRUE);
+ rm.done();
+ }
+
+ public void getExecutionContexts(IContainerDMContext c, DataRequestMonitor<IExecutionDMContext[]> rm) {
+ if (c instanceof ProcessExecutionDMC) {
+ ProcessExecutionDMC edmc = (ProcessExecutionDMC) c;
+ IEDCExecutionDMC[] threads = edmc.getChildren();
+ IExecutionDMContext[] threadArray = new IExecutionDMContext[threads.length];
+ System.arraycopy(threads, 0, threadArray, 0, threads.length);
+ rm.setData(threadArray);
+ }
+ rm.done();
+ }
+
+ public void getExecutionData(IExecutionDMContext dmc, DataRequestMonitor<IExecutionDMData> rm) {
+ if (dmc instanceof ExecutionDMC) {
+ ExecutionDMC exedmc = (ExecutionDMC) dmc;
+ if (exedmc.isSuspended()) {
+ rm.setData(new ExecutionData(exedmc.getStateChangeReason(), exedmc.getStateChangeDetails()));
+ } else {
+ rm.setData(new ExecutionData(StateChangeReason.UNKNOWN, null));
+ }
+ } else
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE,
+ "Given context: " + dmc + " is not a recognized execution context.", null)); //$NON-NLS-1$ //$NON-NLS-2$
+ rm.done();
+ }
+
+ public boolean isStepping(IExecutionDMContext context) {
+ if (context instanceof ExecutionDMC) {
+ ExecutionDMC exedmc = (ExecutionDMC) context;
+ return exedmc.isStepping();
+ }
+ return false;
+ }
+
+ public boolean isSuspended(IExecutionDMContext context) {
+ if (context instanceof ExecutionDMC) {
+ ExecutionDMC exedmc = (ExecutionDMC) context;
+ return exedmc.isSuspended();
+ }
+ return false;
+ }
+
+ /**
+ * Preprocessing for suspend event. This is done before we broadcast the
+ * suspend event across the debugger. Here's what's done in the
+ * preprocessing by default: <br>
+ * 1. Adjust PC after control hits a software breakpoint where the PC
+ * points at the byte right after the breakpoint instruction. This is to
+ * move PC back to the address of the breakpoint instruction.<br>
+ * 2. If we stops at a breakpoint, evaluate condition of the breakpoint
+ * and determine if we should ignore the suspend event and resume or
+ * should honor the suspend event and sent it up the ladder.
+ * <p>
+ * Subclass can override this method to add their own special preprocessing,
+ * while calling super implementation to carry out the default common.
+ * <p>
+ * This must be called in DSF executor thread.
+ *
+ * @param pc
+ * program pointer value from the event, in the format of
+ * big-endian hex string. Can be null.
+ * @param drm
+ * DataRequestMonitor whose result indicates whether to honor
+ * the suspend.
+ */
+ protected void preprocessOnSuspend(final ExecutionDMC dmc, final String pc,
+ final DataRequestMonitor<Boolean> drm) {
+
+ assert getExecutor().isInExecutorThread();
+
+ asyncExec(new Runnable() {
+
+ public void run() {
+ try {
+ Breakpoints bpService = getService(Breakpoints.class);
+ Registers regService = getService(Registers.class);
+ String pcString = pc;
+
+ if (pc == null) {
+ // read PC register
+ pcString = regService.getRegisterValue(dmc, getTargetEnvironmentService().getPCRegisterID());
+ }
+
+ dmc.setPC(pcString);
+
+ // This check is to speed up handling of suspend due to
+ // other reasons such as "step".
+ // The TCF agents should always report the
+ // "stateChangeReason" as BREAKPOINT when a breakpoint
+ // is hit.
+
+ if (dmc.getStateChangeReason() != StateChangeReason.BREAKPOINT) {
+ drm.setData(true);
+ drm.done();
+ return;
+ }
+
+ if (!bpService.usesTCFBreakpointService()) {
+ // generic software breakpoint is used.
+ // We need to move PC back to the breakpoint
+ // instruction.
+ long pcValue;
+
+ pcValue = Long.valueOf(pcString, 16);
+ pcValue -= getTargetEnvironmentService()
+ .getBreakpointInstruction(dmc, new Addr64(pcString, 16)).length;
+ pcString = Long.toHexString(pcValue);
+
+ // Stopped but not due to breakpoint set by debugger.
+ // For instance, some Windows DLL has "int 3"
+ // instructions in it.
+ //
+ if (bpService.findBreakpoint(new Addr64(pcString, 16)) != null) {
+ // Now adjust PC register.
+ regService.writeRegister(dmc, getTargetEnvironmentService().getPCRegisterID(), pcString);
+ dmc.setPC(pcString);
+ }
+ }
+
+ // check if a conditional breakpoint (must be a user bp) is hit
+ //
+ BreakpointDMData bp = bpService.findUserBreakpoint(new Addr64(pcString, 16));
+ if (bp != null) {
+ // evaluate the condition
+ bpService.evaluateBreakpointCondition(dmc, bp, drm);
+ } else {
+ drm.setData(true);
+ drm.done();
+ }
+ } catch (CoreException e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
+ EDCDebugger.getMessageLogger().log(s);
+ drm.setStatus(s);
+ drm.done();
+ }
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(drm.getData())); }
+ }
+
+ }, drm);
+ }
+
+ public void resume(IExecutionDMContext context, final RequestMonitor rm) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(MessageFormat.format("resume context {0}", context))); }
+
+ if (!(context instanceof ExecutionDMC)) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, MessageFormat.format(
+ "The context [{0}] is not a recognized execution context.", context), null));
+ rm.done();
+ }
+
+ final ExecutionDMC dmc = (ExecutionDMC) context;
+
+ prepareToRun(dmc, new DataRequestMonitor<Boolean>(getExecutor(), rm) {
+
+ @Override
+ protected void handleSuccess() {
+ dmc.resume(rm);
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(MessageFormat.format("resume() done on context {0}", dmc))); }
+ }
+ });
+ }
+
+ /**
+ * Prepare for resuming or stepping by <br>
+ * - executing current instruction if PC is at a breakpoint.
+ *
+ * @param dmc
+ * - the execution context, usually a thread.
+ * @param drm
+ * - data request monitor which will contain boolean value on
+ * done indicating whether an instruction is executed during the
+ * preparation.
+ */
+ private void prepareToRun(final ExecutionDMC dmc, final DataRequestMonitor<Boolean> drm) {
+ // If there is breakpoint at current PC, remove it => Single step =>
+ // Restore it.
+
+ final Breakpoints bpService = getService(Breakpoints.class);
+ if (bpService.usesTCFBreakpointService()) {
+ // no need to do anything since the breakpoints service is expected to handle
+ // stepping past breakpoints since it's the one that sets them
+ drm.setData(false);
+ drm.done();
+ return;
+ }
+
+ String latestPC = dmc.getPC();
+
+ if (latestPC != null) {
+ final BreakpointDMData bp = bpService.findUserBreakpoint(new Addr64(latestPC, 16));
+ if (bp != null) {
+ bpService.disableBreakpoint(bp, new RequestMonitor(getExecutor(), drm) {
+
+ @Override
+ protected void handleSuccess() {
+ // Now step over the instruction
+ //
+ dmc.setSuspendEventsEnabled(false);
+ dmc.singleStep(true, new RequestMonitor(getExecutor(), drm) {
+ @Override
+ protected void handleCompleted() {
+ dmc.setSuspendEventsEnabled(true);
+ super.handleCompleted();
+ }
+
+ @Override
+ protected void handleSuccess() {
+ // At this point the single instruction
+ // execution should be done
+ // and the context being suspended.
+ //
+ drm.setData(true); // indicates an instruction
+ // is executed
+
+ // Now restore the breakpoint.
+ bpService.enableBreakpoint(bp, drm);
+ }
+ });
+ }
+ });
+ } else { // no breakpoint at PC
+ drm.setData(false);
+ drm.done();
+ }
+ } else {
+ drm.setData(false);
+ drm.done();
+ }
+ }
+
+ // This is a coarse timer on stepping for internal use.
+ // When needed, turn it on and watch output in console.
+ //
+ private static long steppingStartTime = 0;
+ public static boolean timeStepping() {
+ return false;
+ }
+
+ public static long getSteppingStartTime() {
+ return steppingStartTime;
+ }
+
+ public void step(final IExecutionDMContext context, final StepType outerStepType, final RequestMonitor rm) {
+
+ asyncExec(new Runnable() {
+
+ public void run() {
+ StepType stepType = outerStepType;
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(MessageFormat.format("{0} context {1}", stepType, context))); }
+
+ if (!(context instanceof ExecutionDMC)) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, MessageFormat.format(
+ "The context [{0}] is not a recognized execution context.", context), null));
+ rm.done();
+ }
+
+ if (timeStepping())
+ steppingStartTime = System.currentTimeMillis();
+
+ final ExecutionDMC dmc = (ExecutionDMC) context;
+
+ dmc.setStepping(true);
+
+ IAddress pcAddress = null;
+
+ if (dmc.getPC() == null) { // PC is even unknown, can only do
+ // one-instruction step.
+ stepType = StepType.INSTRUCTION_STEP_INTO;
+ } else
+ pcAddress = new Addr64(dmc.getPC(), 16);
+
+ // For step-out (step-return), no difference between source level or
+ // instruction level.
+ //
+ if (stepType == StepType.STEP_RETURN)
+ stepType = StepType.INSTRUCTION_STEP_RETURN;
+
+ // Source level stepping request.
+ //
+ if (stepType == StepType.STEP_OVER || stepType == StepType.STEP_INTO) {
+ IEDCModules moduleService = getService(Modules.class);
+
+ ISymbolDMContext symCtx = DMContexts.getAncestorOfType(context, ISymbolDMContext.class);
+
+ IEDCModuleDMContext module = moduleService.getModuleByAddress(symCtx, pcAddress);
+
+ // Check if there is source info for PC address.
+ //
+ if (module != null) {
+ IEDCSymbolReader reader = module.getSymbolReader();
+ assert pcAddress != null;
+ if (reader != null) {
+ IAddress linkAddress = module.toLinkAddress(pcAddress);
+ IModuleLineEntryProvider lineEntryProvider
+ = reader.getModuleScope().getModuleLineEntryProvider();
+ ILineEntry line = lineEntryProvider.getLineEntryAtAddress(linkAddress);
+ if (line != null) {
+ // get runtime addresses of the line boundaries.
+ IAddress endAddr = module.toRuntimeAddress(line.getHighAddress());
+
+ // get the next source line entry that has a line #
+ // greater than the current line # (and in the same file),
+ // but is not outside of the function address range
+ // if found, the start addr of that entry is our end
+ // address, otherwise use the existing end address
+ ILineEntry nextLine
+ = lineEntryProvider.getNextLineEntry(
+ lineEntryProvider.getLineEntryAtAddress(linkAddress),
+ stepType == StepType.STEP_OVER);
+ if (nextLine != null) {
+ endAddr = module.toRuntimeAddress(nextLine.getLowAddress());
+ } else { // nextLine == null probably means last line
+ IEDCSymbols symbolsService = getService(Symbols.class);
+ IFunctionScope functionScope
+ = symbolsService.getFunctionAtAddress(dmc.getSymbolDMContext(), pcAddress);
+ if (stepType == StepType.STEP_OVER) {
+ while (functionScope != null
+ && functionScope.getParent() instanceof IFunctionScope) {
+ functionScope = (IFunctionScope)functionScope.getParent();
+ }
+ }
+ if (functionScope != null)
+ endAddr = module.toRuntimeAddress(functionScope.getHighAddress());
+ }
+ if (stepType == StepType.STEP_OVER) {
+ // Create a disabled range
+ Collection<ILineEntry> ranges
+ = lineEntryProvider.getLineEntriesForLines(line.getFilePath(),
+ line.getLineNumber(),
+ line.getLineNumber());
+ if (ranges.size() > 1)
+ {
+ for (ILineEntry iLineEntry : ranges) {
+ dmc.addDisabledRange(module.toRuntimeAddress(iLineEntry.getLowAddress()),
+ module.toRuntimeAddress(iLineEntry.getHighAddress()));
+ }
+ }
+ }
+
+ /*
+ * It's possible that PC is larger than startAddr
+ * (e.g. user does a few instruction level stepping
+ * then switch to source level stepping; or when we
+ * just step out a function). We just parse and
+ * stepping instructions within [pcAddr, endAddr)
+ * instead of all those within [startAddr, endAddr).
+ * One possible problem with the solution is when
+ * control jumps from within [pcAddress, endAddr) to
+ * somewhere within [startAddr, pcAddress), the
+ * stepping would stop at somewhere within
+ * [startAddr, pcAddress) instead of outside of the
+ * [startAddr, endAddr). But that case is rare (e.g.
+ * a source line contains a bunch of statements) and
+ * that "problem" is not unacceptable as user could
+ * just keep stepping or set a breakpoint and run.
+ *
+ * We can overcome the problem but that would incur
+ * much more complexity in the stepping code and
+ * brings down the stepping speed.
+ * ........................ 08/30/2009
+ */
+
+ stepAddressRange(dmc, stepType == StepType.STEP_INTO, pcAddress, endAddr, rm);
+
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null, "source level stepping."); }
+ return;
+ }
+ }
+ }
+
+ // No source found, fall back to instruction level step.
+ if (stepType == StepType.STEP_INTO)
+ stepType = StepType.INSTRUCTION_STEP_INTO;
+ else
+ stepType = StepType.INSTRUCTION_STEP_OVER;
+ }
+
+ // instruction level step
+ //
+ if (stepType == StepType.INSTRUCTION_STEP_OVER)
+ stepOverOneInstruction(dmc, pcAddress, rm);
+ else if (stepType == StepType.INSTRUCTION_STEP_INTO)
+ stepIntoOneInstruction(dmc, rm);
+ else if (stepType == StepType.INSTRUCTION_STEP_RETURN)
+ stepOut(dmc, pcAddress, rm);
+
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
+ }
+ }, rm);
+ }
+
+ private void stepOut(final ExecutionDMC dmc, IAddress pcAddress, final RequestMonitor rm) {
+
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, "Step out from address " + pcAddress.toHexAddressString()); }
+
+ if (dmc.supportsStepMode(StepType.STEP_RETURN)) {
+ dmc.stepOut(rm);
+ return;
+ }
+
+ Stack stackService = getService(Stack.class);
+ IFrameDMContext[] frames;
+ try {
+ frames = stackService.getFramesForDMC(dmc, 0, 1);
+ } catch (CoreException e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
+ EDCDebugger.getMessageLogger().log(s);
+ rm.setStatus(s);
+ rm.done();
+ return;
+ }
+ if (frames.length <= 1) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED,
+ "Cannot step out as no caller frame is available.", null));
+ rm.done();
+ return;
+ }
+
+ if (handleSteppingOutOfInLineFunctions(dmc, frames, rm))
+ return;
+
+ final IAddress stepToAddress = ((StackFrameDMC) frames[1]).getInstructionPtrAddress();
+
+ final Breakpoints bpService = getService(Breakpoints.class);
+
+ prepareToRun(dmc, new DataRequestMonitor<Boolean>(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+
+ boolean goon = true;
+
+ if (getData() == true) {
+ // one instruction has been executed
+ IAddress newPC = new Addr64(dmc.getPC(), 16);
+
+ // And we already stepped out (that instruction is return
+ // instruction).
+ //
+ if (newPC.equals(stepToAddress)) {
+ goon = false;
+ }
+ }
+
+ if (goon) {
+ bpService.setTempBreakpoint(dmc, stepToAddress, new RequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ dmc.resumeForStepping(rm);
+ }
+ });
+ } else
+ rm.done();
+ }
+ });
+ }
+
+ /**
+ * handle module load event. A module is an executable file
+ * or a library (e.g. DLL or shared lib).
+ * Allow subclass to override for special handling if needed.
+ * This must be called in DSF dispatch thread.
+ *
+ * @param dmc
+ * @param moduleProperties
+ */
+ protected void handleModuleLoadedEvent(IEDCExecutionDMC dmc, Map<String, Object> moduleProperties) {
+ ISymbolDMContext symbolContext = dmc.getSymbolDMContext();
+
+ if (symbolContext != null) {
+ Modules modulesService = getService(Modules.class);
+ modulesService.moduleLoaded(symbolContext, dmc, moduleProperties);
+ }
+ }
+
+ /**
+ * handle module unload event. A module is an executable file
+ * or a library (e.g. DLL or shared lib).
+ * Allow subclass to override for special handling if needed.
+ * This must be called in DSF dispatch thread.
+ *
+ * @param dmc
+ * @param moduleProperties
+ */
+ protected void handleModuleUnloadedEvent(IEDCExecutionDMC dmc, Map<String, Object> moduleProperties) {
+ ISymbolDMContext symbolContext = dmc.getSymbolDMContext();
+
+ if (symbolContext != null) {
+ Modules modulesService = getService(Modules.class);
+ modulesService.moduleUnloaded(symbolContext, dmc, moduleProperties);
+ }
+ }
+
+ private boolean handleSteppingOutOfInLineFunctions(final ExecutionDMC dmc,
+ IFrameDMContext[] frames, final RequestMonitor rm) {
+
+ assert frames.length > 1 && frames[0] instanceof StackFrameDMC;
+
+ StackFrameDMC currentFrame = ((StackFrameDMC) frames[0]);
+
+ IEDCModuleDMContext module = currentFrame.getModule();
+ if (module != null) {
+ IFunctionScope func = currentFrame.getFunctionScope();
+ // if inline ...
+ if (func != null && (func.getParent() instanceof IFunctionScope)) {
+
+ // ... but if PC is at beginning of function, then act like not in inline
+ // (i.e. step-out as though standing at call to any non-inline function)
+ if (currentFrame.isInlineShouldBeHidden(null))
+ return false;
+
+ // ... or if PC at at high-address, that means we're actually done with it
+ IAddress functRuntimeHighAddr = module.toRuntimeAddress(func.getHighAddress());
+ IAddress frameInstrPtr = currentFrame.getInstructionPtrAddress();
+ if (functRuntimeHighAddr.equals(frameInstrPtr))
+ return false;
+
+ // getting here means treat the line as a regular line to step over
+ stepAddressRange(dmc, false, frameInstrPtr, functRuntimeHighAddr,
+ new RequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ rm.done();
+ }});
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * check if the instruction at PC is a subroutine call. If yes, set a
+ * breakpoint after it and resume; otherwise just execute one instruction.
+ *
+ * @param dmc
+ * @param pcAddress
+ * @param rm
+ */
+ private void stepOverOneInstruction(final ExecutionDMC dmc, final IAddress pcAddress, final RequestMonitor rm) {
+
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, "address " + pcAddress.toHexAddressString()); }
+
+ if (dmc.supportsStepMode(StepType.INSTRUCTION_STEP_OVER)) {
+ dmc.singleStep(false, rm);
+ return;
+ }
+
+ ITargetEnvironment env = getTargetEnvironmentService();
+ final IDisassembler disassembler = (env != null) ? env.getDisassembler() : null;
+ if (disassembler == null) {
+ rm.setStatus(Disassembly.statusNoDisassembler());
+ rm.done();
+ return;
+ }
+
+ Memory memoryService = getService(Memory.class);
+ IMemoryDMContext mem_dmc = DMContexts.getAncestorOfType(dmc, IMemoryDMContext.class);
+
+ // We need to get the instruction at the PC. We have to
+ // retrieve memory bytes for longest instruction.
+ @SuppressWarnings("null") // (env == null) -> (disassembler == null) -> return above
+ int maxInstLength = env.getLongestInstructionLength();
+
+ // Note this memory read will give us memory bytes with
+ // debugger breakpoints removed, which is just what we want.
+ memoryService.getMemory(mem_dmc, pcAddress, 0, 1, maxInstLength,
+ new DataRequestMonitor<MemoryByte[]>(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ ByteBuffer codeBuf = Disassembly.translateMemoryBytes(getData(), pcAddress, rm);
+ if (codeBuf == null) {
+ return; // rm status set in checkMemoryBytes()
+ }
+
+ IDisassemblyDMContext dis_dmc
+ = DMContexts.getAncestorOfType(dmc, IDisassemblyDMContext.class);
+ Map<String, Object> options = new HashMap<String, Object>();
+ options.put(IDisassemblerOptions.ADDRESS_IS_PC, 1);
+ IDisassembledInstruction inst;
+ try {
+ inst = disassembler.disassembleOneInstruction(pcAddress, codeBuf, options, dis_dmc);
+ } catch (CoreException e) {
+ rm.setStatus(e.getStatus());
+ rm.done();
+ return;
+ }
+
+ final boolean isSubroutineCall = inst.getJumpToAddress() != null
+ && inst.getJumpToAddress().isSubroutineAddress();
+ final IAddress nextInstructionAddress = pcAddress.add(inst.getSize());
+
+ stepIntoOneInstruction(dmc, new RequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ if (!isSubroutineCall)
+ rm.done();
+ else {
+ // If current instruction is subroutine call, set a
+ // temp
+ // breakpoint at next instruction and resume ...
+ //
+ Breakpoints bpService = getService(Breakpoints.class);
+ bpService.setTempBreakpoint(dmc, nextInstructionAddress,
+ new RequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ dmc.resumeForStepping(rm);
+ }
+ });
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Step into or over an address range. Note the startAddr is also the PC
+ * value.
+ *
+ * @param dmc
+ * @param stepIn
+ * - whether to step-in.
+ * @param startAddr
+ * - also the PC register value.
+ * @param endAddr
+ * @param rm
+ * - marked done after the stepping is over and context is
+ * suspended again.
+ */
+ private void stepAddressRange(final ExecutionDMC dmc, final boolean stepIn, final IAddress startAddr,
+ final IAddress endAddr, final RequestMonitor rm) {
+ if (EDCTrace.RUN_CONTROL_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, MessageFormat.format("address range [{0},{1})", startAddr.toHexAddressString(), endAddr.toHexAddressString())); }
+
+ if (dmc.supportsStepMode(stepIn ? StepType.STEP_INTO : StepType.STEP_OVER)) {
+ dmc.stepRange(stepIn, startAddr, endAddr, rm);
+ return;
+ }
+
+ ITargetEnvironment env = getTargetEnvironmentService();
+ final IDisassembler disassembler = (env != null) ? env.getDisassembler() : null;
+ if (disassembler == null) {
+ rm.setStatus(Disassembly.statusNoDisassembler());
+ rm.done();
+ return;
+ }
+
+ final Memory memoryService = getService(Memory.class);
+ IMemoryDMContext mem_dmc = DMContexts.getAncestorOfType(dmc, IMemoryDMContext.class);
+
+ int memSize = startAddr.distanceTo(endAddr).intValue();
+
+ final IAddress pcAddress = startAddr;
+
+ // Note this memory read will give us memory bytes with
+ // debugger breakpoints removed, which is just what we want.
+ memoryService.getMemory(mem_dmc, startAddr, 0, 1, memSize,
+ new DataRequestMonitor<MemoryByte[]>(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ ByteBuffer codeBuf = Disassembly.translateMemoryBytes(getData(), startAddr, rm);
+ if (codeBuf == null) {
+ return; // rm status set in checkMemoryBytes()
+ }
+
+ IDisassemblyDMContext dis_dmc
+ = DMContexts.getAncestorOfType(dmc, IDisassemblyDMContext.class);
+
+ Map<String, Object> options = new HashMap<String, Object>();
+
+ List<IDisassembledInstruction> instList;
+ try {
+ instList
+ = disassembler.disassembleInstructions(startAddr, endAddr, codeBuf,
+ options, dis_dmc);
+ } catch (CoreException e) {
+ rm.setStatus(e.getStatus());
+ rm.done();
+ return;
+ }
+
+ // Now collect all possible stop points
+ //
+ final List<IAddress> stopPoints = new ArrayList<IAddress>();
+ final List<IAddress> runToAndCheckPoints = new ArrayList<IAddress>();
+ boolean insertBPatRangeEnd = true;
+
+ for (IDisassembledInstruction inst : instList) {
+ final IAddress instAddr = inst.getAddress();
+ if (insertBPatRangeEnd == false)
+ insertBPatRangeEnd = true;
+ IJumpToAddress jta = inst.getJumpToAddress();
+ if (jta == null)
+ continue;
+
+ // the instruction is a control-change instruction
+ //
+ if (!jta.isImmediate()) {
+
+ if (inst.getAddress().equals(pcAddress)) {
+ // Control is already at the instruction, evaluate
+ // it.
+ //
+ String expr = (String) jta.getValue();
+ if (expr.equals(JumpToAddress.EXPRESSION_RETURN_FAR)
+ || expr.equals(JumpToAddress.EXPRESSION_RETURN_NEAR)
+ || expr.equals(JumpToAddress.EXPRESSION_LR)) {
+ // The current instruction is return instruction. Just execute it
+ // to step-out and we are done with the stepping. This way we avoid
+ // looking for return address from caller stack frame which may not
+ // even available.
+ // Is it possible that the destination address of the step-out
+ // is still within the [startAddr, endAddr)range ? In theory
+ // yes, but in practice it means one source line has several
+ // function bodies in it, who would do that?
+ //
+ stepIntoOneInstruction(dmc, rm);
+ return;
+ }
+ // evaluate the address expression
+
+ if (!jta.isSubroutineAddress() || stepIn)
+ {
+ IAddressExpressionEvaluator evaluator = getTargetEnvironmentService()
+ .getAddressExpressionEvaluator();
+ if (evaluator == null) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED,
+ "No evaluator for address expression yet.", null));
+ rm.done();
+ return;
+ }
+
+ Registers regService = getService(Registers.class);
+
+ IAddress addr;
+ try {
+ addr = evaluator.evaluate(dmc, expr, regService, memoryService);
+ } catch (CoreException e) {
+ rm.setStatus(e.getStatus());
+ rm.done();
+ return;
+ }
+ // don't add an address if we already have it
+ if (!stopPoints.contains(addr))
+ stopPoints.add(addr);
+ }
+ } else {
+ // we must run to this instruction first
+ //
+ /*
+ * What if control would skip (jump-over) this
+ * instruction within the [startAddr, endAddr) range
+ * ? So we should go on collecting stop points from
+ * the remaining instructions in the range and then
+ * do our two-phase stepping (see below)
+ */
+ if (!runToAndCheckPoints.contains(instAddr))
+ runToAndCheckPoints.add(instAddr);
+ }
+ } else { // "jta" is immediate address.
+
+ IAddress jumpAddress = (IAddress) jta.getValue();
+
+ if (jta.isSoleDestination()) {
+ if (jta.isSubroutineAddress()) {
+ // is subroutine call
+ if (stepIn && !stopPoints.contains(jumpAddress)) {
+ stopPoints.add(jumpAddress);
+ // no need to check remaining instructions
+ // !! Wrong. Control may jump over (skip)this instruction
+ // within the [startAddr, endAddr) range, so we still need
+ // to parse instructions after this instruction.
+ // break;
+ } else {
+ // step over the call instruction. Just stop
+ // at next instruction.
+ // nothing to do.
+ }
+ } else {
+ // Unconditional jump instruction
+ // ignore jump within the address range
+ if (!(startAddr.compareTo(jumpAddress) <= 0 && jumpAddress.compareTo(endAddr) < 0)) {
+ insertBPatRangeEnd = false;
+ if (!stopPoints.contains(jumpAddress))
+ stopPoints.add(jumpAddress);
+ }
+ }
+ } else {
+ // conditional jump
+ // ignore jump within the address range
+ if (!(startAddr.compareTo(jumpAddress) <= 0 && jumpAddress.compareTo(endAddr) < 0)
+ && !stopPoints.contains(jumpAddress))
+ {
+ stopPoints.add(jumpAddress);
+ }
+ }
+ }
+ } // end of parsing instructions
+
+ // need a temp breakpoint at the "endAddr".
+ if (insertBPatRangeEnd && !stopPoints.contains(endAddr))
+ stopPoints.add(endAddr);
+
+ if (runToAndCheckPoints.size() > 0) {
+ // Now do our two-phase stepping.
+ //
+
+ if (runToAndCheckPoints.size() > 1) {
+ /*
+ * Wow, there are two control-change instructions in the
+ * range that requires run-to-check (let's call them RTC
+ * point). In theory the stepping might fail (not stop
+ * as desired) in such case: When we try to run to the
+ * first RTC, the control may skip the first RTC and run
+ * to second RTC (note we don't know the stop points of
+ * the second RTC yet) and run out of the range and be
+ * gone with the wind...
+ *
+ * There is no way we can solve the problem. Good thing
+ * is, in practice is the case even possible ?
+ */
+ // Log (and show it, get rid of the "show" part after
+ // tons of test) warning here.
+ EDCDebugger.getMessageLogger().log(
+ new Status(IStatus.WARNING, EDCDebugger.PLUGIN_ID,
+ MessageFormat.format(
+ "More than one run-to-check points in the address range [{0},{1}). Stepping might fail.",
+ startAddr.toHexAddressString(), endAddr.toHexAddressString())));
+ }
+
+ // ------------ Phase 1: run to the first RTC.
+ //
+ // recursive call
+ stepAddressRange(dmc, stepIn, startAddr, runToAndCheckPoints.get(0), new RequestMonitor(
+ getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ IAddress newPC = new Addr64(dmc.getPC(), 16);
+
+ boolean doneWithStepping = false;
+ for (IAddress addr : stopPoints)
+ if (newPC.equals(addr)) {
+ doneWithStepping = true; // done with the
+ // stepping
+ break;
+ }
+
+ Breakpoints bpService = getService(Breakpoints.class);
+ if (bpService.findUserBreakpoint(newPC) != null) { // hit
+ // a
+ // user
+ // breakpoint
+ doneWithStepping = true;
+ }
+
+ if (!doneWithStepping)
+ // -------- Phase 2: run to the "endAddr".
+ //
+ stepAddressRange(dmc, stepIn, newPC, endAddr, rm); // Recursive
+ // call
+ else
+ rm.done();
+ }
+ });
+ } else { // no RTC points, set temp breakpoints at stopPoints
+ // and run...
+
+ // Make sure we step over breakpoint at PC (if any)
+ //
+ prepareToRun(dmc, new DataRequestMonitor<Boolean>(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+
+ boolean goon = true;
+
+ Breakpoints bpService = getService(Breakpoints.class);
+
+ if (getData() == true) {
+ // one instruction has been executed
+ IAddress newPC = new Addr64(dmc.getPC(), 16);
+
+ if (bpService.findUserBreakpoint(newPC) != null) {
+ // hit a user breakpoint. Stepping finishes.
+ goon = false;
+ } else {
+ // Check if we finish the stepping by
+ // checking the newPC against
+ // our stopPoints instead of checking if
+ // newPC is outside of [startAddr, endAddr)
+ // so that such case would not fail: step
+ // over this address range:
+ //
+ // 0x10000 call ... // a user breakpoint is
+ // set here
+ // 0x10004 ...
+ // 0x1000c ...
+ //
+ //
+ for (IAddress addr : stopPoints)
+ if (newPC.equals(addr)) {
+ goon = false;
+ break;
+ }
+ }
+ }
+
+ if (goon) {
+ // Now set temp breakpoints at our stop points.
+ //
+ CountingRequestMonitor setTempBpRM = new CountingRequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ // we are done setting all temporary
+ // breakpoints
+ dmc.resumeForStepping(rm);
+ }
+ };
+
+ setTempBpRM.setDoneCount(stopPoints.size());
+
+ for (IAddress addr : stopPoints) {
+ bpService.setTempBreakpoint(dmc, addr, setTempBpRM);
+ }
+ } else
+ rm.done();
+ }
+ });
+ }
+
+ }
+ });
+ }
+
+ /**
+ * step-into one instruction at current PC, namely execute only one
+ * instruction.
+ *
+ * @param dmc
+ * @param rm
+ * - this RequestMonitor is marked done when the execution
+ * finishes and target suspends again.
+ */
+ private void stepIntoOneInstruction(final ExecutionDMC dmc, final RequestMonitor rm) {
+
+ prepareToRun(dmc, new DataRequestMonitor<Boolean>(getExecutor(), rm) {
+ @Override
+ protected void handleSuccess() {
+ if (getData() == true /* already executed one instruction */)
+ // The "step" is over
+ rm.done();
+ else {
+ dmc.singleStep(true, rm);
+ }
+ }
+ });
+ }
+
+ public void suspend(IExecutionDMContext context, RequestMonitor requestMonitor) {
+ if (context instanceof ExecutionDMC) {
+ ((ExecutionDMC) context).suspend(requestMonitor);
+ } else {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, MessageFormat
+ .format("The context [{0}] is not a recognized execution context.", context), null));
+ requestMonitor.done();
+ }
+ }
+
+ public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) {
+ rm.done();
+ }
+
+ public void flushCache(IDMContext context) {
+ if (isSnapshot())
+ return;
+ // Flush the Registers cache immediately
+ // For instance the readPCRegister() may get wrong PC value when an
+ // asynchronous suspend event comes too quick after resume.
+ Registers regService = getService(Registers.class);
+ regService.flushCache(context);
+ }
+
+ @Override
+ public void shutdown(RequestMonitor monitor) {
+ if (tcfRunService != null) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ tcfRunService.removeListener(runListener);
+ }
+ });
+ }
+ unregister();
+ super.shutdown(monitor);
+ }
+
+ public RootExecutionDMC getRootDMC() {
+ return rootExecutionDMC;
+ }
+
+ public static class StartedEvent extends AbstractDMEvent<IExecutionDMContext> implements IStartedDMEvent {
+
+ public StartedEvent(IExecutionDMContext context) {
+ super(context);
+ }
+ }
+
+ public static class ExitedEvent extends AbstractDMEvent<IExecutionDMContext> implements IExitedDMEvent {
+ private boolean isTerminatedThanDisconnected;
+
+ public ExitedEvent(IExecutionDMContext context, boolean isTerminatedThanDisconnected) {
+ super(context);
+ this.isTerminatedThanDisconnected = isTerminatedThanDisconnected;
+ }
+
+ public boolean isTerminatedThanDisconnected() {
+ return isTerminatedThanDisconnected;
+ }
+ }
+
+ /*
+ * NOTE:
+ * Methods in this listener are invoked in TCF dispatch thread.
+ * When they call into DSF services/objects, make sure it's done in
+ * DSF executor thread so as to avoid possible racing condition.
+ */
+ private final org.eclipse.tm.tcf.services.IRunControl.RunControlListener runListener = new org.eclipse.tm.tcf.services.IRunControl.RunControlListener() {
+
+ public void containerResumed(String[] context_ids) {
+ }
+
+ public void containerSuspended(String context, String pc, String reason, Map<String, Object> params,
+ String[] suspended_ids) {
+ }
+
+ public void contextAdded(final RunControlContext[] contexts) {
+ getExecutor().execute(new Runnable() {
+ public void run() {
+ for (RunControlContext ctx : contexts) {
+ ExecutionDMC dmc = rootExecutionDMC;
+ String parentID = ctx.getParentID();
+ if (parentID != null)
+ dmc = dmcsByID.get(parentID);
+ if (dmc != null) {
+ dmc.contextAdded(ctx.getProperties(), ctx);
+ }
+ }
+ }
+ });
+ }
+
+ public void contextChanged(RunControlContext[] contexts) {
+ }
+
+ public void contextException(final String context, final String msg) {
+ getExecutor().execute(new Runnable() {
+ public void run() {
+ ExecutionDMC dmc = getContext(context);
+ if (dmc != null)
+ dmc.contextException(msg);
+ }
+ });
+ }
+
+ public void contextRemoved(final String[] context_ids) {
+ getExecutor().execute(new Runnable() {
+ public void run() {
+ for (String contextID : context_ids) {
+ ExecutionDMC dmc = getContext(contextID);
+ assert dmc != null;
+ if (dmc != null)
+ dmc.purgeFromDebugger();
+ }
+ }
+ });
+ }
+
+ public void contextResumed(final String context) {
+ getExecutor().execute(new Runnable() {
+ public void run() {
+ ExecutionDMC dmc = getContext(context);
+ if (dmc != null)
+ dmc.contextResumed(false);
+ }
+ });
+ }
+
+ public void contextSuspended(final String context, final String pc, final String reason,
+ final Map<String, Object> params) {
+ getExecutor().execute(new Runnable() {
+ public void run() {
+ ExecutionDMC dmc = getContext(context);
+ if (dmc != null)
+ dmc.contextSuspended(pc, reason, params);
+ else {
+ EDCDebugger.getMessageLogger().logError(
+ MessageFormat.format("Unkown context [{0}] is reported in suspended event. Make sure TCF agent has reported contextAdded event first.", context),
+ null);
+ }
+ }
+ });
+ }
+ };
+
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor)throws Exception {
+ Element contextsElement = document.createElement(EXECUTION_CONTEXTS);
+ ExecutionDMC[] dmcs = rootExecutionDMC.getChildren();
+ SubMonitor progress = SubMonitor.convert(monitor, dmcs.length * 1000);
+
+ for (ExecutionDMC executionDMC : dmcs) {
+ Element dmcElement = executionDMC.takeSnapshot(album, document, progress.newChild(1000));
+ contextsElement.appendChild(dmcElement);
+ }
+ return contextsElement;
+ }
+
+ public ExecutionDMC getContext(String contextID) {
+ return dmcsByID.get(contextID);
+ }
+
+ public void loadSnapshot(Element snapshotRoot) throws Exception {
+ NodeList ecElements = snapshotRoot.getElementsByTagName(EXECUTION_CONTEXTS);
+ rootExecutionDMC.resumeAll();
+ initializeRootExecutionDMC();
+ rootExecutionDMC.loadSnapshot((Element) ecElements.item(0));
+ }
+
+ public void tcfServiceReady(IService service) {
+ if (service instanceof org.eclipse.tm.tcf.services.IRunControl) {
+ tcfRunService = (org.eclipse.tm.tcf.services.IRunControl) service;
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ tcfRunService.addListener(runListener);
+ }
+ });
+ } else
+ assert false;
+ }
+
+ /**
+ * Stop debugging all execution contexts. This does not kill/terminate
+ * the actual process or thread.
+ * See: {@link #terminateAllContexts(RequestMonitor)}
+ */
+ private void detachAllContexts(){
+ getRootDMC().detach();
+ }
+
+ /**
+ * Terminate all contexts so as to terminate the debug session.
+ *
+ * @param rm can be null.
+ */
+ public void terminateAllContexts(final RequestMonitor rm){
+
+ CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleError() {
+ // failed to terminate at least one process, usually
+ // because connection to target is lost, or some processes
+ // cannot be killed (e.g. OS does not permit that).
+ // Just untarget the contexts.
+ detachAllContexts();
+
+ if (rm != null)
+ rm.done();
+ }
+
+ };
+
+ // It's assumed
+ // 1. First level of children under rootDMC are processes.
+ // 2. Killing them would kill all contexts (processes and threads) being debugged.
+ //
+ ExecutionDMC[] processes = getRootDMC().getChildren();
+ crm.setDoneCount(processes.length);
+
+ for (ExecutionDMC e : processes) {
+ e.terminate(crm);
+ }
+ }
+
+ public void canRunToLine(IExecutionDMContext context, String sourceFile,
+ int lineNumber, final DataRequestMonitor<Boolean> rm) {
+ // I tried to have better filtering as shown in commented code. But that
+ // just made the command fail to be enabled as desired. Not sure about the
+ // exact cause yet, but one problem (from the upper framework) I've seen is
+ // this API is not called whenever user selects a line in source editor (or
+ // disassembly view) and bring up context menu.
+ // Hence we blindly answer yes. The behavior is on par with DSF-GDB.
+ // ................. 03/11/10
+ rm.setData(true);
+ rm.done();
+
+// // Return true if we can find address(es) for the line in the context.
+// //
+// getLineAddress(context, sourceFile, lineNumber, new DataRequestMonitor<List<IAddress>>(getExecutor(), rm){
+// @Override
+// protected void handleCompleted() {
+// if (! isSuccess())
+// rm.setData(false);
+// else {
+// rm.setData(getData().size() > 0);
+// }
+// rm.done();
+// }});
+ }
+
+ public void runToLine(final IExecutionDMContext context, String sourceFile,
+ int lineNumber, boolean skipBreakpoints, final RequestMonitor rm) {
+
+ getLineAddress(context, sourceFile, lineNumber, new DataRequestMonitor<List<IAddress>>(getExecutor(), rm){
+ @Override
+ protected void handleCompleted() {
+ if (! isSuccess()) {
+ rm.setStatus(getStatus());
+ rm.done();
+ }
+ else {
+ runToAddresses(context, getData(), rm);
+ }
+ }});
+ }
+
+ private void runToAddresses(IExecutionDMContext context,
+ final List<IAddress> addrs, final RequestMonitor rm) {
+ // 1. Single step over breakpoint, if PC is at a breakpoint.
+ // 2. Set temp breakpoint at the addresses.
+ // 3. Resume the context.
+ //
+ final ExecutionDMC dmc = (ExecutionDMC)context;
+ assert dmc != null;
+
+ prepareToRun(dmc, new DataRequestMonitor<Boolean>(getExecutor(), rm){
+
+ @Override
+ protected void handleCompleted() {
+ if (! isSuccess()) {
+ rm.setStatus(getStatus());
+ rm.done();
+ return;
+ }
+
+ CountingRequestMonitor settingBP_crm = new CountingRequestMonitor(getExecutor(), rm) {
+ @Override
+ protected void handleCompleted() {
+ if (! isSuccess()) {
+ // as long as we fail to set on temp breakpoint, we bail out.
+ rm.setStatus(getStatus());
+ rm.done();
+ }
+ else {
+ // all temp breakpoints are successfully set.
+ // Now resume the context.
+ dmc.resume(rm);
+ }
+ }};
+
+ settingBP_crm.setDoneCount(addrs.size());
+
+ Breakpoints bpService = getService(Breakpoints.class);
+
+ for (IAddress a : addrs)
+ bpService.setTempBreakpoint(dmc, a, settingBP_crm);
+ }}
+ );
+ }
+
+ public void canRunToAddress(IExecutionDMContext context, IAddress address,
+ DataRequestMonitor<Boolean> rm) {
+ // See comment in canRunToLine() for more.
+ rm.setData(true);
+ rm.done();
+
+// // If the address is not in any module of the run context, return false.
+// Modules moduleService = getService(Modules.class);
+//
+// ISymbolDMContext symCtx = DMContexts.getAncestorOfType(context, ISymbolDMContext.class);
+//
+// ModuleDMC m = moduleService.getModuleByAddress(symCtx, address);
+// rm.setData(m == null);
+// rm.done();
+ }
+
+ public void runToAddress(IExecutionDMContext context, IAddress address,
+ boolean skipBreakpoints, RequestMonitor rm) {
+ List<IAddress> addrs = new ArrayList<IAddress>(1);
+ addrs.add(address);
+ runToAddresses(context, addrs, rm);
+ }
+
+ public void canMoveToLine(IExecutionDMContext context, String sourceFile,
+ int lineNumber, boolean resume, final DataRequestMonitor<Boolean> rm) {
+ // See comment in canRunToLine() for more.
+ rm.setData(true);
+ rm.done();
+
+ // Return true if we can find one and only one address for the line in the context.
+ //
+// getLineAddress(context, sourceFile, lineNumber, new DataRequestMonitor<List<IAddress>>(getExecutor(), rm){
+// @Override
+// protected void handleCompleted() {
+// if (! isSuccess())
+// rm.setData(false);
+// else {
+// rm.setData(getData().size() == 1);
+// }
+// rm.done();
+// }});
+ }
+
+ public void moveToLine(final IExecutionDMContext context, String sourceFile,
+ int lineNumber, final boolean resume, final RequestMonitor rm) {
+ getLineAddress(context, sourceFile, lineNumber, new DataRequestMonitor<List<IAddress>>(getExecutor(), rm){
+ @Override
+ protected void handleCompleted() {
+ if (! isSuccess()) {
+ rm.setStatus(getStatus());
+ rm.done();
+ }
+ else {
+ List<IAddress> addrs = getData();
+ // No, canMoveToLine() does not do sanity check now.
+ // We just move to the first address we found, which may or
+ // may not be the address user wants. Is it better we return
+ // error if "addrs.size() > 1" ? .......03/28/10
+ // assert addrs.size() == 1; // ensured by canMoveToLine().
+ moveToAddress(context, addrs.get(0), resume, rm);
+ }
+ }});
+ }
+
+ public void canMoveToAddress(IExecutionDMContext context, IAddress address,
+ boolean resume, DataRequestMonitor<Boolean> rm) {
+ // Allow moving to any address.
+ rm.setData(true);
+ rm.done();
+ }
+
+ public void moveToAddress(IExecutionDMContext context, IAddress address,
+ boolean resume, RequestMonitor rm) {
+
+ Registers regService = getService(Registers.class);
+
+ assert(context instanceof ExecutionDMC);
+ ExecutionDMC dmc = (ExecutionDMC)context;
+
+ String newPC = address.toString(16);
+
+ if (! newPC.equals(dmc.getPC())) {
+ try {
+ // synchronously change PC, so that change occurs before any resume
+ String regID = getTargetEnvironmentService().getPCRegisterID();
+ RegisterDMC regDMC = regService.findRegisterDMCByName(dmc, regID);
+ assert regDMC != null;
+
+ regService.writeRegister(regDMC, newPC, IFormattedValues.HEX_FORMAT);
+ } catch (CoreException e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Error adjusting the PC register", e);
+ EDCDebugger.getMessageLogger().log(s);
+ rm.setStatus(s);
+ rm.done();
+ return;
+ }
+
+ // update cached PC.
+ dmc.setPC(newPC);
+ }
+
+ if (resume)
+ resume(context, rm);
+ else
+ rm.done();
+ }
+
+ /**
+ * Get runtime addresses mapped to given source line in given run context.
+ *
+ * @param context
+ * @param sourceFile
+ * @param lineNumber
+ * @param drm holds an empty list if no address found, or the run context is not suspended.
+ */
+ private void getLineAddress(IExecutionDMContext context,
+ String sourceFile, int lineNumber, DataRequestMonitor<List<IAddress>> drm) {
+ List<IAddress> addrs = new ArrayList<IAddress>(1);
+
+ ExecutionDMC dmc = (ExecutionDMC) context;
+ if (dmc == null || ! dmc.isSuspended()) {
+ drm.setData(addrs);
+ drm.done();
+ return;
+ }
+
+ Modules moduleService = getService(Modules.class);
+
+ moduleService.getLineAddress(dmc, sourceFile, lineNumber, drm);
+ }
+
+ /**
+ * Check if this context is non-container. Only non-container context
+ * (thread and bare device context) can have register, stack frames, etc.
+ *
+ * @param dmc
+ * @return
+ */
+ static public boolean isNonContainer(IDMContext dmc) {
+ return ! (dmc instanceof IContainerDMContext);
+ }
+
+ public ThreadExecutionDMC[] getSuspendedThreads()
+ {
+ ExecutionDMC[] dmcs = null;
+ List<ThreadExecutionDMC> result = new ArrayList<ThreadExecutionDMC>();
+ synchronized (dmcsByID)
+ {
+ Collection<ExecutionDMC> allDMCs = dmcsByID.values();
+ dmcs = allDMCs.toArray(new ExecutionDMC[allDMCs.size()]);
+ }
+ for (ExecutionDMC executionDMC : dmcs) {
+ if (executionDMC instanceof ThreadExecutionDMC && executionDMC.isSuspended())
+ result.add((ThreadExecutionDMC) executionDMC);
+ }
+ return result.toArray(new ThreadExecutionDMC[result.size()]);
+ }
+
+ /**
+ * Utility method for getting a context property using a default value
+ * if missing
+ */
+ private static boolean getProperty(Map<String, Object> properties, String name, boolean defaultValue) {
+ Boolean b = (Boolean)properties.get(name);
+ return (b == null ? defaultValue : b);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.dsf.debug.service.ISignals;
+import org.eclipse.cdt.dsf.service.DsfSession;
+
+public class Signals extends AbstractEDCService implements ISignals {
+
+ public Signals(DsfSession session) {
+ super(session, new String[] { ISignals.class.getName(), Signals.class.getName() });
+ // TODO Auto-generated constructor stub
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import org.eclipse.cdt.debug.edc.internal.snapshot.Album;
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.debug.edc.services.ISnapshots;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.DsfSession;
+
+public class Snapshots extends AbstractEDCService implements ISnapshots {
+
+ public Snapshots(DsfSession session) {
+ super(session, new String[] { Snapshots.class.getName() });
+ session.addServiceEventListener(this, null);
+ }
+
+ @Override
+ public void shutdown(RequestMonitor rm) {
+ getSession().removeServiceEventListener(this);
+ super.shutdown(rm);
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(final ISuspendedDMEvent e) {
+ if (!this.isSnapshot()) {
+ final String controlSetting = Album.getSnapshotCreationControl();
+ if (!controlSetting.equals(Album.CREATE_MANUAL)){
+ if (e.getReason() != StateChangeReason.SHAREDLIB
+ && (controlSetting.equals(Album.CREATE_WHEN_STOPPED) || controlSetting.equals(Album.CREATE_AT_BEAKPOINTS))) {
+ if (controlSetting.equals(Album.CREATE_WHEN_STOPPED) ||
+ e.getReason() == StateChangeReason.BREAKPOINT) {
+ Album.captureSnapshotForSession(getSession());
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.services.dsf;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.EDCSymbolReader;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.DebugInfoProviderFactory;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.ExecutableSymbolicsReaderFactory;
+import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
+import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext;
+import org.eclipse.cdt.debug.edc.services.IEDCModules;
+import org.eclipse.cdt.debug.edc.services.IEDCSymbols;
+import org.eclipse.cdt.debug.edc.services.IFrameRegisterProvider;
+import org.eclipse.cdt.debug.edc.services.IFrameRegisters;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider;
+import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.IModuleLineEntryProvider;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.ISymbol;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
+import org.eclipse.cdt.dsf.debug.service.ISymbols;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.model.ISourceLocator;
+
+public class Symbols extends AbstractEDCService implements ISymbols, IEDCSymbols {
+ private static Map<IPath, WeakReference<IEDCSymbolReader>> readerCache = new HashMap<IPath, WeakReference<IEDCSymbolReader>>();
+ private ISourceLocator sourceLocator;
+
+ public ISourceLocator getSourceLocator() {
+ return sourceLocator;
+ }
+
+ public void setSourceLocator(ISourceLocator sourceLocator) {
+ this.sourceLocator = sourceLocator;
+ }
+
+ public Symbols(DsfSession session) {
+ super(session, new String[] { IEDCSymbols.class.getName(), ISymbols.class.getName(), Symbols.class.getName() });
+ }
+
+ public void getSymbols(ISymbolDMContext symCtx, DataRequestMonitor<Iterable<ISymbolObjectDMContext>> rm) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCSymbols#getFunctionAtAddress(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress)
+ */
+ public IFunctionScope getFunctionAtAddress(ISymbolDMContext context, IAddress runtimeAddress) {
+ IEDCModules modulesService = getService(Modules.class);
+ IEDCModuleDMContext module = modulesService.getModuleByAddress(context, runtimeAddress);
+ if (module != null) {
+ IEDCSymbolReader reader = module.getSymbolReader();
+ if (reader != null) {
+ IScope scope = reader.getModuleScope().getScopeAtAddress(module.toLinkAddress(runtimeAddress));
+ while (scope != null && !(scope instanceof IFunctionScope)) {
+ scope = scope.getParent();
+ }
+ return (IFunctionScope) scope;
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCSymbols#getFunctionAtAddress(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress)
+ */
+ public String getSymbolNameAtAddress(ISymbolDMContext context, IAddress runtimeAddress) {
+ IEDCModules modulesService = getService(Modules.class);
+ IEDCModuleDMContext module = modulesService.getModuleByAddress(context, runtimeAddress);
+ if (module != null) {
+ IEDCSymbolReader reader = module.getSymbolReader();
+ if (reader != null) {
+ ISymbol symbol = reader.getSymbolAtAddress(module.toLinkAddress(runtimeAddress));
+ if (symbol != null)
+ return symbol.getName();
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCSymbols#getLineEntryForAddress(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress)
+ */
+ public ILineEntry getLineEntryForAddress(ISymbolDMContext context, IAddress runtimeAddress) {
+ IEDCModules modulesService = getService(Modules.class);
+ IEDCModuleDMContext module = modulesService.getModuleByAddress(context, runtimeAddress);
+ if (module != null) {
+ IEDCSymbolReader reader = module.getSymbolReader();
+ if (reader != null) {
+ IAddress linkAddress = module.toLinkAddress(runtimeAddress);
+ IModuleLineEntryProvider lineEntryProvider = reader.getModuleScope().getModuleLineEntryProvider();
+ return lineEntryProvider.getLineEntryAtAddress(linkAddress);
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCSymbols#getLineEntriesForAddressRange(org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext, org.eclipse.cdt.core.IAddress, org.eclipse.cdt.core.IAddress)
+ */
+ public List<ILineEntry> getLineEntriesForAddressRange(ISymbolDMContext context, IAddress start, IAddress end) {
+ List<ILineEntry> lineEntries = new ArrayList<ILineEntry>();
+
+ IEDCModules modulesService = getService(Modules.class);
+ IEDCModuleDMContext module = modulesService.getModuleByAddress(context, start);
+ if (module == null)
+ return lineEntries;
+
+ IAddress linkStartAddress = module.toLinkAddress(start);
+ IAddress linkEndAddress = module.toLinkAddress(end);
+
+ IEDCSymbolReader reader = module.getSymbolReader();
+ if (reader != null) {
+ if (linkStartAddress == null)
+ linkStartAddress = module.getSymbolReader().getModuleScope().getLowAddress();
+ if (linkEndAddress == null)
+ linkEndAddress = module.getSymbolReader().getModuleScope().getHighAddress();
+
+ IModuleScope moduleScope = reader.getModuleScope();
+
+ if (linkEndAddress.compareTo(moduleScope.getHighAddress()) > 0) {
+ // end address is out of the module sections.
+ // we'll keep getting source lines until we reach an address
+ // point where no source line is available.
+ linkEndAddress = moduleScope.getHighAddress();
+ }
+
+ IModuleLineEntryProvider lineEntryProvider = moduleScope.getModuleLineEntryProvider();
+
+ ILineEntry entry = lineEntryProvider.getLineEntryAtAddress(linkStartAddress);
+ while (entry != null && entry.getLowAddress().compareTo(linkEndAddress) < 0) {
+ lineEntries.add(entry);
+ // FIXME: this shouldn't happen
+ if (entry.getLowAddress().compareTo(entry.getHighAddress()) >= 0)
+ entry = lineEntryProvider.getLineEntryAtAddress(entry.getHighAddress().add(1));
+ else
+ entry = lineEntryProvider.getLineEntryAtAddress(entry.getHighAddress());
+ }
+ }
+
+ return lineEntries;
+ }
+
+ /**
+ * Get an accessor for registers in stack frames other than the current one.
+ * <p>
+ * Note: this is not meaningful by itselfis typically used from {@link StackFrameDMC#getFrameRegisters()}.
+ * @param context
+ * @param runtimeAddress
+ * @return {@link IFrameRegisters} or <code>null</code>
+ */
+ public IFrameRegisterProvider getFrameRegisterProvider(ISymbolDMContext context, IAddress runtimeAddress) {
+ Modules modulesService = getService(Modules.class);
+ IEDCModuleDMContext module = modulesService.getModuleByAddress(context, runtimeAddress);
+ if (module != null) {
+ IEDCSymbolReader reader = module.getSymbolReader();
+ if (reader != null) {
+ IFrameRegisterProvider frameRegisterProvider = reader.getModuleScope().getFrameRegisterProvider();
+ return frameRegisterProvider;
+ }
+ }
+ return null;
+ }
+
+ public static IEDCSymbolReader getSymbolReader(IPath modulePath) {
+
+ IEDCSymbolReader reader = null;
+ WeakReference<IEDCSymbolReader> cacheEntry = readerCache.get(modulePath);
+
+ if (cacheEntry != null)
+ reader = cacheEntry.get();
+
+ if (reader != null) {
+ if (reader.getSymbolFile() != null
+ && reader.getSymbolFile().toFile().exists()
+ && reader.getSymbolFile().toFile().lastModified() == reader.getModificationDate()) {
+ return reader;
+ }
+
+ // it's been deleted or modified. remove it from the cache
+ readerCache.remove(modulePath);
+ }
+
+ IExecutableSymbolicsReader exeReader = ExecutableSymbolicsReaderFactory.createFor(modulePath);
+ if (exeReader != null) {
+ IDebugInfoProvider debugProvider = DebugInfoProviderFactory.createFor(modulePath, exeReader); // may be null
+ if (debugProvider != null) {
+ // if debug info came from a different file, the "executable" may be that symbol file too
+ if (!exeReader.getSymbolFile().equals(debugProvider.getExecutableSymbolicsReader().getSymbolFile())) {
+ exeReader.dispose();
+ exeReader = debugProvider.getExecutableSymbolicsReader();
+ }
+ }
+ reader = new EDCSymbolReader(exeReader, debugProvider);
+ }
+
+ if (reader != null) {
+ readerCache.put(modulePath, new WeakReference<IEDCSymbolReader>(reader));
+ }
+
+ return reader;
+ }
+
+ @Override
+ public void shutdown(RequestMonitor rm) {
+ super.shutdown(rm);
+ }
+
+ /**
+ * This is exposed only for testing.
+ */
+ public static void releaseReaderCache() {
+ Collection<WeakReference<IEDCSymbolReader>> readers = readerCache.values();
+ for (WeakReference<IEDCSymbolReader> readerRef : readers) {
+ IEDCSymbolReader reader = readerRef.get();
+ if (reader != null)
+ reader.shutDown();
+ }
+ readerCache.clear();
+ }
+
+ /**
+ * A wrapper method that calls into symbol reader to get runtime address(es)
+ * for a given function name.
+ * Currently this method use symbol table instead of debug info (e.g. dwarf2)
+ * to get function address. See reason in the implementation.
+ *
+ * @param module where the runtime address is.
+ * @param functionName
+ * @return list of runtime addresses.
+ */
+ public List<IAddress> getFunctionAddress(IEDCModuleDMContext module, String functionName) {
+
+ List<IAddress> ret = new ArrayList<IAddress>(2);
+
+ IEDCSymbolReader symReader = module.getSymbolReader();
+ if (symReader == null)
+ return ret;
+
+ // Remove "()" if any
+ int parenIndex = functionName.indexOf('(');
+ if (parenIndex >= 0)
+ functionName = functionName.substring(0, parenIndex);
+
+ // Supposedly we could call this to get what we need, but this may cause full parse of
+ // debug info and lead to heap overflow for a large symbol file (over one giga bytes of
+ // memory required in parsing a 180M symbol file) and chokes the debugger.
+ // So before a good solution is available, we resort to symbol table of the executable.
+ // ................04/02/10
+// Collection<IFunctionScope> functions = symReader.getModuleScope().getFunctionsByName(function);
+// for (IFunctionScope f : functions) {
+// IAddress breakAddr = f.getLowAddress();
+// ...
+
+ // assume it's the human-readable name first
+ Collection<ISymbol> symbols = symReader.findUnmangledSymbols(functionName);
+ if (symbols.isEmpty()) {
+ // else look for a raw symbol
+ symbols = symReader.findSymbols(functionName);
+ }
+
+ for (ISymbol symbol : symbols) {
+ // don't consider zero sized symbol.
+ if (symbol.getSize() == 0)
+ continue;
+
+ IAddress addr = symbol.getAddress();
+ // convert from link to runtime address
+ addr = module.toRuntimeAddress(addr);
+
+ ret.add(addr);
+ }
+
+ return ret;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.snapshot;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
+
+import org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.PathUtils;
+import org.eclipse.cdt.debug.edc.internal.ZipFileUtils;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.debug.edc.services.Stack;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.cdt.debug.internal.core.sourcelookup.MapEntrySourceContainer;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
+import org.eclipse.cdt.dsf.concurrent.Query;
+import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+import org.eclipse.cdt.dsf.service.DsfServicesTracker;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.dsf.service.DsfSession.SessionEndedListener;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer;
+import org.eclipse.debug.internal.core.LaunchManager;
+import org.osgi.framework.Bundle;
+import org.osgi.service.prefs.BackingStoreException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * The Album class represents a series of snapshots that record moments in a
+ * debug session. An Album manages the collection of snapshots, common resources
+ * such as source files, persistence, and association with debug sessions.
+ *
+ * An Album is usually created during a debug session, saved at the conclusion
+ * of the session, and reopened by a launch delegate for a new snapshot debug
+ * session.
+ *
+ * When an Album is saved it's data and resources are archived in a snapshot
+ * file in a default location. When reopened the contents are expanded into a
+ * temporary directory and used to recreate the debug session.
+ */
+@SuppressWarnings("restriction")
+public class Album extends PlatformObject implements IAlbum {
+
+ // XML element names
+ public static final String SNAPSHOT = "snapshot";
+ private static final String ALBUM = "album";
+ private static final String LAUNCH = "launch";
+ private static final String RESOURCES = "resources";
+ private static final String FILE = "file";
+ private static final String INFO = "info";
+
+ public static final String METADATA = "snapshotMetaData";
+ public static final String SNAPSHOT_LIST = "snapshots";
+
+ private static final String ALBUM_DATA = "album.xml";
+ private static final String ALBUM_VERSION = "100";
+
+ private static String[] DSA_FILE_EXTENSIONS = new String[] {"dsa"};
+
+ // Preferences
+ public static final String PREF_CREATION_CONTROL = "creation_control";
+ public static final String CREATE_MANUAL = "manual";
+ public static final String CREATE_WHEN_STOPPED = "suspend";
+ public static final String CREATE_AT_BEAKPOINTS = "breakpoints";
+
+ public static final String PREF_VARIABLE_CAPTURE_DEPTH = "variable_capture_depth";
+ public static final String PLAY_SNAPSHOT_DELAY_TIME = "play_snapshot_delay_time";
+
+ private static final String CAMERA_CLICK_WAV = "/sounds/camera_click.wav";
+
+ private Document document;
+ private Element albumRootElement;
+
+ private final List<Snapshot> snapshotList = new ArrayList<Snapshot>();
+ private String sessionID = "";
+ private String recordingSessionID = "";
+ private IPath albumRootDirectory;
+ private boolean launchConfigSaved;
+ private String launchType;
+ private HashMap<String, Object> launchProperties;
+ private String launchName = "";
+ private String name;
+ private boolean loaded;
+ private boolean metaDataLoaded;
+ private final Set<IPath> files = new HashSet<IPath>();
+
+ private int currentSnapshotIndex;
+ private IPath location;
+ private boolean resourceListSaved;
+ private boolean metadataSaved;
+ private boolean albumInfoSaved;
+ private String displayName;
+ private boolean playingSnapshots;
+
+ /**
+ * Listener for state changes on albums
+ */
+ protected static List<ISnapshotAlbumEventListener> listeners = Collections.synchronizedList(new ArrayList<ISnapshotAlbumEventListener>());
+
+ private static Map<String, Album> albumsBySessionID = Collections.synchronizedMap(new HashMap<String, Album>());
+ private static Map<String, Album> albumsRecordingBySessionID = Collections.synchronizedMap(new HashMap<String, Album>());
+ private static Map<IPath, Album> albumsByLocation = Collections.synchronizedMap(new HashMap<IPath, Album>());
+ private static Map<String, Integer> launchNames = Collections.synchronizedMap(new HashMap<String, Integer>());
+
+ private static boolean sessionEndedListenerAdded;
+ private static SessionEndedListener sessionEndedListener = new SessionEndedListener() {
+
+ public void sessionEnded(DsfSession session) {
+ Album album = albumsRecordingBySessionID.get(session.getId());
+ if (album == null)
+ album = albumsBySessionID.get(session.getId());
+ if (album != null && session.getId().equals(album.getRecordingSessionID())) {
+ album.saveResources(new NullProgressMonitor());
+ album.setRecordingSessionID("");
+ }
+ synchronized (albumsRecordingBySessionID) {
+ albumsRecordingBySessionID.remove(session.getId());
+ }
+ synchronized (albumsBySessionID) {
+ albumsBySessionID.remove(session.getId());
+ }
+
+ if (album != null) {
+ for (ISnapshotAlbumEventListener l : new ArrayList<ISnapshotAlbumEventListener>(listeners)) {
+ l.snapshotSessionEnded(album, session);
+ }
+ }
+ }
+ };
+
+ public interface IAlbumArchiveEntry {
+
+ public String createEntryName(File file);
+
+ }
+
+ public Album() {
+ super();
+ try {
+ setDocument(DebugPlugin.newDocument());
+ if (!sessionEndedListenerAdded)
+ DsfSession.addSessionEndedListener(sessionEndedListener);
+ sessionEndedListenerAdded = true;
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getName()
+ */
+ public String getName() {
+ if (name == null) {
+ name = getDefaultAlbumName();
+ }
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getDisplayName()
+ */
+ public String getDisplayName() {
+ if (displayName == null || displayName.length() == 0) {
+ displayName = getName();
+ }
+ return displayName;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getSessionID()
+ */
+ public String getSessionID() {
+ sessionID = "";
+ if (albumsBySessionID != null) {
+ for (Map.Entry<String, Album> entry : albumsBySessionID.entrySet()){
+ if (entry.getValue().location != null && entry.getValue().location.equals(getLocation())){
+ sessionID = entry.getKey();
+ }
+ }
+ }
+ return sessionID;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getRecordingSessionID()
+ */
+ public String getRecordingSessionID() {
+ return recordingSessionID;
+ }
+
+ public void setRecordingSessionID(String sessionID) {
+ this.recordingSessionID = sessionID;
+ if (sessionID.length() > 0)
+ albumsRecordingBySessionID.put(sessionID, this);
+ }
+
+ public void setSessionID(String sessionID) {
+ this.sessionID = sessionID;
+ if (sessionID.length() > 0)
+ albumsBySessionID.put(sessionID, this);
+ }
+
+ /**
+ * Is the album currently open for recording
+ * @param sessionId
+ * @return true if the album is currently being recording by an active debug session
+ */
+ public static boolean isSnapshotSession(String sessionId) {
+ EDCLaunch launch = EDCLaunch.getLaunchForSession(sessionId);
+ return launch != null && launch.isSnapshotLaunch();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#createSnapshot(org.eclipse.cdt.dsf.service.DsfSession, org.eclipse.cdt.debug.edc.internal.services.dsf.Stack.StackFrameDMC, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public Snapshot createSnapshot(DsfSession session, StackFrameDMC stackFrame, IProgressMonitor monitor) throws Exception {
+ SubMonitor progress = SubMonitor.convert(monitor, "Creating Snapshot", 10000);
+ configureAlbum();
+ progress.worked(100);
+
+ if (getLocation() == null || !getLocation().toFile().exists()){
+ createEmptyAlbum();
+ }
+
+ Snapshot snapshot = new Snapshot(this, session, stackFrame);
+ snapshot.writeSnapshotData(progress.newChild(7900));
+
+ snapshotList.add(snapshot);
+ saveAlbum(progress.newChild(1000));
+
+ monitor.done();
+ return snapshot;
+ }
+
+ private void configureAlbum() {
+ saveAlbumInfo();
+ saveLaunchConfiguration();
+ }
+
+ private void saveAlbumInfo() {
+ if (!albumInfoSaved) {
+ Element infoElement = document.createElement(INFO);
+ infoElement.setAttribute("version", ALBUM_VERSION);
+ Calendar calendar = Calendar.getInstance();
+ infoElement.setAttribute("month", Integer.toString(calendar.get(Calendar.MONTH)));
+ infoElement.setAttribute("day", Integer.toString(calendar.get(Calendar.DATE)));
+ infoElement.setAttribute("year", Integer.toString(calendar.get(Calendar.YEAR)));
+ infoElement.setAttribute("hour", Integer.toString(calendar.get(Calendar.HOUR)));
+ infoElement.setAttribute("minute", Integer.toString(calendar.get(Calendar.MINUTE)));
+ infoElement.setAttribute("second", Integer.toString(calendar.get(Calendar.SECOND)));
+
+ Properties systemProps = System.getProperties();
+ Map<String, Object> infoProps = new HashMap<String, Object>();
+ Set<Object> systemKeys = systemProps.keySet();
+
+ for (Object sysKey : systemKeys) {
+ if (sysKey instanceof String)
+ infoProps.put((String) sysKey, systemProps.get(sysKey));
+ }
+ Element propsElement = SnapshotUtils.makeXMLFromProperties(document, infoProps);
+ infoElement.appendChild(propsElement);
+
+ getAlbumRootElement().appendChild(infoElement);
+ albumInfoSaved = true;
+ }
+ }
+
+ private void saveResourceList() {
+ if (!resourceListSaved) {
+ Element resourcesElement = document.createElement(RESOURCES);
+ for (IPath filePath : files) {
+ Element fileElement = document.createElement(FILE);
+ fileElement.setAttribute("path", filePath.toOSString());
+ resourcesElement.appendChild(fileElement);
+ }
+ getAlbumRootElement().appendChild(resourcesElement);
+ resourceListSaved = true;
+ }
+ }
+
+ private void saveSnapshotMetadata() {
+ if (!metadataSaved || isRecording()) {
+
+ if (metadataSaved){
+ // If metatdata is saved, it must be a live debug session so
+ // we need to add a new snapshot to the snapshot list
+ NodeList snapMetaDataNode = document.getElementsByTagName(METADATA);
+ assert snapMetaDataNode.item(0) != null;
+ document.getDocumentElement().removeChild(snapMetaDataNode.item(0));
+ }
+
+ Element metadataElement = document.createElement(METADATA);
+
+ Element albumElement = document.createElement(ALBUM);
+ albumElement.setAttribute("albumName", this.getDisplayName());
+ metadataElement.appendChild(albumElement);
+
+ Element snapshotsElement = document.createElement(SNAPSHOT_LIST);
+ metadataElement.appendChild(snapshotsElement);
+
+ for (Snapshot snap : snapshotList) {
+ Element snapshotMetadataElement = document.createElement(SNAPSHOT);
+ if (snap.getSnapshotDisplayName().length() == 0) {
+ snapshotMetadataElement.setAttribute("displayName", snap.getSnapshotFileName());
+ } else {
+ snapshotMetadataElement.setAttribute("displayName", snap.getSnapshotDisplayName());
+ }
+ if (snap.getCreationDate() != null) {
+ snapshotMetadataElement.setAttribute("date", snap.getCreationDate().toString());
+ } else {
+ snapshotMetadataElement.setAttribute("date", "unknown");
+ }
+
+ snapshotMetadataElement.setAttribute("description", snap.getSnapshotDescription());
+ snapshotMetadataElement.setAttribute("fileName", snap.getSnapshotFileName());
+ snapshotMetadataElement.setAttribute("referenceLocationSourceFile", snap.getReferenceLocationSourceFile());
+ snapshotMetadataElement.setAttribute("referenceLocationLineNumber", String.valueOf(snap.getReferenceLocationLineNumber()));
+
+ snapshotsElement.appendChild(snapshotMetadataElement);
+ }
+ getAlbumRootElement().appendChild(metadataElement);
+ metadataSaved = true;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void saveLaunchConfiguration() {
+ if (!launchConfigSaved) {
+ EDCLaunch launch = EDCLaunch.getLaunchForSession(getRecordingSessionID());
+ try {
+ Map<String, Object> map = launch.getLaunchConfiguration().getAttributes();
+ Element launchElement = document.createElement(LAUNCH);
+ launchType = launch.getLaunchConfiguration().getType().getIdentifier();
+ launchName = launch.getLaunchConfiguration().getName();
+ Integer count = launchNames.get(launchName);
+ if (count == null) {
+ launchNames.put(launchName, new Integer(0));
+ }
+ else {
+ count = new Integer(count.intValue() + 1);
+ launchNames.put(launchName, count);
+ launchName += " (" + count.toString() + ")";
+ }
+ launchElement.setAttribute("type", launchType);
+ launchElement.setAttribute("name", launchName);
+ Element propsElement = SnapshotUtils.makeXMLFromProperties(document, map);
+ launchElement.appendChild(propsElement);
+ getAlbumRootElement().appendChild(launchElement);
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ launchConfigSaved = true;
+ }
+ }
+
+ private static void addZipEntry(ZipOutputStream zipOut, IAlbumArchiveEntry entry, File file)
+ throws FileNotFoundException, IOException {
+ if (file.exists()) {
+ if (file.isDirectory()) {
+ for (File child : file.listFiles()) {
+ addZipEntry(zipOut, entry, child);
+ }
+ } else {
+ // Add ZIP entry to output stream.m
+ String path = ""; //$NON-NLS-1$
+
+ if (entry != null) {
+ path = entry.createEntryName(file);
+ } else {
+ path = file.getName();
+ }
+
+ zipOut.putNextEntry(new ZipEntry(path));
+
+ // Create a buffer for reading the files
+ byte[] buf = new byte[1024];
+
+ // Transfer bytes from the file to the ZIP file
+ // and compress the files
+ FileInputStream in = new FileInputStream(file);
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ zipOut.write(buf, 0, len);
+ }
+
+ // Complete the entry
+ zipOut.closeEntry();
+ in.close();
+ }
+ }
+ }
+
+ /**
+ * Create and write a full snapshot album from scratch
+ */
+ private void saveAlbum(IProgressMonitor monitor) {
+
+ IPath zipPath = getLocation();
+ ZipOutputStream zipOut = null;
+ try {
+ SubMonitor progress = SubMonitor.convert(monitor, 2000 + (snapshotList.size() * 1000));
+ progress.subTask("Saving album data");
+
+ zipOut = new ZipOutputStream(new FileOutputStream(zipPath.toFile()));
+
+ zipOut.putNextEntry(new ZipEntry(ALBUM_DATA));
+
+ saveResourceList();
+ progress.worked(1000);
+ saveSnapshotMetadata();
+ progress.worked(1000);
+
+ String xml = LaunchManager.serializeDocument(document);
+ zipOut.write(xml.getBytes("UTF8")); //$NON-NLS-1$
+ zipOut.closeEntry();
+
+ for (Snapshot snap : snapshotList) {
+ zipOut.putNextEntry(new ZipEntry(snap.getSnapshotFileName()));
+ snap.saveSnapshot(zipOut);
+ progress.worked(1000);
+ }
+
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ } finally {
+ try {
+ if (zipOut != null) {
+ zipOut.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Create and write a full snapshot album from scratch
+ */
+ private void saveResources(IProgressMonitor monitor) {
+
+ IPath zipPath = getLocation();
+ ZipOutputStream zipOut = null;
+ try {
+ // TODO: Here's we're just rewriting the entire album again
+ // Need to just add the resources alone using proper utils
+ zipOut = new ZipOutputStream(new FileOutputStream(zipPath.toFile()));
+
+ for (IPath path : files) {
+
+ IAlbumArchiveEntry entry = new IAlbumArchiveEntry() {
+
+ public String createEntryName(File file) {
+ StringBuffer entryPath = new StringBuffer();
+
+ entryPath.append("Resources/");
+
+ IPath filepath = new Path(file.getAbsolutePath());
+
+ String deviceName = filepath.getDevice();
+ if (deviceName != null) {
+ // Remove the : from the end
+ entryPath.append(deviceName.substring(0, deviceName.length() - 1));
+ entryPath.append("/");
+ }
+
+ String[] segments = filepath.segments();
+ int numSegments = segments.length - 1;
+
+ for (int i = 0; i < numSegments; i++) {
+ entryPath.append(segments[i]);
+ entryPath.append("/");
+ }
+ entryPath.append(file.getName());
+ return entryPath.toString();
+ }
+ };
+ addZipEntry(zipOut, entry, path.toFile());
+ if (monitor != null) {
+ monitor.worked(1);
+ }
+ }
+ zipOut.putNextEntry(new ZipEntry(ALBUM_DATA));
+
+ saveResourceList();
+ saveSnapshotMetadata();
+
+ String xml = LaunchManager.serializeDocument(document);
+ zipOut.write(xml.getBytes("UTF8")); //$NON-NLS-1$
+ zipOut.closeEntry();
+
+ for (Snapshot snap : snapshotList) {
+ zipOut.putNextEntry(new ZipEntry(snap.getSnapshotFileName()));
+ snap.saveSnapshot(zipOut);
+ }
+
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ } finally {
+ try {
+ if (zipOut != null) {
+ zipOut.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private String getDefaultAlbumName() {
+ return getLaunchName();
+ }
+
+ public void saveAlbum(IPath path) throws TransformerException, IOException {
+ String xml = LaunchManager.serializeDocument(document);
+ File file = path.toFile();
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ FileOutputStream stream = new FileOutputStream(file);
+ stream.write(xml.getBytes("UTF8")); //$NON-NLS-1$
+ stream.close();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#openSnapshot(int)
+ */
+ public void openSnapshot(final int index) {
+
+ final DsfSession session = DsfSession.getSession(sessionID);
+
+ DsfRunnable openIt = new DsfRunnable() {
+ public void run() {
+ currentSnapshotIndex = index;
+ try {
+ loadAlbum(false);
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ if (session != null && snapshotList.size() > index) {
+ Snapshot snapshot = snapshotList.get(index);
+ snapshot.open(session);
+ // Fire the event
+ for (ISnapshotAlbumEventListener l : new ArrayList<ISnapshotAlbumEventListener>(listeners)) {
+ l.snapshotOpened(snapshot);
+ }
+ }
+ }
+ };
+
+ if (session != null && session.getExecutor() != null)
+ {
+ if (session.getExecutor().isInExecutorThread())
+ openIt.run();
+ else
+ session.getExecutor().execute(openIt);
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getCurrentSnapshotIndex()
+ */
+ public int getCurrentSnapshotIndex() {
+ return currentSnapshotIndex;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#openNextSnapshot()
+ */
+ public void openNextSnapshot() throws Exception {
+ int nextIndex = currentSnapshotIndex + 1;
+ if (nextIndex >= snapshotList.size())
+ nextIndex = 0;
+ openSnapshot(nextIndex);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#openPreviousSnapshot()
+ */
+ public void openPreviousSnapshot() throws Exception {
+ int previousIndex = currentSnapshotIndex - 1;
+ if (previousIndex < 0)
+ previousIndex = snapshotList.size() - 1;
+ openSnapshot(previousIndex);
+ }
+
+ public void loadAlbum(boolean force) throws ParserConfigurationException, SAXException, IOException {
+ if (force)
+ loaded = false;
+ if (!loaded) {
+ File albumFile = location.toFile();
+ setName(albumFile.getName());
+
+ if (!isRecording()){
+ // not creating the snapshot, so must be snapshot play back
+ try {
+ ZipFileUtils.unzipFiles(albumFile, getAlbumRootDirectory().toOSString(), new NullProgressMonitor());
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ BufferedInputStream stream = ZipFileUtils.openFile(albumFile, ALBUM_DATA, DSA_FILE_EXTENSIONS);
+ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ parser.setErrorHandler(new DefaultHandler());
+ setDocument(parser.parse(new InputSource(stream)));
+
+ loadAlbumInfo();
+ loadLaunchConfiguration();
+ loadResourceList();
+ try {
+ loadSnapshotMetadata();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ loaded = true;
+ ZipFileUtils.unmount();
+ }
+ }
+ }
+
+ /**
+ * A lightwieght parse to get basic album info and what snapshots are
+ * available.
+ *
+ * @throws ParserConfigurationException
+ * @throws SAXException
+ * @throws IOException
+ */
+ public void loadAlbumMetada(boolean force) throws Exception {
+ if (force)
+ metaDataLoaded = false;
+ if (!metaDataLoaded) {
+
+ File albumFile = location.toFile();
+ setDisplayName(albumFile.getName());
+
+ BufferedInputStream stream = null;
+ try {
+ stream = ZipFileUtils.openFile(albumFile, ALBUM_DATA, DSA_FILE_EXTENSIONS);
+ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ parser.setErrorHandler(new DefaultHandler());
+ setDocument(parser.parse(new InputSource(stream)));
+ loadSnapshotMetadata();
+ loadLaunchConfiguration(); // need to load launch config in case we need to delete it
+
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError("Failed to load album: " + getName(), e);
+ } finally {
+ metaDataLoaded = true;
+ ZipFileUtils.unmount();
+ }
+ }
+ }
+
+ private void loadAlbumInfo() {
+ document.getElementsByTagName(INFO).item(0);
+ }
+
+ private void setDocument(Document newDoc)
+ {
+ document = newDoc;
+ albumRootElement = null;
+ }
+
+ private Element getAlbumRootElement()
+ {
+ if (albumRootElement == null)
+ {
+ NodeList albumRootElements = document.getElementsByTagName(ALBUM);
+ if (albumRootElements.getLength() == 0)
+ {
+ albumRootElement = document.createElement(ALBUM);
+ document.appendChild(albumRootElement);
+ }
+ else
+ {
+ albumRootElement = (Element) albumRootElements.item(0);
+ }
+ }
+ return albumRootElement;
+ }
+
+ private void loadResourceList() {
+ NodeList resources = document.getElementsByTagName(RESOURCES);
+ NodeList elementFiles = ((Element) resources.item(0)).getElementsByTagName(FILE);
+ int numFiles = elementFiles.getLength();
+ for (int i = 0; i < numFiles; i++) {
+ Element fileElement = (Element) elementFiles.item(i);
+ String elementPath = fileElement.getAttribute("path");
+ files.add(PathUtils.createPath(elementPath)); // for cross-created snapshot
+ }
+ }
+
+ private void loadSnapshotMetadata() throws Exception {
+ snapshotList.clear();
+ NodeList snapMetaDataNode = document.getElementsByTagName(METADATA);
+
+ if (snapMetaDataNode.getLength() == 0) {
+ throw new Exception("Invalid or corrupted Album : " + getName());
+ }
+ NodeList albumNameElement = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(ALBUM);
+ Element albumElement = (Element) albumNameElement.item(0);
+ String albumDisplayName = albumElement.getAttribute("albumName");
+
+ setDisplayName(albumDisplayName);
+
+ NodeList elementSnapshots = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(SNAPSHOT);
+ int numSnapshots = elementSnapshots.getLength();
+ for (int i = 0; i < numSnapshots; i++) {
+ Element snapshotElement = (Element) elementSnapshots.item(i);
+ String elementDescription = snapshotElement.getAttribute("description");
+ String elementDate = snapshotElement.getAttribute("date");
+ String elementDispalyName = snapshotElement.getAttribute("displayName");
+ String elementFileName = snapshotElement.getAttribute("fileName");
+ String referenceLocationSourceFile = snapshotElement.getAttribute("referenceLocationSourceFile");
+ String referenceLocationLineNumber = snapshotElement.getAttribute("referenceLocationLineNumber");
+
+ Snapshot s = new Snapshot(this);
+ s.setCreationDate(elementDate);
+ s.setSnapshotFileName(elementFileName);
+ s.setSnapshotDisplayName(elementDispalyName);
+ s.setSnapshotDescription(elementDescription);
+ if (referenceLocationLineNumber.length() > 0){
+ s.setReferenceLocationLineNumber(Long.parseLong(referenceLocationLineNumber));
+ }
+ s.setReferenceLocationSourceFile(referenceLocationSourceFile);
+ snapshotList.add(s);
+ }
+ }
+
+ private void loadLaunchConfiguration() {
+ NodeList launchElements = document.getElementsByTagName(LAUNCH);
+ Element launchElement = (Element) launchElements.item(0);
+ if (launchElement == null){
+ return;
+ }
+ launchType = launchElement.getAttribute("type");
+ launchName = launchElement.getAttribute("name");
+
+ Element propElement = (Element) launchElement.getElementsByTagName(SnapshotUtils.PROPERTIES).item(0);
+ launchProperties = new HashMap<String, Object>();
+ try {
+ SnapshotUtils.initializeFromXML(propElement, launchProperties);
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter.equals(Document.class))
+ return document;
+
+ return super.getAdapter(adapter);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getAlbumRootDirectory()
+ */
+ public IPath getAlbumRootDirectory() {
+ if (albumRootDirectory == null) {
+ IPath path = EDCDebugger.getDefault().getStateLocation().append("SnapshotAlbums");
+ String locationName = location.lastSegment();
+ int extension = locationName.lastIndexOf(".");
+ if (extension > 0) {
+ locationName = locationName.substring(0, extension);
+ }
+ path = path.append(locationName);
+ File dir = path.toFile();
+ if (!dir.exists()) {
+ dir.mkdirs();
+ }
+ albumRootDirectory = path;
+ }
+ return albumRootDirectory;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getLaunchTypeID()
+ */
+ public String getLaunchTypeID() {
+ return launchType;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getLaunchProperties()
+ */
+ public HashMap<String, Object> getLaunchProperties() {
+ return launchProperties;
+ }
+
+ public void setLaunchProperties(HashMap<String, Object> launchProperties) {
+ this.launchProperties = launchProperties;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getLaunchName()
+ */
+ public String getLaunchName() {
+ return launchName;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#playSnapshots(org.eclipse.cdt.dsf.service.DsfSession)
+ */
+ public void playSnapshots() {
+ if (!isPlayingSnapshots())
+ {
+ Job playSnapshotsJob = new Job("Play Snapshots for Album " + getDisplayName()){
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ monitor.beginTask("Play Snapshots for Album " + getDisplayName(), IProgressMonitor.UNKNOWN);
+ while (isPlayingSnapshots() && !monitor.isCanceled())
+ {
+ Album.this.openNextSnapshot();
+ Thread.sleep(getPlaySnapshotInterval());
+ }
+ setPlayingSnapshots(false);
+ monitor.done();
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ return new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, null, e);
+ }
+ return Status.OK_STATUS;
+ }
+
+ };
+ setPlayingSnapshots(true);
+ playSnapshotsJob.schedule();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#addFile(org.eclipse.core.runtime.IPath)
+ */
+ public void addFile(IPath path) {
+ files.add(path);
+ }
+
+ public static Album getAlbumByLocation(IPath path) {
+ return albumsByLocation.get(path);
+ }
+
+ public static IAlbum getAlbumBySession(String sessionId) {
+ return albumsBySessionID.get(sessionId);
+ }
+
+ public static Album getRecordingForSession(String sessionId) {
+ return albumsRecordingBySessionID.get(sessionId);
+ }
+
+ public void setLocation(IPath albumPath) {
+ this.location = albumPath;
+ albumsByLocation.put(albumPath, this);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getLocation()
+ */
+ public IPath getLocation() {
+ return location;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#configureSourceLookupDirector(org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector)
+ */
+ public void configureSourceLookupDirector(ISourceLookupDirector director) {
+ MappingSourceContainer sourceContainer = new MappingSourceContainer(getName());
+ configureMappingSourceContainer(sourceContainer);
+ ArrayList<ISourceContainer> containers = new ArrayList<ISourceContainer>(Arrays.asList(director
+ .getSourceContainers()));
+ containers.add(sourceContainer);
+
+ DirectorySourceContainer directoryContainer = new DirectorySourceContainer(getResourcesDirectory(), true);
+ containers.add(directoryContainer);
+
+ director.setSourceContainers(containers.toArray(new ISourceContainer[containers.size()]));
+ }
+
+ protected IPath getResourcesDirectory()
+ {
+ return getAlbumRootDirectory().append("Resources");
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#configureMappingSourceContainer(org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer)
+ */
+ public void configureMappingSourceContainer(MappingSourceContainer mappingContainer) {
+ IPath albumRoot = getResourcesDirectory();
+ List<MapEntrySourceContainer> containers = new ArrayList<MapEntrySourceContainer>();
+ Set<String> devicesAlreadyAdded = new HashSet<String>();
+ String deviceName = null;
+ for (IPath iPath : files) {
+ deviceName = iPath.getDevice();
+ if (deviceName != null && !devicesAlreadyAdded.contains(deviceName))
+ {
+ String albumRootSuffix = deviceName;
+ if (albumRootSuffix.endsWith(":"))
+ albumRootSuffix = albumRootSuffix.substring(0, albumRootSuffix.length() - 1);
+ devicesAlreadyAdded.add(deviceName);
+ MapEntrySourceContainer newContainer = new MapEntrySourceContainer(PathUtils.createPath(deviceName), albumRoot.append(albumRootSuffix));
+ containers.add(newContainer);
+
+ }
+ }
+ mappingContainer.addMapEntries(containers.toArray(new MapEntrySourceContainer[containers.size()]));
+ }
+
+ public static void setVariableCaptureDepth(int newSetting) {
+ IEclipsePreferences scope = InstanceScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
+ scope.putInt(PREF_VARIABLE_CAPTURE_DEPTH, newSetting);
+ try {
+ scope.flush();
+ } catch (BackingStoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ public static int getVariableCaptureDepth() {
+ return Platform.getPreferencesService().getInt(EDCDebugger.PLUGIN_ID,
+ PREF_VARIABLE_CAPTURE_DEPTH, 5, null);
+ }
+
+ public static void setPlaySnapshotInterval(int delayInMilliseconds) {
+ IEclipsePreferences scope = InstanceScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
+ scope.putInt(PLAY_SNAPSHOT_DELAY_TIME, delayInMilliseconds);
+ try {
+ scope.flush();
+ } catch (BackingStoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ public static int getPlaySnapshotInterval() {
+ return Platform.getPreferencesService().getInt(EDCDebugger.PLUGIN_ID,
+ PLAY_SNAPSHOT_DELAY_TIME, 5000, null);
+ }
+
+ public static void setSnapshotCreationControl(String newSetting) {
+ IEclipsePreferences scope = InstanceScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
+ scope.put(PREF_CREATION_CONTROL, newSetting);
+ try {
+ scope.flush();
+ } catch (BackingStoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ public static String getSnapshotCreationControl() {
+ return Platform.getPreferencesService().getString(EDCDebugger.PLUGIN_ID,
+ PREF_CREATION_CONTROL, CREATE_MANUAL, null);
+ }
+
+ protected static void playSnapshotSound() {
+ Bundle bundle = Platform.getBundle(EDCDebugger.getUniqueIdentifier());
+ if (bundle == null)
+ return;
+
+ URL url = null;
+ try {
+ url = FileLocator.toFileURL(bundle.getEntry(CAMERA_CLICK_WAV));
+ } catch (IOException e) {
+ } catch (RuntimeException e){
+ }
+ finally {
+ if (url != null){
+ File f = new File(url.getFile());
+ SnapshotUtils.playSoundFile(f);
+ }
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getSnapshots()
+ */
+ public List<Snapshot> getSnapshots() {
+ if (snapshotList == null || snapshotList.size() == 0) {
+ try {
+ loadAlbumMetada(false);
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError("Failed to load snapshots", e);
+ }
+ }
+
+ return snapshotList;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#isLoaded()
+ */
+ public boolean isLoaded() {
+ return loaded;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#getIndexOfSnapshot(org.eclipse.cdt.debug.edc.internal.snapshot.Snapshot)
+ */
+ public int getIndexOfSnapshot(Snapshot snap) {
+ return snapshotList.indexOf(snap);
+ }
+
+ public void setCurrentSnapshotIndex(int index) {
+ if (currentSnapshotIndex >= 0 && currentSnapshotIndex < snapshotList.size()) {
+ currentSnapshotIndex = index;
+ }
+ }
+
+ /**
+ * Update album.xml within the Album's .dsa file with new Snapshot data
+ *
+ * @param albumName
+ * - Name of album to display. Use null if value should not be
+ * updated.
+ * @param snap
+ * - Specific snapshot to update. Use null is snapshot should not
+ * be updated.
+ */
+ public void updateSnapshotMetaData(String albumName, Snapshot snap) {
+ NodeList snapMetaDataNode = document.getElementsByTagName(METADATA);
+
+ // try to update album display name
+ if (albumName != null) {
+ NodeList albumNameNode = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(ALBUM);
+ ((Element) albumNameNode.item(0)).setAttribute("albumName", albumName);
+ }
+
+ // try to update snapshot data
+ if (snap != null) {
+
+ NodeList elementSnapshots = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(SNAPSHOT);
+
+ int numSnapshots = elementSnapshots.getLength();
+ for (int i = 0; i < numSnapshots; i++) {
+ Element currentSnapshotNode = (Element) elementSnapshots.item(i);
+ String fileName = currentSnapshotNode.getAttribute("fileName");
+ if (fileName.equals(snap.getSnapshotFileName())) {
+
+ currentSnapshotNode.setAttribute("description", snap.getSnapshotDescription());
+ currentSnapshotNode.setAttribute("displayName", snap.getSnapshotDisplayName());
+
+ break;
+ }
+ }
+ }
+
+ saveAlbumData();
+
+ // refresh all data
+ try {
+ loadAlbumMetada(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void saveAlbumData() {
+ try {
+ File tempFile = File.createTempFile("album", ".xml");
+ File tempFile2 = new File(tempFile.getParent() + File.separator + ALBUM_DATA);
+ tempFile.delete();
+ if (!tempFile2.exists()) {
+ tempFile2.delete();
+ }
+ tempFile2.createNewFile();
+ saveAlbum(new Path(tempFile2.toString()));
+ File[] fileList = { tempFile2 };
+ ZipFileUtils.addFilesToZip(fileList, getLocation().toFile(), DSA_FILE_EXTENSIONS);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (TransformerException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#deleteSnapshot(org.eclipse.cdt.debug.edc.internal.snapshot.Snapshot)
+ */
+ public void deleteSnapshot(Snapshot snap) {
+
+ NodeList snapMetaDataNode = document.getElementsByTagName(METADATA);
+
+ NodeList elementSnapshotList = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(SNAPSHOT_LIST);
+ NodeList elementSnapshots = ((Element) snapMetaDataNode.item(0)).getElementsByTagName(SNAPSHOT);
+
+ int numSnapshots = elementSnapshots.getLength();
+ for (int i = 0; i < numSnapshots; i++) {
+ Element currentSnapshotNode = (Element) elementSnapshots.item(i);
+ String fileName = currentSnapshotNode.getAttribute("fileName");
+ if (fileName.equals(snap.getSnapshotFileName())) {
+ elementSnapshotList.item(0).removeChild(currentSnapshotNode);
+ break;
+ }
+ }
+
+ snapshotList.remove(snap);
+
+ saveAlbumData();
+
+ // refresh all data
+ try {
+ loadAlbum(true);
+ loadAlbumMetada(true);
+ } catch (Exception e) {
+
+ }
+
+ ZipFileUtils.deleteFileFromZip(snap.getSnapshotFileName(), getLocation().toFile(), DSA_FILE_EXTENSIONS);
+ }
+
+ @Override
+ public String toString() {
+ return "Album [name=" + name + ", launchName=" + launchName + ", sessionID=" + sessionID + "]";
+ }
+
+ public IPath createEmptyAlbum() {
+ IPath zipPath = null;
+ try {
+ zipPath = SnapshotUtils.getSnapshotsProject().getLocation();
+ zipPath = zipPath.append(getDefaultAlbumName());
+ zipPath = zipPath.addFileExtension("dsa");
+ boolean created = ZipFileUtils.createNewZip(zipPath.toFile());
+
+ if (created && zipPath.toFile().exists()){
+ setLocation(zipPath);
+ } else {
+ return null;
+ }
+ SnapshotUtils.getSnapshotsProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(e.getLocalizedMessage(), e);
+ }
+ return zipPath;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.snapshot.IAlbum#isRecording()
+ */
+ public boolean isRecording() {
+ return recordingSessionID.length() > 0;
+ }
+
+ /**
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public static void addSnapshotAlbumEventListener(ISnapshotAlbumEventListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * @noreference This method is not intended to be referenced by clients.
+ */
+ public static void removeSnapshotAlbumEventListener(ISnapshotAlbumEventListener listener) {
+ listeners.remove(listener);
+ }
+
+ public static void captureSnapshotForSession(final DsfSession session) {
+ Job createSnapshotJob = new Job("Creating Debug Snapshot") {
+
+ @Override
+ protected IStatus run(final IProgressMonitor monitor) {
+
+ Query<IFrameDMContext> frameQuery = new Query<IFrameDMContext>() {
+ @Override
+ protected void execute(
+ DataRequestMonitor<IFrameDMContext> rm) {
+ DsfServicesTracker servicesTracker = new DsfServicesTracker(
+ EDCDebugger.getBundleContext(),
+ session.getId());
+ try {
+ RunControl runControl = servicesTracker.getService(RunControl.class);
+ IThreadDMContext[] suspendedThreads = runControl.getSuspendedThreads();
+ if (suspendedThreads.length == 0)
+ {
+ rm.setData(null);
+ rm.done();
+ }
+ else
+ {
+ Stack stackService = servicesTracker.getService(Stack.class);
+ if (stackService != null) {
+ stackService.getTopFrame(suspendedThreads[0], rm);
+ }
+ }
+ } finally {
+ servicesTracker.dispose();
+ }
+ }
+ };
+
+ session.getExecutor().execute(frameQuery);
+
+ IStatus status = Status.OK_STATUS;
+ try {
+ final StackFrameDMC stackFrame = (StackFrameDMC) frameQuery.get();
+
+ String sessionId = session.getId();
+ Album album = Album.getRecordingForSession(sessionId);
+ if (album == null) {
+ album = new Album();
+ album.setRecordingSessionID(sessionId);
+ }
+ final Album finalAlbum = album;
+ playSnapshotSound();
+
+ Query<IStatus> query = new Query<IStatus>() {
+ @Override
+ protected void execute(final DataRequestMonitor<IStatus> drm) {
+ try {
+ Snapshot newSnapshot = finalAlbum.createSnapshot(session, stackFrame, monitor);
+ // Fire the event to anyone listening
+ for (ISnapshotAlbumEventListener l : new ArrayList<ISnapshotAlbumEventListener>(listeners)) {
+ l.snapshotCreated(finalAlbum, newSnapshot, session, stackFrame);
+ }
+ drm.setData(Status.OK_STATUS);
+ } catch (Exception e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Error creating snapshot.", e);
+ EDCDebugger.getMessageLogger().log(s);
+ drm.setStatus(s);
+ }
+ drm.done();
+ }
+ };
+
+ session.getExecutor().execute(query);
+
+ status = query.get();
+ } catch (Exception e) {
+ status = new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, "Error creating snapshot", e);
+ }
+
+ return status;
+ }
+ };
+
+ createSnapshotJob.schedule();
+ }
+
+ public boolean isPlayingSnapshots() {
+ return playingSnapshots;
+ }
+
+ public void setPlayingSnapshots(boolean playingSnapshots) {
+ this.playingSnapshots = playingSnapshots;
+ }
+
+ public void stopPlayingSnapshots() {
+ setPlayingSnapshots(false);
+ openSnapshot(getCurrentSnapshotIndex()); // Reloading the current snapshot will resync the UI.
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.snapshot;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
+import org.eclipse.debug.core.sourcelookup.containers.AbstractSourceContainer;
+
+public class AlbumSourceContainer extends AbstractSourceContainer {
+
+ private IAlbum album;
+
+ public static final String TYPE_ID = EDCDebugger.getUniqueIdentifier() + ".containerType.albumMapping"; //$NON-NLS-1$
+
+ public AlbumSourceContainer(IAlbum album) {
+ this.album = album;
+ }
+
+ public AlbumSourceContainer() {
+ }
+
+ public Object[] findSourceElements(String name) throws CoreException {
+ // TODO Auto-generated method stub
+ return new Object[0];
+ }
+
+ public String getName() {
+ return album.getDisplayName();
+ }
+
+ public ISourceContainerType getType() {
+ return getSourceContainerType(TYPE_ID);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.snapshot;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.containers.AbstractSourceContainerTypeDelegate;
+
+public class AlbumSourceContainerType extends AbstractSourceContainerTypeDelegate {
+
+ public ISourceContainer createSourceContainer(String memento) throws CoreException {
+ return new AlbumSourceContainer();
+ }
+
+ public String getMemento(ISourceContainer container) throws CoreException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.snapshot;
+
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.dsf.service.DsfSession;
+
+public interface ISnapshotAlbumEventListener {
+
+ public void snapshotCreated(Album album, Snapshot snapshot,
+ DsfSession session, StackFrameDMC stackFrame);
+
+ public void snapshotOpened(Snapshot snapshot);
+
+ public void snapshotSessionEnded(Album album, DsfSession session);
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.snapshot;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.zip.ZipOutputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.TransformerException;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.ZipFileUtils;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.dsf.service.IDsfService;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.internal.core.LaunchManager;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.DefaultHandler;
+
+@SuppressWarnings("restriction")
+public class Snapshot extends PlatformObject {
+
+ // XML elements
+ public static final String SNAPSHOT = "snapshot";
+
+ public static final String SNAPSHOT_FILENAME_PREFIX = "snapshot_";
+
+ private Document document;
+ private Element snapshotRootElement;
+ private DsfSession session;
+ private Album album;
+ private String snapshotFileName;
+ private String snapshotDisplayName;
+
+ private String creationDate;
+
+ private String snapshotDescription;
+
+ // Reference location information: when a snapshot is created
+ // we record the location in the most recently suspended stack frame.
+ // This is then used to create a default name for the snapshot and
+ // is displayed in the snapshot view.
+ // Of course, when debugging multiple contexts this does not
+ // provide a complete description of the snapshot.
+
+ private String referenceLocationSourceFile = "";
+ private long referenceLocationLineNumber;
+
+ /*
+ * Create a snapshot for reading
+ */
+ public Snapshot(Album album){
+ try {
+ this.album = album;
+ document = DebugPlugin.newDocument();
+ snapshotRootElement = document.createElement(SNAPSHOT);
+ } catch (CoreException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Create a snapshot with prep for writing to file.
+ * @param album
+ * @param recentStackFrame -
+ */
+ public Snapshot(Album album, DsfSession session, StackFrameDMC recentStackFrame){
+ try {
+ assert session != null;
+
+ this.album = album;
+ this.session = session;
+ document = DebugPlugin.newDocument();
+ snapshotRootElement = document.createElement(SNAPSHOT);
+ document.appendChild(snapshotRootElement);
+
+ if (recentStackFrame == null){
+ snapshotDisplayName = snapshotFileName;
+ } else {
+ snapshotDisplayName = createSnapshotNameFromStackFrameDMC(recentStackFrame);
+ }
+ snapshotFileName = SNAPSHOT_FILENAME_PREFIX + System.currentTimeMillis() + ".xml";
+ creationDate = new Date(System.currentTimeMillis()).toString();
+
+ if (recentStackFrame != null){
+ File f = new File(recentStackFrame.getSourceFile());
+ if (f != null){
+ setReferenceLocationSourceFile(f.getName());
+ }
+ setReferenceLocationLineNumber(recentStackFrame.getLineNumber());
+ }
+
+ } catch (CoreException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter.equals(Document.class))
+ return document;
+
+ return super.getAdapter(adapter);
+ }
+
+ private static String getServiceFilter(String sessionId) {
+ return ("(" + IDsfService.PROP_SESSION_ID + "=" + sessionId + ")").intern(); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void open(DsfSession session) {
+ ServiceReference[] references;
+ BufferedInputStream stream = null;
+ try {
+
+ stream = ZipFileUtils.openFile(album.getLocation().toFile(), snapshotFileName, new String[] {"dsa"} );
+ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ parser.setErrorHandler(new DefaultHandler());
+
+ document = parser.parse(new InputSource(stream));
+ NodeList snapNode = document.getElementsByTagName(SNAPSHOT);
+ Element snapShotE = (Element)snapNode.item(0);
+
+ references = EDCDebugger.getBundleContext().getServiceReferences(ISnapshotContributor.class.getName(),
+ getServiceFilter(session.getId()));
+ for (ServiceReference serviceReference : references) {
+ ISnapshotContributor sc = (ISnapshotContributor) EDCDebugger.getBundleContext().getService(
+ serviceReference);
+ sc.loadSnapshot(snapShotE);
+ }
+
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ } finally {
+ ZipFileUtils.unmount();
+ }
+ }
+
+ /**
+ * Write snapshot data.
+ *
+ * @param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
+ to call done() on the given monitor. Accepts null, indicating that no progress should be
+ reported and that the operation cannot be canceled.
+ */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public void writeSnapshotData(IProgressMonitor monitor) throws Exception{
+ try {
+ ServiceReference[] references = EDCDebugger.getBundleContext().getServiceReferences(
+ ISnapshotContributor.class.getName(), getServiceFilter(session.getId()));
+ SubMonitor progress = SubMonitor.convert(monitor, references.length * 1000);
+ progress.subTask("Writing snapshot data");
+ for (ServiceReference serviceReference : references) {
+ if (progress.isCanceled())
+ break;
+ ISnapshotContributor sc = (ISnapshotContributor) EDCDebugger.getBundleContext().getService(
+ serviceReference);
+ Element serviceElement = sc.takeSnapshot(album, document, progress.newChild(1000));
+ if (serviceElement != null)
+ snapshotRootElement.appendChild(serviceElement);
+ }
+ } catch (InvalidSyntaxException e) {
+ EDCDebugger.getMessageLogger().logError("Invalid session ID syntax", e); //$NON-NLS-1$
+ } catch (IllegalStateException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ public String getSnapshotFileName(){
+ return snapshotFileName;
+ }
+
+ public void setSnapshotFileName(String snapshotFileName){
+ this.snapshotFileName = snapshotFileName;
+ }
+
+ public void saveSnapshot(ZipOutputStream zipOut) throws TransformerException, IOException {
+ String xml = LaunchManager.serializeDocument(document);
+ zipOut.write(xml.getBytes("UTF8")); //$NON-NLS-1$
+ zipOut.closeEntry();
+ }
+
+ public String getCreationDate(){
+ return creationDate;
+ }
+
+ public void setCreationDate(String date){
+ this.creationDate = date;
+ }
+
+ /**
+ * Set the display text for the snapshot
+ * @param snapshotDisplayName
+ */
+ public void setSnapshotDisplayName(String snapshotDisplayName) {
+ this.snapshotDisplayName = snapshotDisplayName;
+ }
+
+ /**
+ * Get the display name of the snapshot. If there is no display name, the XML file containing
+ * the snapshot data in the DSA archive will be used.
+ * @return
+ */
+ public String getSnapshotDisplayName() {
+ if (snapshotDisplayName == null || snapshotDisplayName.length() == 0){
+ snapshotDisplayName = snapshotFileName;
+ }
+ return snapshotDisplayName;
+ }
+
+ /**
+ * Additional arbitrary notes to describe a particular snapshot
+ * @return
+ */
+ public String getSnapshotDescription() {
+ if (snapshotDescription == null){
+ snapshotDescription = "";
+ }
+ return snapshotDescription;
+ }
+
+ /**
+ * Set the snapshot description text.
+ * @param descr
+ */
+ public void setSnapshotDescription(String descr) {
+ snapshotDescription = descr;
+ }
+
+ /**
+ * Get the album this snapshot belongs to
+ * @return
+ */
+ public Album getAlbum(){
+ return album;
+ }
+
+ /**
+ * Creates the snapshot name from a stack frame dmc.
+ *
+ * @param frameDMC the frame dmc
+ *
+ * @return the snapshot name
+ */
+ public String createSnapshotNameFromStackFrameDMC(StackFrameDMC stackFrame)
+ {
+ assert stackFrame != null;
+ StringBuilder name = new StringBuilder();
+ if (stackFrame.getFunctionName() != null && stackFrame.getFunctionName().length() != 0) {
+ name.append(stackFrame.getFunctionName());
+ name.append("() : "); //$NON-NLS-1$
+ name.append(stackFrame.getLineNumber());
+ } else if (stackFrame.getModuleName() != null && stackFrame.getModuleName().length() != 0) {
+ name.append(stackFrame.getModuleName());
+ } else if (stackFrame.getInstructionPtrAddress() != null) {
+ name.append(stackFrame.getInstructionPtrAddress().toHexAddressString());
+ }
+
+ return name.toString();
+ }
+
+ public void setReferenceLocationSourceFile(String referenceLocationSourceFile) {
+ assert referenceLocationSourceFile != null;
+ this.referenceLocationSourceFile = referenceLocationSourceFile;
+ }
+
+ public String getReferenceLocationSourceFile() {
+ assert referenceLocationSourceFile != null;
+ return referenceLocationSourceFile;
+ }
+
+ public void setReferenceLocationLineNumber(long referenceLocationLineNumber) {
+ this.referenceLocationLineNumber = referenceLocationLineNumber;
+ }
+
+ public long getReferenceLocationLineNumber() {
+ return referenceLocationLineNumber;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.snapshot;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.launch.AbstractFinalLaunchSequence;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+public class SnapshotLaunchSequence extends AbstractFinalLaunchSequence {
+
+ private final Step[] steps = new Step[] {
+
+ trackerStep,
+
+ new Step() {
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ try {
+ int snapIndex = getLaunch().getAlbum().getCurrentSnapshotIndex();
+ getLaunch().getAlbum().openSnapshot(snapIndex);
+ requestMonitor.done();
+ } catch (Exception e) {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID,
+ "Failed to open snapshot. Reason: " + e.getLocalizedMessage()));
+ }
+ }
+
+ }
+
+ };
+
+ public SnapshotLaunchSequence(DsfExecutor executor, EDCLaunch launch, IProgressMonitor pm) {
+ super(executor, launch, pm, "Configuring Snapshot Launch", "Aborting configuring Snapshot Launch");
+ }
+
+ @Override
+ public Step[] getSteps() {
+ return steps;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.launch.AbstractFinalLaunchSequence#useLocalAgentOnly()
+ */
+ @Override
+ protected boolean useLocalAgentOnly() {
+ return true;
+ }
+
+ @Override
+ protected void specifyRequiredPeer() {
+ // No TCF agent needed.
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.snapshot;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.SourceDataLine;
+import javax.sound.sampled.UnsupportedAudioFileException;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.launch.IEDCLaunchConfigurationConstants;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class SnapshotUtils extends PlatformObject {
+
+ /**
+ * Constants for XML element names and attributes
+ */
+ public static final String PROPERTIES = "properties"; //$NON-NLS-1$
+ private static final String KEY = "key"; //$NON-NLS-1$
+ private static final String VALUE = "value"; //$NON-NLS-1$
+ private static final String SET_ENTRY = "setEntry"; //$NON-NLS-1$
+ private static final String MAP_ENTRY = "mapEntry"; //$NON-NLS-1$
+ private static final String LIST_ENTRY = "listEntry"; //$NON-NLS-1$
+ private static final String SET_ATTRIBUTE = "setAttribute"; //$NON-NLS-1$
+ private static final String MAP_ATTRIBUTE = "mapAttribute"; //$NON-NLS-1$
+ private static final String LIST_ATTRIBUTE = "listAttribute"; //$NON-NLS-1$
+ private static final String BOOLEAN_ATTRIBUTE = "booleanAttribute"; //$NON-NLS-1$
+ private static final String INT_ATTRIBUTE = "intAttribute"; //$NON-NLS-1$
+ private static final String LONG_ATTRIBUTE = "longAttribute"; //$NON-NLS-1$
+ private static final String BIG_INTEGER_ATTRIBUTE = "bigIntegerAttribute"; //$NON-NLS-1$
+ private static final String STRING_ATTRIBUTE = "stringAttribute"; //$NON-NLS-1$
+ private static final String SERIALIZED_ATTRIBUTE = "serializedAttribute"; //$NON-NLS-1$
+
+ @SuppressWarnings("unchecked")
+ static public Element makeXMLFromProperties(Document doc, Map<String, Object> properties) {
+
+ Element rootElement = doc.createElement(PROPERTIES);
+ Iterator<String> keys = properties.keySet().iterator();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ if (key != null) {
+ Object value = properties.get(key);
+ if (value == null) {
+ continue;
+ }
+ Element element = null;
+ String valueString = null;
+ try {
+ if (value instanceof String) {
+ valueString = (String) value;
+ element = createKeyValueElement(doc, STRING_ATTRIBUTE, key, valueString);
+ } else if (value instanceof Integer) {
+ valueString = ((Integer) value).toString();
+ element = createKeyValueElement(doc, INT_ATTRIBUTE, key, valueString);
+ } else if (value instanceof Long) {
+ valueString = ((Long) value).toString();
+ element = createKeyValueElement(doc, LONG_ATTRIBUTE, key, valueString);
+ } else if (value instanceof BigInteger) {
+ valueString = ((BigInteger) value).toString();
+ element = createKeyValueElement(doc, BIG_INTEGER_ATTRIBUTE, key, valueString);
+ } else if (value instanceof Boolean) {
+ valueString = ((Boolean) value).toString();
+ element = createKeyValueElement(doc, BOOLEAN_ATTRIBUTE, key, valueString);
+ } else if (value instanceof List<?>) {
+ element = createListElement(doc, LIST_ATTRIBUTE, key, (List<Object>) value);
+ } else if (value instanceof Map<?,?>) {
+ element = createMapElement(doc, MAP_ATTRIBUTE, key, (Map<Object, Object>) value);
+ } else if (value instanceof HashSet<?>) {
+ element = createSetElement(doc, SET_ATTRIBUTE, key, (Set<Object>) value);
+ }
+ } catch (Exception e) { // Don't do anything here, we'll try to handle it as
+ // as serialized object.
+ }
+ if (element == null)
+ {
+ try {
+ element = createSerializedElement(doc, SERIALIZED_ATTRIBUTE, key, value);
+ } catch (IOException e) {EDCDebugger.getMessageLogger().logError("Error adding to snapshot: " + value.toString(), e);};
+ }
+ rootElement.appendChild(element);
+ }
+ }
+ return rootElement;
+ }
+
+ /**
+ * Helper method that creates a 'key value' element of the specified type
+ * with the specified attribute values.
+ */
+ static protected Element createKeyValueElement(Document doc, String elementType, String key, String value) {
+ Element element = doc.createElement(elementType);
+ element.setAttribute(KEY, key);
+ element.setAttribute(VALUE, value);
+ return element;
+ }
+
+ /**
+ * Creates a new <code>Element</code> for the specified
+ * <code>java.util.List</code>
+ *
+ * @param doc
+ * the doc to add the element to
+ * @param elementType
+ * the type of the element
+ * @param setKey
+ * the key for the element
+ * @param list
+ * the list to fill the new element with
+ * @return the new element
+ */
+ static protected Element createListElement(Document doc, String elementType, String listKey, List<Object> list) {
+ Element listElement = doc.createElement(elementType);
+ listElement.setAttribute(KEY, listKey);
+ Iterator<Object> iterator = list.iterator();
+ while (iterator.hasNext()) {
+ String value = (String) iterator.next();
+ Element element = doc.createElement(LIST_ENTRY);
+ element.setAttribute(VALUE, value);
+ listElement.appendChild(element);
+ }
+ return listElement;
+ }
+
+ /**
+ * Creates a new <code>Element</code> for the specified
+ * <code>java.util.Set</code>
+ *
+ * @param doc
+ * the doc to add the element to
+ * @param elementType
+ * the type of the element
+ * @param setKey
+ * the key for the element
+ * @param set
+ * the set to fill the new element with
+ * @return the new element
+ *
+ * @since 3.3
+ */
+ static protected Element createSetElement(Document doc, String elementType, String setKey, Set<Object> set) {
+ Element setElement = doc.createElement(elementType);
+ setElement.setAttribute(KEY, setKey);
+ Element element = null;
+ for (Object object : set) {
+ element = doc.createElement(SET_ENTRY);
+ element.setAttribute(VALUE, (String) object);
+ setElement.appendChild(element);
+ }
+ return setElement;
+ }
+
+ static protected Element createSerializedElement(Document doc, String elementType, String key, Object value) throws IOException {
+ Element element = doc.createElement(elementType);
+ element.setAttribute(KEY, key);
+ element.appendChild(doc.createTextNode(serializeObjectToString(value)));
+ return element;
+ }
+
+ /**
+ * Creates a new <code>Element</code> for the specified
+ * <code>java.util.Map</code>
+ * <p>
+ * NOTE: this creates a <String, String> map from your map -- your ISnapshot#loadSnapshot() implementation
+ * must recover the right types.
+ * @param doc
+ * the doc to add the element to
+ * @param elementType
+ * the type of the element
+ * @param setKey
+ * the key for the element
+ * @param map
+ * the map to fill the new element with
+ * @return the new element
+ *
+ */
+ static protected Element createMapElement(Document doc, String elementType, String mapKey, Map<Object, Object> map) {
+ Element mapElement = doc.createElement(elementType);
+ mapElement.setAttribute(KEY, mapKey);
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ String key = entry.getKey().toString();
+ String value = entry.getValue() != null ? entry.getValue().toString() : null;
+ Element element = doc.createElement(MAP_ENTRY);
+ element.setAttribute(KEY, key);
+ element.setAttribute(VALUE, value);
+ mapElement.appendChild(element);
+ }
+ return mapElement;
+ }
+
+ /**
+ * Initializes the mapping of attributes from the XML file
+ *
+ * @param root
+ * the root node from the XML document
+ * @throws CoreException
+ */
+ static public void initializeFromXML(Element root, Map<String, Object> properties) throws CoreException {
+ if (root.getNodeName().equalsIgnoreCase(PROPERTIES)) {
+ NodeList list = root.getChildNodes();
+ Node node = null;
+ Element element = null;
+ String nodeName = null;
+ for (int i = 0; i < list.getLength(); ++i) {
+ node = list.item(i);
+ short nodeType = node.getNodeType();
+ if (nodeType == Node.ELEMENT_NODE) {
+ element = (Element) node;
+ nodeName = element.getNodeName();
+ try {
+ if (nodeName.equalsIgnoreCase(STRING_ATTRIBUTE)) {
+ setStringAttribute(element, properties);
+ } else if (nodeName.equalsIgnoreCase(INT_ATTRIBUTE)) {
+ setIntegerAttribute(element, properties);
+ } else if (nodeName.equalsIgnoreCase(LONG_ATTRIBUTE)) {
+ setLongAttribute(element, properties);
+ } else if (nodeName.equalsIgnoreCase(BIG_INTEGER_ATTRIBUTE)) {
+ setBigIntegerAttribute(element, properties);
+ } else if (nodeName.equalsIgnoreCase(BOOLEAN_ATTRIBUTE)) {
+ setBooleanAttribute(element, properties);
+ } else if (nodeName.equalsIgnoreCase(LIST_ATTRIBUTE)) {
+ setListAttribute(element, properties);
+ } else if (nodeName.equalsIgnoreCase(MAP_ATTRIBUTE)) {
+ setMapAttribute(element, properties);
+ } else if (nodeName.equalsIgnoreCase(SET_ATTRIBUTE)) {
+ setSetAttribute(element, properties);
+ } else if (nodeName.equalsIgnoreCase(SERIALIZED_ATTRIBUTE)) {
+ setSerializedAttribute(element, properties);
+ } else {
+ EDCDebugger.getMessageLogger().logError("Unsupported element: " + nodeName, null);
+ }
+ } catch (Exception e) {
+ // Some integers are longs and so will fail when adding
+ // their properties to the launch configuration. LaunchConfigurationInfo
+ // does not support any number but Integer (no BigInteger or Long)
+ // See org.eclipse.debug.internal.core.LaunchConfigurationInfo#getAsXML().
+ EDCDebugger.getMessageLogger().logError("Skipping snapshot element.", e);
+ } finally {
+ // continue on to the next element
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Loads a <code>String</code> from the specified element into the local
+ * attribute mapping
+ *
+ * @param element
+ * the element to load from
+ * @throws CoreException
+ */
+ static protected void setStringAttribute(Element element, Map<String, Object> properties) throws CoreException {
+ properties.put(element.getAttribute(KEY), element.getAttribute(VALUE));
+ }
+
+ /**
+ * Loads an <code>Integer</code> from the specified element into the local
+ * attribute mapping
+ *
+ * @param element
+ * the element to load from
+ * @throws CoreException
+ */
+ static protected void setIntegerAttribute(Element element, Map<String, Object> properties) throws CoreException {
+ properties.put(element.getAttribute(KEY), new Integer(element.getAttribute(VALUE)));
+ }
+
+ /**
+ * Loads an <code>Long</code> from the specified element into the local
+ * attribute mapping
+ *
+ * @param element
+ * the element to load from
+ * @throws CoreException
+ */
+ static protected void setLongAttribute(Element element, Map<String, Object> properties) throws CoreException {
+ properties.put(element.getAttribute(KEY), new Long(element.getAttribute(VALUE)));
+ }
+
+ /**
+ * Loads a serialized <code>Object</code> from the specified element into the local
+ * attribute mapping
+ *
+ * @param element
+ * the element to load from
+ * @throws CoreException
+ * @throws IOException
+ * @throws ClassNotFoundException
+ * @throws DecoderException
+ */
+ static protected void setSerializedAttribute(Element element, Map<String, Object> properties) throws CoreException, IOException, ClassNotFoundException, DecoderException {
+ properties.put(element.getAttribute(KEY), createSerializedObjectFromString(element.getFirstChild().getTextContent()));
+ }
+
+ /**
+ * Loads an <code>BigInteger</code> from the specified element into the local
+ * attribute mapping
+ *
+ * @param element
+ * the element to load from
+ * @throws CoreException
+ */
+ static protected void setBigIntegerAttribute(Element element, Map<String, Object> properties) throws CoreException {
+ properties.put(element.getAttribute(KEY), new BigInteger(element.getAttribute(VALUE)));
+ }
+
+ /**
+ * Loads a <code>Boolean</code> from the specified element into the local
+ * attribute mapping
+ *
+ * @param element
+ * the element to load from
+ * @throws CoreException
+ */
+ static protected void setBooleanAttribute(Element element, Map<String, Object> properties) throws CoreException {
+ properties.put(element.getAttribute(KEY), Boolean.valueOf(element.getAttribute(VALUE)));
+ }
+
+ /**
+ * Reads a <code>List</code> attribute from the specified XML node and loads
+ * it into the mapping of attributes
+ *
+ * @param element
+ * the element to read the list attribute from
+ * @throws CoreException
+ * if the element has an invalid format
+ */
+ static protected void setListAttribute(Element element, Map<String, Object> properties) throws CoreException {
+ String listKey = element.getAttribute(KEY);
+ NodeList nodeList = element.getChildNodes();
+ int entryCount = nodeList.getLength();
+ List<Object> list = new ArrayList<Object>(entryCount);
+ Node node = null;
+ Element selement = null;
+ for (int i = 0; i < entryCount; i++) {
+ node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ selement = (Element) node;
+ if (selement.getNodeName().equalsIgnoreCase(LIST_ENTRY)) {
+ String value = selement.getAttribute(VALUE);
+ list.add(value);
+ }
+ }
+ }
+ properties.put(listKey, list);
+ }
+
+ /**
+ * Reads a <code>Set</code> attribute from the specified XML node and loads
+ * it into the mapping of attributes
+ *
+ * @param element
+ * the element to read the set attribute from
+ * @throws CoreException
+ * if the element has an invalid format
+ *
+ * @since 3.3
+ */
+ static protected void setSetAttribute(Element element, Map<String, Object> properties) throws CoreException {
+ String setKey = element.getAttribute(KEY);
+ NodeList nodeList = element.getChildNodes();
+ int entryCount = nodeList.getLength();
+ Set<Object> set = new HashSet<Object>(entryCount);
+ Node node = null;
+ Element selement = null;
+ for (int i = 0; i < entryCount; i++) {
+ node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ selement = (Element) node;
+ if (selement.getNodeName().equalsIgnoreCase(SET_ENTRY)) {
+ set.add(element.getAttribute(VALUE));
+ }
+ }
+ }
+ properties.put(setKey, set);
+ }
+
+ /**
+ * Reads a <code>Map</code> attribute from the specified XML node and loads
+ * it into the mapping of attributes
+ * <p>
+ * NOTE: this creates a <String, String> map -- your ISnapshot#loadSnapshot() implementation
+ * must recover the right types.
+ *
+ * @param element
+ * the element to read the map attribute from
+ * @throws CoreException
+ * if the element has an invalid format
+ */
+ static protected void setMapAttribute(Element element, Map<String, Object> properties) throws CoreException {
+ String mapKey = element.getAttribute(KEY);
+ NodeList nodeList = element.getChildNodes();
+ int entryCount = nodeList.getLength();
+ Map<Object, Object> map = new HashMap<Object, Object>(entryCount);
+ Node node = null;
+ Element selement = null;
+ for (int i = 0; i < entryCount; i++) {
+ node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ selement = (Element) node;
+ if (selement.getNodeName().equalsIgnoreCase(MAP_ENTRY)) {
+ map.put(selement.getAttribute(KEY), selement.getAttribute(VALUE));
+ }
+ }
+ }
+ properties.put(mapKey, map);
+ }
+
+ static public IProject getSnapshotsProject() throws CoreException {
+ IProject snapshotsProject = null;
+ // See if the default project exists
+ String defaultProjectName = "Snapshots";
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ snapshotsProject = workspace.getRoot().getProject(
+ defaultProjectName);
+ if (snapshotsProject.exists()) {
+ if (!snapshotsProject.isOpen())
+ snapshotsProject.open(new NullProgressMonitor());
+ } else {
+ IProjectDescription description = workspace.newProjectDescription(defaultProjectName);
+ description.setLocation(null);
+ try {
+ snapshotsProject.create(description, new NullProgressMonitor());
+ snapshotsProject.open(new NullProgressMonitor());
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+ return snapshotsProject;
+ }
+
+ // TODO: This was taken from SnapshotLaunchDelegate. Need to refactor properly to make this common....
+ /**
+ * Load an album and launch the session without creating a Snapshot launch configuration.
+ * Only creates the launch configuration type specified in the album data.
+ */
+ static public boolean launchAlbumSession(Album album){
+ IPath albumPath = album.getLocation();
+
+ try {
+ if (!album.isLoaded()){
+ album.loadAlbum(false);
+ }
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ return false;
+ }
+
+ ILaunchManager lm = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType launchType = lm.getLaunchConfigurationType(album.getLaunchTypeID());
+ if (launchType == null) {
+ // Can't launch TODO: Need error or exception
+ return false;
+ }
+ ILaunchConfiguration proxyLaunchConfig = findExistingLaunchForAlbum(album);
+ if (proxyLaunchConfig == null) {
+ String lcName = lm.generateLaunchConfigurationName(album.getDisplayName());
+ ILaunchConfigurationWorkingCopy proxyLaunchConfigWC = null;
+ try {
+ proxyLaunchConfigWC = launchType.newInstance(null, lcName);
+ proxyLaunchConfigWC.setAttributes(album.getLaunchProperties());
+ proxyLaunchConfigWC.setAttribute(IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE, albumPath.toOSString());
+ proxyLaunchConfig = proxyLaunchConfigWC.doSave();
+
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+
+ }
+
+ if (proxyLaunchConfig != null)
+ {
+ final ILaunchConfiguration finalProxyLC = proxyLaunchConfig;
+ Job launchJob = new Job("Launching " + albumPath.toFile().getName()) {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ finalProxyLC.launch(ILaunchManager.DEBUG_MODE, new NullProgressMonitor(), false, true);
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ return Status.CANCEL_STATUS;
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ launchJob.schedule();
+ }
+
+ return false;
+ }
+
+ static public boolean isSnapshotLaunchConfig(ILaunchConfiguration launchConfiguration)
+ {
+ try {
+ String albumFile = launchConfiguration.getAttribute(IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE, "");
+ return albumFile.length() > 0;
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ return false;
+ }
+
+ static public ILaunchConfiguration findExistingLaunchForAlbum(IAlbum album) {
+ ILaunchManager lm = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType launchType = lm.getLaunchConfigurationType(album.getLaunchTypeID());
+ if (launchType == null){
+ return null;
+ }
+
+ try {
+ ILaunchConfiguration[] configurations = lm.getLaunchConfigurations(launchType);
+ for (ILaunchConfiguration configuration : configurations) {
+ if (album.getLocation().toOSString().equals(configuration.getAttribute(
+ IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE, "")))
+ return configuration;
+ }
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ return null;
+ }
+
+ /**
+ * Taken from org.eclipse.cdt.debug.ui.breakpointactions#SoundAction
+ * @param soundFile
+ */
+ static public void playSoundFile(final File soundFile) {
+
+ class SoundPlayer extends Thread {
+
+ public void run() {
+ AudioInputStream soundStream;
+ try {
+ soundStream = AudioSystem.getAudioInputStream(soundFile);
+ AudioFormat audioFormat = soundStream.getFormat();
+ DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
+ SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
+ byte[] soundBuffer = new byte[5000];
+ sourceDataLine.open(audioFormat);
+ sourceDataLine.start();
+ int dataCount = 0;
+
+ while ((dataCount = soundStream.read(soundBuffer, 0, soundBuffer.length)) != -1) {
+ if (dataCount > 0) {
+ sourceDataLine.write(soundBuffer, 0, dataCount);
+ }
+ }
+ sourceDataLine.drain();
+ sourceDataLine.close();
+
+ }
+ // Don't report any exceptions, some VMs may not play the sound
+ catch (UnsupportedAudioFileException e) {
+ } catch (IOException e) {
+ } catch (IllegalArgumentException e) {
+ } catch (LineUnavailableException e) {
+ } finally {
+
+ }
+
+ }
+
+ }
+
+ if (soundFile.exists()) {
+ new SoundPlayer().start();
+ }
+ }
+
+ private static String serializeObjectToString(Object value) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(baos);
+ oos.writeObject(value);
+ return new String(new Hex().encode(baos.toByteArray()));
+ }
+
+ private static Object createSerializedObjectFromString(String objectValue) throws DecoderException, IOException, ClassNotFoundException {
+ final ClassLoader classLoader = EDCDebugger.getDefault().getClass().getClassLoader();
+ ByteArrayInputStream bais = new ByteArrayInputStream(Hex.decodeHex(objectValue.toCharArray()));
+ ObjectInputStream ois = new ObjectInputStream(bais) {
+
+ @Override
+ protected Class<?> resolveClass(ObjectStreamClass desc)
+ throws IOException, ClassNotFoundException {
+ String name = desc.getName();
+ try {
+ return classLoader.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ return super.resolveClass(desc);
+ }
+ }};
+ return ois.readObject();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public class ArrayBoundType extends Type implements IArrayBoundType {
+
+ // bound of this array dimension. E.g., for "int a[7][8]", this would be
+ // either 7 or 8.
+ private final long bound;
+
+ // number of array elements associated with each index of this array
+ // dimension.
+ // E.g., for "int a[7][8]", "a[1]" comprises 8 elements, but "a[1][2]"
+ // comprises 1 element.
+ private long elements;
+
+ // array dimension ordinal. E.g., for "int a[7][8]", "[7]" is index 1 and
+ // "[8]" is index 0;
+ private long dimensionIndex = 0;
+
+ public ArrayBoundType(IScope scope, long arrayBound) {
+ super("", scope, 0, null); //$NON-NLS-1$
+
+ if (arrayBound < 1) {
+ this.bound = 0;
+ this.elements = 0;
+ } else {
+ this.bound = arrayBound;
+ this.elements = 1;
+ }
+ }
+
+ public long getBoundCount() {
+ return this.bound;
+ }
+
+ public long getElementCount() {
+ return this.elements;
+ }
+
+ public long getDimensionIndex() {
+ return this.dimensionIndex;
+ }
+
+ public void multiplyElementCount(long multiply) {
+ this.elements *= multiply;
+ }
+
+ public void incDimensionIndex() {
+ this.dimensionIndex++;
+ }
+
+ @Override
+ public IType getType() {
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+
+public class ArrayType extends MayBeQualifiedType implements IArrayType {
+
+ protected ArrayList<IArrayBoundType> bounds = new ArrayList<IArrayBoundType>();
+
+ public ArrayType(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ super(name, scope, byteSize, properties);
+ }
+
+ public int getBoundsCount() {
+ return bounds.size();
+ }
+
+ public void addBound(IArrayBoundType bound) {
+ // existing array dimensions now represent bound.getBoundCount() times
+ // as many array elements,
+ // and have a higher dimensional position
+ for (IArrayBoundType existingBound : this.bounds) {
+ existingBound.multiplyElementCount(bound.getBoundCount());
+ existingBound.incDimensionIndex();
+ }
+ bounds.add(bound);
+ byteSize = 0; // recalculate
+ }
+
+ public IArrayBoundType[] getBounds() {
+ IArrayBoundType[] boundsArray = new IArrayBoundType[bounds.size()];
+ for (int i = 0; i < bounds.size(); i++) {
+ boundsArray[i] = bounds.get(i);
+ }
+ return boundsArray;
+ }
+
+ // get the Nth bound. E.g., for "a[X][Y]", getBound(0) returns info for
+ // "[X]"
+ public IArrayBoundType getBound(int index) {
+ if (index >= 0 && index < bounds.size())
+ return bounds.get(index);
+
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Type#getByteSize()
+ */
+ @Override
+ public int getByteSize() {
+ if (byteSize == 0) {
+ if (bounds.size() > 0) {
+ updateByteSizeFromSubType();
+ IType subtype = TypeUtils.getStrippedType(getType());
+ if (subtype instanceof IArrayType) {
+ for (IArrayBoundType bound : ((IArrayType)subtype).getBounds())
+ byteSize *= bound.getBoundCount();
+ }
+ }
+ }
+ return byteSize;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public class CPPBasicType extends MayBeQualifiedType implements ICPPBasicType {
+
+ private final int baseType;
+ private final int qualifierBits;
+
+ public CPPBasicType(String name, IScope scope, int baseType, int qualifierBits, int byteSize,
+ Map<Object, Object> properties) {
+ super(name, scope, byteSize, properties);
+ this.baseType = baseType;
+ this.qualifierBits = qualifierBits;
+ }
+
+ public CPPBasicType(String name, int baseType, int qualifierBits, int byteSize) {
+ super(name, null, byteSize, null);
+ this.baseType = baseType;
+ this.qualifierBits = qualifierBits;
+ }
+
+
+ /**
+ * WARNING: this only works for CPPBasicType itself. No other types in
+ * the hierarchy implement this correctly.
+ * (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 12345; // super.hashCode();
+ result = prime * result + baseType;
+ result = prime * result + qualifierBits;
+ return result;
+ }
+
+ /**
+ * WARNING: this only works for CPPBasicType itself. No other types in
+ * the hierarchy implement this correctly.
+ * (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ //if (!super.equals(obj))
+ // return false;
+ if (getClass() != obj.getClass())
+ return false;
+ // do not test name, since it's essentially random
+ CPPBasicType other = (CPPBasicType) obj;
+ if (baseType != other.baseType)
+ return false;
+ if (qualifierBits != other.qualifierBits)
+ return false;
+ return true;
+ }
+
+ public int getQualifierBits() {
+ return this.qualifierBits;
+ }
+
+ public int getBaseType() {
+ return this.baseType;
+ }
+
+ public boolean isLong() {
+ return (this.qualifierBits & ICPPBasicType.IS_LONG) != 0;
+ }
+
+ public boolean isLongLong() {
+ return (this.qualifierBits & ICPPBasicType.IS_LONG_LONG) != 0;
+ }
+
+ public boolean isShort() {
+ return (this.qualifierBits & ICPPBasicType.IS_SHORT) != 0;
+ }
+
+ public boolean isSigned() {
+ return (this.qualifierBits & ICPPBasicType.IS_SIGNED) != 0;
+ }
+
+ public boolean isUnsigned() {
+ return (this.qualifierBits & ICPPBasicType.IS_UNSIGNED) != 0;
+ }
+
+ public boolean isComplex() {
+ return (this.qualifierBits & ICPPBasicType.IS_COMPLEX) != 0;
+ }
+
+ @Override
+ public IType getType() {
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class ClassType extends CompositeType {
+
+ public ClassType(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ super(name, scope, ICompositeType.k_class, byteSize, properties, "class"); //$NON-NLS-1$
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.core.runtime.IPath;
+
+public abstract class CompileUnitScope extends Scope implements ICompileUnitScope {
+
+ protected IPath filePath;
+
+ protected Collection<ILineEntry> lineEntries;
+
+ public CompileUnitScope(IPath filePath, IModuleScope parent, IAddress lowAddress, IAddress highAddress) {
+ super(filePath != null ? filePath.lastSegment() : "", lowAddress, highAddress, parent); //$NON-NLS-1$
+
+ this.filePath = filePath;
+ }
+
+ public IPath getFilePath() {
+ return filePath;
+ }
+
+ public IFunctionScope getFunctionAtAddress(IAddress linkAddress) {
+ IScope scope = getScopeAtAddress(linkAddress);
+ while (scope != null && !(scope instanceof IFunctionScope)) {
+ scope = scope.getParent();
+ }
+
+ return (IFunctionScope) scope;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ICompileUnitScope#getFunctions()
+ */
+ public Collection<IFunctionScope> getFunctions() {
+ List<IFunctionScope> functions = new ArrayList<IFunctionScope>(children.size());
+ for (IScope scope : getChildren()) {
+ if (scope instanceof IFunctionScope)
+ functions.add((IFunctionScope) scope);
+ }
+ return Collections.unmodifiableCollection(functions);
+ }
+
+
+ /**
+ * Parse the line table data - to be implemented by debug format specific
+ * sub classes.
+ *
+ * @return the list of line table entries (may be empty)
+ */
+ protected abstract Collection<ILineEntry> parseLineTable();
+
+
+ public Collection<ILineEntry> getLineEntries() {
+ if (lineEntries == null) {
+ lineEntries = parseLineTable();
+ }
+ return Collections.unmodifiableCollection(lineEntries);
+ }
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("CompileUnitScope ["); //$NON-NLS-1$
+ builder.append("lowAddress="); //$NON-NLS-1$
+ builder.append(lowAddress);
+ builder.append(", highAddress="); //$NON-NLS-1$
+ builder.append(highAddress);
+ builder.append(", "); //$NON-NLS-1$
+ if (filePath != null) {
+ builder.append("path="); //$NON-NLS-1$
+ builder.append(filePath.toOSString());
+ builder.append(", "); //$NON-NLS-1$
+ }
+ builder.append("]"); //$NON-NLS-1$
+ return builder.toString();
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class CompositeType extends MayBeQualifiedType implements ICompositeType {
+
+ // kind of composite (class, struct, union)
+ private final int key;
+
+ // composite name without "class ", "struct " or "union " prefix
+ private String baseName;
+
+ // fields in the composite
+ protected ArrayList<IField> fields = new ArrayList<IField>();
+
+ // classes inherited from
+ protected ArrayList<IInheritance> inheritances = null;
+
+ // template parameters
+ protected ArrayList<ITemplateParam> templateParams = null;
+ boolean nameIncludesTemplateParams;
+
+ /**
+ * fields of anonymous union types, with unknown offsets
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfInfoReader#processUnionType()
+ */
+ protected ArrayList<IField> unknownOffsetFields = null;
+
+ protected static class OffsetAndLength {
+ public long offset;
+ public long length;
+ }
+
+ public CompositeType(String name, IScope scope, int key, int byteSize, Map<Object, Object> properties, String prefix) {
+ super(name, scope, byteSize, properties);
+ this.baseName = name;
+ this.name = prefix + " " + name; //$NON-NLS-1$
+ this.key = key;
+ nameIncludesTemplateParams = name.contains("<"); //$NON-NLS-1$
+ }
+
+ public int getKey() {
+ return this.key;
+ }
+
+ public int fieldCount() {
+ if (unknownOffsetFields != null)
+ setAnonymousUnionOffsets();
+ return fields.size();
+ }
+
+ public void addField(IField field) {
+ if (field.getFieldOffset() < 0) {
+ if (unknownOffsetFields == null)
+ unknownOffsetFields = new ArrayList<IField>();
+ unknownOffsetFields.add(field);
+ } else
+ fields.add(field);
+ }
+
+ public IField[] getFields() {
+ if (unknownOffsetFields != null)
+ setAnonymousUnionOffsets();
+ ArrayList<IField> fieldList = new ArrayList<IField>(fields);
+
+ return fieldList.toArray(new IField[fields.size()]);
+ }
+
+ public void addTemplateParam(ITemplateParam templateParam) {
+ if (templateParams == null) {
+ templateParams = new ArrayList<ITemplateParam>(2);
+ }
+ templateParams.add(templateParam);
+ }
+
+ public ITemplateParam[] getTemplateParams() {
+ if (templateParams == null)
+ return new ITemplateParam[0];
+
+ ArrayList<ITemplateParam> templateParamList = new ArrayList<ITemplateParam>(templateParams);
+
+ return templateParamList.toArray(new ITemplateParam[templateParams.size()]);
+ }
+
+ @Override
+ public String getName() {
+ if (templateParams != null && !nameIncludesTemplateParams)
+ addTemplateStringToNames();
+ return name;
+ }
+
+ public String getBaseName() {
+ if (templateParams != null && !nameIncludesTemplateParams)
+ addTemplateStringToNames();
+ return baseName;
+ }
+
+ // add template parameters (e.g. "<Long>") to name and base name
+ private void addTemplateStringToNames() {
+ nameIncludesTemplateParams = true;
+ String templateName = "<"; //$NON-NLS-1$
+ for (int i = 0; i < templateParams.size(); i++) {
+ templateName += templateParams.get(i).getName();
+ if (i + 1 < templateParams.size())
+ templateName += ","; //$NON-NLS-1$
+ }
+ templateName += ">"; //$NON-NLS-1$
+ // remove composite type names (e.g., "class")
+ templateName = templateName.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ templateName = templateName.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ templateName = templateName.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ name += templateName;
+ baseName += templateName;
+ }
+
+ public int inheritanceCount() {
+ return inheritances == null ? 0 : inheritances.size();
+ }
+
+ public void addInheritance(IInheritance inheritance) {
+ if (inheritances == null)
+ inheritances = new ArrayList<IInheritance>();
+ inheritances.add(inheritance);
+ }
+
+ public IInheritance[] getInheritances() {
+ if (inheritances == null)
+ return new IInheritance[0];
+
+ return inheritances.toArray(new IInheritance[inheritances.size()]);
+ }
+
+ public IField[] findFields(String name) {
+ // For a qualified name containing "::", save the qualifiers to match against
+ String baseFieldName = name;
+ ArrayList<String> nameQualifiers = new ArrayList<String>();
+
+ if (name.contains("::")) { //$NON-NLS-1$
+ StringTokenizer st = new StringTokenizer(name, "::", false); //$NON-NLS-1$
+ while (st.hasMoreTokens()) {
+ baseFieldName = st.nextToken();
+ nameQualifiers.add(baseFieldName);
+ }
+
+ // last token in the array is the base field name
+ nameQualifiers.remove(nameQualifiers.size() - 1);
+
+ // if the first nameQualifier is the composite's name, remove it
+ // E.g., if we're in class foo, change "foo::x" to "x".
+ if ((nameQualifiers.size() >= 0) && nameQualifiers.get(0).equals(this.baseName))
+ nameQualifiers.remove(0);
+ }
+
+ // try for a fast exit: match against the non-inherited fields and names of
+ // composites we're inheriting from
+ if (nameQualifiers.size() == 0) {
+ if (unknownOffsetFields != null)
+ setAnonymousUnionOffsets();
+ for (int i = 0; i < fields.size(); i++) {
+ if (((FieldType) fields.get(i)).getName().equals(baseFieldName)) {
+ IField[] foundFields = new IField[1];
+ foundFields[0] = fields.get(i);
+ return foundFields;
+ }
+ }
+
+ if (inheritances != null) {
+ for (IInheritance inheritance : inheritances) {
+ String inheritanceName = inheritance.getName();
+ // for templates, remove composite type names (e.g., "class")
+ if (inheritanceName.indexOf('<') != -1) {
+ inheritanceName = inheritanceName.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ inheritanceName = inheritanceName.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ inheritanceName = inheritanceName.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ if (inheritanceName.equals(baseFieldName)) {
+ IField[] foundFields = new IField[1];
+
+ // treat the inherited type as a field
+ FieldType newField = new FieldType(inheritanceName, scope, this,
+ inheritance.getFieldsOffset(), 0 /* bitSize */, 0 /* bitOffset */,
+ inheritance.getType().getByteSize(), inheritance.getAccessibility(),
+ inheritance.getProperties());
+ newField.setType(inheritance.getType());
+
+ foundFields[0] = newField;
+ return foundFields;
+ }
+ }
+ }
+ }
+
+ // check the inherited types
+ if (inheritances == null)
+ return null;
+
+ ArrayList<IField> matches = new ArrayList<IField>();
+
+ for (IInheritance inheritance : inheritances) {
+ if (inheritance.getType() instanceof ICompositeType) {
+ ICompositeType inheritComposite = (ICompositeType)inheritance.getType();
+ matches = findInheritedByName(baseFieldName, inheritComposite, inheritComposite.getBaseName(), inheritance.getFieldsOffset(), matches);
+ }
+ }
+
+ // eliminate partial matches
+ matches = pruneMatches(nameQualifiers, matches);
+
+ // create the list of all inherited fields
+ IField[] foundFields = null;
+
+ // gather the names and offsets of the inherited fields
+ if (matches.size() > 0) {
+ foundFields = new IField[matches.size()];
+ for (int i = 0; i < matches.size(); i++) {
+ foundFields[i] = matches.get(i);
+ }
+ }
+
+ return foundFields;
+ }
+
+ /**
+ * From a list of fields whose name matches the one we're looking for, remove those
+ * whose "::" qualifiers do not match. E.g., "foo::x" would match "bar::foo::x", but
+ * it would not match "bar::x" - so "bar::x" would be pruned.
+ *
+ * @param nameQualifiers qualifiers of the field we're matching against
+ * @param matches list of fields whose base name matches, but whose qualifiers may not match
+ * @return list of fields whose base name and qualifiers match the field we're looking for
+ */
+ private ArrayList<IField> pruneMatches(ArrayList<String> nameQualifiers, ArrayList<IField> matches) {
+ if (nameQualifiers.size() == 0)
+ return matches;
+
+ for (int i = 0; i < matches.size(); i++) {
+ ArrayList<String> matchQualifiers = new ArrayList<String>();
+ String matchName = matches.get(i).getName();
+
+ if (!matchName.contains("::")) //$NON-NLS-1$
+ continue;
+
+ // tokenize the match's name
+ StringTokenizer st = new StringTokenizer(matchName, "::", false); //$NON-NLS-1$
+ while (st.hasMoreTokens()) {
+ matchQualifiers.add(st.nextToken());
+ }
+
+ // last token in the array is the base name, which we already know matches
+ matchQualifiers.remove(matchQualifiers.size() - 1);
+
+ for (int nameIndex = 0, matchIndex = 0;
+ nameIndex < nameQualifiers.size() && matchIndex < matchQualifiers.size();
+ nameIndex++) {
+ // match against each name qualifier, in order
+ boolean found = false;
+ while (!found && matchIndex < matchQualifiers.size()) {
+ found = nameQualifiers.get(nameIndex).equals(matchQualifiers.get(matchIndex));
+ matchIndex++;
+ }
+
+ // if did not find a qualifier, remove the match
+ if (!found) {
+ matches.remove(i);
+ break;
+ }
+ }
+ }
+
+ return matches;
+ }
+
+ /**
+ * Find all inherited fields whose base name, ignoring "::" qualifiers, match the search name
+ *
+ * @param name name to match
+ * @param composite composite type whose fields or inherited fields may match
+ * @param prefix string of "::" qualifiers so far
+ * @param offset byte offset of the composite from the composite that inherits from it
+ * @param matches list of matches found so far
+ * @return list of matches
+ */
+ private ArrayList<IField> findInheritedByName(String name, ICompositeType composite, String prefix, long offset, ArrayList<IField> matches) {
+ IField[] fields = composite.getFields();
+ if (fields != null) {
+ for (IField field : fields) {
+ String fieldName = field.getName();
+
+ if (fieldName.equals(name)) {
+ // create a field with the prefixed name
+ FieldType newField = new FieldType(prefix + "::" + field.getName(), scope, //$NON-NLS-1$
+ composite, offset + field.getFieldOffset(), 0 /* bitSize */, 0 /* bitOffset */,
+ field.getType().getByteSize(), field.getAccessibility(),
+ field.getProperties());
+ newField.setType(field.getType());
+ matches.add(newField);
+ break;
+ }
+ }
+ }
+
+ IInheritance[] compositeInheritances = composite.getInheritances();
+ if (compositeInheritances.length == 0)
+ return matches;
+
+ for (IInheritance inheritance : compositeInheritances) {
+ if (inheritance.getName().equals(name)) {
+ // treat the inherited type as a field
+ FieldType newField = new FieldType(inheritance.getName(), scope, this,
+ offset + inheritance.getFieldsOffset(), 0 /* bitSize */, 0 /* bitOffset */,
+ inheritance.getType().getByteSize(), inheritance.getAccessibility(),
+ inheritance.getProperties());
+ newField.setType(inheritance.getType());
+ }
+
+ if (inheritance.getType() instanceof ICompositeType) {
+ ICompositeType inheritComposite = (ICompositeType)inheritance.getType();
+ matches = findInheritedByName(name, inheritComposite, prefix + "::" + inheritComposite.getBaseName(), //$NON-NLS-1$
+ offset + inheritance.getFieldsOffset(), matches);
+ }
+ }
+
+ return matches;
+ }
+
+ /**
+ * Fields with unknown offsets may be between other members or at the end
+ */
+ private void setAnonymousUnionOffsets() {
+ OffsetAndLength[] offsetSizes = new OffsetAndLength[fields.size() + inheritanceCount()];
+ int count = 0;
+ if (fields.size() > 0) {
+ for ( ; count < fields.size(); count++) {
+ offsetSizes[count] = new OffsetAndLength();
+ offsetSizes[count].offset = fields.get(count).getFieldOffset();
+ offsetSizes[count].length = fields.get(count).getByteSize();
+ }
+ }
+
+ if (inheritances != null) {
+ for (IInheritance inheritance : inheritances) {
+ offsetSizes[count] = new OffsetAndLength();
+ offsetSizes[count].offset = inheritance.getFieldsOffset();
+ offsetSizes[count].length = inheritance.getType().getByteSize();
+ count++;
+ }
+ }
+
+ // sort by offsets
+ if (offsetSizes.length > 1) {
+ boolean sorted;
+ int passCnt = 1;
+ do {
+ sorted = true;
+ for (int i = 0; i < offsetSizes.length - passCnt; i++) {
+ if (offsetSizes[i].offset > offsetSizes[i + 1].offset) {
+ OffsetAndLength temp = offsetSizes[i];
+ offsetSizes[i] = offsetSizes[i + 1];
+ offsetSizes[i + 1] = temp;
+ sorted = false;
+ }
+ }
+ passCnt++;
+ } while (!sorted && passCnt < offsetSizes.length);
+ }
+
+ // find the offset for each anonymous union's data - between other members or at the end
+ int i = 0;
+ long fieldOffset = 0;
+ for (IField unknownOffsetField : unknownOffsetFields) {
+ for ( ; i < offsetSizes.length; i++) {
+ if (fieldOffset < offsetSizes[i].offset)
+ break;
+ fieldOffset = offsetSizes[i].offset + offsetSizes[i].length;
+ }
+ unknownOffsetField.setFieldOffset(fieldOffset);
+ if (i >= offsetSizes.length)
+ fieldOffset += unknownOffsetField.getByteSize();
+ fields.add(unknownOffsetField);
+ }
+
+ unknownOffsetFields = null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+/**
+ * Pseudo-type that represents the const qualifier
+ */
+public class ConstType extends Type implements IConstType {
+
+ public ConstType(IScope scope, Map<Object, Object> properties) {
+ super("const", scope, 0, properties); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Type#getByteSize()
+ */
+ @Override
+ public int getByteSize() {
+ return updateByteSizeFromSubType();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.core.executables.Executable;
+import org.eclipse.cdt.debug.core.executables.ISourceFilesProvider;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Symbols;
+import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+public class EDCSourceFilesProvider implements ISourceFilesProvider {
+
+ public String[] getSourceFiles(Executable executable, IProgressMonitor monitor) {
+
+ try {
+ // get cached reader
+ IEDCSymbolReader reader = Symbols.getSymbolReader(executable.getPath());
+ if (reader != null) {
+ // note: don't dispose reader here
+ return reader.getSourceFiles(monitor);
+ }
+ } catch (Exception e) {
+ }
+
+ return new String[0];
+ }
+
+ public int getPriority(Executable executable) {
+ // this forces us to be called before the DE source files provider
+ return ISourceFilesProvider.HIGH_PRIORITY + 10;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class Enumeration extends MayBeQualifiedType implements IEnumeration {
+
+ ArrayList<IEnumerator> enumerators = new ArrayList<IEnumerator>();
+ HashMap<Long, IEnumerator> enumeratorsByConstant = new HashMap<Long, IEnumerator>();
+
+ public Enumeration(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ super(name, scope, byteSize, properties);
+ name = "enum"; //$NON-NLS-1$
+ }
+
+ public int enumeratorCount() {
+ return enumerators.size();
+ }
+
+ public void addEnumerator(IEnumerator enumerator) {
+ enumerators.add(enumerator);
+ enumeratorsByConstant.put(enumerator.getValue(), enumerator);
+ }
+
+ public IEnumerator getEnumeratorByName(String name) {
+ for (int i = 0; i < enumerators.size(); i++) {
+ if (enumerators.get(i).getName().equals(name))
+ return enumerators.get(i);
+ }
+ return null;
+ }
+
+ public IEnumerator getEnumeratorByValue(long value) {
+ return this.enumeratorsByConstant.get(new Long(value));
+ }
+
+ public IEnumerator[] getEnumerators() {
+ IEnumerator[] enumeratorArray = new IEnumerator[enumerators.size()];
+ for (int i = 0; i < enumerators.size(); i++) {
+ enumeratorArray[i] = enumerators.get(i);
+ }
+ return enumeratorArray;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+
+public class Enumerator implements IEnumerator {
+
+ protected final String name;
+ protected final Long value;
+
+ public Enumerator(String name, long value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public long getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return "name = " + name + ", value = " + value; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class FieldType extends Type implements IField {
+
+ private final ICompositeType compositeType;
+ private long fieldOffset;
+ private final int bitSize;
+ private final int bitOffset;
+ private final int accessibility;
+
+ public FieldType(String name, IScope scope, ICompositeType compositeType, long fieldOffset, int bitSize,
+ int bitOffset, int byteSize, int accessibility, Map<Object, Object> properties) {
+ super(name, scope, byteSize, properties);
+
+ this.compositeType = compositeType;
+ this.fieldOffset = fieldOffset;
+ this.bitSize = bitSize;
+ this.bitOffset = bitOffset;
+ this.accessibility = accessibility;
+ }
+
+ public long getFieldOffset() {
+ return this.fieldOffset;
+ }
+
+ public int getBitSize() {
+ return this.bitSize;
+ }
+
+ public int getBitOffset() {
+ return this.bitOffset;
+ }
+
+ public int getAccessibility() {
+ return this.accessibility;
+ }
+
+ public ICompositeType getCompositeTypeOwner() {
+ return this.compositeType;
+ }
+
+ public void setFieldOffset(long offset) {
+ this.fieldOffset = offset;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Type#getByteSize()
+ */
+ @Override
+ public int getByteSize() {
+ return updateByteSizeFromSubType();
+ }
+
+ @Override
+ public String toString() {
+ return name + " offset = " + fieldOffset //$NON-NLS-1$
+ + ", byteSize = " + getByteSize() //$NON-NLS-1$
+ + (bitOffset != 0 ? ", bitOffset = " + bitOffset : "") //$NON-NLS-1$ //$NON-NLS-2$
+ + (bitSize != 0 ? ", bitSize = " + bitSize : "") //$NON-NLS-1$ //$NON-NLS-2$
+ + ", accessibility = " + //$NON-NLS-1$
+ (accessibility == ICompositeType.ACCESS_PRIVATE ?
+ "private" //$NON-NLS-1$
+ : (accessibility == ICompositeType.ACCESS_PROTECTED ?
+ "protected" //$NON-NLS-1$
+ : "public")); //$NON-NLS-1$
+ }
+
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.PathUtils;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.EDCLineAddresses;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfCompileUnit;
+import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Manage line table entries of one source file (.c/.cpp/header) in one compile
+ * unit. <br>
+ * This is an internal class used by {@linkplain ModuleLineEntryProvider}.
+ */
+class FileLineEntryProvider implements ILineEntryProvider {
+
+ private List<ILineEntry> lineEntries = new ArrayList<ILineEntry>();
+ private List<ILineEntry> cuEntries = null;
+
+ /**
+ * Line entries sorted by line number. Line table entries mapped to the same
+ * source line are put together in one entry in this map.
+ *
+ * Just use TreeMap so line number keys are sorted in ascending order.
+ */
+ private TreeMap<Integer, List<ILineEntry>> lineEntriesByLine = new TreeMap<Integer, List<ILineEntry>>();
+
+ private TreeMap<IAddress, ILineEntry> lineEntriesByAddress = new TreeMap<IAddress, ILineEntry>();
+
+ private IPath filePath;
+
+ private final ICompileUnitScope compileUnitScope;
+
+ private boolean sorted;
+
+ public FileLineEntryProvider(ICompileUnitScope compileUnitScope, IPath path) {
+ this.compileUnitScope = compileUnitScope;
+ this.filePath = path;
+ this.sorted = true;
+ }
+
+ protected void setCULineEntries(Collection<ILineEntry> entries) {
+ cuEntries = new ArrayList<ILineEntry>(entries);
+ }
+
+ protected List<ILineEntry> getCULineEntries() {
+ if (cuEntries == null)
+ setCULineEntries(compileUnitScope.getLineEntries());
+ return cuEntries;
+ }
+
+ public ICompileUnitScope getCU() {
+ return compileUnitScope;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ // note: peeking into lowAddress to avoid dynamically parsing stuff while viewing #toString()
+ return filePath + " at " + ((CompileUnitScope)compileUnitScope).lowAddress + ": " + lineEntries.size() + " entries"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * @param entry
+ */
+ public void addLineEntry(ILineEntry entry) {
+ //System.out.println("Adding " + entry + " for " + compileUnitScope);
+ lineEntries.add(entry);
+
+ List<ILineEntry> currentMappings = lineEntriesByLine.get(entry.getLineNumber());
+ if (currentMappings == null) {
+ currentMappings = new ArrayList<ILineEntry>();
+ lineEntriesByLine.put(entry.getLineNumber(), currentMappings);
+ }
+ currentMappings.add(entry);
+
+ ILineEntry currentByAddress = lineEntriesByAddress.get(entry.getLowAddress());
+
+ if ( currentByAddress == null
+ || entry.getHighAddress().compareTo(currentByAddress.getHighAddress()) > 0) {
+ lineEntriesByAddress.put(entry.getLowAddress(), entry);
+ }
+
+ sorted = false;
+ }
+
+ public ILineEntry getLineEntryAtAddress(IAddress linkAddress) {
+ // NOTE: lineEntries can, and does, have multiple entries with the same low address
+ if (!sorted) {
+ // sort by start address for faster lookup by address
+ Collections.sort(lineEntries);
+ sorted = true;
+ }
+ int insertion = getLineEntryInsertionForAddress(linkAddress, lineEntries);
+ if (-1 != insertion)
+ return lineEntries.get(insertion);
+ return null;
+ }
+
+ private int getLineEntryInsertionForAddress(IAddress linkAddress,
+ final List<? extends ILineEntry> entriesToSearch) {
+
+ int insertion = Collections.binarySearch(entriesToSearch, linkAddress);
+ ILineEntry newEntry;
+ int newInsertion;
+
+ if (insertion >= 0) {
+ // line entry's low address exactly matches linkAddress, but if the line
+ // entry has an empty address range, see if a previous or subsequent
+ // line entry with the same start address has a nonempty range
+ ILineEntry entry = entriesToSearch.get(insertion);
+
+ if (entry.getHighAddress().compareTo(entry.getLowAddress()) != 0)
+ return insertion;
+
+ if (insertion > 0) {
+ newInsertion = insertion - 1;
+ newEntry = entriesToSearch.get(newInsertion);
+ while (newEntry.getLowAddress().compareTo(entry.getLowAddress()) == 0) {
+ if (newEntry.getHighAddress().compareTo(newEntry.getLowAddress()) != 0) {
+ return newInsertion;
+ }
+ if (--newInsertion < 0)
+ break;
+ newEntry = entriesToSearch.get(newInsertion);
+ }
+ }
+
+ if (insertion < entriesToSearch.size() - 1) {
+ newInsertion = insertion + 1;
+ newEntry = entriesToSearch.get(newInsertion);
+ while (newEntry.getLowAddress().compareTo(entry.getLowAddress()) == 0) {
+ if (newEntry.getHighAddress().compareTo(newEntry.getLowAddress()) != 0) {
+ return newInsertion;
+ }
+ if (++newInsertion == entriesToSearch.size())
+ break;
+ newEntry = lineEntries.get(newInsertion);
+ }
+ }
+
+ return insertion;
+ }
+
+ if (insertion == -1) {
+ return -1;
+ }
+
+ // after a failed binary search, link address is > low address of -insertion-2
+ // so see if a previous entry with the same low address has a nonempty range
+ // that includes linkAddress
+ insertion = -insertion - 2;
+
+ ILineEntry entry = entriesToSearch.get(insertion);
+
+ // low address of entry at insertion cannot match linkAddress
+ if (insertion > 0 && entry.getHighAddress().compareTo(entry.getLowAddress()) == 0) {
+ newInsertion = insertion - 1;
+ newEntry = entriesToSearch.get(newInsertion);
+ while (newEntry.getLowAddress().compareTo(entry.getLowAddress()) == 0) {
+ if (newEntry.getHighAddress().compareTo(newEntry.getLowAddress()) != 0) {
+ if (linkAddress.compareTo(newEntry.getHighAddress()) < 0)
+ return newInsertion;
+ }
+ if (--newInsertion < 0)
+ break;
+ newEntry = entriesToSearch.get(newInsertion);
+ }
+ }
+
+ if (linkAddress.compareTo(entry.getHighAddress()) < 0)
+ return insertion;
+
+ return -1;
+ }
+
+ public Collection<ILineEntry> getLineEntriesForLines(IPath path, int startLineNumber, int endLineNumber) {
+ // FIXME: ugly drive letter stuff
+ if (!filePath.setDevice(null).equals(path.setDevice(null)) )
+ {
+ if (!PathUtils.isCaseSensitive() && filePath.toOSString().compareToIgnoreCase(path.toOSString()) != 0)
+ return Collections.emptyList();
+ }
+
+ int lntSize = lineEntries.size();
+ // Note: this may not be the last line:
+ // lineEntries.get(lntSize-1).getLineNumber();
+ // as I've seen line table like this for a source file
+ // (illustrated by line #s):
+ // 7, 8, 25, 26, 12, 14
+ // where line (7, 8) forms one function, (25,26) one function,
+ // and (12, 14) one function.
+ int endLine = (endLineNumber != -1) ? endLineNumber :
+ lineEntriesByLine.lastKey();
+
+ List<ILineEntry> entries = new ArrayList<ILineEntry>(), startMappings;
+
+ /* in case the caller has requested something other than a single line,
+ * make certain this doesn't fail if the the caller passes a
+ * startLineNumber that doesn't have a direct mapping in the LNT
+ */
+ for (; null == (startMappings = lineEntriesByLine.get(startLineNumber))
+ && startLineNumber < endLine
+ ; ++startLineNumber) {}
+
+ if (startMappings != null) {
+ if (startLineNumber == endLineNumber) {
+ entries.addAll(startMappings);
+ } else if (endLineNumber == -1) {
+ // return the entries for the rest of the file
+ entries = lineEntries.subList(lineEntries.indexOf(startMappings.get(0)), lntSize);
+ } else {
+ List<ILineEntry> endMappings = lineEntriesByLine.get(endLineNumber);
+ if (endMappings != null) {
+ entries = lineEntries.subList(lineEntries.indexOf(startMappings.get(0)), lineEntries
+ .indexOf(endMappings.get(endMappings.size() - 1)) + 1);
+ } else {
+ // no mapping for end line #. just go to the end of the file
+ entries = lineEntries.subList(lineEntries.indexOf(startMappings.get(0)), lntSize);
+ }
+ }
+ }
+
+ return Collections.unmodifiableCollection(entries);
+ }
+
+ public ILineEntry getNextLineEntry(ILineEntry entry, boolean collapseInlineFunctions) {
+ if (entry == null || isLastEntryInCU(entry))
+ return null;
+ IFunctionScope entryFn = compileUnitScope.getFunctionAtAddress(entry.getLowAddress());
+ IFunctionScope container = ignoreInlineFunctions(entryFn);
+ if (container == null) // relies on ignoreInlineFunctions() to return null if func==null
+ return null;
+
+ do { // loop is for continue to retry the next entry in same function
+
+ // check if there's even a need to do further operations
+ IAddress desiredAddr = entry.getHighAddress();
+ if (desiredAddr.compareTo(container.getHighAddress()) > 0)
+ return null;
+
+ SortedMap<IAddress, ILineEntry> tailAddrs
+ = desiredAddr.equals(entry.getLowAddress()) // can be equal due to DWARF generation bug
+ ? null : lineEntriesByAddress.tailMap(desiredAddr);
+
+ // no other lines in this provider; try the CU line entries
+ if (tailAddrs == null || tailAddrs.isEmpty()) {
+ // the following case is that we are at the first line of an inline
+ // that would otherwise be the last line of a function.
+ if (collapseInlineFunctions && entryFn != container
+ && entry.getLowAddress().equals(entryFn.getLowAddress())
+ && entryFn.getParent().equals(container))
+ return getDifferentLineEntryInCU(container, entry, entryFn.getHighAddress(),
+ collapseInlineFunctions);
+ else
+ return getDifferentLineEntryInCU(entryFn, entry, desiredAddr,
+ collapseInlineFunctions);
+ }
+
+ IAddress foundAddr = tailAddrs.firstKey();
+ ILineEntry next = tailAddrs.get(foundAddr);
+ IFunctionScope foundFn = compileUnitScope.getFunctionAtAddress(foundAddr);
+
+ // [1] if the function of our the current instr ptr entry and
+ // the function at the found addr are identical, then take it!!
+ // (i.e. we lucked out!! all lines in the gap
+ // in other providers are nested inlines.)
+ if (entryFn.equals(foundFn)) {
+ // ... ok, well, line number must be different, too.
+ if (next.getLineNumber() != entry.getLineNumber())
+ return next;
+ entry = next; // just pretend this was the entry ...
+ continue; // ... and try again
+
+ // [2]if the foundAddr is immediately after this entry ...
+ } else if (foundAddr.equals(desiredAddr) && foundFn != null) { // [2]
+
+ /// ... and it is
+ // [2a] an inline parent of this inline
+ // [2b] an inline and a direct sibling (i.e. not a cousin) of this inline
+ // then take it!!
+ if (entryFn.getParent().equals(foundFn) // [2a]
+ || entryFn.getParent().equals(foundFn.getParent())) { // [2b]
+ return tailAddrs.get(foundAddr);
+
+ // ... or if it is
+ // [2c] the first line of an inline whose next line in the
+ // lineEntriesByLine table is identical to next
+ // then we'll call that good enough
+ // (you may be reading this if you have an inline function
+ // in the same file as the parent function invoking it and
+ // nothing in between; step over is probably broken for you.)
+ } else if (foundFn.getParent().equals(entryFn)
+ && areEntriesAdjacentLines(entry, next)) {
+ return next;
+ }
+
+ // similar to [2a] & [2b], even if foundAddr shows a gap
+ // [3] if the current location is an inline, and entry
+ // adjacent to the one passed is identical to next
+ // again, call it good enough for now.
+ } else if (collapseInlineFunctions
+ && (entryFn.getParent().equals(foundFn)
+ || entryFn.getParent().equals(container))
+ && areEntriesAdjacentLines(entry, next)) {
+ return next;
+ }
+
+
+ // getting here means 1 (or both) of 2 things (both slightly irrelevant)
+ //
+ // either:
+ // a] entryFn is an ancestor of foundFn
+ // b] there's a gap between the desiredAddr & the foundAddr
+ //
+ // in either case, the next entry will be found in
+ // this.compileUnitScope.lineEntries . so get it from there.
+ return getDifferentLineEntryInCU(entryFn, entry, desiredAddr,
+ collapseInlineFunctions);
+
+ } while (true);
+ }
+
+ /**
+ * @param c the child to compare
+ * @param x the function we are seeking to call an ancestor
+ * @return true if there's a function in this linkage where <b>x</b> is an ancestor of <b>c</b>
+ */
+ private static boolean isAncestorFunction(IFunctionScope c, IFunctionScope x) {
+ for (IScope p = c.getParent(); p != null; p = p.getParent())
+ if (p.equals(x))
+ return true;
+ return false;
+ }
+
+ static boolean isInlinedFunction(IFunctionScope function) {
+ return function != null && function.getParent() instanceof IFunctionScope;
+ }
+
+ private static IFunctionScope ignoreInlineFunctions(IFunctionScope function) {
+ if (function == null)
+ return null;
+
+ while (function.getParent() instanceof IFunctionScope) {
+ function = (IFunctionScope) function.getParent();
+ }
+ return function;
+ }
+
+ /**
+ * @param entry
+ * @param next
+ */
+ private boolean areEntriesAdjacentLines(ILineEntry entry, ILineEntry next) {
+ if (entry.getLineNumber() == next.getLineNumber())
+ return false;
+ SortedMap<Integer, List<ILineEntry>> tailLines
+ = lineEntriesByLine.tailMap(entry.getLineNumber());
+ if (tailLines != null) {
+ List<ILineEntry> entries = tailLines.get(entry.getLineNumber());
+ if (entries != null) {
+ int entryIdx = entries.indexOf(entry);
+ if (-1 != entryIdx) {
+ if (entry.equals(entries.get(0))
+ && ++entryIdx == entries.size()) {
+ entries = tailLines.get(next.getLineNumber());
+ if (entries != null && next.equals(entries.get(0))) {
+ return true;
+ } } } } }
+
+ return false;
+ }
+
+ /**
+ * @param entryFn
+ * @param desiredAddr
+ * @return
+ */
+ private ILineEntry getDifferentLineEntryInCU(IFunctionScope entryFn,
+ ILineEntry origEntry, IAddress desiredAddr,
+ boolean collapseInlineFunctions) {
+ if (compileUnitScope instanceof DwarfCompileUnit) { // known to be a sorted list
+ if (getCULineEntries().isEmpty())
+ return null;
+ int insertion = getLineEntryInsertionForAddress(desiredAddr, cuEntries);
+ if (-1 != insertion)
+ for (; insertion < cuEntries.size(); ++insertion) {
+ ILineEntry next = cuEntries.get(insertion);
+ if (isGoodEntry(next, entryFn, origEntry, collapseInlineFunctions))
+ return next;
+ }
+ } else {
+ boolean firstFound = false;
+ for (ILineEntry next : getCULineEntries()) {
+ if (!firstFound && desiredAddr.compareTo(next.getLowAddress()) < 0)
+ continue;
+ else
+ firstFound = true;
+
+ if (isGoodEntry(next, entryFn, origEntry, collapseInlineFunctions))
+ return next;
+ }
+ }
+
+
+ // by deduction, if we don't hit any of those 3 cases and exhaust all
+ // entries in the compuleUnitScope, then every entry after the current
+ // one is some sort of inline nested to the current function (possibly
+ // even several different inlines nested separately, possibly even with
+ // code from the original function at the original line number).
+ //
+ // in simple terms, it means the step-over that led here
+ // will turn into a step out
+
+ return null;
+ }
+
+ private boolean isGoodEntry(ILineEntry e, IFunctionScope origFn,
+ ILineEntry origEntry, boolean collapseInlineFunctions) {
+ IFunctionScope nextFn
+ = compileUnitScope.getFunctionAtAddress(e.getLowAddress());
+
+ if (origFn.equals(nextFn) && e.getLineNumber() != origEntry.getLineNumber())
+ return true; // case [1] described in caller getNextLineEntry()
+
+ else if (!collapseInlineFunctions || !isAncestorFunction(nextFn, origFn))
+ return true; // case [2] or [3] described in caller getNextLineEntry()
+
+ return false;
+ }
+
+ private boolean isFirstEntryInCU(ILineEntry e) throws IllegalArgumentException {
+ if (e == null)
+ throw new IllegalArgumentException("isFirstEntryInCU() called with null");
+ int cuEntriesSize = getCULineEntries().size();
+ return (cuEntriesSize > 0 && e.equals(cuEntries.get(0)));
+ }
+
+ private boolean isLastEntryInCU(ILineEntry e) throws IllegalArgumentException {
+ if (e == null)
+ throw new IllegalArgumentException("isFirstEntryInCU() called with null");
+ int cuEntriesSize = getCULineEntries().size();
+ return (cuEntriesSize > 0 && e.equals(cuEntries.get(cuEntriesSize-1)));
+ }
+
+ public ILineEntry getPreviousLineEntry(ILineEntry entry, boolean collapseInlineFunctions) {
+ if (entry == null || isFirstEntryInCU(entry))
+ return null;
+ IAddress entryAddr = entry.getLowAddress();
+ IFunctionScope func = compileUnitScope.getFunctionAtAddress(entryAddr);
+ IFunctionScope container = ignoreInlineFunctions(func);
+ if (container == null) // relies on ignoreInlineFunctions() to return null if func==null
+ return null;
+ SortedMap<IAddress, ILineEntry> headAddrs = lineEntriesByAddress.headMap(entryAddr);
+ if (headAddrs.isEmpty())
+ return null;
+
+ if (!collapseInlineFunctions)
+ return getPreviousLineEntryByAddress(entry, container.getLowAddress(), headAddrs);
+
+ IFunctionScope prevFunc = compileUnitScope.getFunctionAtAddress(entryAddr);
+ IFunctionScope prevContainer = ignoreInlineFunctions(prevFunc);
+ if (prevContainer == null || !prevContainer.equals(container)) {
+ return null; // relies on ignoreInlineFunctions() to return null if nextFunc==null
+ }
+
+ boolean inline = !func.equals(container);
+ boolean prevInline = !prevFunc.equals(prevContainer);
+ if (inline && prevInline) {
+ ILineEntry testPrev = headAddrs.get(headAddrs.lastKey());
+
+ // take the first head in tailAddrs if the function containing entry is
+ // [1] identical to the function containing the lastKey of headAddrs
+ // (i.e. in the same inline; skips nested inlines in other providers)
+ // [2] an inline parent of this the previous inline
+ // && the top addr of testPrev is immediately bottom addr of entry
+ // [3] an inline and a sibling of this previous inline
+ // && the top addr of testPrev is immediately bottom addr of entry
+ if (func.equals(prevFunc) // [1]
+ || (testPrev.getHighAddress().equals(entryAddr)
+ && (prevFunc.getParent().equals(func) // [2]
+ || prevFunc.getParent().equals(func.getParent())))) { // [3]
+ return testPrev;
+ }
+
+ if (!filePath.equals(compileUnitScope.getFilePath()))
+ // fall out and force reliance on the provider mapped from the
+ // compileUnitScope's filePath (i.e. the parent to these inlines)
+ return null;
+ }
+
+ SortedMap<Integer, List<ILineEntry>> headLines
+ = inline
+ ? lineEntriesByLine.headMap(headAddrs.get(headAddrs.lastKey()).getLineNumber()+1)
+ : lineEntriesByLine.headMap(entry.getLineNumber());
+
+ while (!headLines.isEmpty()) {
+ List<ILineEntry> entries = headLines.get(headLines.lastKey());
+ for (int i = entries.size()-1; i >= 0; --i) {
+ ILineEntry prev = entries.get(i);
+ if (!prev.equals(entry)
+ && prev.getHighAddress().compareTo(entryAddr) <= 0
+ && prev.getLowAddress().compareTo(container.getLowAddress()) >= 0
+ && prev.getLineNumber() != entry.getLineNumber()) {
+ return prev;
+ }
+ }
+ headLines = headLines.headMap(headLines.lastKey());
+ }
+ return null;
+ }
+
+ /**
+ * @param entry
+ * @param bottom
+ * @param addrEntries
+ * @return
+ */
+ private ILineEntry getPreviousLineEntryByAddress(ILineEntry entry, IAddress bottom,
+ SortedMap<IAddress, ILineEntry> addrEntries) {
+ while (!addrEntries.isEmpty()) {
+ ILineEntry prev = addrEntries.get(addrEntries.lastKey());
+ if (prev == null || prev.getLowAddress().compareTo(bottom) < 0)
+ break;
+ if (prev.getLineNumber() != entry.getLineNumber())
+ return prev;
+ addrEntries = addrEntries.headMap(prev.getLowAddress());
+ }
+ return null;
+ }
+
+ public ILineEntry getLineEntryInFunction(IAddress linkAddress, IFunctionScope parentFunction) {
+
+ // get all line entries with low address <= linkAddress
+ SortedMap<IAddress, ILineEntry> subMap = lineEntriesByAddress.headMap(linkAddress.add(1));
+
+ if (subMap.isEmpty())
+ {
+ // if no line entries have a low address <= linkAddress, but the address is
+ // definitely in the function, use the first entry
+ if (parentFunction.getLowAddress().compareTo(linkAddress) >= 0
+ && parentFunction.getHighAddress().compareTo(linkAddress) < 0)
+ return lineEntriesByAddress.values().iterator().next();
+ return null;
+ }
+
+ // look for an entry that includes linkAddress; if linkAddress is in the gap between
+ // two lineEntriesByAddress entries, assume the gap is due to inlined functions' code
+ ILineEntry entry = subMap.get(subMap.lastKey());
+
+ if ( entry.getHighAddress().compareTo(linkAddress) >= 0
+ || subMap.size() < lineEntriesByAddress.size()) {
+ return entry;
+ }
+
+ return null;
+ }
+
+ /**
+ * ONLY call when caller has 'collapseInlineFunctions == true' and the caller
+ * has failed to get the address in any other way.
+ *
+ * @param cuScope the scope in which to search for the entry of interest
+ * @param entry the entry in hand, for which the preceding entry is desired
+ * @return a line-entry whose end address is exactly first address of the passed entry
+ */
+ protected ILineEntry getPreviousLineEntryInCU(ILineEntry entry) {
+ IAddress desiredEndAddress = entry.getLowAddress();
+
+ for (ILineEntry testEntry : getCULineEntries()) {
+ if (!desiredEndAddress.equals(testEntry.getHighAddress()))
+ continue;
+ IFunctionScope entryFn
+ = compileUnitScope.getFunctionAtAddress(entry.getLowAddress());
+ IScope entryParent = entryFn.getParent();
+
+ IFunctionScope prevFn
+ = compileUnitScope.getFunctionAtAddress(testEntry.getLowAddress());
+ if (isInlinedFunction(entryFn)
+ && (isAncestorFunction(entryFn, prevFn)
+ || entryParent != null && entryParent.equals(prevFn.getParent()))) {
+ return testEntry;
+ }
+ if (isAncestorFunction(prevFn, entryFn)) {
+ // TODO: add logic to get entry at start of testEntry
+ // something like:
+ // dp {
+ // desiredEndAddress = testEntry.getLowAddress();
+ // testEntry = getPreviousLineEntryInCU(cuScope, testEntry, null);
+ // get back to where the function for testEntry is a sibling or
+ // ancestor of entryFn
+ // } while (testEntry != null)
+ //
+ // but this is mostly here to help with step-out while standing
+ // on first instruction of inline coinciding with source, so fix later
+ }
+ break; // got the address of interest; just wasn't useful as we wanted
+ }
+ return null;
+ }
+
+ /*
+ * Find code line for the anchor in one compile unit.
+ * The returned value should only contain one entry.
+ *
+ * (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider#findClosestLineWithCode(org.eclipse.core.runtime.IPath, int, int)
+ */
+ public List<ILineAddresses> findClosestLineWithCode(IPath sourceFile,
+ int anchorLine, int neighbor_limit) {
+ List<ILineAddresses> ret = new ArrayList<ILineAddresses>(1);
+
+ // FIXME: ugly drive letter stuff
+ if (!filePath.setDevice(null).equals(sourceFile.setDevice(null)) )
+ {
+ if (!PathUtils.isCaseSensitive() && filePath.toOSString().compareToIgnoreCase(sourceFile.toOSString()) != 0)
+ return ret;
+ }
+
+ EDCLineAddresses codeLine = null;
+
+ codeLine = getLineAddresses(anchorLine);
+ if (codeLine != null) {
+ // The anchor itself has code
+ ret.add(codeLine);
+ return ret;
+ }
+
+ int limit;
+
+ // Anchor has no code, but there are code lines above the anchor.
+ // find the closest one.
+ EDCLineAddresses candidate_above = null;
+ if (anchorLine > lineEntriesByLine.firstKey()) {
+ if (neighbor_limit == -1)
+ limit = lineEntriesByLine.firstKey();
+ else {
+ limit = anchorLine - neighbor_limit;
+ if (limit < lineEntriesByLine.firstKey())
+ limit = lineEntriesByLine.firstKey();
+ }
+
+ for (int i = anchorLine-1; i >= limit; i--) {
+ candidate_above = getLineAddresses(i);
+ if (candidate_above != null)
+ break;
+ }
+ }
+
+ // there are code lines below the anchor.
+ // find the closest one.
+ EDCLineAddresses candidate_below = null;
+ if (anchorLine < lineEntriesByLine.lastKey()) {
+ if (neighbor_limit == -1)
+ limit = lineEntriesByLine.lastKey();
+ else {
+ limit = anchorLine + neighbor_limit;
+ if (limit > lineEntriesByLine.lastKey())
+ limit = lineEntriesByLine.lastKey();
+ }
+
+ for (int i = anchorLine+1; i <= limit; i++) {
+ candidate_below = getLineAddresses(i);
+ if (candidate_below != null)
+ break;
+ }
+ }
+
+ if (candidate_above == null)
+ codeLine = candidate_below;
+ else {
+ if (candidate_below == null)
+ codeLine = candidate_above;
+ else {
+ int distance_above = anchorLine - candidate_above.getLineNumber();
+ int distance_below = candidate_below.getLineNumber() - anchorLine;
+
+ if (distance_above == distance_below)
+ codeLine = candidate_below;
+ else
+ codeLine = (distance_above < distance_below)? candidate_above : candidate_below;
+ }
+ }
+
+ if (codeLine != null)
+ ret.add(codeLine);
+
+ return ret;
+ }
+
+ /**
+ * Create EDCLineAddresses object for a line if it has code.
+ * @param line
+ * @return null if the line has no code.
+ */
+ private EDCLineAddresses getLineAddresses(int line) {
+ if (! lineEntriesByLine.containsKey(line))
+ return null;
+
+ List<IAddress> line_addrs = new ArrayList<IAddress>();
+ int lastColumn = -2;
+
+ for (ILineEntry e : lineEntriesByLine.get(line)) {
+ /*
+ * When there is more than one line mapping for the source line:
+ *
+ * If they are multiple logical code segments for the same source
+ * line, but in different columns, only remember address of the
+ * first entry.
+ *
+ * If they are templates or inline functions, the column will be the
+ * same, and we record addresses of all entries.
+ */
+ int entryColumn = e.getColumnNumber();
+ if (line_addrs.size() == 0 || lastColumn == entryColumn)
+ line_addrs.add(e.getLowAddress());
+
+ lastColumn = entryColumn;
+ }
+
+ return new EDCLineAddresses(line, line_addrs);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.core.runtime.IPath;
+
+public class FunctionScope extends Scope implements IFunctionScope {
+
+ protected ILocationProvider frameBaseLocationProvider;
+ protected List<IVariable> parameters = new ArrayList<IVariable>();
+ private int declLine;
+ private int declColumn;
+ private IPath declFile;
+
+ public FunctionScope(String name, IScope parent, IAddress lowAddress, IAddress highAddress,
+ ILocationProvider frameBaseLocationProvider) {
+ super(name, lowAddress, highAddress, parent);
+
+ this.frameBaseLocationProvider = frameBaseLocationProvider;
+ }
+
+ public Collection<IVariable> getParameters() {
+ return Collections.unmodifiableCollection(parameters);
+ }
+
+ public ILocationProvider getFrameBaseLocation() {
+ return frameBaseLocationProvider;
+ }
+
+ public Collection<IVariable> getVariablesInTree() {
+ List<IVariable> variables = new ArrayList<IVariable>();
+ variables.addAll(super.getVariables());
+
+ // check for variables in children as well
+ for (IScope child : children) {
+ if (child instanceof IFunctionScope)
+ variables.addAll(((IFunctionScope) child).getVariablesInTree());
+ else if (child instanceof ILexicalBlockScope)
+ variables.addAll(((ILexicalBlockScope) child).getVariablesInTree());
+ else
+ variables.addAll(child.getVariables());
+ }
+
+ return variables;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IFunctionScope#getScopedVariables(org.eclipse.cdt.core.IAddress)
+ */
+ public Collection<IVariable> getScopedVariables(IAddress linkAddress) {
+ // Unfortunately, lexical blocks and inlined functions may span several scopes or use ranges;
+ // don't #getScopeAtAddress() and go up the parent chain, but iterate them top-down.
+
+ List<IVariable> scoped = new ArrayList<IVariable>();
+ List<String> varNames = new ArrayList<String>();
+
+ recurseGetScopedVariables(this, scoped, varNames, linkAddress);
+
+ return scoped;
+ }
+
+ protected static void recurseGetScopedVariables(IScope scope, List<IVariable> scoped, List<String> varNames, IAddress linkAddress) {
+ long scopeOffset = linkAddress.add(scope.getLowAddress().getValue().negate()).getValue().longValue();
+
+ for (IVariable var : scope.getVariables()) {
+ if (scopeOffset >= var.getStartScope() && var.getLocationProvider().isLocationKnown(linkAddress)) {
+ String varName = var.getName();
+ if (!varNames.contains(varName)) {
+ scoped.add(var);
+ varNames.add(varName);
+ }
+ }
+ }
+
+ boolean isFunctionScope = (scope instanceof IFunctionScope);
+ if (isFunctionScope) {
+ for (IVariable var : ((FunctionScope) scope).getParameters()) {
+ if (scopeOffset >= var.getStartScope() && var.getLocationProvider().isLocationKnown(linkAddress)) {
+ String varName = var.getName();
+ if (!varNames.contains(varName)) {
+ scoped.add(var);
+ varNames.add(varName);
+ }
+ }
+ }
+ }
+
+ for (IScope kid : scope.getChildren()) {
+ // notice this is > instead of >= like caller getScopedVariables() ...
+ // thus stepping out of scope to next instr won't result in scoped variables still being seen
+ if (kid.getLowAddress().compareTo(linkAddress) <= 0 && kid.getHighAddress().compareTo(linkAddress) > 0)
+ recurseGetScopedVariables(kid, scoped, varNames, linkAddress);
+ else if (isFunctionScope && linkAddress.compareTo(kid.getHighAddress()) == 0)
+ recurseGetScopedVariables(kid, scoped, varNames, kid.getHighAddress());
+ else if (kid instanceof ILexicalBlockScope) {
+ // RVCT 4.x Dwarf lexical blocks may contain local variables whose live ranges extend
+ // beyond the bounds of their enclosing lexical blocks, so check lexical block variables
+ recurseGetLexicalBlockVariables(kid, scoped, varNames, linkAddress);
+ }
+ }
+ }
+
+ /**
+ * Find lexical block variables whose lifetimes include the given address, whether or not the address is
+ * inside the lexical block.
+ * Note: RVCT 4.x Dwarf lexical blocks may contain local variables whose live ranges extend beyond
+ * the bounds of their enclosing lexical blocks
+ **/
+ private static void recurseGetLexicalBlockVariables(IScope scope, List<IVariable> scoped, List<String> varNames, IAddress linkAddress) {
+ if (!(scope instanceof ILexicalBlockScope))
+ return;
+
+ for (IVariable var : scope.getVariables()) {
+ ILocationProvider locationProvider = var.getLocationProvider();
+ if (!locationProvider.lifetimeMustMatchScope() && locationProvider.isLocationKnown(linkAddress)) {
+ String varName = var.getName();
+ if (!varNames.contains(varName)) {
+ scoped.add(var);
+ varNames.add(varName);
+ }
+ }
+ }
+
+ for (IScope kid : scope.getChildren()) {
+ recurseGetLexicalBlockVariables(kid, scoped, varNames, linkAddress);
+ }
+ }
+
+ public void addParameter(IVariable parameter) {
+ parameters.add(parameter);
+ }
+
+ public IPath getDeclFile() {
+ return declFile;
+ }
+
+ public void setDeclFile(IPath declFile) {
+ this.declFile = declFile;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IFunctionScope#getDeclLine()
+ */
+ public int getDeclLine() {
+ return declLine;
+ }
+
+ public void setDeclLine(int declLine) {
+ this.declLine = declLine;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IFunctionScope#getDeclColumn()
+ */
+ public int getDeclColumn() {
+ return declColumn;
+ }
+
+ public void setDeclColumn(int declColumn) {
+ this.declColumn = declColumn;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#addChild(org.eclipse.cdt.debug.edc.internal.symbols.IScope)
+ */
+ @Override
+ public void addChild(IScope scope) {
+ super.addChild(scope);
+
+ if (scope instanceof IFunctionScope)
+ addLineInfoToParent(scope);
+ }
+
+
+ public void setLocationProvider(ILocationProvider locationProvider) {
+ this.frameBaseLocationProvider = locationProvider;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+public interface IAggregate {
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+public interface IArrayBoundType {
+
+ /** bound of this array dimension. E.g., for "int a[7][8]", this would be
+ * either 7 or 8.
+ */
+ public long getBoundCount();
+
+ /** number of array elements associated with each index of this array
+ * dimension.
+ * E.g., for "int a[7][8]", "a[1]" comprises 8 elements, but "a[1][2]"
+ * comprises 1 element.
+ */
+ public long getElementCount();
+
+ /** array dimension ordinal. E.g., for "int a[7][8]", "[7]" is index 1 and
+ * "[8]" is index 0;
+ */
+ public long getDimensionIndex();
+
+ public void multiplyElementCount(long multiply);
+
+ public void incDimensionIndex();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IArrayType extends IType, IAggregate {
+
+ /**
+ * get the type that this is an array of
+ */
+ public IType getType();
+
+ public int getBoundsCount();
+
+ public void addBound(IArrayBoundType bound);
+
+ public IArrayBoundType[] getBounds();
+
+ // get the Nth bound. E.g., for "a[X][Y]", getBound(0) returns info for
+ // "[X]"
+ public IArrayBoundType getBound(int index);
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * Interface for basic types.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IBasicType extends IType {
+
+ public static final int t_unspecified = IASTSimpleDeclSpecifier.t_unspecified;
+ public static final int t_void = IASTSimpleDeclSpecifier.t_void;
+ public static final int t_char = IASTSimpleDeclSpecifier.t_char;
+ public static final int t_int = IASTSimpleDeclSpecifier.t_int;
+ public static final int t_float = IASTSimpleDeclSpecifier.t_float;
+ public static final int t_double = IASTSimpleDeclSpecifier.t_double;
+
+ /**
+ * This returns the built-in type for the declaration. The type is then
+ * refined by qualifiers for signed/unsigned and short/long. The type could
+ * also be unspecified which usually means int.
+ *
+ */
+ public int getBaseType();
+
+ public boolean isSigned();
+
+ public boolean isUnsigned();
+
+ public boolean isShort();
+
+ public boolean isLong();
+
+ public boolean isLongLong();
+
+ public boolean isComplex();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
+
+/**
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ICPPBasicType extends IBasicType {
+
+ public static final int IS_LONG = 1;
+ public static final int IS_SHORT = 1 << 1;
+ public static final int IS_SIGNED = 1 << 2;
+ public static final int IS_UNSIGNED = 1 << 3;
+ public static final int IS_COMPLEX = 1 << 4; // for gpp-types
+ public static final int IS_IMAGINARY = 1 << 5; // for gpp-types
+ public static final int IS_LONG_LONG = 1 << 6; // for gpp-types
+ public static final int LAST = IS_LONG_LONG;
+
+ // Extra types
+ public static final int t_bool = ICPPASTSimpleDeclSpecifier.t_bool;
+ public static final int t_wchar_t = ICPPASTSimpleDeclSpecifier.t_wchar_t;
+
+ /**
+ * @return a combination of qualifiers.
+ */
+ public int getQualifierBits();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public interface ICompositeType extends IType, IAggregate {
+
+ // accessibility of an inherited class or of a composite's field
+ public static int ACCESS_PUBLIC = 0;
+ public static int ACCESS_PROTECTED = 1;
+ public static int ACCESS_PRIVATE = 2;
+
+ public static final int k_class = ICPPASTCompositeTypeSpecifier.k_class;
+ public static final int k_struct = IASTCompositeTypeSpecifier.k_struct;
+ public static final int k_union = IASTCompositeTypeSpecifier.k_union;
+
+ /**
+ * Kind of composite (class, struct, union)
+ *
+ * @return kind
+ */
+ public int getKey();
+
+ /**
+ * Number of fields/enumerators in composite
+ *
+ * @return count
+ */
+ public int fieldCount();
+
+ /**
+ * Add a field/member to the end of the list of fields or enumerators
+ * Intended for use by a debug information parser.
+ *
+ * @param field
+ * field to add
+ */
+ public void addField(IField field);
+
+ /**
+ * Get an array of fields/enumerators in composite
+ *
+ * @return array of fields/enumerators, or IField.EMPTY_FIELD_ARRAY if no
+ * fields/enumerators
+ */
+ public IField[] getFields();
+
+ /**
+ * Add a template parameter to the end of the list of template parameters
+ * Intended for use by a debug information parser.
+ *
+ * @param templateParam
+ * template parameter to add
+ */
+ public void addTemplateParam(ITemplateParam templateParam);
+
+ /**
+ * Get an array of template parameters in composite
+ *
+ * @return array of template parameters, or empty array if no
+ * template parameters
+ */
+ public ITemplateParam[] getTemplateParams();
+
+ /**
+ * Find the composite fields/members with the given name
+ *
+ * @param name field name, which may contain "::" separators
+ * @return array of matching fields if any exist, or null otherwise
+ */
+ public IField[] findFields(String name);
+
+ /**
+ * Number of classes and structs from which the composite inherits
+ *
+ * @return count
+ */
+ public int inheritanceCount();
+
+ /**
+ * Add an inherited-from class or struct to the end of the list
+ * of inherited-from classes and structs
+ * Intended for use by a debug information parser.
+ *
+ * @param inheritance
+ * information about class/struct from which this
+ * composite inherits
+ */
+ public void addInheritance(IInheritance inheritance);
+
+ /**
+ * Get an array of inherited-from classes/structs for composite
+ *
+ * @return array of information about classes and structs from which this
+ * composite inherits, or an empty array if nothing is inherited
+ */
+ public IInheritance[] getInheritances();
+
+ /**
+ * Get the name without a type prefix (e.g., "foo" instead of "class foo")
+ *
+ * @return composite name without a type
+ */
+ public String getBaseName();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+/**
+ * Interface used to identify pseudo-types that represent const qualifiers
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IConstType extends IQualifierType {
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public interface IEnumeration extends IType {
+
+ public int enumeratorCount();
+
+ public void addEnumerator(IEnumerator enumerator);
+
+ public IEnumerator getEnumeratorByName(String name);
+
+ public IEnumerator getEnumeratorByValue(long value);
+
+ /**
+ * returns an array of the IEnumerators declared in this enumeration
+ */
+ IEnumerator[] getEnumerators();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IField extends IType {
+
+ public static final IField[] EMPTY_FIELD_ARRAY = new IField[0];
+
+ public long getFieldOffset();
+
+ public int getBitSize();
+
+ public int getBitOffset();
+
+ public int getAccessibility();
+
+ // member offset may need to be computed
+ public void setFieldOffset(long offset);
+
+ /**
+ * Returns the composite type that owns the field.
+ */
+ ICompositeType getCompositeTypeOwner();
+
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+
+/**
+ * This represents a type which is a proxy for an unparsed type.
+ */
+public interface IForwardTypeReference extends IType {
+ /** Realize (if needed) and return the actual type */
+ IType getReferencedType();
+}
--- /dev/null
+/*******************************************************************************
+
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public interface IInheritance extends IType {
+
+ /**
+ * Get offset to inherited fields
+ *
+ * @return offset within inherited type to the fields inherited
+ */
+ public long getFieldsOffset();
+
+ /**
+ * Get type of accessibility to inherited fields (public, protected, or private)
+ *
+ * @return type of accessibility (one of: {@link ICompositeType}'s
+ * ACCESS_PUBLIC, ACCESS_PROTECTED, or ACCESS_PRIVATE)
+ */
+ public int getAccessibility();
+
+ /**
+ * Get properties
+ *
+ * @return general map of type properties
+ */
+ public Map<Object, Object> getProperties();
+
+ /**
+ * Get type inherited from
+ *
+ * @return type
+ */
+ public IType getType();
+
+ /**
+ * Set type inherited from
+ *
+ * @param type
+ * type inherited from
+ */
+ public void setType(IType type);
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Collection;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+
+/**
+ * Interface representing a lexical block scope. A lexical block is a block of
+ * code inside of a function. A lexical block may contain other lexical blocks
+ * as children.
+ */
+public interface ILexicalBlockScope extends IScope {
+
+ /**
+ * Gets the list of variables in this scope and any child scopes
+ *
+ * @return unmodifiable list of variables which may be empty
+ */
+ Collection<IVariable> getVariablesInTree();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public interface IMayBeQualifedType extends IType {
+
+ /**
+ * Whether this is a const type
+ *
+ * @return true only if this is a const type
+ */
+ public boolean isConst();
+
+ /**
+ * Whether this is a volatile type
+ *
+ * @return true only if this is a volatile type
+ */
+ public boolean isVolatile();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * Interface for pointer types
+ */
+public interface IPointerType extends IType {
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * Interface used to identify pseudo-types that represent qualifiers
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IQualifierType extends IType {
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public interface IReferenceType extends IType {
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.core.IAddress;
+
+public interface IRuntimeSection extends ISection {
+
+ static final String PROPERTY_RUNTIME_ADDRESS = "runtime_address"; //$NON-NLS-1$
+
+ /**
+ * Get the base runtime address of the section
+ *
+ * @return the base runtime address
+ */
+ IAddress getRuntimeAddress();
+
+ /**
+ * Relocates the section to the given runtime base address
+ *
+ * @param runtimeAddress
+ * the relocated base address of the section
+ */
+ void relocate(IAddress runtimeAddress);
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+
+/**
+ * Interface representing a section in memory. It is segment in Elf file and a
+ * section in PE file.
+ */
+public interface ISection {
+
+ /**
+ * Commonly known properties of a section
+ */
+ static final String PROPERTY_ID = "id"; //$NON-NLS-1$
+ static final String PROPERTY_SIZE = "size"; //$NON-NLS-1$
+ static final String PROPERTY_LINK_ADDRESS = "link_address"; //$NON-NLS-1$
+ /** Canonical section name: one of NAME_TEXT, NAME_DATA, NAME_RODATA, or NAME_BSS */
+ static final String PROPERTY_NAME = "name"; //$NON-NLS-1$
+
+ /* TODO: not used
+ static final String PROPERTY_READABLE = "readable";
+ static final String PROPERTY_WRITABLE = "writable";
+ static final String PROPERTY_EXECUTABLE = "executable";
+ */
+
+ static final String NAME_TEXT = ".text"; //$NON-NLS-1$
+ static final String NAME_DATA = ".data"; //$NON-NLS-1$
+ static final String NAME_RODATA = ".rodata"; // read only data //$NON-NLS-1$
+ static final String NAME_BSS = ".bss"; // uninitialized data //$NON-NLS-1$
+
+ /**
+ * Get the section id
+ *
+ * @return the section id
+ */
+ int getId();
+
+ /**
+ * Get the section size
+ *
+ * @return the section size
+ */
+ long getSize();
+
+ /**
+ * Get the base link address of the section
+ *
+ * @return the base link address
+ */
+ IAddress getLinkAddress();
+
+ /**
+ * Get the properties of the section
+ *
+ * @return the section properties
+ */
+ Map<String, Object> getProperties();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ *
+ */
+public interface ISubroutineType extends IType {
+ // TODO (parameters) (for now, only gathers the return type)
+}
--- /dev/null
+/*******************************************************************************
+
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public interface ITemplateParam extends IType {
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+/**
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ITypedef extends IType {
+
+ /**
+ * Get the type that this is the typedef of
+ *
+ * @return typedef'ed type
+ */
+ public IType getType();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class InheritanceType extends Type implements IInheritance {
+
+ // access type of inheritance
+ private final int accessibility;
+
+ // offset in inherited class to the inherited fields
+ private final long fieldsOffset;
+
+ public InheritanceType(IScope scope, int accessibility, long inheritanceOffset, Map<Object, Object> properties) {
+ super("", scope, 0, properties); //$NON-NLS-1$
+ this.accessibility = accessibility;
+ this.fieldsOffset = inheritanceOffset;
+ }
+
+ public int getAccessibility() {
+ return this.accessibility;
+ }
+
+ public long getFieldsOffset() {
+ return this.fieldsOffset;
+ }
+
+ @Override
+ public String getName() {
+ if (getType() instanceof ICompositeType)
+ return ((ICompositeType) getType()).getBaseName();
+
+ if (getType() == null)
+ return "$"; //$NON-NLS-1$ // do not confuse expression formatter
+ else
+ return getType().getName();
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.symbols.IInvalidVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public class InvalidVariableLocation implements IInvalidVariableLocation {
+
+ private String message;
+
+ public InvalidVariableLocation(String message) {
+ if (message == null)
+ this.message = ""; //$NON-NLS-1$
+ else
+ this.message = message;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Invalid: " + getMessage(); //$NON-NLS-1$
+ }
+
+
+ public String getMessage() {
+ return this.message;
+ }
+
+ public void setMessage(String message) {
+ if (message == null)
+ this.message = ""; //$NON-NLS-1$
+ else
+ this.message = message;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#readValue()
+ */
+ public BigInteger readValue(int bytes) throws CoreException {
+ throw EDCDebugger.newCoreException(message);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#addOffset(long)
+ */
+ public IVariableLocation addOffset(long offset) {
+ return this;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#getLocationName(org.eclipse.cdt.dsf.service.DsfServicesTracker)
+ */
+ public String getLocationName() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#getAddress()
+ */
+ public IAddress getAddress() {
+ return null;
+ }
+
+ public void writeValue(int bytes, BigInteger value) throws CoreException {
+ throw EDCDebugger.newCoreException(SymbolsMessages.InvalidVariableLocation_CannotWriteInvalidLocation);
+ }
+
+ public IDMContext getContext() {
+ return null;
+ }
+
+ public EDCServicesTracker getServicesTracker() {
+ return null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+
+public class LexicalBlockScope extends Scope implements ILexicalBlockScope {
+
+ public LexicalBlockScope(String name, IScope parent, IAddress lowAddress, IAddress highAddress) {
+ super(name, lowAddress, highAddress, parent);
+ }
+
+ public Collection<IVariable> getVariablesInTree() {
+ List<IVariable> variables = new ArrayList<IVariable>();
+ variables.addAll(super.getVariables());
+
+ // check for variables in children as well
+ for (IScope child : children) {
+ if (child instanceof IFunctionScope)
+ variables.addAll(((IFunctionScope) child).getVariablesInTree());
+ else if (child instanceof ILexicalBlockScope)
+ variables.addAll(((ILexicalBlockScope) child).getVariablesInTree());
+ else
+ variables.addAll(child.getVariables());
+ }
+
+ return variables;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.core.runtime.IPath;
+
+public class LineEntry implements ILineEntry {
+
+ protected IPath filePath;
+ protected int lineNumber;
+ protected int columnNumber;
+ protected IAddress lowAddress;
+ protected IAddress highAddress;
+
+ public LineEntry(IPath filePath, int lineNumber, int columnNumber, IAddress lowAddress, IAddress highAddress) {
+ this.filePath = filePath;
+ this.lineNumber = lineNumber;
+ this.columnNumber = columnNumber;
+ this.lowAddress = lowAddress;
+ this.highAddress = highAddress;
+ }
+
+ public IPath getFilePath() {
+ return filePath;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public int getColumnNumber() {
+ return columnNumber;
+ }
+
+ public IAddress getLowAddress() {
+ return lowAddress;
+ }
+
+ public IAddress getHighAddress() {
+ return highAddress;
+ }
+
+ public void setHighAddress(IAddress highAddress) {
+ this.highAddress = highAddress;
+ }
+
+ public int compareTo(Object o) {
+ if (o instanceof ILineEntry) {
+ // some entries have low==high
+ int diff = lowAddress.compareTo(((ILineEntry) o).getLowAddress());
+ if (diff != 0)
+ return diff;
+ if (highAddress != null && ((ILineEntry) o).getHighAddress() != null)
+ return highAddress.compareTo(((ILineEntry) o).getHighAddress());
+ return 0;
+ } else if (o instanceof IAddress) {
+ return lowAddress.compareTo(o);
+ }
+
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "LineEntry [lowAddress="
+ + (lowAddress != null ? lowAddress.toHexAddressString() : "null")
+ + ", highAddress="
+ + (highAddress != null ? highAddress.toHexAddressString() : "null")
+ + ((filePath != null) ? ", path=" + filePath.toOSString() + ", " : ", ")
+ + "line=" + lineNumber + ", column=" + columnNumber
+ + "]" ; //$NON-NLS-1$
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+public class MayBeQualifiedType extends Type implements IMayBeQualifedType {
+ boolean qualifiersChecked = false;
+ boolean isConst = false;
+ boolean isVolatile = false;
+
+ public MayBeQualifiedType(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ super(name, scope, byteSize, properties);
+ }
+
+ public boolean isConst() {
+ if (!qualifiersChecked)
+ checkQualifiers();
+ return isConst;
+ }
+
+ public boolean isVolatile() {
+ if (!qualifiersChecked)
+ checkQualifiers();
+ return isVolatile;
+ }
+
+ /**
+ * Check whether the type is qualified. If so, set the proper booleans.
+ */
+ private void checkQualifiers() {
+ IType checkedType = getType(); // so it will be resolved
+ while (checkedType != null && (checkedType instanceof IQualifierType)) {
+ isConst |= checkedType instanceof ConstType;
+ isVolatile |= checkedType instanceof VolatileType;
+ checkedType = checkedType.getType();
+ }
+ qualifiersChecked = true;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.math.BigInteger;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.MemoryUtils;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Memory;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExecutionDMC;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.symbols.IMemoryVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.datamodel.DMContexts;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.core.model.MemoryByte;
+
+public class MemoryVariableLocation implements IMemoryVariableLocation {
+
+ protected IAddress address;
+ protected boolean isRuntimeAddress;
+ protected EDCServicesTracker tracker;
+ private IDMContext context;
+
+ public MemoryVariableLocation(EDCServicesTracker tracker, IDMContext context, BigInteger addressValue,
+ boolean isRuntimeAddress) {
+ initialize(tracker,context,addressValue,isRuntimeAddress, false);
+ }
+
+ public MemoryVariableLocation(EDCServicesTracker tracker, IDMContext context, BigInteger addressValue,
+ boolean isRuntimeAddress, boolean checkNonLocalConstVariable) {
+ // checkConstVariableAddress should only be true for global or static (non-local) constant variables
+ initialize(tracker,context,addressValue,isRuntimeAddress, checkNonLocalConstVariable);
+ }
+
+ private void initialize(EDCServicesTracker tracker, IDMContext context, BigInteger addressValue,
+ boolean isRuntimeAddress, boolean checkNonLocalConstVariable) {
+ this.tracker = tracker;
+ this.context = context;
+ BigInteger MAXADDR = BigInteger.valueOf(0xffffffffL);
+ ITargetEnvironment targetEnvironment = tracker.getService(ITargetEnvironment.class);
+ if (targetEnvironment != null && targetEnvironment.getPointerSize() == 8) {
+ MAXADDR = BigInteger.valueOf(0xffffffffffffffffL);
+ }
+ this.address = new Addr64(addressValue.and(MAXADDR));
+ this.isRuntimeAddress = isRuntimeAddress;
+
+ if (checkNonLocalConstVariable)
+ checkNonLocalConstVariableAddr();
+ }
+
+ /**
+ * Since a global or static constant variable may be either at a fixed ROM address or at a runtime address,
+ * and a compiler may not know which is correct when generating debug info, check the address
+ */
+ private void checkNonLocalConstVariableAddr() {
+ // TODO: Instead of this, query whether the constant variable's address is a fixed or runtime address
+
+ // Try reading a byte at the supposed address, and, if that fails, switch the sense of this.isRuntimeAddress
+ IAddress theAddress = address;
+ if (!isRuntimeAddress) {
+ StackFrameDMC frame = DMContexts.getAncestorOfType(context, StackFrameDMC.class);
+ if (frame == null) {
+ isRuntimeAddress = !isRuntimeAddress;
+ return;
+ }
+ theAddress = frame.getModule().toRuntimeAddress(theAddress);
+ }
+
+ ExecutionDMC exeDMC = DMContexts.getAncestorOfType(context, ExecutionDMC.class);
+
+ Memory memoryService = tracker.getService(Memory.class);
+ ArrayList<MemoryByte> memBuffer = new ArrayList<MemoryByte>(1);
+ IStatus memGetStatus = memoryService.getMemory(exeDMC, theAddress, memBuffer, 1, 1);
+ if (!memGetStatus.isOK()) {
+ isRuntimeAddress = !isRuntimeAddress;
+ return;
+ }
+
+ if (!memBuffer.get(0).isReadable()) {
+ isRuntimeAddress = !isRuntimeAddress;
+ return;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "0x" + Long.toHexString(address.getValue().longValue()) + //$NON-NLS-1$
+ (isRuntimeAddress ? "" : " (link address)"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public IAddress getAddress() {
+ try {
+ return getRealAddress();
+ } catch (CoreException e) {
+ return null;
+ }
+ }
+
+ public boolean isRuntimeAddress() {
+ return isRuntimeAddress;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#readValue()
+ */
+ public BigInteger readValue(int varSize) throws CoreException {
+ IAddress theAddress = address;
+ if (!isRuntimeAddress) {
+ theAddress = getRealAddress();
+ }
+
+ ExecutionDMC exeDMC = DMContexts.getAncestorOfType(context, ExecutionDMC.class);
+
+ Memory memoryService = tracker.getService(Memory.class);
+ ArrayList<MemoryByte> memBuffer = new ArrayList<MemoryByte>();
+ IStatus memGetStatus = memoryService.getMemory(exeDMC, theAddress, memBuffer, varSize, 1);
+ if (!memGetStatus.isOK()) {
+ throw EDCDebugger.newCoreException(MessageFormat.format(
+ SymbolsMessages.MemoryVariableLocation_CannotReadAddrFormat, theAddress.toHexAddressString()));
+ }
+
+ // check each byte
+ for (int i = 0; i < memBuffer.size(); i++) {
+ if (!memBuffer.get(i).isReadable())
+ throw EDCDebugger.newCoreException(MessageFormat.format(
+ SymbolsMessages.MemoryVariableLocation_CannotReadAddrFormat, theAddress.add(i).getValue().toString(16)));
+ }
+
+ MemoryByte[] memArray = memBuffer.toArray(new MemoryByte[varSize]);
+
+ return MemoryUtils.convertByteArrayToUnsignedLong(
+ memArray, getEndian());
+ }
+
+ private int getEndian() {
+ ITargetEnvironment targetEnvironment = tracker.getService(ITargetEnvironment.class);
+ int endian = MemoryUtils.LITTLE_ENDIAN;
+ if (targetEnvironment != null)
+ endian = targetEnvironment.isLittleEndian(context) ? MemoryUtils.LITTLE_ENDIAN : MemoryUtils.BIG_ENDIAN;
+ return endian;
+ }
+
+ /**
+ * @return
+ * @throws CoreException
+ */
+ private IAddress getRealAddress() throws CoreException {
+ IAddress theAddress = address;
+ if (!isRuntimeAddress) {
+ StackFrameDMC frame = DMContexts.getAncestorOfType(context, StackFrameDMC.class);
+ if (frame == null)
+ throw EDCDebugger.newCoreException(SymbolsMessages.MemoryVariableLocation_CannotFindFrame);
+ theAddress = frame.getModule().toRuntimeAddress(theAddress);
+ }
+ return theAddress;
+ }
+
+ public IVariableLocation addOffset(long offset) {
+ return new MemoryVariableLocation(tracker, context,
+ address.getValue().add(BigInteger.valueOf(offset)), isRuntimeAddress);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#getLocationName(org.eclipse.cdt.dsf.service.DsfServicesTracker)
+ */
+ public String getLocationName() {
+ if (!isRuntimeAddress) {
+ try {
+ return SymbolsMessages.MemoryVariableLocation_Hex + Long.toHexString(getRealAddress().getValue().longValue());
+ } catch (CoreException e) {
+ return SymbolsMessages.MemoryVariableLocation_Hex + Long.toHexString(address.getValue().longValue()) + SymbolsMessages.MemoryVariableLocation_LinkTime; // should not happen
+ }
+ } else {
+ return SymbolsMessages.MemoryVariableLocation_Hex + Long.toHexString(address.getValue().longValue());
+ }
+ }
+
+ public void writeValue(final int bytes, BigInteger value) throws CoreException {
+ final byte[] buffer = MemoryUtils.convertSignedBigIntToByteArray(value, getEndian(), bytes);
+ final IAddress theAddress = !isRuntimeAddress ? getRealAddress() : address;
+ final ExecutionDMC exeDMC = DMContexts.getAncestorOfType(context, ExecutionDMC.class);
+ final Memory memory = tracker.getService(Memory.class);
+ IStatus status = memory.setMemory(exeDMC, theAddress, 1, bytes, buffer);
+ if (!status.isOK()) {
+ throw EDCDebugger.newCoreException(MessageFormat.format(
+ SymbolsMessages.MemoryVariableLocation_CannotWriteAddrFormat, theAddress.toHexAddressString()), status.getException());
+ }
+ }
+
+ public IDMContext getContext() {
+ return context;
+ }
+
+ public EDCServicesTracker getServicesTracker() {
+ return tracker;
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.PathUtils;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules.EDCLineAddresses;
+import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider;
+import org.eclipse.cdt.debug.edc.symbols.IModuleLineEntryProvider;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * This class holds a conglomeration of line entry data for an entire
+ * module.
+ */
+public class ModuleLineEntryProvider implements IModuleLineEntryProvider {
+
+ /**
+ * basically, a typedef of {@link ArrayList}<{@link FileLineEntryProvider}>
+ */
+ private final class FileLineEntryProviders extends ArrayList<FileLineEntryProvider> {
+ private static final long serialVersionUID = -2157263701372708990L;
+ }
+
+ /**
+ * basically, a typedef of {@link HashMap}<{@link IPath},{@link FileLineEntryProvider}>
+ */
+ private final class PathToLineEntryMap extends HashMap<IPath, FileLineEntryProviders> {
+ private static final long serialVersionUID = 7064789571684986782L;
+ }
+
+ // CUs we've already considered
+ private Set<ICompileUnitScope> parsedCUs = new HashSet<ICompileUnitScope>();
+ // mapping to find info for a given path
+ private PathToLineEntryMap pathToLineEntryMap = new PathToLineEntryMap();
+ // all known providers
+ private FileLineEntryProviders fileProviders = new FileLineEntryProviders();
+ // cached array of providers
+ private FileLineEntryProvider[] fileProviderArray;
+
+ public ModuleLineEntryProvider() {
+
+ }
+
+
+ /**
+ * Add the line entries from a compilation unit to the mapper.
+ * @param scope
+ */
+ public void addCompileUnit(ICompileUnitScope cu) {
+ if (parsedCUs.contains(cu))
+ return;
+
+ parsedCUs.add(cu);
+
+ Collection<ILineEntry> lineEntries = cu.getLineEntries();
+
+ if (lineEntries.size() > 0) {
+ // files created for this compile unit scope (union of all CUs in this.lineEntryMap)
+ // (kept because we visit the same file a lot in this function)
+ Map<IPath, FileLineEntryProvider> fileProviders = new HashMap<IPath, FileLineEntryProvider>(4);
+
+ // go through each entry and extract entries for each file.
+ //
+ // allocate one FileLineEntryProvider per CU
+ //
+ for (ILineEntry entry : lineEntries) {
+ IPath path = entry.getFilePath();
+
+ FileLineEntryProvider provider = fileProviders.get(path);
+ if (provider == null) {
+ // This will look for an existing one and create a
+ // new one if none exits.
+ provider = getFileLineProviderForCU(cu, path);
+ provider.setCULineEntries(lineEntries);
+ fileProviders.put(path, provider);
+ }
+
+ provider.addLineEntry(entry);
+ }
+ }
+
+ // then, look for lines provided by decl file/line/column entries
+ for (IScope child : cu.getChildren()) {
+ addCompileUnitChild(cu, child);
+ }
+ }
+
+
+
+ /**
+ * Add (or update) a compile unit child entry (function) by adding a
+ * line entry for its declaration location, which may differ from the
+ * first line inside the function to which the low PC refers.
+ * @param cu
+ * @param child
+ */
+ public void addCompileUnitChild(ICompileUnitScope cu, IScope child) {
+ if (child instanceof IFunctionScope) {
+ IFunctionScope func = (IFunctionScope) child;
+ IPath declFile = func.getDeclFile();
+
+ if (declFile != null) {
+ // this is the slow path for dynamic parsing
+ FileLineEntryProvider provider
+ = getFileLineProviderForCU(cu, declFile);
+
+ int declLine = func.getDeclLine();
+ int declColumn = func.getDeclColumn();
+
+ // is there already an entry at this line?
+ Collection<ILineEntry> curEntries
+ = provider.getLineEntriesForLines(declFile, declLine, declLine);
+ if (curEntries.isEmpty()) {
+ // no, add one, and make it range from our start to the first actual line
+
+ LineEntry entry
+ = new LineEntry(declFile, declLine, declColumn,
+ func.getLowAddress(), func.getLowAddress());
+ provider.addLineEntry(entry);
+ }
+ }
+ }
+ }
+
+
+ private FileLineEntryProvider getFileLineProviderForCU(
+ ICompileUnitScope cu, IPath declFile) {
+ FileLineEntryProviders providers = pathToLineEntryMap.get(declFile);
+ if (providers != null) {
+ for (FileLineEntryProvider p : providers) {
+ if (p.getCU().equals(cu)) {
+ return p;
+ }
+ }
+ }
+ FileLineEntryProvider provider = new FileLineEntryProvider(cu, declFile);
+ registerFileLineEntryProvider(declFile, provider);
+ return provider;
+ }
+
+
+
+ private void registerFileLineEntryProvider(IPath path,
+ FileLineEntryProvider provider) {
+ FileLineEntryProviders fileEntries = pathToLineEntryMap.get(path);
+ if (fileEntries == null) {
+ fileEntries = new FileLineEntryProviders();
+ pathToLineEntryMap.put(path, fileEntries);
+ }
+ fileEntries.add(provider);
+ fileProviders.add(provider);
+ fileProviderArray = null;
+ }
+
+ /**
+ * Get the line entry providers for the given source file.
+ * @path sourceFile the absolute path to the source file
+ * @return the unmodifiable list of providers for the file, possibly empty.
+ */
+ public Collection<ILineEntryProvider> getLineEntryProvidersForFile(IPath sourceFile) {
+ List<? extends ILineEntryProvider> cus = pathToLineEntryMap.get(sourceFile);
+ if (cus != null)
+ return Collections.unmodifiableCollection(cus);
+
+ for (Map.Entry<IPath, FileLineEntryProviders> entry : pathToLineEntryMap.entrySet()) {
+ if (!PathUtils.isCaseSensitive() && entry.getKey().toOSString().compareToIgnoreCase(sourceFile.toOSString()) == 0) {
+ cus = entry.getValue();
+ pathToLineEntryMap.put(sourceFile, entry.getValue());
+ return Collections.unmodifiableCollection(cus);
+ }
+ }
+
+ for (Map.Entry<IPath, FileLineEntryProviders> entry : pathToLineEntryMap.entrySet()) {
+ if (entry.getKey().equals(sourceFile)) {
+ cus = entry.getValue();
+ return Collections.unmodifiableCollection(cus);
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ILineEntryProvider#getLineEntriesForLines(org.eclipse.core.runtime.IPath, int, int)
+ */
+ public Collection<ILineEntry> getLineEntriesForLines(IPath file,
+ int startLineNumber, int endLineNumber) {
+ FileLineEntryProviders matches = pathToLineEntryMap.get(file);
+ if (matches == null)
+ return Collections.emptyList();
+
+ List<ILineEntry> ret = null;
+ for (FileLineEntryProvider provider : matches) {
+ Collection<ILineEntry> entries
+ = provider.getLineEntriesForLines(file, startLineNumber, endLineNumber);
+ if (!entries.isEmpty()) {
+ if (ret == null)
+ ret = new ArrayList<ILineEntry>(entries);
+ else
+ ret.addAll(entries);
+ }
+ }
+ if (ret == null)
+ return Collections.emptyList();
+
+ return ret;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ILineEntryProvider#getLineEntryAtAddress(org.eclipse.cdt.core.IAddress)
+ */
+ public ILineEntry getLineEntryAtAddress(IAddress linkAddress) {
+ // scanning files can introduce new file providers; avoid ConcurrentModificationException
+ if (fileProviderArray == null) {
+ fileProviderArray = fileProviders.toArray(new FileLineEntryProvider[fileProviders.size()]);
+ }
+ for (FileLineEntryProvider provider : fileProviderArray) {
+ // Narrow down the search to avoid iterating potentially hundreds
+ // of duplicates of the same file
+ // (e.g. for stl_vector.h, expanded N times for N std::vector<T> uses).
+ // (Don't use #getScopeAtAddress() since this preparses too much.)
+ if (provider.getCU().getLowAddress().compareTo(linkAddress) <= 0
+ && provider.getCU().getHighAddress().compareTo(linkAddress) > 0) {
+ ILineEntry entry = provider.getLineEntryAtAddress(linkAddress);
+ if (entry != null
+
+ /* FIXME: sigh ...
+ *
+ * yet another RVCT DWARF inlined LNT entry generation bug ...
+ *
+ * we just can't have entry.highAddr == entry.lowAddr!
+ *
+ * that just TOTALLY ruins the illusion of stepping.
+ *
+ * see, if we pass back the address we're on,
+ * no step-over range will get created,
+ * and the debugger will just run ...
+ *
+ * ... and that's just NotGood-TM .
+ */
+ && !entry.getLowAddress().equals(entry.getHighAddress())
+
+ ) {
+ return entry;
+ }
+ }
+ }
+ return null;
+ }
+
+
+ public ILineEntry getLineEntryInFunction(IAddress linkAddress, IFunctionScope parentFunction) {
+ ILineEntry functionEntry = getLineEntryAtAddress(parentFunction.getLowAddress());
+ if (functionEntry == null)
+ return null;
+ Collection<ILineEntryProvider> parentProviders
+ = getLineEntryProvidersForFile(functionEntry.getFilePath());
+ for (ILineEntryProvider iLineEntryProvider : parentProviders) {
+ if (iLineEntryProvider instanceof FileLineEntryProvider) {
+ ILineEntry entry
+ = ((FileLineEntryProvider)iLineEntryProvider).getLineEntryInFunction(linkAddress, parentFunction);
+ if (entry != null)
+ return entry;
+ }
+ }
+ return null;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ILineEntryProvider#getNextLineEntry(org.eclipse.cdt.debug.edc.internal.symbols.ILineEntry)
+ */
+ public ILineEntry getNextLineEntry(final ILineEntry entry, final boolean collapseInlineFunctions) {
+ if (entry == null)
+ return null;
+
+ final IAddress entryLowAddr = entry.getLowAddress();
+ final IAddress entryHighAddr = entry.getHighAddress();
+ final IPath entryPath = entry.getFilePath();
+ FileLineEntryProviders matches = pathToLineEntryMap.get(entryPath);
+ if (matches == null)
+ return null;
+
+ // avoid possible concurrent access if we read new files while searching
+ FileLineEntryProvider[] matchArray = matches.toArray(new FileLineEntryProvider[matches.size()]);
+
+ for (FileLineEntryProvider provider : matchArray) {
+ ICompileUnitScope cuScope = provider.getCU();
+ // Narrow down the search to avoid iterating potentially hundreds of duplicates of the same file
+ // (e.g. for stl_vector.h, expanded N times for N std::vector<T> uses).
+ // (Don't use #getScopeAtAddress() since this preparses too much.).
+ if (cuScope.getLowAddress().compareTo(entryLowAddr) <= 0
+ // NOTE: high addrs for both scope & line entries are inclusive: thus >= 0
+ && cuScope.getHighAddress().compareTo(entryHighAddr) >= 0) {
+
+ // provider.getNextLineEntry() returns null for only 1 reason:
+ // 1) there are no more entries at all for the compileUnitScope
+ //
+ // so there's no need to continue looking in other providers
+ return provider.getNextLineEntry(entry, collapseInlineFunctions);
+ }
+ }
+
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ILineEntryProvider#getPreviousLineEntry
+ */
+ public ILineEntry getPreviousLineEntry(ILineEntry entry,
+ boolean collapseInlineFunctions) {
+ if (entry == null)
+ return null;
+
+ FileLineEntryProviders matches = pathToLineEntryMap.get(entry.getFilePath());
+ if (matches != null) {
+ final IAddress entryLowAddr = entry.getLowAddress();
+ final IAddress entryHighAddr = entry.getHighAddress();
+ boolean entryIsInline = false, inlineEstablished = false;
+
+ // avoid possible concurrent access if we read new files while searching
+ FileLineEntryProvider[] matchArray = matches.toArray(new FileLineEntryProvider[matches.size()]);
+
+ for (FileLineEntryProvider provider : matchArray) {
+ ICompileUnitScope cuScope = provider.getCU();
+ // Narrow down the search to avoid iterating potentially hundreds of duplicates of the same file
+ // (e.g. for stl_vector.h, expanded N times for N std::vector<T> uses).
+ // (Don't use #getScopeAtAddress() since this preparses too much.).
+ //
+ //
+ if (cuScope.getLowAddress().compareTo(entryLowAddr) <= 0
+ // NOTE: high addrs for both scope & line entries are inclusive: thus >= 0
+ && cuScope.getHighAddress().compareTo(entryHighAddr) >= 0) {
+ if (!inlineEstablished) {
+ entryIsInline = FileLineEntryProvider.isInlinedFunction(cuScope.getFunctionAtAddress(entryLowAddr));
+ inlineEstablished = true;
+ }
+ ILineEntry prev = provider.getPreviousLineEntry(entry, collapseInlineFunctions);
+ if (prev == null && collapseInlineFunctions && entryIsInline) {
+ // retry with the provider mapped from the compileUnitScope.filePath
+ return provider.getPreviousLineEntryInCU(entry);
+ }
+
+ if (prev != null) { // in case there's another provider
+ return prev;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider#findClosestLineWithCode(org.eclipse.core.runtime.IPath, int, int)
+ */
+ public List<ILineAddresses> findClosestLineWithCode(IPath sourceFile,
+ int anchorLine, int neighbor_limit) {
+ List<ILineAddresses> ret = new ArrayList<ILineAddresses>(1);
+
+ /* Check all compile units in the module for code line.
+ */
+ Collection<? extends ILineEntryProvider> fileProviders =
+ getLineEntryProvidersForFile(sourceFile);
+ if (fileProviders.isEmpty())
+ return ret;
+
+ for (ILineEntryProvider fileProvider : fileProviders) {
+
+ // Find code line in one CU using the source file
+ List<ILineAddresses> la = fileProvider.findClosestLineWithCode(sourceFile, anchorLine, neighbor_limit);
+ if (la.isEmpty())
+ continue;
+ assert (la.size() == 1);
+ ILineAddresses newCodeLine = la.get(0);
+
+ if (ret.isEmpty())
+ ret.add(newCodeLine);
+ else {
+ boolean merged = false;
+ for (ILineAddresses e : ret)
+ if (newCodeLine.getLineNumber() == e.getLineNumber()) {
+ ((EDCLineAddresses)e).addAddress(newCodeLine.getAddress());
+ merged = true;
+ break;
+ }
+
+ if (!merged)
+ ret.add(newCodeLine);
+ }
+ }
+
+ return ret;
+ }
+
+
+ public boolean hasSourceFile(IPath sourceFile) {
+ Collection<? extends ILineEntryProvider> fileProviders =
+ getLineEntryProvidersForFile(sourceFile);
+ return fileProviders != null && ! fileProviders.isEmpty();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class PointerType extends MayBeQualifiedType implements IPointerType {
+
+ public PointerType(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ super(name, scope, byteSize, properties);
+ }
+
+ // create an internal pointer for expression evaluation
+ public PointerType() {
+ super("", null, 0, null); //$NON-NLS-1$
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class ReferenceType extends Type implements IReferenceType {
+
+ public ReferenceType(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ super(name, scope, byteSize, properties);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.debug.edc.symbols.IRegisterOffsetVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public class RegisterOffsetVariableLocation extends RegisterVariableLocation implements IRegisterOffsetVariableLocation {
+
+ protected final long offset;
+ private int addressSize;
+
+ public RegisterOffsetVariableLocation(EDCServicesTracker tracker, IDMContext context, String name, int id, long offset) {
+ super(tracker, context, name, id);
+ this.offset = offset;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.RegisterVariableLocation#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + " + " + getOffset(); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IRegisterOffsetVariableLocation#getOffset()
+ */
+ public long getOffset() {
+ return offset;
+ }
+
+ public BigInteger readValue(int bytes) throws CoreException {
+ BigInteger regval = super.readValue(bytes);
+ return regval.add(BigInteger.valueOf(offset));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.RegisterVariableLocation#addOffset(long)
+ */
+ @Override
+ public IVariableLocation addOffset(long offset) {
+ return new RegisterOffsetVariableLocation(tracker, context, name, id, offset + this.offset);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.RegisterVariableLocation#getLocationName(org.eclipse.cdt.dsf.service.DsfServicesTracker)
+ */
+ @Override
+ public String getLocationName() {
+ try {
+ if (addressSize == 0) {
+ addressSize = 4;
+ ITargetEnvironment targetEnvironment = tracker.getService(ITargetEnvironment.class);
+ if (targetEnvironment != null)
+ addressSize = targetEnvironment.getPointerSize();
+ }
+ BigInteger regval = super.readValue(addressSize);
+ regval = regval.add(BigInteger.valueOf(offset));
+ return SymbolsMessages.RegisterOffsetVariableLocation_Hex + Long.toHexString(regval.longValue());
+ } catch (CoreException e) {
+ // fallback
+ return super.getLocationName() + (offset < 0 ? SymbolsMessages.RegisterOffsetVariableLocation_Positive : SymbolsMessages.RegisterOffsetVariableLocation_Negative ) + Math.abs(offset);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.RegisterVariableLocation#getAddress()
+ */
+ @Override
+ public IAddress getAddress() {
+ return null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.services.Registers;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.symbols.IRegisterVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public class RegisterVariableLocation implements IRegisterVariableLocation {
+
+ protected String name;
+ protected int id;
+ protected final IDMContext context;
+ protected final EDCServicesTracker tracker;
+
+ public RegisterVariableLocation(EDCServicesTracker tracker, IDMContext context, String name, int id) {
+ this.tracker = tracker;
+ this.context = context;
+ this.name = name;
+ this.id = id;
+ if (name == null) {
+ Registers registerservice = tracker.getService(Registers.class);
+ this.name = registerservice.getRegisterNameFromCommonID(getRegisterID());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getRegisterName() != null ? getRegisterName() : "R" + id + //$NON-NLS-1$
+ (context instanceof StackFrameDMC && ((StackFrameDMC) context).getLevel() > 0 ?
+ " (level " + ((StackFrameDMC) context).getLevel() + ")" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ public String getRegisterName() {
+ return name;
+ }
+
+ public int getRegisterID() {
+ return id;
+ }
+
+ public BigInteger readValue(int bytes) throws CoreException {
+ if (context instanceof StackFrameDMC)
+ return ((StackFrameDMC)context).getFrameRegisters().getRegister(id, bytes);
+ else
+ throw EDCDebugger.newCoreException(SymbolsMessages.RegisterVariableLocation_CannotReadFramelessRegister);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#addOffset(long)
+ */
+ public IVariableLocation addOffset(long offset) {
+ return new RegisterOffsetVariableLocation(tracker, context, name, id, offset);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#getLocationName(org.eclipse.cdt.dsf.service.DsfServicesTracker)
+ */
+ public String getLocationName() {
+ return "$" + getRegisterName(); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#getAddress()
+ */
+ public IAddress getAddress() {
+ return null;
+ }
+
+ public IDMContext getContext() {
+ return context;
+ }
+
+ public EDCServicesTracker getServicesTracker()
+ {
+ return tracker;
+ }
+ public void writeValue(int bytes, BigInteger value) throws CoreException {
+ if (context instanceof StackFrameDMC)
+ ((StackFrameDMC)context).getFrameRegisters().writeRegister(id, bytes, value);
+ else
+ throw EDCDebugger.newCoreException(SymbolsMessages.RegisterVariableLocation_CannotWriteFramelessRegister);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.text.MessageFormat;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+
+public class RuntimeSection implements IRuntimeSection {
+
+ private ISection section;
+ // the relocated address of the section at runtime
+ private IAddress runtimeAddress;
+
+ public RuntimeSection(ISection section) {
+ this.section = section;
+ // the section may not get relocted, so set the runtime
+ // address to be the link address
+ runtimeAddress = section.getLinkAddress();
+ }
+
+ public int getId() {
+ return section.getId();
+ }
+
+ public long getSize() {
+ return section.getSize();
+ }
+
+ public IAddress getLinkAddress() {
+ return section.getLinkAddress();
+ }
+
+ public Map<String, Object> getProperties() {
+ return section.getProperties();
+ }
+
+ public IAddress getRuntimeAddress() {
+ return runtimeAddress;
+ }
+
+ public void relocate(IAddress runtimeAddress) {
+ this.runtimeAddress = runtimeAddress;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormat.format("[sectionID={0}, link address={1}, runtime address={2}]", getId(), getLinkAddress().toHexAddressString(), //$NON-NLS-1$
+ runtimeAddress.toHexAddressString());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RuntimeSection other = (RuntimeSection) obj;
+ if (!getLinkAddress().equals(other.getLinkAddress()))
+ return false;
+ if (!getRuntimeAddress().equals(other.getRuntimeAddress()))
+ return false;
+ if (getId() != other.getId())
+ return false;
+ return true;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.RangeList;
+import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.IRangeList;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.cdt.debug.edc.symbols.IRangeList.Entry;
+import org.eclipse.cdt.utils.Addr32;
+import org.eclipse.cdt.utils.Addr64;
+
+public abstract class Scope implements IScope {
+
+ protected String name;
+ protected IAddress lowAddress;
+ protected IAddress highAddress;
+ protected IScope parent;
+ protected List<IScope> children = new ArrayList<IScope>();
+ protected List<IVariable> variables = new ArrayList<IVariable>();
+ protected List<IEnumerator> enumerators = new ArrayList<IEnumerator>();
+ private TreeMap<IRangeList.Entry, IScope> addressToScopeMap;
+
+ protected IRangeList rangeList;
+
+ public Scope(String name, IAddress lowAddress, IAddress highAddress, IScope parent) {
+ this.name = name;
+ this.lowAddress = lowAddress;
+ this.highAddress = highAddress;
+ this.parent = parent;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public IAddress getLowAddress() {
+ return lowAddress;
+ }
+
+ public IAddress getHighAddress() {
+ return highAddress;
+ }
+
+ /**
+ * Tell whether the address range for the scope is empty.
+ * @return flag
+ */
+ public boolean hasEmptyRange() {
+ return (lowAddress == null || highAddress == null)
+ || (lowAddress.isZero() && highAddress.isZero())
+ || (lowAddress.getValue().longValue() == -1 && highAddress.isZero()); // TODO: remove this case
+ }
+
+ /**
+ * Return the list of non-contiguous ranges for this scope.
+ * @return list or <code>null</code>
+ */
+ public IRangeList getRangeList() {
+ return rangeList;
+ }
+
+ public void setLowAddress(IAddress lowAddress) {
+ this.lowAddress = lowAddress;
+ }
+
+ public void setHighAddress(IAddress highAddress) {
+ this.highAddress = highAddress;
+ }
+
+ public void setRangeList(IRangeList ranges) {
+ this.rangeList = ranges;
+ setLowAddress(new Addr32(rangeList.getLowAddress()));
+ setHighAddress(new Addr32(rangeList.getHighAddress()));
+ }
+
+ public IScope getParent() {
+ return parent;
+ }
+
+ public Collection<IScope> getChildren() {
+ return Collections.unmodifiableCollection(children);
+ }
+
+ public Collection<IVariable> getVariables() {
+ return Collections.unmodifiableCollection(variables);
+ }
+
+ public Collection<IEnumerator> getEnumerators() {
+ return Collections.unmodifiableCollection(enumerators);
+ }
+
+ public IScope getScopeAtAddress(IAddress linkAddress) {
+ // see if it's in this scope
+ if (linkAddress.compareTo(lowAddress) >= 0 && linkAddress.compareTo(highAddress) < 0) {
+
+ ensureScopeRangeLookup();
+
+ long addr = linkAddress.getValue().longValue();
+ IRangeList.Entry addressEntry = new IRangeList.Entry(addr, addr);
+ SortedMap<Entry,IScope> tailMap = addressToScopeMap.tailMap(addressEntry);
+
+ if (tailMap.isEmpty())
+ return this;
+
+ IScope child = tailMap.values().iterator().next();
+ if (linkAddress.compareTo(child.getLowAddress()) >= 0
+ && linkAddress.compareTo(child.getHighAddress()) < 0) {
+ return child.getScopeAtAddress(linkAddress);
+ }
+
+ return this;
+ }
+
+ return null;
+ }
+
+ /**
+ * Make sure our mapping of address range to scope is valid.
+ */
+ private void ensureScopeRangeLookup() {
+ if (addressToScopeMap == null) {
+ addressToScopeMap = new TreeMap<Entry, IScope>();
+
+ for (IScope scope : children) {
+ addScopeRange(scope);
+ }
+ //System.out.println("Mapping for " + getName()+ ": "+ addressToScopeMap.size() + " entries");
+ }
+ }
+
+ /**
+ * @param scope
+ */
+ private void addScopeRange(IScope scope) {
+ IRangeList ranges = scope.getRangeList();
+ if (ranges != null) {
+ for (IRangeList.Entry entry : ranges) {
+ addressToScopeMap.put(entry, scope);
+ }
+ } else {
+ addressToScopeMap.put(new IRangeList.Entry(
+ scope.getLowAddress().getValue().longValue(),
+ scope.getHighAddress().getValue().longValue()),
+ scope);
+ }
+ }
+
+ /**
+ * Adds the given scope as a child of this scope
+ *
+ * @param scope
+ */
+ public void addChild(IScope scope) {
+ children.add(scope);
+ if (addressToScopeMap != null) {
+ addScopeRange(scope);
+ }
+ }
+
+ /**
+ * Adds the given variable to this scope
+ *
+ * @param variable
+ */
+ public void addVariable(IVariable variable) {
+ variables.add(variable);
+ }
+
+ /**
+ * Adds the given variable to this scope
+ *
+ * @param variable
+ */
+ public void addEnumerator(IEnumerator enumerator) {
+ enumerators.add(enumerator);
+ }
+
+ public int compareTo(Object o) {
+ if (o instanceof IScope) {
+ return lowAddress.compareTo(((IScope) o).getLowAddress());
+ } else if (o instanceof IAddress) {
+ return lowAddress.compareTo(o);
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Scope ["); //$NON-NLS-1$
+ if (rangeList != null) {
+ builder.append("ranges="); //$NON-NLS-1$
+ builder.append(rangeList);
+ } else {
+ builder.append("lowAddress="); //$NON-NLS-1$
+ builder.append(lowAddress != null ? lowAddress.toHexAddressString() : "null");
+ builder.append(", highAddress="); //$NON-NLS-1$
+ builder.append(highAddress != null ? highAddress.toHexAddressString() : "null");
+ }
+ builder.append(", "); //$NON-NLS-1$
+ if (name != null) {
+ builder.append("name="); //$NON-NLS-1$
+ builder.append(name);
+ builder.append(", "); //$NON-NLS-1$
+ }
+ builder.append("]"); //$NON-NLS-1$
+ return builder.toString();
+ }
+
+ public void fixupRanges(IAddress baseAddress) {
+ // compile unit scopes not generated by the compiler so
+ // figure it out from the functions
+ IAddress newLowAddress = Addr64.MAX;
+ IAddress newHighAddress = Addr64.ZERO;
+ boolean any = false;
+
+ for (IScope kid : getChildren()) {
+ // the compiler may generate (bad) low/high pc's which are not
+ // in the actual module space for some functions. to work
+ // around this, only honor addresses that are above the
+ // actual link address
+ if (kid.hasEmptyRange()) {
+ continue;
+ }
+
+ if (kid.getLowAddress().compareTo(baseAddress) > 0) {
+ if (kid.getLowAddress().compareTo(newLowAddress) < 0) {
+ newLowAddress = kid.getLowAddress();
+ any = true;
+ }
+
+ if (kid.getHighAddress().compareTo(newHighAddress) > 0) {
+ newHighAddress = kid.getHighAddress();
+ any = true;
+ }
+ }
+ }
+
+ if (any) {
+ //System.out.println("Needed to fix up ranges for " + getName());
+ lowAddress = newLowAddress;
+ highAddress = newHighAddress;
+ rangeList = null;
+ } else {
+ if (lowAddress == null) {
+ lowAddress = highAddress = Addr32.ZERO;
+ }
+ }
+ }
+
+ /**
+ * Merge the code range(s) from the given scope into this one.
+ * @param scope
+ */
+ protected void mergeScopeRange(IScope scope) {
+ if (hasEmptyRange()) {
+ // copy range
+ if (scope.getRangeList() != null) {
+ setRangeList(scope.getRangeList());
+ } else {
+ setLowAddress(scope.getLowAddress());
+ setHighAddress(scope.getHighAddress());
+ }
+ } else {
+ if (scope.getLowAddress() != null && scope.getLowAddress().compareTo(lowAddress) < 0
+ && !scope.getLowAddress().isZero()) // ignore random 0 entries
+ {
+ if (rangeList != null) {
+ if (scope.getRangeList() != null) {
+ // TODO: merge properly
+ rangeList = null;
+ } else {
+ ((RangeList)rangeList).addLowRange(scope.getLowAddress().getValue().longValue());
+ }
+ }
+ lowAddress = scope.getLowAddress();
+ }
+ if (scope.getHighAddress() != null && scope.getHighAddress().compareTo(highAddress) > 0) {
+ if (rangeList != null) {
+ if (scope.getRangeList() != null) {
+ // TODO: merge properly
+ rangeList = null;
+ } else {
+ ((RangeList)rangeList).addHighRange(scope.getHighAddress().getValue().longValue());
+ }
+ }
+ highAddress = scope.getHighAddress();
+ }
+ }
+ }
+
+ protected void addLineInfoToParent(IScope scope) {
+ IScope cu = parent;
+ while (cu != null) {
+ if (cu instanceof ICompileUnitScope && cu.getParent() instanceof IModuleScope) {
+ IModuleScope module = (IModuleScope) cu.getParent();
+ ModuleLineEntryProvider provider = (ModuleLineEntryProvider) module.getModuleLineEntryProvider();
+ provider.addCompileUnitChild((ICompileUnitScope) cu, scope);
+ break;
+ }
+ cu = cu.getParent();
+ }
+ }
+
+ /**
+ *
+ */
+ public void dispose() {
+ for (IScope scope : children)
+ scope.dispose();
+ children.clear();
+ for (IVariable var : variables)
+ var.dispose();
+ variables.clear();
+ enumerators.clear();
+ if (addressToScopeMap != null)
+ addressToScopeMap.clear();
+ rangeList = null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+
+public class Section implements ISection {
+
+ private final int id;
+
+ private final long size;
+
+ // the address generated by the linker
+ private final IAddress linkAddress;
+
+ private final Map<String, Object> properties;
+
+ public Section(int id, long size, IAddress linkAddress, Map<String, Object> properties) {
+ this.id = id;
+ this.size = size;
+ this.linkAddress = linkAddress;
+ this.properties = new HashMap<String, Object>(properties); // make a
+ // copy
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public IAddress getLinkAddress() {
+ return linkAddress;
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormat.format("[sectionID={0}, link address={1}]", id, linkAddress.toHexAddressString()); //$NON-NLS-1$);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Section other = (Section) obj;
+ if (linkAddress != other.linkAddress)
+ return false;
+ if (id != other.id)
+ return false;
+ return true;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class StructType extends CompositeType {
+
+ public StructType(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ super(name, scope, ICompositeType.k_struct, byteSize, properties, "struct"); //$NON-NLS-1$
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class SubroutineType extends Type implements ISubroutineType {
+
+ public SubroutineType(IScope scope, Map<Object, Object> properties) {
+ super("", scope, 0, properties); //$NON-NLS-1$
+ }
+
+ // create an internal pointer for expression evaluation
+ public SubroutineType() {
+ super("", null, 0, null); //$NON-NLS-1$
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.symbols.ISymbol;
+
+public class Symbol implements ISymbol {
+
+ protected String name;
+ protected IAddress address;
+ protected long size;
+
+ public Symbol(String name, IAddress address, long size) {
+ this.name = name;
+ this.address = address;
+ this.size = size;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public IAddress getAddress() {
+ return address;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public int compareTo(Object o) {
+ if (o instanceof Symbol) {
+ return address.compareTo(((Symbol) o).address);
+ } else if (o instanceof IAddress) {
+ return address.compareTo(o);
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "name=" + name + //$NON-NLS-1$
+ ", address=0x" + Long.toHexString(address.getValue().longValue()) + //$NON-NLS-1$
+ ", size=" + size; //$NON-NLS-1$
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.osgi.util.NLS;
+
+public class SymbolsMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.edc.internal.symbols.SymbolsMessages"; //$NON-NLS-1$
+
+ public static String InvalidVariableLocation_CannotWriteInvalidLocation;
+
+ public static String MemoryVariableLocation_CannotFindFrame;
+ public static String MemoryVariableLocation_CannotReadAddrFormat;
+ public static String MemoryVariableLocation_CannotWriteAddrFormat;
+ public static String MemoryVariableLocation_Hex;
+ public static String MemoryVariableLocation_LinkTime;
+
+ public static String RegisterOffsetVariableLocation_Hex;
+ public static String RegisterOffsetVariableLocation_Negative;
+ public static String RegisterOffsetVariableLocation_Positive;
+ public static String RegisterVariableLocation_CannotReadFramelessRegister;
+ public static String RegisterVariableLocation_CannotWriteFramelessRegister;
+
+ public static String ValueVariableLocation_CannotModifyDerivedValue;
+ public static String ValueVariableLocation_NoValueAvailable;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, SymbolsMessages.class);
+ }
+
+ private SymbolsMessages() {
+ }
+}
--- /dev/null
+###############################################################################
+# Copyright (c) 2010 Nokia 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:
+# Nokia - Initial API and implementation
+###############################################################################
+InvalidVariableLocation_CannotWriteInvalidLocation=Can't write to an invalid variable location
+MemoryVariableLocation_CannotFindFrame=Cannot find frame
+MemoryVariableLocation_CannotReadAddrFormat=cannot read address {0}
+MemoryVariableLocation_CannotWriteAddrFormat=cannot write address {0}
+MemoryVariableLocation_Hex=0x
+MemoryVariableLocation_LinkTime=\ (link time)
+RegisterOffsetVariableLocation_Hex=0x
+RegisterOffsetVariableLocation_Negative=\ -
+RegisterOffsetVariableLocation_Positive=\ +
+RegisterVariableLocation_CannotReadFramelessRegister=cannot read register without frame
+RegisterVariableLocation_CannotWriteFramelessRegister=cannot write register without frame
+ValueVariableLocation_CannotModifyDerivedValue=cannot modify derived value
+ValueVariableLocation_NoValueAvailable=no value available
--- /dev/null
+/*******************************************************************************
+
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.TypeUtils;
+
+public class TemplateParamType implements ITemplateParam {
+
+ final String name;
+ IType type;
+
+ public TemplateParamType(String name, IType type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ public String getName() {
+ if (name == null || name.length() == 0) {
+ getType();
+ return TypeUtils.getFullTypeName(type);
+ } else {
+ return this.name;
+ }
+ }
+
+ public IType getType() {
+ if (this.type instanceof IForwardTypeReference) {
+ this.type = ((IForwardTypeReference) this.type).getReferencedType();
+ }
+
+ IType nextType = this.type;
+ while (nextType != null) {
+ if (nextType instanceof IForwardTypeReference)
+ nextType = ((IForwardTypeReference) nextType).getReferencedType();
+ nextType = nextType.getType();
+ }
+
+ return this.type;
+ }
+
+ public IScope getScope() {
+ return null;
+ }
+
+ public int getByteSize() {
+ return 0;
+ }
+
+ public Map<Object, Object> getProperties() {
+ return null;
+ }
+
+ public void setType(IType type) {
+ }
+
+ public void dispose() {
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+
+
+public class Type implements IType {
+
+ /** This property key maps to an {@link IForwardTypeReference} object */
+ public static final String TYPE_REFERENCE = "type_reference"; //$NON-NLS-1$
+
+ protected String name;
+ protected IScope scope;
+ protected int byteSize;
+ protected IType type; // subtype, if any, maybe IForwardTypeReference
+ protected Map<Object, Object> properties; // may be null
+
+ public Type(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ this.name = name;
+ this.scope = scope;
+ this.byteSize = byteSize;
+ this.properties = properties;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#dispose()
+ */
+ public void dispose() {
+ properties = null;
+ scope = null;
+ type = null;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public IScope getScope() {
+ return scope;
+ }
+
+ public int getByteSize() {
+ return byteSize;
+ }
+
+ public Map<Object, Object> getProperties() {
+ return properties;
+ }
+
+ public IType getType() {
+ if (type == null && properties != null) {
+ type = (IType) properties.get(TYPE_REFERENCE);
+ }
+ if (type instanceof IForwardTypeReference) {
+ type = ((IForwardTypeReference) type).getReferencedType();
+ }
+ return type;
+ }
+
+ public void setType(IType type) {
+ this.type = type;
+ }
+
+ public void setScope(IScope scope) {
+ this.scope = scope;
+ }
+
+ protected int updateByteSizeFromSubType() {
+ if (byteSize == 0) {
+ IType theType = getType();
+ if (theType != null)
+ byteSize = theType.getByteSize();
+ }
+ return byteSize;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class TypedefType extends MayBeQualifiedType implements ITypedef {
+
+ public TypedefType(String name, IScope scope, Map<Object, Object> properties) {
+ super(name, scope, 0, properties);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Type#getByteSize()
+ */
+ @Override
+ public int getByteSize() {
+ return updateByteSizeFromSubType();
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+public class UnionType extends CompositeType {
+
+ public UnionType(String name, IScope scope, int byteSize, Map<Object, Object> properties) {
+ super(name, scope, ICompositeType.k_union, byteSize, properties, "union"); //$NON-NLS-1$
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.symbols.IValueVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This is an actual value, calculated from somewhere, which does not have a location.
+ */
+public class ValueVariableLocation implements IValueVariableLocation {
+
+ private BigInteger value;
+
+ public ValueVariableLocation(BigInteger value) {
+ this.value = value;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "0x" + Long.toHexString(value.longValue()); //$NON-NLS-1$
+ }
+
+
+ public BigInteger readValue(int bytes) throws CoreException {
+ if (value == null)
+ throw EDCDebugger.newCoreException(SymbolsMessages.ValueVariableLocation_NoValueAvailable);
+ return value;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#addOffset(long)
+ */
+ public IVariableLocation addOffset(long offset) {
+ return new ValueVariableLocation(value.add(BigInteger.valueOf(offset)));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#getLocationName(org.eclipse.cdt.dsf.service.DsfServicesTracker)
+ */
+ public String getLocationName() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariableLocation#getAddress()
+ */
+ public IAddress getAddress() {
+ return null;
+ }
+
+ public void writeValue(int bytes, BigInteger value) throws CoreException {
+ throw EDCDebugger.newCoreException(SymbolsMessages.ValueVariableLocation_CannotModifyDerivedValue);
+ }
+
+ public IDMContext getContext() {
+ return null;
+ }
+
+ public EDCServicesTracker getServicesTracker() {
+ return null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.core.runtime.IPath;
+
+
+public class Variable implements IVariable {
+
+ protected String name;
+ protected IScope scope;
+ protected IType type;
+ protected ILocationProvider locationProvider;
+ protected long startScope;
+ protected boolean isDeclared;
+ protected IPath definingFile;
+
+
+ public Variable(String name, IScope scope, IType type, ILocationProvider locationProvider, boolean isDeclared, IPath definingFile) {
+ this.name = name;
+ this.scope = scope;
+ this.type = type;
+ this.locationProvider = locationProvider;
+ this.isDeclared = isDeclared;
+ this.definingFile = definingFile;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IVariable#dispose()
+ */
+ public void dispose() {
+ type = null;
+ locationProvider = null;
+ scope = null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IVariable#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IVariable#getScope()
+ */
+ public IScope getScope() {
+ return scope;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IVariable#getType()
+ */
+ public IType getType() {
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+
+ return type;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IVariable#getLocationProvider()
+ */
+ public ILocationProvider getLocationProvider() {
+ return locationProvider;
+ }
+
+ public void setStartScope(long start) {
+ this.startScope = start;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IVariable#getStartScope()
+ */
+ public long getStartScope() {
+ return startScope;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IVariable#isDeclared()
+ */
+ public boolean isDeclared() {
+ return isDeclared;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IVariable#getDefiningFile()
+ */
+ public IPath getDefiningFile() {
+ return definingFile;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Var ["); //$NON-NLS-1$
+ if (name != null) {
+ builder.append(name);
+ }
+ if (scope != null) {
+ builder.append(", "); //$NON-NLS-1$
+ builder.append("scope="); //$NON-NLS-1$
+ builder.append(scope.getName());
+ }
+ builder.append("]"); //$NON-NLS-1$
+ return builder.toString();
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols;
+
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+
+/**
+ * Pseudo-type that represents the volatile qualifier
+ */
+public class VolatileType extends Type implements IQualifierType {
+
+ public VolatileType(IScope scope, Map<Object, Object> properties) {
+ super("volatile", scope, 0, properties); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Type#getByteSize()
+ */
+ @Override
+ public int getByteSize() {
+ return updateByteSizeFromSubType();
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.CompileUnitScope;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AttributeList;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.CompilationUnitHeader;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.core.runtime.IPath;
+
+public class DwarfCompileUnit extends CompileUnitScope {
+
+ protected DwarfDebugInfoProvider provider;
+ protected AttributeList attributes;
+ private List<IPath> fileList;
+ private boolean rangesDirty;
+
+ // computation unit header
+ protected final CompilationUnitHeader header;
+
+ // whether the computation unit has been parsed to find variables and children with address ranges
+ protected boolean parsedForVarsAndAddresses = false;
+
+ // whether the computation unit has been parsed to find types
+ protected boolean parsedForTypes = false;
+
+
+ public DwarfCompileUnit(DwarfDebugInfoProvider provider, IModuleScope parent, IPath filePath,
+ IAddress lowAddress, IAddress highAddress, CompilationUnitHeader header, boolean hasChildren,
+ AttributeList attributes) {
+ super(filePath, parent, lowAddress, highAddress);
+
+ this.provider = provider;
+ this.attributes = attributes;
+ this.header = header;
+
+ // if there are no children, say the children have been parsed
+ if (!hasChildren) {
+ this.parsedForVarsAndAddresses = true;
+ this.parsedForTypes = true;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + header.debugInfoOffset;
+ result = prime * result + provider.getSymbolFile().hashCode();
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ DwarfCompileUnit other = (DwarfCompileUnit) obj;
+ if (header.debugInfoOffset != other.header.debugInfoOffset)
+ return false;
+ if (!provider.getSymbolFile().equals(other.provider.getSymbolFile()))
+ return false;
+ return true;
+ }
+
+ public AttributeList getAttributeList() {
+ return attributes;
+ }
+
+ @Override
+ protected Collection<ILineEntry> parseLineTable() {
+ DwarfInfoReader reader = new DwarfInfoReader(provider);
+ fileList = new ArrayList<IPath>();
+ return reader.parseLineTable(this, attributes, fileList);
+ }
+
+ public void setLowAddress(IAddress address) {
+ this.lowAddress = address;
+ }
+
+ public void setHighAddress(IAddress address) {
+ this.highAddress = address;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getLowAddress()
+ */
+ @Override
+ public IAddress getLowAddress() {
+ // the address is known in the compile unit tag;
+ // if anything inside is outside that range, it's a bug.
+ return super.getLowAddress();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getHighAddress()
+ */
+ @Override
+ public IAddress getHighAddress() {
+ // the address is known in the compile unit tag;
+ // if anything inside is outside that range, it's a bug.
+ return super.getHighAddress();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.CompileUnitScope#getFunctionAtAddress(org.eclipse.cdt.core.IAddress)
+ */
+ @Override
+ public IFunctionScope getFunctionAtAddress(IAddress linkAddress) {
+ if (rangesDirty) {
+ fixupRanges();
+ }
+ ensureParsedForAddresses();
+ return super.getFunctionAtAddress(linkAddress);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.CompileUnitScope#getFunctions()
+ */
+ @Override
+ public Collection<IFunctionScope> getFunctions() {
+ ensureParsedForAddresses();
+ return super.getFunctions();
+ }
+
+ /**
+ * For compilers that don't generate compile unit scopes, e.g. GCCE with
+ * dlls, this fixes up the low and high addresses of the compile unit based
+ * on the function scopes
+ */
+ protected void fixupRanges() {
+
+ // fix up scope addresses in case compiler doesn't generate them.
+ if (hasEmptyRange() && parsedForVarsAndAddresses) {
+ fixupRanges(provider.getBaseLinkAddress());
+ }
+
+ rangesDirty = false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getChildren()
+ */
+ @Override
+ public Collection<IScope> getChildren() {
+ return super.getChildren();
+ }
+
+ public void setAttributes(AttributeList attributes) {
+ this.attributes = attributes;
+ }
+
+ public boolean isParsedForAddresses() {
+ return parsedForVarsAndAddresses;
+ }
+
+ public boolean isParsedForVariables() {
+ return parsedForVarsAndAddresses;
+ }
+
+ public boolean isParsedForTypes() {
+ return parsedForTypes;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getVariables()
+ */
+ @Override
+ public Collection<IVariable> getVariables() {
+ ensureParsedForVariables();
+ return super.getVariables();
+ }
+
+ public void setParsedForAddresses(boolean parsedForAddresses) {
+ this.parsedForVarsAndAddresses = parsedForAddresses;
+ }
+
+ public void setParsedForVariables(boolean parsedForVariables) {
+ this.parsedForVarsAndAddresses = parsedForVariables;
+ }
+
+ public void setParsedForTypes(boolean parsedForTypes) {
+ this.parsedForTypes = parsedForTypes;
+ }
+
+ private void ensureParsedForAddresses() {
+ if (!parsedForVarsAndAddresses) {
+ DwarfInfoReader reader = new DwarfInfoReader(provider);
+ reader.parseCompilationUnitForAddresses(this);
+ }
+ }
+
+ /**
+ * Get the file path for a file number
+ * @param declFileNum
+ * @return IPath for the file, or <code>null</code>
+ */
+ public IPath getFileEntry(int declFileNum) {
+ if (fileList == null)
+ parseLineTable();
+ if (declFileNum <= 0 || declFileNum > fileList.size())
+ return null;
+ return fileList.get(declFileNum - 1);
+ }
+
+ private void ensureParsedForVariables() {
+ if (!parsedForVarsAndAddresses) {
+ DwarfInfoReader reader = new DwarfInfoReader(provider);
+ reader.parseCompilationUnitForAddresses(this);
+ }
+ }
+
+ @Override
+ public IScope getScopeAtAddress(IAddress linkAddress) {
+ ensureParsedForAddresses();
+ return super.getScopeAtAddress(linkAddress);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("DwarfCompileUnit [");
+
+ builder.append("SymFile=");
+ builder.append(provider.getSymbolFile().lastSegment());
+
+ builder.append(", SectionOffset=0x");
+ builder.append(Integer.toHexString(header.debugInfoOffset));
+
+ builder.append(", lowAddr=");
+ builder.append(lowAddress != null ? lowAddress.toHexAddressString() : null);
+
+ builder.append(", highAddr=");
+ builder.append(highAddress != null ? highAddress.toHexAddressString() : null);
+ if (filePath != null) {
+ builder.append(", path=");
+ builder.append(filePath.toOSString());
+ }
+ builder.append(", parsedForVarsAndAddresses=");
+ builder.append(parsedForVarsAndAddresses);
+ builder.append(", parsedForTypes=");
+ builder.append(parsedForTypes);
+ builder.append("]\n");
+ return builder.toString();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#addChild(org.eclipse.cdt.debug.edc.internal.symbols.IScope)
+ */
+ @Override
+ public void addChild(IScope scope) {
+ super.addChild(scope);
+
+ // if we don't know our scope yet...
+ if (hasEmptyRange()) {
+ rangesDirty = true;
+ } else {
+ // the CU may have an incomplete idea of its scope; fit the new scope in
+ mergeScopeRange(scope);
+ }
+
+ addLineInfoToParent(scope);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+/**
+ * DWARF constant.
+ */
+public class DwarfConstants {
+
+ /* Tags. */
+
+ public final static int DW_TAG_array_type = 0x01;
+ public final static int DW_TAG_class_type = 0x02;
+ public final static int DW_TAG_entry_point = 0x03;
+ public final static int DW_TAG_enumeration_type = 0x04;
+ public final static int DW_TAG_formal_parameter = 0x05;
+ public final static int DW_TAG_imported_declaration = 0x08;
+ public final static int DW_TAG_label = 0x0a;
+ public final static int DW_TAG_lexical_block = 0x0b;
+ public final static int DW_TAG_member = 0x0d;
+ public final static int DW_TAG_pointer_type = 0x0f;
+ public final static int DW_TAG_reference_type = 0x10;
+ public final static int DW_TAG_compile_unit = 0x11;
+ public final static int DW_TAG_string_type = 0x12;
+ public final static int DW_TAG_structure_type = 0x13;
+ public final static int DW_TAG_subroutine_type = 0x15;
+ public final static int DW_TAG_typedef = 0x16;
+ public final static int DW_TAG_union_type = 0x17;
+ public final static int DW_TAG_unspecified_parameters = 0x18;
+ public final static int DW_TAG_variant = 0x19;
+ public final static int DW_TAG_common_block = 0x1a;
+ public final static int DW_TAG_common_inclusion = 0x1b;
+ public final static int DW_TAG_inheritance = 0x1c;
+ public final static int DW_TAG_inlined_subroutine = 0x1d;
+ public final static int DW_TAG_module = 0x1e;
+ public final static int DW_TAG_ptr_to_member_type = 0x1f;
+ public final static int DW_TAG_set_type = 0x20;
+ public final static int DW_TAG_subrange_type = 0x21;
+ public final static int DW_TAG_with_stmt = 0x22;
+ public final static int DW_TAG_access_declaration = 0x23;
+ public final static int DW_TAG_base_type = 0x24;
+ public final static int DW_TAG_catch_block = 0x25;
+ public final static int DW_TAG_const_type = 0x26;
+ public final static int DW_TAG_constant = 0x27;
+ public final static int DW_TAG_enumerator = 0x28;
+ public final static int DW_TAG_file_type = 0x29;
+ public final static int DW_TAG_friend = 0x2a;
+ public final static int DW_TAG_namelist = 0x2b;
+ public final static int DW_TAG_namelist_item = 0x2c;
+ public final static int DW_TAG_packed_type = 0x2d;
+ public final static int DW_TAG_subprogram = 0x2e;
+ public final static int DW_TAG_template_type_param = 0x2f; // called DW_TAG_template_type_parameter in DWARF 3
+ public final static int DW_TAG_template_value_param = 0x30; // called DW_TAG_template_value_parameter in DWARF 3
+ public final static int DW_TAG_thrown_type = 0x31;
+ public final static int DW_TAG_try_block = 0x32;
+ public final static int DW_TAG_variant_part = 0x33;
+ public final static int DW_TAG_variable = 0x34;
+ public final static int DW_TAG_volatile_type = 0x35;
+ public final static int DW_TAG_dwarf_procedure = 0x36; // DWARF 3
+ public final static int DW_TAG_restrict_type = 0x37; // DWARF 3
+ public final static int DW_TAG_interface_type = 0x38; // DWARF 3
+ public final static int DW_TAG_namespace = 0x39; // DWARF 3
+ public final static int DW_TAG_imported_module = 0x3a; // DWARF 3
+ public final static int DW_TAG_unspecified_type = 0x3b; // DWARF 3
+ public final static int DW_TAG_partial_unit = 0x3c; // DWARF 3
+ public final static int DW_TAG_imported_unit = 0x3d; // DWARF 3
+ public final static int DW_TAG_condition = 0x3f; // DWARF 3
+ public final static int DW_TAG_shared_type = 0x40; // DWARF 3
+ public final static int DW_TAG_lo_user = 0x4080;
+ public final static int DW_TAG_MIPS_loop = 0x4081;
+ public final static int DW_TAG_format_label = 0x4101;
+ public final static int DW_TAG_function_template = 0x4102;
+ public final static int DW_TAG_class_template = 0x4103;
+ public final static int DW_TAG_hi_user = 0xffff;
+
+ /* Children determination encodings. */
+ public final static int DW_CHILDREN_no = 0;
+ public final static int DW_CHILDREN_yes = 1;
+
+ /* DWARF attributes encodings. */
+ public final static short DW_AT_sibling = 0x01;
+ public final static short DW_AT_location = 0x02;
+ public final static short DW_AT_name = 0x03;
+ public final static short DW_AT_ordering = 0x09;
+ public final static short DW_AT_subscr_data = 0x0a;
+ public final static short DW_AT_byte_size = 0x0b;
+ public final static short DW_AT_bit_offset = 0x0c;
+ public final static short DW_AT_bit_size = 0x0d;
+ public final static short DW_AT_element_list = 0x0f;
+ public final static short DW_AT_stmt_list = 0x10;
+ public final static short DW_AT_low_pc = 0x11;
+ public final static short DW_AT_high_pc = 0x12;
+ public final static short DW_AT_language = 0x13;
+ public final static short DW_AT_member = 0x14;
+ public final static short DW_AT_discr = 0x15;
+ public final static short DW_AT_discr_value = 0x16;
+ public final static short DW_AT_visibility = 0x17;
+ public final static short DW_AT_import = 0x18;
+ public final static short DW_AT_string_length = 0x19;
+ public final static short DW_AT_common_reference = 0x1a;
+ public final static short DW_AT_comp_dir = 0x1b;
+ public final static short DW_AT_const_value = 0x1c;
+ public final static short DW_AT_containing_type = 0x1d;
+ public final static short DW_AT_default_value = 0x1e;
+ public final static short DW_AT_inline = 0x20;
+ public final static short DW_AT_is_optional = 0x21;
+ public final static short DW_AT_lower_bound = 0x22;
+ public final static short DW_AT_producer = 0x25;
+ public final static short DW_AT_prototyped = 0x27;
+ public final static short DW_AT_return_addr = 0x2a;
+ public final static short DW_AT_start_scope = 0x2c;
+ public final static short DW_AT_stride_size = 0x2e;
+ public final static short DW_AT_upper_bound = 0x2f;
+ public final static short DW_AT_abstract_origin = 0x31;
+ public final static short DW_AT_accessibility = 0x32;
+ public final static short DW_AT_address_class = 0x33;
+ public final static short DW_AT_artificial = 0x34;
+ public final static short DW_AT_base_types = 0x35;
+ public final static short DW_AT_calling_convention = 0x36;
+ public final static short DW_AT_count = 0x37;
+ public final static short DW_AT_data_member_location = 0x38;
+ public final static short DW_AT_decl_column = 0x39;
+ public final static short DW_AT_decl_file = 0x3a;
+ public final static short DW_AT_decl_line = 0x3b;
+ public final static short DW_AT_declaration = 0x3c;
+ public final static short DW_AT_discr_list = 0x3d;
+ public final static short DW_AT_encoding = 0x3e;
+ public final static short DW_AT_external = 0x3f;
+ public final static short DW_AT_frame_base = 0x40;
+ public final static short DW_AT_friend = 0x41;
+ public final static short DW_AT_identifier_case = 0x42;
+ public final static short DW_AT_macro_info = 0x43;
+ public final static short DW_AT_namelist_items = 0x44;
+ public final static short DW_AT_priority = 0x45;
+ public final static short DW_AT_segment = 0x46;
+ public final static short DW_AT_specification = 0x47;
+ public final static short DW_AT_static_link = 0x48;
+ public final static short DW_AT_type = 0x49;
+ public final static short DW_AT_use_location = 0x4a;
+ public final static short DW_AT_variable_parameter = 0x4b;
+ public final static short DW_AT_virtuality = 0x4c;
+ public final static short DW_AT_vtable_elem_location = 0x4d;
+ public final static short DW_AT_allocated = 0x4e; // DWARF 3
+ public final static short DW_AT_associated = 0x4f; // DWARF 3
+ public final static short DW_AT_data_location = 0x50; // DWARF 3
+ public final static short DW_AT_byte_stride = 0x51; // DWARF 3
+ public final static short DW_AT_entry_pc = 0x52; // DWARF 3
+ public final static short DW_AT_use_UTF8 = 0x53; // DWARF 3
+ public final static short DW_AT_extension = 0x54; // DWARF 3
+ public final static short DW_AT_ranges = 0x55; // DWARF 3
+ public final static short DW_AT_trampoline = 0x56; // DWARF 3
+ public final static short DW_AT_call_column = 0x57; // DWARF 3
+ public final static short DW_AT_call_file = 0x58; // DWARF 3
+ public final static short DW_AT_call_line = 0x59; // DWARF 3
+ public final static short DW_AT_description = 0x5a; // DWARF 3
+ public final static short DW_AT_binary_scale = 0x5b; // DWARF 3
+ public final static short DW_AT_decimal_scale = 0x5c; // DWARF 3
+ public final static short DW_AT_small = 0x5d; // DWARF 3
+ public final static short DW_AT_decimal_sign = 0x5e; // DWARF 3
+ public final static short DW_AT_digit_count = 0x5f; // DWARF 3
+ public final static short DW_AT_picture_string = 0x60; // DWARF 3
+ public final static short DW_AT_mutable = 0x61; // DWARF 3
+ public final static short DW_AT_threads_scaled = 0x62; // DWARF 3
+ public final static short DW_AT_explicit = 0x63; // DWARF 3
+ public final static short DW_AT_object_pointer = 0x64; // DWARF 3
+ public final static short DW_AT_endianity = 0x65; // DWARF 3
+ public final static short DW_AT_elemental = 0x66; // DWARF 3
+ public final static short DW_AT_pure = 0x67; // DWARF 3
+ public final static short DW_AT_recursive = 0x68; // DWARF 3
+ public final static short DW_AT_lo_user = 0x2000;
+ public final static short DW_AT_MIPS_fde = 0x2001;
+ public final static short DW_AT_MIPS_loop_begin = 0x2002;
+ public final static short DW_AT_MIPS_tail_loop_begin = 0x2003;
+ public final static short DW_AT_MIPS_epilog_begin = 0x2004;
+ public final static short DW_AT_MIPS_loop_unroll_factor = 0x2005;
+ public final static short DW_AT_MIPS_software_pipeline_depth = 0x2006;
+ public final static short DW_AT_MIPS_linkage_name = 0x2007;
+ public final static short DW_AT_MIPS_stride = 0x2008;
+ public final static short DW_AT_MIPS_abstract_name = 0x2009;
+ public final static short DW_AT_MIPS_clone_origin = 0x200a;
+ public final static short DW_AT_MIPS_has_inlines = 0x200b;
+ public final static short DW_AT_MIPS_stride_byte = 0x200c;
+ public final static short DW_AT_MIPS_stride_elem = 0x200d;
+ public final static short DW_AT_MIPS_ptr_dopetype = 0x200e;
+ public final static short DW_AT_MIPS_allocatable_dopetype = 0x200f;
+ public final static short DW_AT_MIPS_assumed_shape_dopetype = 0x2010;
+ public final static short DW_AT_MIPS_assumed_size = 0x2011;
+ public final static short DW_AT_sf_names = 0x2101;
+ public final static short DW_AT_src_info = 0x2102;
+ public final static short DW_AT_mac_info = 0x2103;
+ public final static short DW_AT_src_coords = 0x2104;
+ public final static short DW_AT_body_begin = 0x2105;
+ public final static short DW_AT_body_end = 0x2106;
+ public final static short DW_AT_hi_user = 0x3fff;
+
+ /* DWARF form encodings. */
+ public final static int DW_FORM_addr = 0x01;
+ public final static int DW_FORM_block2 = 0x03;
+ public final static int DW_FORM_block4 = 0x04;
+ public final static int DW_FORM_data2 = 0x05;
+ public final static int DW_FORM_data4 = 0x06;
+ public final static int DW_FORM_data8 = 0x07;
+ public final static int DW_FORM_string = 0x08;
+ public final static int DW_FORM_block = 0x09;
+ public final static int DW_FORM_block1 = 0x0a;
+ public final static int DW_FORM_data1 = 0x0b;
+ public final static int DW_FORM_flag = 0x0c;
+ public final static int DW_FORM_sdata = 0x0d;
+ public final static int DW_FORM_strp = 0x0e;
+ public final static int DW_FORM_udata = 0x0f;
+ public final static int DW_FORM_ref_addr = 0x10;
+ public final static int DW_FORM_ref1 = 0x11;
+ public final static int DW_FORM_ref2 = 0x12;
+ public final static int DW_FORM_ref4 = 0x13;
+ public final static int DW_FORM_ref8 = 0x14;
+ public final static int DW_FORM_ref_udata = 0x15;
+ public final static int DW_FORM_indirect = 0x16;
+
+ /* Since DWARF4 */
+ public final static int DW_FORM_sec_offset = 0x17;
+ public final static int DW_FORM_exprloc = 0x18;
+ public final static int DW_FORM_flag_present = 0x19;
+ public final static int DW_FORM_ref_sig8 = 0x20;
+ /* Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. */
+ public final static int DW_FORM_GNU_addr_index = 0x1f01;
+ public final static int DW_FORM_GNU_str_index = 0x1f02;
+ /* Extensions for DWZ multifile.
+ See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open . */
+ public final static int DW_FORM_GNU_ref_alt = 0x1f20;
+ public final static int DW_FORM_GNU_strp_alt = 0x1f21;
+
+
+ /* DWARF location operation encodings. */
+ public final static int DW_OP_addr = 0x03; /* Constant address. */
+ public final static int DW_OP_deref = 0x06;
+ public final static int DW_OP_const1u = 0x08; /* Unsigned 1-byte constant. */
+ public final static int DW_OP_const1s = 0x09; /* Signed 1-byte constant. */
+ public final static int DW_OP_const2u = 0x0a; /* Unsigned 2-byte constant. */
+ public final static int DW_OP_const2s = 0x0b; /* Signed 2-byte constant. */
+ public final static int DW_OP_const4u = 0x0c; /* Unsigned 4-byte constant. */
+ public final static int DW_OP_const4s = 0x0d; /* Signed 4-byte constant. */
+ public final static int DW_OP_const8u = 0x0e; /* Unsigned 8-byte constant. */
+ public final static int DW_OP_const8s = 0x0f; /* Signed 8-byte constant. */
+ public final static int DW_OP_constu = 0x10; /* Unsigned LEB128 constant. */
+ public final static int DW_OP_consts = 0x11; /* Signed LEB128 constant. */
+ public final static int DW_OP_dup = 0x12;
+ public final static int DW_OP_drop = 0x13;
+ public final static int DW_OP_over = 0x14;
+ public final static int DW_OP_pick = 0x15; /* 1-byte stack index. */
+ public final static int DW_OP_swap = 0x16;
+ public final static int DW_OP_rot = 0x17;
+ public final static int DW_OP_xderef = 0x18;
+ public final static int DW_OP_abs = 0x19;
+ public final static int DW_OP_and = 0x1a;
+ public final static int DW_OP_div = 0x1b;
+ public final static int DW_OP_minus = 0x1c;
+ public final static int DW_OP_mod = 0x1d;
+ public final static int DW_OP_mul = 0x1e;
+ public final static int DW_OP_neg = 0x1f;
+ public final static int DW_OP_not = 0x20;
+ public final static int DW_OP_or = 0x21;
+ public final static int DW_OP_plus = 0x22;
+ public final static int DW_OP_plus_uconst = 0x23; /* Unsigned LEB128 addend. */
+ public final static int DW_OP_shl = 0x24;
+ public final static int DW_OP_shr = 0x25;
+ public final static int DW_OP_shra = 0x26;
+ public final static int DW_OP_xor = 0x27;
+ public final static int DW_OP_bra = 0x28; /* Signed 2-byte constant. */
+ public final static int DW_OP_eq = 0x29;
+ public final static int DW_OP_ge = 0x2a;
+ public final static int DW_OP_gt = 0x2b;
+ public final static int DW_OP_le = 0x2c;
+ public final static int DW_OP_lt = 0x2d;
+ public final static int DW_OP_ne = 0x2e;
+ public final static int DW_OP_skip = 0x2f; /* Signed 2-byte constant. */
+ public final static int DW_OP_lit0 = 0x30; /* Literal 0. */
+ public final static int DW_OP_lit1 = 0x31; /* Literal 1. */
+ public final static int DW_OP_lit2 = 0x32; /* Literal 2. */
+ public final static int DW_OP_lit3 = 0x33; /* Literal 3. */
+ public final static int DW_OP_lit4 = 0x34; /* Literal 4. */
+ public final static int DW_OP_lit5 = 0x35; /* Literal 5. */
+ public final static int DW_OP_lit6 = 0x36; /* Literal 6. */
+ public final static int DW_OP_lit7 = 0x37; /* Literal 7. */
+ public final static int DW_OP_lit8 = 0x38; /* Literal 8. */
+ public final static int DW_OP_lit9 = 0x39; /* Literal 9. */
+ public final static int DW_OP_lit10 = 0x3a; /* Literal 10. */
+ public final static int DW_OP_lit11 = 0x3b; /* Literal 11. */
+ public final static int DW_OP_lit12 = 0x3c; /* Literal 12. */
+ public final static int DW_OP_lit13 = 0x3d; /* Literal 13. */
+ public final static int DW_OP_lit14 = 0x3e; /* Literal 14. */
+ public final static int DW_OP_lit15 = 0x3f; /* Literal 15. */
+ public final static int DW_OP_lit16 = 0x40; /* Literal 16. */
+ public final static int DW_OP_lit17 = 0x41; /* Literal 17. */
+ public final static int DW_OP_lit18 = 0x42; /* Literal 18. */
+ public final static int DW_OP_lit19 = 0x43; /* Literal 19. */
+ public final static int DW_OP_lit20 = 0x44; /* Literal 20. */
+ public final static int DW_OP_lit21 = 0x45; /* Literal 21. */
+ public final static int DW_OP_lit22 = 0x46; /* Literal 22. */
+ public final static int DW_OP_lit23 = 0x47; /* Literal 23. */
+ public final static int DW_OP_lit24 = 0x48; /* Literal 24. */
+ public final static int DW_OP_lit25 = 0x49; /* Literal 25. */
+ public final static int DW_OP_lit26 = 0x4a; /* Literal 26. */
+ public final static int DW_OP_lit27 = 0x4b; /* Literal 27. */
+ public final static int DW_OP_lit28 = 0x4c; /* Literal 28. */
+ public final static int DW_OP_lit29 = 0x4d; /* Literal 29. */
+ public final static int DW_OP_lit30 = 0x4e; /* Literal 30. */
+ public final static int DW_OP_lit31 = 0x4f; /* Literal 31. */
+ public final static int DW_OP_reg0 = 0x50; /* Register 0. */
+ public final static int DW_OP_reg1 = 0x51; /* Register 1. */
+ public final static int DW_OP_reg2 = 0x52; /* Register 2. */
+ public final static int DW_OP_reg3 = 0x53; /* Register 3. */
+ public final static int DW_OP_reg4 = 0x54; /* Register 4. */
+ public final static int DW_OP_reg5 = 0x55; /* Register 5. */
+ public final static int DW_OP_reg6 = 0x56; /* Register 6. */
+ public final static int DW_OP_reg7 = 0x57; /* Register 7. */
+ public final static int DW_OP_reg8 = 0x58; /* Register 8. */
+ public final static int DW_OP_reg9 = 0x59; /* Register 9. */
+ public final static int DW_OP_reg10 = 0x5a; /* Register 10. */
+ public final static int DW_OP_reg11 = 0x5b; /* Register 11. */
+ public final static int DW_OP_reg12 = 0x5c; /* Register 12. */
+ public final static int DW_OP_reg13 = 0x5d; /* Register 13. */
+ public final static int DW_OP_reg14 = 0x5e; /* Register 14. */
+ public final static int DW_OP_reg15 = 0x5f; /* Register 15. */
+ public final static int DW_OP_reg16 = 0x60; /* Register 16. */
+ public final static int DW_OP_reg17 = 0x61; /* Register 17. */
+ public final static int DW_OP_reg18 = 0x62; /* Register 18. */
+ public final static int DW_OP_reg19 = 0x63; /* Register 19. */
+ public final static int DW_OP_reg20 = 0x64; /* Register 20. */
+ public final static int DW_OP_reg21 = 0x65; /* Register 21. */
+ public final static int DW_OP_reg22 = 0x66; /* Register 22. */
+ public final static int DW_OP_reg23 = 0x67; /* Register 24. */
+ public final static int DW_OP_reg24 = 0x68; /* Register 24. */
+ public final static int DW_OP_reg25 = 0x69; /* Register 25. */
+ public final static int DW_OP_reg26 = 0x6a; /* Register 26. */
+ public final static int DW_OP_reg27 = 0x6b; /* Register 27. */
+ public final static int DW_OP_reg28 = 0x6c; /* Register 28. */
+ public final static int DW_OP_reg29 = 0x6d; /* Register 29. */
+ public final static int DW_OP_reg30 = 0x6e; /* Register 30. */
+ public final static int DW_OP_reg31 = 0x6f; /* Register 31. */
+ public final static int DW_OP_breg0 = 0x70; /* Base register 0. */
+ public final static int DW_OP_breg1 = 0x71; /* Base register 1. */
+ public final static int DW_OP_breg2 = 0x72; /* Base register 2. */
+ public final static int DW_OP_breg3 = 0x73; /* Base register 3. */
+ public final static int DW_OP_breg4 = 0x74; /* Base register 4. */
+ public final static int DW_OP_breg5 = 0x75; /* Base register 5. */
+ public final static int DW_OP_breg6 = 0x76; /* Base register 6. */
+ public final static int DW_OP_breg7 = 0x77; /* Base register 7. */
+ public final static int DW_OP_breg8 = 0x78; /* Base register 8. */
+ public final static int DW_OP_breg9 = 0x79; /* Base register 9. */
+ public final static int DW_OP_breg10 = 0x7a; /* Base register 10. */
+ public final static int DW_OP_breg11 = 0x7b; /* Base register 11. */
+ public final static int DW_OP_breg12 = 0x7c; /* Base register 12. */
+ public final static int DW_OP_breg13 = 0x7d; /* Base register 13. */
+ public final static int DW_OP_breg14 = 0x7e; /* Base register 14. */
+ public final static int DW_OP_breg15 = 0x7f; /* Base register 15. */
+ public final static int DW_OP_breg16 = 0x80; /* Base register 16. */
+ public final static int DW_OP_breg17 = 0x81; /* Base register 17. */
+ public final static int DW_OP_breg18 = 0x82; /* Base register 18. */
+ public final static int DW_OP_breg19 = 0x83; /* Base register 19. */
+ public final static int DW_OP_breg20 = 0x84; /* Base register 20. */
+ public final static int DW_OP_breg21 = 0x85; /* Base register 21. */
+ public final static int DW_OP_breg22 = 0x86; /* Base register 22. */
+ public final static int DW_OP_breg23 = 0x87; /* Base register 23. */
+ public final static int DW_OP_breg24 = 0x88; /* Base register 24. */
+ public final static int DW_OP_breg25 = 0x89; /* Base register 25. */
+ public final static int DW_OP_breg26 = 0x8a; /* Base register 26. */
+ public final static int DW_OP_breg27 = 0x8b; /* Base register 27. */
+ public final static int DW_OP_breg28 = 0x8c; /* Base register 28. */
+ public final static int DW_OP_breg29 = 0x8d; /* Base register 29. */
+ public final static int DW_OP_breg30 = 0x8e; /* Base register 30. */
+ public final static int DW_OP_breg31 = 0x8f; /* Base register 31. */
+ public final static int DW_OP_regx = 0x90; /* Unsigned LEB128 register. */
+ public final static int DW_OP_fbreg = 0x91; /* Signed LEB128 register. */
+ public final static int DW_OP_bregx = 0x92; /*
+ * ULEB128 register followed by
+ * SLEB128 off.
+ */
+ public final static int DW_OP_piece = 0x93; /*
+ * ULEB128 size of piece
+ * addressed.
+ */
+ public final static int DW_OP_deref_size = 0x94; /*
+ * 1-byte size of data
+ * retrieved.
+ */
+ public final static int DW_OP_xderef_size = 0x95; /*
+ * 1-byte size of data
+ * retrieved.
+ */
+ public final static int DW_OP_nop = 0x96;
+ public final static int DW_OP_push_object_address = 0x97; // DWARF 3
+ public final static int DW_OP_call2 = 0x98; // 2-byte offset of DIE // DWARF
+ // 3
+ public final static int DW_OP_call4 = 0x99; // 4-byte offset of DIE // DWARF
+ // 3
+ public final static int DW_OP_call_ref = 0x9a; // 4- or 8-byte offset of DIE
+ // // DWARF 3
+ public final static int DW_OP_form_tls_address = 0x9b; // DWARF 3
+ public final static int DW_OP_call_frame_cfa = 0x9c; // DWARF 3
+ public final static int DW_OP_bit_piece = 0x9d; // DWARF 3
+
+ public final static int DW_OP_lo_user = 0xe0; /*
+ * Implementation-defined range
+ * start.
+ */
+ public final static int DW_OP_hi_user = 0xff; /*
+ * Implementation-defined range
+ * end.
+ */
+
+ /* DWARF base type encodings. */
+ public final static int DW_ATE_void = 0x0;
+ public final static int DW_ATE_address = 0x1;
+ public final static int DW_ATE_boolean = 0x2;
+ public final static int DW_ATE_complex_float = 0x3;
+ public final static int DW_ATE_float = 0x4;
+ public final static int DW_ATE_signed = 0x5;
+ public final static int DW_ATE_signed_char = 0x6;
+ public final static int DW_ATE_unsigned = 0x7;
+ public final static int DW_ATE_unsigned_char = 0x8;
+ public final static int DW_ATE_imaginary_float = 0x09; // DWARF 3
+ public final static int DW_ATE_packed_decimal = 0x0a; // DWARF 3
+ public final static int DW_ATE_numeric_string = 0x0b; // DWARF 3
+ public final static int DW_ATE_edited = 0x0c; // DWARF 3
+ public final static int DW_ATE_signed_fixed = 0x0d; // DWARF 3
+ public final static int DW_ATE_unsigned_fixed = 0x0e; // DWARF 3
+ public final static int DW_ATE_decimal_float = 0x0f; // DWARF 3
+
+ public final static int DW_ATE_lo_user = 0x80;
+ public final static int DW_ATE_hi_user = 0xff;
+
+ /* DWARF decimal sign encodings. */
+ public final static int DW_DS_unsigned = 0x01; // DWARF 3
+ public final static int DW_DS_leading_overpunch = 0x02; // DWARF 3
+ public final static int DW_DS_trailing_overpunch = 0x03;// DWARF 3
+ public final static int DW_DS_leading_separate = 0x04; // DWARF 3
+ public final static int DW_DS_trailing_separate = 0x05; // DWARF 3
+
+ /* DWARF endianity encodings. */
+ public final static int DW_END_default = 0x00; // DWARF 3
+ public final static int DW_END_big = 0x01; // DWARF 3
+ public final static int DW_END_little = 0x02; // DWARF 3
+ public final static int DW_END_lo_user = 0x40; // DWARF 3
+ public final static int DW_END_hi_user = 0xff; // DWARF 3
+
+ /* DWARF accessibility encodings. */
+ public final static int DW_ACCESS_public = 1;
+ public final static int DW_ACCESS_protected = 2;
+ public final static int DW_ACCESS_private = 3;
+
+ /* DWARF visibility encodings. */
+ public final static int DW_VIS_local = 1;
+ public final static int DW_VIS_exported = 2;
+ public final static int DW_VIS_qualified = 3;
+
+ /* DWARF virtuality encodings. */
+ public final static int DW_VIRTUALITY_none = 0;
+ public final static int DW_VIRTUALITY_virtual = 1;
+ public final static int DW_VIRTUALITY_pure_virtual = 2;
+
+ /* DWARF language encodings. */
+ public final static int DW_LANG_C89 = 0x0001;
+ public final static int DW_LANG_C = 0x0002;
+ public final static int DW_LANG_Ada83 = 0x0003;
+ public final static int DW_LANG_C_plus_plus = 0x0004;
+ public final static int DW_LANG_Cobol74 = 0x0005;
+ public final static int DW_LANG_Cobol85 = 0x0006;
+ public final static int DW_LANG_Fortran77 = 0x0007;
+ public final static int DW_LANG_Fortran90 = 0x0008;
+ public final static int DW_LANG_Pascal83 = 0x0009;
+ public final static int DW_LANG_Modula2 = 0x000a;
+ public final static int DW_LANG_Java = 0x000b; // DWARF 3
+ public final static int DW_LANG_C99 = 0x000c; // DWARF 3
+ public final static int DW_LANG_Ada95 = 0x000d; // DWARF 3
+ public final static int DW_LANG_Fortran95 = 0x000e; // DWARF 3
+ public final static int DW_LANG_PLI = 0x000f; // DWARF 3
+ public final static int DW_LANG_PL1 = 0x000f; // misspelling found in
+ // org.eclipse.cdt.utils.debug.dwarf.DwarfConstants
+ public final static int DW_LANG_ObjC = 0x0010; // DWARF 3
+ public final static int DW_LANG_ObjC_plus_plus = 0x0011;// DWARF 3
+ public final static int DW_LANG_UPC = 0x0012; // DWARF 3
+ public final static int DW_LANG_D = 0x0013; // DWARF 3
+ public final static int DW_LANG_lo_user = 0x8000;
+ public final static int DW_LANG_Mips_Assembler = 0x8001;
+ public final static int DW_LANG_hi_user = 0xffff;
+
+ /* DWARF identifier case encodings. */
+ public final static int DW_ID_case_sensitive = 0;
+ public final static int DW_ID_up_case = 1;
+ public final static int DW_ID_down_case = 2;
+ public final static int DW_ID_case_insensitive = 3;
+
+ /* DWARF calling conventions encodings. */
+ public final static int DW_CC_normal = 0x1;
+ public final static int DW_CC_program = 0x2;
+ public final static int DW_CC_nocall = 0x3;
+ public final static int DW_CC_lo_user = 0x40;
+ public final static int DW_CC_hi_user = 0xff;
+
+ /* DWARF inline encodings. */
+ public final static int DW_INL_not_inlined = 0;
+ public final static int DW_INL_inlined = 1;
+ public final static int DW_INL_declared_not_inlined = 2;
+ public final static int DW_INL_declared_inlined = 3;
+
+ /* DWARF ordering encodings. */
+ public final static int DW_ORD_row_major = 0;
+ public final static int DW_ORD_col_major = 1;
+
+ /* DWARF discriminant descriptor encodings. */
+ public final static int DW_DSC_label = 0;
+ public final static int DW_DSC_range = 1;
+
+ /* DWARF standard opcode encodings. */
+ public final static int DW_LNS_copy = 1;
+ public final static int DW_LNS_advance_pc = 2;
+ public final static int DW_LNS_advance_line = 3;
+ public final static int DW_LNS_set_file = 4;
+ public final static int DW_LNS_set_column = 5;
+ public final static int DW_LNS_negate_stmt = 6;
+ public final static int DW_LNS_set_basic_block = 7;
+ public final static int DW_LNS_const_add_pc = 8;
+ public final static int DW_LNS_fixed_advance_pc = 9;
+ public final static int DW_LNS_set_prologue_end = 10; // DWARF 3
+ public final static int DW_LNS_set_epilog_begin = 11; // misspelling found
+ // in
+ // org.eclipse.cdt.utils.debug.dwarf.DwarfConstants
+ public final static int DW_LNS_set_epilogue_begin = 11; // DWARF 3
+ public final static int DW_LNS_set_isa = 12; // DWARF 3
+
+ public final static int LINE_IsStmt = 0x01;
+ public final static int LINE_BasicBlock = 0x02;
+ public final static int LINE_PrologueEnd = 0x04;
+ public final static int LINE_EpilogueBegin = 0x08;
+ public final static int LINE_EndSequence = 0x10;
+
+ /* DWARF extended opcide encodings. */
+ public final static int DW_LNE_end_sequence = 1;
+ public final static int DW_LNE_set_address = 2;
+ public final static int DW_LNE_define_file = 3;
+ public final static int DW_LNE_lo_user = 0x80; // DWARF 3
+ public final static int DW_LNE_hi_user = 0xff; // DWARF 3
+
+ /* DWARF macinfo type encodings. */
+ public final static int DW_MACINFO_define = 1;
+ public final static int DW_MACINFO_undef = 2;
+ public final static int DW_MACINFO_start_file = 3;
+ public final static int DW_MACINFO_end_file = 4;
+ public final static int DW_MACINFO_vendor_ext = 255;
+
+ /* DWARF macro type encodings. */
+ /* since DWARF4 */
+ public final static int DW_MACRO_end = 0;
+ public final static int DW_MACRO_define = 1;
+ public final static int DW_MACRO_undef = 2;
+ public final static int DW_MACRO_start_file = 3;
+ public final static int DW_MACRO_end_file = 4;
+ public final static int DW_MACRO_define_indirect = 5;
+ public final static int DW_MACRO_undef_indirect = 6;
+ public final static int DW_MACRO_transparent_include = 7;
+ public final static int DW_MACRO_define_indirect_alt = 0x08;
+ public final static int DW_MACRO_undef_indirect_alt = 0x09;
+ public final static int DW_MACRO_transparent_include_alt = 0x0a;
+ public final static int DW_MACRO_lo_user = 0xe0;
+ public final static int DW_MACRO_hi_user = 0xff;
+
+ /* DWARF call frame instruction encodings. */
+ public final static int DW_CFA_advance_loc = 0x40; // low 6 bits delta
+ public final static int DW_CFA_offset = 0x80; // low 6 bits register,
+ // operand ULEB128 offset
+ public final static int DW_CFA_restore = 0xc0; // low 6 bits register
+ public final static int DW_CFA_extended = 0; // same as DW_CFA_nop
+
+ public final static int DW_CFA_nop = 0x00; // same as DW_CFA_extended
+ public final static int DW_CFA_set_loc = 0x01; // operand: address
+ public final static int DW_CFA_advance_loc1 = 0x02; // operand: 1-byte delta
+ public final static int DW_CFA_advance_loc2 = 0x03; // operand: 2-byte delta
+ public final static int DW_CFA_advance_loc4 = 0x04; // operand: 4-byte delta
+ public final static int DW_CFA_offset_extended = 0x05; // operands: ULEB128
+ // register, ULEB128
+ // offset
+ public final static int DW_CFA_restore_extended = 0x06; // operand: ULEB128
+ // register
+ public final static int DW_CFA_undefined = 0x07; // operand: ULEB128
+ // register
+ public final static int DW_CFA_same_value = 0x08; // operand: ULEB128
+ // register
+ public final static int DW_CFA_register = 0x09; // operands: ULEB128
+ // register, ULEB128
+ // register
+ public final static int DW_CFA_remember_state = 0x0a;
+ public final static int DW_CFA_restore_state = 0x0b;
+ public final static int DW_CFA_def_cfa = 0x0c; // operands: ULEB128
+ // register, ULEB128 offset
+ public final static int DW_CFA_def_cfa_register = 0x0d; // operand: ULEB128
+ // register
+ public final static int DW_CFA_def_cfa_offset = 0x0e; // operand: ULEB128
+ // offset
+ public final static int DW_CFA_def_cfa_expression = 0x0f; // operand: BLOCK
+ // // DWARF 3
+ public final static int DW_CFA_expression = 0x10; // operands: ULEB128
+ // register, BLOCK //
+ // DWARF 3
+ public final static int DW_CFA_offset_extended_sf = 0x11; // operands:
+ // ULEB128
+ // register,
+ // SLEB128
+ // offset //
+ // DWARF 3
+ public final static int DW_CFA_def_cfa_sf = 0x12; // operands: ULEB128
+ // register, SLEB128
+ // offset // DWARF 3
+ public final static int DW_CFA_def_cfa_offset_sf = 0x13;// operand: SLEB128
+ // offset // DWARF 3
+ public final static int DW_CFA_val_offset = 0x14; // operands: ULEB128,
+ // ULEB128// DWARF 3
+ public final static int DW_CFA_val_offset_sf = 0x15; // operands: ULEB128,
+ // SLEB128// DWARF 3
+ public final static int DW_CFA_val_expression = 0x16; // operands: ULEB128,
+ // BLOCK// DWARF 3
+ public final static int DW_CFA_low_user = 0x1c;
+ public final static int DW_CFA_MIPS_advance_loc8 = 0x1d;
+ public final static int DW_CFA_GNU_window_save = 0x2d;
+ public final static int DW_CFA_GNU_args_size = 0x2e;
+ public final static int DW_CFA_high_user = 0x3f;
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.symbols.IForwardTypeReference;
+import org.eclipse.cdt.debug.edc.internal.symbols.Scope;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.CommonInformationEntry;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.FrameDescriptionEntry;
+import org.eclipse.cdt.debug.edc.services.IFrameRegisterProvider;
+import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
+import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.IRangeList;
+import org.eclipse.cdt.debug.edc.symbols.IRangeList.Entry;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * This class handles the low-level aspects of reading DWARF data.
+ * There exists one provider per symbol file.
+ */
+public class DwarfDebugInfoProvider implements IDebugInfoProvider {
+
+ /**
+ * This represents a forward type reference, which is a type
+ * that resolves itself when referenced.
+ */
+ static public class ForwardTypeReference implements IType, IForwardTypeReference {
+
+ static public final IType NULL_TYPE_ENTRY = new IType() {
+
+ public int getByteSize() {
+ return 0;
+ }
+
+ public String getName() {
+ return DwarfMessages.DwarfDebugInfoProvider_UnhandledType;
+ }
+
+ public Map<Object, Object> getProperties() {
+ return Collections.emptyMap();
+ }
+
+ public IScope getScope() {
+ return null;
+ }
+
+ public IType getType() {
+ return null;
+ }
+
+ public void setType(IType type) {
+ throw new IllegalStateException();
+ }
+
+ public void dispose() {
+ }
+ };
+
+ private DwarfDebugInfoProvider provider;
+ private IType type = null;
+
+ private final long offset;
+
+ public ForwardTypeReference(DwarfDebugInfoProvider provider, long offset) {
+ this.provider = provider;
+ this.offset = offset;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.dwarf.IForwardTypeReference#getReferencedType()
+ */
+ public IType getReferencedType() {
+ if (type == null) {
+ // to prevent recursion
+ IType newType = NULL_TYPE_ENTRY;
+ newType = provider.resolveTypeReference(this);
+ if (newType == null) {
+ // FIXME
+ newType = NULL_TYPE_ENTRY;
+ }
+ type = newType;
+ }
+ return type;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getByteSize()
+ */
+ public int getByteSize() {
+ return getReferencedType().getByteSize();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getName()
+ */
+ public String getName() {
+ return getReferencedType().getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getProperties()
+ */
+ public Map<Object, Object> getProperties() {
+ return getReferencedType().getProperties();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getScope()
+ */
+ public IScope getScope() {
+ return getReferencedType().getScope();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#getType()
+ */
+ public IType getType() {
+ return getReferencedType().getType();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#setType(org.eclipse.cdt.debug.edc.internal.symbols.IType)
+ */
+ public void setType(IType type_) {
+ getReferencedType().setType(type_);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IType#dispose()
+ */
+ public void dispose() {
+ type = null;
+ provider = null;
+ }
+ }
+
+ static public class CompilationUnitHeader {
+ long length;
+ short version;
+ int abbreviationOffset;
+ byte addressSize;
+ int debugInfoOffset;
+ byte offsetSize;
+ DwarfCompileUnit scope;
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Offset: " + debugInfoOffset).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append("Length: " + length).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append("Version: " + version).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append("Abbreviation: " + abbreviationOffset).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append("Address size: " + addressSize).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append("Offset size: " + offsetSize).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ return sb.toString();
+ }
+ }
+
+ static class AbbreviationEntry {
+ short tag;
+ ArrayList<Attribute> attributes;
+ boolean hasChildren;
+
+ AbbreviationEntry(long code, short tag, boolean hasChildren) {
+ // abbreviation code not stored
+ this.tag = tag;
+ this.hasChildren = hasChildren;
+ attributes = new ArrayList<Attribute>();
+ }
+ }
+
+ static class Attribute {
+ short tag;
+ byte form;
+
+ Attribute(short tag, byte form) {
+ this.tag = tag;
+ this.form = form;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("tag: " + Long.toHexString(tag)); //$NON-NLS-1$
+ sb.append(" form: " + Long.toHexString(form)); //$NON-NLS-1$
+ return sb.toString();
+ }
+ }
+
+ static class AttributeValue {
+ private Object value;
+
+ // for indirect form, this is the actual form
+ private byte actualForm;
+
+ AttributeValue(byte form, IStreamBuffer in, byte addressSize, IStreamBuffer debugStrings) {
+ actualForm = form;
+
+ try {
+ value = readAttribute(in, addressSize, debugStrings);
+ } catch (IOException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if (value != null) {
+ Class<? extends Object> clazz = value.getClass();
+ if (clazz.isArray()) {
+ int len = Array.getLength(value);
+ sb.append(len).append(' ');
+ sb.append(clazz.getComponentType().toString());
+ sb.append(':');
+ for (int i = 0; i < len; i++) {
+ byte b = Array.getByte(value, i);
+ sb.append(' ').append(Integer.toHexString(b));
+ }
+ } else {
+ if (value instanceof Number) {
+ Number n = (Number) value;
+ sb.append(Long.toHexString(n.longValue()));
+ } else if (value instanceof String) {
+ sb.append(value);
+ } else {
+ sb.append(value);
+ }
+ }
+ }
+ return sb.toString();
+ }
+ //TODO: support DWARF 64-bit format
+ private Object readAttribute(IStreamBuffer in, byte addressSize, IStreamBuffer debugStrings) throws IOException {
+ Object obj = null;
+ switch (actualForm) {
+ case DwarfConstants.DW_FORM_addr:
+ obj = DwarfInfoReader.readAddress(in, addressSize);
+ case DwarfConstants.DW_FORM_ref_addr:
+ break;
+
+ case DwarfConstants.DW_FORM_block: {
+ int size = (int) DwarfInfoReader.read_unsigned_leb128(in);
+ byte[] bytes = new byte[size];
+ in.get(bytes);
+ obj = bytes;
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_block1: {
+ int size = in.get() & 0xff;
+ byte[] bytes = new byte[size];
+ in.get(bytes);
+ obj = bytes;
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_block2: {
+ int size = in.getShort();
+ byte[] bytes = new byte[size];
+ in.get(bytes);
+ obj = bytes;
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_block4: {
+ int size = in.getInt();
+ byte[] bytes = new byte[size];
+ in.get(bytes);
+ obj = bytes;
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_data1:
+ obj = new Byte(in.get());
+ break;
+
+ case DwarfConstants.DW_FORM_data2:
+ obj = new Short(in.getShort());
+ break;
+
+ case DwarfConstants.DW_FORM_data4:
+ obj = new Integer(in.getInt());
+ break;
+
+ case DwarfConstants.DW_FORM_data8:
+ obj = new Long(in.getLong());
+ break;
+
+ case DwarfConstants.DW_FORM_sdata:
+ obj = new Long(DwarfInfoReader.read_signed_leb128(in));
+ break;
+
+ case DwarfConstants.DW_FORM_udata:
+ obj = new Long(DwarfInfoReader.read_unsigned_leb128(in));
+ break;
+
+ case DwarfConstants.DW_FORM_string: {
+ int c;
+ StringBuffer sb = new StringBuffer();
+ while ((c = (in.get() & 0xff)) != -1) {
+ if (c == 0) {
+ break;
+ }
+ sb.append((char) c);
+ }
+ obj = sb.toString();
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_flag:
+ obj = new Byte(in.get());
+ break;
+
+ case DwarfConstants.DW_FORM_strp: {
+ int offset = in.getInt();
+ if (debugStrings == null) {
+ obj = new String();
+ } else if (offset < 0 || offset > debugStrings.capacity()) {
+ obj = new String();
+ } else {
+ debugStrings.position(offset);
+ obj = DwarfInfoReader.readString(debugStrings);
+ }
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_ref1:
+ obj = new Integer(in.get() & 0xff);
+ break;
+
+ case DwarfConstants.DW_FORM_ref2:
+ obj = new Integer(in.getShort() & 0xffff);
+ break;
+
+ case DwarfConstants.DW_FORM_ref4:
+ obj = new Integer(in.getInt());
+ break;
+
+ case DwarfConstants.DW_FORM_ref8:
+ obj = new Long(in.getLong());
+ break;
+
+ case DwarfConstants.DW_FORM_ref_udata:
+ obj = new Long(DwarfInfoReader.read_unsigned_leb128(in));
+ break;
+
+ case DwarfConstants.DW_FORM_indirect: {
+ actualForm = (byte) DwarfInfoReader.read_unsigned_leb128(in);
+ return readAttribute(in, addressSize, debugStrings);
+ }
+
+ case DwarfConstants.DW_FORM_sec_offset :
+// if (header.offsetSize == 8)
+// obj = new Long(in.getLong));
+// else
+ obj = new Long(in.getInt() & 0xffffffffL);
+ break;
+ case DwarfConstants.DW_FORM_exprloc :
+ long size = DwarfInfoReader.read_unsigned_leb128(in);
+ byte[] bytes = new byte[(int) size];
+ in.get(bytes);
+ obj = bytes;
+ break;
+ case DwarfConstants.DW_FORM_flag_present :
+ // 0 byte value
+ obj = Byte.valueOf((byte)1);
+ break;
+ case DwarfConstants.DW_FORM_ref_sig8 :
+ obj = new Long(in.getLong());
+ break;
+
+
+ default:
+ assert (false);
+ break;
+ }
+
+ return obj;
+ }
+
+ /**
+ * @param attr
+ * @param in
+ * @param addressSize
+ * @param debugStrings
+ */
+ //TODO: support DWARF 64-bit format
+ public static void skipAttributeValue(short form, IStreamBuffer in,
+ byte addressSize) throws IOException {
+ switch (form) {
+ case DwarfConstants.DW_FORM_addr:
+ case DwarfConstants.DW_FORM_ref_addr:
+ in.position(in.position() + addressSize);
+ break;
+
+ case DwarfConstants.DW_FORM_block: {
+ int size = (int) DwarfInfoReader.read_unsigned_leb128(in);
+ in.position(in.position() + size);
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_block1: {
+ int size = in.get() & 0xff;
+ in.position(in.position() + size);
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_block2: {
+ int size = in.getShort();
+ in.position(in.position() + size);
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_block4: {
+ int size = in.getInt();
+ in.position(in.position() + size);
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_data1:
+ in.position(in.position() + 1);
+ break;
+
+ case DwarfConstants.DW_FORM_data2:
+ in.position(in.position() + 2);
+ break;
+
+ case DwarfConstants.DW_FORM_data4:
+ in.position(in.position() + 4);
+ break;
+
+ case DwarfConstants.DW_FORM_data8:
+ in.position(in.position() + 8);
+ break;
+
+ case DwarfConstants.DW_FORM_sdata:
+ DwarfInfoReader.read_signed_leb128(in);
+ break;
+
+ case DwarfConstants.DW_FORM_udata:
+ DwarfInfoReader.read_unsigned_leb128(in);
+ break;
+
+ case DwarfConstants.DW_FORM_string: {
+ int c;
+ while ((c = (in.get() & 0xff)) != -1) {
+ if (c == 0) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case DwarfConstants.DW_FORM_flag:
+ in.position(in.position() + 1);
+ break;
+
+ case DwarfConstants.DW_FORM_strp:
+ in.position(in.position() + 4);
+ break;
+
+ case DwarfConstants.DW_FORM_ref1:
+ in.position(in.position() + 1);
+ break;
+
+ case DwarfConstants.DW_FORM_ref2:
+ in.position(in.position() + 2);
+ break;
+
+ case DwarfConstants.DW_FORM_ref4:
+ in.position(in.position() + 4);
+ break;
+
+ case DwarfConstants.DW_FORM_ref8:
+ in.position(in.position() + 8);
+ break;
+
+ case DwarfConstants.DW_FORM_ref_udata:
+ DwarfInfoReader.read_unsigned_leb128(in);
+ break;
+
+ case DwarfConstants.DW_FORM_indirect: {
+ form = (short) DwarfInfoReader.read_unsigned_leb128(in);
+ skipAttributeValue(form, in, addressSize);
+ break;
+ }
+
+ case DwarfConstants.DW_FORM_sec_offset :
+// if (header.offsetSize == 8)
+// in.position(in.position() + 8);
+// else
+ in.position(in.position() + 4);
+ break;
+ case DwarfConstants.DW_FORM_exprloc :
+ int size = (int) DwarfInfoReader.read_unsigned_leb128(in);
+ in.position(in.position() + size);
+ break;
+ case DwarfConstants.DW_FORM_flag_present :
+ // 0 byte value
+ in.position(in.position() + 0); // for readability
+ break;
+ case DwarfConstants.DW_FORM_ref_sig8 :
+ in.position(in.position() + 8);
+ break;
+
+ default:
+ assert (false);
+ break;
+ }
+ }
+
+ /**
+ * Parse attributes and then skip to sibling, if any
+ *
+ * @param names array to hold up to two names
+ * @param entry debug info entry
+ * @param in buffer stream of debug info
+ * @param addressSize
+ * @param debugStrings
+ * @return DW_AT_name value, or null if there is no or invalid DW_AT_name attribute
+ */
+ public static void skipAttributesToSibling(AbbreviationEntry entry, IStreamBuffer in, byte addressSize) {
+
+ long sibling = -1;
+
+ // go through the attributes and throw away everything except the sibling
+ int len = entry.attributes.size();
+ for (int i = 0; i < len; i++) {
+ Attribute attr = entry.attributes.get(i);
+ try {
+ if (attr.tag == DwarfConstants.DW_AT_sibling) {
+ if (attr.form == DwarfConstants.DW_FORM_ref_udata) {
+ sibling = DwarfInfoReader.read_unsigned_leb128(in);
+ } else if (attr.form == DwarfConstants.DW_FORM_ref4) {
+ sibling = in.getInt();
+ } else {
+ // TODO: allow other forms for sibling value
+ AttributeValue.skipAttributeValue(attr.form, in, addressSize);
+ }
+ } else {
+ AttributeValue.skipAttributeValue(attr.form, in, addressSize);
+ }
+ } catch (IOException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ break;
+ }
+ }
+
+ if (sibling != -1)
+ in.position(sibling);
+ }
+
+
+ public byte getActualForm() {
+ return actualForm;
+ }
+
+ /**
+ * Get the value as a 64-bit signed long, sign-extending any shorter attribute
+ * @return value as signed long
+ */
+ public long getValueAsSignedLong() {
+ if (value instanceof Number) {
+ return ((Number) value).longValue();
+ }
+ return 0;
+ }
+
+ /**
+ * Get the value as a 64-bit long.
+ *
+ * A Byte, Short, or Integer is zero-extended.
+ *
+ * @return value as long
+ */
+ public long getValueAsLong() {
+ if (value instanceof Byte) {
+ return ((Byte) value).byteValue() & 0xff;
+ }
+ if (value instanceof Short) {
+ return ((Short) value).shortValue() & 0xffff;
+ }
+ if (value instanceof Integer) {
+ return ((Integer) value).intValue() & 0xffffffff;
+ }
+ // fallthrough
+ if (value instanceof Number) {
+ return ((Number) value).longValue();
+ }
+ return 0;
+ }
+
+ /**
+ * Get the value as a 32-bit int.
+ *
+ * A Byte or Short is zero-extended.
+ *
+ * @return value as int
+ */
+ public int getValueAsInt() {
+ if (value instanceof Byte) {
+ return ((Byte) value).byteValue() & 0xff;
+ }
+ if (value instanceof Short) {
+ return ((Short) value).shortValue() & 0xffff;
+ }
+ // fallthrough
+ if (value instanceof Number) {
+ return ((Number) value).intValue();
+ }
+ return 0;
+ }
+
+ /**
+ * Get the value as a string
+ * @return String or "" if not a string
+ */
+ public String getValueAsString() {
+ if (value != null)
+ return value.toString();
+ return null;
+ }
+
+ /**
+ * Get the byte array value (which is empty if this is not a byte array)
+ * @return array
+ */
+ public byte[] getValueAsBytes() {
+ if (value instanceof byte[])
+ return (byte[]) value;
+ return new byte[0];
+ }
+ }
+
+ static class AttributeList {
+
+ Map<Short, AttributeValue> attributeMap;
+
+ AttributeList(AbbreviationEntry entry, IStreamBuffer in, byte addressSize, IStreamBuffer debugStrings) {
+
+ int len = entry.attributes.size();
+ attributeMap = new HashMap<Short, AttributeValue>(len);
+ for (int i = 0; i < len; i++) {
+ Attribute attr = entry.attributes.get(i);
+ attributeMap.put(Short.valueOf(attr.tag), new AttributeValue(attr.form, in, addressSize, debugStrings));
+ }
+
+ }
+
+ public static void skipAttributes(AbbreviationEntry entry, IStreamBuffer in, byte addressSize) {
+
+ int len = entry.attributes.size();
+ for (int i = 0; i < len; i++) {
+ Attribute attr = entry.attributes.get(i);
+ try {
+ AttributeValue.skipAttributeValue(attr.form, in, addressSize);
+ } catch (IOException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ break;
+ }
+ }
+
+ }
+
+ public long getAttributeValueAsLong(short attributeName) {
+ AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
+ if (attr != null) {
+ return attr.getValueAsLong();
+ }
+ return 0;
+ }
+
+ public int getAttributeValueAsInt(short attributeName) {
+ AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
+ if (attr != null) {
+ return attr.getValueAsInt();
+ }
+ return 0;
+ }
+
+
+ public long getAttributeValueAsSignedLong(short attributeName) {
+ AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
+ if (attr != null) {
+ return attr.getValueAsSignedLong();
+ }
+ return 0;
+ }
+
+ public String getAttributeValueAsString(short attributeName) {
+ AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
+ if (attr != null) {
+ return attr.getValueAsString();
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ public byte[] getAttributeValueAsBytes(short attributeName) {
+ AttributeValue attr = attributeMap.get(Short.valueOf(attributeName));
+ if (attr != null) {
+ return attr.getValueAsBytes();
+ }
+ return new byte[0];
+ }
+
+ public AttributeValue getAttribute(short attributeName) {
+ return attributeMap.get(Short.valueOf(attributeName));
+ }
+
+ /**
+ * Tell whether the attributes do not have a code range.
+ * <p>
+ * Note: a singular DW_AT_low_pc means an entry point
+ * <p>
+ * Also note: a compile unit can have code represented by DW_AT_stmt_list
+ * @return true if the attributes represent code
+ */
+ public boolean hasCodeRangeAttributes() {
+ return attributeMap.containsKey(DwarfConstants.DW_AT_high_pc)
+ || attributeMap.containsKey(DwarfConstants.DW_AT_ranges);
+ }
+ }
+
+ static public class PublicNameInfo {
+ public final String nameWithNameSpace;
+ public final CompilationUnitHeader cuHeader;
+ public final short tag; // DW_TAG_xxx
+
+ public PublicNameInfo(String nameWithNameSpace, CompilationUnitHeader cuHeader, short tag) {
+ this.nameWithNameSpace = nameWithNameSpace;
+ this.cuHeader = cuHeader;
+ this.tag = tag;
+ }
+ }
+
+ // list of compilation units per source file
+ protected HashMap<IPath, List<ICompileUnitScope>> compileUnitsPerFile = new HashMap<IPath, List<ICompileUnitScope>>();
+
+ // list of compile units in .debug_info order
+ protected ArrayList<DwarfCompileUnit> compileUnits = new ArrayList<DwarfCompileUnit>();
+
+ // list of compile units with code (non-zero high address), sorted by low address
+ protected ArrayList<DwarfCompileUnit> sortedCompileUnitsWithCode = new ArrayList<DwarfCompileUnit>();
+
+ // function and type declarations can be referenced by offsets relative to
+ // the compile unit or to the entire .debug_info section. therefore we keep
+ // maps by .debug_info offset, and for compile unit relative offsets, we
+ // just add the compile unit offset into the .debug_info section.
+ protected Map<Long, AttributeList> functionsByOffset = new HashMap<Long, AttributeList>();
+ protected Map<Long, IType> typesByOffset = Collections.synchronizedMap(new HashMap<Long, IType>());
+
+ // for casting to a type, keep certain types by name
+ protected Map<String, List<IType>> typesByName = new HashMap<String, List<IType>>();
+ // for casting to a type, track whether the cast name includes an aggregate designator
+ enum TypeAggregate { Class, Struct, Union, None };
+
+ // map of entities which created scopes
+ protected Map<Long, Scope> scopesByOffset = new HashMap<Long, Scope>();
+
+ // entry points for CUs in the .debug_info section, used to dynamically parse CUs as needed
+ protected TreeMap<Long, CompilationUnitHeader> debugOffsetsToCompileUnits = new TreeMap<Long, CompilationUnitHeader>();
+
+ // forward references for tags we have not parsed yet. (These will go into typesByOffset once handled)
+ //Map<Long, ForwardDwarfDefinition> forwardDwarfDefinitions = new HashMap<Long, ForwardDwarfDefinition>();
+
+ // these are just for faster lookups
+ protected Map<String, List<IFunctionScope>> functionsByName = new HashMap<String, List<IFunctionScope>>();
+ protected Map<String, List<IVariable>> variablesByName = new HashMap<String, List<IVariable>>();
+ protected Map<String, List<PublicNameInfo>> publicFunctions = new HashMap<String, List<PublicNameInfo>>();
+ protected Map<String, List<PublicNameInfo>> publicVariables = new HashMap<String, List<PublicNameInfo>>();
+
+ // abbreviation tables (lists of abbrev entries), mapped by .debug_abbrev offset
+ protected Map<Integer, Map<Long, AbbreviationEntry>> abbreviationMaps = new HashMap<Integer, Map<Long, AbbreviationEntry>>();
+
+ // mapping of PC range to frame description entries
+ protected TreeMap<IRangeList.Entry, FrameDescriptionEntry> frameDescEntries = new TreeMap<IRangeList.Entry, FrameDescriptionEntry>();
+ // mapping of CIE offsets to parsed common info entries
+ protected Map<Long, CommonInformationEntry> commonInfoEntries = new HashMap<Long, CommonInformationEntry>();
+
+
+ protected Set<String> referencedFiles = new HashSet<String>();
+ protected boolean buildReferencedFilesList = true;
+
+ private IPath symbolFilePath;
+ private long symbolFileLastModified;
+ private boolean parsedInitially = false;
+ private boolean parsedForVarsAndAddresses = false;
+ private boolean parsedForScopesAndAddresses = false;
+ private boolean parsedForTypes = false;
+ private boolean parsedForGlobalVars = false;
+
+ private final IExecutableSymbolicsReader exeReader;
+ private final DwarfModuleScope moduleScope;
+
+ final DwarfFileHelper fileHelper;
+
+ private IFrameRegisterProvider frameRegisterProvider;
+
+ private static String SOURCE_FILES_CACHE = "_source_files"; //$NON-NLS-1$
+
+ public DwarfDebugInfoProvider(IExecutableSymbolicsReader exeReader) {
+ this.exeReader = exeReader;
+ this.symbolFilePath = exeReader.getSymbolFile();
+ this.symbolFileLastModified = symbolFilePath.toFile().lastModified();
+ this.moduleScope = new DwarfModuleScope(this);
+ this.fileHelper = new DwarfFileHelper(symbolFilePath);
+ this.frameRegisterProvider = new DwarfFrameRegisterProvider(this);
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return DwarfMessages.DwarfDebugInfoProvider_DwarfProviderFor + symbolFilePath;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IDebugInfoProvider#dispose()
+ */
+ public void dispose() {
+ // several views in DSF hold onto all our debug info,
+ // so go through and explicitly break links
+ if (moduleScope != null) {
+ moduleScope.dispose();
+ }
+
+ // help GC
+ compileUnitsPerFile.clear();
+ compileUnits.clear();
+ functionsByOffset.clear();
+ typesByOffset.clear();
+ scopesByOffset.clear();
+ debugOffsetsToCompileUnits.clear();
+ functionsByName.clear();
+ variablesByName.clear();
+ publicFunctions.clear();
+ publicVariables.clear();
+ abbreviationMaps.clear();
+ referencedFiles.clear();
+ moduleScope.dispose();
+ parsedInitially = false;
+ parsedForTypes = false;
+ parsedForVarsAndAddresses = false;
+ parsedForScopesAndAddresses = false;
+
+ fileHelper.dispose();
+ frameRegisterProvider.dispose();
+ }
+
+ void ensureParsedInitially() {
+ if (!parsedInitially) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ parsedInitially = true;
+ reader.parseInitial();
+ }
+ }
+
+ void ensureParsedForScopes() {
+ if (!parsedForScopesAndAddresses) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ if (!parsedInitially) {
+ parsedInitially = true;
+ reader.parseInitial();
+ }
+ parsedForScopesAndAddresses = true;
+ reader.parseForAddresses(false);
+ }
+ }
+
+ void ensureParsedForScope(IAddress linkAddress) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ if (!parsedInitially) {
+ parsedInitially = true;
+ reader.parseInitial();
+ }
+ reader.parseForAddress(linkAddress);
+ }
+
+ void ensureParsedForVariables() {
+ if (!parsedForVarsAndAddresses) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ if (!parsedInitially) {
+ parsedInitially = true;
+ reader.parseInitial();
+ }
+ parsedForVarsAndAddresses = true;
+ reader.parseForAddresses(true);
+ }
+ }
+
+ private void ensureParsedForGlobalVariables() {
+ if (parsedForGlobalVars)
+ return;
+ parsedForGlobalVars = true;
+
+ if (publicVariables.size() == 0)
+ return;
+
+ // determine compilation units containing globals
+ HashSet<CompilationUnitHeader> cuWithGlobalsArray = new HashSet<CompilationUnitHeader>(publicVariables.size());
+ for (List<PublicNameInfo> infoList : publicVariables.values()) {
+ for (PublicNameInfo info : infoList) {
+ cuWithGlobalsArray.add(info.cuHeader);
+ }
+ }
+
+ // parse compilation units containing global variables
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ for (CompilationUnitHeader cuHeader : cuWithGlobalsArray)
+ reader.parseCompilationUnitForAddresses(cuHeader.scope);
+ }
+
+ void ensureParsedForTypes() {
+ if (!parsedForTypes) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ if (!parsedInitially) {
+ parsedInitially = true;
+ reader.parseInitial();
+ }
+ parsedForTypes = true;
+ reader.parseForTypes();
+ }
+ }
+
+ public void setParsedInitially() {
+ parsedInitially = true;
+ }
+
+ public void setParsedForAddresses() {
+ parsedForVarsAndAddresses = true;
+ }
+
+ public IPath getSymbolFile() {
+ return symbolFilePath;
+ }
+
+ public IModuleScope getModuleScope() {
+ return moduleScope;
+ }
+
+ public IAddress getBaseLinkAddress() {
+ return exeReader.getBaseLinkAddress();
+ }
+
+ public Collection<IFunctionScope> getFunctionsByName(String name) {
+ List<IFunctionScope> result;
+
+ ensureParsedInitially();
+
+ String baseName = name;
+
+ /* use same semantics as before, where qualified name lookups would fail
+ // pubnames uses qualified names but is indexed by basename
+ if (name != null) {
+ int baseStart = name.lastIndexOf("::");
+ if (baseStart != -1)
+ baseName = name.substring(baseStart + 2);
+ }
+ */
+
+ // first, match against public function names
+ if (publicFunctions.size() > 0) {
+ if (name != null) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ List<PublicNameInfo> nameMatches = publicFunctions.get(baseName);
+
+ if (nameMatches != null) {
+ // parse the compilation units that have matches
+ if (nameMatches.size() == 1) { // quick usual case
+ reader.parseCompilationUnitForAddresses(nameMatches.get(0).cuHeader.scope);
+ } else {
+ ArrayList<DwarfCompileUnit> cuList = new ArrayList<DwarfCompileUnit>();
+
+ for (PublicNameInfo info : nameMatches) {
+ if (!cuList.contains(info.cuHeader.scope)) {
+ cuList.add(info.cuHeader.scope);
+ }
+ }
+
+ for (DwarfCompileUnit cu : cuList) {
+ reader.parseCompilationUnitForAddresses(cu);
+ }
+ }
+ } else {
+ // not a public name, so parse all compilation units looking for functions
+ ensureParsedForScopes();
+ }
+ } else {
+ // name is null, so parse all compilation units looking for functions
+ ensureParsedForScopes();
+ }
+ } else {
+ // no public names, so parse all compilation units looking for functions
+ ensureParsedForScopes();
+ }
+
+ if (name != null) {
+ result = functionsByName.get(baseName);
+ if (result == null)
+ return new ArrayList<IFunctionScope>(0);
+ } else {
+ result = new ArrayList<IFunctionScope>(functionsByName.size()); // at least this big
+ for (List<IFunctionScope> functions : functionsByName.values())
+ result.addAll(functions);
+ ((ArrayList<IFunctionScope>) result).trimToSize();
+ }
+ return Collections.unmodifiableCollection(result);
+ }
+
+ public Collection<IVariable> getVariablesByName(String name, boolean globalsOnly) {
+ List<IVariable> result;
+
+ ensureParsedInitially();
+
+ if (name == null) {
+ if (publicVariables.size() > 0) {
+ // name is null, so parse all compilation units looking for variables
+ if (globalsOnly)
+ ensureParsedForGlobalVariables();
+ else
+ ensureParsedForVariables();
+ }
+
+ result = new ArrayList<IVariable>(variablesByName.size()); // at least this big
+ for (List<IVariable> variables : variablesByName.values())
+ result.addAll(variables);
+
+ return Collections.unmodifiableCollection(result);
+ }
+
+ String baseName = name;
+ int baseNameStart = name.lastIndexOf("::"); //$NON-NLS-1$
+ if (baseNameStart != -1)
+ baseName = name.substring(baseNameStart + 2);
+
+ // match against public variable names, which the initial parse populated
+ if (publicVariables.size() > 0) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ List<PublicNameInfo> nameMatches = publicVariables.get(baseName);
+
+ if (nameMatches != null) {
+ // parse the compilation units that have matches
+ if (nameMatches.size() == 1) { // quick usual case
+ reader.parseCompilationUnitForAddresses(nameMatches.get(0).cuHeader.scope);
+ } else {
+ ArrayList<DwarfCompileUnit> cuList = new ArrayList<DwarfCompileUnit>();
+
+ for (PublicNameInfo info : nameMatches) {
+ if (!cuList.contains(info.cuHeader.scope)) {
+ cuList.add(info.cuHeader.scope);
+ }
+ }
+
+ for (DwarfCompileUnit cu : cuList) {
+ reader.parseCompilationUnitForAddresses(cu);
+ }
+ }
+ } else {
+ // not a public name, so parse all compilation units looking for variables
+ if (!globalsOnly)
+ ensureParsedForVariables();
+ }
+ }
+
+ result = variablesByName.get(name);
+
+ // check against unqualified name because RVCT 2.x did not include namespace
+ // info for globals that are inside namespaces
+ if (result == null && baseNameStart != -1)
+ result = variablesByName.get(baseName);
+
+ if (result == null)
+ return new ArrayList<IVariable>(0);
+
+ return Collections.unmodifiableCollection(result);
+ }
+
+ /**
+ * @return the publicFunctions
+ */
+ public Map<String, List<PublicNameInfo>> getPublicFunctions() {
+ ensureParsedInitially();
+ return publicFunctions;
+ }
+ /**
+ * @return the publicVariables
+ */
+ public Map<String, List<PublicNameInfo>> getPublicVariables() {
+ ensureParsedInitially();
+ return publicVariables;
+ }
+
+ public ICompileUnitScope getCompileUnitForAddress(IAddress linkAddress) {
+ ensureParsedForScope(linkAddress);
+
+ IScope scope = moduleScope.getScopeAtAddress(linkAddress);
+ while (scope != null && !(scope instanceof ICompileUnitScope)) {
+ scope = scope.getParent();
+ }
+
+ return (ICompileUnitScope) scope;
+ }
+
+ public List<ICompileUnitScope> getCompileUnitsForFile(IPath filePath) {
+ ensureParsedInitially();
+
+ List<ICompileUnitScope> cuList = compileUnitsPerFile.get(filePath);
+
+ if (cuList != null)
+ return cuList;
+
+ // FIXME: we need a looser check here: on Windows, we added drive letters to all
+ // paths before populating compileUnitsPerFile, even if there is not really a
+ // drive (see DwarFileHelper).
+ for (Map.Entry<IPath, List<ICompileUnitScope>> entry : compileUnitsPerFile.entrySet()) {
+ if (entry.getKey().setDevice(null).equals(filePath.setDevice(null))) {
+ return entry.getValue();
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+ @SuppressWarnings("unchecked")
+ public String[] getSourceFiles(IProgressMonitor monitor) {
+ if (referencedFiles.isEmpty()) {
+ // Check the persistent cache
+ String cacheKey = getSymbolFile().toOSString() + SOURCE_FILES_CACHE;
+ Set<String> cachedFiles = EDCDebugger.getDefault().getCache().getCachedData(cacheKey, Set.class, symbolFileLastModified);
+ if (cachedFiles == null)
+ {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ reader.quickParseDebugInfo(monitor);
+ assert referencedFiles.size() > 0;
+ EDCDebugger.getDefault().getCache().putCachedData(cacheKey, new HashSet<String>(referencedFiles), symbolFileLastModified);
+ }
+ else
+ referencedFiles = cachedFiles;
+ }
+
+ return referencedFiles.toArray(new String[referencedFiles.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IDebugInfoProvider#getTypes()
+ */
+ public Collection<IType> getTypes() {
+ ensureParsedForTypes();
+
+ ArrayList<IType> types = new ArrayList<IType>(typesByOffset.values());
+ return types;
+ }
+
+ /////////////////
+
+ // Lazy evaluation methods
+
+
+ /**
+ * Fetch a type lazily. Either we've already parsed the type, or we have
+ * a reference to it, or we can find its compilation unit and parse its types.
+ * We do not fix up cross references until someone asks for
+ * it (e.g. from an IType or IVariable implementation).
+ */
+ public IType readType(long offset_) {
+ Long offset = Long.valueOf(offset_);
+ IType type = typesByOffset.get(offset);
+ if (type == null) {
+ // make sure we've parsed it
+ CompilationUnitHeader header = fetchCompileUnitHeader(offset_);
+ if (header != null) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ reader.parseCompilationUnitForTypes(header.scope);
+ type = typesByOffset.get(offset);
+ // may be unhandled currently
+ if (type == null) {
+ // workaround for GCC-E 3.x bug where some, but not all, type offsets are off by 4
+ // assume if you hit this null case that the problem may be the GCC-E bug
+ type = typesByOffset.get(offset - 4);
+ if (type == null)
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfDebugInfoProvider_NotParsingType1 + Long.toHexString(offset_) +
+ DwarfMessages.DwarfDebugInfoProvider_NotParsingType2 + symbolFilePath, null);
+ }
+ } else {
+ // may be unhandled currently
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfDebugInfoProvider_CannotResolveCompUnit1 + Long.toHexString(offset_) +
+ DwarfMessages.DwarfDebugInfoProvider_CannotResolveCompUnit2 + symbolFilePath, null);
+ }
+ }
+ return type;
+ }
+
+
+ /**
+ * Scan compilation unit header for its subprograms and addresses.
+ */
+ public CompilationUnitHeader scanCompilationHeader(long offset_) {
+ // make sure we've parsed it
+ CompilationUnitHeader header = fetchCompileUnitHeader(offset_);
+ if (header != null) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ reader.parseCompilationUnitForAddresses(header.scope);
+ }
+ return header;
+ }
+
+
+ /**
+ * Fetch a referenced type lazily.
+ * @param scope
+ */
+ IType resolveTypeReference(ForwardTypeReference ref) {
+ IType type = typesByOffset.get(ref.offset);
+ if (type == null) {
+ type = readType(ref.offset);
+ }
+ return type;
+ }
+ /**
+ * @return
+ */
+ public IExecutableSymbolicsReader getExecutableSymbolicsReader() {
+ return exeReader;
+ }
+
+ /**
+ * Remember where a compilation unit header lives in the debug info.
+ * @param debugInfoOffset
+ * @param currentCUHeader
+ */
+ public void registerCompileUnitHeader(int debugInfoOffset,
+ CompilationUnitHeader currentCUHeader) {
+ debugOffsetsToCompileUnits.put((long) debugInfoOffset, currentCUHeader);
+ }
+
+ /**
+ * Get a compilation unit header that contains the given offset.
+ * @param debugInfoOffset an offset which is on or after a compilation unit's debug offset
+ * @return {@link CompilationUnitHeader} containing the offset
+ */
+ public CompilationUnitHeader fetchCompileUnitHeader(long debugInfoOffset) {
+ CompilationUnitHeader match = debugOffsetsToCompileUnits.get(debugInfoOffset);
+ if (match != null)
+ return match;
+
+ // it's inside one
+ SortedMap<Long,CompilationUnitHeader> headMap = debugOffsetsToCompileUnits.headMap(debugInfoOffset);
+ // urgh, sorted map... no easy way to get to the end
+ for (CompilationUnitHeader header : headMap.values()) {
+ match = header;
+ }
+ return match;
+ }
+
+ /**
+ * Get the frame description entry for the given PC
+ * @param framePC
+ * @return FDE or <code>null</code>
+ */
+ public FrameDescriptionEntry findFrameDescriptionEntry(IAddress framePC) {
+ DwarfInfoReader reader = new DwarfInfoReader(this);
+ if (frameDescEntries.isEmpty()) {
+ reader.parseForFrameIndices();
+ }
+
+ long pc = framePC.getValue().longValue();
+ SortedMap<Entry, FrameDescriptionEntry> tailMap = frameDescEntries.tailMap(new IRangeList.Entry(pc, pc));
+ if (tailMap.isEmpty())
+ return null;
+
+ FrameDescriptionEntry entry = tailMap.values().iterator().next();
+ if (entry.getCIE() == null) {
+ CommonInformationEntry cie = null;
+ if (!commonInfoEntries.containsKey(entry.ciePtr)) {
+ try {
+ cie = reader.parseCommonInfoEntry(entry.ciePtr, entry.addressSize, framePC);
+ } catch (IOException e) {
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfDebugInfoProvider_FailedToReadCIE + entry.ciePtr, e);
+ }
+ commonInfoEntries.put(entry.ciePtr, cie);
+ } else {
+ cie = commonInfoEntries.get(entry.ciePtr);
+ }
+ entry.setCIE(cie);
+ }
+
+ return entry;
+ }
+
+ /**
+ * @return
+ */
+ public IFrameRegisterProvider getFrameRegisterProvider() {
+ return frameRegisterProvider;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider#getTypesByName(java.lang.String)
+ */
+ public Collection<IType> getTypesByName(String name) {
+ // is name has "struct", "class" or "union", search without that
+ name = name.trim();
+
+ String baseName = name;
+ TypeAggregate aggregate = TypeAggregate.None;
+
+ if (baseName.startsWith("class ")) { //$NON-NLS-1$
+ aggregate = TypeAggregate.Class;
+ baseName = baseName.replace("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ } else if (baseName.startsWith("struct ")) { //$NON-NLS-1$
+ aggregate = TypeAggregate.Struct;
+ baseName = baseName.replace("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ } else if (baseName.startsWith("union ")) { //$NON-NLS-1$
+ aggregate = TypeAggregate.Union;
+ baseName = baseName.replace("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ Collection<IType> types = typesByName.get(baseName);
+
+ String templateName = null;
+ String templateName2 = null;
+
+ if (types == null) {
+ // if we didn't match and this is a template name,
+ // remove extra spaces and composite type names
+ if (baseName.indexOf('<') != -1) {
+ templateName = baseName;
+
+ while (templateName.contains(" ")) //$NON-NLS-1$
+ templateName = templateName.replaceAll(" ", " "); //$NON-NLS-1$ //$NON-NLS-2$
+ templateName = templateName.replaceAll(", ", ","); //$NON-NLS-1$ //$NON-NLS-2$
+ templateName = templateName.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ templateName = templateName.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ templateName = templateName.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ types = typesByName.get(templateName);
+
+ // template name without "<...>", rather than with "<...>", might match
+ if (types == null) {
+ templateName2 = templateName.substring(0, templateName.indexOf('<'));
+
+ types = typesByName.get(templateName2);
+
+ // screen out types whose template list does not match
+ if (types != null) {
+ ArrayList<IType> matchingTypes = null;
+ for (Iterator<IType> it = types.iterator(); it.hasNext(); ) {
+ IType nextType = it.next();
+ String match = nextType.getName();
+ // for templates, remove composite type names (e.g., "class")
+ match = match.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ match = match.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ match = match.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ if (match.equals(templateName)) {
+ if (matchingTypes == null)
+ matchingTypes = new ArrayList<IType>(types.size());
+ matchingTypes.add(nextType);
+ }
+ }
+ types = matchingTypes; // may be null
+ }
+ }
+ }
+
+ if (types == null) {
+ // Maybe we optimistically searched for relevant types;
+ // if that fails, do the full parse of types now
+ if (!parsedForTypes) {
+ ensureParsedForTypes();
+ types = getTypesByName(baseName);
+ if (types != null)
+ return types; // non-template return
+
+ if (baseName.indexOf('<') != -1) {
+ types = typesByName.get(templateName);
+ if (types == null)
+ types = typesByName.get(templateName2);
+ else
+ templateName2 = null; // did not match name without "<...>"
+ }
+ }
+
+ if (types == null)
+ return new ArrayList<IType>(0);
+ }
+ }
+
+ // screen out types whose template list does not match
+ if (templateName2 != null) {
+ ArrayList<IType> matchingTypes = new ArrayList<IType>(types.size());
+ for (Iterator<IType> it = types.iterator(); it.hasNext(); ) { // types can't be null
+ IType nextType = it.next();
+ String match = nextType.getName();
+ // for templates, remove composite type names (e.g., "class")
+ match = match.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ match = match.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ match = match.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+
+ if (match.equals(templateName))
+ matchingTypes.add(nextType);
+ }
+ types = matchingTypes;
+ }
+
+ // make sure that the aggregate type matches as well as the name
+ if (aggregate == TypeAggregate.None)
+ return Collections.unmodifiableCollection(types);
+
+ Iterator<IType> itr = types.iterator();
+ while (itr.hasNext()) {
+ IType nextType = itr.next();
+ if ((aggregate == TypeAggregate.Class && !nextType.getName().contains("class ")) || //$NON-NLS-1$
+ (aggregate == TypeAggregate.Struct && !nextType.getName().contains("struct ")) || //$NON-NLS-1$
+ (aggregate == TypeAggregate.Union && !nextType.getName().contains("union "))) //$NON-NLS-1$
+ types.remove(nextType);
+ }
+
+ if (types.isEmpty())
+ return new ArrayList<IType>(0);
+
+ return Collections.unmodifiableCollection(types);
+ }
+
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.files.ExecutableSymbolicsReaderFactory;
+import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider;
+import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProviderFactory;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ *
+ */
+public class DwarfDebugInfoProviderFactory implements
+ IDebugInfoProviderFactory {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IDebugInfoProviderFactory#createDebugInfoProvider(org.eclipse.core.runtime.IPath)
+ */
+ public IDebugInfoProvider createDebugInfoProvider(IPath binaryPath, IExecutableSymbolicsReader exeReader) {
+ // DWARF info lives either in the executable itself or in an associated symbol file.
+ if (exeReader != null) {
+ if (exeReader.findExecutableSection(DwarfInfoReader.DWARF_DEBUG_INFO) != null) {
+ return new DwarfDebugInfoProvider(exeReader);
+ }
+ }
+
+ // else, look alongside for a *.sym or *.dbg file
+ IPath symFile = ExecutableSymbolicsReaderFactory.findSymbolicsFile(binaryPath);
+ if (symFile != null) {
+ IExecutableSymbolicsReader symExeReader = ExecutableSymbolicsReaderFactory.createFor(symFile);
+ if (symExeReader != null) {
+ return new DwarfDebugInfoProvider(symExeReader);
+ }
+ }
+
+ // no one has DWARF
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.internal.HostOS;
+import org.eclipse.cdt.debug.edc.internal.PathUtils;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * An instance of this class per DwarfDebugInfoProvider assists in
+ * canonicalizing filepaths (from DW_AT_comp_dir and DW_AT_stmt_list)
+ */
+public class DwarfFileHelper {
+
+ private boolean DEBUG = false;
+
+ @SuppressWarnings("unused") // no longer used, but kept for compatibility and possible futureuse
+ private final IPath symbolFile;
+
+ private Map<String, IPath> compDirAndNameMap;
+
+ private int hits;
+ private long nextDump;
+
+ public DwarfFileHelper(IPath symbolFile) {
+ this.symbolFile = symbolFile;
+ this.compDirAndNameMap = new HashMap<String, IPath>();
+ }
+
+ protected String getKey(String compDir, String name) {
+ return compDir + "/" + name;
+ }
+
+ /**
+ * This is to combine the given compDir and file name from Dwarf to form a
+ * file path. Some processing is done to canonicalize the path, including <br>
+ * -- extra path delimiter are removed. <br>
+ * -- "a/b/" and "../c/d" are combined to "/a/c/d". <br>
+ * -- change path delimiter to native, namely on Windows "/" => "\\" and the
+ * opposite on unix/linux
+ *
+ * @param compDir
+ * compDir from dwarf data.
+ * @param name
+ * file name from dwarf data with full or partial or no path.
+ * @return most complete file path that we can get from Dwarf data, which
+ * may still be a partial path. Note letter case of the names
+ * remains unchanged. Empty string is returned if the given name is
+ * an invalid file name, e.g. <internal>.
+ *
+ */
+ public IPath normalizeFilePath(String compDir, String name) {
+ if (name == null || name.length() == 0)
+ return null;
+
+ // don't count the entry "<internal>" from GCCE compiler
+ if (name.charAt(0) == '<')
+ return null;
+
+ // Create a key for doing a lookup in our IPath cache
+ String key = getKey(compDir, name);
+
+ // Look in the cache
+ IPath path = compDirAndNameMap.get(key);
+ if (path == null) {
+ path = normalizePath(compDir, name);
+ compDirAndNameMap.put(key, path);
+ } else {
+ hits++;
+ }
+ if (DEBUG && System.currentTimeMillis() > nextDump) {
+ System.out.println("DwarfFileHelper entries: " + compDirAndNameMap.size() + "; hits: " + hits);
+ nextDump = System.currentTimeMillis() + 1000;
+ }
+ return path;
+ }
+
+ /**
+ * Takes a DW_AT_comp_dir and a filename from DWARF,
+ * canonicalizes it and creates an IPath.
+ *
+ * @param compDir the compilation directory, as found in DWARF data
+ * @param name the file specification, as found in DWARF data
+ * @return IPath, never <code>null</code>
+ */
+ private IPath normalizePath(String compDir, String name) {
+
+ String fullName = name;
+
+ IPath path = PathUtils.createPath(name);
+
+ // Combine dir & name if needed.
+ if (!path.isAbsolute() && compDir.length() > 0) {
+ fullName = compDir;
+ if (!compDir.endsWith(File.separator))
+ fullName += File.separatorChar;
+ fullName += name;
+
+ path = PathUtils.createPath(fullName);
+ }
+
+ return path;
+ }
+
+ /**
+ * Convert cygwin path and change path delimiter to native.
+ * No longer used but left for backwards compatbility.
+ *
+ * @param path
+ * @return
+ */
+ public IPath fixUpPath(String path) {
+ /*
+ * translate cygwin drive path like /cygdrive/c/system/main.c
+ * //G/System/main.cpp
+ */
+ boolean isCygwin = false;
+ int deleteTill = 0;
+
+ // These paths may appear in Cygwin-compiled code, so check on any host
+ if (path.length() > 12 && path.startsWith("/cygdrive/") && ('/' == path.charAt(11))) { //$NON-NLS-1$
+ isCygwin = true;
+ deleteTill = 10;
+ }
+
+ // These paths may appear in Cygwin-compiled code, so check on any host
+ if (path.length() > 4 && path.startsWith("//") && ('/' == path.charAt(3))) { //$NON-NLS-1$
+ isCygwin = true;
+ deleteTill = 2;
+ }
+
+ // New-style Cygwin is different and has neither prefix.
+ // But this check only makes sense on a Windows host, since
+ // it may be a valid Unix-host path.
+ //
+ // /C/sources/foo.c --> c:\sources\foo.c
+ if (HostOS.IS_WIN32 && path.length() > 3
+ && path.charAt(0) == '/'
+ && Character.isLetter(path.charAt(1))
+ && path.charAt(2) == '/') {
+ isCygwin = true;
+ deleteTill = 1;
+ }
+
+ if (isCygwin) {
+ StringBuilder buf = new StringBuilder(path);
+ buf.delete(0, deleteTill);
+ buf.insert(1, ':');
+ path = buf.toString();
+ }
+
+ // convert to path on runtime platform
+ //
+ return PathUtils.createPath(path);
+ }
+
+ /**
+ *
+ */
+ public void dispose() {
+ compDirAndNameMap.clear();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.TreeMap;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.MemoryStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExecutionDMC;
+import org.eclipse.cdt.debug.edc.internal.symbols.MemoryVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.RegisterOffsetVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.RegisterVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.ValueVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AttributeValue;
+import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.services.IFrameRegisterProvider;
+import org.eclipse.cdt.debug.edc.services.IFrameRegisters;
+import org.eclipse.cdt.debug.edc.services.Registers;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.symbols.IRangeList;
+import org.eclipse.cdt.debug.edc.symbols.IRangeList.Entry;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.datamodel.DMContexts;
+import org.eclipse.cdt.dsf.debug.service.IStack;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Apply the rules for the CIE and FDE to a given frame.
+ */
+public class DwarfFrameRegisterProvider implements IFrameRegisterProvider {
+
+ public static class InstructionState {
+ public EDCServicesTracker tracker;
+ /** Calculated register locations for the current frame */
+ public Map<Integer, AbstractRule> regRules;
+ /** Context for the current frame */
+ public IFrameDMContext context;
+ /** Accessor for registers from child frame */
+ public IFrameRegisters childRegisters;
+ /** Initial locations from CIE */
+ public Map<Integer, AbstractRule> initialRules;
+ /** Implicit stack for DW_CFA_remember_state and DW_CFA_restore_state*/
+ public Stack<Map<Integer, AbstractRule>> stateStack;
+
+ CFARegisterRule cfaRegRule = new CFARegisterRule(0, 0);
+
+ private final int addressSize;
+ public boolean cfaOffsetsAreReversed;
+
+ public InstructionState(EDCServicesTracker tracker,
+ IFrameDMContext context,
+ IFrameRegisters childRegisters,
+ FrameDescriptionEntry fde) {
+ this.tracker = tracker;
+ this.addressSize = fde.addressSize;
+ this.cfaOffsetsAreReversed = fde.getCIE().cfaOffsetsAreReversed;
+ this.regRules = new TreeMap<Integer, AbstractRule>();
+ this.stateStack = new Stack<Map<Integer,AbstractRule>>();
+ this.initialRules = new TreeMap<Integer, AbstractRule>();
+
+ this.context = context;
+ this.childRegisters = childRegisters;
+ //this.cfaValue = BigInteger.ZERO;
+ }
+
+ // pseudo register for current CFA
+ public static final int CFA = -1;
+
+ /**
+ * Read the current CFA.
+ * @return
+ */
+ public BigInteger readCFA() throws CoreException {
+ BigInteger regval = childRegisters.getRegister(cfaRegRule.regnum, addressSize);
+ return regval.add(BigInteger.valueOf(cfaOffsetsAreReversed ? -cfaRegRule.offset : cfaRegRule.offset));
+ }
+
+ /**
+ * Get the register which is the CFA base.
+ * @return register number
+ */
+ public int getCFARegister() {
+ return cfaRegRule.regnum;
+ }
+
+
+ };
+
+ public static abstract class AbstractRule {
+ public abstract IVariableLocation evaluate(InstructionState state) throws CoreException;
+ }
+
+ static class ErrorRule extends AbstractRule {
+ private String message;
+
+ public ErrorRule(String message) {
+ this.message = message;
+ }
+ @Override
+ public String toString() {
+ return "error: " + message;
+ }
+
+ @Override
+ public IVariableLocation evaluate(InstructionState state) throws CoreException {
+ throw EDCDebugger.newCoreException(message);
+ }
+ }
+
+ static class UndefinedRule extends AbstractRule {
+ private final int regnum;
+
+ public UndefinedRule(int regnum) {
+ this.regnum = regnum;
+ }
+ @Override
+ public String toString() {
+ return "R"+regnum+": undefined";
+ }
+ @Override
+ public IVariableLocation evaluate(InstructionState state) throws CoreException {
+ return null;
+ }
+ }
+
+ static class SameValueRule extends AbstractRule {
+ private final int regnum;
+
+ public SameValueRule(int regnum) {
+ this.regnum = regnum;
+ }
+ @Override
+ public String toString() {
+ return "R"+regnum+": same value";
+ }
+ @Override
+ public IVariableLocation evaluate(InstructionState state) throws CoreException {
+ return new ValueVariableLocation(state.childRegisters.getRegister(regnum, state.addressSize));
+ }
+ }
+
+ static class OffsetRule extends AbstractRule {
+ private final long offset;
+
+ public OffsetRule(long offset) {
+ this.offset = offset;
+ }
+ @Override
+ public String toString() {
+ return "offset("+offset+")";
+ }
+ @Override
+ public IVariableLocation evaluate(InstructionState state) throws CoreException {
+ BigInteger cfa = state.readCFA();
+ return new MemoryVariableLocation(state.tracker, state.context,
+ cfa.add(BigInteger.valueOf(offset)), true);
+ }
+ }
+ static class ValueOffsetRule extends AbstractRule {
+ private final long offset;
+
+ public ValueOffsetRule(long offset) {
+ this.offset = offset;
+ }
+ @Override
+ public String toString() {
+ return "val_offset("+offset+")";
+ }
+ @Override
+ public IVariableLocation evaluate(InstructionState state) throws CoreException {
+ BigInteger cfa = state.readCFA();
+ return new ValueVariableLocation(cfa.add(BigInteger.valueOf(offset)));
+ }
+ }
+ static class RegisterRule extends AbstractRule {
+ private final int regnum;
+
+ public RegisterRule(int regnum) {
+ this.regnum = regnum;
+ }
+ @Override
+ public String toString() {
+ return "register("+regnum+")";
+ }
+ @Override
+ public IVariableLocation evaluate(InstructionState state) throws CoreException {
+ return new RegisterVariableLocation(state.tracker, state.context, null, regnum);
+ }
+ }
+
+ static class CFARegisterRule extends AbstractRule {
+ private int regnum;
+ private long offset;
+
+ public CFARegisterRule(int regnum, long offset) {
+ this.regnum = regnum;
+ this.offset = offset;
+ }
+ @Override
+ public String toString() {
+ return "["+regnum+"]"+(offset < 0 ? "-" : "+")+Math.abs(offset);
+ }
+ @Override
+ public IVariableLocation evaluate(InstructionState state) throws CoreException {
+ return new RegisterOffsetVariableLocation(state.tracker, state.context, null, regnum, offset);
+ }
+ }
+ private static ErrorRule UNIMPLEMENTED = new ErrorRule(
+ "unimplemented support for reading this location");
+
+
+ /** The base class for instructions
+ */
+ public static abstract class AbstractInstruction {
+
+ public AbstractInstruction() {
+ }
+
+ /**
+ * Apply this instruction to the state.
+ * @param state
+ * @throws CoreException
+ */
+ abstract public void applyInstruction(InstructionState state) throws CoreException;
+ }
+
+ /** The base class for rules that apply to registers
+ */
+ public static abstract class AbstractRegisterInstruction extends AbstractInstruction {
+ protected final int thereg;
+
+ public AbstractRegisterInstruction(int thereg) {
+ super();
+ this.thereg = thereg;
+ }
+
+ @Override
+ public String toString() {
+ return "R"+thereg;
+ }
+ }
+
+ public static class NopInstruction extends AbstractInstruction {
+
+ public NopInstruction() {
+ super();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.AbstractRegisterRule#toString()
+ */
+ @Override
+ public String toString() {
+ return "nop";
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.AbstractRegisterRule#applyRule(org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext, java.util.Map, java.util.Map)
+ */
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+
+ }
+
+ }
+ /** CFA=RN+o */
+ public static class DefCFAInstruction extends AbstractInstruction {
+ long regnum;
+ long offset;
+ public DefCFAInstruction(long regnum, long offset) {
+ this.regnum = regnum;
+ }
+ @Override
+ public String toString() {
+ return "CFA := R"+regnum + " + " + offset;
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.cfaRegRule.regnum = (int) regnum;
+ state.cfaRegRule.offset = offset;
+ }
+ }
+
+ /** CFA=RN (+o) */
+ public static class DefCFARegisterInstruction extends AbstractInstruction {
+ long regnum;
+ public DefCFARegisterInstruction(long regnum) {
+ this.regnum = regnum;
+ }
+ @Override
+ public String toString() {
+ return "CFA register := R"+regnum;
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.cfaRegRule.regnum = (int) regnum;
+ }
+ }
+
+
+ /** CFA=(RN) +o */
+ public static class DefCFAOffsetInstruction extends AbstractInstruction {
+ long offset;
+ public DefCFAOffsetInstruction(long offset) {
+ this.offset = offset;
+ }
+ @Override
+ public String toString() {
+ return "CFA offset := "+offset;
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.cfaRegRule.offset = offset;
+ }
+ }
+
+
+ public static class DefCFAExpressionInstruction extends AbstractInstruction {
+ IStreamBuffer expression;
+ public DefCFAExpressionInstruction(IStreamBuffer expression) {
+ this.expression = expression;
+ }
+ @Override
+ public String toString() {
+ return "CFA := expression";
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ throw EDCDebugger.newCoreException("CFA expression not implemented");
+ }
+ }
+
+ public static class SameValueInstruction extends AbstractRegisterInstruction {
+ public SameValueInstruction(int thereg) {
+ super(thereg);
+ }
+ @Override
+ public String toString() {
+ return super.toString() + "same value";
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.regRules.put(thereg, new SameValueRule(thereg));
+ }
+ }
+
+ public static class UndefinedInstruction extends AbstractRegisterInstruction {
+ public UndefinedInstruction(int thereg) {
+ super(thereg);
+ }
+ @Override
+ public String toString() {
+ return super.toString() + "undefined";
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.regRules.put(thereg, new UndefinedRule(thereg));
+ }
+ }
+
+ public static class OffsetInstruction extends AbstractRegisterInstruction {
+ long offset;
+
+ public OffsetInstruction(int thereg, long offset) {
+ super(thereg);
+ this.offset = offset;
+ }
+ @Override
+ public String toString() {
+ return super.toString() + "@ CFA + " + offset;
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.regRules.put(thereg, new OffsetRule(offset));
+ }
+ }
+
+ public static class ValueOffsetInstruction extends AbstractRegisterInstruction {
+ long offset;
+ public ValueOffsetInstruction(int thereg, long offset) {
+ super(thereg);
+ this.offset = offset;
+ }
+ @Override
+ public String toString() {
+ return super.toString() + "CFA + " + offset;
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.regRules.put(thereg, new ValueOffsetRule(offset));
+ }
+ }
+
+ public static class RegisterInstruction extends AbstractRegisterInstruction {
+ int regnum;
+ public RegisterInstruction(int thereg, int regnum) {
+ super(thereg);
+ this.regnum = regnum;
+ }
+ @Override
+ public String toString() {
+ return super.toString() + "copy R"+regnum;
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ if (state.regRules.containsKey(regnum))
+ state.regRules.put(thereg, state.regRules.get(regnum));
+ else
+ state.regRules.put(thereg, new UndefinedRule(regnum));
+ }
+ }
+
+ public static class ExpressionInstruction extends AbstractRegisterInstruction {
+ IStreamBuffer expression;
+ public ExpressionInstruction(int thereg, IStreamBuffer expression) {
+ super(thereg);
+ this.expression = expression;
+ }
+ @Override
+ public String toString() {
+ return super.toString() + "@ expression";
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.regRules.put(thereg, UNIMPLEMENTED);
+ }
+ }
+ public static class ValueExpressionInstruction extends AbstractRegisterInstruction {
+ IStreamBuffer expression;
+ public ValueExpressionInstruction(int thereg, IStreamBuffer expression) {
+ super(thereg);
+ this.expression = expression;
+ }
+ @Override
+ public String toString() {
+ return super.toString() + " expression";
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.regRules.put(thereg, UNIMPLEMENTED);
+ }
+ }
+ public static class RestoreInstruction extends AbstractRegisterInstruction {
+ public RestoreInstruction(int thereg) {
+ super(thereg);
+ }
+ @Override
+ public String toString() {
+ return super.toString() + " restore";
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ if (state.initialRules.containsKey(thereg))
+ state.regRules.put(thereg, state.initialRules.get(thereg));
+ else
+ state.regRules.remove(thereg);
+ }
+ }
+
+ public static class RememberStateInstruction extends AbstractInstruction {
+ public RememberStateInstruction() {
+ super();
+ }
+ @Override
+ public String toString() {
+ return "remember state";
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.stateStack.push(new TreeMap<Integer, AbstractRule>(state.regRules));
+ }
+ }
+ public static class RestoreStateInstruction extends AbstractInstruction {
+ public RestoreStateInstruction() {
+ super();
+ }
+ @Override
+ public String toString() {
+ return "restore state";
+ }
+ @Override
+ public void applyInstruction(InstructionState state) throws CoreException {
+ state.regRules.clear();
+ state.regRules.putAll(state.stateStack.pop());
+ }
+ }
+
+
+ /**
+ * A "CIE" from the .debug_frame section.
+ */
+ public static class CommonInformationEntry {
+
+ final long codeAlignmentFactor;
+ final long dataAlignmentFactor;
+ final int version;
+ final int returnAddressRegister;
+ final int addressSize;
+ final IStreamBuffer instructions;
+ private List<AbstractInstruction> initialLocations;
+ private CoreException initialLocationsError;
+ private boolean cfaOffsetSfIsFactored;
+ private boolean cfaOffsetsAreReversed;
+
+ public CommonInformationEntry(long codeAlignmentFactor,
+ long dataAlignmentFactor, int returnAddressRegister,
+ int version,
+ IStreamBuffer instructions,
+ int addressSize,
+ String producer, String augmentation) {
+
+ this.codeAlignmentFactor = codeAlignmentFactor;
+ this.dataAlignmentFactor = dataAlignmentFactor;
+ this.returnAddressRegister = returnAddressRegister;
+ this.version = version;
+ this.instructions = instructions;
+ this.addressSize = addressSize;
+
+ checkAugmentations(producer, augmentation);
+ }
+
+ /**
+ * Handle augmentations we find.
+ */
+ private void checkAugmentations(String producer, String augmentation) {
+ // RVCT has bugs with the frame info in DWARF 1/2 and some DWARF 3
+
+ if (augmentation.startsWith("armcc") && augmentation.contains("+")) {
+ // this means bugs are fixed
+
+ } else {
+ if (producer != null && producer.contains("RVCT")) { //$NON-NLS-1$
+ if (version == 1) {
+ cfaOffsetSfIsFactored = true;
+ cfaOffsetsAreReversed = true;
+ }
+
+ if (version == 3) {
+ cfaOffsetsAreReversed = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the rules defining initial locations for all the registers
+ * @return list of instructions
+ */
+ public List<AbstractInstruction> getInitialLocations(DwarfDebugInfoProvider provider) throws CoreException {
+ if (initialLocations == null) {
+ try {
+ initialLocations = parseInitialLocations(provider);
+ } catch (CoreException e) {
+ initialLocationsError = e;
+ }
+ }
+ if (initialLocationsError != null)
+ throw initialLocationsError;
+ return initialLocations;
+ }
+
+ /**
+ * Parse the instructions for calculating the initial locations for all the registers
+ * @return map of register to rule
+ */
+ public List<AbstractInstruction> parseInitialLocations(DwarfDebugInfoProvider provider) throws CoreException {
+ List<AbstractInstruction> instrs = new ArrayList<AbstractInstruction>();
+
+ IStreamBuffer buffer = instructions.wrapSubsection(instructions.capacity());
+
+ buffer.position(0);
+
+ try {
+ while (buffer.hasRemaining()) {
+ int opcode = buffer.get() & 0xff;
+ AbstractInstruction inst = parseInstruction(opcode, buffer, provider, addressSize, dataAlignmentFactor);
+ if (inst != null)
+ instrs.add(inst);
+ }
+ } catch (Exception e) {
+ throw EDCDebugger.newCoreException("Malformed data at " + buffer, e);
+ }
+
+
+ return instrs;
+ }
+
+ public AbstractInstruction parseInstruction(int opcode, IStreamBuffer buffer,
+ DwarfDebugInfoProvider provider, int addressSize, long dataAlignmentFactor) throws IOException {
+ int reg;
+ long offset;
+ IStreamBuffer expr;
+
+ //
+ // CFA DEFINITION INSTRUCTIONS
+ //
+ if (opcode >= DwarfConstants.DW_CFA_offset && opcode < DwarfConstants.DW_CFA_offset + 0x40) {
+ reg = opcode - DwarfConstants.DW_CFA_offset;
+ offset = DwarfInfoReader.read_unsigned_leb128(buffer) * dataAlignmentFactor;
+ return new OffsetInstruction(reg, offset);
+ } else if (opcode >= DwarfConstants.DW_CFA_restore && opcode < DwarfConstants.DW_CFA_restore + 0x40) {
+ reg = opcode - DwarfConstants.DW_CFA_restore;
+ return new RestoreInstruction(reg);
+ } else {
+ switch (opcode) {
+ case DwarfConstants.DW_CFA_nop:
+ // ignore
+ return new NopInstruction();
+ case DwarfConstants.DW_CFA_def_cfa:
+ reg = readRegister(buffer);
+ offset = DwarfInfoReader.read_unsigned_leb128(buffer);
+ if (cfaOffsetSfIsFactored) {
+ offset *= dataAlignmentFactor;
+ }
+ return new DefCFAInstruction(reg, offset);
+ case DwarfConstants.DW_CFA_def_cfa_sf:
+ reg = readRegister(buffer);
+ offset = DwarfInfoReader.read_signed_leb128(buffer) * dataAlignmentFactor;
+ return new DefCFAInstruction(reg, offset);
+ case DwarfConstants.DW_CFA_def_cfa_register: {
+ reg = readRegister(buffer);
+ return new DefCFARegisterInstruction(reg);
+ }
+ case DwarfConstants.DW_CFA_def_cfa_offset: {
+ offset = DwarfInfoReader.read_unsigned_leb128(buffer); // non-factored, usually
+ if (cfaOffsetSfIsFactored) {
+ offset *= dataAlignmentFactor;
+ }
+ return new DefCFAOffsetInstruction(offset);
+ }
+ case DwarfConstants.DW_CFA_def_cfa_offset_sf: {
+ offset = DwarfInfoReader.read_signed_leb128(buffer) * dataAlignmentFactor;
+ return new DefCFAOffsetInstruction(offset);
+ }
+ case DwarfConstants.DW_CFA_def_cfa_expression: {
+ byte form = buffer.get();
+ expr = readExpression(form, addressSize, provider, buffer);
+ return new DefCFAExpressionInstruction(expr);
+ }
+ }
+ }
+
+ //
+ // REGISTER RULE INSTRUCTIONS
+ //
+ if (opcode >= DwarfConstants.DW_CFA_offset && opcode < DwarfConstants.DW_CFA_offset + 0x40) {
+ reg = opcode - DwarfConstants.DW_CFA_offset;
+ offset = DwarfInfoReader.read_unsigned_leb128(buffer) * dataAlignmentFactor;
+ return new OffsetInstruction(reg, offset);
+ } else if (opcode >= DwarfConstants.DW_CFA_restore && opcode < DwarfConstants.DW_CFA_restore + 0x40) {
+ reg = opcode - DwarfConstants.DW_CFA_restore;
+ return new RestoreInstruction(reg);
+ } else {
+ switch (opcode) {
+ case DwarfConstants.DW_CFA_nop:
+ break;
+
+ case DwarfConstants.DW_CFA_undefined:
+ reg = readRegister(buffer);
+ return new UndefinedInstruction(reg);
+ case DwarfConstants.DW_CFA_same_value:
+ reg = readRegister(buffer);
+ return new SameValueInstruction(reg);
+ case DwarfConstants.DW_CFA_offset_extended:
+ reg = readRegister(buffer);
+ offset = DwarfInfoReader.read_unsigned_leb128(buffer) * dataAlignmentFactor;
+ return new OffsetInstruction(reg, offset);
+ case DwarfConstants.DW_CFA_offset_extended_sf:
+ reg = readRegister(buffer);
+ offset = DwarfInfoReader.read_signed_leb128(buffer) * dataAlignmentFactor;
+ return new OffsetInstruction(reg, offset);
+ case DwarfConstants.DW_CFA_val_offset:
+ reg = readRegister(buffer);
+ offset = DwarfInfoReader.read_unsigned_leb128(buffer) * dataAlignmentFactor;
+ return new ValueOffsetInstruction(reg, offset);
+ case DwarfConstants.DW_CFA_val_offset_sf:
+ reg = readRegister(buffer);
+ offset = DwarfInfoReader.read_signed_leb128(buffer) * dataAlignmentFactor;
+ return new ValueOffsetInstruction(reg, offset);
+ case DwarfConstants.DW_CFA_register: {
+ reg = readRegister(buffer);
+ int otherReg = readRegister(buffer);
+ return new RegisterInstruction(reg, otherReg);
+ }
+ case DwarfConstants.DW_CFA_expression: {
+ reg = readRegister(buffer);
+ byte form = buffer.get();
+ expr = readExpression(form, addressSize, provider, buffer);
+ return new ExpressionInstruction(reg, expr);
+ }
+ case DwarfConstants.DW_CFA_val_expression: {
+ reg = readRegister(buffer);
+ byte form = buffer.get();
+ expr = readExpression(form, addressSize, provider, buffer);
+ return new ValueExpressionInstruction(reg, expr);
+ }
+ case DwarfConstants.DW_CFA_restore_extended:
+ reg = readRegister(buffer);
+ return new RestoreInstruction(reg);
+ case DwarfConstants.DW_CFA_remember_state:
+ return new RememberStateInstruction();
+ case DwarfConstants.DW_CFA_restore_state:
+ return new RestoreStateInstruction();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param buffer
+ * @return
+ * @throws IOException
+ */
+ private int readRegister(IStreamBuffer buffer) throws IOException {
+ return (int) DwarfInfoReader.read_unsigned_leb128(buffer);
+ }
+
+ private IStreamBuffer readExpression(byte form, int addressSize,
+ DwarfDebugInfoProvider provider, IStreamBuffer buffer) {
+ AttributeValue value = new AttributeValue(form, buffer, (byte) addressSize, null);
+ return new MemoryStreamBuffer(value.getValueAsBytes(),
+ provider.getExecutableSymbolicsReader().getByteOrder());
+ }
+
+ }
+
+
+ /**
+ * A "FDE" entry from the .debug_frame section.
+ */
+ public static class FrameDescriptionEntry {
+
+ final Long fdePtr;
+ final Long ciePtr;
+ final int addressSize;
+ private final IStreamBuffer instructions;
+
+ private final long low, high;
+
+ private CommonInformationEntry cie;
+ private CoreException parseException;
+ private TreeMap<Entry, List<AbstractInstruction>> instructionMap;
+
+ public CommonInformationEntry getCIE() {
+ return cie;
+ }
+
+ public void setCIE(CommonInformationEntry cie) {
+ this.cie = cie;
+ }
+
+ public FrameDescriptionEntry(long fdePtr, long ciePtr, long low, long high,
+ IStreamBuffer instructions, int addressSize) {
+ this.ciePtr = ciePtr;
+ this.fdePtr = fdePtr;
+ this.instructions = instructions;
+ this.addressSize = addressSize;
+ this.low = low;
+ this.high = high;
+ }
+
+ @Override
+ public String toString() {
+ return "FDE at " + instructions + " with CIE " + (cie != null ? cie : ciePtr);
+ }
+
+ /**
+ * Get the register for this frame which denotes the return address
+ * @return register number
+ */
+ public int getReturnAddressRegister() {
+ return cie != null ? cie.returnAddressRegister : 0;
+ }
+
+ /**
+ * Get the instruction map for the FDE
+ * @param provider
+ * @return
+ */
+ public TreeMap<IRangeList.Entry, List<AbstractInstruction>> getInstructions(
+ DwarfDebugInfoProvider provider) throws CoreException {
+ if (instructionMap == null) {
+ try {
+ instructionMap = parseRules(provider);
+ } catch (CoreException e) {
+ parseException = e;
+ }
+ }
+ if (parseException != null)
+ throw parseException;
+ return instructionMap;
+ }
+
+ private TreeMap<Entry, List<AbstractInstruction>> parseRules(
+ DwarfDebugInfoProvider provider) throws CoreException {
+ TreeMap<IRangeList.Entry, List<AbstractInstruction>> rules = new TreeMap<IRangeList.Entry, List<AbstractInstruction>>();
+
+ IStreamBuffer buffer = instructions.wrapSubsection(instructions.capacity());
+
+ buffer.position(0);
+
+ // adjust range and store row as we see set_loc/advance_loc instructions
+ long current = low;
+ List<AbstractInstruction> row = new ArrayList<AbstractInstruction>();
+
+ try {
+ while (buffer.hasRemaining()) {
+ int opcode = buffer.get() & 0xff;
+
+ //
+ // ROW CREATION INSTRUCTIONS
+ //
+ long offset = -1;
+ if (opcode >= DwarfConstants.DW_CFA_advance_loc && opcode < DwarfConstants.DW_CFA_advance_loc + 0x40) {
+ offset = (opcode - DwarfConstants.DW_CFA_advance_loc) * cie.codeAlignmentFactor;
+ } else {
+ switch (opcode) {
+ case DwarfConstants.DW_CFA_set_loc:
+ offset = DwarfInfoReader.readAddress(buffer, addressSize) - current;
+ break;
+ case DwarfConstants.DW_CFA_advance_loc1:
+ offset = (buffer.get() & 0xff) * cie.codeAlignmentFactor;
+ break;
+ case DwarfConstants.DW_CFA_advance_loc2:
+ offset = (buffer.getShort() & 0xffff) * cie.codeAlignmentFactor;
+ break;
+ case DwarfConstants.DW_CFA_advance_loc4:
+ offset = (buffer.getInt() & 0xffffffff) * cie.codeAlignmentFactor;
+ break;
+ }
+ }
+
+ if (offset > 0) {
+ rules.put(new IRangeList.Entry(current, current + offset), row);
+ current += offset;
+ row = new ArrayList<DwarfFrameRegisterProvider.AbstractInstruction>();
+ continue;
+ }
+
+ //
+ // REGISTER RULE INSTRUCTIONS
+ //
+
+ AbstractInstruction instr = cie.parseInstruction(opcode, buffer, provider, addressSize, cie.dataAlignmentFactor);
+ if (instr != null) {
+ row.add(instr);
+ } else {
+ assert(false);
+ throw EDCDebugger.newCoreException("Unimplemented opcode " + opcode + " at " + buffer);
+ }
+ }
+ } catch (CoreException e) {
+ throw e;
+ } catch (Exception e) {
+ throw EDCDebugger.newCoreException("error parsing FDE rules at " + buffer, e);
+ }
+
+ if (!row.isEmpty()) {
+ // finish instruction stream
+ rules.put(new IRangeList.Entry(current, high), row);
+ }
+ return rules;
+ }
+ }
+
+ private DwarfDebugInfoProvider provider;
+
+ public DwarfFrameRegisterProvider(
+ DwarfDebugInfoProvider provider) {
+ this.provider = provider;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IFrameRegisterProvider#dispose()
+ */
+ public void dispose() {
+ provider = null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.IFrameRegisterProvider#getFrameRegisters(org.eclipse.cdt.dsf.service.DsfSession, org.eclipse.cdt.dsf.service.DsfServicesTracker, org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext)
+ */
+ public IFrameRegisters getFrameRegisters(DsfSession session,
+ EDCServicesTracker tracker,
+ IFrameDMContext context) throws CoreException {
+ Registers registers = tracker.getService(Registers.class);
+ IStack stack = tracker.getService(IStack.class);
+ ExecutionDMC exeDMC = DMContexts.getAncestorOfType(context, ExecutionDMC.class);
+
+ if (registers == null || exeDMC == null || !(context instanceof StackFrameDMC))
+ throw EDCDebugger.newCoreException("cannot read frame");
+
+ StackFrameDMC stackFrame = (StackFrameDMC) context;
+
+ if (stackFrame.getLevel() == 0) {
+ // shouldn't actually get here, but whatevah
+ return new org.eclipse.cdt.debug.edc.services.Stack.CurrentFrameRegisters(exeDMC, registers);
+ }
+
+ // get the child frame's registers
+ StackFrameDMC childFrame = getChildFrame(session, stack, exeDMC, stackFrame);
+ if (childFrame == null)
+ return null;
+
+ IFrameRegisters childRegisters = childFrame.getFrameRegisters();
+
+ IAddress linkaddress = childFrame.getInstructionPtrAddress();
+ IEDCModuleDMContext module = childFrame.getModule();
+ if (module != null) {
+ linkaddress = module.toLinkAddress(linkaddress);
+ }
+
+ FrameDescriptionEntry currentFrameEntry = provider.findFrameDescriptionEntry(linkaddress);
+ if (currentFrameEntry == null)
+ return null;
+
+ return new DwarfFrameRegisters(tracker, childFrame, currentFrameEntry, childRegisters, provider);
+ }
+
+ /**
+ * Find the frame called by stackFrame
+ */
+ private StackFrameDMC getChildFrame(DsfSession session, IStack stack,
+ ExecutionDMC exeDMC, StackFrameDMC stackFrame) throws CoreException {
+ StackFrameDMC childFrame = stackFrame.getCalledFrame();
+ return childFrame;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.math.BigInteger;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.symbols.ValueVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.AbstractInstruction;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.AbstractRule;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.FrameDescriptionEntry;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.InstructionState;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.services.IFrameRegisters;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.symbols.IRangeList.Entry;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This class wraps the cached values from reading unwind information from the CIE and FDE
+ * records along each frame of the call stack to a given stack frame.
+ * This must be recreated on each context suspend.
+ */
+public class DwarfFrameRegisters implements IFrameRegisters {
+
+ private final EDCServicesTracker tracker;
+ private final StackFrameDMC context;
+ private final IFrameRegisters childRegisters;
+
+ // holds BigInteger or CoreException
+ private Map<Integer, Object> cachedRegisters = new TreeMap<Integer, Object>();
+ private final FrameDescriptionEntry fde;
+ private final DwarfDebugInfoProvider provider;
+ private Map<Integer, AbstractRule> frameRegisterRules;
+ private InstructionState state;
+
+ /**
+ * @param provider
+ *
+ */
+ public DwarfFrameRegisters(EDCServicesTracker tracker,
+ StackFrameDMC context,
+ FrameDescriptionEntry fde,
+ IFrameRegisters childRegisters, DwarfDebugInfoProvider provider) {
+ if (childRegisters == null)
+ throw new NullPointerException();
+ this.provider = provider;
+ this.tracker = tracker;
+ this.context = context;
+ this.fde = fde;
+ this.childRegisters = childRegisters;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IFrameRegisters#getRegister(int)
+ */
+ public BigInteger getRegister(int regnum, int bytes) throws CoreException {
+ BigInteger value = null;
+ CoreException exception = null;
+ Object object = cachedRegisters.get(regnum);
+ if (object == null) {
+ try {
+ IVariableLocation loc = getRegisterLocation(regnum);
+ if (loc == null)
+ throw EDCDebugger.newCoreException(MessageFormat.format(DwarfMessages.DwarfFrameRegisters_CannotReadRegister,
+ regnum, context.getInstructionPtrAddress().toHexAddressString()));
+ value = loc.readValue(bytes);
+ cachedRegisters.put(regnum, value);
+ } catch (CoreException e) {
+ exception = e;
+ cachedRegisters.put(regnum, exception);
+ }
+ } else {
+ if (object instanceof CoreException)
+ exception = (CoreException) object;
+ else
+ value = (BigInteger) object;
+ }
+
+ if (exception != null)
+ throw exception;
+
+ return value;
+ }
+
+
+ /**
+ * Calculate the location of a register for the current frame and PC.
+ * @param regnum
+ * @return location or <code>null</code> if undefined
+ * @throws CoreException in case of a problem calculating the location
+ */
+ private IVariableLocation getRegisterLocation(int regnum) throws CoreException {
+
+ if (frameRegisterRules == null) {
+ state = new InstructionState(tracker, context, childRegisters, fde);
+ frameRegisterRules = calculateFrameRegisterRules(state);
+ }
+
+ AbstractRule rule = frameRegisterRules.get(regnum);
+ if (rule == null) {
+ // Note: according to DWARF-3 spec, any register not mentioned here is undefined.
+ // But DWARF-2 producers didn't know what to do (or is it ABI-defined?),
+ // so for these, assume the current register for now
+ if (fde.getCIE().version < 3)
+ if (regnum == state.getCFARegister())
+ return new ValueVariableLocation(state.readCFA());
+ else
+ return new ValueVariableLocation(childRegisters.getRegister(regnum, fde.addressSize));
+ else
+ return null;
+ }
+
+ return rule.evaluate(state);
+ }
+
+
+ /**
+ * Create locations for every register in this frame.
+ * @param state
+ * @return mapping of register to location
+ * @throws CoreException
+ */
+ private Map<Integer, AbstractRule> calculateFrameRegisterRules(InstructionState state) throws CoreException {
+ if (fde.getCIE() == null)
+ throw EDCDebugger.newCoreException(MessageFormat.format(DwarfMessages.DwarfFrameRegisters_NoCommonInfoEntry, fde));
+
+ List<AbstractInstruction> initialLocationInstructions =
+ fde.getCIE().getInitialLocations(provider);
+
+ for (AbstractInstruction instr : initialLocationInstructions) {
+ instr.applyInstruction(state);
+ }
+
+ // Run through rules until we hit one past our range
+ TreeMap<Entry, List<AbstractInstruction>> fdeInstrs = fde.getInstructions(provider);
+
+ long currentPC = context.getModule().toLinkAddress(context.getInstructionPtrAddress()).getValue().longValue();
+
+ for (Map.Entry<Entry, List<AbstractInstruction>> instrEntry : fdeInstrs.entrySet()) {
+ Entry entry = instrEntry.getKey();
+ if (entry.low > currentPC) // execute all instructions <= PC
+ break;
+
+ try {
+ for (AbstractInstruction instr : instrEntry.getValue()) {
+ instr.applyInstruction(state);
+ }
+ } catch (Exception e) {
+ throw EDCDebugger.newCoreException(DwarfMessages.DwarfFrameRegisters_ErrorCalculatingLocation, e);
+ }
+ }
+
+ return state.regRules;
+ }
+
+
+ public void writeRegister(int regnum, int bytes, BigInteger value) throws CoreException {
+ CoreException exception = null;
+ try {
+ IVariableLocation loc = getRegisterLocation(regnum);
+ if (loc == null)
+ throw EDCDebugger.newCoreException(MessageFormat.format(DwarfMessages.DwarfFrameRegisters_CannotWriteRegister,
+ regnum, context.getInstructionPtrAddress().toHexAddressString()));
+ loc.writeValue(bytes, value);
+ cachedRegisters.put(regnum, value);
+ } catch (CoreException e) {
+ exception = e;
+ cachedRegisters.put(regnum, exception);
+ }
+
+ if (exception != null)
+ throw exception;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.FunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.core.runtime.IPath;
+
+public class DwarfFunctionScope extends FunctionScope implements IFunctionScope {
+ protected int declFileNum;
+
+ public DwarfFunctionScope(String name, IScope parent, IAddress lowAddress, IAddress highAddress,
+ ILocationProvider frameBaseLocationProvider) {
+ super(name, parent, lowAddress, highAddress, frameBaseLocationProvider);
+ }
+
+ /**
+ * Set the declaration file number entry from the line number table
+ * @param declFileNum
+ */
+ public void setDeclFileNum(int declFileNum) {
+ this.declFileNum = declFileNum;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.FunctionScope#getDeclFile()
+ */
+ @Override
+ public IPath getDeclFile() {
+ IPath file = super.getDeclFile();
+ if (file == null && declFileNum != 0) {
+ // ask the parent
+ IScope cu = getParent();
+ while (cu != null) {
+ if (cu instanceof DwarfCompileUnit) {
+ return ((DwarfCompileUnit) cu).getFileEntry(declFileNum);
+ }
+ cu = cu.getParent();
+ }
+ }
+ return file;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description:
+ *
+ */
+
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.TreeMap;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.MemoryStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.PathUtils;
+import org.eclipse.cdt.debug.edc.internal.symbols.ArrayBoundType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.CPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ClassType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ConstType;
+import org.eclipse.cdt.debug.edc.internal.symbols.Enumeration;
+import org.eclipse.cdt.debug.edc.internal.symbols.Enumerator;
+import org.eclipse.cdt.debug.edc.internal.symbols.FieldType;
+import org.eclipse.cdt.debug.edc.internal.symbols.FunctionScope;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
+import org.eclipse.cdt.debug.edc.internal.symbols.InheritanceType;
+import org.eclipse.cdt.debug.edc.internal.symbols.LexicalBlockScope;
+import org.eclipse.cdt.debug.edc.internal.symbols.LineEntry;
+import org.eclipse.cdt.debug.edc.internal.symbols.PointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ReferenceType;
+import org.eclipse.cdt.debug.edc.internal.symbols.Scope;
+import org.eclipse.cdt.debug.edc.internal.symbols.StructType;
+import org.eclipse.cdt.debug.edc.internal.symbols.SubroutineType;
+import org.eclipse.cdt.debug.edc.internal.symbols.TemplateParamType;
+import org.eclipse.cdt.debug.edc.internal.symbols.TypedefType;
+import org.eclipse.cdt.debug.edc.internal.symbols.UnionType;
+import org.eclipse.cdt.debug.edc.internal.symbols.VolatileType;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AbbreviationEntry;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.Attribute;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AttributeList;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AttributeValue;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.CompilationUnitHeader;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.ForwardTypeReference;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.PublicNameInfo;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.CommonInformationEntry;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.FrameDescriptionEntry;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.BaseExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglerEABI;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglingException;
+import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSection;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IRangeList;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IUnmangler;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.cdt.utils.Addr32;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * Handle restartable parsing of Dwarf information from a single module.
+ * This class may be instantiated multiple times to parse specific subsets
+ * of the Dwarf data. The {@link DwarfDebugInfoProvider}
+ * holds the global state of everything parsed so far.
+ */
+public class DwarfInfoReader {
+
+ private class BaseAndScopedNames {
+ public String baseName; // e.g., "bar"
+ public String nameWithScope; // e.g., "foo::bar"
+ }
+
+ private BaseAndScopedNames baseAndScopedNames = new BaseAndScopedNames();
+
+ // These are only for developer of the reader.
+ //
+ private static boolean DEBUG = false;
+ private static String dumpFileName = "C:\\temp\\_EDC_DwarfReaderDump.txt"; //$NON-NLS-1$
+
+ // TODO 64-bit Dwarf currently unsupported
+
+ /* Section names. */
+ public final static String DWARF_DEBUG_INFO = ".debug_info"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_RANGES = ".debug_ranges"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_ABBREV = ".debug_abbrev"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_ARANGES = ".debug_aranges"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_LINE = ".debug_line"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_FRAME = ".debug_frame"; //$NON-NLS-1$
+ public final static String DWARF_EH_FRAME = ".eh_frame"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_LOC = ".debug_loc"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_PUBNAMES = ".debug_pubnames"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_STR = ".debug_str"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_FUNCNAMES = ".debug_funcnames"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_TYPENAMES = ".debug_typenames"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_VARNAMES = ".debug_varnames"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_WEAKNAMES = ".debug_weaknames"; //$NON-NLS-1$
+ public final static String DWARF_DEBUG_MACINFO = ".debug_macinfo"; //$NON-NLS-1$
+
+ private Map<Long, Collection<LocationEntry>> locationEntriesByOffset = Collections.synchronizedMap(new HashMap<Long, Collection<LocationEntry>>());
+
+ // the target for all the reading
+ private DwarfDebugInfoProvider provider;
+
+ private IExecutableSymbolicsReader exeReader;
+ private DwarfModuleScope moduleScope;
+ private IPath symbolFilePath;
+
+ private IExecutableSection debugInfoSection;
+ private IExecutableSection publicNamesSection;
+ private CompilationUnitHeader currentCUHeader;
+
+ private Map<IType, IType> typeToParentMap = Collections.synchronizedMap(new HashMap<IType, IType>());
+ private IType currentParentType;
+ private Scope currentParentScope;
+ private DwarfCompileUnit currentCompileUnitScope;
+
+ private DwarfFileHelper fileHelper;
+
+ private RangeList codeRanges;
+
+ private ICPPBasicType voidType = null;
+
+ private IUnmangler unmangler;
+
+ // comparator for sorting and searching based on compilation unit low address
+ private static Comparator<DwarfCompileUnit> sComparatorByLowAddress = new Comparator<DwarfCompileUnit>() {
+ public int compare(DwarfCompileUnit o1, DwarfCompileUnit o2) {
+ return (o1.getLowAddress().compareTo(o2.getLowAddress()));
+ }};
+
+ /**
+ * Create a reader for the provider. This constructor and any methods
+ * on this reader class will incrementally update the provider.
+ * @param provider
+ */
+ public DwarfInfoReader(DwarfDebugInfoProvider provider) {
+ this.provider = provider;
+ exeReader = provider.getExecutableSymbolicsReader();
+ if (exeReader instanceof BaseExecutableSymbolicsReader)
+ unmangler = ((BaseExecutableSymbolicsReader) exeReader).getUnmangler();
+ if (unmangler == null)
+ unmangler = new UnmanglerEABI();
+
+ symbolFilePath = provider.getSymbolFile();
+ fileHelper = provider.fileHelper;
+ moduleScope = (DwarfModuleScope) provider.getModuleScope();
+ debugInfoSection = exeReader.findExecutableSection(DWARF_DEBUG_INFO);
+ publicNamesSection = exeReader.findExecutableSection(DWARF_DEBUG_PUBNAMES);
+
+ codeRanges = getCodeRanges();
+ }
+
+ private String unmangle(String name) {
+ if (!unmangler.isMangled(name))
+ return name;
+
+ try {
+ name = unmangler.unmangle(name);
+ } catch (UnmanglingException ue) {
+ }
+
+ return name;
+ }
+
+
+ private String unmangleType(String name) {
+ if (!unmangler.isMangled(name))
+ return name;
+
+ try {
+ name = unmangler.unmangleType(name);
+ } catch (UnmanglingException ue) {
+ }
+
+ return name;
+ }
+
+ /**
+ * @return
+ */
+ private RangeList getCodeRanges() {
+ RangeList codeRanges = new RangeList();
+ for (ISection section : exeReader.getSections()) {
+ if (section.getProperties().get(ISection.PROPERTY_NAME).equals(ISection.NAME_TEXT)) {
+ long start = section.getLinkAddress().getValue().longValue();
+ long size = section.getSize();
+ codeRanges.addRange(start, start + size);
+ }
+ }
+ return codeRanges;
+ }
+
+ protected IStreamBuffer getDwarfSection(String sectionName) {
+ // the exe reader and section already handle caching this
+ IStreamBuffer buffer = null;
+ IExecutableSection section = exeReader.findExecutableSection(sectionName);
+ if (section != null) {
+ buffer = section.getBuffer();
+ }
+ return buffer;
+ }
+
+ /**
+ * Parse top-level debugging information about compilation units and globally
+ * visible objects, but do not expand or gather data about other objects in
+ * compilation units.
+ */
+ public void parseInitial() {
+ Job parseInitialJob = new Job(DwarfMessages.DwarfInfoReader_ReadingSymbolInfo + symbolFilePath) {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceInitialParseFor + symbolFilePath)); }
+ synchronized (provider) {
+ parseCUDebugInfo(monitor);
+ parsePublicNames();
+ }
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceFinishedInitialParse)); }
+ return Status.OK_STATUS;
+ }
+ };
+
+ try {
+ parseInitialJob.schedule();
+ parseInitialJob.join();
+ } catch (InterruptedException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ /**
+ * Parse all compilation units for addresses
+ *
+ * @param includeCUWithoutCode
+ * whether to parse compile units without code. For variable
+ * info, we need to look into those CUs, whereas for scope
+ * info, we don't.
+ */
+ public void parseForAddresses(boolean includeCUWithoutCode) {
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceAddressesParseFor + symbolFilePath)); }
+ synchronized (provider) {
+ for (DwarfCompileUnit compileUnit : provider.compileUnits) {
+ if (DEBUG) {
+ // For internal check.
+ if (compileUnit.getHighAddress().isZero())
+ assert(compileUnit.getChildren().size() == 0);
+ else
+ assert(compileUnit.getChildren().size() >= 0);
+ }
+
+ if (includeCUWithoutCode || // parse every CU
+ ! compileUnit.getHighAddress().isZero()) // parse only those with code.
+ {
+ parseCompilationUnitForAddresses(compileUnit);
+ }
+ }
+ }
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceFinishedAddressesParse)); }
+
+ moduleScope.fixupRanges(Addr32.ZERO);
+
+ if (DEBUG) {
+ dumpSymbols();
+ }
+
+ }
+
+ /**
+ * Parse compilation unit corresponding to address
+ *
+ * @param linkAddress
+ * address in a compile unit that contains code
+ */
+ public void parseForAddress(IAddress linkAddress) {
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceAddressParseFor + symbolFilePath)); }
+
+ // find compilation unit containing address, and parse it
+ synchronized (provider) {
+ DwarfCompileUnit cu = new DwarfCompileUnit(provider, null, null, linkAddress, linkAddress, null, false, null);
+ int index = Collections.binarySearch (provider.sortedCompileUnitsWithCode, cu, sComparatorByLowAddress);
+
+ if (index >= 0) {
+ cu = provider.sortedCompileUnitsWithCode.get(index);
+ parseCompilationUnitForAddresses(cu);
+ } else if (index < -1 && -index - 2 < provider.sortedCompileUnitsWithCode.size()) {
+ cu = provider.sortedCompileUnitsWithCode.get(-index - 2);
+ if (cu.getLowAddress().compareTo(linkAddress) <= 0 && cu.getHighAddress().compareTo(linkAddress) >= 0)
+ parseCompilationUnitForAddresses(cu);
+ } else {
+ return;
+ }
+
+ if (moduleScope.getLowAddress().compareTo(cu.getLowAddress()) > 0)
+ moduleScope.setLowAddress(cu.getLowAddress());
+ if (moduleScope.getHighAddress().compareTo(cu.getHighAddress()) < 0)
+ moduleScope.setHighAddress(cu.getHighAddress());
+ }
+
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceFinishedAddressParse)); }
+ }
+
+ /**
+ * Parse names in the .debug_pubnames section
+ */
+ private void parsePublicNames() {
+
+ if (publicNamesSection == null || debugInfoSection == null) { // no public names and/or debug info
+ return;
+ }
+
+ IStreamBuffer bufferPublicNames = publicNamesSection.getBuffer();
+ if (bufferPublicNames == null)
+ return;
+
+ IStreamBuffer bufferDebuginfo = debugInfoSection.getBuffer();
+ if (bufferDebuginfo == null)
+ return;
+
+ long fileIndex = 0;
+ long fileEndIndex = bufferPublicNames.capacity();
+
+ // parse all the sets in the .debug_pubnames section
+ while (fileIndex < fileEndIndex) {
+ fileIndex = parsePublicNamesSet(bufferPublicNames, fileIndex, bufferDebuginfo);
+ }
+ }
+
+ /**
+ * Parse one set of global objects and functions
+ */
+ private long parsePublicNamesSet(IStreamBuffer bufferPublicNames, long fileIndex, IStreamBuffer bufferDebugInfo) {
+ bufferPublicNames.position(fileIndex);
+
+ // get the set's data length
+ int setLength = bufferPublicNames.getInt();
+
+ // get the entire set
+
+ IStreamBuffer dataPublicNames = bufferPublicNames.wrapSubsection(setLength);
+
+ // get header info for set of public names
+ // skip over Dwarf version
+ dataPublicNames.position(2);
+ int debugInfoOffset = dataPublicNames.getInt();
+ int debugInfoLength = dataPublicNames.getInt();
+
+ try {
+ // read the entire compile unit
+ bufferDebugInfo.position(debugInfoOffset);
+
+ IStreamBuffer dataInfoBytes = bufferDebugInfo.wrapSubsection(debugInfoLength);
+
+ CompilationUnitHeader header = provider.debugOffsetsToCompileUnits.get(Long.valueOf(debugInfoOffset));
+
+ // get stored abbrev table, or read and parse an abbrev table
+ Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(header.abbreviationOffset);
+
+ // read names and corresponding types
+ // ignore the 4 bytes of 0 at the end of the set
+ while (dataPublicNames.remaining() > 4) {
+ // read the object offset and name
+ int objectOffset = dataPublicNames.getInt();
+ String name = readString(dataPublicNames);
+
+ // read the type of object
+ dataInfoBytes.position(objectOffset);
+ long code = read_unsigned_leb128(dataInfoBytes);
+ AbbreviationEntry entry = abbrevs.get(new Long(code));
+
+ // ignore empty names, names without debug info, and
+ // compiler-generated special symbols
+ if (entry != null && name.length() > 0 && !name.startsWith("<")) {
+ // if the name is mangled, unmangle it
+ name = unmangle(name);
+
+ String baseName = name;
+ int baseStart = name.lastIndexOf("::"); //$NON-NLS-1$
+ if (baseStart != -1)
+ baseName = name.substring(baseStart + 2);
+ if (entry.tag == DwarfConstants.DW_TAG_variable) {
+ List<PublicNameInfo> variables = provider.publicVariables.get(baseName);
+ if (variables == null) {
+ variables = new ArrayList<PublicNameInfo>();
+ }
+ variables.add(new PublicNameInfo(name, header, entry.tag));
+ provider.publicVariables.put(baseName, variables);
+ } else if (entry.tag == DwarfConstants.DW_TAG_subprogram) {
+ List<PublicNameInfo> functions = provider.publicFunctions.get(baseName);
+ if (functions == null) {
+ functions = new ArrayList<PublicNameInfo>();
+ functions.add(new PublicNameInfo(name, header, entry.tag));
+ } else {
+ // we don't store debug info offsets, so polymorphic functions for a compilation
+ // unit have identical PublicNameInfo fields; throw all but one away
+ ArrayList<PublicNameInfo> arrayList = (ArrayList<PublicNameInfo>)functions;
+ boolean found = false;
+ for (int i = arrayList.size() - 1;
+ !found && (i >= 0) && (arrayList.get(i).cuHeader == header); i--)
+ found = arrayList.get(i).nameWithNameSpace.equals(name);
+ if (!found)
+ functions.add(new PublicNameInfo(name, header, entry.tag));
+ }
+ provider.publicFunctions.put(baseName, functions);
+
+ }
+ }
+ }
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(null, t);
+ }
+
+ return fileIndex + setLength + 4;
+ }
+
+ /**
+ * Parse all compilation units for types
+ */
+ public void parseForTypes() {
+ synchronized (provider) {
+ for (DwarfCompileUnit compileUnit : provider.compileUnits) {
+ parseCompilationUnitForTypes(compileUnit);
+ }
+ }
+ }
+ /**
+ * Parse compilation unit headers and top-level info in the .debug_info section
+ * @param monitor
+ */
+ private void parseCUDebugInfo(IProgressMonitor monitor) {
+
+ if (debugInfoSection == null) { // no Dwarf data.
+ return;
+ }
+
+ // if we haven't built the referenced files list from a quick parse yet,
+ // flag it here so we can build the file list as we parse.
+ if (provider.referencedFiles.isEmpty()) {
+ provider.buildReferencedFilesList = true;
+ }
+
+ IStreamBuffer buffer = debugInfoSection.getBuffer();
+ IStreamBuffer debugStrings = getDebugStrings();
+ boolean havePubNames = publicNamesSection != null && publicNamesSection.getBuffer() != null;
+
+ int totalWork = (int) (buffer.capacity() / 1024);
+ monitor.beginTask(DwarfMessages.DwarfInfoReader_ReadDebugInfo, totalWork);
+ try {
+ if (buffer != null) {
+ long fileIndex = 0;
+ long fileEndIndex = buffer.capacity();
+
+ while (fileIndex < fileEndIndex) {
+ long oldIndex = fileIndex;
+ fileIndex = parseCompilationUnitForNames(buffer, fileIndex, debugStrings, fileEndIndex, havePubNames);
+ monitor.worked((int) ((fileIndex - oldIndex) / 1024));
+ }
+ }
+ } finally {
+ monitor.done();
+ }
+ provider.compileUnits.trimToSize();
+ // sort by low address the list of compilation units with code
+ provider.sortedCompileUnitsWithCode.trimToSize();
+ Collections.sort(provider.sortedCompileUnitsWithCode, sComparatorByLowAddress);
+ provider.buildReferencedFilesList = false;
+ }
+
+ /**
+ * Parse the compile unit quickly looking for variables that are globally visible
+ *
+ * @return offset of next compilation unit
+ */
+ private long parseCompilationUnitForNames(IStreamBuffer buffer, long fileIndex, IStreamBuffer debugStrings, long fileEndIndex, boolean havePubNames) {
+ buffer.position(fileIndex);
+ int lengthSize = 0;
+ currentCUHeader = new CompilationUnitHeader();
+ try {
+ InitialLengthValue sectionLength = readInitialLengthField(buffer);
+ currentCUHeader.length = sectionLength.length;
+ currentCUHeader.offsetSize = sectionLength.offsetSize;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ currentCUHeader.version = buffer.getShort();
+ if (currentCUHeader.offsetSize == 8) {
+ currentCUHeader.abbreviationOffset = (int) buffer.getLong();
+ lengthSize = 12;
+ } else {
+ currentCUHeader.abbreviationOffset = buffer.getInt();
+ lengthSize = 4;
+ }
+ currentCUHeader.addressSize = buffer.get();
+ currentCUHeader.debugInfoOffset = (int) fileIndex;
+
+ /*
+ * With certain GCC-E 3.x compilers, some subset of compile unit headers can have unit
+ * lengths 4 bytes too long. Before reading compile unit data, make sure there is a
+ * valid compile unit header right after this unit's data. Adjust the length if needed.
+ * To validate, check that the DWARF version and the address size are
+ * the same as the previous compile unit's.
+ */
+ if (fileIndex + currentCUHeader.length + (2 * lengthSize) < fileEndIndex) {
+ // try good case
+ short nextVersion;
+ byte nextAddrSize;
+ buffer.position(fileIndex + currentCUHeader.length + (2 * lengthSize)); // to next version
+ nextVersion = buffer.getShort();
+ buffer.position(fileIndex + currentCUHeader.length + (2 * lengthSize) + 2 + currentCUHeader.offsetSize); // to next address size
+ nextAddrSize = buffer.get();
+ // TODO: is this adjustment still necessary?
+ if (currentCUHeader.version != nextVersion || currentCUHeader.addressSize != nextAddrSize) {
+ // try adjusting back by 4 bytes
+ buffer.position(fileIndex + currentCUHeader.length + 4); // to next version
+ nextVersion = buffer.getShort();
+ buffer.position(fileIndex + currentCUHeader.length + 10); // to next address size
+ nextAddrSize = buffer.get();
+
+ if (currentCUHeader.version == nextVersion && currentCUHeader.addressSize == nextAddrSize) {
+ // all this work to adjust the length...
+ currentCUHeader.length -= 4;
+ }
+ }
+ }
+
+ // now read the whole compile unit into memory. note that we're
+ // reading the whole section including the size that we already
+ // read because other code will use the offset of the buffer as
+ // the offset of the section to store things by offset (types,
+ // function declarations, etc).
+ buffer.position(fileIndex);
+
+ IStreamBuffer in = buffer.wrapSubsection(currentCUHeader.length + lengthSize);
+
+ // skip over the header info we already read
+ if (currentCUHeader.offsetSize == 8) {
+ in.position(lengthSize + 2 + 8 + 1); // sizeof (length + version + offset + address size)
+ } else {
+ in.position(lengthSize + 2 + 4 + 1); // sizeof (length + version + offset + address size)
+ }
+
+ try {
+ // get stored abbrev table, or read and parse an abbrev table
+ Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(currentCUHeader.abbreviationOffset);
+
+ // read the compile unit's attribute list
+ long code = read_unsigned_leb128(in);
+ AbbreviationEntry entry = abbrevs.get(Long.valueOf(code));
+
+ AttributeList attributeList = new AttributeList(entry, in, currentCUHeader.addressSize, debugStrings);
+ processCompileUnit(currentCUHeader, entry.hasChildren, attributeList);
+
+ if (!havePubNames) {
+ // record file scope variables
+ byte addressSize = currentCUHeader.addressSize;
+ while (in.remaining() > 0) {
+ code = read_unsigned_leb128(in);
+
+ if (code != 0) {
+ entry = abbrevs.get(Long.valueOf(code));
+
+ switch (entry.tag) {
+ // record names of interest, but not other Dwarf attributes
+ case DwarfConstants.DW_TAG_variable:
+ {
+ // get variable names at the compile unit scope level
+ parseAttributesForNames(true, baseAndScopedNames, entry, in, addressSize, debugStrings);
+ if (baseAndScopedNames.baseName != null)
+ storePublicNames(provider.publicVariables, baseAndScopedNames, currentCUHeader, (short) DwarfConstants.DW_TAG_variable);
+ break;
+ }
+ case DwarfConstants.DW_TAG_imported_declaration: // for possible namespace alias
+ case DwarfConstants.DW_TAG_namespace:
+ case DwarfConstants.DW_TAG_subprogram:
+ case DwarfConstants.DW_TAG_enumerator:
+ case DwarfConstants.DW_TAG_class_type:
+ case DwarfConstants.DW_TAG_structure_type:
+ case DwarfConstants.DW_TAG_array_type:
+ case DwarfConstants.DW_TAG_base_type:
+ case DwarfConstants.DW_TAG_enumeration_type:
+ case DwarfConstants.DW_TAG_pointer_type:
+ case DwarfConstants.DW_TAG_ptr_to_member_type:
+ case DwarfConstants.DW_TAG_subroutine_type:
+ case DwarfConstants.DW_TAG_typedef:
+ case DwarfConstants.DW_TAG_union_type:
+ case DwarfConstants.DW_TAG_access_declaration:
+ case DwarfConstants.DW_TAG_catch_block:
+ case DwarfConstants.DW_TAG_common_block:
+ case DwarfConstants.DW_TAG_common_inclusion:
+ case DwarfConstants.DW_TAG_condition:
+ case DwarfConstants.DW_TAG_const_type:
+ case DwarfConstants.DW_TAG_constant:
+ case DwarfConstants.DW_TAG_entry_point:
+ case DwarfConstants.DW_TAG_file_type:
+ case DwarfConstants.DW_TAG_formal_parameter:
+ case DwarfConstants.DW_TAG_friend:
+ case DwarfConstants.DW_TAG_imported_module:
+ case DwarfConstants.DW_TAG_inheritance:
+ case DwarfConstants.DW_TAG_inlined_subroutine:
+ case DwarfConstants.DW_TAG_interface_type:
+ case DwarfConstants.DW_TAG_label:
+ case DwarfConstants.DW_TAG_lexical_block:
+ case DwarfConstants.DW_TAG_member:
+ case DwarfConstants.DW_TAG_module:
+ case DwarfConstants.DW_TAG_namelist:
+ case DwarfConstants.DW_TAG_namelist_item:
+ case DwarfConstants.DW_TAG_packed_type:
+ case DwarfConstants.DW_TAG_reference_type:
+ case DwarfConstants.DW_TAG_restrict_type:
+ case DwarfConstants.DW_TAG_set_type:
+ case DwarfConstants.DW_TAG_shared_type:
+ case DwarfConstants.DW_TAG_string_type:
+ case DwarfConstants.DW_TAG_subrange_type:
+ case DwarfConstants.DW_TAG_template_type_param:
+ case DwarfConstants.DW_TAG_template_value_param:
+ case DwarfConstants.DW_TAG_thrown_type:
+ case DwarfConstants.DW_TAG_try_block:
+ case DwarfConstants.DW_TAG_unspecified_parameters:
+ case DwarfConstants.DW_TAG_variant:
+ case DwarfConstants.DW_TAG_variant_part:
+ case DwarfConstants.DW_TAG_volatile_type:
+ case DwarfConstants.DW_TAG_with_stmt:
+ {
+ AttributeValue.skipAttributesToSibling(entry, in, addressSize);
+ break;
+ }
+ // case DwarfConstants.DW_TAG_compile_unit:
+ // case DwarfConstants.DW_TAG_partial_unit:
+ // case DwarfConstants.DW_TAG_unspecified_type:
+ default:
+ // skip entire entries
+ AttributeList.skipAttributes(entry, in, addressSize);
+ break;
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed1
+ + debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed2 + symbolFilePath, t);
+ }
+
+ // skip past the compile unit. note that the
+ // currentCUHeader.length does not include
+ // the size of the unit length itself
+ fileIndex += currentCUHeader.length + lengthSize;
+
+ return fileIndex;
+ }
+
+ /**
+ * Parse attributes, returning names
+ *
+ * @param onlyExternal only return names if they have external visibility
+ * @param names array to hold up to two names
+ * @param entry debug info entry
+ * @param in buffer stream of debug info
+ * @param addressSize
+ * @param debugStrings
+ * @return DW_AT_name value in names[0], unmangled DW_AT_MIPS_linkage_name value in
+ * names[1], or nulls
+ */
+ private void parseAttributesForNames(boolean onlyExternal, BaseAndScopedNames baseAndScopedNames, AbbreviationEntry entry, IStreamBuffer in,
+ byte addressSize, IStreamBuffer debugStrings) {
+
+ String name = null;
+ baseAndScopedNames.baseName = null;
+ baseAndScopedNames.nameWithScope = null;
+ boolean isExternal = false;
+
+ // go through the attributes and throw away everything except the names
+ int len = entry.attributes.size();
+ for (int i = 0; i < len; i++) {
+ Attribute attr = entry.attributes.get(i);
+ try {
+ if ( attr.tag == DwarfConstants.DW_AT_name
+ || attr.tag == DwarfConstants.DW_AT_MIPS_linkage_name) {
+ // names should be DW_FORM_string or DW_FORM_strp
+ if (attr.form == DwarfConstants.DW_FORM_string) {
+ int c;
+ StringBuffer sb = new StringBuffer();
+ while ((c = (in.get() & 0xff)) != -1) {
+ if (c == 0) {
+ break;
+ }
+ sb.append((char) c);
+ }
+ name = sb.toString();
+ } else if (attr.form == DwarfConstants.DW_FORM_strp) {
+ int debugStringOffset = in.getInt();
+ if ( debugStrings != null
+ && debugStringOffset >= 0
+ && debugStringOffset < debugStrings.capacity()) {
+ debugStrings.position(debugStringOffset);
+ name = DwarfInfoReader.readString(debugStrings);
+ }
+ }
+
+ if (name != null) {
+ if (attr.tag == DwarfConstants.DW_AT_name) {
+ baseAndScopedNames.baseName = name;
+ baseAndScopedNames.nameWithScope = name;
+ } else {
+ if (exeReader instanceof BaseExecutableSymbolicsReader) {
+ try {
+ baseAndScopedNames.nameWithScope = unmangler.unmangle(unmangler.undecorate(name));
+ } catch(UnmanglingException ue) {
+ }
+ }
+ }
+ name = null;
+ }
+ } else if (attr.tag == DwarfConstants.DW_AT_external) {
+ if (attr.form == DwarfConstants.DW_FORM_flag) {
+ isExternal = in.get() != 0;
+ } else {
+ AttributeValue.skipAttributeValue(attr.form, in, addressSize);
+ }
+ } else {
+ AttributeValue.skipAttributeValue(attr.form, in, addressSize);
+ }
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(null, t);
+ break;
+ }
+ }
+
+ // if only looking for externals, throw away internals
+ if (onlyExternal && !isExternal) {
+ baseAndScopedNames.baseName = null;
+ baseAndScopedNames.nameWithScope = null;
+ } else {
+ // if only have the scoped name, derive the base name
+ if (baseAndScopedNames.nameWithScope != null && baseAndScopedNames.baseName == null) {
+ int baseStart = baseAndScopedNames.nameWithScope.lastIndexOf("::"); //$NON-NLS-1$
+ if (baseStart != -1)
+ baseAndScopedNames.baseName = baseAndScopedNames.nameWithScope.substring(baseStart + 2);
+ else
+ baseAndScopedNames.baseName = baseAndScopedNames.nameWithScope;
+ }
+ }
+ }
+
+ /**
+ * Store compilation unit level names from Dwarf .debug_info
+ *
+ * @param namesStore
+ * @param names
+ * @param offset
+ */
+ private void storePublicNames(Map<String, List<PublicNameInfo>> namesStore, BaseAndScopedNames baseAndScopedNames,
+ CompilationUnitHeader cuHeader, short tag) {
+
+ List<PublicNameInfo> currentNames = namesStore.get(baseAndScopedNames.baseName);
+ if (currentNames == null) {
+ currentNames = new ArrayList<PublicNameInfo>();
+ namesStore.put(baseAndScopedNames.baseName, currentNames);
+ }
+ currentNames.add(new PublicNameInfo(baseAndScopedNames.nameWithScope, cuHeader, tag));
+ }
+
+ private synchronized void parseCompilationUnitForAddressesPrivate(DwarfCompileUnit compileUnit, IProgressMonitor monitor)
+ {
+ synchronized (compileUnit) {
+ if (compileUnit.isParsedForAddresses())
+ return;
+
+ compileUnit.setParsedForAddresses(true);
+
+ CompilationUnitHeader header = compileUnit.header;
+
+ if (header == null)
+ return;
+
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().trace(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceAddressParse1 + Integer.toHexString(header.debugInfoOffset) + DwarfMessages.DwarfInfoReader_TraceAddressParse2 + header.scope.getFilePath())); }
+
+ IStreamBuffer buffer = debugInfoSection.getBuffer();
+
+ if (buffer == null)
+ return;
+
+ int fileIndex = header.debugInfoOffset;
+
+ // read the compile unit debug info into memory
+ buffer.position(fileIndex);
+
+ int lengthSize = 0, headerLength = 0;
+ if (header.offsetSize == 8) {
+ lengthSize = 12;
+ headerLength = lengthSize + 2 + 8 + 1; // unit length(8) + version + abbrev table offset + address size
+ } else {
+ lengthSize = 4;
+ headerLength = lengthSize + 2 + 4 + 1; // unit length(8) + version + abbrev table offset + address size
+ }
+
+ IStreamBuffer data = buffer.wrapSubsection(header.length + lengthSize);
+
+ // skip over the header, since we've already read it
+ data.position(headerLength);
+
+ currentCompileUnitScope = compileUnit;
+ currentParentScope = compileUnit;
+ registerScope(header.debugInfoOffset, compileUnit);
+ currentCUHeader = header;
+
+ try {
+ // get stored abbrev table, or read and parse an abbrev table
+ Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(header.abbreviationOffset);
+
+ parseForAddresses(data, abbrevs, header, new Stack<Scope>(), monitor);
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed1
+ + debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed2 + symbolFilePath, t);
+ }
+ }
+ }
+
+ /**
+ * Given compilation unit, parse to get variables and all children that have address ranges.
+ *
+ * @param compileUnit
+ */
+ public void parseCompilationUnitForAddresses(final DwarfCompileUnit compileUnit) {
+ synchronized (provider) {
+ synchronized (compileUnit) {
+ if (compileUnit.isParsedForAddresses())
+ return;
+ }
+ parseCompilationUnitForAddressesPrivate(compileUnit, new NullProgressMonitor());
+ }
+ }
+
+ synchronized public void parseCompilationUnitForTypes(DwarfCompileUnit compileUnit) {
+ synchronized (provider) {
+ synchronized(compileUnit) {
+ if (compileUnit.isParsedForTypes())
+ return;
+
+ compileUnit.setParsedForTypes(true);
+
+ CompilationUnitHeader header = compileUnit.header;
+
+ if (header == null)
+ return;
+
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().trace(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceTypeParse1 + Integer.toHexString(header.debugInfoOffset) + DwarfMessages.DwarfInfoReader_TraceTypeParse2 + header.scope.getFilePath())); }
+
+ IStreamBuffer buffer = debugInfoSection.getBuffer();
+
+ if (buffer == null)
+ return;
+
+ int fileIndex = header.debugInfoOffset;
+
+ // read the compile unit debug info into memory
+ buffer.position(fileIndex);
+
+ int lengthSize = 0, headerLength = 0;
+ if (header.offsetSize == 8) {
+ lengthSize = 12;
+ headerLength = lengthSize + 2 + 8 + 1; // unit length(8) + version + abbrev table offset + address size
+ } else {
+ lengthSize = 4;
+ headerLength = lengthSize + 2 + 4 + 1; // unit length(8) + version + abbrev table offset + address size
+ }
+
+ IStreamBuffer data = buffer.wrapSubsection(header.length + lengthSize);
+
+ // skip over the header, since we've already read it
+ data.position(headerLength);
+
+ currentCompileUnitScope = compileUnit;
+ currentParentScope = compileUnit;
+ registerScope(header.debugInfoOffset, compileUnit);
+ currentCUHeader = header;
+
+ try {
+ // get stored abbrev table, or read and parse an abbrev table
+ Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(header.abbreviationOffset);
+
+ parseForTypes(data, abbrevs, header, new Stack<Scope>());
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseTraceInfoSectionFailed1
+ + debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseTraceInfoSectionFailed2 + symbolFilePath, t);
+ }
+ }
+ }
+ }
+
+ public void quickParseDebugInfo(IProgressMonitor monitor) {
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceQuickParse + symbolFilePath)); }
+ synchronized (provider) {
+ doQuickParseDebugInfo(monitor);
+ }
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceFinishedQuickParse)); }
+ }
+
+ /**
+ * Does a quick parse of the .debug_info section just to get a list of
+ * referenced files from the compile units.
+ */
+ private void doQuickParseDebugInfo(IProgressMonitor monitor) {
+
+ if (debugInfoSection == null) { // no Dwarf data.
+ return;
+ }
+
+ // get the compile units out of the .debug_info section
+ IStreamBuffer buffer = debugInfoSection.getBuffer();
+ if (buffer == null)
+ return;
+
+ try {
+ int lengthSize = 0;
+ long fileIndex = 0;
+ long fileEndIndex = buffer.capacity();
+
+ monitor.beginTask(DwarfMessages.DwarfInfoReader_ReadDebugInfo, (int) (fileEndIndex / 1024));
+
+ buffer.position(0);
+ while (fileIndex < fileEndIndex) {
+ buffer.position(fileIndex);
+ long unit_length = 0;
+ byte offsetSize = 0;
+ int debug_abbrev_offset = 0;
+
+ try {
+ InitialLengthValue sectionLength = readInitialLengthField(buffer);
+ unit_length = sectionLength.length;
+ offsetSize = sectionLength.offsetSize;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ short version = buffer.getShort();
+ if (offsetSize == 8) {
+ debug_abbrev_offset = (int) buffer.getLong();
+ lengthSize = 12;
+ } else {
+ debug_abbrev_offset = buffer.getInt();
+ lengthSize = 4;
+ }
+ byte address_size = buffer.get();
+
+ /*
+ * With certain GCC-E 3.x compilers, some subset of compile unit headers can have unit
+ * lengths 4 bytes too long. So before reading this unit's data, make sure there is a
+ * valid compile unit header right after this unit's data. Adjust the length if needed.
+ * To validate, check that the DWARF version and the address size are
+ * the same as the previous compile unit's.
+ */
+ if (fileIndex + unit_length + (2 * lengthSize) < fileEndIndex) {
+ // try good case
+ short nextVersion;
+ byte nextAddrSize;
+ buffer.position(fileIndex + unit_length + (2 * lengthSize)); // to next version
+ nextVersion = buffer.getShort();
+ buffer.position(fileIndex + unit_length + (2 * lengthSize) + 2 + debug_abbrev_offset); // to next address size
+ nextAddrSize = buffer.get();
+ // TODO: is this adjustment still necessary?
+ if (version != nextVersion || address_size != nextAddrSize) {
+ // try adjusting back by 4 bytes
+ buffer.position(fileIndex + unit_length + 4); // to next version
+ nextVersion = buffer.getShort();
+ buffer.position(fileIndex + unit_length + 10); // to next address size
+ nextAddrSize = buffer.get();
+
+ if (version == nextVersion && address_size == nextAddrSize) {
+ unit_length -= 4;
+ } // otherwise, just let things bomb
+ }
+ }
+
+ buffer.position(fileIndex + lengthSize);
+ IStreamBuffer data = buffer.wrapSubsection(unit_length);
+ // skip header info already read
+ if (offsetSize == 8) {
+ data.position(2 + 8 + 1); // sizeof (version + offset + address size)
+ } else {
+ data.position(2 + 4 + 1); // sizeof (version + offset + address size)
+ }
+
+ // get the abbreviation entry for the compile unit
+ // find the offset to the
+ Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(debug_abbrev_offset);
+
+ long code = read_unsigned_leb128(data);
+ AbbreviationEntry entry = abbrevs.get(Long.valueOf(code));
+ AttributeList attributeList = new AttributeList(entry, data, address_size, getDebugStrings());
+
+ // get comp_dir and name and figure out the path
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ String compDir = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_comp_dir);
+
+ IPath filePath = fileHelper.normalizeFilePath(compDir, name);
+ provider.referencedFiles.add(filePath.toOSString());
+
+ // do a quick parse of the line table to get any other
+ // referenced files
+ AttributeValue a = attributeList.getAttribute(DwarfConstants.DW_AT_stmt_list);
+ if (a != null) {
+ int stmtList = a.getValueAsInt();
+ quickParseLineInfo(stmtList, compDir);
+ }
+
+ // skip past the compile unit. note that the unit_length does
+ // not include the size of the unit length itself
+ long oldIndex = fileIndex;
+ fileIndex += unit_length + lengthSize;
+ monitor.worked((int) ((fileIndex - oldIndex) / 1024));
+ }
+
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseSectionSourceFilesFailed1
+ + debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseSectionSourceFilesFailed2 + symbolFilePath, t);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ *
+ */
+ class InitialLengthValue {
+ /**
+ * section length
+ */
+ long length;
+
+ /**
+ * section offset size in bytes.
+ */
+ byte offsetSize; //
+ }
+
+ /**
+ * Read section length field from the beginning of dwarf section.
+ *
+ * <p>See Chapter 7.4 32-Bit and 64-Bit DWARF Formats</p>
+ * @param data - stream buffer positioned at the start of section length record
+ * @return section length info. Cannot be null.
+ * @throws IOException
+ */
+ InitialLengthValue readInitialLengthField(IStreamBuffer data) throws IOException {
+ InitialLengthValue info = new InitialLengthValue();
+ info.length = data.getInt() & 0xffffffffL;
+
+ if (info.length == 0xffffffffL) {
+ info.length = data.getLong();
+ info.offsetSize = 8;
+ } else if (info.length == 0) { // IRIX
+ info.length = data.getLong();
+ info.offsetSize = 8;
+ } else
+ info.offsetSize = 4;
+ return info;
+ }
+
+
+ /**
+ * Get the .debug_strings section.
+ * @return ByteBuffer or <code>null</code>
+ */
+ private IStreamBuffer getDebugStrings() {
+ return getDwarfSection(DWARF_DEBUG_STR);
+ }
+
+ /**
+ * Does a quick parse of the .debug_line section just to get a list of
+ * referenced files from the line table.
+ */
+ private void quickParseLineInfo(int lineTableOffset, String compileUnitDirectory) {
+ IPath compileUnitDirectoryPath = PathUtils.createPath(compileUnitDirectory);
+ try {
+ // do a quick parse of the line table just to get referenced files
+ IStreamBuffer data = getDwarfSection(DWARF_DEBUG_LINE);
+ if (data != null) {
+ data.position(lineTableOffset);
+
+ /*
+ * Skip past the bytes of the header that we don't care about:
+ * unit_length (4 bytes), version (2 bytes), header_length (4 bytes),
+ * minimum_instruction_length (1 byte), default_is_stmt (1 byte),
+ * line_base (1 byte), line_range (1 byte)
+ */
+ data.position(data.position() + 14);
+
+ // we need to get this value so we can skip over
+ // standard_opcode_lengths
+ int opcode_base = data.get() & 0xff;
+ data.position(data.position() + opcode_base - 1);
+
+ // include_directories
+ ArrayList<String> dirList = new ArrayList<String>();
+
+ // add the compilation directory of the CU as the first
+ // directory
+ dirList.add(compileUnitDirectory);
+
+ while (true) {
+ String str = readString(data);
+ if (str.length() == 0)
+ break;
+
+ // if the directory is relative, append it to the CU dir
+ IPath dir = PathUtils.createPath(str);
+ if (!dir.isAbsolute() && dir.getDevice() == null) {
+ dir = compileUnitDirectoryPath.append(str);
+ }
+ dirList.add(dir.toString());
+ }
+
+ while (true) {
+ String fileName = readString(data);
+ if (fileName.length() == 0) // no more file entry
+ break;
+
+ // dir index
+ long leb128 = DwarfInfoReader.read_unsigned_leb128(data);
+
+ IPath fullPath = fileHelper.normalizeFilePath(dirList.get((int) leb128), fileName);
+ if (fullPath != null) {
+ provider.referencedFiles.add(fullPath.toOSString());
+ }
+
+ // skip the modification time and file size
+ leb128 = read_unsigned_leb128(data);
+ leb128 = read_unsigned_leb128(data);
+ }
+ }
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(null, t);
+ }
+ }
+
+
+ /**
+ * Parse the line table for a given compile unit
+ * @param attributes
+ * @param fileList list for file entries
+ * @return new array of ILineEntry
+ */
+ @SuppressWarnings("unused")
+ public Collection<ILineEntry> parseLineTable(IScope scope, AttributeList attributes, List<IPath> fileList) {
+ synchronized (provider) {
+ List<ILineEntry> lineEntries = new ArrayList<ILineEntry>();
+ try {
+ IStreamBuffer data = getDwarfSection(DWARF_DEBUG_LINE);
+ AttributeValue a = attributes.getAttribute(DwarfConstants.DW_AT_stmt_list);
+ if (data != null && a != null) {
+ int stmtList = a.getValueAsInt();
+ data.position(stmtList);
+
+ /*
+ * Read line table header:
+ *
+ * total_length: 4 bytes (excluding itself)
+ * version: 2
+ * prologue length: 4
+ * minimum_instruction_len: 1
+ * default_is_stmt: 0 or 1
+ * line_base: 1
+ * line_range: 1
+ * opcode_base: 1
+ * standard_opcode_lengths: (value of opcode_base)
+ */
+
+ // Remember the CU line tables we've parsed.
+ int length = data.getInt() + 4;
+
+ // Skip the following till "opcode_base"
+ int version = data.getShort();
+ int prologue_length = data.getInt();
+ int minimum_instruction_length = data.get() & 0xff;
+ boolean default_is_stmt = data.get() > 0;
+ int line_base = data.get(); // signed
+ int line_range = data.get() & 0xff;
+
+ int opcode_base = data.get() & 0xff;
+ byte[] opcodes = new byte[opcode_base - 1];
+ data.get(opcodes);
+
+ // Read in directories.
+ //
+ ArrayList<String> dirList = new ArrayList<String>();
+
+ // Put the compilation directory of the CU as the first dir
+ String compDir = attributes.getAttributeValueAsString(DwarfConstants.DW_AT_comp_dir);
+ dirList.add(compDir);
+
+ IPath compDirPath = PathUtils.createPath(compDir);
+
+ String str, fileName;
+
+ while (true) {
+ str = readString(data);
+ if (str.length() == 0)
+ break;
+ // If the directory is relative, append it to the CU dir
+ IPath dir = PathUtils.createPath(str);
+ if (!dir.isAbsolute() && dir.getDevice() == null) {
+ dir = compDirPath.append(str);
+ }
+ dirList.add(dir.toString());
+ }
+
+ // Read file names
+ //
+ long leb128;
+ while (true) {
+ fileName = readString(data);
+ if (fileName.length() == 0) // no more file entry
+ break;
+
+ // dir index
+ leb128 = read_unsigned_leb128(data);
+
+ IPath fullPath = fileHelper.normalizeFilePath(dirList.get((int) leb128), fileName);
+ // add a null as a placeholder when the filename is enclosed in '<' & '>' (e.g., "<stdin>")
+ fileList.add(fullPath);
+
+ // Skip the following
+ //
+ // modification time
+ leb128 = read_unsigned_leb128(data);
+
+ // file size in bytes
+ leb128 = read_unsigned_leb128(data);
+ }
+
+ long info_address = 0;
+ long info_file = 1;
+ int info_line = 1;
+ int info_column = 0;
+ boolean is_stmt = default_is_stmt;
+ int info_flags = 0;
+ long info_ISA = 0;
+
+ long lineInfoEnd = stmtList + length;
+ while (data.position() < lineInfoEnd) {
+ byte opcodeB = data.get();
+ int opcode = 0xFF & opcodeB;
+
+ if (opcode >= opcode_base) {
+ info_line += (((opcode - opcode_base) % line_range) + line_base);
+ info_address += (opcode - opcode_base) / line_range * minimum_instruction_length;
+ if (is_stmt && fileList.size() > 0) {
+ IPath path = fileList.get((int) info_file - 1);
+ // added a null as a placeholder when the filename was enclosed in '<' & '>' (e.g., "<stdin>")
+ if (path != null)
+ lineEntries.add(new LineEntry(path, info_line, info_column, new Addr32(info_address), null));
+ }
+ info_flags &= ~(DwarfConstants.LINE_BasicBlock | DwarfConstants.LINE_PrologueEnd | DwarfConstants.LINE_EpilogueBegin);
+ } else if (opcode == 0) {
+ long op_size = read_unsigned_leb128(data);
+ long op_pos = data.position();
+ int code = data.get() & 0xff;
+ switch (code) {
+ case DwarfConstants.DW_LNE_define_file: {
+ fileName = readString(data);
+ long dir = read_unsigned_leb128(data);
+ long modTime = read_unsigned_leb128(data);
+ long fileSize = read_unsigned_leb128(data);
+ IPath fullPath = fileHelper.normalizeFilePath(dirList.get((int) dir), fileName);
+ if (fullPath != null) {
+ fileList.add(fullPath);
+ }
+ break;
+ }
+ case DwarfConstants.DW_LNE_end_sequence:
+ info_flags |= DwarfConstants.LINE_EndSequence;
+
+ if (lineEntries.size() > 0) {
+ // this just marks the end of a line number
+ // program sequence. use
+ // its address to set the high address of the
+ // last line entry
+ lineEntries.get(lineEntries.size() - 1).setHighAddress(new Addr32(info_address));
+ }
+
+ // it also resets the state machine
+ info_address = 0;
+ info_file = 1;
+ info_line = 1;
+ info_column = 0;
+ is_stmt = default_is_stmt;
+ info_flags = 0;
+ info_ISA = 0;
+ break;
+
+ case DwarfConstants.DW_LNE_set_address:
+ info_address = data.getInt();
+ break;
+ default:
+ data.position((int) (data.position() + op_size - 1));
+ break;
+ }
+ assert (data.position() == op_pos + op_size);
+ } else {
+ switch (opcode) {
+ case DwarfConstants.DW_LNS_copy:
+ if (is_stmt && fileList.size() > 0) {
+ lineEntries.add(new LineEntry(fileList.get((int) info_file - 1), info_line,
+ info_column, new Addr32(info_address), null));
+ }
+ info_flags &= ~(DwarfConstants.LINE_BasicBlock | DwarfConstants.LINE_PrologueEnd | DwarfConstants.LINE_EpilogueBegin);
+ break;
+ case DwarfConstants.DW_LNS_advance_pc:
+ info_address += read_unsigned_leb128(data) * minimum_instruction_length;
+ break;
+ case DwarfConstants.DW_LNS_advance_line:
+ info_line += read_signed_leb128(data);
+ break;
+ case DwarfConstants.DW_LNS_set_file:
+ info_file = read_unsigned_leb128(data);
+ break;
+ case DwarfConstants.DW_LNS_set_column:
+ info_column = (int) read_unsigned_leb128(data);
+ break;
+ case DwarfConstants.DW_LNS_negate_stmt:
+ is_stmt = !is_stmt;
+ break;
+ case DwarfConstants.DW_LNS_set_basic_block:
+ info_flags |= DwarfConstants.LINE_BasicBlock;
+ break;
+ case DwarfConstants.DW_LNS_const_add_pc:
+ info_address += (255 - opcode_base) / line_range * minimum_instruction_length;
+ break;
+ case DwarfConstants.DW_LNS_fixed_advance_pc:
+ info_address += data.getShort();
+ break;
+ case DwarfConstants.DW_LNS_set_prologue_end:
+ info_flags |= DwarfConstants.LINE_PrologueEnd;
+ break;
+ case DwarfConstants.DW_LNS_set_epilogue_begin:
+ info_flags |= DwarfConstants.LINE_EpilogueBegin;
+ break;
+ case DwarfConstants.DW_LNS_set_isa:
+ info_ISA = read_unsigned_leb128(data);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(null, t);
+ }
+
+ // sort by start address
+ Collections.sort(lineEntries);
+
+ // fill in the end addresses as needed
+ ILineEntry previousEntry = null;
+ for (ILineEntry line : lineEntries) {
+ if (previousEntry != null && previousEntry.getHighAddress() == null) {
+ previousEntry.setHighAddress(line.getLowAddress());
+ }
+
+ previousEntry = line;
+ }
+
+ // the last line entry
+ if (previousEntry != null) {
+ IAddress prevHigh = previousEntry.getHighAddress();
+ if (prevHigh == null)
+ previousEntry.setHighAddress(scope.getHighAddress());
+// FIXME: the following is causing JUnit tests to fail
+// else if (prevHigh != null && prevHigh.compareTo(scope.getHighAddress()) > 0)
+// previousEntry.setHighAddress(scope.getHighAddress());
+ }
+
+ return lineEntries;
+ }
+ }
+
+ private void parseForAddresses(IStreamBuffer in, Map<Long, AbbreviationEntry> abbrevs, CompilationUnitHeader header,
+ Stack<Scope> nestingStack, IProgressMonitor monitor)
+ throws IOException {
+
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().trace(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceScopeAddressParse1 + header.scope.getName() + DwarfMessages.DwarfInfoReader_TraceScopeAddressParse2 + Long.toHexString(header.debugInfoOffset))); }
+
+ try {
+ long startWork = in.remaining();
+ long lastWork = startWork;
+ int workChunk = (int)(startWork / Integer.MAX_VALUE) + 1;
+ int work = (int)(startWork / workChunk);
+ monitor.beginTask(DwarfMessages.DwarfInfoReader_TraceScopeAddressParse1 + header.scope.getName() + DwarfMessages.DwarfInfoReader_TraceScopeAddressParse2 + Long.toHexString(header.debugInfoOffset), work);
+
+ while (in.remaining() > 0) {
+
+ work = (int)((lastWork - in.remaining()) / workChunk);
+ monitor.worked(work);
+ lastWork = in.remaining();
+
+ long offset = in.position() + currentCUHeader.debugInfoOffset;
+ long code = read_unsigned_leb128(in);
+
+ if (code != 0) {
+ AbbreviationEntry entry = abbrevs.get(new Long(code));
+ if (entry == null) {
+ assert false;
+ continue;
+ }
+
+ if (entry.hasChildren) {
+ nestingStack.push(currentParentScope);
+ }
+
+ if (isDebugInfoEntryWithAddressRange(entry.tag)) {
+ AttributeList attributeList = new AttributeList(entry, in, header.addressSize, getDebugStrings());
+ processDebugInfoEntry(offset, entry, attributeList, header);
+
+ // if we didn't create a scope for a routine or lexical block, then ignore its innards
+ switch (entry.tag) {
+ case DwarfConstants.DW_TAG_subprogram:
+ case DwarfConstants.DW_TAG_inlined_subroutine:
+ case DwarfConstants.DW_TAG_lexical_block:
+ if (entry.hasChildren && (provider.scopesByOffset.get(offset) == null)) {
+ // because some versions of GCC-E 3.x produce invalid sibling offsets,
+ // always read entry attributes, rather than skipping using siblings
+ // keep track of nesting just like in parseForTypes()
+ int nesting = 1;
+ while ((nesting > 0) && (in.remaining() > 0)) {
+ offset = in.position() + currentCUHeader.debugInfoOffset;
+ code = read_unsigned_leb128(in);
+
+ if (code != 0) {
+ entry = abbrevs.get(new Long(code));
+ if (entry == null) {
+ assert false;
+ continue;
+ }
+ if (entry.hasChildren)
+ nesting++;
+ // skip the attributes we're not reading...
+ AttributeList.skipAttributes(entry, in, header.addressSize);
+ } else {
+ nesting--;
+ }
+ }
+
+ // nesting loop ends after reading 0 code of skipped entry
+ if (nestingStack.isEmpty()) {
+ // FIXME
+ currentParentScope = null;
+ } else {
+ currentParentScope = nestingStack.pop();
+ }
+ }
+ break;
+ }
+ } else {
+ // skip the attributes we're not reading...
+ AttributeList.skipAttributes(entry, in, header.addressSize);
+ }
+
+ } else {
+ if (code == 0) {
+ if (nestingStack.isEmpty()) {
+ // FIXME
+ currentParentScope = null;
+ } else {
+ currentParentScope = nestingStack.pop();
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ monitor.done();
+ }
+ }
+
+
+ private void parseForTypes(IStreamBuffer in, Map<Long, AbbreviationEntry> abbrevs, CompilationUnitHeader header,
+ Stack<Scope> nestingStack)
+ throws IOException {
+
+ if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().trace(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceParseTypes1 + header.scope.getName() + DwarfMessages.DwarfInfoReader_TraceParseTypes2 + Long.toHexString(header.debugInfoOffset))); }
+
+ Stack<IType> typeStack = new Stack<IType>();
+ typeToParentMap.clear();
+
+ currentParentScope = currentCompileUnitScope;
+
+ while (in.remaining() > 0) {
+ long offset = in.position() + currentCUHeader.debugInfoOffset;
+ long code = read_unsigned_leb128(in);
+
+ if (code != 0) {
+ AbbreviationEntry entry = abbrevs.get(new Long(code));
+ if (entry == null) {
+ assert false;
+ continue;
+ }
+ if (entry.hasChildren) {
+ nestingStack.push(currentParentScope);
+ typeStack.push(currentParentType);
+ }
+
+ if (isForwardTypeTag(entry.tag) || isForwardTypeChildTag(entry.tag)) {
+
+ processDebugInfoEntry(offset, entry,
+ new AttributeList(entry, in, header.addressSize, getDebugStrings()),
+ header);
+
+ } else {
+ switch (entry.tag) {
+ case DwarfConstants.DW_TAG_subprogram:
+ case DwarfConstants.DW_TAG_inlined_subroutine:
+ case DwarfConstants.DW_TAG_lexical_block: {
+ Scope scope = provider.scopesByOffset.get(offset); // may be null
+ if (scope != null)
+ currentParentScope = scope;
+ break;
+ }
+ }
+
+ // skip the attributes we're not reading...
+ AttributeList.skipAttributes(entry, in, header.addressSize);
+ }
+ } else {
+ // code == 0
+ if (nestingStack.isEmpty()) {
+ // FIXME
+ currentParentType = null;
+ currentParentScope = null;
+ } else {
+ currentParentScope = nestingStack.pop();
+ currentParentType = typeStack.pop();
+ }
+ }
+ }
+ }
+
+ /**
+ * Tell if a tag will be parsed on-demand to generate an IType, and will
+ * be accessible via provider.getType() or provider.readType().
+ * <p>
+ * Note: DW_TAG_member is usually considered a child of struct/class/etc., but
+ * a static class variable contains a reference to it, so we must be able to
+ * locate it.
+ * @param tag
+ * @return true if type is parsed and should have a ForwardDwarfDefinition
+ */
+ private boolean isForwardTypeTag(short tag) {
+ switch (tag) {
+ case DwarfConstants.DW_TAG_array_type:
+ case DwarfConstants.DW_TAG_class_type:
+ case DwarfConstants.DW_TAG_enumeration_type:
+ case DwarfConstants.DW_TAG_member:
+ case DwarfConstants.DW_TAG_pointer_type:
+ case DwarfConstants.DW_TAG_reference_type:
+ case DwarfConstants.DW_TAG_structure_type:
+ case DwarfConstants.DW_TAG_subroutine_type:
+ case DwarfConstants.DW_TAG_typedef:
+ case DwarfConstants.DW_TAG_union_type:
+ //case DwarfConstants.DW_TAG_unspecified_parameters:
+ case DwarfConstants.DW_TAG_inheritance:
+ case DwarfConstants.DW_TAG_ptr_to_member_type:
+ //case DwarfConstants.DW_TAG_with_stmt:
+ case DwarfConstants.DW_TAG_base_type:
+ //case DwarfConstants.DW_TAG_catch_block:
+ case DwarfConstants.DW_TAG_const_type:
+ //case DwarfConstants.DW_TAG_enumerator:
+ //case DwarfConstants.DW_TAG_file_type:
+ //case DwarfConstants.DW_TAG_friend:
+ case DwarfConstants.DW_TAG_template_type_param:
+ //case DwarfConstants.DW_TAG_template_value_param:
+ //case DwarfConstants.DW_TAG_thrown_type:
+ //case DwarfConstants.DW_TAG_try_block:
+ case DwarfConstants.DW_TAG_volatile_type:
+ case DwarfConstants.DW_TAG_subrange_type:
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Tell if a tag is a parsed child of an IType. This should not be explicitly
+ * referenced in provider.typesByOffset or .forwardDwarfDefinitions but as
+ * children of other ForwardDwarfDefinitions parsed on demand.
+ *<p>
+ * Note: DW_TAG_member is usually considered a child of struct/class/etc., but
+ * a static class variable contains a reference to it, so we must be able to
+ * locate it. Thus, it is not listed here.
+ * @param tag
+ * @return true if component is parsed and a child of a forward definition
+ */
+ private boolean isForwardTypeChildTag(short tag) {
+ switch (tag) {
+ //case DwarfConstants.DW_TAG_unspecified_parameters:
+ case DwarfConstants.DW_TAG_inheritance:
+ case DwarfConstants.DW_TAG_enumerator:
+ case DwarfConstants.DW_TAG_member:
+ case DwarfConstants.DW_TAG_subrange_type:
+ //case DwarfConstants.DW_TAG_friend:
+ //case DwarfConstants.DW_TAG_template_type_param:
+ //case DwarfConstants.DW_TAG_template_value_param:
+ //case DwarfConstants.DW_TAG_thrown_type:
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Fully parse any debug info entry.
+ * @param offset
+ * @param entry
+ * @param attributeList
+ * @param header
+ * @param compositeNesting
+ */
+ private void processDebugInfoEntry(long offset, AbbreviationEntry entry, AttributeList attributeList,
+ CompilationUnitHeader header) {
+ //System.out.println("Handling " + entry.tag + " at " + Long.toHexString(offset));
+ short tag = entry.tag;
+
+ // We are only interested in certain tags.
+ switch (tag) {
+ case DwarfConstants.DW_TAG_array_type:
+ processArrayType(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_class_type:
+ processClassType(offset, attributeList, header, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_enumeration_type:
+ processEnumType(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_formal_parameter:
+ processVariable(offset, attributeList, true);
+ break;
+ case DwarfConstants.DW_TAG_lexical_block:
+ processLexicalBlock(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_member:
+ processField(offset, attributeList, header, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_pointer_type:
+ processPointerType(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_reference_type:
+ processReferenceType(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_structure_type:
+ processStructType(offset, attributeList, header, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_subroutine_type:
+ processSubroutineType(offset, attributeList, header, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_typedef:
+ processTypeDef(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_union_type:
+ processUnionType(offset, attributeList, header, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_unspecified_parameters:
+ break;
+ case DwarfConstants.DW_TAG_inheritance:
+ processInheritance(offset, attributeList, header, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_ptr_to_member_type:
+ processPtrToMemberType(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_with_stmt:
+ break;
+ case DwarfConstants.DW_TAG_base_type:
+ processBasicType(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_catch_block:
+ break;
+ case DwarfConstants.DW_TAG_const_type:
+ processConstType(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_enumerator:
+ processEnumerator(offset, attributeList);
+ break;
+ case DwarfConstants.DW_TAG_file_type:
+ break;
+ case DwarfConstants.DW_TAG_friend:
+ break;
+ case DwarfConstants.DW_TAG_subprogram:
+ processSubprogram(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_inlined_subroutine:
+ processInlinedSubroutine(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_template_type_param:
+ processTemplateTypeParam(offset, attributeList, header, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_template_value_param:
+ break;
+ case DwarfConstants.DW_TAG_thrown_type:
+ break;
+ case DwarfConstants.DW_TAG_try_block:
+ break;
+ case DwarfConstants.DW_TAG_variable:
+ processVariable(offset, attributeList, false);
+ break;
+ case DwarfConstants.DW_TAG_volatile_type:
+ processVolatileType(offset, attributeList, entry.hasChildren);
+ break;
+ case DwarfConstants.DW_TAG_subrange_type:
+ processArrayBoundType(offset, attributeList, entry.hasChildren);
+ break;
+ }
+ }
+
+ /**
+ * Tell whether a tag has or may have content with an address range
+ *
+ * Note: tag DW_TAG_compile_unit was parsed in the initial parse
+ *
+ * @param tag
+ * @return
+ */
+ private boolean isDebugInfoEntryWithAddressRange(short tag) {
+ switch (tag) {
+ // tags allowed to have both DW_AT_low_pc and DW_AT_high_pc or DW_at_ranges
+ case DwarfConstants.DW_TAG_catch_block:
+ case DwarfConstants.DW_TAG_inlined_subroutine:
+ case DwarfConstants.DW_TAG_lexical_block:
+ case DwarfConstants.DW_TAG_module:
+ case DwarfConstants.DW_TAG_partial_unit:
+ case DwarfConstants.DW_TAG_subprogram:
+ case DwarfConstants.DW_TAG_try_block:
+ case DwarfConstants.DW_TAG_with_stmt:
+ return true;
+ // TODO: take DW_TAG_variable out of here?
+ case DwarfConstants.DW_TAG_variable:
+ case DwarfConstants.DW_TAG_formal_parameter:
+ return true;
+ }
+ return false;
+ }
+ //TODO: support DWARF 64-bit format
+ static long readAddress(IStreamBuffer in, int addressSize) throws IOException {
+ long value = 0;
+
+ switch (addressSize) {
+ case 2:
+ value = in.getShort();
+ break;
+ case 4:
+ value = in.getInt();
+ break;
+ case 8:
+ value = in.getLong();
+ break;
+ default:
+ // ????
+ }
+ return value;
+ }
+
+
+ /* unsigned */
+ static long read_unsigned_leb128(IStreamBuffer in) throws IOException {
+ /* unsigned */
+ long result = 0;
+ int shift = 0;
+ byte b;
+
+ while (true) {
+ if (!in.hasRemaining())
+ break; // throw new IOException("no more data");
+ b = in.get();
+ result |= ((long) (b & 0x7f) << shift);
+ if ((b & 0x80) == 0) {
+ break;
+ }
+ shift += 7;
+ }
+ return result;
+ }
+
+ /* signed */
+ public static long read_signed_leb128(IStreamBuffer in) throws IOException {
+ /* unsigned */
+ long result = 0;
+ int shift = 0;
+ int size = 32;
+ byte b;
+
+ while (true) {
+ if (!in.hasRemaining())
+ throw new IOException(CCorePlugin.getResourceString("Util.exception.noData")); //$NON-NLS-1$
+ b = in.get();
+ result |= ((long) (b & 0x7f) << shift);
+ shift += 7;
+ if ((b & 0x80) == 0) {
+ break;
+ }
+ }
+ if ((shift < size) && (b & 0x40) != 0) {
+ result |= -(1 << shift);
+ }
+ return result;
+ }
+
+
+ /**
+ * Read a null-ended string from the given "data" stream. data : IN, byte
+ * buffer
+ */
+ public static String readString(IStreamBuffer data) {
+ String str;
+
+ StringBuilder sb = new StringBuilder();
+ while (data.hasRemaining()) {
+ byte c = data.get();
+ if (c == 0) {
+ break;
+ }
+ sb.append((char) c);
+ }
+
+ str = sb.toString();
+ return str;
+ }
+
+ private Collection<LocationEntry> getLocationRecord(long offset) {
+ // first check the cache
+ Collection<LocationEntry> entries = locationEntriesByOffset.get(offset);
+ if (entries == null) {
+ // not found so try to get the entries from the offset
+
+ // note: some compilers generate MULTIPLE ENTRIES for the same location,
+ // and the last one tends to be more correct... use a map here when reading
+ TreeMap<IRangeList.Entry, LocationEntry> entryMap = new TreeMap<IRangeList.Entry, LocationEntry>();
+
+ try {
+ IStreamBuffer data = getDwarfSection(DWARF_DEBUG_LOC);
+ if (data != null) {
+ data.position(offset);
+
+ boolean first = true;
+ long base = 0;
+
+ while (data.hasRemaining()) {
+
+ long lowPC = readAddress(data, currentCUHeader.addressSize);
+ long highPC = readAddress(data, currentCUHeader.addressSize);
+
+ if (lowPC == 0 && highPC == 0) {
+ // end of list entry
+ break;
+ } else if (first) {
+ first = false;
+ long maxaddress = currentCUHeader.addressSize == 4 ? Integer.MAX_VALUE : Long.MAX_VALUE;
+ if (lowPC == maxaddress) {
+ // base address selection entry
+ base = highPC;
+ continue;
+ } else if (currentCompileUnitScope.getRangeList() == null) {
+ // if the compilation unit has a contiguous range, no implicit base is needed
+ base = currentCompileUnitScope.getLowAddress().getValue().longValue();
+ }
+ }
+
+ // location list entry
+ int numOpCodes = data.getShort();
+ byte[] bytes = new byte[numOpCodes];
+ data.get(bytes);
+ LocationEntry entry = new LocationEntry(lowPC + base, highPC + base, bytes);
+ entryMap.put(new IRangeList.Entry(lowPC + base, highPC + base), entry);
+ }
+
+ entries = entryMap.values();
+ locationEntriesByOffset.put(offset, entries);
+ }
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(null, t);
+ }
+ }
+
+ return entries;
+ }
+
+
+ private Map<Long, AbbreviationEntry> parseDebugAbbreviation(int abbreviationOffset) throws IOException {
+ Integer key = Integer.valueOf(abbreviationOffset);
+ Map<Long, AbbreviationEntry> abbrevs = provider.abbreviationMaps.get(key);
+ if (abbrevs == null) {
+ abbrevs = new HashMap<Long, AbbreviationEntry>();
+ provider.abbreviationMaps.put(key, abbrevs);
+ IStreamBuffer data = getDwarfSection(DWARF_DEBUG_ABBREV);
+ if (data != null) {
+ data.position(abbreviationOffset);
+ while (data.remaining() > 0) {
+ long code = read_unsigned_leb128(data);
+ if (code == 0) {
+ break;
+ }
+ short tag = (short) read_unsigned_leb128(data);
+ boolean hasChildren = data.get() == DwarfConstants.DW_CHILDREN_yes;
+ AbbreviationEntry entry = new AbbreviationEntry(code, tag, hasChildren);
+
+ // attributes
+ short name = 0;
+ byte form = 0;
+ do {
+ name = (short) read_unsigned_leb128(data);
+ form = (byte) read_unsigned_leb128(data);
+ if (name != 0) {
+ entry.attributes.add(new Attribute(name, form));
+ }
+ } while (name != 0 && form != 0);
+ entry.attributes.trimToSize();
+
+ abbrevs.put(Long.valueOf(code), entry);
+ }
+ }
+ }
+ return abbrevs;
+ }
+
+
+ private void registerType(long offset, IType type, boolean hasChildren) {
+ provider.typesByOffset.put(offset, type);
+
+ typeToParentMap.put(type, currentParentType);
+ if (hasChildren)
+ currentParentType = type;
+ if (DEBUG) {
+ if (type != null) {
+ System.out.print(DwarfMessages.DwarfInfoReader_ReadType + type.getName());
+ while (type.getType() != null) {
+ type = type.getType();
+ System.out.print(" " + type.getName()); //$NON-NLS-1$
+ }
+ System.out.println();
+ }
+ }
+ }
+
+ private void registerScope(long offset, Scope scope) {
+ provider.scopesByOffset.put(offset, scope);
+ }
+
+ /**
+ * Read a range list referenced from a code scope.
+ * @param offset
+ * @param base the specified DW_AT_low_pc value (or 0)
+ * @return a new RangeList
+ */
+ public RangeList readRangeList(int offset, AttributeValue baseValue) {
+ synchronized (provider) {
+ IStreamBuffer data = getDwarfSection(DWARF_DEBUG_RANGES);
+ if (data == null) {
+ return null;
+ }
+
+ try {
+ data.position(offset);
+
+ /*
+ * Read range list entry:
+ *
+ * start: DW_FORM_addr
+ * end: DW_FORM_addr
+ *
+ * When start == all ones, it is a base address selection entry,
+ * and end is the base address. The base address does not need to
+ * be specified, and is the compialtion unit's base address by default.
+ *
+ * When start == end == 0, this is the end of the list.
+ */
+
+ RangeList list = new RangeList();
+
+ long base = 0;
+ long start = data.getInt();
+ long end = data.getInt();
+
+ if (start == -1) {
+ base = end;
+
+ start = data.getInt();
+ end = data.getInt();
+ } else if (baseValue != null) {
+ base = baseValue.getValueAsLong();
+ } else if (currentCompileUnitScope != null && currentCompileUnitScope.getRangeList() == null) {
+ base = currentCompileUnitScope.getLowAddress().getValue().longValue();
+ }
+ do {
+ if (start == 0 && end == 0) {
+ break;
+ } else if (start != end) {
+ // ignore bogus entries: GCC-E sometimes generates these buggily (for artifical non-inlined functions)
+ if (base + start >= codeRanges.getLowAddress()) {
+ list.addRange(base + start, base + end);
+ }
+ }
+ start = data.getInt();
+ end = data.getInt();
+
+ } while (true);
+
+ return list;
+
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_RangeReadFailed, t);
+ return null;
+ }
+ }
+ }
+
+
+
+ /**
+ * Set up the address range for a scope by using its DW_AT_low_pc/DW_AT_high_pc
+ * or DW_AT_ranges attributes, or DW_AT_stmt_list in a pinch
+ * @param attributeList
+ * @param scope
+ */
+ private void setupAddresses(AttributeList attributeList, Scope scope) {
+
+ // get the high and low pc from the attributes list
+ AttributeValue value
+ = attributeList.getAttribute(DwarfConstants.DW_AT_high_pc);
+ if (value != null) {
+ IAddress low = new Addr32(attributeList.getAttributeValueAsLong(DwarfConstants.DW_AT_low_pc));
+ scope.setLowAddress(low);
+ IAddress high = new Addr32(attributeList.getAttributeValueAsLong(DwarfConstants.DW_AT_high_pc));
+ IScope parent = scope.getParent();
+ if (low.compareTo(high) <= 0) {
+ if (parent instanceof DwarfFunctionScope) {
+ IAddress parentHigh = parent.getHighAddress();
+ if (parentHigh != null && high.compareTo(parentHigh) > 0) {
+ ((Scope)parent).setHighAddress(high);
+ }
+ }
+ } else {
+ // relying on the following to confirm that this is an RVCT inline DWARF generation bug
+ if (scope instanceof DwarfFunctionScope && parent instanceof DwarfFunctionScope) {
+ high = fix_Dwarf_InlineHighAddress_Problem(scope);
+ if (high == null)
+ // at least prevent ecl.bz Bug 329324 from happening
+ high = parent.getHighAddress();
+
+ // wow, RVCT, you're neat... I think you mean, point to the high PC of the parent
+ } else if (parent != null && parent.getHighAddress() != null) {
+ high = parent.getHighAddress();
+ // may still be bogus, check again next
+ }
+
+ if (low.compareTo(high) > 0) {
+ scope.setLowAddress(high);
+ high = low;
+ }
+ }
+ scope.setHighAddress(high);
+ return;
+ }
+
+ // look for a range
+ value = attributeList.getAttribute(DwarfConstants.DW_AT_ranges);
+ if (value != null) {
+ AttributeValue baseValue = attributeList.getAttribute(DwarfConstants.DW_AT_low_pc);
+ RangeList ranges = readRangeList(value.getValueAsInt(), baseValue);
+ if (ranges != null) {
+ scope.setRangeList(ranges);
+
+ // if the range list high and low pc extend outside the parent's
+ // high/low range, adjust the parent (found in GCC-E)
+ if (ranges.getLowAddress() < scope.getParent().getLowAddress().getValue().longValue()) {
+ if (scope.getParent() instanceof Scope)
+ ((Scope)scope.getParent()).setLowAddress(new Addr32(ranges.getLowAddress()));
+ }
+ if (ranges.getHighAddress() > scope.getParent().getHighAddress().getValue().longValue()) {
+ if (scope.getParent() instanceof Scope)
+ ((Scope)scope.getParent()).setHighAddress(new Addr32(ranges.getHighAddress()));
+ }
+ return;
+ }
+ }
+
+ // in a CU, GCC-E may have only generated this, so we need to dig into the line table
+ if (scope instanceof ICompileUnitScope) {
+ value = attributeList.getAttribute(DwarfConstants.DW_AT_stmt_list);
+ if (value != null) {
+ RangeList ranges = new RangeList();
+ for (ILineEntry entry : ((ICompileUnitScope) scope).getLineEntries()) {
+ // ignore (for now) entries that seem far out of range
+ if (entry.getLowAddress().getValue().longValue() >= codeRanges.getLowAddress()) {
+ ranges.addRange(entry.getLowAddress().getValue().longValue(),
+ entry.getHighAddress().getValue().longValue());
+ }
+ }
+ scope.setRangeList(ranges);
+ return;
+ }
+ }
+
+ // no code, apparently
+ scope.setLowAddress(new Addr32(0));
+ scope.setHighAddress(new Addr32(0));
+ }
+
+ /**
+ * for cases where the compiler generates an incorrect high-address,
+ * the line entry provider can give information about the current and
+ * subsequent lines within an inline.
+ * <br>
+ * caveats: this is not meant to handle nested broken inlines. the
+ * algorithm assumes
+ * - an outer inline nesting another inline will use set of ranges, not a high-low
+ * - an inline function will likely be in another file
+ * - if not, it will likely be prior to the function that inlines it
+ * - and if not, it will likely be more than 32 lines after the function that inlines it
+ * @param scope
+ * @return high address if determined, or null if prerequisites for finding it aren't met.
+ */
+ private IAddress fix_Dwarf_InlineHighAddress_Problem(Scope scope) {
+ IAddress low = scope.getLowAddress();
+ IAddress highest = scope.getParent().getHighAddress();
+ Iterator<ILineEntry> lineEntries
+ = currentCompileUnitScope.getLineEntries().iterator();
+
+ ILineEntry entry;
+ do {
+ entry = lineEntries.next();
+ if (entry == null)
+ return null;
+ } while (low.compareTo(entry.getHighAddress()) > 0);
+
+ IAddress high = null;
+ IPath actualPath = entry.getFilePath(), otherPath = null;
+ int actualLine = entry.getLineNumber();
+ int thisLine = actualLine, lastLine = 0; // XXX false positive on uninitialized variable below causes needless initialization of lastLine = 0
+ boolean jumpedBack = false, jumpedAway = false;
+ OUTER:do {
+ IAddress nextHigh = entry.getHighAddress();
+ if (highest != null && nextHigh != null && highest.compareTo(nextHigh) < 0) {
+ nextHigh = entry.getLowAddress();
+ if (high == null || nextHigh.compareTo(high) < 0)
+ high = nextHigh;
+ break;
+ }
+ high = nextHigh;
+ if (!jumpedAway && otherPath == null)
+ lastLine = thisLine;
+
+ if (high == null)
+ break OUTER;
+
+ do {
+ entry = lineEntries.next();
+ if (entry == null)
+ break OUTER;
+ } while (high.equals(entry.getHighAddress()));
+
+ if (otherPath != null) {
+ if (entry.getFilePath().equals(actualPath)) // done with nesting
+ break;
+ } else if (!entry.getFilePath().equals(actualPath)) {// easiest test for done with inline
+ otherPath = entry.getFilePath();
+ } else {
+ thisLine = entry.getLineNumber();
+ if (!jumpedBack && !jumpedAway) {
+ if (thisLine < actualLine) {
+ jumpedBack = true;
+ } else if (thisLine > lastLine + 24) { // XXX false positive here causes needless init of lastLine = 0 above
+ jumpedAway = true;
+ }
+ } else if (jumpedBack) {
+ if (thisLine > actualLine) // jumped back ahead; done
+ break;
+ } else if (jumpedAway) {
+ if (thisLine < actualLine
+ || thisLine < lastLine
+ || thisLine > lastLine + 24)
+ break;
+ }
+ }
+ } while (entry != null);
+
+ return high;
+ }
+
+ /**
+ * Process a compile unit
+ *
+ * @param attributeList
+ * @param childrenPosition
+ */
+ private void processCompileUnit(CompilationUnitHeader header, boolean hasChildren, AttributeList attributeList) {
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ String compDir = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_comp_dir);
+ //System.out.println("processing compile unit: " + Integer.toHexString(header.debugInfoOffset) + ": " + name);
+
+ IPath filePath = fileHelper.normalizeFilePath(compDir, name);
+
+ currentCompileUnitScope = new DwarfCompileUnit(provider, moduleScope, filePath, null, null, header, hasChildren, attributeList);
+ header.scope = currentCompileUnitScope;
+ currentParentScope = currentCompileUnitScope;
+
+ setupAddresses(attributeList, currentCompileUnitScope);
+
+ // some compilers (RVCT) may generate multiple compile units for the
+ // same file.
+ List<ICompileUnitScope> matchingCompileUnits = provider.compileUnitsPerFile.get(filePath);
+
+ if (matchingCompileUnits == null) {
+ // first one. create it now.
+ matchingCompileUnits = new ArrayList<ICompileUnitScope>();
+ }
+
+ matchingCompileUnits.add(currentCompileUnitScope);
+ provider.compileUnitsPerFile.put(filePath, matchingCompileUnits);
+ provider.compileUnits.add(currentCompileUnitScope);
+
+ if (!currentCompileUnitScope.getHighAddress().isZero()) // has code
+ provider.sortedCompileUnitsWithCode.add(currentCompileUnitScope);
+
+ moduleScope.addChild(currentCompileUnitScope);
+
+ provider.registerCompileUnitHeader(currentCUHeader.debugInfoOffset, currentCUHeader);
+
+ if (provider.buildReferencedFilesList) {
+ provider.referencedFiles.add(filePath.toOSString());
+
+ // do a quick parse of the line table to get any other referenced files.
+ // note that even the full parse doesn't parse the line table information.
+ // that is calculated (and then cached) on demand
+ AttributeValue a = attributeList.getAttribute(DwarfConstants.DW_AT_stmt_list);
+ if (a != null) {
+ int stmtList = a.getValueAsInt();
+ quickParseLineInfo(stmtList, compDir);
+ }
+ }
+
+ // remove unused attributes
+ attributeList.attributeMap.remove(DwarfConstants.DW_AT_name);
+ //attributeList.attributeMap.remove(DwarfConstants.DW_AT_comp_dir); // needed later
+ attributeList.attributeMap.remove(DwarfConstants.DW_AT_low_pc);
+ attributeList.attributeMap.remove(DwarfConstants.DW_AT_high_pc);
+ attributeList.attributeMap.remove(DwarfConstants.DW_AT_ranges);
+ }
+
+ private void processLexicalBlock(long offset, AttributeList attributeList, boolean hasChildren) {
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+
+ if (!attributeList.hasCodeRangeAttributes()) {
+ // ignore any that don't have a valid range
+ return;
+ }
+
+ LexicalBlockScope lb = new LexicalBlockScope(name, currentParentScope, null, null);
+ setupAddresses(attributeList, lb);
+
+ currentParentScope.addChild(lb);
+ registerScope(offset, lb);
+ if (hasChildren)
+ currentParentScope = lb;
+ }
+
+ static class DereferencedAttributes {
+ public CompilationUnitHeader header;
+ public AttributeList attributeList;
+
+ public DereferencedAttributes(CompilationUnitHeader header,
+ AttributeList attributeList) {
+ this.header = header;
+ this.attributeList = attributeList;
+ }
+
+ }
+
+ /**
+ * DW_AT_abstract_origin and DW_AT_specification can refer to types in other
+ * compilation units. This will dynamically parse that CU if needed in order
+ * to get the attributes for the type.
+ *
+ * @param debugInfoOffset
+ * @return AttributeList or <code>null</code> (should not happen)
+ */
+ private DereferencedAttributes getDereferencedAttributes(AttributeList attributeList, short tag) {
+ CompilationUnitHeader providingCU = currentCUHeader;
+ AttributeValue derefLocation = attributeList.getAttribute(tag);
+ if (derefLocation == null)
+ return null;
+
+ // get the offset into the .debug_info section
+ long debugInfoOffset = derefLocation.getValueAsLong();
+ if (derefLocation.getActualForm() == DwarfConstants.DW_FORM_ref_addr) {
+ // this is already relative to the .debug_info section
+ } else {
+ // relative to the CU
+ debugInfoOffset += providingCU.debugInfoOffset;
+ }
+
+ AttributeList attributes = provider.functionsByOffset.get(debugInfoOffset);
+ if (attributes == null) {
+ // dereferenced function does not exist yet
+ providingCU = provider.fetchCompileUnitHeader(debugInfoOffset);
+ attributes = provider.functionsByOffset.get(debugInfoOffset);
+ if (attributes == null) {
+ // dereferenced entry is not parsed yet, perhaps because it's
+ // later in the current compile unit (despite Dwarf 3 spec saying
+ // that's not allowed)
+ IStreamBuffer buffer = getDwarfSection(DWARF_DEBUG_INFO);
+
+ if (buffer != null) {
+ buffer.position(debugInfoOffset);
+
+ try {
+ // get stored abbrev table, or read and parse an abbrev table
+ Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(providingCU.abbreviationOffset);
+
+ long code = read_unsigned_leb128(buffer);
+
+ if (code != 0) {
+ AbbreviationEntry entry = abbrevs.get(new Long(code));
+ if (entry != null) {
+ attributes = new AttributeList(entry, buffer, providingCU.addressSize, getDebugStrings());
+ }
+ }
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed1
+ + debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed2 + symbolFilePath, t);
+ }
+ }
+ }
+ } else {
+ providingCU = provider.fetchCompileUnitHeader(debugInfoOffset);
+ if (providingCU == null) {
+ assert(false);
+ return null;
+ }
+ }
+
+ if (attributes == null)
+ return null;
+
+ return new DereferencedAttributes(providingCU, attributes);
+ }
+
+ private void processSubprogram(long offset, AttributeList attributeList, boolean hasChildren) {
+ // if it's a declaration just add to the offsets map for later lookup
+ if (attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_declaration) > 0) {
+ provider.functionsByOffset.put(offset, attributeList);
+ return;
+ }
+
+ // functions with no high/low pc aren't real functions. just treat them
+ // as declarations as they will be pointed to by abstract_origin from
+ // another subprogram tag
+ if (!attributeList.hasCodeRangeAttributes()) {
+ provider.functionsByOffset.put(offset, attributeList);
+ return;
+ }
+
+ CompilationUnitHeader otherCU = null;
+
+ boolean isArtificial = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_artificial) > 0;
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ if (name.length() == 0) {
+ // no name. see if we can get it from a declaration
+ DereferencedAttributes deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_abstract_origin);
+ if (deref != null) {
+ // this should either have a name or point to another
+ // declaration
+ otherCU = deref.header;
+ AttributeList attributes = deref.attributeList;
+ name = attributes.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ isArtificial |= attributes.getAttributeValueAsInt(DwarfConstants. DW_AT_artificial) > 0;
+ if (name.length() == 0) {
+ deref = getDereferencedAttributes(attributes, DwarfConstants.DW_AT_specification);
+ if (deref != null) {
+ // this should either have a name or point to another
+ // declaration
+ otherCU = deref.header;
+ attributes = deref.attributeList;
+ name = attributes.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ isArtificial |= attributes.getAttributeValueAsInt(DwarfConstants. DW_AT_artificial) > 0;
+ }
+ }
+ }
+ }
+ if (name.length() == 0) {
+ DereferencedAttributes deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_specification);
+ if (deref != null) {
+ // this should either have a name or point to another
+ // declaration
+ otherCU = deref.header;
+ AttributeList attributes = deref.attributeList;
+ name = attributes.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ }
+ }
+
+ if (name.length() == 0) {
+ // the name should either be an attribute of the compile unit, or be
+ // in the declaration which according to the spec will always be
+ // before its definition in the Dwarf.
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_SubprogramNameNotFound1 + Long.toHexString(offset) +
+ DwarfMessages.DwarfInfoReader_SubprogramNameNotFound2, null);
+ return;
+ }
+
+ DwarfFunctionScope function = new DwarfFunctionScope(name, currentCompileUnitScope, null, null, null);
+ setupAddresses(attributeList, function);
+
+ Scope originalParentScope = currentParentScope;
+ registerScope(offset, function);
+ currentParentScope = function; // needed for getLocationProvider(), etc.
+
+ AttributeValue frameBaseAttribute = attributeList.getAttribute(DwarfConstants.DW_AT_frame_base);
+ ILocationProvider locationProvider = getLocationProvider(frameBaseAttribute);
+ function.setLocationProvider(locationProvider);
+
+ // Note: we may still have cases where DW_AT_low_pc and/or DW_AT_high_pc are 0x0
+ // (some "ignored inlined" functions in GCC). We want to keep track of their scope
+ // (though not store them in the CU), because child tag parses expect to find a parent into
+ // which to write their formal parameters and locals.
+ if (!function.getLowAddress().isZero() && !function.getHighAddress().isZero() && !isArtificial) {
+ // find the declaration location
+ int declLine = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_line);
+ int declColumn = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_column);
+ int declFileNum = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_file);
+
+ if (otherCU != null)
+ function.setDeclFile(otherCU.scope.getFileEntry(declFileNum));
+ else
+ function.setDeclFileNum(declFileNum);
+ function.setDeclLine(declLine);
+ function.setDeclColumn(declColumn);
+
+ currentCompileUnitScope.addChild(function);
+
+ // keep track of all functions by name for faster lookup
+ List<IFunctionScope> functions = provider.functionsByName.get(name);
+ if (functions == null) {
+ functions = new ArrayList<IFunctionScope>();
+ provider.functionsByName.put(name, functions);
+ }
+ functions.add(function);
+ }
+
+ // if the entry has no children, then restore the original parent scope
+ if (!hasChildren)
+ currentParentScope = originalParentScope;
+ }
+
+ /**
+ * Get the already-parsed or forward reference to a type from a DW_AT_type attribute, if present
+ * @param attributeMap the map of Long, AttributeValue from AttributeList or Object, Object from Type
+ * @return offset to referenced type or 0 if no type attribute
+ */
+ private IType getTypeOrReference(AttributeList attributeList, CompilationUnitHeader header) {
+ AttributeValue typeAttribute = attributeList.getAttribute(DwarfConstants.DW_AT_type);
+ if (typeAttribute == null)
+ return null;
+ return getTypeOrReference(typeAttribute, header);
+ }
+ /**
+ * Get the already-parsed or forward reference to a type from a DW_AT_type attribute, if present
+ * @param attributeMap the map of Long, AttributeValue from AttributeList or Object, Object from Type
+ * @return offset to referenced type or 0 if no type attribute
+ */
+ private IType getTypeOrReference(AttributeValue typeAttribute, CompilationUnitHeader header) {
+ if (typeAttribute == null)
+ return null;
+
+ // get the offset into the .debug_info section
+ long debugInfoOffset = typeAttribute.getValueAsLong();
+ if (typeAttribute.getActualForm() == DwarfConstants.DW_FORM_ref_addr) {
+ // this is already relative to the .debug_info section
+ } else {
+ debugInfoOffset += header.debugInfoOffset;
+ }
+
+ IType type = provider.typesByOffset.get(debugInfoOffset);
+ if (type == null) {
+ type = new ForwardTypeReference(provider, debugInfoOffset);
+ }
+ return type;
+ }
+
+ private void processInlinedSubroutine(long offset, AttributeList attributeList, boolean hasChildren) {
+ // functions with no high/low pc aren't real (probably an error)
+ if (!attributeList.hasCodeRangeAttributes()) {
+ return;
+ }
+
+ DereferencedAttributes deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_abstract_origin);
+ if (deref == null) {
+ if (attributeList.getAttribute(DwarfConstants.DW_AT_abstract_origin) != null) {
+ // TODO: GCC-E can reference forward tags (!) so we need to handle these another way
+ } else {
+ assert(false);
+ }
+ return;
+ }
+
+ CompilationUnitHeader otherCU = deref.header;
+ AttributeList origAttributes = deref.attributeList;
+
+ // this should either have a name or point to another
+ // declaration
+ String name = origAttributes.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ if (name.length() == 0) {
+ deref = getDereferencedAttributes(origAttributes, DwarfConstants.DW_AT_specification);
+ if (deref != null) {
+ // this should either have a name or point to another
+ // declaration
+ //otherCU = deref.header;
+ AttributeList declarationAttributes = deref.attributeList;
+ name = declarationAttributes.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ }
+ }
+
+ if (name.length() == 0) {
+ // the name should either be an attribute of the compile unit, or be
+ // in the declaration which according to the spec will always be
+ // before its definition in the Dwarf.
+ return;
+ }
+
+ // find the declaration location
+ int declLine = origAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_line);
+ int declColumn = origAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_column);
+ int declFileNum = origAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_file);
+
+ if (declFileNum == 0) {
+ assert(false);
+ return;
+ }
+
+ DwarfFunctionScope function = new DwarfFunctionScope(name, currentParentScope, null, null, null);
+ setupAddresses(attributeList, function);
+
+ function.setDeclFile(otherCU.scope.getFileEntry(declFileNum));
+ function.setDeclLine(declLine);
+ function.setDeclColumn(declColumn);
+
+ currentParentScope.addChild(function);
+
+ registerScope(offset, function);
+ if (hasChildren)
+ currentParentScope = function;
+
+ // keep track of all functions by name for faster lookup
+ List<IFunctionScope> functions = provider.functionsByName.get(name);
+ if (functions == null) {
+ functions = new ArrayList<IFunctionScope>();
+ provider.functionsByName.put(name, functions);
+ }
+ functions.add(function);
+ }
+
+ private void processSubroutineType(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ SubroutineType type = new SubroutineType(currentParentScope, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+
+ // TODO: associate parameters with this type in child tag parse
+
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processClassType(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+
+ // if the name is mangled, unmangle it
+ name = unmangleType(name);
+
+ // GCC may put the template parameter of an inherited class at the end of the name,
+ // so strip that off
+ // TODO: support template parameters at end of name or as separate DW_TAG_template_value_param
+ if (name.endsWith(">")) {
+ int templateStart = name.indexOf("<");
+ if (templateStart != -1)
+ name = name.substring(0, templateStart);
+ }
+
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+
+ ClassType type = new ClassType(name, currentParentScope, byteSize, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+ storeTypeByName(name, type);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processStructType(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+
+ // if the name is mangled, unmangle it
+ name = unmangleType(name);
+
+ StructType type = new StructType(name, currentParentScope, byteSize, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+ storeTypeByName(name, type);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processUnionType(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+
+ // if the name is mangled, unmangle it
+ name = unmangleType(name);
+
+ UnionType type = new UnionType(name, currentParentScope, byteSize, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+ storeTypeByName(name, type);
+
+ /*
+ * For an anonymous union, which has members accessible by methods in a class, ARM RVCT
+ * does not create an unnamed class member so you know the offset of the union's members.
+ * Instead, it just gives the anonymous union's type a name of the form "__C" followed
+ * by a number. And it places the union's DWARF info after all class member and inherited
+ * member DWARF info.
+ *
+ * E.g., when you have 2 named members and 2 anonymous unions in this order:
+ * 4-byte union <anonymous 1>
+ * 4-byte long long1
+ * 4-byte union <anonymous 2>
+ * 4-byte long long2
+ * ARM RVCT DWARF info says the class has 2 members:
+ * long1 at offset 4
+ * long2 at offset 12
+ * ARM RVCT DWARF info is in the order:
+ * member long1
+ * member long2
+ * type union <anonymous 1>
+ * type union <anonymous 2>
+ * So the rules for handling anonymous unions in RVCT DWARF are:
+ * 1st read offsets and sizes of non-anonymous members, which leaves offset holes
+ * for anonymous unions
+ * 2nd read anonymous union type info, which have compiler-generated names of
+ * "__C" following by a number, and assign unnamed members to offset holes
+ */
+ boolean isRVCTAnonymousUnion = false;
+ try {
+ isRVCTAnonymousUnion = name.startsWith("__C") && (name.length() > 3) && //$NON-NLS-1$
+ (name.charAt(3) != '-') && (Long.parseLong(name.substring(3)) > -1);
+ } catch (NumberFormatException nfe) {}
+
+ // if needed, create an "unnamed" member field with an offset to be determined later
+ if (isRVCTAnonymousUnion && getCompositeParent(typeToParentMap.get(currentParentType)) != null) {
+ ICompositeType compositeType = getCompositeParent(typeToParentMap.get(currentParentType));
+
+ // unnamed member accessibility depends on the enclosing composite's type -
+ // public for a struct or union, private for a class
+ int accessibility = ICompositeType.ACCESS_PUBLIC;
+ if (compositeType instanceof ClassType)
+ accessibility = ICompositeType.ACCESS_PRIVATE;
+
+ // empty field names confuse the expressions service
+ String fieldName = "$unnamed$" + (compositeType.fieldCount() + 1); //$NON-NLS-1$
+
+ // since we're generating a field, give it a -1 offset. We cannot tell the real
+ // offset until we determine offsets of all previously defined members - which
+ // may include inherited types not yet read in Dwarf info
+ FieldType fieldType = new FieldType(fieldName, currentParentScope, compositeType, -1, 0, 0,
+ byteSize, accessibility, null);
+ fieldType.setType(type);
+
+ // add the member to the deepest nested (last added) compositeNesting
+ // member
+ compositeType.addField(fieldType);
+ registerType(offset, fieldType, false);
+ }
+
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processInheritance(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ ICompositeType compositeType = getCompositeParent();
+
+ // The allowed attributes are DW_AT_type, DW_AT_data_member_location,
+ // and DW_AT_accessibility
+ long fieldsOffset = 0;
+ byte[] offsetBlock = attributeList.getAttributeValueAsBytes(DwarfConstants.DW_AT_data_member_location);
+ // unsigned LEB128 encoding
+ if (offsetBlock.length > 0 && offsetBlock[0] == DwarfConstants.DW_OP_plus_uconst) {
+ for (int i = 1, shift = 0; i < offsetBlock.length; i++) {
+ fieldsOffset += (offsetBlock[i] & 0x7f) << shift;
+ shift += 7;
+ }
+ }
+
+ // default accessibility is private
+ int accessibility = ICompositeType.ACCESS_PRIVATE;
+ if (attributeList.getAttribute(DwarfConstants.DW_AT_accessibility) != null) {
+ accessibility = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_accessibility);
+
+ if (accessibility == DwarfConstants.DW_ACCESS_public)
+ accessibility = ICompositeType.ACCESS_PUBLIC;
+ else if (accessibility == DwarfConstants.DW_ACCESS_private)
+ accessibility = ICompositeType.ACCESS_PRIVATE;
+ else
+ accessibility = ICompositeType.ACCESS_PROTECTED;
+ }
+
+ InheritanceType type = new InheritanceType(currentParentScope, accessibility, fieldsOffset, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+
+ // add the member to the deepest nested (last added) compositeNesting
+ // member
+ if (compositeType != null)
+ compositeType.addInheritance(type);
+
+ registerType(offset, type, hasChildren);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processField(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+
+ // GCC-E has fields like "_vptr.BaseClass" which will be a problem for us
+ // (since '.' is an operator); rename these here
+ name = name.replace('.', '$');
+
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+ int bitSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_bit_size);
+ int bitOffset = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_bit_offset);
+
+ long fieldOffset = 0;
+ byte[] offsetBlock = attributeList.getAttributeValueAsBytes(DwarfConstants.DW_AT_data_member_location);
+ // unsigned LEB128 encoding
+ if (offsetBlock.length > 0 && offsetBlock[0] == DwarfConstants.DW_OP_plus_uconst) {
+ for (int i = 1, shift = 0; i < offsetBlock.length; i++) {
+ fieldOffset += (offsetBlock[i] & 0x7f) << shift;
+ shift += 7;
+ }
+ }
+
+ ICompositeType compositeType = getCompositeParent();
+
+ // default accessibility depends on the composite type -
+ // public for a struct or union, private for a class
+ int accessibility = ICompositeType.ACCESS_PUBLIC;
+ if (attributeList.getAttribute(DwarfConstants.DW_AT_accessibility) != null) {
+ accessibility = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_accessibility);
+
+ if (accessibility == DwarfConstants.DW_ACCESS_public)
+ accessibility = ICompositeType.ACCESS_PUBLIC;
+ else if (accessibility == DwarfConstants.DW_ACCESS_private)
+ accessibility = ICompositeType.ACCESS_PRIVATE;
+ else
+ accessibility = ICompositeType.ACCESS_PROTECTED;
+ } else if (compositeType != null && compositeType instanceof ClassType)
+ accessibility = ICompositeType.ACCESS_PRIVATE;
+
+ // Empty fields confuse the expressions service (#10369)
+ if (name.length() == 0) {
+ if (compositeType != null) {
+ name = "$unnamed$" + (compositeType.fieldCount() + 1); //$NON-NLS-1$
+ } else {
+ name = "$unnamed$"; //$NON-NLS-1$
+ }
+ }
+
+ FieldType type = new FieldType(name, currentParentScope, compositeType, fieldOffset, bitSize, bitOffset,
+ byteSize, accessibility, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ // add the member to the deepest nested (last added) compositeNesting
+ // member
+ if (compositeType != null)
+ compositeType.addField(type);
+ registerType(offset, type, hasChildren);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processTemplateTypeParam(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ IType paramType = getTypeOrReference(attributeList.getAttribute(DwarfConstants.DW_AT_type), currentCUHeader);
+
+ TemplateParamType type = new TemplateParamType(name, paramType);
+
+ ICompositeType compositeType = getCompositeParent();
+
+ // add the template param to the deepest nested (last added) compositeNesting
+ // member
+ if (compositeType != null)
+ compositeType.addTemplateParam(type);
+ registerType(offset, type, hasChildren);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private ICompositeType getCompositeParent() {
+ return getCompositeParent(currentParentType);
+ }
+
+ private ICompositeType getCompositeParent(IType parent) {
+ while (parent != null) {
+ if (parent instanceof ICompositeType)
+ return ((ICompositeType) parent);
+ parent = typeToParentMap.get(parent);
+ }
+ return null;
+ }
+
+ private void processArrayType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+
+ ArrayType type = new ArrayType(name, currentParentScope, byteSize, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private IArrayType getArrayParent() {
+ IType parent = currentParentType;
+ while (parent != null) {
+ if (parent instanceof IArrayType)
+ return ((IArrayType) parent);
+ parent = typeToParentMap.get(parent);
+ }
+ return null;
+ }
+
+ private void processArrayBoundType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ long arrayBound = 0;
+ if (attributeList.getAttribute(DwarfConstants.DW_AT_upper_bound) != null)
+ arrayBound = attributeList.getAttributeValueAsLong(DwarfConstants.DW_AT_upper_bound) + 1;
+
+ ArrayBoundType type = new ArrayBoundType(currentParentScope, arrayBound);
+
+ IArrayType array = getArrayParent();
+ if (array == null)
+ throw new IllegalStateException();
+ array.addBound(type);
+
+ registerType(offset, type, hasChildren);
+
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processReferenceType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+
+ if (byteSize == 0)
+ byteSize = currentCUHeader.addressSize;
+
+ ReferenceType type = new ReferenceType(name, currentParentScope, byteSize, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processPointerType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+
+ if (byteSize == 0)
+ byteSize = currentCUHeader.addressSize;
+
+ PointerType type = new PointerType(name, currentParentScope, byteSize, null);
+ type.setType(getTypeOrReferenceOrVoid(attributeList));
+ registerType(offset, type, hasChildren);
+ storeTypeByName(name, type);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processPtrToMemberType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+
+ // unnamed data types don't get stored by name
+ if (name.length() == 0)
+ name = "" + offset;
+
+ PointerType type = new PointerType(name, currentParentScope, currentCUHeader.addressSize, null);
+ type.setType(getTypeOrReferenceOrVoid(attributeList));
+ registerType(offset, type, hasChildren);
+ storeTypeByName(name, type);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processConstType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ ConstType type = new ConstType(currentParentScope, null);
+ type.setType(getTypeOrReferenceOrVoid(attributeList));
+ registerType(offset, type, hasChildren);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processVolatileType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ VolatileType type = new VolatileType(currentParentScope, null);
+ type.setType(getTypeOrReferenceOrVoid(attributeList));
+ registerType(offset, type, hasChildren);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ // for void pointers, GCC will produce qualifiers and pointers without types or references,
+ // so create a void to be qualified or pointed to
+ private IType getTypeOrReferenceOrVoid(AttributeList attributeList) {
+ IType typeOrReference = getTypeOrReference(attributeList, currentCUHeader);
+ if (typeOrReference != null)
+ return typeOrReference;
+
+ if (moduleScope != null && voidType == null) {
+ voidType = new CPPBasicType("void", moduleScope, IBasicType.t_void, 0, 0, null);
+ }
+
+ if (voidType != null)
+ return voidType;
+
+ return new CPPBasicType("void", currentParentScope, IBasicType.t_void, 0, 0, null);
+ }
+
+ private void processEnumType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ // if the name is mangled, unmangle it
+ name = unmangleType(name);
+
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+
+ Enumeration type = new Enumeration(name, currentParentScope, byteSize, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+ storeTypeByName(name, type);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private Enumeration getEnumerationParent() {
+ IType parent = currentParentType;
+ while (parent != null) {
+ if (parent instanceof Enumeration)
+ return ((Enumeration) parent);
+ parent = typeToParentMap.get(parent);
+ }
+ return null;
+ }
+
+ private void processEnumerator(long offset, AttributeList attributeList) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ if (unmangler.isMangled(name)) {
+ try {
+ name = unmangler.unmangle(name);
+ } catch (UnmanglingException ue) {
+ }
+ }
+ long value = attributeList.getAttributeValueAsSignedLong(DwarfConstants.DW_AT_const_value);
+
+ Enumerator enumerator = new Enumerator(name, value);
+
+ Enumeration enumeration = getEnumerationParent();
+ if (enumeration == null)
+ throw new IllegalStateException();
+ enumeration.addEnumerator(enumerator);
+ ((Scope)enumeration.getScope()).addEnumerator(enumerator);
+
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(enumerator)); }
+ }
+
+ private void processTypeDef(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+
+ // if the name is mangled, unmangle it
+ name = unmangleType(name);
+
+ TypedefType type = new TypedefType(name, currentParentScope, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+ storeTypeByName(name, type);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processBasicType(long offset, AttributeList attributeList, boolean hasChildren) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); }
+
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size);
+
+ int baseType = IBasicType.t_unspecified;
+ int qualifierBits = 0;
+ int encoding = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_encoding);
+
+ switch (encoding) {
+ case DwarfConstants.DW_ATE_boolean:
+ baseType = ICPPBasicType.t_bool;
+ break;
+ case DwarfConstants.DW_ATE_float:
+ if (name.contains("float")) { //$NON-NLS-1$
+ baseType = IBasicType.t_float;
+ } else if (name.contains("long double")) { //$NON-NLS-1$
+ baseType = IBasicType.t_double;
+ qualifierBits |= ICPPBasicType.IS_LONG;
+ } else if (name.contains("double")) { //$NON-NLS-1$
+ baseType = IBasicType.t_double;
+ }
+ break;
+ case DwarfConstants.DW_ATE_signed:
+ baseType = IBasicType.t_int;
+ qualifierBits |= ICPPBasicType.IS_SIGNED;
+ if (name.contains("short")) { //$NON-NLS-1$
+ qualifierBits |= ICPPBasicType.IS_SHORT;
+ } else if (name.contains("long long")) { //$NON-NLS-1$
+ qualifierBits |= ICPPBasicType.IS_LONG_LONG;
+ } else if (name.contains("long")) { //$NON-NLS-1$
+ qualifierBits |= ICPPBasicType.IS_LONG;
+ }
+ break;
+ case DwarfConstants.DW_ATE_signed_char:
+ baseType = IBasicType.t_char;
+ qualifierBits |= ICPPBasicType.IS_SIGNED;
+ break;
+ case DwarfConstants.DW_ATE_unsigned:
+ baseType = IBasicType.t_int;
+ qualifierBits |= ICPPBasicType.IS_UNSIGNED;
+ if (name.contains("short")) { //$NON-NLS-1$
+ qualifierBits |= ICPPBasicType.IS_SHORT;
+ } else if (name.contains("long long")) { //$NON-NLS-1$
+ qualifierBits |= ICPPBasicType.IS_LONG_LONG;
+ } else if (name.contains("long")) { //$NON-NLS-1$
+ qualifierBits |= ICPPBasicType.IS_LONG;
+ }
+ break;
+ case DwarfConstants.DW_ATE_unsigned_char:
+ baseType = IBasicType.t_char;
+ qualifierBits |= ICPPBasicType.IS_UNSIGNED;
+ break;
+ case DwarfConstants.DW_ATE_complex_float:
+ qualifierBits |= ICPPBasicType.IS_COMPLEX;
+ if (name.contains("float")) { //$NON-NLS-1$
+ baseType = IBasicType.t_float;
+ } else if (name.contains("long double")) { //$NON-NLS-1$
+ baseType = IBasicType.t_double;
+ qualifierBits |= ICPPBasicType.IS_LONG;
+ } else if (name.contains("double")) { //$NON-NLS-1$
+ baseType = IBasicType.t_double;
+ }
+ break;
+ case DwarfConstants.DW_ATE_imaginary_float:
+ qualifierBits |= ICPPBasicType.IS_IMAGINARY;
+ if (name.contains("float")) { //$NON-NLS-1$
+ baseType = IBasicType.t_float;
+ } else if (name.contains("long double")) { //$NON-NLS-1$
+ baseType = IBasicType.t_double;
+ qualifierBits |= ICPPBasicType.IS_LONG;
+ } else if (name.contains("double")) { //$NON-NLS-1$
+ baseType = IBasicType.t_double;
+ }
+ break;
+ case DwarfConstants.DW_ATE_void:
+ baseType = IBasicType.t_void;
+ break;
+ case DwarfConstants.DW_ATE_address:
+ case DwarfConstants.DW_ATE_packed_decimal:
+ case DwarfConstants.DW_ATE_numeric_string:
+ case DwarfConstants.DW_ATE_edited:
+ case DwarfConstants.DW_ATE_signed_fixed:
+ case DwarfConstants.DW_ATE_unsigned_fixed:
+ case DwarfConstants.DW_ATE_decimal_float:
+ default:
+ break;
+ }
+
+ // RVCT has interesting conceptions about "encoding" here. Be sure not to get confused later.
+ if (name.equals("void") && byteSize == 0) //$NON-NLS-1$
+ baseType = IBasicType.t_void;
+
+ CPPBasicType type = new CPPBasicType(name, currentParentScope, baseType, qualifierBits, byteSize, null);
+ type.setType(getTypeOrReference(attributeList, currentCUHeader));
+ registerType(offset, type, hasChildren);
+ storeTypeByName(name, type);
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); }
+ }
+
+ private void processVariable(long offset, AttributeList attributeList, boolean isParameter) {
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(attributeList)); }
+
+ AttributeValue locationAttribute = attributeList.getAttribute(DwarfConstants.DW_AT_location);
+ ILocationProvider locationProvider = getLocationProvider(locationAttribute);
+ if (locationProvider == null) {
+ // No location means either this is a placeholder (in a subprogram declaration)
+ // or it may have been optimized out. See section
+ // 2.6 of the Dwarf3 spec. for now we're ignoring it but we may be able
+ // to show it in the view with some special decoration to indicate that
+ // it's been optimized out
+
+ // assume it is a forward reference if we're inside a function (formal_parameter or local)...
+ provider.functionsByOffset.put(offset, attributeList);
+ return;
+ }
+
+ // variables can have abstract origins with most of their contents
+ CompilationUnitHeader otherCU = currentCUHeader;
+ AttributeList otherAttributes = attributeList;
+ String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ if (name.length() == 0) {
+ // no name.
+ // if there is a DW_AT_specification or DW_AT_abstract_origin attribute, use it to get variable attributes
+ DereferencedAttributes deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_specification);
+ if (deref == null)
+ deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_abstract_origin);
+ if (deref != null) {
+ // this should either have a name or point to another
+ // declaration
+ otherCU = deref.header;
+ otherAttributes = deref.attributeList;
+ name = otherAttributes.getAttributeValueAsString(DwarfConstants.DW_AT_name);
+ }
+ }
+
+ boolean global = (otherAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_external) == 1);
+
+ // if the name is mangled, unmangle it
+ if (name.startsWith("_Z")) {
+ name = unmangle(name);
+ } else if (global) {
+ // GCCE uses DW_AT_MIPS_linkage_name for the mangled name of an externally visible variable
+ String mangledName = otherAttributes.getAttributeValueAsString(DwarfConstants.DW_AT_MIPS_linkage_name);
+ if (unmangler.isMangled(mangledName)) {
+ try {
+ name = unmangler.unmangle(mangledName);
+ } catch (UnmanglingException ue) {
+ }
+ }
+ }
+
+ IType type = getTypeOrReference(otherAttributes.getAttribute(DwarfConstants.DW_AT_type), otherCU);
+ if (type != null) {
+ long startScope = attributeList.getAttributeValueAsLong(DwarfConstants.DW_AT_start_scope);
+ boolean isDeclared = otherAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_artificial) <= 0;
+
+ int definingFileNum = otherAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_file);
+ if (definingFileNum > 0 && attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_declaration) > 0) {
+ // variable is declared here, but not defined here
+ definingFileNum = 0;
+ }
+
+ IPath definingFile = null;
+
+ // find the file it's defined in
+ if (definingFileNum > 0) {
+ // find the enclosing compile unit to get access to its list of
+ // .debug_line file names
+ IScope cuScope = currentParentScope;
+ while (cuScope != null && !(cuScope instanceof DwarfCompileUnit))
+ cuScope = cuScope.getParent();
+
+ if (cuScope != null) {
+ definingFile = ((DwarfCompileUnit) cuScope).getFileEntry(definingFileNum);
+ }
+ }
+
+ DwarfVariable variable = new DwarfVariable(name,
+ global ? moduleScope : currentParentScope,
+ locationProvider,
+ type,
+ isDeclared,
+ definingFile);
+
+ variable.setStartScope(startScope);
+
+ if (isParameter) {
+ if (currentParentScope instanceof FunctionScope) {
+ ((FunctionScope) currentParentScope).addParameter(variable);
+ } else {
+ assert (false);
+ }
+ } else {
+ if (global) {
+ // add global variables to the module scope
+ moduleScope.addVariable(variable);
+ // AND to the CU scope
+ if (currentCompileUnitScope != null) {
+ currentCompileUnitScope.addVariable(variable);
+ }
+ } else {
+ // the parent scope could be compile unit, function or
+ // lexical block
+ currentParentScope.addVariable(variable);
+ }
+
+ // keep track of all variables by name for faster lookup
+ List<IVariable> variables = provider.variablesByName.get(name);
+ if (variables == null) {
+ variables = new ArrayList<IVariable>();
+ provider.variablesByName.put(name, variables);
+ }
+ variables.add(variable);
+ }
+
+ if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(variable)); }
+ }
+ }
+
+
+ private ILocationProvider getLocationProvider(AttributeValue locationValue) {
+ if (locationValue != null) {
+ byte actualForm = locationValue.getActualForm();
+ if (actualForm == DwarfConstants.DW_FORM_data4) {
+ // location list
+ Collection<LocationEntry> entryList = getLocationRecord(locationValue.getValueAsLong());
+ if (entryList != null) {
+ return new LocationList(entryList.toArray(new LocationEntry[entryList.size()]),
+ exeReader.getByteOrder(),
+ currentCUHeader.addressSize, currentParentScope);
+ }
+ } else if (actualForm == DwarfConstants.DW_FORM_block
+ || actualForm == DwarfConstants.DW_FORM_block1
+ || actualForm == DwarfConstants.DW_FORM_block2
+ || actualForm == DwarfConstants.DW_FORM_block4) {
+ // location expression
+ IStreamBuffer locationData = new MemoryStreamBuffer(locationValue.getValueAsBytes(), exeReader.getByteOrder());
+ return new LocationExpression(locationData,
+ currentCUHeader.addressSize,
+ currentParentScope);
+ } else {
+ // should not happen according to the spec
+ assert (false);
+ }
+ }
+
+ return null;
+ }
+
+ private void dumpSymbols() {
+ if (DEBUG) {
+ PrintStream out = null;
+ try {
+ out = new PrintStream(new File(dumpFileName));
+ } catch (FileNotFoundException e) {
+ System.out.println(DwarfMessages.DwarfInfoReader_DumpFileOpenOrCreateFailed + dumpFileName);
+ return;
+ }
+
+ // If to write to console
+ // PrintStream out = System.out;
+
+ out.println("Module - " + symbolFilePath);
+ out.println(" Variables - " + moduleScope.getVariables().size());
+ out.println(" Compile units - " + moduleScope.getChildren().size());
+ out.println();
+
+ for (IScope cu : moduleScope.getChildren()) {
+ out.println(" Compile unit - " + cu.toString());
+ out.println(" Variables - " + cu.getVariables().size());
+ out.println(" Functions - " + cu.getChildren().size());
+ out.println();
+
+ for (IScope func : cu.getChildren()) {
+ out.println(" Function - " + func.toString());
+ out.println(" Variables - " + func.getVariables().size());
+ out.println(" Parameters - " + ((IFunctionScope) func).getParameters().size());
+ out.println(" Lexical blocks - " + func.getChildren().size());
+ out.println();
+
+ // not accurate: can contain IFunctionScope too!
+ for (IScope block : func.getChildren()) {
+ out.println(" Lexical block - " + block.toString());
+ out.println(" Variables - " + block.getVariables().size());
+ out.println();
+ }
+ }
+ }
+
+ out.close();
+ }
+ }
+
+ public void parseForFrameIndices() {
+ synchronized (provider) {
+ if (!provider.frameDescEntries.isEmpty())
+ return;
+
+ IExecutableSection frameSection = exeReader.findExecutableSection(DWARF_DEBUG_FRAME);
+ if (frameSection == null)
+ return;
+
+ IStreamBuffer buffer = frameSection.getBuffer();
+ buffer.position(0);
+
+ int addressSize = 4; // TODO: 64-bit Dwarf
+ long cie_id = addressSize == 4 ? 0xffffffff : ~0L;
+
+ // in the first pass, just get a mapping of PC ranges to FDEs,
+ // so we can locate entries quickly (don't pre-parse CIEs or decompile FDE instructions yet)
+ while (buffer.position() < buffer.capacity()) {
+ try {
+ long fdePtr = buffer.position();
+ long headerLength = readAddress(buffer, addressSize);
+ long nextPosition = buffer.position() + headerLength;
+
+ long ciePtr = readAddress(buffer, addressSize);
+ if (ciePtr != cie_id) {
+ long initialLocation = readAddress(buffer, addressSize);
+ long addressRange = readAddress(buffer, addressSize);
+ IStreamBuffer instructions = buffer.wrapSubsection(nextPosition - buffer.position());
+ IRangeList.Entry entry = new IRangeList.Entry(initialLocation, initialLocation + addressRange);
+ FrameDescriptionEntry fde = new FrameDescriptionEntry(fdePtr, ciePtr,
+ entry.low, entry.high,
+ instructions, addressSize);
+ provider.frameDescEntries.put(entry, fde);
+ }
+
+ buffer.position(nextPosition);
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_FrameIndicesReadFailed, t);
+ break;
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Parse a CIE
+ * @param ciePtr
+ * @param addressSize
+ * @param framePC
+ * @return the CIE or <code>null</code> in case of error
+ */
+ public CommonInformationEntry parseCommonInfoEntry(Long ciePtr, int addressSize, IAddress framePC) throws IOException {
+ synchronized (provider) {
+ IExecutableSection frameSection = exeReader.findExecutableSection(DWARF_DEBUG_FRAME);
+ if (frameSection == null)
+ return null;
+
+ IStreamBuffer buffer = frameSection.getBuffer();
+ buffer.position(ciePtr);
+
+ long headerLength = readAddress(buffer, addressSize);
+ if (headerLength > buffer.capacity()) {
+ assert(false);
+ return null;
+ }
+
+ long nextPosition = buffer.position() + headerLength;
+
+ /* cie_id = */ readAddress(buffer, addressSize);
+
+ byte version = buffer.get();
+ String augmentation = readString(buffer);
+ long codeAlignmentFactor = read_unsigned_leb128(buffer);
+ long dataAlignmentFactor = read_signed_leb128(buffer);
+ int returnAddressRegister = version < 3 ? buffer.get() & 0xff : (int) read_unsigned_leb128(buffer);
+
+
+ IStreamBuffer instructions = buffer.wrapSubsection(nextPosition - buffer.position());
+
+ String producer = null;
+ ICompileUnitScope cuScope = provider.getCompileUnitForAddress(framePC);
+ if (cuScope instanceof DwarfCompileUnit)
+ producer = ((DwarfCompileUnit) cuScope).getAttributeList().getAttributeValueAsString(DwarfConstants.DW_AT_producer);
+
+ return new CommonInformationEntry(codeAlignmentFactor, dataAlignmentFactor,
+ returnAddressRegister, version, instructions, addressSize,
+ producer, augmentation);
+ }
+ }
+
+ private void storeTypeByName(String name, IType type) {
+ if (name.length() == 0)
+ return;
+
+ List<IType> typeList = provider.typesByName.get(name);
+ if (typeList == null) {
+ typeList = new ArrayList<IType>();
+
+ // for a template, remove extra spaces and composite type names (e.g., "class")
+ if (name.indexOf('<') != -1) {
+ while (name.contains(" ")) //$NON-NLS-1$
+ name = name.replaceAll(" ", " "); //$NON-NLS-1$ //$NON-NLS-2$
+ name = name.replaceAll(", ", ","); //$NON-NLS-1$ //$NON-NLS-2$
+ name = name.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ name = name.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ name = name.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ provider.typesByName.put(name, typeList);
+ }
+ typeList.add(type);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import org.eclipse.osgi.util.NLS;
+
+public class DwarfMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfMessages"; //$NON-NLS-1$
+
+ public static String DwarfDebugInfoProvider_FailedToReadCIE;
+
+ public static String DwarfDebugInfoProvider_DwarfProviderFor;
+
+ public static String DwarfDebugInfoProvider_NotParsingType1;
+ public static String DwarfDebugInfoProvider_NotParsingType2;
+
+ public static String DwarfDebugInfoProvider_CannotResolveCompUnit1;
+ public static String DwarfDebugInfoProvider_CannotResolveCompUnit2;
+
+ public static String DwarfDebugInfoProvider_UnhandledType;
+
+ public static String DwarfFrameRegisters_CannotReadRegister;
+
+ public static String DwarfFrameRegisters_CannotWriteRegister;
+
+ public static String DwarfFrameRegisters_ErrorCalculatingLocation;
+
+ public static String DwarfFrameRegisters_NoCommonInfoEntry;
+
+ public static String DwarfInfoReader_DumpFileOpenOrCreateFailed;
+
+ public static String DwarfInfoReader_FrameIndicesReadFailed;
+
+ public static String DwarfInfoReader_ParseDebugInfoSectionFailed1;
+ public static String DwarfInfoReader_ParseDebugInfoSectionFailed2;
+
+ public static String DwarfInfoReader_ParseSectionSourceFilesFailed1;
+ public static String DwarfInfoReader_ParseSectionSourceFilesFailed2;
+
+ public static String DwarfInfoReader_ParseTraceInfoSectionFailed1;
+ public static String DwarfInfoReader_ParseTraceInfoSectionFailed2;
+
+ public static String DwarfInfoReader_RangeReadFailed;
+
+ public static String DwarfInfoReader_ReadDebugInfo;
+
+ public static String DwarfInfoReader_ReadingSymbolInfo;
+
+ public static String DwarfInfoReader_ReadType;
+
+ public static String DwarfInfoReader_SubprogramNameNotFound1;
+ public static String DwarfInfoReader_SubprogramNameNotFound2;
+
+ public static String DwarfInfoReader_TraceAddressParse1;
+ public static String DwarfInfoReader_TraceAddressParse2;
+
+ public static String DwarfInfoReader_TraceAddressesParseFor;
+ public static String DwarfInfoReader_TraceFinishedAddressesParse;
+
+ public static String DwarfInfoReader_TraceAddressParseFor;
+ public static String DwarfInfoReader_TraceFinishedAddressParse;
+
+ public static String DwarfInfoReader_TraceFinishedInitialParse;
+
+ public static String DwarfInfoReader_TraceFinishedQuickParse;
+
+ public static String DwarfInfoReader_TraceInitialParseFor;
+
+ public static String DwarfInfoReader_TraceParseTypes1;
+ public static String DwarfInfoReader_TraceParseTypes2;
+
+ public static String DwarfInfoReader_TraceQuickParse;
+
+ public static String DwarfInfoReader_TraceScopeAddressParse1;
+ public static String DwarfInfoReader_TraceScopeAddressParse2;
+
+ public static String DwarfInfoReader_TraceTypeParse1;
+ public static String DwarfInfoReader_TraceTypeParse2;
+
+ public static String UnknownVariableAddress;
+ public static String NotImplementedFormat;
+ public static String InternalErrorFormat;
+
+ public static String LocationExpression_BadStackSize;
+
+ public static String LocationExpression_DW_OP;
+
+ public static String LocationExpression_MultiRegisterVariable;
+
+ public static String LocationExpression_UnexpectedOperand;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, DwarfMessages.class);
+ }
+
+ private DwarfMessages() {
+ }
+}
--- /dev/null
+###############################################################################
+# Copyright (c) 2010 Nokia 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:
+# Nokia - Initial API and implementation
+###############################################################################
+UnknownVariableAddress=Unknown variable address
+NotImplementedFormat=Not implemented ({0})
+DwarfDebugInfoProvider_CannotResolveCompUnit1=Cannot resolve compilation unit header for type at
+DwarfDebugInfoProvider_CannotResolveCompUnit2=\ in
+DwarfDebugInfoProvider_DwarfProviderFor=DWARF debug info provider for
+DwarfDebugInfoProvider_FailedToReadCIE=Failed to read CIE at
+DwarfDebugInfoProvider_NotParsingType1=Cannot parse type at
+DwarfDebugInfoProvider_NotParsingType2=\ in
+DwarfDebugInfoProvider_UnhandledType=<<unhandled type>>
+DwarfFrameRegisters_CannotReadRegister=Cannot read register {0} at {1}
+DwarfFrameRegisters_CannotWriteRegister=Cannot write register {0} at {1}
+DwarfFrameRegisters_ErrorCalculatingLocation=Internal error calculating location
+DwarfFrameRegisters_NoCommonInfoEntry=No CIE for {0}
+DwarfInfoReader_DumpFileOpenOrCreateFailed=Failed to open or create the dump file:
+DwarfInfoReader_FrameIndicesReadFailed=Failed to read frame indices
+DwarfInfoReader_ParseDebugInfoSectionFailed1=Failed to parse debug info from section
+DwarfInfoReader_ParseDebugInfoSectionFailed2=\ in file
+DwarfInfoReader_ParseSectionSourceFilesFailed1=Failed to parse source files from section
+DwarfInfoReader_ParseSectionSourceFilesFailed2=\ in file
+DwarfInfoReader_ParseTraceInfoSectionFailed1=Failed to parse type debug info from section
+DwarfInfoReader_ParseTraceInfoSectionFailed2=\ in file
+DwarfInfoReader_RangeReadFailed=Failed to read ranges
+DwarfInfoReader_ReadDebugInfo=Read Debug Info
+DwarfInfoReader_ReadingSymbolInfo=Reading Debug Symbol Information:
+DwarfInfoReader_ReadType=Read type
+DwarfInfoReader_SubprogramNameNotFound1=Name of subprogram at
+DwarfInfoReader_SubprogramNameNotFound2=\ not found
+DwarfInfoReader_TraceAddressParse1=Address parse for
+DwarfInfoReader_TraceAddressParse2=\ :
+DwarfInfoReader_TraceAddressesParseFor=Addresses parse for
+DwarfInfoReader_TraceFinishedAddressesParse=Finished addresses parse
+DwarfInfoReader_TraceAddressParseFor=Address parse for
+DwarfInfoReader_TraceFinishedAddressParse=Finished address parse
+DwarfInfoReader_TraceFinishedInitialParse=Finished initial parse
+DwarfInfoReader_TraceFinishedQuickParse=Finished quick parse
+DwarfInfoReader_TraceInitialParseFor=Initial parse for
+DwarfInfoReader_TraceParseTypes1=Parsing types for
+DwarfInfoReader_TraceParseTypes2=\ @
+DwarfInfoReader_TraceQuickParse=Quick parse for
+DwarfInfoReader_TraceScopeAddressParse1=Address parse of
+DwarfInfoReader_TraceScopeAddressParse2=\ @
+DwarfInfoReader_TraceTypeParse1=Type parse of
+DwarfInfoReader_TraceTypeParse2=\ :
+InternalErrorFormat=Internal error ({0})
+LocationExpression_BadStackSize=Stack size not 1
+LocationExpression_DW_OP=DW_OP
+LocationExpression_MultiRegisterVariable=multi-register variable
+LocationExpression_UnexpectedOperand=Unexpected DW_OP
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
+import org.eclipse.cdt.debug.edc.internal.symbols.ModuleLineEntryProvider;
+import org.eclipse.cdt.debug.edc.internal.symbols.Scope;
+import org.eclipse.cdt.debug.edc.services.IFrameRegisterProvider;
+import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.IModuleLineEntryProvider;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.cdt.utils.Addr32;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * This represents the high-level view of an executable module's symbolics.
+ */
+public class DwarfModuleScope extends Scope implements IModuleScope {
+
+ private final DwarfDebugInfoProvider provider;
+ private ModuleLineEntryProvider lineEntryMapper;
+
+ public DwarfModuleScope(DwarfDebugInfoProvider provider) {
+ super("", null, null, null);
+ this.provider = provider;
+
+ if (provider == null) {
+ lowAddress = Addr32.ZERO;
+ highAddress = Addr32.ZERO;
+ } else {
+ name = provider.getSymbolFile().lastSegment();
+
+ // for now, use the code sections' ranges
+ lowAddress = Addr32.MAX;
+ highAddress = Addr32.ZERO;
+ for (ISection section : provider.getExecutableSymbolicsReader().getSections()) {
+ if (section.getProperties().get(ISection.PROPERTY_NAME).equals(ISection.NAME_DATA)) {
+ if (section.getLinkAddress().compareTo(lowAddress) < 0)
+ lowAddress = section.getLinkAddress();
+ IAddress end = section.getLinkAddress().add(section.getSize());
+ if (end.compareTo(highAddress) > 0)
+ highAddress = end;
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IModuleScope#getSymbolFile()
+ */
+ public IPath getSymbolFile() {
+ return provider.getSymbolFile();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IModuleScope#getCompileUnitForAddress(org.eclipse.cdt.core.IAddress)
+ */
+ public ICompileUnitScope getCompileUnitForAddress(IAddress linkAddress) {
+ if (provider != null)
+ return provider.getCompileUnitForAddress(linkAddress);
+ else
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IModuleScope#getCompileUnitForFile(org.eclipse.core.runtime.IPath)
+ */
+ public List<ICompileUnitScope> getCompileUnitsForFile(IPath filePath) {
+ if (provider != null)
+ return provider.getCompileUnitsForFile(filePath);
+ else
+ return Collections.emptyList();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IModuleScope#getFunctionsByName(java.lang.String)
+ */
+ public Collection<IFunctionScope> getFunctionsByName(String name) {
+ if (provider != null)
+ return provider.getFunctionsByName(name);
+ else
+ return Collections.emptyList();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IModuleScope#getVariablesByName(java.lang.String)
+ */
+ public Collection<IVariable> getVariablesByName(String name, boolean globalsOnly) {
+ // at the module scope, the variables we want are global
+ if (provider != null)
+ return provider.getVariablesByName(name, globalsOnly);
+ else
+ return Collections.emptyList();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getScopeAtAddress(org.eclipse.cdt.core.IAddress)
+ */
+ @Override
+ public IScope getScopeAtAddress(IAddress linkAddress) {
+ ensureParsed();
+ return super.getScopeAtAddress(linkAddress);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getEnumerators()
+ */
+ @Override
+ public Collection<IEnumerator> getEnumerators() {
+ ensureParsed();
+ return super.getEnumerators();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getChildren()
+ */
+ @Override
+ public Collection<IScope> getChildren() {
+ ensureParsed();
+ return super.getChildren();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getVariables()
+ */
+ @Override
+ public Collection<IVariable> getVariables() {
+ ensureParsedForVariables();
+ return super.getVariables();
+ }
+
+ private void ensureParsed() {
+ if (provider != null)
+ provider.ensureParsedInitially();
+ }
+
+ private void ensureParsedForVariables() {
+ if (provider != null)
+ provider.ensureParsedForVariables();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IModuleScope#getTypes()
+ */
+ public Collection<IType> getTypes() {
+ if (provider != null)
+ return provider.getTypes();
+ else
+ return Collections.emptyList();
+ }
+
+ /**
+ * Fixup ranges after a full-module address parse.
+ */
+ /*
+ public void fixupRanges() {
+ System.out.println("Fixing up ranges for " + getChildren().size() + " children");
+
+ // fix up scope addresses in case compiler doesn't generate them.
+ for (IScope cu : getChildren()) {
+ ((DwarfCompileUnit) cu).fixupRanges();
+ }
+
+ IAddress newLowAddress = new Addr64(BigInteger.valueOf(0xFFFFFFFFL));
+ IAddress newHighAddress = new Addr64(BigInteger.valueOf(0));
+
+ // now fix up the module scope
+ for (IScope cu : getChildren()) {
+ if (cu.getLowAddress().compareTo(newLowAddress) < 0) {
+ newLowAddress = cu.getLowAddress();
+ }
+
+ if (cu.getHighAddress().compareTo(newHighAddress) > 0) {
+ newHighAddress = cu.getHighAddress();
+ }
+ }
+
+ this.lowAddress = newLowAddress;
+ this.highAddress = newHighAddress;
+ }
+ */
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.IModuleScope#getModuleLineEntryProvider()
+ */
+ public IModuleLineEntryProvider getModuleLineEntryProvider() {
+ if (lineEntryMapper == null) {
+ lineEntryMapper = new ModuleLineEntryProvider();
+
+ // handle the currently parsed children
+ for (IScope scope : getChildren()) {
+ if (scope instanceof ICompileUnitScope) {
+ lineEntryMapper.addCompileUnit((ICompileUnitScope) scope);
+ }
+ }
+ }
+ return lineEntryMapper;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#addChild(org.eclipse.cdt.debug.edc.internal.symbols.IScope)
+ */
+ @Override
+ public void addChild(IScope scope) {
+ super.addChild(scope);
+
+ // initial module scope range is a guess
+ mergeScopeRange(scope);
+
+ if (scope instanceof ICompileUnitScope && lineEntryMapper != null) {
+ lineEntryMapper.addCompileUnit((ICompileUnitScope) scope);
+ }
+ }
+
+ /**
+ * Update info when a compile unit has been fully parsed.
+ * @param scope
+ */
+ public void updateLineInfoForCU(ICompileUnitScope scope) {
+ // be sure the decl entries for inlined functions are detected
+ if (lineEntryMapper != null)
+ lineEntryMapper.addCompileUnit(scope);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.symbols.IModuleScope#getFrameRegisterProvider()
+ */
+ public IFrameRegisterProvider getFrameRegisterProvider() {
+ if (provider != null)
+ return provider.getFrameRegisterProvider();
+ else
+ return null;
+ }
+
+ /**
+ * Help garbage collection
+ */
+ public void dispose() {
+ lineEntryMapper = null;
+ super.dispose();
+ }
+}
+
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.Variable;
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.core.runtime.IPath;
+
+public class DwarfVariable extends Variable {
+
+ public DwarfVariable(String name, IScope scope, ILocationProvider locationProvider,
+ IType type, boolean isDeclared, IPath definingFile) {
+ super(name, scope, null, locationProvider, isDeclared, definingFile);
+
+ this.type = type;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.nio.ByteOrder;
+import java.util.Collection;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.BaseExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider;
+import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSection;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.ISymbol;
+import org.eclipse.cdt.debug.edc.symbols.IUnmangler;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+
+/**
+ * This class handles the high-level retrieval of symbolic information, using
+ * {@link IDebugInfoProvider} and {@link IExecutableSymbolicsReader}.
+ */
+public class EDCSymbolReader implements IEDCSymbolReader {
+
+ private static final String[] NO_SOURCE_FILES = new String[0];
+
+ private static final IModuleScope EMPTY_MODULE_SCOPE = new DwarfModuleScope(null);
+
+ private IDebugInfoProvider debugInfoProvider;
+ private IExecutableSymbolicsReader exeSymReader;
+
+ public EDCSymbolReader(IExecutableSymbolicsReader exeSymReader, IDebugInfoProvider debugInfoProvider) {
+ if (exeSymReader == null)
+ throw new IllegalArgumentException();
+
+ this.exeSymReader = exeSymReader;
+ this.debugInfoProvider = debugInfoProvider;
+
+ // we expect these two files to be the same
+ if (debugInfoProvider != null)
+ if (!debugInfoProvider.getSymbolFile().equals(exeSymReader.getSymbolFile()))
+ throw new IllegalArgumentException();
+ }
+
+ public void shutDown() {
+ if (exeSymReader != null) {
+ exeSymReader.dispose();
+ exeSymReader = null;
+ }
+ if (debugInfoProvider != null) {
+ debugInfoProvider.dispose();
+ debugInfoProvider = null;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSymbolicsReader#dispose()
+ */
+ public void dispose() {
+ exeSymReader.dispose();
+ }
+
+ public Collection<IExecutableSection> getExecutableSections() {
+ return exeSymReader.getExecutableSections();
+ }
+
+ public IExecutableSection findExecutableSection(String sectionName) {
+ return exeSymReader.findExecutableSection(sectionName);
+ }
+
+ public Collection<ISection> getSections() {
+ return exeSymReader.getSections();
+ }
+
+ public IAddress getBaseLinkAddress() {
+ return exeSymReader.getBaseLinkAddress();
+ }
+
+ public long getModificationDate() {
+ return exeSymReader.getModificationDate();
+ }
+
+ public Collection<ISymbol> findSymbols(String name) {
+ return exeSymReader.findSymbols(name);
+ }
+
+ public Collection<ISymbol> findUnmangledSymbols(String name) {
+ return exeSymReader.findUnmangledSymbols(name);
+ }
+
+ public Collection<ISymbol> getSymbols() {
+ return exeSymReader.getSymbols();
+ }
+
+ public ISymbol getSymbolAtAddress(IAddress linkAddress) {
+ return exeSymReader.getSymbolAtAddress(linkAddress);
+ }
+
+ public IPath getSymbolFile() {
+ return debugInfoProvider != null ? debugInfoProvider.getSymbolFile() :
+ (exeSymReader != null ? exeSymReader.getSymbolFile() : null);
+ }
+
+ public ByteOrder getByteOrder() {
+ return exeSymReader.getByteOrder();
+ }
+
+ public IModuleScope getModuleScope() {
+ if (debugInfoProvider != null)
+ return debugInfoProvider.getModuleScope();
+ else
+ return EMPTY_MODULE_SCOPE;
+ }
+
+ public String[] getSourceFiles() {
+ if (debugInfoProvider != null)
+ {
+ final String[][] resultHolder = new String[1][];
+ Job quickParseJob = new Job("Reading Debug Symbol Information: " + debugInfoProvider.getSymbolFile().toOSString()) {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ String[] sourceFiles = getSourceFiles(monitor);
+ resultHolder[0] = sourceFiles;
+ return Status.OK_STATUS;
+ }
+ };
+
+ try {
+ quickParseJob.schedule();
+ quickParseJob.join();
+ } catch (InterruptedException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ return resultHolder[0];
+ }
+ else
+ return NO_SOURCE_FILES;
+ }
+
+ public String[] getSourceFiles(IProgressMonitor monitor) {
+ if (debugInfoProvider != null)
+ return debugInfoProvider.getSourceFiles(monitor);
+ else
+ return NO_SOURCE_FILES;
+ }
+
+ public boolean hasRecognizedDebugInformation() {
+ return debugInfoProvider != null;
+ }
+
+ public IDebugInfoProvider getDebugInfoProvider() {
+ return debugInfoProvider;
+ }
+
+ public IUnmangler getUnmangler() {
+ if (exeSymReader instanceof BaseExecutableSymbolicsReader)
+ return ((BaseExecutableSymbolicsReader) exeSymReader).getUnmangler();
+ return null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.util.Arrays;
+
+public class LocationEntry {
+
+ private final long highPC;
+ private final long lowPC;
+ private final byte[] bytes;
+
+ public LocationEntry(long lowPC, long highPC, byte[] bytes) {
+ this.lowPC = lowPC;
+ this.highPC = highPC;
+ this.bytes = bytes;
+ }
+
+ /** Get the link address for the exclusive high end of the range. */
+ public long getHighPC() {
+ return highPC;
+ }
+
+ /** Get the link address for the inclusive low end of the range. */
+ public long getLowPC() {
+ return lowPC;
+ }
+
+ public byte[] getBytes() {
+ return bytes;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("LocationEntry ["); //$NON-NLS-1$
+ if (bytes != null) {
+ builder.append("bytes="); //$NON-NLS-1$
+ builder.append(Arrays.toString(bytes));
+ builder.append(", "); //$NON-NLS-1$
+ }
+ builder.append("highPC="); //$NON-NLS-1$
+ builder.append(highPC);
+ builder.append(", lowPC="); //$NON-NLS-1$
+ builder.append(lowPC);
+ builder.append("]"); //$NON-NLS-1$
+ return builder.toString();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.math.BigInteger;
+import java.text.MessageFormat;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.symbols.InvalidVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.MemoryVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.RegisterVariableLocation;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IRegisterVariableLocation;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+public class LocationExpression implements ILocationProvider {
+
+ protected final IStreamBuffer location;
+ protected final int addressSize;
+ protected final IScope scope;
+
+ public LocationExpression(IStreamBuffer location, int addressSize, IScope scope) {
+ this.location = location;
+ this.addressSize = addressSize;
+ this.scope = scope;
+ }
+
+ synchronized public IVariableLocation getLocation(EDCServicesTracker tracker, IFrameDMContext context, IAddress forLinkAddress, boolean isNonLocalConstVariable) {
+
+ if (location == null) {
+ return null;
+ }
+
+ // This is intentionally small. No existing code that I've seen has more
+ // than a sprinkling of operands, much less ones that push.
+ IVariableLocation[] opStack = new IVariableLocation[8];
+ int opStackPtr = 0;
+
+ location.position(0);
+
+ try {
+ while (location.hasRemaining()) {
+ byte opcodeB = location.get();
+ int opcode = 0xFF & opcodeB;
+
+ if (opcode >= DwarfConstants.DW_OP_lit0 && opcode <= DwarfConstants.DW_OP_lit31) {
+ opStack[opStackPtr++] = new MemoryVariableLocation(tracker,
+ context,
+ BigInteger.valueOf(opcode - DwarfConstants.DW_OP_lit0), true);
+ }
+ else if (opcode >= DwarfConstants.DW_OP_reg0 && opcode <= DwarfConstants.DW_OP_reg31) {
+ opStack[opStackPtr++] = new RegisterVariableLocation(tracker, context, null, (opcode - DwarfConstants.DW_OP_reg0));
+ }
+ else if (opcode >= DwarfConstants.DW_OP_breg0 && opcode <= DwarfConstants.DW_OP_breg31) {
+ RegisterVariableLocation loc = new RegisterVariableLocation(tracker, context, null, (opcode - DwarfConstants.DW_OP_breg0));
+ try {
+ BigInteger value = loc.readValue(addressSize);
+
+ long offset = DwarfInfoReader.read_signed_leb128(location);
+ opStack[opStackPtr++] = new MemoryVariableLocation(tracker, context,
+ value.add(BigInteger.valueOf(offset)), true);
+ } catch (CoreException e) {
+ return new InvalidVariableLocation(e.getMessage());
+ }
+ } else {
+
+ switch (opcode) {
+ case DwarfConstants.DW_OP_nop:
+ // ignore
+ break;
+
+ case DwarfConstants.DW_OP_addr: /* Constant address. */
+ long addrValue = DwarfInfoReader.readAddress(location, addressSize);
+ opStack[opStackPtr++] = new MemoryVariableLocation(tracker, context,
+ BigInteger.valueOf(addrValue), true, isNonLocalConstVariable);
+ break;
+
+ case DwarfConstants.DW_OP_deref: {
+ ensureStack(opStackPtr, 1);
+ try {
+ BigInteger addr = opStack[opStackPtr - 1].readValue(addressSize);
+ IVariableLocation loc = new MemoryVariableLocation(tracker, context,
+ addr, true, isNonLocalConstVariable);
+ opStack[opStackPtr - 1] = loc;
+ } catch (CoreException e) {
+ return new InvalidVariableLocation(e.getMessage());
+ }
+ break;
+ }
+
+ case DwarfConstants.DW_OP_plus_uconst: {
+ ensureStack(opStackPtr, 1);
+ long offset = DwarfInfoReader.read_unsigned_leb128(location);
+ opStack[opStackPtr-1] = opStack[opStackPtr-1].addOffset(offset);
+ break;
+ }
+
+ case DwarfConstants.DW_OP_const1u: /*
+ * Unsigned 1-byte
+ * constant.
+ */
+ case DwarfConstants.DW_OP_const1s: /*
+ * Signed 1-byte
+ * constant.
+ */
+ case DwarfConstants.DW_OP_const2u: /*
+ * Unsigned 2-byte
+ * constant.
+ */
+ case DwarfConstants.DW_OP_const2s: /*
+ * Signed 2-byte
+ * constant.
+ */
+ case DwarfConstants.DW_OP_const4u: /*
+ * Unsigned 4-byte
+ * constant.
+ */
+ case DwarfConstants.DW_OP_const4s: /*
+ * Signed 4-byte
+ * constant.
+ */
+ case DwarfConstants.DW_OP_const8u: /*
+ * Unsigned 8-byte
+ * constant.
+ */
+ case DwarfConstants.DW_OP_const8s: /*
+ * Signed 8-byte
+ * constant.
+ */
+ case DwarfConstants.DW_OP_constu: /* Unsigned LEB128 constant. */
+ case DwarfConstants.DW_OP_consts: /* Signed LEB128 constant. */
+ case DwarfConstants.DW_OP_dup:
+ case DwarfConstants.DW_OP_drop:
+ case DwarfConstants.DW_OP_over:
+ case DwarfConstants.DW_OP_pick: /* 1-byte stack index. */
+ case DwarfConstants.DW_OP_swap:
+ case DwarfConstants.DW_OP_rot:
+ case DwarfConstants.DW_OP_xderef:
+ case DwarfConstants.DW_OP_abs:
+ case DwarfConstants.DW_OP_and:
+ case DwarfConstants.DW_OP_div:
+ case DwarfConstants.DW_OP_minus:
+ case DwarfConstants.DW_OP_mod:
+ case DwarfConstants.DW_OP_mul:
+ case DwarfConstants.DW_OP_neg:
+ case DwarfConstants.DW_OP_not:
+ case DwarfConstants.DW_OP_or:
+ case DwarfConstants.DW_OP_plus:
+ case DwarfConstants.DW_OP_shl:
+ case DwarfConstants.DW_OP_shr:
+ case DwarfConstants.DW_OP_shra:
+ case DwarfConstants.DW_OP_xor:
+ case DwarfConstants.DW_OP_bra: /* Signed 2-byte constant. */
+ case DwarfConstants.DW_OP_eq:
+ case DwarfConstants.DW_OP_ge:
+ case DwarfConstants.DW_OP_gt:
+ case DwarfConstants.DW_OP_le:
+ case DwarfConstants.DW_OP_lt:
+ case DwarfConstants.DW_OP_ne:
+ case DwarfConstants.DW_OP_skip: /* Signed 2-byte constant. */
+ return new InvalidVariableLocation(MessageFormat.format(
+ DwarfMessages.NotImplementedFormat, DwarfMessages.LocationExpression_DW_OP + opcode));
+
+ case DwarfConstants.DW_OP_regx: /* Unsigned LEB128 register. */
+ long regNum = DwarfInfoReader.read_unsigned_leb128(location);
+ opStack[opStackPtr++] = new RegisterVariableLocation(tracker, context, null, ((int) regNum));
+ break;
+
+ case DwarfConstants.DW_OP_fbreg: /* Signed LEB128 offset. */
+ long offset = DwarfInfoReader.read_signed_leb128(location);
+
+ IFunctionScope functionScope = null;
+ functionScope = getFunctionScope(forLinkAddress);
+
+ IVariableLocation framePtrLoc = functionScope.getFrameBaseLocation().getLocation(tracker,
+ context, forLinkAddress, false);
+ if (framePtrLoc != null) {
+ if (framePtrLoc instanceof InvalidVariableLocation)
+ return framePtrLoc;
+
+ // first resolve the frame base value and then add
+ // the offset
+ if (framePtrLoc instanceof IRegisterVariableLocation) {
+ BigInteger frame = framePtrLoc.readValue(addressSize);
+
+ opStack[opStackPtr++] = new MemoryVariableLocation(tracker, context,
+ frame.add(BigInteger.valueOf(offset)), true);
+ } else {
+ opStack[opStackPtr++] = framePtrLoc.addOffset(offset);
+ }
+ }
+
+ break;
+
+ case DwarfConstants.DW_OP_piece:
+ /*
+ * ULEB128 size of piece
+ * addressed.
+ * TODO: GCC emits this for long long (is a composition operator
+ * that combines values -- this may tax the IVariableLocation concept)
+ */
+ assert (false);
+ return new InvalidVariableLocation(MessageFormat.format(DwarfMessages.NotImplementedFormat,
+ DwarfMessages.LocationExpression_MultiRegisterVariable));
+
+ case DwarfConstants.DW_OP_bregx: /*
+ * ULEB128 register followed
+ * by SLEB128 off.
+ */
+ case DwarfConstants.DW_OP_deref_size: /*
+ * 1-byte size of data
+ * retrieved.
+ */
+ case DwarfConstants.DW_OP_xderef_size: /*
+ * 1-byte size of
+ * data retrieved.
+ */
+ case DwarfConstants.DW_OP_push_object_address:
+ case DwarfConstants.DW_OP_call2:
+ case DwarfConstants.DW_OP_call4:
+ case DwarfConstants.DW_OP_call_ref:
+ assert (false);
+ return new InvalidVariableLocation(MessageFormat.format(DwarfMessages.NotImplementedFormat,
+ DwarfMessages.LocationExpression_DW_OP + opcode));
+
+ default:
+ assert (false);
+ return new InvalidVariableLocation(MessageFormat.format(DwarfMessages.InternalErrorFormat,
+ DwarfMessages.LocationExpression_UnexpectedOperand + opcode));
+ }
+
+ }
+
+ }
+ } catch (CoreException e) {
+ return new InvalidVariableLocation(e.getMessage());
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+
+ if (opStackPtr != 1) {
+ assert(false);
+ return new InvalidVariableLocation(MessageFormat.format(DwarfMessages.InternalErrorFormat,
+ DwarfMessages.LocationExpression_BadStackSize));
+ }
+
+ return opStack[0];
+ }
+
+ /**
+ */
+ private void ensureStack(int opStackPtr, int needed) throws CoreException {
+ if (opStackPtr < needed) {
+ throw EDCDebugger.newCoreException(MessageFormat.format(
+ DwarfMessages.InternalErrorFormat,
+ MessageFormat.format("expected {0} stack operands but had {1}", needed, opStackPtr)));
+ }
+ }
+
+ /**
+ * @param forLinkAddress
+ * @param functionScope
+ * @return
+ * @throws CoreException
+ */
+ private IFunctionScope getFunctionScope(IAddress forLinkAddress) throws CoreException {
+ IFunctionScope functionScope = null;
+
+ if (scope instanceof IFunctionScope) {
+ functionScope = (IFunctionScope) scope;
+ } else {
+ IScope parent = scope.getParent();
+ while (parent != null && !(parent instanceof IFunctionScope)) {
+ parent = parent.getParent();
+ }
+
+ if (parent == null) {
+ throw EDCDebugger.newCoreException("No function scope for " + scope + " at " + forLinkAddress.toHexAddressString());
+ } else {
+ functionScope = (IFunctionScope) parent;
+ }
+ }
+
+ // inlined functions may be nested
+ while (functionScope.getParent() instanceof IFunctionScope)
+ functionScope = (IFunctionScope) functionScope.getParent();
+ return functionScope;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ILocationProvider#isLocationKnown(org.eclipse.cdt.core.IAddress)
+ */
+ public boolean isLocationKnown(IAddress forLinkAddress) {
+ // a location expression has the lifetime of its scope
+ return true;
+ }
+
+ public IScope getScope() {
+ return scope;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ILocationProvider#lifetimeMustMatchScope()
+ */
+ public boolean lifetimeMustMatchScope() {
+ return true;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.nio.ByteOrder;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.MemoryStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.symbols.InvalidVariableLocation;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.debug.edc.symbols.ILocationProvider;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+
+public class LocationList implements ILocationProvider {
+
+ protected LocationEntry[] locationList;
+ protected int addressSize;
+ protected IScope scope;
+ protected ByteOrder byteOrder;
+
+ public LocationList(LocationEntry[] locationList, ByteOrder byteOrder, int addressSize, IScope scope) {
+ this.locationList = locationList;
+ this.byteOrder = byteOrder;
+ this.addressSize = addressSize;
+ this.scope = scope;
+ }
+
+ public LocationEntry[] getLocationEntries() {
+ return locationList;
+ }
+
+ public IVariableLocation getLocation(EDCServicesTracker tracker, IFrameDMContext context, IAddress forLinkAddress, boolean isNonLocalConstVariable) {
+
+ if (locationList != null) {
+ IScope searchScope = scope;
+ do {
+ // the scope may be an inlined function scope, whose frame base is only provided in a parent.
+ IVariableLocation location = searchForLocation(tracker, context, forLinkAddress, searchScope);
+ if (location != null) {
+ return location;
+ }
+ searchScope = searchScope.getParent();
+ } while (!(searchScope instanceof IModuleScope));
+ }
+
+ // variable may exist in the current scope, but not be live at the given
+ // address
+ return locationList != null ? new InvalidVariableLocation(DwarfMessages.UnknownVariableAddress) : null;
+ }
+
+ private IVariableLocation searchForLocation(EDCServicesTracker tracker,
+ IFrameDMContext context, IAddress forLinkAddress,
+ IScope scope) {
+ long address = forLinkAddress.getValue().longValue();
+
+ // find the location entry for the given address
+ for (LocationEntry entry : locationList) {
+ if (address >= entry.getLowPC() && address < entry.getHighPC()) {
+ IStreamBuffer locationData = new MemoryStreamBuffer(entry.getBytes(), byteOrder);
+ LocationExpression expression = new LocationExpression(locationData, addressSize, scope);
+ return expression.getLocation(tracker, context, forLinkAddress, false);
+ }
+ }
+
+ return null;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ILocationProvider#isLocationKnown(org.eclipse.cdt.core.IAddress)
+ */
+ public boolean isLocationKnown(IAddress forLinkAddress) {
+ long address = forLinkAddress.getValue().longValue();
+
+ for (LocationEntry entry : locationList) {
+ if (address >= entry.getLowPC() && address < entry.getHighPC()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.ILocationProvider#lifetimeMustMatchScope()
+ */
+ public boolean lifetimeMustMatchScope() {
+ return false; // may or may not match
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.dwarf;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.eclipse.cdt.debug.edc.symbols.IRangeList;
+
+/**
+ * This is a range of non-contiguous addresses
+ */
+public class RangeList implements IRangeList {
+ /** consecutive start, end entries */
+ private List<Long> ranges = new ArrayList<Long>();
+ private long low = Long.MAX_VALUE, high = Long.MIN_VALUE;
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "[0x" + Long.toHexString(getLowAddress()) + " to 0x" + Long.toHexString(getHighAddress()) + ")";
+ }
+
+ public void addRange(long start, long end) {
+ if (!ranges.isEmpty()) {
+ if (ranges.get(ranges.size() - 1) == start) {
+ ranges.set(ranges.size() - 1, end);
+ if (end > high)
+ high = end;
+ return;
+ }
+ }
+ ranges.add(start);
+ ranges.add(end);
+
+ // track these dynamically since the list is not guaranteed to be ordered
+ if (start < low)
+ low = start;
+ if (end > high)
+ high = end;
+ }
+
+ public long getLowAddress() {
+ if (ranges.isEmpty())
+ return 0;
+ else
+ return low;
+ }
+
+ public long getHighAddress() {
+ if (ranges.isEmpty())
+ return 0;
+ else
+ return high;
+ }
+
+ /** Get an iterator over the ranges. Fetch two entries at a time,
+ * which describe a [start, end) range. */
+ public Iterator<Entry> iterator() {
+ return new Iterator<Entry>() {
+ int index = 0;
+
+ public boolean hasNext() {
+ return index < ranges.size();
+ }
+
+ public Entry next() {
+ if (index >= ranges.size())
+ throw new NoSuchElementException();
+ index += 2;
+ return new IRangeList.Entry(ranges.get(index - 2), ranges.get(index - 1));
+ }
+
+ public void remove() {
+ // TODO Auto-generated method stub
+
+ }
+
+ };
+ }
+
+ /**
+ * Fixup a range list by adding a new low range
+ * @param addr
+ */
+ public void addLowRange(long addr) {
+ if (low > addr) {
+ low = addr;
+ if (!ranges.isEmpty()) {
+ ranges.set(0, low);
+ }
+ }
+ }
+
+ /**
+ * Fixup a range list by adding a new high range
+ * @param addr
+ */
+ public void addHighRange(long addr) {
+ if (high < addr) {
+ high = addr;
+ if (!ranges.isEmpty()) {
+ ranges.set(ranges.size() - 1, addr);
+ }
+ }
+
+ }
+
+ /**
+ * Tell if an address is in a range.
+ * @param addr
+ */
+ public boolean isInRange(long addr) {
+ for (Entry entry : this) {
+ if (entry.low >= addr && addr < entry.high)
+ return true;
+ }
+ return false;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.elf;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.FileStreamBuffer;
+
+/**
+ * Buffering version of the {@link IRandomReadAccessFile} interface that uses the
+ * {@link IStreamBuffer} implementation.
+ */
+public class BufferedRandomReadAccessFile implements
+ IRandomReadAccessFile {
+
+ /** source file */
+ private RandomAccessFile file;
+
+ /** endian-aware buffer */
+ private FileStreamBuffer buffer;
+ /** native Java (big endian) buffer */
+ private FileStreamBuffer bigEndianBuffer;
+
+ /** current basis pointer */
+ private long filePointer;
+
+ /**
+ * @param osString
+ * @throws IOException
+ */
+ public BufferedRandomReadAccessFile(String file, boolean isle) throws IOException {
+ this.file = new RandomAccessFile(file, "r"); //$NON-NLS-1$
+ // for ELF-aware reading
+ this.buffer = new FileStreamBuffer(this.file, isle ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
+ // for native DataInput APIs
+ this.bigEndianBuffer = new FileStreamBuffer(this.file, ByteOrder.BIG_ENDIAN);
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readFully(byte[])
+ */
+ public void readFully(byte[] b) throws IOException {
+ try {
+ bigEndianBuffer.get(b);
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readFully(byte[], int, int)
+ */
+ public void readFully(byte[] b, int off, int len) throws IOException {
+ try {
+ bigEndianBuffer.get(b, off, len);
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#skipBytes(int)
+ */
+ public int skipBytes(int n) throws IOException {
+ long cap = bigEndianBuffer.capacity();
+ long cur = bigEndianBuffer.position();
+ long pos = Math.min(cap, cur + n);
+ bigEndianBuffer.position(pos);
+ buffer.position(pos);
+ return (int) (pos - cur);
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readBoolean()
+ */
+ public boolean readBoolean() throws IOException {
+ try {
+ return bigEndianBuffer.get() != 0;
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readByte()
+ */
+ public byte readByte() throws IOException {
+ try {
+ return bigEndianBuffer.get();
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readUnsignedByte()
+ */
+ public int readUnsignedByte() throws IOException {
+ try {
+ return bigEndianBuffer.get() & 0xff;
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readShort()
+ */
+ public short readShort() throws IOException {
+ try {
+ return bigEndianBuffer.getShort();
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readUnsignedShort()
+ */
+ public int readUnsignedShort() throws IOException {
+ try {
+ return bigEndianBuffer.getShort() & 0xffff;
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readChar()
+ */
+ public char readChar() throws IOException {
+ try {
+ return bigEndianBuffer.getChar();
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readInt()
+ */
+ public int readInt() throws IOException {
+ try {
+ return bigEndianBuffer.getInt();
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readLong()
+ */
+ public long readLong() throws IOException {
+ try {
+ return bigEndianBuffer.getLong();
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readFloat()
+ */
+ public float readFloat() throws IOException {
+ try {
+ return Float.intBitsToFloat(bigEndianBuffer.getInt());
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readDouble()
+ */
+ public double readDouble() throws IOException {
+ try {
+ return Double.longBitsToDouble(bigEndianBuffer.getLong());
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readLine()
+ */
+ public String readLine() throws IOException {
+ try {
+ StringBuilder sb = new StringBuilder();
+ try {
+ int b;
+ while ((b = bigEndianBuffer.get()) != 0) {
+ sb.append((char) (b & 0xff));
+ }
+ } catch (BufferUnderflowException e) {
+ if (sb.length() == 0)
+ return null;
+ }
+ return sb.toString();
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.DataInput#readUTF()
+ */
+ public String readUTF() throws IOException {
+ try {
+ short length = bigEndianBuffer.getShort();
+ StringBuilder sb = new StringBuilder();
+ while (length-- > 0) {
+ // TODO
+ int c = (bigEndianBuffer.get()) & 0xff;
+ if (c >= 0x80)
+ assert false;
+ sb.append(c);
+ }
+ return sb.toString();
+ } finally {
+ buffer.position(bigEndianBuffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.io.Closeable#close()
+ */
+ public void close() throws IOException {
+ file.close();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#setEndian(boolean)
+ */
+ public void setEndian(boolean le) {
+ buffer.setOrder(le ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
+
+ // keep bigEndianBuffer the same
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#setFileOffset(long)
+ */
+ public void setFileOffset(long offset) throws IOException {
+ this.filePointer = offset;
+ try {
+ buffer.position(offset);
+ } catch (IllegalArgumentException e) {
+ throw (IOException) (new IOException().initCause(e));
+ } finally {
+ bigEndianBuffer.position(offset);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#getFilePointer()
+ */
+ public long getFilePointer() throws IOException {
+ return filePointer;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#seek(long)
+ */
+ public void seek(long pos) throws IOException {
+ try {
+ buffer.position(pos + filePointer);
+ } catch (IllegalArgumentException e) {
+ throw (IOException) (new IOException().initCause(e));
+ } finally {
+ bigEndianBuffer.position(pos + filePointer);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#readShortE()
+ */
+ public short readShortE() throws IOException {
+ try {
+ return buffer.getShort();
+ } finally {
+ bigEndianBuffer.position(buffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#readIntE()
+ */
+ public long readIntE() throws IOException {
+ try {
+ return buffer.getInt();
+ } finally {
+ bigEndianBuffer.position(buffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#readLongE()
+ */
+ public long readLongE() throws IOException {
+ try {
+ return buffer.getLong();
+ } finally {
+ bigEndianBuffer.position(buffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#readFullyE(byte[])
+ */
+ public void readFullyE(byte[] bytes) throws IOException {
+ try {
+ buffer.get(bytes);
+ if (buffer.getOrder() != ByteOrder.BIG_ENDIAN) {
+ for(int i=0; i < (bytes.length / 2); i++)
+ {
+ byte tmp = bytes[i];
+ bytes[i] = bytes[bytes.length - i -1];
+ bytes[bytes.length - i -1] = tmp;
+ }
+ }
+ } finally {
+ bigEndianBuffer.position(buffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#read(byte[], int, int)
+ */
+ public int read(byte[] b, int off, int len) throws IOException {
+ try {
+ long max = bigEndianBuffer.capacity();
+ int toRead = (int) Math.min(max - (off + len), len);
+ buffer.get(b, off, toRead);
+ return toRead;
+ } finally {
+ bigEndianBuffer.position(buffer.position());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#read(byte[])
+ */
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#length()
+ */
+ public long length() throws IOException {
+ return buffer.capacity();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#createReadByteBuffer(long, long)
+ */
+ public ByteBuffer createReadByteBuffer(long offset, long size)
+ throws IOException {
+ // we don't use this in EDC, so this is slower than wanted
+ // TODO
+ assert false;
+ try {
+ byte[] contents = new byte[(int) size];
+ long cur = buffer.position();
+ buffer.position(offset);
+ buffer.get(contents);
+ buffer.position(cur);
+ return ByteBuffer.wrap(contents);
+ } catch (IllegalArgumentException e) {
+ throw (IOException) (new IOException().initCause(e));
+ }
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 QNX Software 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:
+ * QNX Software Systems - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.elf;
+
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel.MapMode;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.elf.IRandomReadAccessFile;
+
+/**
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ERandomAccessFile extends RandomAccessFile implements IRandomReadAccessFile {
+ private boolean isle;
+ private long ptr_offset;
+ int val[] = new int[4];
+
+ public ERandomAccessFile(String file, String mode) throws IOException {
+ super(file, mode);
+ }
+
+ public ERandomAccessFile(File file, String mode) throws IOException {
+ super(file, mode);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomAccessFile#setEndian(boolean)
+ */
+ public void setEndian(boolean le)
+ {
+ isle = le;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomAccessFile#readShortE()
+ */
+ public final short readShortE() throws IOException {
+ val[0] = read();
+ val[1] = read();
+ if ((val[0] | val[1]) < 0)
+ throw new EOFException();
+ if ( isle ) {
+ return (short)((val[1] << 8) + val[0]);
+ }
+ return (short)((val[0] << 8) + val[1]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomAccessFile#readIntE()
+ */
+ public final long readIntE() throws IOException
+ {
+ val[0] = read();
+ val[1] = read();
+ val[2] = read();
+ val[3] = read();
+ if ((val[0] | val[1] | val[2] | val[3]) < 0)
+ throw new EOFException();
+ if ( isle ) {
+ return ((val[3] << 24) + (val[2] << 16) + (val[1] << 8) + val[0]);
+ }
+ return ((val[0] << 24) + (val[1] << 16) + (val[2] << 8) + val[3]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomAccessFile#readLongE()
+ */
+ public final long readLongE() throws IOException
+ {
+ byte [] bytes = new byte[8];
+ long result = 0;
+ super.readFully(bytes);
+ int shift = 0;
+ if ( isle )
+ for(int i=7; i >= 0; i-- )
+ {
+ shift = i*8;
+ result += ( ((long)bytes[i]) << shift ) & ( 0xffL << shift );
+ }
+ else
+ for(int i=0; i <= 7; i++ )
+ {
+ shift = (7-i)*8;
+ result += ( ((long)bytes[i]) << shift ) & ( 0xffL << shift );
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomAccessFile#readFullyE(byte[])
+ */
+ public final void readFullyE(byte [] bytes) throws IOException
+ {
+ super.readFully(bytes);
+ byte tmp = 0;
+ if( isle )
+ for(int i=0; i < (bytes.length / 2); i++)
+ {
+ tmp = bytes[i];
+ bytes[i] = bytes[bytes.length - i -1];
+ bytes[bytes.length - i -1] = tmp;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomAccessFile#setFileOffset(long)
+ */
+ public void setFileOffset( long offset ) throws IOException {
+ ptr_offset = offset;
+ super.seek( offset );
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomAccessFile#getFilePointer()
+ */
+ @Override
+ public long getFilePointer() throws IOException {
+ long ptr = super.getFilePointer();
+ ptr = ptr - ptr_offset;
+ return ptr;
+ }
+
+ @Override
+ public void seek( long pos ) throws IOException {
+ long real_pos = pos + ptr_offset;
+ super.seek( real_pos );
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.utils.IRandomReadAccessFile#createByteBuffer(long, long)
+ */
+ public ByteBuffer createReadByteBuffer(long offset, long size) throws IOException {
+ return getChannel().map(MapMode.READ_ONLY, offset, size).load().asReadOnlyBuffer();
+ }
+}
+
+
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 QNX Software 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:
+ * QNX Software Systems - initial API and implementation
+ * Markus Schorn (Wind River Systems)
+ * Ed Swartz (Nokia) - temporary fork into EDC to adapt to IRandomReadAccessFile
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.elf;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.core.IAddressFactory;
+import org.eclipse.cdt.core.ISymbolReader;
+import org.eclipse.cdt.utils.Addr32;
+import org.eclipse.cdt.utils.Addr32Factory;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.cdt.utils.Addr64Factory;
+
+public class Elf {
+ public final static int ELF32_ADDR_SIZE = 4;
+ public final static int ELF32_OFF_SIZE = 4;
+ public final static int ELF64_ADDR_SIZE = 8;
+ public final static int ELF64_OFF_SIZE = 8;
+
+ protected IRandomReadAccessFile efile;
+
+ protected ELFhdr ehdr;
+ protected Section[] sections;
+ protected String file;
+ protected byte[] section_strtab;
+
+ private int syms = 0;
+ private Symbol[] symbols;
+ private Symbol[] symtab_symbols;
+ private Section symtab_sym;
+ private Symbol[] dynsym_symbols;
+ private Section dynsym_sym;
+ private boolean sections_mapped; // Have sections been mapped? Used to clean up properly in Elf.Dispose.
+
+ protected String EMPTY_STRING = ""; //$NON-NLS-1$
+
+ public class ELFhdr {
+
+ /* e_ident offsets */
+ public final static int EI_MAG0 = 0;
+ public final static int EI_MAG1 = 1;
+ public final static int EI_MAG2 = 2;
+ public final static int EI_MAG3 = 3;
+ public final static int EI_CLASS = 4;
+ public final static int EI_DATA = 5;
+ public final static int EI_VERSION = 6;
+ public final static int EI_PAD = 7;
+ public final static int EI_NDENT = 16;
+
+ /* e_ident[EI_CLASS] */
+ public final static int ELFCLASSNONE = 0;
+ public final static int ELFCLASS32 = 1;
+ public final static int ELFCLASS64 = 2;
+
+ /* e_ident[EI_DATA] */
+ public final static int ELFDATANONE = 0;
+ public final static int ELFDATA2LSB = 1;
+ public final static int ELFDATA2MSB = 2;
+
+ /* values of e_type */
+ public final static int ET_NONE = 0;
+ public final static int ET_REL = 1;
+ public final static int ET_EXEC = 2;
+ public final static int ET_DYN = 3;
+ public final static int ET_CORE = 4;
+ public final static int ET_LOPROC = 0xff00;
+ public final static int ET_HIPROC = 0xffff;
+
+ /* values of e_machine */
+ public final static int EM_NONE = 0;
+ public final static int EM_M32 = 1;
+ public final static int EM_SPARC = 2;
+ public final static int EM_386 = 3;
+ public final static int EM_68K = 4;
+ public final static int EM_88K = 5;
+ public final static int EM_486 = 6;
+ public final static int EM_860 = 7;
+ public final static int EM_MIPS = 8;
+ public final static int EM_MIPS_RS3_LE = 10;
+ public final static int EM_RS6000 = 11;
+ public final static int EM_PARISC = 15;
+ public final static int EM_nCUBE = 16;
+ public final static int EM_VPP550 = 17;
+ public final static int EM_SPARC32PLUS = 18;
+ public final static int EM_PPC = 20;
+ public final static int EM_PPC64 = 21;
+ public final static int EM_ARM = 40;
+ public final static int EM_SH = 42;
+ public final static int EM_SPARCV9 = 43;
+ public final static int EM_TRICORE = 44;
+ public final static int EM_H8_300 = 46;
+ public final static int EM_H8_300H = 47;
+ public final static int EM_IA_64 = 50;
+ public final static int EM_COLDFIRE = 52;
+ public final static int EM_STARCORE = 58;
+ public final static int EM_X86_64 = 62;
+ public final static int EM_ST100 = 60;
+
+ /** @since 5.2 */
+ public final static int EM_68HC08 = 71; /* Freescale MC68HC08 Microcontroller */
+
+ public final static int EM_AVR = 83;
+ public final static int EM_FR30 = 84; /* Fujitsu FR30 */
+ public final static int EM_V850 = 87;
+ public final static int EM_M32R = 88;
+ public final static int EM_MN10300 = 89;
+ public final static int EM_MN10200 = 90;
+ public final static int EM_XTENSA = 94;
+ public final static int EM_MSP430 = 105;
+ public final static int EM_BLACKFIN = 106;
+ public final static int EM_EXCESS = 111;
+ public final static int EM_NIOSII = 113;
+ public final static int EM_C166 = 116;
+ public final static int EM_M16C = 117;
+
+ /** @since 5.2 */
+ public final static int EM_RS08 = 132; /* Freescale RS08 embedded processor */
+
+ public final static int EM_MMDSP = 160;
+ public final static int EM_NIOS = 0xFEBB;
+ public final static int EM_CYGNUS_POWERPC = 0x9025;
+ public final static int EM_CYGNUS_M32R = 0x9041;
+ public final static int EM_CYGNUS_V850 = 0x9080;
+ public final static int EM_CYGNUS_MN10200 = 0xdead;
+ public final static int EM_CYGNUS_MN10300 = 0xbeef;
+ public final static int EM_CYGNUS_FR30 = 0x3330;
+ public final static int EM_XSTORMY16 = 0xad45;
+ public final static int EM_CYGNUS_FRV = 0x5441;
+ public final static int EM_IQ2000 = 0xFEBA;
+ public static final int EM_XILINX_MICROBLAZE = 0xbaab;
+ public static final int EM_SDMA = 0xcafe;
+ public static final int EM_CRADLE = 0x4d55;
+
+ public byte e_ident[] = new byte[EI_NDENT];
+ public int e_type; /* file type (Elf32_Half) */
+ public int e_machine; /* machine type (Elf32_Half) */
+ public long e_version; /* version number (Elf32_Word) */
+ public IAddress e_entry; /* entry point (Elf32_Addr) */
+ public long e_phoff; /* Program hdr offset (Elf32_Off) */
+ public long e_shoff; /* Section hdr offset (Elf32_Off) */
+ public long e_flags; /* Processor flags (Elf32_Word) */
+ public short e_ehsize; /* sizeof ehdr (Elf32_Half) */
+ public short e_phentsize; /* Program header entry size (Elf32_Half) */
+ public short e_phnum; /* Number of program headers (Elf32_Half) */
+ public short e_shentsize; /* Section header entry size (Elf32_Half) */
+ public short e_shnum; /* Number of section headers (Elf32_Half) */
+ public short e_shstrndx; /* String table index (Elf32_Half) */
+
+ protected ELFhdr() throws IOException {
+ efile.seek(0);
+ efile.readFully(e_ident);
+ if (e_ident[ELFhdr.EI_MAG0] != 0x7f || e_ident[ELFhdr.EI_MAG1] != 'E' || e_ident[ELFhdr.EI_MAG2] != 'L'
+ || e_ident[ELFhdr.EI_MAG3] != 'F')
+ throw new IOException(CCorePlugin.getResourceString("Util.exception.notELF")); //$NON-NLS-1$
+ efile.setEndian(e_ident[ELFhdr.EI_DATA] == ELFhdr.ELFDATA2LSB);
+ e_type = efile.readShortE();
+ e_machine = efile.readShortE();
+ e_version = efile.readIntE();
+ switch (e_ident[ELFhdr.EI_CLASS]) {
+ case ELFhdr.ELFCLASS32 : {
+ byte[] addrArray = new byte[ELF32_ADDR_SIZE];
+ efile.readFullyE(addrArray);
+ e_entry = new Addr32(addrArray);
+ e_phoff = efile.readIntE();
+ e_shoff = efile.readIntE();
+ }
+ break;
+ case ELFhdr.ELFCLASS64 : {
+ byte[] addrArray = new byte[ELF64_ADDR_SIZE];
+ efile.readFullyE(addrArray);
+ e_entry = new Addr64(addrArray);
+ e_phoff = readUnsignedLong(efile);
+ e_shoff = readUnsignedLong(efile);
+ }
+ break;
+ case ELFhdr.ELFCLASSNONE :
+ default :
+ throw new IOException("Unknown ELF class " + e_ident[ELFhdr.EI_CLASS]); //$NON-NLS-1$
+ }
+ e_flags = efile.readIntE();
+ e_ehsize = efile.readShortE();
+ e_phentsize = efile.readShortE();
+ e_phnum = efile.readShortE();
+ e_shentsize = efile.readShortE();
+ e_shnum = efile.readShortE();
+ e_shstrndx = efile.readShortE();
+ }
+
+ protected ELFhdr(byte[] bytes) throws IOException {
+ if (bytes.length <= e_ident.length) {
+ throw new EOFException(CCorePlugin.getResourceString("Util.exception.notELF")); //$NON-NLS-1$
+ }
+ System.arraycopy(bytes, 0, e_ident, 0, e_ident.length);
+ if (e_ident[ELFhdr.EI_MAG0] != 0x7f || e_ident[ELFhdr.EI_MAG1] != 'E' || e_ident[ELFhdr.EI_MAG2] != 'L'
+ || e_ident[ELFhdr.EI_MAG3] != 'F')
+ throw new IOException(CCorePlugin.getResourceString("Util.exception.notELF")); //$NON-NLS-1$
+ boolean isle = (e_ident[ELFhdr.EI_DATA] == ELFhdr.ELFDATA2LSB);
+ int offset = e_ident.length;
+ e_type = makeShort(bytes, offset, isle);
+ offset += 2;
+ e_machine = makeShort(bytes, offset, isle);
+ offset += 2;
+ e_version = makeInt(bytes, offset, isle);
+ offset += 4;
+ switch (e_ident[ELFhdr.EI_CLASS]) {
+ case ELFhdr.ELFCLASS32 : {
+ byte[] addrArray = new byte[ELF32_ADDR_SIZE];
+ System.arraycopy(bytes, offset, addrArray, 0, ELF32_ADDR_SIZE);
+ offset += ELF32_ADDR_SIZE;
+ e_entry = new Addr32(addrArray);
+ e_phoff = makeInt(bytes, offset, isle);
+ offset += ELF32_OFF_SIZE;
+ e_shoff = makeInt(bytes, offset, isle);
+ offset += ELF32_OFF_SIZE;
+ }
+ break;
+ case ELFhdr.ELFCLASS64 : {
+ byte[] addrArray = new byte[ELF64_ADDR_SIZE];
+ System.arraycopy(bytes, offset, addrArray, 0, ELF64_ADDR_SIZE);
+ offset += ELF64_ADDR_SIZE;
+ e_entry = new Addr64(addrArray);
+ e_phoff = makeUnsignedLong(bytes, offset, isle);
+ offset += ELF64_OFF_SIZE;
+ e_shoff = makeUnsignedLong(bytes, offset, isle);
+ offset += ELF64_OFF_SIZE;
+ }
+ break;
+ case ELFhdr.ELFCLASSNONE :
+ default :
+ throw new IOException("Unknown ELF class " + e_ident[ELFhdr.EI_CLASS]); //$NON-NLS-1$
+ }
+ e_flags = makeInt(bytes, offset, isle);
+ offset += 4;
+ e_ehsize = makeShort(bytes, offset, isle);
+ offset += 2;
+ e_phentsize = makeShort(bytes, offset, isle);
+ offset += 2;
+ e_phnum = makeShort(bytes, offset, isle);
+ offset += 2;
+ e_shentsize = makeShort(bytes, offset, isle);
+ offset += 2;
+ e_shnum = makeShort(bytes, offset, isle);
+ offset += 2;
+ e_shstrndx = makeShort(bytes, offset, isle);
+ offset += 2;
+ }
+
+ private final short makeShort(byte[] val, int offset, boolean isle) throws IOException {
+ if (val.length < offset + 2)
+ throw new IOException();
+ if (isle) {
+ return (short) ( (val[offset + 1] << 8) + val[offset + 0]);
+ }
+ return (short) ( (val[offset + 0] << 8) + val[offset + 1]);
+ }
+
+ private final long makeInt(byte[] val, int offset, boolean isle) throws IOException {
+ if (val.length < offset + 4)
+ throw new IOException();
+ if (isle) {
+ return ( (val[offset + 3] << 24) + (val[offset + 2] << 16) + (val[offset + 1] << 8) + val[offset + 0]);
+ }
+ return ( (val[offset + 0] << 24) + (val[offset + 1] << 16) + (val[offset + 2] << 8) + val[offset + 3]);
+ }
+
+ private final long makeLong(byte[] val, int offset, boolean isle) throws IOException {
+ long result = 0;
+ int shift = 0;
+ if (isle)
+ for (int i = 7; i >= 0; i--) {
+ shift = i * 8;
+ result += ( ((long)val[offset + i]) << shift) & (0xffL << shift);
+ }
+ else
+ for (int i = 0; i <= 7; i++) {
+ shift = (7 - i) * 8;
+ result += ( ((long)val[offset + i]) << shift) & (0xffL << shift);
+ }
+ return result;
+ }
+
+ private final long makeUnsignedLong(byte[] val, int offset, boolean isle) throws IOException {
+ long result = makeLong(val, offset, isle);
+ if (result < 0) {
+ throw new IOException("Maximal file offset is " + Long.toHexString(Long.MAX_VALUE) + //$NON-NLS-1$
+ " given offset is " + Long.toHexString(result)); //$NON-NLS-1$
+ }
+ return result;
+
+ }
+
+ }
+
+ public class Section {
+
+ /* sh_type */
+ public final static int SHT_NULL = 0;
+ public final static int SHT_PROGBITS = 1;
+ public final static int SHT_SYMTAB = 2;
+ public final static int SHT_STRTAB = 3;
+ public final static int SHT_RELA = 4;
+ public final static int SHT_HASH = 5;
+ public final static int SHT_DYNAMIC = 6;
+ public final static int SHT_NOTE = 7;
+ public final static int SHT_NOBITS = 8;
+ public final static int SHT_REL = 9;
+ public final static int SHT_SHLIB = 10;
+ public final static int SHT_DYNSYM = 11;
+
+ public final static int SHT_LOPROC = 0x70000000;
+
+ /* sh_flags */
+ public final static int SHF_WRITE = 1;
+ public final static int SHF_ALLOC = 2;
+ public final static int SHF_EXECINTR = 4;
+
+ public long sh_name;
+ public long sh_type;
+ public long sh_flags;
+ public IAddress sh_addr;
+ public long sh_offset;
+ public long sh_size;
+ public long sh_link;
+ public long sh_info;
+ public long sh_addralign;
+ public long sh_entsize;
+
+ /**
+ * @since 5.1
+ */
+ public ByteBuffer mapSectionData() throws IOException {
+ sections_mapped = true;
+ return efile.createReadByteBuffer(sh_offset, sh_size);
+ }
+
+ public byte[] loadSectionData() throws IOException {
+ byte[] data = new byte[(int)sh_size];
+ efile.seek(sh_offset);
+ efile.read(data);
+ return data;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ if (section_strtab == null) {
+ final int shstrndx= ehdr.e_shstrndx & 0xffff; // unsigned short
+ if (shstrndx > sections.length || shstrndx < 0)
+ return EMPTY_STRING;
+ int size = (int)sections[shstrndx].sh_size;
+ if (size <= 0 || size > efile.length())
+ return EMPTY_STRING;
+ section_strtab = new byte[size];
+ efile.seek(sections[shstrndx].sh_offset);
+ efile.read(section_strtab);
+ }
+ int str_size = 0;
+ if (sh_name > section_strtab.length) {
+ return EMPTY_STRING;
+ }
+ while (section_strtab[(int)sh_name + str_size] != 0)
+ str_size++;
+ return new String(section_strtab, (int)sh_name, str_size);
+ } catch (IOException e) {
+ return EMPTY_STRING;
+ }
+ }
+ }
+
+ protected String string_from_elf_section(Elf.Section section, int index) throws IOException {
+ if (index > section.sh_size) {
+ return EMPTY_STRING;
+ }
+
+ StringBuffer str = new StringBuffer();
+ //Most string symbols will be less than 50 bytes in size
+ byte [] tmp = new byte[50];
+
+ efile.seek(section.sh_offset + index);
+ while(true) {
+ int len = efile.read(tmp);
+ for(int i = 0; i < len; i++) {
+ if(tmp[i] == 0) {
+ len = 0;
+ break;
+ }
+ str.append((char)tmp[i]);
+ }
+ if(len <= 0) {
+ break;
+ }
+ }
+
+ return str.toString();
+ }
+
+ public class Symbol implements Comparable<Object> {
+
+ /* Symbol bindings */
+ public final static int STB_LOCAL = 0;
+ public final static int STB_GLOBAL = 1;
+ public final static int STB_WEAK = 2;
+ /* Symbol type */
+ public final static int STT_NOTYPE = 0;
+ public final static int STT_OBJECT = 1;
+ public final static int STT_FUNC = 2;
+ public final static int STT_SECTION = 3;
+ public final static int STT_FILE = 4;
+ /* Special Indexes */
+ public final static int SHN_UNDEF = 0;
+ public final static int SHN_LORESERVE = 0xffffff00;
+ public final static int SHN_LOPROC = 0xffffff00;
+ public final static int SHN_HIPROC = 0xffffff1f;
+ public final static int SHN_LOOS = 0xffffff20;
+ public final static int SHN_HIOS = 0xffffff3f;
+ public final static int SHN_ABS = 0xfffffff1;
+ public final static int SHN_COMMON = 0xfffffff2;
+ public final static int SHN_XINDEX = 0xffffffff;
+ public final static int SHN_HIRESERVE = 0xffffffff;
+
+ /* NOTE: 64 bit and 32 bit ELF sections has different order */
+ public long st_name;
+ public IAddress st_value;
+ public long st_size;
+ public short st_info;
+ public short st_other;
+ public short st_shndx;
+
+ private String name = null;
+
+ private final Section sym_section;
+
+ public Symbol(Section section) {
+ sym_section = section;
+ }
+
+ public int st_type() {
+ return st_info & 0xf;
+ }
+
+ public int st_bind() {
+ return (st_info >> 4) & 0xf;
+ }
+
+ public int compareTo(Object obj) {
+ /*
+ * long thisVal = 0; long anotherVal = 0; if ( obj instanceof Symbol ) {
+ * Symbol sym = (Symbol)obj; thisVal = this.st_value; anotherVal =
+ * sym.st_value; } else if ( obj instanceof Long ) { Long val =
+ * (Long)obj; anotherVal = val.longValue(); thisVal = this.st_value; }
+ * return (thisVal <anotherVal ? -1 : (thisVal==anotherVal ? 0 :
+ * 1));
+ */
+ return this.st_value.compareTo( ((Symbol)obj).st_value);
+ }
+
+ @Override
+ public String toString() {
+ if (name == null) {
+ try {
+ Section sections[] = getSections();
+ Section symstr = sections[(int)sym_section.sh_link];
+ name = string_from_elf_section(symstr, (int)st_name);
+ } catch (IOException e) {
+ return EMPTY_STRING;
+ }
+ }
+ return name;
+ }
+
+ }
+
+ /**
+ * We have to implement a separate compararator since when we do the binary
+ * search down below we are using a Long and a Symbol object and the Long
+ * doesn't know how to compare against a Symbol so if we compare Symbol vs
+ * Long it is ok, but not if we do Long vs Symbol.
+ */
+
+ class SymbolComparator implements Comparator<Object> {
+
+ IAddress val1, val2;
+ public int compare(Object o1, Object o2) {
+
+ if (o1 instanceof IAddress) {
+ val1 = (IAddress)o1;
+ } else if (o1 instanceof Symbol) {
+ val1 = ((Symbol)o1).st_value;
+ } else {
+ return -1;
+ }
+
+ if (o2 instanceof IAddress) {
+ val2 = (IAddress)o2;
+ } else if (o2 instanceof Symbol) {
+ val2 = ((Symbol)o2).st_value;
+ } else {
+ return -1;
+ }
+ return val1.compareTo(val2);
+ }
+ }
+
+ public class PHdr {
+
+ public final static int PT_NULL = 0;
+ public final static int PT_LOAD = 1;
+ public final static int PT_DYNAMIC = 2;
+ public final static int PT_INTERP = 3;
+ public final static int PT_NOTE = 4;
+ public final static int PT_SHLIB = 5;
+ public final static int PT_PHDR = 6;
+
+ public final static int PF_X = 1;
+ public final static int PF_W = 2;
+ public final static int PF_R = 4;
+ /* NOTE: 64 bit and 32 bit ELF have different order and size of elements */
+ public long p_type;
+ public long p_offset;
+ public IAddress p_vaddr;
+ public IAddress p_paddr;
+ public long p_filesz;
+ public long p_memsz;
+ public long p_flags;
+ public long p_align;
+ }
+
+ public PHdr[] getPHdrs() throws IOException {
+ if (ehdr.e_phnum == 0) {
+ return new PHdr[0];
+ }
+ efile.seek(ehdr.e_phoff);
+ final int length= ehdr.e_phnum & 0xffff; // interpret as unsigned short
+ PHdr phdrs[] = new PHdr[length];
+ for (int i = 0; i < length; i++) {
+ phdrs[i] = new PHdr();
+ switch (ehdr.e_ident[ELFhdr.EI_CLASS]) {
+ case ELFhdr.ELFCLASS32 : {
+ byte[] addrArray = new byte[ELF32_ADDR_SIZE];
+
+ phdrs[i].p_type = efile.readIntE();
+ phdrs[i].p_offset = efile.readIntE();
+ efile.readFullyE(addrArray);
+ phdrs[i].p_vaddr = new Addr32(addrArray);
+ efile.readFullyE(addrArray);
+ phdrs[i].p_paddr = new Addr32(addrArray);
+ phdrs[i].p_filesz = efile.readIntE();
+ phdrs[i].p_memsz = efile.readIntE();
+ phdrs[i].p_flags = efile.readIntE();
+ phdrs[i].p_align = efile.readIntE();
+ }
+ break;
+ case ELFhdr.ELFCLASS64 : {
+ byte[] addrArray = new byte[ELF64_ADDR_SIZE];
+
+ phdrs[i].p_type = efile.readIntE();
+ phdrs[i].p_flags = efile.readIntE();
+ phdrs[i].p_offset = readUnsignedLong(efile);
+ efile.readFullyE(addrArray);
+ phdrs[i].p_vaddr = new Addr64(addrArray);
+ efile.readFullyE(addrArray);
+ phdrs[i].p_paddr = new Addr64(addrArray);
+ phdrs[i].p_filesz = readUnsignedLong(efile);
+ phdrs[i].p_memsz = readUnsignedLong(efile);
+ phdrs[i].p_align = readUnsignedLong(efile);
+ }
+ break;
+ case ELFhdr.ELFCLASSNONE :
+ default :
+ throw new IOException("Unknown ELF class " + ehdr.e_ident[ELFhdr.EI_CLASS]); //$NON-NLS-1$
+ }
+
+ }
+ return phdrs;
+ }
+
+ public class Dynamic {
+
+ public final static int DYN_ENT_SIZE_32 = 8;
+ public final static int DYN_ENT_SIZE_64 = 16;
+
+ public final static int DT_NULL = 0;
+ public final static int DT_NEEDED = 1;
+ public final static int DT_PLTRELSZ = 2;
+ public final static int DT_PLTGOT = 3;
+ public final static int DT_HASH = 4;
+ public final static int DT_STRTAB = 5;
+ public final static int DT_SYMTAB = 6;
+ public final static int DT_RELA = 7;
+ public final static int DT_RELASZ = 8;
+ public final static int DT_RELAENT = 9;
+ public final static int DT_STRSZ = 10;
+ public final static int DT_SYMENT = 11;
+ public final static int DT_INIT = 12;
+ public final static int DT_FINI = 13;
+ public final static int DT_SONAME = 14;
+ public final static int DT_RPATH = 15;
+ public long d_tag;
+ public long d_val;
+ private final Section section;
+ private String name;
+
+ protected Dynamic(Section section) {
+ this.section = section;
+ }
+
+ @Override
+ public String toString() {
+ if (name == null) {
+ switch ((int)d_tag) {
+ case DT_NEEDED :
+ case DT_SONAME :
+ case DT_RPATH :
+ try {
+ Section symstr = sections[(int)section.sh_link];
+ name = string_from_elf_section(symstr, (int)d_val);
+ } catch (IOException e) {
+ name = EMPTY_STRING;
+ }
+ break;
+ default :
+ name = EMPTY_STRING;
+ }
+ }
+ return name;
+ }
+ }
+
+ public Dynamic[] getDynamicSections(Section section) throws IOException {
+ if (section.sh_type != Section.SHT_DYNAMIC) {
+ return new Dynamic[0];
+ }
+ ArrayList<Dynamic> dynList = new ArrayList<Dynamic>();
+ efile.seek(section.sh_offset);
+ int off = 0;
+ // We must assume the section is a table ignoring the sh_entsize as it
+ // is not
+ // set for MIPS.
+ while (off < section.sh_size) {
+ Dynamic dynEnt = new Dynamic(section);
+ switch (ehdr.e_ident[ELFhdr.EI_CLASS]) {
+ case ELFhdr.ELFCLASS32 : {
+ dynEnt.d_tag = efile.readIntE();
+ dynEnt.d_val = efile.readIntE();
+ off += Dynamic.DYN_ENT_SIZE_32;
+ }
+ break;
+ case ELFhdr.ELFCLASS64 : {
+ dynEnt.d_tag = efile.readLongE();
+ dynEnt.d_val = efile.readLongE();
+ off += Dynamic.DYN_ENT_SIZE_64;
+ }
+ break;
+ case ELFhdr.ELFCLASSNONE :
+ default :
+ throw new IOException("Unknown ELF class " + ehdr.e_ident[ELFhdr.EI_CLASS]); //$NON-NLS-1$
+ }
+
+ if (dynEnt.d_tag != Dynamic.DT_NULL)
+ dynList.add(dynEnt);
+ }
+ return dynList.toArray(new Dynamic[0]);
+ }
+
+ private void commonSetup(IRandomReadAccessFile rraFile, String file, long offset) throws IOException {
+ try {
+ efile = rraFile;
+ efile.seek(offset);
+ ehdr = new ELFhdr();
+ this.file = file;
+ } finally {
+ if (ehdr == null) {
+ dispose();
+ }
+ }
+ }
+
+ //A hollow entry, to be used with caution in controlled situations
+ protected Elf() {
+ }
+
+ public Elf(String file, long offset) throws IOException {
+ commonSetup(new ERandomAccessFile(file, "r"), file, offset); //$NON-NLS-1$
+ }
+
+ public Elf(String file) throws IOException {
+ commonSetup(new ERandomAccessFile(file, "r"), file, 0); //$NON-NLS-1$
+ }
+
+ public Elf(IRandomReadAccessFile rraFile, String file, long offset) throws IOException {
+ commonSetup(rraFile, file, offset);
+ }
+
+ public ELFhdr getELFhdr() throws IOException {
+ return ehdr;
+ }
+
+ public class Attribute {
+
+ public static final int ELF_TYPE_EXE = 1;
+ public static final int ELF_TYPE_SHLIB = 2;
+ public static final int ELF_TYPE_OBJ = 3;
+ public static final int ELF_TYPE_CORE = 4;
+
+ public static final int DEBUG_TYPE_NONE = 0;
+ public static final int DEBUG_TYPE_STABS = 1;
+ public static final int DEBUG_TYPE_DWARF = 2;
+
+ String cpu;
+ int type;
+ int debugType;
+ boolean bDebug;
+ boolean isle;
+ IAddressFactory addressFactory;
+
+ public String getCPU() {
+ return cpu;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public boolean hasDebug() {
+ return debugType != DEBUG_TYPE_NONE;
+ }
+
+ public int getDebugType() {
+ return debugType;
+ }
+
+ public boolean isLittleEndian() {
+ return isle;
+ }
+
+ public IAddressFactory getAddressFactory() {
+ return addressFactory;
+ }
+ }
+
+ public Attribute getAttributes() throws IOException {
+ Attribute attrib = new Attribute();
+
+ switch (ehdr.e_type) {
+ case Elf.ELFhdr.ET_CORE :
+ attrib.type = Attribute.ELF_TYPE_CORE;
+ break;
+ case Elf.ELFhdr.ET_EXEC :
+ attrib.type = Attribute.ELF_TYPE_EXE;
+ break;
+ case Elf.ELFhdr.ET_REL :
+ attrib.type = Attribute.ELF_TYPE_OBJ;
+ break;
+ case Elf.ELFhdr.ET_DYN :
+ attrib.type = Attribute.ELF_TYPE_SHLIB;
+ break;
+ }
+
+ switch (ehdr.e_machine & 0xFFFF) {
+ case Elf.ELFhdr.EM_386 :
+ case Elf.ELFhdr.EM_486 :
+ attrib.cpu = "x86"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_68K :
+ attrib.cpu = "m68k"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_PPC :
+ case Elf.ELFhdr.EM_CYGNUS_POWERPC :
+ case Elf.ELFhdr.EM_RS6000 :
+ attrib.cpu = "ppc"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_PPC64 :
+ attrib.cpu = "ppc64"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_SH :
+ attrib.cpu = "sh"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_ARM :
+ attrib.cpu = "arm"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_MIPS_RS3_LE :
+ case Elf.ELFhdr.EM_MIPS :
+ attrib.cpu = "mips"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_SPARC32PLUS :
+ case Elf.ELFhdr.EM_SPARC :
+ case Elf.ELFhdr.EM_SPARCV9 :
+ attrib.cpu = "sparc"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_H8_300 :
+ case Elf.ELFhdr.EM_H8_300H :
+ attrib.cpu = "h8300"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_V850 :
+ case Elf.ELFhdr.EM_CYGNUS_V850 :
+ attrib.cpu = "v850"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_MN10300 :
+ case Elf.ELFhdr.EM_CYGNUS_MN10300 :
+ attrib.cpu = "mn10300"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_MN10200 :
+ case Elf.ELFhdr.EM_CYGNUS_MN10200 :
+ attrib.cpu = "mn10200"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_M32R :
+ attrib.cpu = "m32r"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_FR30 :
+ case Elf.ELFhdr.EM_CYGNUS_FR30 :
+ attrib.cpu = "fr30"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_XSTORMY16 :
+ attrib.cpu = "xstormy16"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_CYGNUS_FRV :
+ attrib.cpu = "frv"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_IQ2000 :
+ attrib.cpu = "iq2000"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_EXCESS :
+ attrib.cpu = "excess"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_NIOSII :
+ attrib.cpu = "alteranios2"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_NIOS :
+ attrib.cpu = "alteranios"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_IA_64 :
+ attrib.cpu = "ia64"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_COLDFIRE:
+ attrib.cpu = "coldfire"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_AVR :
+ attrib.cpu = "avr"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_MSP430 :
+ attrib.cpu = "msp430"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_XTENSA:
+ attrib.cpu = "xtensa"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_ST100:
+ attrib.cpu = "st100"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_X86_64:
+ attrib.cpu = "x86_64"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_XILINX_MICROBLAZE:
+ attrib.cpu = "microblaze"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_C166:
+ attrib.cpu = "c166"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_TRICORE:
+ attrib.cpu = "TriCore"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_M16C:
+ attrib.cpu = "M16C"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_STARCORE:
+ attrib.cpu = "StarCore"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_BLACKFIN :
+ attrib.cpu = "bfin"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_SDMA:
+ attrib.cpu = "sdma"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_CRADLE:
+ attrib.cpu = "cradle"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_MMDSP:
+ attrib.cpu = "mmdsp"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_68HC08:
+ attrib.cpu = "hc08"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_RS08:
+ attrib.cpu = "rs08"; //$NON-NLS-1$
+ break;
+ case Elf.ELFhdr.EM_NONE :
+ default :
+ attrib.cpu = "none"; //$NON-NLS-1$
+ }
+ switch (ehdr.e_ident[Elf.ELFhdr.EI_DATA]) {
+ case Elf.ELFhdr.ELFDATA2LSB :
+ attrib.isle = true;
+ break;
+ case Elf.ELFhdr.ELFDATA2MSB :
+ attrib.isle = false;
+ break;
+ }
+ switch (ehdr.e_ident[ELFhdr.EI_CLASS]) {
+ case ELFhdr.ELFCLASS32 :
+ attrib.addressFactory = new Addr32Factory();
+ break;
+ case ELFhdr.ELFCLASS64 :
+ attrib.addressFactory = new Addr64Factory();
+ break;
+ case ELFhdr.ELFCLASSNONE :
+ default :
+ attrib.addressFactory = null;
+ }
+ // getSections
+ // find .debug using toString
+ Section[] sec = getSections();
+ if (sec != null) {
+ for (int i = 0; i < sec.length; i++) {
+ String s = sec[i].toString();
+ if (s.startsWith(".debug")) { //$NON-NLS-1$
+ attrib.debugType = Attribute.DEBUG_TYPE_DWARF;
+ break;
+ } else if (s.equals(".stab")) { //$NON-NLS-1$
+ attrib.debugType = Attribute.DEBUG_TYPE_STABS;
+ break;
+ }
+ }
+ }
+ return attrib;
+ }
+
+ public static Attribute getAttributes(String file) throws IOException {
+ Elf elf = new Elf(file);
+ Attribute attrib = elf.getAttributes();
+ elf.dispose();
+ return attrib;
+ }
+
+ public static Attribute getAttributes(byte[] array) throws IOException {
+
+ Elf emptyElf = new Elf();
+ emptyElf.ehdr = emptyElf.new ELFhdr(array);
+ emptyElf.sections = new Elf.Section[0];
+ Attribute attrib = emptyElf.getAttributes();
+ emptyElf.dispose();
+
+ return attrib;
+ }
+
+ public static boolean isElfHeader(byte[] e_ident) {
+ if (e_ident.length < 4 || e_ident[ELFhdr.EI_MAG0] != 0x7f || e_ident[ELFhdr.EI_MAG1] != 'E'
+ || e_ident[ELFhdr.EI_MAG2] != 'L' || e_ident[ELFhdr.EI_MAG3] != 'F')
+ return false;
+ return true;
+ }
+
+ public void dispose() {
+ try {
+ if (efile != null) {
+ efile.close();
+ efile = null;
+
+ // ensure the mappings get cleaned up
+ if (sections_mapped)
+ System.gc();
+ }
+ } catch (IOException e) {
+ }
+ }
+
+ /**
+ * Make sure we do not leak the fds.
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ dispose();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public Section getSectionByName(String name) throws IOException {
+ if (sections == null)
+ getSections();
+ for (int i = 0; i < sections.length; i++) {
+ if (sections[i].toString().equals(name)) {
+ return sections[i];
+ }
+ }
+ return null;
+ }
+
+ public Section[] getSections(int type) throws IOException {
+ if (sections == null)
+ getSections();
+ ArrayList<Section> slist = new ArrayList<Section>();
+ for (int i = 0; i < sections.length; i++) {
+ if (sections[i].sh_type == type)
+ slist.add(sections[i]);
+ }
+ return slist.toArray(new Section[0]);
+ }
+
+ public Section[] getSections() throws IOException {
+ if (sections == null) {
+ if (ehdr.e_shoff == 0) {
+ sections = new Section[0];
+ return sections;
+ }
+ final int length= ehdr.e_shnum & 0xffff; // unsigned short
+ sections = new Section[length];
+ for (int i = 0; i < length; i++) {
+ efile.seek(ehdr.e_shoff + i * (ehdr.e_shentsize & 0xffff)); // unsigned short
+ sections[i] = new Section();
+ sections[i].sh_name = efile.readIntE();
+ sections[i].sh_type = efile.readIntE();
+ switch (ehdr.e_ident[ELFhdr.EI_CLASS]) {
+ case ELFhdr.ELFCLASS32 : {
+ byte[] addrArray = new byte[ELF32_ADDR_SIZE];
+ sections[i].sh_flags = efile.readIntE();
+ efile.readFullyE(addrArray);
+ sections[i].sh_addr = new Addr32(addrArray);
+ sections[i].sh_offset = efile.readIntE();
+ sections[i].sh_size = efile.readIntE();
+ }
+ break;
+ case ELFhdr.ELFCLASS64 : {
+ byte[] addrArray = new byte[ELF64_ADDR_SIZE];
+ sections[i].sh_flags = efile.readLongE();
+ efile.readFullyE(addrArray);
+ sections[i].sh_addr = new Addr64(addrArray);
+ sections[i].sh_offset = readUnsignedLong(efile);
+ sections[i].sh_size = readUnsignedLong(efile);
+ }
+ break;
+ case ELFhdr.ELFCLASSNONE :
+ default :
+ throw new IOException("Unknown ELF class " + ehdr.e_ident[ELFhdr.EI_CLASS]); //$NON-NLS-1$
+ }
+
+ sections[i].sh_link = efile.readIntE();
+ sections[i].sh_info = efile.readIntE();
+ switch (ehdr.e_ident[ELFhdr.EI_CLASS]) {
+ case ELFhdr.ELFCLASS32 : {
+ sections[i].sh_addralign = efile.readIntE();
+ sections[i].sh_entsize = efile.readIntE();
+ }
+ break;
+ case ELFhdr.ELFCLASS64 : {
+ sections[i].sh_addralign = efile.readLongE();
+ sections[i].sh_entsize = readUnsignedLong(efile);
+ }
+ break;
+ case ELFhdr.ELFCLASSNONE :
+ default :
+ throw new IOException("Unknown ELF class " + ehdr.e_ident[ELFhdr.EI_CLASS]); //$NON-NLS-1$
+ }
+ if (sections[i].sh_type == Section.SHT_SYMTAB)
+ syms = i;
+ if (syms == 0 && sections[i].sh_type == Section.SHT_DYNSYM)
+ syms = i;
+ }
+ }
+ return sections;
+ }
+
+ private Symbol[] loadSymbolsBySection(Section section) throws IOException {
+ int numSyms = 1;
+ if (section.sh_entsize != 0) {
+ numSyms = (int)section.sh_size / (int)section.sh_entsize;
+ }
+ ArrayList<Symbol> symList = new ArrayList<Symbol>(numSyms);
+ long offset = section.sh_offset;
+ for (int c = 0; c < numSyms; offset += section.sh_entsize, c++) {
+ efile.seek(offset);
+ Symbol symbol = new Symbol(section);
+ switch (ehdr.e_ident[ELFhdr.EI_CLASS]) {
+ case ELFhdr.ELFCLASS32 : {
+ byte[] addrArray = new byte[ELF32_ADDR_SIZE];
+
+ symbol.st_name = efile.readIntE();
+ efile.readFullyE(addrArray);
+ symbol.st_value = new Addr32(addrArray);
+ symbol.st_size = efile.readIntE();
+ symbol.st_info = efile.readByte();
+ symbol.st_other = efile.readByte();
+ symbol.st_shndx = efile.readShortE();
+ }
+ break;
+ case ELFhdr.ELFCLASS64 : {
+ byte[] addrArray = new byte[ELF64_ADDR_SIZE];
+
+ symbol.st_name = efile.readIntE();
+ symbol.st_info = efile.readByte();
+ symbol.st_other = efile.readByte();
+ symbol.st_shndx = efile.readShortE();
+ efile.readFullyE(addrArray);
+ symbol.st_value = new Addr64(addrArray);
+ symbol.st_size = readUnsignedLong(efile);
+ }
+ break;
+ case ELFhdr.ELFCLASSNONE :
+ default :
+ throw new IOException("Unknown ELF class " + ehdr.e_ident[ELFhdr.EI_CLASS]); //$NON-NLS-1$
+ }
+ if (symbol.st_info == 0)
+ continue;
+ symList.add(symbol);
+ }
+ Symbol[] results = symList.toArray(new Symbol[0]);
+ Arrays.sort(results);
+ return results;
+ }
+
+ public void loadSymbols() throws IOException {
+ if (symbols == null) {
+ Section section[] = getSections(Section.SHT_SYMTAB);
+ if (section.length > 0) {
+ symtab_sym = section[0];
+ symtab_symbols = loadSymbolsBySection(section[0]);
+ } else {
+ symtab_sym = null;
+ symtab_symbols = new Symbol[0];
+ }
+
+ section = getSections(Section.SHT_DYNSYM);
+ if (section.length > 0) {
+ dynsym_sym = section[0];
+ dynsym_symbols = loadSymbolsBySection(section[0]);
+ } else {
+ dynsym_sym = null;
+ dynsym_symbols = new Symbol[0];
+ }
+
+ if (symtab_sym != null) {
+ // sym = symtab_sym;
+ symbols = symtab_symbols;
+ } else if (dynsym_sym != null) {
+ // sym = dynsym_sym;
+ symbols = dynsym_symbols;
+ }
+ }
+ }
+
+ public Symbol[] getSymbols() {
+ return symbols;
+ }
+
+ public Symbol[] getDynamicSymbols() {
+ return dynsym_symbols;
+ }
+
+ public Symbol[] getSymtabSymbols() {
+ return symtab_symbols;
+ }
+
+ /* return the address of the function that address is in */
+ public Symbol getSymbol(IAddress vma) {
+ if (symbols == null) {
+ return null;
+ }
+
+ //@@@ If this works, move it to a single instance in this class.
+ SymbolComparator symbol_comparator = new SymbolComparator();
+
+ int ndx = Arrays.binarySearch(symbols, vma, symbol_comparator);
+ if (ndx > 0)
+ return symbols[ndx];
+ if (ndx == -1) {
+ return null;
+ }
+ ndx = -ndx - 1;
+ return symbols[ndx - 1];
+ }
+ /*
+ * public long swapInt( long val ) { if ( ehdr.e_ident[ELFhdr.EI_DATA] ==
+ * ELFhdr.ELFDATA2LSB ) { short tmp[] = new short[4]; tmp[0] = (short)(val &
+ * 0x00ff); tmp[1] = (short)((val >> 8) & 0x00ff); tmp[2] = (short)((val >>
+ * 16) & 0x00ff); tmp[3] = (short)((val >> 24) & 0x00ff); return ((tmp[0] < <
+ * 24) + (tmp[1] < < 16) + (tmp[2] < < 8) + tmp[3]); } return val; }
+ *
+ * public int swapShort( short val ) { if ( ehdr.e_ident[ELFhdr.EI_DATA] ==
+ * ELFhdr.ELFDATA2LSB ) { short tmp[] = new short[2]; tmp[0] = (short)(val &
+ * 0x00ff); tmp[1] = (short)((val >> 8) & 0x00ff); return (short)((tmp[0] < <
+ * 8) + tmp[1]); } return val; }
+ */
+ public String getFilename() {
+ return file;
+ }
+
+ protected long readUnsignedLong(IRandomReadAccessFile file) throws IOException {
+ long result = file.readLongE();
+ if (result < 0) {
+ throw new IOException("Maximal file offset is " + Long.toHexString(Long.MAX_VALUE) + //$NON-NLS-1$
+ " given offset is " + Long.toHexString(result)); //$NON-NLS-1$
+ }
+ return result;
+ }
+
+ /* TODO: not used in EDC
+ private ISymbolReader createDwarfReader() {
+ DwarfReader reader = null;
+ // Check if Dwarf data exists
+ try {
+ reader = new DwarfReader(this);
+ } catch (IOException e) {
+ // No Dwarf data in the Elf.
+ }
+ return reader;
+ }
+ */
+
+ public ISymbolReader getSymbolReader() {
+ ISymbolReader reader = null;
+ //reader = createDwarfReader(); // TODO: not used in EDC
+ return reader;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * QNX Software Systems - Initial API and implementation
+ * Nokia - split out interface
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.elf;
+
+import java.io.Closeable;
+import java.io.DataInput;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+
+/**
+ * Abstraction for the file reading used by the ERandomAccessFile and Elf classes.
+ * <p>
+ * This provides endian-aware streamed read access to a file (methods ending in 'E').
+ * The {@link #setEndian(boolean)} call must be invoked prior to calling them.
+ * <p>
+ * This interface permits clients to interleave calls to the big-endian {@link DataInput}
+ * methods as well as the mixed-endian methods in this interface. The endianness
+ * may also be changed at will.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @since 7.0
+ */
+public interface IRandomReadAccessFile extends DataInput, Closeable {
+
+ /**
+ * Set endianness of file content.
+ * @param le true for little-endian, false for big-endian.
+ */
+ void setEndian(boolean le);
+
+ /**
+ * Set a base offset from which seek() and getFilePointer() measure,
+ * and seek there.
+ */
+ void setFileOffset(long offset) throws IOException;
+
+ /**
+ * Get the basis for seek operations
+ * @see #setFileOffset(long)
+ * @return offset
+ * @throws IOException
+ */
+ long getFilePointer() throws IOException;
+
+ /**
+ * Seek relative to the pointer set by {@link #setFileOffset(long)}.
+ * @see RandomAccessFile#seek(long)
+ */
+ void seek(long pos) throws IOException;
+
+ /**
+ * Read 2 bytes and construct a short according to endianness.
+ * @see DataInput#readShort()
+ */
+ short readShortE() throws IOException;
+
+ /**
+ * Read 4 bytes and construct an int according to endianness.
+ * @see DataInput#readInt()
+ */
+ long readIntE() throws IOException;
+
+ /**
+ * Read 8 bytes and construct a long according to endianness.
+ * @see DataInput#readLong()
+ */
+ long readLongE() throws IOException;
+
+ /**
+ * Read content and swap the entire range as if it were one large
+ * integer, according to the endianness.
+ * <p>
+ * This assumes the incoming data is big-endian, so only swaps if
+ * {@link #setEndian(boolean)} was called with 'true'.
+ * @see DataInput#readFully(byte[])
+ */
+ void readFullyE(byte[] bytes) throws IOException;
+
+ /** @see RandomAccessFile#read(byte[], int, int) */
+ int read(byte b[], int off, int len) throws IOException;
+
+ /**
+ * @see RandomAccessFile#read(byte[])
+ */
+ int read(byte b[]) throws IOException;
+
+ /**
+ * @see RandomAccessFile#length()
+ */
+ long length() throws IOException;
+
+ /**
+ * Get a read-only buffer for the given range
+ * @param offset absolute offset (<b>not</b> relative to {@link #setFileOffset(long)}).
+ * @param size the size in bytes; may not extend beyond EOF.
+ */
+ ByteBuffer createReadByteBuffer(long offset, long size) throws IOException;
+}
--- /dev/null
+
+This is a fork of the Elf and related classes from org.eclipse.cdt.core.utils
+which is intended to to merged back into CDT core for the 7.1 or 8.0 release.
+Don't add any new functionality to these.
+
+ https://bugs.eclipse.org/bugs/show_bug.cgi?id=315420
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSection;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.ISymbol;
+import org.eclipse.cdt.debug.edc.symbols.IUnmangler;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Base implementation of a symbolics reader. Subclasses populae sections and symbols
+ * on construction.
+ */
+public abstract class BaseExecutableSymbolicsReader implements IExecutableSymbolicsReader {
+
+ protected final IPath binaryFile;
+
+ protected Map<String, IExecutableSection> executableSections = new HashMap<String, IExecutableSection>();
+ protected List<ISection> sections = new ArrayList<ISection>();
+ protected List<ISymbol> symbols = new ArrayList<ISymbol>();
+ protected IAddress exeBaseAddress;
+ protected long modificationDate;
+ protected ISectionMapper sectionMapper;
+
+ protected IUnmangler unmangler;
+
+ /**
+ *
+ */
+ public BaseExecutableSymbolicsReader(IPath binaryFile) {
+ this.binaryFile = binaryFile;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSymbolicsReader#dispose()
+ */
+ public void dispose() {
+ if (sectionMapper != null) {
+ sectionMapper.dispose();
+ sectionMapper = null;
+ }
+ sections.clear();
+ symbols.clear();
+ }
+
+ public IPath getSymbolFile() {
+ return binaryFile;
+ }
+
+ public Collection<IExecutableSection> getExecutableSections() {
+ return Collections.unmodifiableCollection(executableSections.values());
+ }
+
+ public IExecutableSection findExecutableSection(String sectionName) {
+ return executableSections.get(sectionName);
+ }
+
+ public Collection<ISection> getSections() {
+ return Collections.unmodifiableCollection(sections);
+ }
+
+ public Collection<ISymbol> getSymbols() {
+ return Collections.unmodifiableCollection(symbols);
+ }
+
+ public ISymbol getSymbolAtAddress(IAddress linkAddress) {
+ int insertion = Collections.binarySearch(symbols, linkAddress);
+ if (insertion >= 0) {
+ return symbols.get(insertion);
+ }
+
+ if (insertion == -1) {
+ return null;
+ }
+
+ insertion = -insertion - 1;
+
+ ISymbol symbol = symbols.get(insertion - 1);
+ if (linkAddress.compareTo(symbol.getAddress().add(symbol.getSize())) < 0) {
+ return symbol;
+ }
+
+ return null;
+ }
+
+ public IAddress getBaseLinkAddress() {
+ return exeBaseAddress;
+ }
+
+ public long getModificationDate() {
+ return modificationDate;
+ }
+
+ public Collection<ISymbol> findSymbols(String name) {
+ List<ISymbol> matchSymbols = new ArrayList<ISymbol>();
+
+ // look for exact symbols
+ for (ISymbol symbol : symbols) {
+ String symName = symbol.getName();
+ if (symName.equals(name)) {
+ matchSymbols.add(symbol);
+ }
+ }
+ if (!matchSymbols.isEmpty())
+ return matchSymbols;
+
+ // try for a decorated symbol if no match
+ if (unmangler != null) {
+ for (ISymbol symbol : symbols) {
+ String symName = unmangler.undecorate(symbol.getName());
+ if (symName.equals(name)) {
+ matchSymbols.add(symbol);
+ }
+ }
+ }
+
+ return matchSymbols;
+ }
+
+ public Collection<ISymbol> findUnmangledSymbols(String name) {
+ List<ISymbol> matchSymbols = new ArrayList<ISymbol>();
+
+ if (unmangler != null) {
+ name = unmangler.undecorate(name);
+
+ String nameNoSpaces = name.replaceAll("\\s", "");
+
+ // remove full qualifier
+ if (nameNoSpaces.startsWith("::"))
+ nameNoSpaces = nameNoSpaces.substring(2);
+
+ boolean nameNoArguments = !nameNoSpaces.endsWith(")");
+
+ // avoid unmangling a lot of irrelevant symbols by filtering out symbols not containing the base name
+ String undecoratedBase = nameNoSpaces;
+ int idx = undecoratedBase.lastIndexOf(':');
+ if (idx >= 0)
+ undecoratedBase = undecoratedBase.substring(idx+1);
+ idx = undecoratedBase.indexOf('(');
+ if (idx >= 0)
+ undecoratedBase = undecoratedBase.substring(0, idx);
+
+ for (ISymbol symbol : symbols) {
+ String symName = symbol.getName();
+ if (!symName.contains(undecoratedBase))
+ continue;
+
+ try {
+ String unmangled = unmangler.unmangle(unmangler.undecorate(symName));
+ if (unmangled != null) {
+ String unmangledNoSpaces;
+ // remove any 'const' which is in front of '(' for now
+ unmangledNoSpaces = unmangled.replaceAll("\\bconst\\s*(?=\\()", "");
+ unmangledNoSpaces = unmangledNoSpaces.replaceAll("\\s", "");
+
+ // remove full qualifier
+ if (unmangledNoSpaces.startsWith("::"))
+ unmangledNoSpaces = unmangledNoSpaces.substring(2);
+
+ if (nameNoSpaces.equals(unmangledNoSpaces)) {
+ matchSymbols.add(symbol);
+ } else if (nameNoArguments) {
+ // try to match the name against a function
+ idx = unmangledNoSpaces.lastIndexOf('(');
+ if (idx >= 0) {
+ String unmangledNoArguments = unmangledNoSpaces.substring(0, idx);
+ if (unmangledNoArguments.equals(nameNoSpaces)) {
+ matchSymbols.add(symbol);
+ }
+ }
+ }
+ }
+ } catch (UnmanglingException e) {
+ // nope
+ }
+ }
+ if (!matchSymbols.isEmpty())
+ return matchSymbols;
+ }
+
+ return matchSymbols;
+ }
+
+ public IUnmangler getUnmangler() {
+ return unmangler;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider;
+import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProviderFactory;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * Factory for creating debug symbolics providers from executables.
+ */
+public class DebugInfoProviderFactory {
+ private static Map<String, IDebugInfoProviderFactory> providerMap = new HashMap<String, IDebugInfoProviderFactory>();
+
+ static {
+ initializeExtensions();
+ }
+
+ /**
+ * Create a debug info provider for the given binary (usually the executable being
+ * debugged). It's up to a {@link IDebugInfoProviderFactory}
+ * implementation to determine how it maps a binary to a symbolics file.
+ * @param binaryPath path to a host file
+ * @param exeReader the reader for that file, or <code>null</code>
+ * @return {@link IDebugInfoProvider} or <code>null</code> if nothing supports it
+ */
+ public static IDebugInfoProvider createFor(IPath binaryPath, IExecutableSymbolicsReader exeReader) {
+ for (Map.Entry<String, IDebugInfoProviderFactory> entry: providerMap.entrySet()) {
+ String name = entry.getKey();
+ IDebugInfoProviderFactory providerProvider = entry.getValue();
+ try {
+ IDebugInfoProvider provider = providerProvider.createDebugInfoProvider(binaryPath, exeReader);
+ if (provider != null)
+ return provider;
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError("Debug info reader " + name + " failed", t);
+ }
+ }
+ return null;
+ }
+
+ protected static void initializeExtensions() {
+ IConfigurationElement[] elements =
+ Platform.getExtensionRegistry().getConfigurationElementsFor(IDebugInfoProviderFactory.EXTENSION_ID);
+ for (IConfigurationElement element : elements) {
+ try {
+ String name = element.getAttribute("name"); //$NON-NLS-1$
+ IDebugInfoProviderFactory formatProvider =
+ (IDebugInfoProviderFactory) element.createExecutableExtension("class"); //$NON-NLS-1$
+ providerMap.put(name, formatProvider);
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError("Could not create executable symbolics provider extension", e);
+ }
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
+import org.eclipse.cdt.debug.edc.internal.symbols.Section;
+import org.eclipse.cdt.debug.edc.internal.symbols.Symbol;
+import org.eclipse.cdt.debug.edc.internal.symbols.elf.Elf;
+import org.eclipse.cdt.debug.edc.internal.symbols.elf.Elf.PHdr;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSection;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * This class handles reading ELF files for the purposes of detecting symbolics.
+ */
+public class ElfExecutableSymbolicsReader extends BaseExecutableSymbolicsReader {
+ protected boolean isLE;
+
+ public ElfExecutableSymbolicsReader(IPath binaryFile, Elf elf) throws IOException {
+ super(binaryFile);
+
+ Elf.ELFhdr header = elf.getELFhdr();
+ isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB;
+ exeBaseAddress = getExeSegment(elf).p_vaddr;
+ modificationDate = binaryFile.toFile().lastModified();
+
+ sectionMapper = new SectionMapper(binaryFile, isLE);
+
+ recordSections(elf);
+ readSymbols(elf);
+
+ // TODO: better selection. We assume for now that all ELF targets we know about (ARM, Linux) use the same mangling.
+ unmangler = new UnmanglerEABI();
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "ELF symbolics reader for " + binaryFile; //$NON-NLS-1$
+ }
+
+ /**
+ * Determine the executable format and record the sections.
+ * @param elfFile
+ *
+ * @throws IOException if file contents cannot be read
+ */
+ private void recordSections(Elf elfFile) throws IOException {
+
+ // start from zero so that we can use it as index to the array list
+ // for quick access.
+ int id = 0;
+ Map<String, Object> props;
+
+ // Use segments instead of sections in the Elf file.
+ PHdr[] segments = elfFile.getPHdrs();
+
+ for (PHdr s : segments) {
+ if (s.p_type == PHdr.PT_LOAD) {
+ props = new HashMap<String, Object>();
+
+ if ((s.p_flags & PHdr.PF_X) != 0)
+ props.put(ISection.PROPERTY_NAME, ISection.NAME_TEXT);
+ else
+ // There is no clear way to tell if a segment is
+ // data or bss segment.
+ props.put(ISection.PROPERTY_NAME, ISection.NAME_DATA);
+
+ Section section = new Section(id++, s.p_memsz, s.p_vaddr, props);
+ sections.add(section);
+ }
+ }
+
+ // remember how to map the sections
+ Elf.Section[] sections = elfFile.getSections();
+ for (Elf.Section section : sections) {
+ String name = section.toString();
+
+ if (name.length() > 0) {
+ if (executableSections.containsKey(name))
+ throw new IllegalStateException("duplicate section " + name);
+ IExecutableSection exeSection = new ExecutableSection(sectionMapper, name,
+ new SectionInfo(section.sh_offset, section.sh_size));
+ executableSections.put(name, exeSection);
+ }
+ }
+ }
+
+ protected void readSymbols(Elf elfFile) throws IOException {
+ // load the symbol table
+ elfFile.loadSymbols();
+ Set<IAddress> symbolAddressSet = new TreeSet<IAddress>();
+
+ for (Elf.Symbol symbol : elfFile.getSymtabSymbols()) {
+ String name = symbol.toString();
+ // Multiple symbol entries for the same address are generated.
+ // Do not add duplicate symbols with 0 size to the list since it confuses
+ // debugger
+ if (name.length() > 0) {
+ if (symbol.st_size != 0 || !symbolAddressSet.contains(symbol.st_value)) {
+ // need to get rid of labels with size 0
+ if (symbol.st_size != 0 || !name.startsWith("|")) {
+ symbols.add(new Symbol(symbol.toString(), symbol.st_value, symbol.st_size));
+ symbolAddressSet.add(symbol.st_value);
+ }
+ }
+ }
+ }
+
+ // now sort it by address for faster lookups
+ Collections.sort(symbols);
+ }
+
+ /**
+ * Find the executable (text) segment of the elf file, assuming there is
+ * only one that segment.
+ *
+ * @param elf
+ * @return exe segment header or null on error.
+ * @throws IOException
+ */
+ private PHdr getExeSegment(Elf elf) throws IOException {
+ PHdr[] segments = elf.getPHdrs();
+
+ for (PHdr s : segments) {
+ if (s.p_type == PHdr.PT_LOAD && ((s.p_flags & PHdr.PF_X) != 0))
+ return s;
+ }
+
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IExecutableSymbolicsReader#getByteOrder()
+ */
+ public ByteOrder getByteOrder() {
+ return isLE ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.debug.edc.internal.symbols.elf.BufferedRandomReadAccessFile;
+import org.eclipse.cdt.debug.edc.internal.symbols.elf.Elf;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReaderFactory;
+import org.eclipse.cdt.utils.elf.Elf.ELFhdr;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Factory for creating readers of symbolics in executables.
+ */
+public class ElfExecutableSymbolicsReaderFactory implements IExecutableSymbolicsReaderFactory {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IExecutableSymbolicsReaderFactory#getConfidence(org.eclipse.core.runtime.IPath)
+ */
+ public int getConfidence(IPath binaryFile) {
+ Elf elfFile = getElfFile(binaryFile);
+ if (elfFile == null) {
+ // treat the symbol file as an executable, if existing
+ IPath symbolFilePath = ExecutableSymbolicsReaderFactory.findSymbolicsFile(binaryFile);
+ if (symbolFilePath != null) {
+ elfFile = getElfFile(symbolFilePath);
+ }
+ }
+ return elfFile != null ? IExecutableSymbolicsReaderFactory.NORMAL_CONFIDENCE :
+ IExecutableSymbolicsReaderFactory.NO_CONFIDENCE;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IExecutableSymbolicsReaderFactory#createExecutableSymbolicsReader(org.eclipse.core.runtime.IPath)
+ */
+ public IExecutableSymbolicsReader createExecutableSymbolicsReader(
+ IPath binaryFile) {
+
+ IExecutableSymbolicsReader reader = detectExecutable(binaryFile);
+ if (reader == null) {
+ // treat the symbol file as an executable, if existing
+ IPath symbolFilePath = ExecutableSymbolicsReaderFactory.findSymbolicsFile(binaryFile);
+ if (symbolFilePath != null) {
+ reader = detectExecutable(symbolFilePath);
+ }
+ }
+
+ return reader;
+ }
+
+ private IExecutableSymbolicsReader detectExecutable(IPath binaryFile) {
+ try {
+ Elf elfFile = getElfFile(binaryFile);
+ if (elfFile != null) {
+ return new ElfExecutableSymbolicsReader(binaryFile, elfFile);
+ }
+ } catch (IOException e) {
+ // this class elides actual I/O errors with format errors; ignore
+ }
+ return null;
+ }
+
+ private Elf getElfFile(IPath binaryFile) {
+ try {
+ // quickly check the endianness (Elf repeats this)
+ FileInputStream fis = new FileInputStream(binaryFile.toOSString());
+ byte[] e_ident = new byte[16];
+ fis.read(e_ident);
+ if (e_ident[ELFhdr.EI_MAG0] != 0x7f || e_ident[ELFhdr.EI_MAG1] != 'E' || e_ident[ELFhdr.EI_MAG2] != 'L'
+ || e_ident[ELFhdr.EI_MAG3] != 'F')
+ throw new IOException(CCorePlugin.getResourceString("Util.exception.notELF")); //$NON-NLS-1$
+
+ boolean isle = (e_ident[ELFhdr.EI_DATA] == ELFhdr.ELFDATA2LSB);
+
+ // If this constructor succeeds, it's ELF
+ Elf elf = new Elf(new BufferedRandomReadAccessFile(binaryFile.toOSString(), isle),
+ binaryFile.toOSString(), 0);
+ return elf;
+ } catch (IOException e) {
+ // this class elides actual I/O errors with format errors; ignore
+ }
+
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.io.IOException;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSection;
+
+/**
+ *
+ */
+public class ExecutableSection implements IExecutableSection {
+
+ private final String name;
+ private final ISectionMapper executableSectionMapper;
+ private final SectionInfo section;
+ private IStreamBuffer buffer;
+ private boolean deadSection;
+
+ /**
+ * @param section
+ * @param name
+ * @param elfExecutableSymbolicsReader
+ */
+ public ExecutableSection(ISectionMapper executableSectionMapper,
+ String name,
+ SectionInfo section) {
+ this.executableSectionMapper = executableSectionMapper;
+ this.name = name;
+ this.section = section;
+ this.deadSection = false;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return name + " @ " + section + (deadSection ? " <<BROKEN>>": ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSection#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSection#getBuffer()
+ */
+ public IStreamBuffer getBuffer() {
+ if (buffer == null && !deadSection) {
+ try {
+ buffer = executableSectionMapper.getSectionBuffer(section);
+ } catch (IOException e) {
+ deadSection = true;
+ EDCDebugger.getMessageLogger().logError("Failed to read section " + name, e);
+ }
+ }
+ return buffer;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSection#dispose()
+ */
+ public void dispose() {
+ executableSectionMapper.releaseSectionBuffer(section);
+ buffer = null;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReaderFactory;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Platform;
+
+/**
+ * Factory for creating readers of symbolics in executables.
+ */
+public class ExecutableSymbolicsReaderFactory {
+
+ private static final String SYM_EXTENSION = "sym";
+ private static final String DBG_EXTENSION = "dbg";
+
+ private static Map<String, IExecutableSymbolicsReaderFactory> providerMap = new HashMap<String, IExecutableSymbolicsReaderFactory>();
+
+ static {
+ initializeExtensions();
+ }
+
+ public static IExecutableSymbolicsReader createFor(IPath binaryFile) {
+ IExecutableSymbolicsReaderFactory provider = null;
+ String providerName = null;
+ int highestConfidence = IExecutableSymbolicsReaderFactory.NO_CONFIDENCE;
+
+ // find the extension with the highest confidence for this binary
+ for (Map.Entry<String, IExecutableSymbolicsReaderFactory> entry : providerMap.entrySet()) {
+ IExecutableSymbolicsReaderFactory factory = entry.getValue();
+ try {
+ int confidence = factory.getConfidence(binaryFile);
+ if (confidence > highestConfidence) {
+ highestConfidence = confidence;
+ provider = factory;
+ providerName = entry.getKey();
+ }
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError("Executable reader " + entry.getKey() + " failed", t);
+ }
+ }
+
+ if (provider != null) {
+ try {
+ IExecutableSymbolicsReader reader = provider.createExecutableSymbolicsReader(binaryFile);
+ if (reader != null)
+ return reader;
+ } catch (Throwable t) {
+ EDCDebugger.getMessageLogger().logError("Executable reader " + providerName + " failed", t);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get a symbolics file which is associated with the given executable.
+ * @param binaryFile
+ * @return IPath or <code>null</code> if no candidate (or already looks like a sym file)
+ */
+ public static IPath findSymbolicsFile(IPath binaryFile) {
+
+ // Check to see if there is a sym file we should use for the symbols
+ //
+ // Note: there may be for "foo.exe" --> "foo.exe.sym" or "foo.sym"
+ //
+ // Note #2: there may be BOTH. Pick the newest one.
+ //
+ List<IPath> candidates = new ArrayList<IPath>();
+
+ IPath symFile;
+ symFile = binaryFile.removeFileExtension().addFileExtension(SYM_EXTENSION);
+ if (symFile.toFile().exists())
+ candidates.add(symFile);
+ symFile = binaryFile.removeFileExtension().addFileExtension(DBG_EXTENSION);
+ if (symFile.toFile().exists())
+ candidates.add(symFile);
+
+ symFile = binaryFile.addFileExtension(SYM_EXTENSION);
+ if (symFile.toFile().exists())
+ candidates.add(symFile);
+ symFile = binaryFile.addFileExtension(DBG_EXTENSION);
+ if (symFile.toFile().exists())
+ candidates.add(symFile);
+
+ if (candidates.isEmpty())
+ return null;
+
+ if (candidates.size() > 1) {
+ Collections.sort(candidates, new java.util.Comparator<IPath>() {
+ public int compare(IPath o1, IPath o2) {
+ long diff = o1.toFile().lastModified() - o2.toFile().lastModified();
+ return diff > 0 ? -1 : diff < 0 ? 1 : 0;
+ }
+ });
+ }
+
+ return candidates.get(0);
+ }
+
+ protected static void initializeExtensions() {
+ IConfigurationElement[] elements =
+ Platform.getExtensionRegistry().getConfigurationElementsFor(IExecutableSymbolicsReaderFactory.EXTENSION_ID);
+ for (IConfigurationElement element : elements) {
+ try {
+ String name = element.getAttribute("name"); //$NON-NLS-1$
+ IExecutableSymbolicsReaderFactory formatProvider =
+ (IExecutableSymbolicsReaderFactory) element.createExecutableExtension("class"); //$NON-NLS-1$
+ providerMap.put(name, formatProvider);
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError("Could not create executable symbolics provider extension", e);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * Statistics tracking for file operations (used for debugging and unit tests)
+ */
+public class FileStatistics {
+ /** if set, dump info to console at runtme */
+ public static boolean DEBUG = false;
+ /** # of executables opened in session */
+ public static int executablesOpened;
+ /** # of executables currently open in session */
+ public static int executablesOpen;
+ /** amount of memory for buffers currently allocated on heap */
+ public static long currentHeapAllocatedBuffers;
+ /** amount of memory for buffers currently allocated in memory maps */
+ /** amount of memory for buffers ever allocated on heap */
+ public static long totalHeapAllocatedBuffers;
+ public static long currentMemoryMappedBuffers;
+ /** amount of memory for buffers ever allocated in memory maps */
+ public static long totalMemoryMappedBuffers;
+
+ /** Log interesting information */
+ public static void log(String line) {
+ if (DEBUG)
+ System.out.println(line);
+ }
+
+ private static String now() {
+ Calendar cal = Calendar.getInstance();
+ DateFormat sdf = SimpleDateFormat.getTimeInstance();
+ return sdf.format(cal.getTime());
+ }
+
+ public static void dump() {
+ System.out.println("File statistics at " + now() + ":");
+ System.out.println("\t# executables opened: " + executablesOpened);
+ System.out.println("\t# executables still open: " + executablesOpen);
+ System.out.println("\tcurrent heap buffer allocation: " + currentHeapAllocatedBuffers);
+ System.out.println("\ttotal heap buffer allocation: " + totalHeapAllocatedBuffers);
+ System.out.println("\tcurrent memory mapped buffer allocation: " + currentMemoryMappedBuffers);
+ System.out.println("\ttotal memory mapped buffer allocation: " + totalMemoryMappedBuffers);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.io.IOException;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Handle mapping sections of an executable into memory and caching the content.
+ * This may hold files open and consume heap and system memory.
+ */
+public interface ISectionMapper {
+ /**
+ * Get the associated file on which content is mapped.
+ */
+ IPath getMappedFile();
+
+ /**
+ * Get the contents of a section. The buffer may be cached or may be fetched on-demand,
+ * so there is no (memory) limit on its size.
+ * The buffer has the appropriate endianness for the file.
+ * @param section a backend-specific object representing a section
+ * @return an {@link IStreamBuffer} for the contents
+ * @throws IOException if contents could not be (re-)read.
+ */
+ IStreamBuffer getSectionBuffer(SectionInfo section) throws IOException;
+
+ /**
+ * Explicitly release a section's buffer. References to the previously-returned
+ * ByteBuffer may become invalid.
+ */
+ void releaseSectionBuffer(SectionInfo section);
+
+ /**
+ * Free any cached content and close any open files.
+ */
+ void dispose();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteOrder;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
+import org.eclipse.cdt.debug.edc.internal.symbols.Section;
+import org.eclipse.cdt.debug.edc.internal.symbols.Symbol;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSection;
+import org.eclipse.cdt.debug.edc.symbols.ISymbol;
+import org.eclipse.cdt.utils.Addr32;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.cdt.utils.coff.Coff.SectionHeader;
+import org.eclipse.cdt.utils.coff.PE;
+import org.eclipse.cdt.utils.coff.PE.NTOptionalHeader;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * This class handles PE-COFF files for the purpose of supporting symbolics.
+ */
+public class PEFileExecutableSymbolicsReader extends BaseExecutableSymbolicsReader {
+
+ static public final String CODEVIEW_SECTION_NAME = "CodeView_Data";
+ /** .CRT is another initialized data section utilized by the Microsoft C/C++ run-time libraries
+ * @since 5.2
+ */
+ public final static String _CRT = ".CRT"; //$NON-NLS-1$
+
+ protected boolean isLE;
+ protected Map<Integer, ISection> sectionsByPEID = new HashMap<Integer, ISection>();
+
+ public PEFileExecutableSymbolicsReader(IPath binaryFile, PE peFile) throws IOException {
+ super(binaryFile);
+ isLE = true;
+ exeBaseAddress = new Addr32(peFile.getNTOptionalHeader().ImageBase);
+ modificationDate = binaryFile.toFile().lastModified();
+
+ sectionMapper = new SectionMapper(binaryFile, isLE);
+
+ recordSections(peFile);
+
+ // TODO: better selection.
+ boolean isWin32 = false, isEABI = false;
+ for (ISymbol symbol : symbols) {
+ String symname = symbol.getName();
+ if (symname.startsWith("__Z") && symname.endsWith("v")) {
+ isWin32 = true;
+ isEABI = true;
+ break;
+ } else if (symname.startsWith("_Z") && symname.endsWith("v")) {
+ isEABI = true;
+ break;
+ } else if (symname.contains("@") && symname.contains("?")) {
+ isWin32 = true;
+ break;
+ }
+ }
+ if (isWin32 && isEABI)
+ unmangler = new UnmanglerWin32EABI();
+ else if (isEABI)
+ unmangler = new UnmanglerEABI();
+ else
+ unmangler = new UnmanglerWin32();
+
+
+ }
+
+ /**
+ * Determine the executable format and record the sections.
+ * @param peFile
+ *
+ * @throws IOException if file reading fails
+ */
+ private void recordSections(PE peFile) throws IOException {
+ // start from zero so that we can use it as index to the array list
+ // for quick access.
+ int id = 0;
+ Map<String, Object> props;
+
+ SectionHeader[] secHeaders = peFile.getSectionHeaders();
+ long imageBase = peFile.getNTOptionalHeader().ImageBase & 0xffffffffL;
+ SectionHeader rDataHeader = null;
+ int peSectionID = 0;
+
+ for (SectionHeader s : secHeaders) {
+ peSectionID++;
+ String name = new String(s.s_name).trim();
+ if (name.startsWith("/")) //$NON-NLS-1$
+ {
+ int stringTableOffset = Integer.parseInt(name.substring(1));
+ name = peFile.getStringTableEntry(stringTableOffset);
+ }
+
+ // Remember how to map this section
+ if (executableSections.containsKey(name))
+ throw new IllegalStateException("duplicate section " + name);
+ IExecutableSection exeSection = new ExecutableSection(sectionMapper, name,
+ new SectionInfo(s.s_scnptr, s.s_paddr));
+ executableSections.put(name, exeSection);
+
+ String sectionName = name;
+ // Convert the name to our unified name.
+ if (sectionName.equals(SectionHeader._TEXT))
+ name = ISection.NAME_TEXT;
+ else if (sectionName.equals(SectionHeader._DATA) || sectionName.equals(_CRT))
+ name = ISection.NAME_DATA;
+ else if (sectionName.equals(".rdata")) // add this name in SectionHeader ?
+ {
+ name = ISection.NAME_RODATA;
+ rDataHeader = s;
+ }
+ else if (sectionName.equals(SectionHeader._BSS))
+ name = ISection.NAME_BSS;
+ else { // ignore other section.
+ continue;
+ }
+
+ // Well, PE is a _modified_ version of COFF, where
+ // section.s_paddr of COFF becomes "VirtualSize"
+ // (memory size of the section) in PE, while s_size
+ // is raw data size (file size of the section).
+ long size = s.s_paddr; // not s_size !
+
+ props = new HashMap<String, Object>();
+ props.put(ISection.PROPERTY_NAME, name);
+
+ // Note the s_vaddr is relative to image base.
+ // For Section we need absolute address.
+ Section newSection = new Section(id++, size, new Addr64(Long.toString(imageBase + s.s_vaddr)), props);
+ sections.add(newSection);
+ sectionsByPEID.put(peSectionID, newSection);
+ }
+
+ // load the symbol table
+ //
+ /*
+ * Note this "rawSymbols" array contains both standard and auxiliary
+ * symbol records. It's assumed symbols in the array are in the same
+ * order they appear in the symbol table section, no sorting of any kind
+ * is done.
+ *
+ * Actually auxiliary symbols should not be treated the same as standard
+ * symbols by Coff and PE in CDT core. But fixing that would break API,
+ * which is not allowed for CDT 7.0 at this time......... 04/07/10
+ */
+ org.eclipse.cdt.utils.coff.Coff.Symbol[] rawSymbols = peFile.getSymbols();
+
+ for (int i=0; i < rawSymbols.length; i++) {
+ org.eclipse.cdt.utils.coff.Coff.Symbol symbol = rawSymbols[i];
+
+ if (!(symbol.n_type == 0)) // Change to Coff.isNoSymbol for CDT 8.0.
+ {
+ String symName = symbol.getName(peFile.getStringTable());
+ symbols.add(new Symbol(symName, new Addr32(symbol.n_value), 1));
+ }
+
+ // skip auxiliary symbol record(s) if any as otherwise they may
+ // give us bogus match in any symbol table lookup.
+ if (symbol.n_numaux > 0) {
+ i += symbol.n_numaux;
+ }
+ }
+
+ if (rDataHeader != null)
+ checkForCodeView(peFile, rDataHeader, imageBase, id);
+
+ // now sort it by address for faster lookups
+ Collections.sort(symbols);
+ }
+
+ private void checkForCodeView(PE peFile, SectionHeader rDataHeader, long imageBase, int id) throws IOException { //$NON-NLS-1$
+ // figure out the file offset of the debug directory
+ // entries
+ final int IMAGE_DIRECTORY_ENTRY_DEBUG = 6;
+ final int DEBUGDIRSZ = 28;
+ NTOptionalHeader ntHeader = peFile.getNTOptionalHeader();
+ if (ntHeader == null
+ || ntHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_DEBUG)
+ return;
+
+ int debugDir = ntHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
+ if (debugDir == 0)
+ return;
+
+ int debugFormats = ntHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / 28;
+ if (debugFormats == 0)
+ return;
+
+ int offsetInto_rdata = debugDir
+ - rDataHeader.s_vaddr;
+ int fileOffset = rDataHeader.s_scnptr
+ + offsetInto_rdata;
+ RandomAccessFile accessFile = new RandomAccessFile(
+ binaryFile.toOSString(), "r");
+
+ // loop through the debug directories looking for
+ // CodeView (type 2)
+ for (int j = 0; j < debugFormats; j++) {
+ PE.IMAGE_DEBUG_DIRECTORY dir = new PE.IMAGE_DEBUG_DIRECTORY(
+ accessFile, fileOffset);
+
+ if ((dir.Type == 2) && (dir.SizeOfData > 0)) {
+ // CodeView found, seek to actual data
+ int debugBase = dir.PointerToRawData;
+ accessFile.seek(debugBase);
+
+ // sanity check. the first four bytes of the
+ // CodeView
+ // data should be "NB11"
+ String s2 = accessFile.readLine();
+ if (s2.startsWith("NB11")) { //$NON-NLS-1$
+ // Attribute att = peFile.getAttribute();
+ long start = debugBase;
+ long size = accessFile.length() - start;
+
+ String name = CODEVIEW_SECTION_NAME;
+ IExecutableSection exeSection = new ExecutableSection(sectionMapper, name,
+ new SectionInfo(start, size));
+ executableSections.put(name, exeSection);
+ }
+ }
+ fileOffset += DEBUGDIRSZ;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IExecutableSymbolicsReader#getByteOrder()
+ */
+ public ByteOrder getByteOrder() {
+ return isLE ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSymbolicsReader#getSymbolAtAddress()
+ */
+ @Override
+ public ISymbol getSymbolAtAddress(IAddress linkAddress) {
+ int insertion = Collections.binarySearch(symbols, linkAddress);
+ if (insertion >= 0) {
+ return symbols.get(insertion++);
+ }
+
+ if (insertion == -1) {
+ return null;
+ }
+
+ insertion = -insertion - 1;
+
+ if (insertion == symbols.size()) {
+ return null;
+ }
+
+ return symbols.get(insertion - 1);
+ }
+
+ public ISection getSectionByPEID(int peID)
+ {
+ return sectionsByPEID.get(peID);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.io.IOException;
+
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader;
+import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReaderFactory;
+import org.eclipse.cdt.utils.coff.PE;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Factory for creating readers of symbolics in executables.
+ */
+public class PEFileExecutableSymbolicsReaderFactory implements IExecutableSymbolicsReaderFactory {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IExecutableSymbolicsReaderFactory#getConfidence(org.eclipse.core.runtime.IPath)
+ */
+ public int getConfidence(IPath binaryFile) {
+ try {
+ // If this constructor succeeds, it's PE
+ @SuppressWarnings("unused")
+ PE peFile = new PE(binaryFile.toOSString());
+ return IExecutableSymbolicsReaderFactory.NORMAL_CONFIDENCE;
+ } catch (IOException e) {
+ // this class elides actual I/O errors with format errors; ignore
+ }
+ return IExecutableSymbolicsReaderFactory.NO_CONFIDENCE;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IExecutableSymbolicsReaderFactory#createExecutableSymbolicsReader(org.eclipse.core.runtime.IPath)
+ */
+ public IExecutableSymbolicsReader createExecutableSymbolicsReader(
+ IPath binaryFile) {
+
+ try {
+ // If this constructor succeeds, it's PE
+ PE peFile = new PE(binaryFile.toOSString());
+ return new PEFileExecutableSymbolicsReader(binaryFile, peFile);
+ } catch (IOException e) {
+ // this class elides actual I/O errors with format errors; ignore
+ }
+ return null;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+/**
+ * Information about the range of content for a section. Used as a key in {@link SectionMapper}.
+ */
+public class SectionInfo {
+
+ public long fileOffset;
+ public long sectionSize;
+
+ public SectionInfo(long fileOffset, long sectionSize) {
+ this.fileOffset = fileOffset;
+ this.sectionSize = sectionSize;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "section from " + Long.toHexString(fileOffset) + " - " + Long.toHexString(fileOffset + sectionSize); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (fileOffset ^ (fileOffset >>> 32));
+ result = prime * result + (int) (sectionSize ^ (sectionSize >>> 32));
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SectionInfo other = (SectionInfo) obj;
+ if (fileOffset != other.fileOffset)
+ return false;
+ if (sectionSize != other.sectionSize)
+ return false;
+ return true;
+ }
+
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.FileStreamBuffer;
+import org.eclipse.cdt.debug.edc.internal.MemoryStreamBuffer;
+import org.eclipse.cdt.utils.ERandomAccessFile;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Handle mapping sections into memory and caching this content.
+ */
+public class SectionMapper implements ISectionMapper {
+
+ private final IPath hostFile;
+ private final boolean isLE;
+ private ERandomAccessFile efile;
+ /** all sections loaded for any reason */
+ private Map<SectionInfo, IStreamBuffer> loadedSections;
+ /** subset of sections loaded into memory maps */
+ private Map<SectionInfo, IStreamBuffer> mappedBuffers;
+
+ public SectionMapper(IPath hostFile, boolean isLE) {
+ this.hostFile = hostFile;
+ this.isLE = isLE;
+ this.efile = null;
+ this.loadedSections = new HashMap<SectionInfo, IStreamBuffer>();
+ this.mappedBuffers = new HashMap<SectionInfo, IStreamBuffer>();
+ }
+
+ public void dispose() {
+ for (Map.Entry<SectionInfo, IStreamBuffer> entry: loadedSections.entrySet()) {
+ IStreamBuffer buffer = entry.getValue();
+ if (buffer == null)
+ continue;
+ if (mappedBuffers.containsKey(entry.getKey()))
+ FileStatistics.currentMemoryMappedBuffers -= buffer.capacity();
+ else
+ FileStatistics.currentHeapAllocatedBuffers -= buffer.capacity();
+ }
+ loadedSections.clear();
+ mappedBuffers.clear();
+ close();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.ISectionMapper#getMappedFile()
+ */
+ public IPath getMappedFile() {
+ return hostFile;
+ }
+
+ /**
+ *
+ */
+ private void ensureOpen() throws IOException {
+ if (efile == null) {
+ FileStatistics.log("Opening " + hostFile.toFile());
+ FileStatistics.executablesOpened++;
+ FileStatistics.executablesOpen++;
+ efile = new ERandomAccessFile(hostFile.toFile(), "r");
+ }
+ }
+
+
+ private void close() {
+ if (!mappedBuffers.isEmpty())
+ throw new IllegalStateException("cannot close file; mapped buffers open");
+ if (efile != null) {
+ try {
+ FileStatistics.log("Closing " + hostFile.toFile());
+ FileStatistics.executablesOpen--;
+ efile.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ efile = null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.IExecutableSectionMapper#getSectionBuffer(org.eclipse.cdt.debug.edc.internal.symbols.exe.SectionInfo)
+ */
+ public IStreamBuffer getSectionBuffer(SectionInfo section) throws IOException {
+
+ ensureOpen();
+
+ IStreamBuffer buffer = loadedSections.get(section);
+ if (buffer == null) {
+ buffer = loadSection(section);
+
+ loadedSections.put(section, buffer);
+ // TODO: flush data occasionally, before #dispose()
+ }
+
+ return buffer;
+ }
+
+ private IStreamBuffer loadSection(SectionInfo section)
+ throws IOException {
+ FileStatistics.log("Loading " + section + " from " + hostFile.toFile());
+
+ IStreamBuffer buffer = null;
+
+ // If the sym file is too large, it's useless reading it
+ // into the heap and choking the memory.
+ // Just read it on-demand from disk.
+ try {
+ if (section.sectionSize > 4 * 1024 * 1024) {
+ buffer = loadSectionIntoFileStreamBuffer(section);
+ } else {
+ buffer = loadSectionIntoHeap(section);
+ }
+ } catch (IOException e) {
+ buffer = loadSectionIntoHeap(section);
+ }
+
+ return buffer;
+ }
+
+ /**
+ * Load section contents into a streaming buffer. This is a little slower but
+ * does not allocate any more than a page of memory at a time.
+ *
+ * @param section
+ * @param buffer
+ * @return new {@link IStreamBuffer}
+ */
+ private IStreamBuffer loadSectionIntoFileStreamBuffer(SectionInfo section) throws IOException {
+ IStreamBuffer buffer = null;
+ try {
+ buffer = new FileStreamBuffer(efile, isLE ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN,
+ section.fileOffset, section.sectionSize);
+ mappedBuffers.put(section, buffer);
+ FileStatistics.currentMemoryMappedBuffers += buffer.capacity();
+ FileStatistics.totalMemoryMappedBuffers += buffer.capacity();
+ } catch (Throwable e2) {
+ EDCDebugger.getMessageLogger().logError("Failed to make buffer for section " + section, e2);
+ }
+ return buffer;
+ }
+
+ private IStreamBuffer loadSectionIntoHeap(SectionInfo section)
+ throws IOException {
+ IStreamBuffer buffer;
+ // try to load the section into memory because it will
+ // be faster
+ byte[] data = new byte[(int)section.sectionSize];
+ efile.seek(section.fileOffset);
+ efile.read(data);
+ buffer = new MemoryStreamBuffer(data, isLE ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
+ FileStatistics.currentHeapAllocatedBuffers += data.length;
+ FileStatistics.totalHeapAllocatedBuffers += data.length;
+ return buffer;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.exe.ISectionMapper#releaseBuffer(java.nio.ByteBuffer)
+ */
+ public void releaseSectionBuffer(SectionInfo section) {
+ IStreamBuffer buffer = loadedSections.remove(section);
+ if (buffer != null) {
+ if (mappedBuffers.remove(section) != null) {
+ FileStatistics.currentMemoryMappedBuffers -= buffer.capacity();
+ if (mappedBuffers.isEmpty()) {
+ close();
+ }
+ } else {
+ FileStatistics.currentHeapAllocatedBuffers -= buffer.capacity();
+ }
+ }
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.WeakHashMap;
+
+import org.eclipse.cdt.debug.edc.symbols.IUnmangler;
+
+/**
+ * Unmangler for the ARM/Itanium/etc. EABI (http://www.codesourcery.com/public/cxx-abi/abi.html)
+ * <p>
+ * TODO: <expression> <closure-type-name> <lambda-sig>
+ */
+public class UnmanglerEABI implements IUnmangler {
+
+ private static boolean DEBUG = false;
+
+ enum SubstType {
+ PREFIX,
+ TEMPLATE_PREFIX,
+ TYPE,
+ QUAL_TYPE,
+ TEMPLATE_TEMPLATE_PARAM,
+
+ }
+ public UnmanglerEABI() {
+
+ }
+
+ static class UnmangleState {
+ private char[] symbol;
+ private int index;
+ StringBuilder buffer;
+ private Stack<Integer> pushes ; // lengths of buffer when pushed
+ private List<String> substitutions;
+ private Map<Integer, SubstType> substitutionTypes;
+ private int lastTypeNameIndex;
+
+ private List<String> templateArgs;
+ private int templateArgBase;
+ private Stack<Integer> templateArgStack; // length of templateArgs when pushed
+
+ private Stack<Integer> backtracks ; // grouped entries: index value, lengths of buffer, and substitutions length when pushed
+
+ private final boolean nameOnly;
+
+ public UnmangleState(String symbol, boolean nameOnly) {
+ this.symbol = symbol.toCharArray();
+ this.nameOnly = nameOnly;
+ index = 0;
+ buffer = new StringBuilder();
+ pushes = new Stack<Integer>();
+ substitutions = new ArrayList<String>();
+ substitutionTypes = new HashMap<Integer, UnmanglerEABI.SubstType>();
+ templateArgs = new ArrayList<String>();
+ templateArgStack = new Stack<Integer>();
+ backtracks = new Stack<Integer>();
+ lastTypeNameIndex = -1;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ String remaining = getRemaining();
+ if (remaining.length() == 0)
+ remaining = "<<end>>";
+ return "state: at [" + remaining + "], so far: " + current();
+ }
+
+ /**
+ * Push when entering a new decoding context (BNF expression).
+ */
+ public void push() {
+ pushes.push(buffer.length());
+ }
+
+ /**
+ * Pop the current decoded string and restore context to
+ * the calling context.
+ * @return decoded string
+ */
+ public String pop() {
+ int oldpos = pushes.isEmpty() ? 0 : pushes.pop();
+ String str = buffer.substring(oldpos, buffer.length());
+ buffer.setLength(oldpos);
+ return str;
+ }
+
+ /**
+ * Push template argument state
+ */
+ public void pushTemplateArgs() {
+ templateArgStack.push(templateArgBase);
+ templateArgStack.push(templateArgs.size());
+ }
+
+ /**
+ * Pop template argument state
+ * @throws UnmanglingException
+ */
+ public void popTemplateArgs() throws UnmanglingException {
+ try {
+ templateArgs.subList(templateArgStack.pop(), templateArgs.size()).clear();
+ templateArgBase = templateArgStack.pop();
+ } catch (Exception e) {
+ throw new UnmanglingException("template stack empty", buffer.toString());
+ }
+ }
+ /**
+ * Push all state, when entering a possible backtrack scenario.
+ * Use #safePop() if an operation succeeds, or #safeBacktrack()
+ * if it failed and you want to retry.
+ */
+ public void safePush() {
+ backtracks.push(index);
+ backtracks.push(lastTypeNameIndex);
+ backtracks.push(buffer.length());
+ backtracks.push(substitutions.size());
+ backtracks.push(pushes.size());
+ }
+
+ /**
+ * Call when a #safePush() branch has succeeded to discard backtrack state.
+ */
+ public void safePop() {
+ backtracks.pop();
+ backtracks.pop();
+ backtracks.pop();
+ backtracks.pop();
+ backtracks.pop();
+ }
+
+ /**
+ * Call when a #safePush() branch has failed to reset backtrack state.
+ * (To perform another backtrack, call #safePush() again)
+ */
+ public void safeBacktrack() {
+ int oldSize = backtracks.pop();
+ pushes.subList(oldSize, pushes.size()).clear();
+ oldSize = backtracks.pop();
+ substitutions.subList(oldSize, substitutions.size()).clear();
+ while (substitutionTypes.size() > oldSize)
+ substitutionTypes.remove(substitutionTypes.size() - 1);
+ buffer.setLength(backtracks.pop());
+ lastTypeNameIndex = backtracks.pop();
+ index = backtracks.pop();
+ }
+
+ /**
+ * Tell if there is any current string (length > 0)
+ * @return
+ */
+ public boolean hasCurrent() {
+ int oldpos = pushes.isEmpty() ? 0 : pushes.peek();
+ int end = buffer.length();
+ return end > oldpos;
+ }
+
+ /**
+ * Get the current constructed string (since the last #push())
+ * @return
+ */
+ public String current() {
+ int oldpos = pushes.isEmpty() ? 0 : pushes.peek();
+ String str = buffer.substring(oldpos, buffer.length());
+ return str;
+ }
+
+ /**
+ * Remember the current constructed string as a substitution.
+ * @param substType
+ */
+ public void remember(SubstType substType) {
+ remember(current(), substType);
+ }
+
+ public boolean lastSubstitutionIsPrefix(SubstType substType) {
+ if (substitutions.size() == 0)
+ return false;
+ String current = current();
+ if (substitutions.get(substitutions.size() - 1).length() >= current.length())
+ return false;
+ return lastSubstitution() == substType;
+ }
+ /**
+ * Remember the given string as a substitution.
+ * @param name
+ * @param substType
+ */
+ public void remember(String name, SubstType substType) {
+ if (name.length() == 0)
+ return;
+ int num = substitutions.size();
+ if (num > 0 && substitutions.get(num - 1).equals(name))
+ return;
+ substitutions.add(name);
+ substitutionTypes.put(num, substType);
+ lastTypeNameIndex = num;
+ if (DEBUG) System.out.println(num+" := " + name + " --> " + substType);
+ }
+
+ /**
+ * Replace the last substitution.
+ * @param name
+ * @param substType
+ */
+ public void rememberInstead(String name, SubstType substType) {
+ int num = substitutions.size() - 1;
+ substitutions.set(num, name);
+ substitutionTypes.put(num, substType);
+ if (DEBUG) System.out.println(num+" ::= " + name + " -- > " + substType);
+ }
+
+ /**
+ * Pop the current decoded string as in {@link #pop()}
+ * and remember the string as a substitution.
+ * @return String
+ */
+ public String popAndRemember(SubstType substType) {
+ String name = pop();
+ remember(name, substType);
+ return name;
+ }
+
+ public char peek() {
+ return index < symbol.length ? symbol[index] : 0;
+ }
+
+ public char peek(int offset) {
+ return index + offset < symbol.length ? symbol[index + offset] : 0;
+ }
+
+ public void consume(char ch) throws UnmanglingException {
+ if (ch != get())
+ throw unexpected();
+ }
+ public char get() {
+ return index < symbol.length ? symbol[index++] : 0;
+ }
+
+ public void unget() {
+ if (index > 0) index--;
+ }
+ public void skip() {
+ if (index < symbol.length)
+ index++;
+ }
+
+ public void skip2() {
+ index = Math.min(index + 2, symbol.length);
+ }
+
+ public boolean done() {
+ return index >= symbol.length;
+ }
+
+ public UnmanglingException unexpected() {
+ return new UnmanglingException("Unexpected text at " + getRemaining(), buffer.toString());
+ }
+ public UnmanglingException unexpected(String what) {
+ return new UnmanglingException("Wanted " + what + " but got unexpected text at " + getRemaining(), buffer.toString());
+ }
+ public UnmanglingException notImplemented() {
+ return new UnmanglingException("Unimplemented at " + getRemaining(),
+ buffer.toString());
+ }
+
+ /**
+ * @return
+ */
+ private String getRemaining() {
+ if (index >= symbol.length)
+ return "";
+ return new String(symbol, index, symbol.length - index);
+ }
+
+ /**
+ * @throws UnmanglingException
+ *
+ */
+ public void throwIfDone() throws UnmanglingException {
+ if (done())
+ throw new UnmanglingException("Unexpected end of symbol",
+ buffer.toString());
+ }
+
+ public void updateSubstitution(SubstType substType) {
+ int num = substitutions.size() - 1;
+ substitutionTypes.put(num, substType);
+ if (DEBUG) System.out.println(num + " ::= " + substType);
+ }
+
+ /**
+ * @return
+ */
+ public SubstType lastSubstitution() {
+ return substitutionTypes.get(substitutions.size() - 1);
+ }
+
+ /**
+ * @param arg
+ */
+ public void rememberTemplateArg(String arg) {
+ templateArgs.add(arg);
+ }
+
+ /**
+ * @param num
+ * @return
+ * @throws UnmanglingException
+ */
+ public String getTemplateArg(int num) throws UnmanglingException {
+ num -= templateArgBase;
+ if (num < 0 || num >= templateArgs.size())
+ throw unexpected("template argument in range 0-" + (templateArgs.size() - templateArgBase)+"; got " + num);
+ return templateArgs.get(num);
+ }
+
+ public String lastSubstitutedName() {
+ if (lastTypeNameIndex < 0)
+ return "";
+ return substitutions.get(lastTypeNameIndex);
+ }
+ }
+
+ private static WeakHashMap<String, String> unmangledMap = new WeakHashMap<String, String>();
+ private static WeakHashMap<String, String> withoutArgsMap = new WeakHashMap<String, String>();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IUnmangler#undecorate(java.lang.String)
+ */
+ public String undecorate(String symbol) {
+ // symbols may have @@GLIBC... type suffixes
+ int atat = symbol.indexOf("@@");
+ if (atat > 0)
+ symbol = symbol.substring(0, atat);
+ return symbol;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IUnmangler#isMangled(java.lang.String)
+ */
+ public boolean isMangled(String symbol) {
+ if (symbol == null)
+ return false;
+ if (symbol.startsWith("_Z"))
+ return true;
+ // this is used for enum constants
+ if (symbol.startsWith("__N"))
+ return true;
+ return false;
+ }
+
+ public String unmangleWithoutArgs(String symbol) throws UnmanglingException {
+ return unmangle(symbol, true);
+ }
+
+ public String unmangle(String symbol) throws UnmanglingException {
+ return unmangle(symbol, false);
+ }
+
+ public String unmangleType(String symbol) throws UnmanglingException {
+ if (symbol == null)
+ return null;
+
+ if (unmangledMap.containsKey(symbol))
+ return unmangledMap.get(symbol);
+
+ if (symbol.startsWith("_Z")) {
+ UnmangleState state = new UnmangleState(symbol, false);
+ state.skip2();
+ String unmangled = "";
+ if (state.peek() == 'S') {
+ unmangled += unmangleSubstitution(state);
+ }
+ while (!state.done()) {
+ if (state.peek() == 'I') {
+ // unscoped-template-name
+ state.remember(unmangled, SubstType.TEMPLATE_PREFIX);
+ String args = unmangleTemplateArgs(state, false);
+ state.buffer.append(args);
+ unmangled += args;
+ } else {
+ if (unmangled.equals("::std"))
+ unmangled += "::";
+ unmangled += unmangleType(state);
+ }
+ state.remember(unmangled, SubstType.TYPE);
+ }
+ unmangledMap.put(symbol, unmangled);
+ return unmangled;
+ }
+ return symbol;
+ }
+
+ public String unmangle(String symbol, boolean skipArgs) throws UnmanglingException {
+ if (symbol == null)
+ return null;
+
+ String unmangled;
+
+ if (skipArgs) {
+ if (withoutArgsMap.containsKey(symbol))
+ unmangled = withoutArgsMap.get(symbol);
+ else {
+ unmangled = doUnmangle(symbol, true);
+ withoutArgsMap.put(symbol, unmangled);
+ }
+ } else if (unmangledMap.containsKey(symbol)) {
+ unmangled = unmangledMap.get(symbol);
+ } else {
+ unmangled = doUnmangle(symbol, skipArgs);
+ unmangledMap.put(symbol, unmangled);
+
+ do {// for break below if conditionals succeed
+ int paren = unmangled.indexOf('(');
+ if (0 < paren) {
+ String unmangledWithoutArgs = unmangled.substring(0, paren-1);
+ if (unmangledWithoutArgs != null && unmangledWithoutArgs.length() != 0) {
+ withoutArgsMap.put(symbol, unmangledWithoutArgs);
+ break;
+ } }
+ withoutArgsMap.put(symbol, unmangled);
+ } while (false);// allows break above to skip default case
+ }
+
+ return unmangled;
+ }
+
+ /**
+ * @param symbol
+ * @return
+ * @throws UnmanglingException
+ */
+ private String doUnmangle(String symbol, boolean nameOnly) throws UnmanglingException {
+ /*
+ Entities with C linkage and global namespace variables are not mangled. Mangled names have the general structure:
+
+
+ <mangled-name> ::= _Z <encoding>
+ <encoding> ::= <function name> <bare-function-type>
+ ::= <data name>
+ ::= <special-name>
+ */
+ if (symbol.startsWith("_Z")) {
+ String suffix = "";
+ int idx = symbol.indexOf('@');
+ if (idx >= 0) {
+ suffix = symbol.substring(idx);
+ symbol = symbol.substring(0, idx);
+ }
+
+ UnmangleState state = new UnmangleState(symbol, nameOnly);
+ state.skip2();
+
+ String unmangled = unmangleEncoding(state);
+ unmangled += suffix;
+ return unmangled;
+ } else if (symbol.startsWith("__N")) {
+ UnmangleState state = new UnmangleState(symbol, true);
+ state.skip2();
+
+ String unmangled = unmangleName(state);
+ return unmangled;
+ } else {
+ return symbol;
+ }
+ }
+
+ /*
+ <encoding> ::= <function name> <bare-function-type>
+ ::= <data name>
+ ::= <special-name>
+ */
+ private String unmangleEncoding(UnmangleState state) throws UnmanglingException {
+ state.push();
+
+ String name;
+
+ // ferret out <special-name>
+ char ch = state.peek();
+ if (ch == 'T' || ch == 'G') {
+ name = unmangleSpecialName(state);
+ } else {
+ name = unmangleName(state);
+ }
+
+ if (!state.done() && !state.nameOnly) {
+ boolean isTemplate = name.endsWith(">"); // HACK
+ if (isTemplate) {
+ state.buffer.append(unmangleType(state));
+ state.buffer.append(' ');
+ }
+ state.buffer.append(name);
+ state.buffer.append(unmangleBareFunctionType(state, false));
+ } else {
+ state.buffer.append(name);
+ }
+
+ return state.pop();
+ }
+
+ private void unmangleSpecialNameCallOffset(UnmangleState state, char ch)
+ throws UnmanglingException {
+
+ switch (ch) {
+ case 'h': {
+ // h <nv-offset> _
+ int offset = doUnmangleNumber(state);
+ state.consume('_');
+ state.buffer.append("<non-virtual base override at offset ");
+ appendHexNumber(state.buffer, offset);
+ break;
+ }
+ case 'v': {
+ // v <offset number> _ <virtual offset number> _
+ int offset = doUnmangleNumber(state);
+ state.consume('_');
+ int voffset = doUnmangleNumber(state);
+ state.consume('_');
+ state.buffer.append("<virtual base override at offset ");
+ appendHexNumber(state.buffer, offset);
+ state.buffer.append(", vcall offset ");
+ appendHexNumber(state.buffer, voffset);
+ break;
+ }
+ default:
+ throw state.unexpected("special name call-offset");
+ }
+ }
+
+ /*
+ <special-name> ::= TV <type> # virtual table
+ ::= TT <type> # VTT structure (construction vtable index)
+ ::= TI <type> # typeinfo structure
+ ::= TS <type> # typeinfo name (null-terminated byte string)
+ <special-name> ::= GV <object name> # Guard variable for one-time initialization
+ # No <type>
+ <special-name> ::= T <call-offset> <base encoding>
+ # base is the nominal target function of thunk
+ <call-offset> ::= h <nv-offset> _
+ ::= v <v-offset> _
+ <nv-offset> ::= <offset number>
+ # non-virtual base override
+ <v-offset> ::= <offset number> _ <virtual offset number>
+ # virtual base override, with vcall offset
+
+ <special-name> ::= Tc <call-offset> <call-offset> <base encoding>
+ # base is the nominal target function of thunk
+ # first call-offset is 'this' adjustment
+ # second call-offset is result adjustment
+
+ */
+ private String unmangleSpecialName(UnmangleState state) throws UnmanglingException {
+ state.push();
+
+ char ch = state.get();
+ if (ch == 'T') {
+ String type = null;
+ ch = state.get();
+ switch (ch) {
+ case 'V':
+ type = unmangleType(state);
+ state.buffer.append("<virtual table for ");
+ state.buffer.append(type);
+ state.buffer.append('>');
+ break;
+ case 'T':
+ type = unmangleType(state);
+ state.buffer.append("<VTT structure for ");
+ state.buffer.append(type);
+ state.buffer.append('>');
+ break;
+ case 'I':
+ type = unmangleType(state);
+ state.buffer.append("<typeinfo structure for ");
+ state.buffer.append(type);
+ state.buffer.append('>');
+ break;
+ case 'S':
+ type = unmangleType(state);
+ state.buffer.append("<typeinfo name for ");
+ state.buffer.append(type);
+ state.buffer.append('>');
+ break;
+ case 'h':
+ case 'v':
+ unmangleSpecialNameCallOffset(state, ch);
+ state.buffer.append(" for ");
+ state.buffer.append(unmangleEncoding(state));
+ state.buffer.append('>');
+ break;
+ case 'c': {
+ // c <call-offset> <call-offset> <base encoding>
+ state.buffer.append("<covariant : 'this' adjustment ");
+ unmangleSpecialNameCallOffset(state, state.get());
+ state.buffer.append("> result adjustment ");
+ unmangleSpecialNameCallOffset(state, state.get());
+ state.buffer.append("> for ");
+ state.buffer.append(unmangleEncoding(state));
+ state.buffer.append('>');
+ break;
+ }
+ default:
+ throw state.unexpected("special name");
+ }
+ } else if (ch == 'G') {
+ switch (state.get()) {
+ case 'V':
+ state.buffer.append("<one-time-init guard for ");
+ state.buffer.append(unmangleName(state));
+ state.buffer.append('>');
+ break;
+ default:
+ throw state.unexpected("special name");
+ }
+ }
+
+ return state.pop();
+ }
+
+ private void appendHexNumber(StringBuilder builder, int offset) {
+ if (offset < 0) {
+ builder.append("-0x");
+ builder.append(Integer.toHexString(-offset));
+ } else {
+ builder.append("0x");
+ builder.append(Integer.toHexString(offset));
+ }
+ }
+
+ /**
+ * @param state
+ * @param name
+ * @return
+ * @throws UnmanglingException
+ */
+ private String doUnmangleFunctionWithName(UnmangleState state, boolean expectReturn, String name) throws UnmanglingException {
+ state.push();
+
+ state.consume('F');
+
+ if (expectReturn) {
+ state.buffer.append(unmangleType(state));
+ state.buffer.append(' ');
+ }
+
+ if (name != null)
+ state.buffer.append(name);
+
+ state.buffer.append(unmangleBareFunctionType(state, false));
+
+ state.consume('E');
+
+ return state.pop();
+ }
+
+ /**
+ * @param state
+ * @param expectReturn true if a return type precedes argument list
+ * @throws UnmanglingException
+ */
+ private String unmangleBareFunctionType(UnmangleState state, boolean expectReturn) throws UnmanglingException {
+ state.push();
+ if (expectReturn) {
+ state.buffer.append(unmangleType(state));
+ state.buffer.append(' ');
+ }
+ state.buffer.append('(');
+ if (state.peek() == 'v') {
+ state.skip();
+ } else {
+ boolean first = true;
+ while (!state.done() && state.peek() != 'E') {
+ if (first) {
+ first = false;
+ } else {
+ state.buffer.append(',');
+ }
+ state.buffer.append(unmangleType(state));
+ }
+ }
+ state.buffer.append(')');
+ return state.pop();
+ }
+
+ /*
+ <name> ::= <nested-name> = N ...
+ ::= <unscoped-name> = number or St ...
+ ::= <unscoped-template-name> <template-args> = unscoped | S ... | I ...
+ ::= <local-name> # See Scope Encoding below = Z ...
+
+ */
+ private String unmangleName(UnmangleState state) throws UnmanglingException {
+ state.push();
+ char ch = state.peek();
+ if (ch == 'N') {
+ state.buffer.append(unmangleNestedName(state, true));
+ } else if (ch == 'Z') {
+ state.buffer.append(unmangleLocalName(state));
+ } else if (ch == 0) {
+ state.throwIfDone();
+ } else {
+ // must be unscoped-name or unscoped-template-name
+
+ if (ch == 'S' && state.peek(1) == 't') {
+ state.skip2();
+ state.buffer.append("::std::");
+ }
+ String name = unmangleUnqualifiedName(state);
+ state.buffer.append(name);
+ if (state.peek() == 'I') {
+ // unscoped-template-name
+ state.remember(name, SubstType.TEMPLATE_PREFIX);
+ String args = unmangleTemplateArgs(state, false);
+ state.buffer.append(args);
+ state.remember(name + args, SubstType.TYPE);
+ }
+ }
+ return state.pop();
+ }
+
+ /*
+ <local-name> := Z <function encoding> E <entity name> [<discriminator>]
+ := Z <function encoding> E s [<discriminator>]
+
+ <discriminator> := _ <non-negative number> # when number < 10
+ := __ <non-negative number> _ # when number >= 10
+
+ */
+ private String unmangleLocalName(UnmangleState state) throws UnmanglingException {
+ state.push();
+ state.consume('Z');
+ state.buffer.append(unmangleEncoding(state));
+ state.consume('E');
+
+ boolean isStringLiteral = false;
+ if (state.peek() == 's') {
+ isStringLiteral = true;
+ state.skip();
+ if (state.peek() == '_')
+ state.buffer.append("::");
+ } else {
+ addNameWithColons(state, unmangleName(state));
+ }
+ if (state.peek() == '_') {
+ state.skip();
+ int num;
+ if (state.peek() == '_') {
+ // >= 10
+ num = doUnmangleNonNegativeNumber(state);
+ state.consume('_');
+ } else {
+ char ch = state.get();
+ if (ch >= '0' && ch <= '9') {
+ num = ch - '0';
+ } else {
+ throw state.unexpected("number");
+ }
+ }
+ if (isStringLiteral)
+ state.buffer.append("string literal");
+ state.buffer.append("#" + num);
+ }
+ return state.pop();
+ }
+
+ /*
+ <source-name> ::= <positive length number> <identifier>
+ <number> ::= [n] <non-negative decimal integer>
+ <identifier> ::= <unqualified source code identifier>
+ */
+ private String unmangleSourceName(UnmangleState state) throws UnmanglingException {
+ state.push();
+ char ch = state.peek();
+ if (ch >= '0' && ch <= '9') {
+ int length = doUnmangleNumber(state);
+ while (length-- > 0) {
+ state.throwIfDone();
+ state.buffer.append(state.get());
+ }
+ return state.pop();
+ } else {
+ throw state.unexpected();
+ }
+ }
+
+ /*
+ * [0-9]+
+ */
+ private int doUnmangleNonNegativeNumber(UnmangleState state) {
+ int number = 0;
+ char ch;
+ while ((ch = state.get()) != 0 && ch >= '0' && ch <= '9') {
+ number = number * 10 + (ch - '0');
+ }
+ state.unget();
+ return number;
+ }
+
+ /*
+ * [n] <non-negative decimal number>
+ */
+ private int doUnmangleNumber(UnmangleState state) {
+ boolean neg = false;
+ if (state.peek() == 'n') {
+ state.skip();
+ neg = true;
+ }
+ int number = doUnmangleNonNegativeNumber(state);
+ return neg ? -number : number;
+ }
+
+ /*
+ <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E
+ ::= N [<CV-qualifiers>] <template-prefix> <template-args> E (args = I...)
+
+ */
+ private String unmangleNestedName(UnmangleState state, boolean allowCV)
+ throws UnmanglingException {
+
+ state.push();
+
+ state.consume('N');
+
+ String cvquals = allowCV ? unmangleCVQualifiers(state) : null;
+
+ state.buffer.append(unmanglePrefix(state, SubstType.PREFIX));
+
+ state.consume('E');
+
+ if (allowCV && (cvquals != null && cvquals.length() > 0)) {
+ state.buffer.append(' ');
+ state.buffer.append(cvquals);
+ }
+ return state.pop();
+ }
+
+
+ /*
+ <template-args> ::= I <template-arg>+ E
+
+ */
+ private String unmangleTemplateArgs(UnmangleState state, boolean substArg) throws UnmanglingException {
+ state.push();
+
+ int origTypeIndex = state.lastTypeNameIndex;
+
+ String typeName = state.lastSubstitutedName();
+
+ if (!substArg || state.peek() == 'I') {
+ state.consume('I');
+ substArg = false;
+ }
+ state.buffer.append('<');
+ boolean lastArgWasSubst = false;
+ if (state.peek() != 'E') {
+ boolean first = true;
+ do {
+ if (first)
+ first = false;
+ else
+ state.buffer.append(',');
+ boolean unfinishedTemplateSubst = false;
+ if (state.peek() == 'S') {
+ char ch2 = state.peek(1);
+ if (ch2 == 't') {
+ state.buffer.append("::std::");
+ state.skip2();
+ first = true;
+ continue; // more of this arg to come
+ }
+ lastArgWasSubst = true;
+ if (ch2 == 'a' || ch2 == 'b') {
+ unfinishedTemplateSubst = true;
+ }
+ }
+ String arg = unmangleTemplateArg(state);
+ if (unfinishedTemplateSubst)
+ arg += unmangleTemplateArgs(state, false);
+ state.buffer.append(arg);
+ if (lastArgWasSubst && state.done())
+ break;
+ lastArgWasSubst = false;
+ } while (state.peek() != 'E');
+ }
+
+ if (!substArg && !lastArgWasSubst)
+ state.consume('E');
+
+ if (state.buffer.lastIndexOf(">") == state.buffer.length() - 1)
+ state.buffer.append(' ');
+ state.buffer.append('>');
+
+ if (state.lastSubstitution() == SubstType.TEMPLATE_TEMPLATE_PARAM)
+ state.rememberInstead(typeName + state.current(), SubstType.TEMPLATE_TEMPLATE_PARAM);
+ else if (state.lastTypeNameIndex > origTypeIndex)
+ state.remember(typeName + state.current(), SubstType.TYPE);
+ state.lastTypeNameIndex = origTypeIndex;
+
+ return state.pop();
+ }
+
+ /*
+ <template-arg> ::= <type> # type or template
+ ::= X <expression> E # expression
+ ::= <expr-primary> # simple expressions ('L')
+ ::= I <template-arg>* E # argument pack
+ ::= sp <expression> # pack expansion of (C++0x)
+
+ */
+ private String unmangleTemplateArg(UnmangleState state) throws UnmanglingException {
+ state.push();
+
+ String arg = null;
+ char ch = state.peek();
+ if (ch == 'X') {
+ throw state.notImplemented();
+ } else if (ch == 'I') {
+ arg = unmangleTemplateArgs(state, false);
+ } else if (ch == 's' && state.peek(1) == 'p') {
+ throw state.notImplemented();
+ } else if (ch == 'L') {
+ arg = unmangleExprPrimary(state);
+ } else {
+ arg = unmangleType(state);
+ }
+ state.rememberTemplateArg(arg);
+ state.buffer.append(arg);
+
+ return state.pop();
+ }
+
+
+ /**
+<expr-primary> ::= L <type> <value number> E # integer literal
+ ::= L <type> <value float> E # floating literal
+ ::= L <string type> E # string literal
+ ::= L <nullptr type> E # nullptr literal (i.e., "LDnE")
+ ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000)
+ ::= L <mangled-name> E # external name
+
+ * @param state
+ * @return
+ */
+ private String unmangleExprPrimary(UnmangleState state) throws UnmanglingException {
+ state.push();
+ state.consume('L');
+
+ try {
+ state.safePush();
+
+ String type = null;
+ String suffix = null;
+ switch (state.peek()) {
+ case 'i': // int
+ suffix = "";
+ break;
+ case 'j': // unsigned int
+ suffix = "U";
+ break;
+ case 'l': // long
+ suffix = "L";
+ break;
+ case 'm': // unsigned long
+ suffix = "UL";
+ break;
+ case 'x': // long long
+ suffix = "LL";
+ break;
+ case 'y': // unsigned long long
+ suffix = "ULL";
+ break;
+ }
+ if (suffix != null) {
+ state.skip();
+ state.buffer.append(doUnmangleNumber(state));
+ state.buffer.append(suffix);
+ } else {
+ // show other types
+ type = unmangleType(state);
+ state.buffer.append('(');
+ state.buffer.append(type);
+ state.buffer.append(')');
+ state.buffer.append(doUnmangleNumber(state));
+ }
+ state.safePop();
+ } catch (UnmanglingException e) {
+ state.safeBacktrack();
+
+ // must be mangled-name or something else
+ state.buffer.append(unmangleName(state));
+ }
+ state.consume('E');
+
+ return state.popAndRemember(SubstType.TEMPLATE_TEMPLATE_PARAM);
+
+ }
+ /*
+ <template-param> ::= T_ # first template parameter
+ ::= T <parameter-2 non-negative number> _
+
+ */
+ private String unmangleTemplateParam(UnmangleState state) throws UnmanglingException {
+ state.push();
+
+ state.consume('T');
+ int num = doUnmangleBase10(state);
+ state.buffer.append(state.getTemplateArg(num));
+
+ return state.popAndRemember(SubstType.TEMPLATE_TEMPLATE_PARAM);
+ }
+
+ /**
+ * Base-10, where _ = 0 and 1..x = 0..x-1
+ * @param state
+ * @return
+ * @throws UnmanglingException
+ */
+ private int doUnmangleBase10(UnmangleState state) throws UnmanglingException {
+ char ch;
+ if (state.peek() == '_') {
+ state.skip();
+ return 0;
+ }
+ int num = 0;
+ while ((ch = state.get()) != '_') {
+ state.throwIfDone();
+ num = (num * 10) + (ch - '0');
+ }
+ return num + 1;
+ }
+
+ /*
+ <substitution> ::= S <seq-id> _
+ ::= S_
+
+ <substitution> ::= St # ::std::
+ <substitution> ::= Sa # ::std::allocator
+ <substitution> ::= Sb # ::std::basic_string
+ <substitution> ::= Ss # ::std::basic_string < char,
+ ::std::char_traits<char>,
+ ::std::allocator<char> >
+ <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> >
+ <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> >
+ <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
+
+ */
+ private String unmangleSubstitution(UnmangleState state) throws UnmanglingException {
+ state.push();
+ state.consume('S');
+
+ char ch = state.peek();
+ if (ch == '_' || (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z')) {
+ int num = doUnmangleBase36(state);
+ if (num < 0 || num >= state.substitutions.size())
+ throw state.unexpected("substitution id in the range 0-"+ state.substitutions.size() + " but got " + num);
+ String val = state.substitutions.get(num);
+
+ SubstType type = state.substitutionTypes.get(num);
+ switch (type) {
+ case PREFIX:
+ //...?
+ state.buffer.append(val);
+ break;
+ case TEMPLATE_PREFIX:
+ state.buffer.append(val);
+ state.buffer.append(unmangleTemplateArgs(state, true));
+ break;
+ case TEMPLATE_TEMPLATE_PARAM:
+ state.buffer.append(val);
+ break;
+ case QUAL_TYPE:
+ case TYPE:
+ // ...?
+ state.buffer.append(val);
+ break;
+ }
+ } else {
+ switch (ch) {
+ case 't':
+ state.buffer.append("::std"); break;
+ case 'a':
+ state.buffer.append("::std::allocator"); break;
+ case 'b':
+ state.buffer.append("::std::basic_string"); break;
+ case 's':
+ state.buffer.append("::std::basic_string<char,::std::char_traits<char>,::std::allocator<char> >"); break;
+ case 'i':
+ state.buffer.append("::std::basic_istream<char,::std::char_traits<char> >"); break;
+ case 'o':
+ state.buffer.append("::std::basic_ostream<char,::std::char_traits<char> >"); break;
+ case 'd':
+ state.buffer.append("::std::basic_iostream<char,::std::char_traits<char> >"); break;
+ default:
+ throw state.unexpected("std:: substitution");
+ }
+ state.skip();
+ }
+
+ return state.pop();
+ }
+
+ /**
+ * As a special case, the first substitutable entity is encoded as "S_",
+ * i.e. with no number, so the numbered entities are the second one as
+ * "S0_", the third as "S1_", the twelfth as "SA_", the thirty-eighth as
+ * "S10_", etc.
+ * @throws UnmanglingException
+ */
+ private int doUnmangleBase36(UnmangleState state) throws UnmanglingException {
+ int num = 0;
+ char ch = state.peek();
+ if (ch == '_') {
+ state.skip();
+ return 0;
+ }
+ while ((ch = state.get()) != '_') {
+ state.throwIfDone();
+ num = (num * 10);
+ if (ch >= '0' && ch <= '9')
+ num += (ch - '0');
+ else if (ch >= 'A' && ch <= 'Z')
+ num += (ch - 'A') + 10;
+ else
+ throw state.unexpected("BASE-36 number");
+ }
+ return num + 1;
+ }
+
+ /*
+ <prefix> ::= <prefix> <unqualified-name> # ... 0-9
+ ::= <template-prefix> <template-args> --> template=T... args=I...
+ ::= <template-param> --> T...
+ ::= # empty
+ ::= <substitution> --> S...
+ ::= <prefix> <data-member-prefix> --> name M
+
+ left-recursion elimination:
+ <prefix> ::= <template-prefix> <template-args> <prefix'>
+ ::= <template-param> <prefix'>
+ ::= <substitution> <prefix'>
+ ::= # empty
+ <prefix'> ::= <unqualified-name> <prefix'>
+ ::= <data-member-prefix> M <prefix'>
+ ::= #empty
+ */
+ private String unmanglePrefix(UnmangleState state, SubstType substType) throws UnmanglingException {
+ state.push();
+
+ boolean any = false;
+ boolean lastSubst = false;
+
+ while (true) {
+ char ch = state.peek();
+
+ if (ch == 'E') {
+ break;
+ }
+
+ String part = null;
+
+ if (ch == 'T') {
+ part = unmangleTemplateParam(state);
+ state.remember(substType);
+ }
+ else if (ch == 'S') {
+ part = unmangleSubstitution(state);
+ lastSubst = true;
+ }
+ else if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')
+ || (ch == 'C' || ch == 'D' || ch == 'L')) {
+ part = unmangleUnqualifiedName(state);
+ }
+ else if (ch == 'I') {
+ if (!any)
+ throw state.unexpected();
+
+ if (state.hasCurrent()) {
+ state.updateSubstitution(SubstType.TEMPLATE_PREFIX);
+ part = state.current();
+ }
+ String args = unmangleTemplateArgs(state, false);
+ state.buffer.append(args);
+ continue;
+ }
+ else {
+ throw state.unexpected();
+ }
+
+ lastSubst = false;
+ any = true;
+
+ if (lastSubst)
+ any = true;
+ if (state.hasCurrent()) {
+ addNameWithColons(state, part);
+ } else {
+ state.buffer.append(part);
+ }
+
+ if (ch != 'S' && state.peek() != 'E') {
+ state.remember(substType);
+ }
+ }
+
+
+ return state.pop();
+ }
+
+ /**
+ * @param state
+ * @param name
+ */
+ private void addNameWithColons(UnmangleState state, String name) {
+ if (state.hasCurrent() && !name.startsWith("::"))
+ state.buffer.append("::");
+ state.buffer.append(name);
+ }
+
+ /*
+ <template-prefix> ::= <prefix> <template unqualified-name> ... 0-9
+ ::= <template-param> # T*
+ ::= <substitution> # S*
+ --> followed by <template-args> (I)
+
+ left-recursion elimination:
+ <template-prefix> ::= <template-param> <template-prefix'> # T*
+ ::= <substitution> <template-prefix'> # S*
+ <template-prefix'> ::= <template unqualified-name> <template-prefix'> ... 0-9
+ ::= #empty
+ --> followed by <template-args> (I)
+ */
+ String unmangleTemplatePrefix(UnmangleState state) throws UnmanglingException {
+ state.push();
+
+ char ch = state.peek();
+ if (ch == 'S') {
+ state.buffer.append(unmangleSubstitution(state));
+ state.buffer.append(unmangleTemplatePrefixPrime(state));
+ return state.pop();
+ }
+
+ if (ch == 'T') {
+ state.buffer.append(unmangleTemplateParam(state));
+ state.buffer.append(unmangleTemplatePrefixPrime(state));
+ }
+
+ return state.popAndRemember(SubstType.TEMPLATE_PREFIX);
+ }
+
+ private String unmangleTemplatePrefixPrime(UnmangleState state) throws UnmanglingException {
+ state.push();
+ while (true) {
+ try {
+ state.buffer.append(unmangleUnqualifiedName(state));
+ if (state.peek() == 'I') {
+ // unscoped-template-name
+ state.buffer.append(unmangleTemplateArgs(state, false));
+ }
+ } catch (UnmanglingException e) {
+ break;
+ }
+ }
+
+ return state.pop();
+ }
+
+ /*
+<type> ::= <builtin-type> = rVKPROCGU ...
+ ::= <function-type> =
+ ::= <class-enum-type>
+ ::= <array-type>
+ ::= <pointer-to-member-type> = M...
+ ::= <template-param>
+ ::= <template-template-param> <template-args>
+ ::= <substitution> # See Compression below
+
+ <type> ::= <CV-qualifiers> <type>
+ ::= P <type> # pointer-to
+ ::= R <type> # reference-to
+ ::= O <type> # rvalue reference-to (C++0x)
+ ::= C <type> # complex pair (C 2000)
+ ::= G <type> # imaginary (C 2000)
+ ::= U <source-name> <type> # vendor extended type qualifier
+
+ <CV-qualifiers> ::= [r] [V] [K] # restrict (C99), volatile, const
+ */
+ private String unmangleType(UnmangleState state) throws UnmanglingException {
+ state.push();
+ char ch = state.get();
+ switch (ch) {
+ //
+ // qualified types
+ //
+ case 'r':
+ case 'V':
+ case 'K':
+ state.unget();
+ String cvquals = unmangleCVQualifiers(state);
+ state.buffer.append(unmangleType(state));
+ if (cvquals.length() > 0) {
+ state.buffer.append(' ');
+ state.buffer.append(cvquals);
+ }
+ if (state.lastSubstitutionIsPrefix(SubstType.QUAL_TYPE))
+ state.remember(SubstType.QUAL_TYPE);
+ return state.popAndRemember(SubstType.QUAL_TYPE);
+ case 'P':
+ state.buffer.append(unmangleType(state));
+ if (state.lastSubstitutionIsPrefix(SubstType.QUAL_TYPE))
+ state.remember(SubstType.QUAL_TYPE);
+ ptrOrRefize(state.buffer, "*");
+ return state.popAndRemember(SubstType.QUAL_TYPE);
+ case 'R':
+ state.buffer.append(unmangleType(state));
+ if (state.lastSubstitutionIsPrefix(SubstType.QUAL_TYPE))
+ state.remember(SubstType.QUAL_TYPE);
+ ptrOrRefize(state.buffer, "&");
+ return state.popAndRemember(SubstType.QUAL_TYPE);
+ case 'O': // rvalue reference-to
+ case 'C': // complex pair
+ case 'G': // imaginary
+ throw state.notImplemented();
+ case 'U': // vendor extension
+ {
+ // TODO: assuming the extension precedes the type,
+ // e.g. int __declspec(dllimport) foo();
+ state.buffer.append(unmangleSourceName(state));
+ state.buffer.append(' ');
+ state.buffer.append(unmangleType(state));
+ return state.popAndRemember(SubstType.TYPE);
+ }
+
+ //
+ // built-in types
+ //
+ case 'v':
+ state.buffer.append("void"); break;
+ case 'w':
+ state.buffer.append("wchar_t"); break;
+ case 'b':
+ state.buffer.append("bool"); break;
+ case 'c':
+ state.buffer.append("char"); break;
+ case 'a':
+ state.buffer.append("signed char"); break;
+ case 'h':
+ state.buffer.append("unsigned char"); break;
+ case 's':
+ state.buffer.append("short"); break;
+ case 't':
+ state.buffer.append("unsigned short"); break;
+ case 'i':
+ state.buffer.append("int"); break;
+ case 'j':
+ state.buffer.append("unsigned int"); break;
+ case 'l':
+ state.buffer.append("long"); break;
+ case 'm':
+ state.buffer.append("unsigned long"); break;
+ case 'x':
+ state.buffer.append("long long"); break;
+ case 'y':
+ state.buffer.append("unsigned long long"); break;
+ case 'n':
+ state.buffer.append("__int128"); break;
+ case 'o':
+ state.buffer.append("unsigned __int128"); break;
+ case 'f':
+ state.buffer.append("float"); break;
+ case 'd':
+ state.buffer.append("double"); break;
+ case 'e':
+ state.buffer.append("long double"); break;
+ case 'g':
+ state.buffer.append("__float128"); break;
+ case 'z':
+ state.buffer.append("..."); break;
+ case 'D': {
+ ch = state.get();
+ switch (ch) {
+ case 'd':
+ state.buffer.append("::std::decimal::decimal64"); break;
+ case 'e':
+ state.buffer.append("::std::decimal::decimal128"); break;
+ case 'f':
+ state.buffer.append("::std::decimal::decimal32"); break;
+ case 'h':
+ state.buffer.append("::std::decimal::binary16"); break; // TODO: a guess; what's the actual C++ name for the half-float?
+ case 'i':
+ state.buffer.append("char32_t"); break;
+ case 's':
+ state.buffer.append("char16_t"); break;
+ default:
+ // Dp, Dt, DT
+ state.unget(); throw state.notImplemented();
+ }
+ }
+ case 'u':
+ state.buffer.append(unmangleName(state));
+ return state.popAndRemember(SubstType.TYPE);
+
+ //
+ // <class-enum-type> ::= <unqualified-name> | <nested-name>
+ //
+ case 'N':
+ state.unget();
+ state.buffer.append(unmangleNestedName(state, false));
+ state.remember(SubstType.TYPE);
+ break;
+
+ case 'F':
+ // <function-type> ::= F [Y] <bare-function-type> E
+ if (state.peek() == 'Y') {
+ state.skip();
+ state.buffer.append("extern \"C\" ");
+ }
+ state.buffer.append(unmangleBareFunctionType(state, true));
+ state.consume('E');
+ state.remember(SubstType.TYPE);
+ break;
+
+ case 'M': {
+ state.unget();
+ String name = unmanglePtm(state);
+ state.buffer.append(name);
+ state.remember(name, SubstType.TYPE);
+ break;
+ }
+
+ case 'S':
+ state.unget();
+ state.buffer.append(unmangleSubstitution(state));
+ break;
+
+ case 'T':
+ // either <template-param> or <template-template-param> <template-args>
+ state.unget();
+ state.buffer.append(unmangleTemplateParam(state));
+ if (state.peek() == 'I') {
+ state.buffer.append(unmangleTemplateArgs(state, false));
+ }
+ break;
+
+ case 'A':
+ state.unget();
+ state.buffer.append(unmangleArrayType(state));
+ break;
+
+ default:
+ state.unget();
+ String unqual = unmangleUnqualifiedName(state);
+ state.buffer.append(unqual);
+ if (state.peek() == 'I') {
+ // unscoped-template-name
+ state.remember(unqual, SubstType.TEMPLATE_PREFIX);
+ state.buffer.append(unmangleTemplateArgs(state, false));
+ }
+ state.remember(SubstType.TYPE);
+ break;
+ }
+ return state.pop();
+ }
+
+
+ /**
+ * Insert a "*" or "&" into a string. If this is a function type,
+ * insert in front of the argument list, not after.
+ * @param buffer
+ * @param string
+ */
+ private void ptrOrRefize(StringBuilder buffer, String string) {
+ char last = buffer.length() > 0 ? buffer.charAt(buffer.length() - 1) : 0;
+ if (last == ')' || last == ']') {
+ char match = last == ')' ? '(' : '[';
+ int stack = 0;
+ int idx = buffer.length() - 1;
+ while (idx > 0) {
+ char ch = buffer.charAt(idx);
+ if (ch == last)
+ stack++;
+ else if (ch == match) {
+ stack--;
+ if (stack == 0)
+ break;
+ }
+ idx--;
+ }
+ buffer.insert(idx, '(' + string + ')');
+ } else {
+ buffer.append(string);
+ }
+ }
+
+ /*
+ <array-type> ::= A <positive dimension number> _ <element type>
+ ::= A [<dimension expression>] _ <element type>
+
+ */
+ private String unmangleArrayType(UnmangleState state) throws UnmanglingException {
+ state.push();
+ state.consume('A');
+
+ String count;
+
+ char ch = state.peek();
+ if (ch >= '0' && ch <= '9') {
+ int num = doUnmangleNonNegativeNumber(state);
+ count = "" + num;
+ } else {
+ throw state.notImplemented();
+ }
+ state.consume('_');
+
+ state.buffer.append(unmangleType(state));
+
+ state.buffer.append('[');
+ state.buffer.append(count);
+ state.buffer.append(']');
+
+ return state.pop();
+ }
+
+ /*
+ <pointer-to-member-type> ::= M <class type> <member type>
+ */
+ private String unmanglePtm(UnmangleState state) throws UnmanglingException {
+ state.push();
+ state.consume('M');
+ String klass = unmangleType(state);
+ String ptrquals = unmangleCVQualifiers(state);
+ try {
+ state.safePush();
+ state.buffer.append(doUnmangleFunctionWithName(state, true, '(' + klass + "::*)"));
+ state.safePop();
+ } catch (UnmanglingException e) {
+ // may be pointer to member (field)
+ state.safeBacktrack();
+ state.buffer.append(unmangleType(state));
+ state.buffer.append(' ');
+ state.buffer.append(klass);
+ state.buffer.append("::*");
+ }
+ if (ptrquals.length() > 0) {
+ state.buffer.append(' ');
+ state.buffer.append(ptrquals);
+ }
+ return state.pop();
+ }
+
+ /**
+ * Unmangle any sequence of CV quals
+ * @param state state
+ * @return String
+ */
+ private String unmangleCVQualifiers(UnmangleState state) {
+ state.push();
+ while (true) {
+ boolean matched = true;
+ switch (state.peek()) {
+ case 'r':
+ state.skip();
+ if (state.hasCurrent()) state.buffer.append(' ');
+ state.buffer.append("restrict");
+ break;
+ case 'V':
+ state.skip();
+ if (state.hasCurrent()) state.buffer.append(' ');
+ state.buffer.append("volatile");
+ break;
+ case 'K':
+ state.skip();
+ if (state.hasCurrent()) state.buffer.append(' ');
+ state.buffer.append("const");
+ break;
+ default:
+ matched = false;
+ break;
+ }
+ if (!matched)
+ break;
+ }
+ return state.pop();
+ }
+
+ static class Operator {
+ String name;
+ /** for unary or binary ops; other questionable ones are 0 */
+ int numops;
+
+ public Operator(String name, int numops) {
+ this.name = name;
+ this.numops = numops;
+ }
+ }
+
+ static Map<String, Operator> operators = new HashMap<String, Operator>();
+
+ private static void registerOperator(String code, String name, int opcnt) {
+ if (operators.containsKey(code))
+ throw new IllegalStateException();
+ operators.put(code, new Operator(name, opcnt));
+ }
+
+ static {
+ registerOperator("nw", "new", 0);
+ registerOperator("na", "new[]", 0);
+ registerOperator("dl", "delete", 0);
+ registerOperator("da", "delete[]", 0);
+ registerOperator("ps", "+", 1);
+ registerOperator("ng", "-", 1);
+ registerOperator("ad", "&", 1);
+ registerOperator("de", "*", 1);
+ registerOperator("co", "~", 1);
+ registerOperator("pl", "+", 2);
+ registerOperator("mi", "-", 2);
+ registerOperator("ml", "*", 2);
+ registerOperator("dv", "/", 2);
+ registerOperator("rm", "%", 2);
+ registerOperator("an", "&", 2);
+ registerOperator("or", "|", 2);
+ registerOperator("eo", "^", 2);
+ registerOperator("aS", "=", 2);
+ registerOperator("pL", "+=", 2);
+ registerOperator("mI", "-=", 2);
+ registerOperator("mL", "*=", 2);
+ registerOperator("dV", "/=", 2);
+ registerOperator("rM", "%=", 2);
+ registerOperator("aN", "&=", 2);
+ registerOperator("oR", "|=", 2);
+ registerOperator("eO", "^=", 2);
+ registerOperator("ls", "<<", 2);
+ registerOperator("rs", ">>", 2);
+ registerOperator("lS", "<<=", 2);
+ registerOperator("rS", ">>=", 2);
+ registerOperator("eq", "==", 2);
+ registerOperator("ne", "!=", 2);
+ registerOperator("lt", "<", 2);
+ registerOperator("gt", ">", 2);
+ registerOperator("le", "<=", 2);
+ registerOperator("ge", ">=", 2);
+ registerOperator("nt", "!", 1);
+ registerOperator("aa", "&&", 2);
+ registerOperator("oo", "||", 2);
+ registerOperator("pp", "++", 1);
+ registerOperator("mm", "--", 1);
+ registerOperator("cm", ",", 2);
+ registerOperator("pm", "->*", 2);
+ registerOperator("pt", "->", 2);
+ registerOperator("cl", "()", 1);
+ registerOperator("ix", "[]", 2);
+ registerOperator("qu", "?", 3);
+ registerOperator("st", "sizeof ", 0); // type
+ registerOperator("sz", "sizeof", 1); // expression
+ registerOperator("at", "alignof ", 0); // type
+ registerOperator("az", "alignof", 1); // expression
+ registerOperator("cv", "()", 1);
+ }
+
+ /*
+ <unqualified-name> ::= <operator-name> = lowercase
+ ::= <ctor-dtor-name> = C1-3 D0-2 ...
+ ::= <source-name> = <number> ...
+ ::= <unnamed-type-name> = Ut ...
+
+ */
+ private String unmangleUnqualifiedName(UnmangleState state) throws UnmanglingException {
+ char ch = state.peek();
+ if (ch >= '0' && ch <= '9') {
+ return unmangleSourceName(state);
+ }
+ else if (ch >= 'a' && ch <= 'z') {
+ return unmangleOperatorName(state);
+ }
+ else if (ch == 'U') {
+ return unmangleUnnamedTypeName(state);
+ }
+ else if (ch == 'C') {
+ state.push();
+ String last = simpleName(state.lastSubstitutedName());
+ state.get();
+ switch (state.get()) {
+ case '1':
+ case '2':
+ case '3':
+ state.buffer.append(last);
+ return state.pop();
+ default:
+ state.unget();
+ throw state.unexpected("constructor name");
+ }
+ }
+ else if (ch == 'D') {
+ state.push();
+ String last = simpleName(state.lastSubstitutedName());
+ state.get();
+ state.buffer.append('~');
+ state.buffer.append(last);
+ switch (state.get()) {
+ case '0':
+ return state.pop();
+ case '1':
+ return state.pop();
+ case '2':
+ return state.pop();
+ default:
+ state.unget();
+ throw state.unexpected("destructor name");
+ }
+ }
+ throw state.unexpected();
+ }
+
+ /**
+ * @param name
+ * @return
+ */
+ private String simpleName(String name) {
+ int idx = name.lastIndexOf("::");
+ if (idx >= 0)
+ return name.substring(idx + 2);
+ return name;
+ }
+
+ /*
+ <unnamed-type-name> ::= Ut [ <nonnegative number> ] _
+ <unnamed-type-name> ::= <closure-type-name>
+
+ <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
+
+ */
+ private String unmangleUnnamedTypeName(UnmangleState state) throws UnmanglingException {
+ state.push();
+ state.consume('U');
+ switch (state.get()) {
+ case 't':
+ state.buffer.append("<unnamed #");
+ if (state.peek() != '_') {
+ state.buffer.append("" + (doUnmangleNonNegativeNumber(state) + 2));
+ } else {
+ state.buffer.append("1");
+ }
+ state.buffer.append('>');
+ state.consume('_');
+ break;
+ case 'l':
+ throw state.notImplemented();
+ default:
+ throw state.unexpected();
+ }
+ return state.pop();
+ }
+
+ /*
+ */
+ private String unmangleOperatorName(UnmangleState state) throws UnmanglingException {
+ state.push();
+ char ch = state.get();
+ String op = "" + ch;
+ if (ch == 'v') {
+ // vendor type <digit> <source-name>
+ ch = state.get();
+ if (ch >= '0' && ch <= '9') {
+ int opcount = ch - '0';
+ op = unmangleSourceName(state);
+ boolean first = true;
+
+ // pretend it's a function, to differentiate
+ state.buffer.append('(');
+ while (opcount-- > 0) {
+ if (first)
+ first = false;
+ else
+ state.buffer.append(',');
+ }
+ state.buffer.append(')');
+ } else {
+ throw state.unexpected();
+ }
+ return state.pop();
+ }
+
+ ch = state.get();
+ if (!Character.isLetter(ch)) {
+ throw state.unexpected();
+ }
+
+ op += ch;
+
+ Operator oper = operators.get(op);
+ if (oper == null) {
+ throw state.unexpected();
+ }
+
+ state.buffer.append("operator ");
+
+ // special cases
+ if (op.equals("cv")) {
+ state.buffer.append(unmangleType(state));
+ // fall through
+ }
+
+ state.buffer.append(oper.name);
+ return state.pop();
+
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import org.eclipse.cdt.debug.edc.symbols.IUnmangler;
+
+/**
+ * Stub unmangler for Win32 symbols. Big fat TODO
+ */
+public class UnmanglerWin32 implements IUnmangler {
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IUnmangler#undecorate(java.lang.String)
+ */
+ public String undecorate(String symbol) {
+ // remove standard underscore
+ if (symbol.startsWith("_")) {
+ symbol = symbol.substring(1);
+ }
+ // and DLL import prefix
+ if (symbol.startsWith("_imp_")) {
+ symbol = symbol.substring(5);
+ }
+ // and a stdcall/fastcall suffix
+ int at = symbol.lastIndexOf('@');
+ boolean isStdcall = false;
+ if (at > 0) {
+ try {
+ Integer.parseInt(symbol.substring(at+1));
+ isStdcall = true;
+ } catch (NumberFormatException e) {
+ }
+ }
+ if (isStdcall) {
+ symbol = symbol.substring(0, at);
+
+ // and a fastcall prefix
+ if (symbol.startsWith("@"))
+ symbol = symbol.substring(1);
+ }
+ return symbol;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IUnmangler#isMangled(java.lang.String)
+ */
+ public boolean isMangled(String symbol) {
+ return symbol != null && symbol.startsWith("?");
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IUnmangler#unmangleNameOnly(java.lang.String)
+ */
+ public String unmangleWithoutArgs(String symbol) throws UnmanglingException {
+ // big fat TODO
+ return symbol;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IUnmangler#unmangle(java.lang.String)
+ */
+ public String unmangle(String symbol) throws UnmanglingException {
+ // big fat TODO
+ return symbol;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.IUnmangler#unmangle(java.lang.String)
+ */
+ public String unmangleType(String symbol) throws UnmanglingException {
+ // big fat TODO
+ return symbol;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+
+/**
+ * Unmangler for EABI symbols in a Win32 executable.
+ */
+public class UnmanglerWin32EABI extends UnmanglerWin32 {
+
+ private UnmanglerEABI eabiUnmangler = new UnmanglerEABI();
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglerWin32#unmangle(java.lang.String)
+ */
+ @Override
+ public String unmangle(String symbol) throws UnmanglingException {
+ return eabiUnmangler.unmangle(symbol);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.internal.symbols.files;
+
+import org.eclipse.cdt.debug.edc.symbols.IUnmangler;
+
+/**
+ * This exception is thrown by {@link IUnmangler#unmangle(String)}
+ * when decoding partially or fully fails. A portion of the
+ * unmangled content may be retrieved.
+ */
+public class UnmanglingException extends Exception {
+
+ private static final long serialVersionUID = 3469819721317573378L;
+ private final String partialUnmangling;
+
+ public UnmanglingException(String message, String partialUnmangling) {
+ super(message);
+ this.partialUnmangling = partialUnmangling;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Throwable#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString() + ": got " + partialUnmangling;
+ }
+
+ public String getPartialUnmangling() {
+ return partialUnmangling;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.core.model.ICProject;
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.debug.edc.ITCFAgentLauncher;
+import org.eclipse.cdt.debug.edc.ITCFConnectionListener;
+import org.eclipse.cdt.debug.edc.ITCFServiceManager;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.TCFServiceManager;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Breakpoints;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Memory;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Processes;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
+import org.eclipse.cdt.debug.edc.services.IDSFServiceUsingTCF;
+import org.eclipse.cdt.debug.edc.services.Registers;
+import org.eclipse.cdt.debug.edc.tcf.extension.ProtocolConstants;
+import org.eclipse.cdt.debug.edc.tcf.extension.services.ISimpleRegisters;
+import org.eclipse.cdt.debug.edc.tcf.extension.services.SimpleRegistersProxy;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.Sequence;
+import org.eclipse.cdt.dsf.service.DsfServicesTracker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.tcf.protocol.IService;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.protocol.Protocol;
+import org.eclipse.tm.tcf.services.IMemory;
+import org.eclipse.tm.tcf.services.IProcesses;
+import org.eclipse.tm.tcf.services.IProcesses.DoneCommand;
+import org.eclipse.tm.tcf.services.IProcesses.DoneGetChildren;
+import org.eclipse.tm.tcf.services.IProcesses.DoneGetContext;
+import org.eclipse.tm.tcf.services.IProcesses.ProcessContext;
+import org.eclipse.tm.tcf.services.IRegisters;
+import org.eclipse.tm.tcf.services.IRunControl;
+import org.eclipse.tm.tcf.util.TCFTask;
+
+public abstract class AbstractFinalLaunchSequence extends Sequence {
+
+ private EDCLaunch launch;
+
+ protected DsfServicesTracker tracker;
+ protected List<Step> steps = new ArrayList<Step>();
+
+ /**
+ * The single TCF peer associated with this session. Do not reference this
+ * field explicitly except to set it. Use {@link #getTCFPeer()}
+ */
+ private IPeer tcfPeer;
+
+ /**
+ * Attributes that the debugger requires the TCF peer to match. Derivatives
+ * populate this when we call {@link #specifyRequiredPeer()}
+ */
+ protected final Map<String, String> peerAttributes = new HashMap<String, String>();
+
+ /**********************************************
+ * Common steps
+ **********************************************/
+
+ // Common first step for the sequence
+ //
+ protected final Step trackerStep = new Step() {
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ tracker = new DsfServicesTracker(EDCDebugger.getDefault().getBundle().getBundleContext(), launch
+ .getSession().getId());
+ requestMonitor.done();
+ }
+
+ @Override
+ public void rollBack(RequestMonitor requestMonitor) {
+ if (tracker != null)
+ tracker.dispose();
+ tracker = null;
+ requestMonitor.done();
+ }
+ };
+
+ // Common last step for the sequence
+ //
+ protected final Step cleanupStep = new Step() {
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ tracker.dispose();
+ tracker = null;
+ requestMonitor.done();
+ }
+ };
+
+ /**
+ * EDC currently only supports the scenario where a single TCF peer provides
+ * all the services needed by a debug session (ILaunch). It must provide the
+ * run-control service at a minimum (although other services are probably
+ * required, too). This step should be executed immediately after
+ * trackerStep.
+ */
+ protected Step initFindPeerStep = new Step() {
+
+ @Override
+ public String getTaskName() {
+ return "Find or launch TCF peer";
+ }
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ findPeer(requestMonitor);
+ requestMonitor.done();
+ }
+ };
+
+ /**
+ * @since 2.0
+ */
+ protected Step rememberTCFChannelStep = new StepWithProgress() {
+
+ @Override
+ public String getTaskName() {
+ return "Remember TCF Channel";
+ }
+
+ @Override
+ public void execute(RequestMonitor rm, IProgressMonitor pm) {
+ try {
+ IPeer peer = getTCFPeer();
+ assert peer != null : "initFindPeerStep must be run prior to this one";
+
+ ITCFServiceManager tcfServiceManager = EDCDebugger.getDefault().getServiceManager();
+ IChannel channel = tcfServiceManager.getChannelForPeer(peer);
+ if (channel == null)
+ assert channel != null : "No open channel found to the peer";
+ else
+ launch.usingTCFChannel(channel);
+ }
+ finally {
+ rm.done();
+ }
+ }
+
+ };
+
+ /**
+ * This step registers a listener that detects when the connection goes down.
+ * It invokes #handleConnectionClosed(), which by default terminates the
+ * launch.
+ * @since 2.0
+ */
+ protected Step initDetectConnectionErrorStep = new Step() {
+
+ class TCFConnectionListener implements ITCFConnectionListener {
+
+ private IChannel myPeerChannel;
+
+ public void peerChannelOpened(IPeer peer, IChannel channel) {
+ if (peer == getTCFPeer()) {
+ myPeerChannel = channel;
+ }
+ }
+
+ public void peerChannelClosed(IPeer peer, IChannel channel,
+ Throwable error) {
+ if (peer == getTCFPeer() && channel == myPeerChannel) {
+ try {
+ handleConnectionClosed(channel, error);
+ } finally {
+ EDCDebugger.getDefault().getServiceManager().removeConnectionListener(this);
+ }
+ }
+ }
+ }
+
+ final TCFConnectionListener peerListener = new TCFConnectionListener();
+
+ @Override
+ public String getTaskName() {
+ return "Init connection error listener";
+ }
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+
+ // ensure we detect connection issues
+ IPeer thePeer = getTCFPeer();
+ if (thePeer != null) {
+ EDCDebugger.getDefault().getServiceManager().addConnectionListener(peerListener);
+ IChannel channel = EDCDebugger.getDefault().getServiceManager().getChannelForPeer(thePeer);
+ if (channel != null && channel.getState() == IChannel.STATE_OPEN) {
+ peerListener.peerChannelOpened(thePeer, channel);
+ }
+ }
+
+ requestMonitor.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.concurrent.Sequence.Step#rollBack(org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ @Override
+ public void rollBack(RequestMonitor rm) {
+ EDCDebugger.getDefault().getServiceManager().removeConnectionListener(peerListener);
+ super.rollBack(rm);
+ }
+ };
+
+ protected Step initRunControlStep = new Step() {
+
+ @Override
+ public String getTaskName() {
+ return "Init RunControl service";
+ }
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ assert getTCFPeer() != null : "initFindPeerStep must be run prior to this one";
+ RunControl runcontrol = tracker.getService(RunControl.class);
+ findTCFServiceForDSFService(runcontrol, IRunControl.NAME, requestMonitor);
+ requestMonitor.done();
+ }
+ };
+
+ /*
+ * Initialize Registers service.
+ */
+ protected Step initRegistersServiceStep = new Step() {
+
+ @Override
+ public String getTaskName() {
+ return "Init Registers service";
+ }
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ IPeer peer = getTCFPeer();
+ assert peer != null : "initFindPeerStep must be run prior to this one";
+
+ final Registers registers = tracker.getService(Registers.class);
+
+ ITCFServiceManager tcfServiceManager = EDCDebugger.getDefault().getServiceManager();
+ final IChannel channel = tcfServiceManager.getChannelForPeer(peer);
+
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ // First check if IRegisters service is provided.
+ // If not, look for ISimpleRegisters service.
+ //
+ IService regSvc = null;
+ try {
+ regSvc = getTCFService(IRegisters.NAME);
+ } catch (CoreException e) {
+ // ignore, look for SimpleRegisters service.
+ // Report error when SimpleRegisters service is discarded...02/16/10
+ }
+
+ if (regSvc != null) { // registers service ready
+ registers.tcfServiceReady(regSvc);
+ }
+ else { // look for ISimpleRegisters service.
+ try {
+ regSvc = getTCFService(ISimpleRegisters.NAME);
+ } catch (CoreException e) {
+ }
+ if (regSvc == null) {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Fail to find Registers service in agent."));
+ }
+ else {
+ ISimpleRegisters simpleRegProxy = channel.getRemoteService(ISimpleRegisters.class);
+ if (simpleRegProxy == null) {
+ simpleRegProxy = new SimpleRegistersProxy(channel);
+ channel.setServiceProxy(ISimpleRegisters.class, simpleRegProxy);
+ }
+ registers.tcfServiceReady(simpleRegProxy);
+ }
+ }
+
+ requestMonitor.done();
+ }
+ });
+ }
+ };
+
+ /*
+ * Initialize the memory service
+ */
+ protected Step initMemoryServiceStep = new Step() {
+
+ @Override
+ public String getTaskName() {
+ return "Init Memory service";
+ }
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ assert getTCFPeer() != null : "initFindPeerStep must be run prior to this one";
+ Memory memory = tracker.getService(Memory.class);
+ findTCFServiceForDSFService(memory, IMemory.NAME, requestMonitor);
+ requestMonitor.done();
+ }
+ };
+
+ /**
+ * init breakpoints service.
+ */
+ protected Step initBreakpointsServiceStep = new Step() {
+
+ @Override
+ public String getTaskName() {
+ return "Init Breakpoints service";
+ }
+
+ @Override
+ public void execute(RequestMonitor requestMonitor) {
+ assert getTCFPeer() != null : "initFindPeerStep must be run prior to this one";
+ Breakpoints breakpoints = tracker.getService(Breakpoints.class);
+ findTCFServiceForDSFService(breakpoints,
+ org.eclipse.tm.tcf.services.IBreakpoints.NAME,
+ requestMonitor);
+
+ requestMonitor.done();
+ }
+
+ };
+
+ /*
+ * Initialize the processes service
+ */
+ protected Step initProcessesServiceStep = new Step() {
+
+ @Override
+ public String getTaskName() {
+ return "Init Processes service";
+ }
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ assert getTCFPeer() != null : "initFindPeerStep must be run prior to this one";
+ Processes p = tracker.getService(Processes.class);
+ findTCFServiceForDSFService(p, IProcesses.NAME, requestMonitor);
+ requestMonitor.done();
+ }
+ };
+
+ /*
+ * Launch the process
+ */
+ protected Step launchStep = new Step() {
+
+ @Override
+ public String getTaskName() {
+ return "Launch target";
+ }
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ assert getTCFPeer() != null : "initFindPeerStep must be run prior to this one";
+ IService service;
+ try {
+ service = getTCFService(IProcesses.NAME);
+ } catch (CoreException e1) {
+ requestMonitor.setStatus(e1.getStatus());
+ requestMonitor.done();
+ return;
+ }
+
+ // Note that requestMonitor is passed down, so don't call
+ // requestMonitor.done() here !
+ launchProcess(launch, (IProcesses) service, requestMonitor);
+ }
+ };
+
+ /*
+ * Attach to a process
+ */
+ protected Step attachStep = new Step() {
+
+ @Override
+ public String getTaskName() {
+ return "Attach";
+ }
+
+ @Override
+ public void execute(final RequestMonitor requestMonitor) {
+ assert getTCFPeer() != null : "initFindPeerStep must be run prior to this one";
+ IService service;
+ try {
+ service = getTCFService(IProcesses.NAME);
+ } catch (CoreException e1) {
+ requestMonitor.setStatus(e1.getStatus());
+ requestMonitor.done();
+ return;
+ }
+
+ // Note that requestMonitor is passed down, so don't call
+ // requestMonitor.done() here !
+ attachProcess(launch, (IProcesses) service, requestMonitor);
+ }
+ };
+
+ /**
+ * Get a particular service from the TCF peer associated with this launch
+ *
+ * @param tcfServiceName
+ * the name of the service
+ * @return the service or null if not available
+ * @throws CoreException
+ */
+ protected IService getTCFService(String tcfServiceName) throws CoreException {
+ TCFServiceManager tcfServiceManager = (TCFServiceManager) EDCDebugger.getDefault().getServiceManager();
+ return tcfServiceManager.getPeerService(getTCFPeer(), tcfServiceName);
+ }
+
+ /**
+ * Looks for a peer that matches the criteria set by the subclass, and puts
+ * it in {@link #tcfPeer} if successful. If more than one peer fits the
+ * bill, the subclass is given the chance to choose via
+ * {@link #selectPeer(IPeer[])}
+ *
+ * @param requestMonitor
+ */
+ protected void findPeer(RequestMonitor requestMonitor) {
+ try {
+ // We already found it. No-op
+ if (getTCFPeer() != null) {
+ return;
+ }
+
+ // See if subclass wants an explicit peer.
+ if ((tcfPeer = selectExplicitPeer()) != null) {
+ return;
+ }
+
+ // See if any already running (and discovered) peers fit the bill
+ TCFServiceManager tcfServiceManager = (TCFServiceManager) EDCDebugger.getDefault().getServiceManager();
+ boolean useLocalAgentOnly = useLocalAgentOnly();
+ IPeer[] runningPeers = tcfServiceManager.getRunningPeers(IRunControl.NAME, peerAttributes, useLocalAgentOnly);
+ if (runningPeers.length > 0) {
+ int index = selectPeer(runningPeers);
+ if (index >= 0 && index < runningPeers.length) {
+ tcfPeer = runningPeers[index];
+ return;
+ }
+ }
+
+ // Invoke any registered agent-launchers which could make the
+ // desired peer available.
+ List<IPeer> launchedPeers = new ArrayList<IPeer>();
+ ITCFAgentLauncher[] agentLaunchers = tcfServiceManager.findSuitableAgentLaunchers(IRunControl.NAME, peerAttributes, useLocalAgentOnly);
+ for (ITCFAgentLauncher agentLauncher : agentLaunchers) {
+ IPeer peer = tcfServiceManager.launchAgent(agentLauncher, peerAttributes); // this could take a little while...
+ if (peer != null) {
+ launchedPeers.add(peer);
+ }
+ }
+ if (launchedPeers.size() > 0) {
+ int index = selectPeer(launchedPeers.toArray(new IPeer[launchedPeers.size()]));
+ if (index >= 0 && index < launchedPeers.size()) {
+ tcfPeer = launchedPeers.get(index);
+ return;
+ }
+ }
+
+ requestMonitor.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Could not find a suitable TCF peer", null));
+ } catch (CoreException e) {
+ requestMonitor.setStatus(e.getStatus());
+ }
+ }
+
+ /**
+ * Subclass may override this to select a peer that is not
+ * otherwise discovered by the Locator service.
+ * <p>
+ * If this returns non-<code>null</code>, this peer will be
+ * used instead of querying the locator service and using
+ * {@link #selectPeer(IPeer[])}.
+ * <p>
+ * By default, this returns <code>null</code>.
+ * @return an IPeer or <code>null</code>
+ * @throws CoreException if unable to select the desired explicit peer
+ * @since 2.0
+ */
+ protected IPeer selectExplicitPeer() throws CoreException {
+ return null;
+ }
+
+ /**
+ * Subclass should override this to select a peer from the array of peers
+ * which match the required minimum set of attributes specified by
+ * {@link #specifyRequiredPeer()}. The default behavior is to use the first candidate.
+ *
+ * <p>
+ * This methods represents a way for a specific launcher to do runtime peer
+ * selection. Choosing the right peer might require opening a channel to it
+ * to determine more detailed capabilities than what's available via its
+ * attributes. Or perhaps some additional decision making is required, not
+ * requiring a channel--e.g., more closely examining one of the peer
+ * attributes. Either way, {@link #specifyRequiredPeer()} provides the
+ * <i>minimum</i> set of attributes a peer should have to be considered for
+ * the launch. This method is used to provide further and final pruning of
+ * any candidates.
+ *
+ * @param peers
+ * the candidates
+ * @return the index of the peer to use; if the returned value is outside
+ * the range of candidates, then none of the peers are used.
+ * @since 2.0
+ */
+ public int selectPeer(IPeer[] peers) {
+ assert peers.length > 0;
+ return 0;
+ }
+
+ /**
+ * Find the given TCF service and link it to the given DSF service. The TCF
+ * service will be used to carry out the DSF one.
+ *
+ * <p>
+ * Note caller must call the "rm.done".
+ *
+ * @param dsfService
+ * @param tcfServiceName
+ * TCF service required
+ * @param tcfPeerAttrs
+ * TCF peer attributes required to match.
+ * @param rm
+ */
+ protected void findTCFServiceForDSFService(IDSFServiceUsingTCF dsfService, String tcfServiceName,
+ RequestMonitor rm) {
+
+ try {
+ IService service = getTCFService(tcfServiceName);
+ if (service == null)
+ throw EDCDebugger.newCoreException("Required service \"" + tcfServiceName + "\" is not available.");
+
+ dsfService.tcfServiceReady(service);
+ } catch (CoreException e1) {
+ if (e1.getStatus().matches(IStatus.CANCEL))
+ rm.cancel();
+ rm.setStatus(e1.getStatus());
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public AbstractFinalLaunchSequence(DsfExecutor executor, EDCLaunch launch, IProgressMonitor pm) {
+ this(executor, launch, pm, "Configuring Debugger", "Aborting configuring debugger");
+ }
+
+ /**
+ *
+ * @param executor
+ * @param launch
+ * @param pm
+ * @param sequenceName
+ * name to display in the progress monitor when the sequence is
+ * running.
+ * @param abortName
+ * name to display in the progress monitor when the sequence is
+ * aborting due to error.
+ * @param useLocalAgentOnly
+ * whether to ignore peers from remote hosts
+ * @since 2.0
+ */
+ public AbstractFinalLaunchSequence(DsfExecutor executor, EDCLaunch launch, IProgressMonitor pm,
+ String sequenceName, String abortName) {
+ super(executor, pm, sequenceName, abortName);
+ this.launch = launch;
+ specifyRequiredPeer();
+ }
+
+ @Override
+ public Step[] getSteps() {
+ return steps.toArray(new Step[steps.size()]);
+ }
+
+ /**
+ * This method is invoked by the the step
+ * {@link AbstractFinalLaunchSequence#launchStep} to launch the thing to be
+ * debugged. The base implementation knows how to carry out the launch via a
+ * TCF IProcesses service. When dealing with an agent that doesn't provide
+ * that service (typically the case for embedded bareboard debugging), the
+ * subclass must override this method to inject its custom launch logic.
+ *
+ * @param launch
+ * the launch object
+ * @param ps
+ * the TCF processes service; operation will fail if null
+ * @param requestMonitor
+ * monitor to invoke when processing is done
+ */
+ protected void launchProcess(final ILaunch launch, final IProcesses ps, final RequestMonitor requestMonitor) {
+ try {
+ ILaunchConfiguration cfg = launch.getLaunchConfiguration();
+
+ // Get absolute program path.
+ ICProject cproject = LaunchUtils.getCProject(cfg);
+ // This works even if cproject is null.
+ IPath program = LaunchUtils.verifyProgramPath(cfg, cproject);
+ final String file = program.toOSString();
+
+ final String workingDirectory = LaunchUtils.getWorkingDirectoryPath(cfg);
+ final String[] args = LaunchUtils.getProgramArgumentsArray(cfg);
+ final Map<String, String> env = LaunchUtils.getEnvironmentVariables(cfg);
+ final boolean append = cfg.getAttribute(ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES, true);
+ final boolean attach = false;
+
+ final IProcesses.DoneGetEnvironment done_env = new IProcesses.DoneGetEnvironment() {
+ public void doneGetEnvironment(IToken token, Exception error, Map<String, String> def) {
+ if (error != null) {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), error
+ .getLocalizedMessage(), error));
+ requestMonitor.done();
+ return;
+ }
+ final Map<String, String> vars = new HashMap<String, String>();
+ if (append)
+ vars.putAll(def);
+ if (env != null)
+ vars.putAll(env);
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ ps.start(workingDirectory, file, args, vars, attach, new IProcesses.DoneStart() {
+ public void doneStart(IToken token, Exception error, ProcessContext process) {
+ if (error != null) {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(),
+ error.getLocalizedMessage(), error));
+ requestMonitor.done();
+ return;
+ }
+
+ requestMonitor.done();
+ }
+ });
+ }
+ });
+ }
+ };
+
+ if (append) {
+ Protocol.invokeLater(new Runnable() {
+ public void run() {
+ ps.getEnvironment(done_env);
+ }
+ });
+ } else {
+ done_env.doneGetEnvironment(null, null, null);
+ }
+ } catch (Exception x) {
+ requestMonitor.setStatus(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), x
+ .getLocalizedMessage(), x));
+ requestMonitor.done();
+ }
+ }
+
+ public void attachProcess(EDCLaunch launch, IProcesses service, RequestMonitor requestMonitor) {
+
+ try {
+ int pid = launch.getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_ATTACH_PROCESS_ID, -1);
+
+ String preTargetedID = Integer.toString(pid);
+
+ // 1) get process list from agent (getChildren)
+ String[] processes = getProcessList(service);
+ int numProcesses = processes.length;
+ // 2) get contexts for each ID
+ ChooseProcessItem[] items = null;
+ ProcessContext[] contexts = null;
+
+ items = new ChooseProcessItem[numProcesses];
+ contexts = new ProcessContext[numProcesses];
+
+ for (int i = 0; i < numProcesses; i++) {
+ contexts[i] = getProcessContext(processes[i], service);
+ String procID = contexts[i].getProperties().get(ProtocolConstants.PROP_OS_ID).toString();
+ if (procID == null)
+ procID = "unknown";
+ ChooseProcessItem item = new ChooseProcessItem(procID, contexts[i].getName());
+ items[i] = item;
+ }
+
+ int selectedIndex = 0;
+ if (pid == -1)
+ {
+ // 3) bring up dialog to choose which process
+ ChooseProcessItem selected = chooseProcess(items, "");
+ for (selectedIndex = 0; selectedIndex < numProcesses; selectedIndex++) {
+ if (selected.processID.equals(items[selectedIndex].processID))
+ break;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < contexts.length; i++) {
+ String procID = (String) contexts[i].getProperties().get(ProtocolConstants.PROP_OS_ID);
+ if (procID.equals(preTargetedID))
+ {
+ selectedIndex = i;
+ break;
+ }
+ }
+
+ }
+
+ // 4) attach
+ doAttachTask(contexts[selectedIndex]);
+
+ } catch (CoreException e) {
+ if (e.getStatus().matches(IStatus.CANCEL))
+ requestMonitor.cancel();
+ else {
+ requestMonitor.setStatus(e.getStatus());
+ }
+ }
+
+ requestMonitor.done();
+ }
+
+ /**
+ * Call TCF to carry out the attach.
+ *
+ * @param context
+ * @throws CoreException
+ */
+ protected void doAttachTask(final ProcessContext context) throws CoreException {
+ final TCFTask<Object> task = new TCFTask<Object>() {
+ public void run() {
+ context.attach(new DoneCommand() {
+ public void doneCommand(IToken token, Exception error) {
+ if (error == null)
+ done(this);
+ else
+ error(error);
+ }
+ });
+ }
+ };
+ try {
+ task.getE();
+ } catch (Error e) {
+ String msg = "failed to attach to process: " + context.getID() + ", caused by: "
+ + e.getCause().getMessage();
+ if (!task.isCancelled()) {
+ EDCDebugger.getMessageLogger().logError(msg, e);
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), msg, e));
+ } else {
+ throw new CoreException(Status.CANCEL_STATUS);
+ }
+ }
+ }
+
+ protected ProcessContext getProcessContext(final String processID, final IProcesses service) throws CoreException {
+ final TCFTask<IProcesses.ProcessContext> task = new TCFTask<IProcesses.ProcessContext>() {
+
+ public void run() {
+ service.getContext(processID, new DoneGetContext() {
+
+ public void doneGetContext(IToken token, Exception error, ProcessContext context) {
+ if (error == null)
+ done(context);
+ else
+ error(error);
+ }
+ });
+ }
+
+ };
+ try {
+ return task.getE();
+ } catch (Error e) {
+ String msg = "failed to create context for process: " + processID + ", caused by: "
+ + e.getCause().getMessage();
+ if (!task.isCancelled()) {
+ EDCDebugger.getMessageLogger().logError(msg, e);
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), msg, e));
+ } else {
+ throw new CoreException(Status.CANCEL_STATUS);
+ }
+ }
+ }
+
+ /**
+ * @throws CoreException
+ * @since 2.0
+ */
+ protected ProcessContext getProcessContextByName(String contextName,
+ IProcesses ps) throws CoreException {
+ final String[] contextIDs = getProcessList(ps);
+ for (String contextID : contextIDs) {
+ ProcessContext context = getProcessContext(contextID, ps);
+ if (context.getName().equals(contextName))
+ return context;
+ }
+ return null;
+ }
+
+ /**
+ * Get list of running processes from the target.
+ *
+ * @param service
+ * TCF processes service.
+ * @return array of IDs which are internal IDs created by debugger for the
+ * processes, not process ID in the target OS.
+ * @throws CoreException
+ */
+ protected String[] getProcessList(final IProcesses service) throws CoreException {
+ TCFTask<String[]> task = new TCFTask<String[]>() {
+
+ public void run() {
+ service.getChildren(null, false, new DoneGetChildren() {
+
+ public void doneGetChildren(IToken token, Exception error, String[] contextIds) {
+ if (error == null)
+ done(contextIds);
+ else
+ error(error);
+ }
+ });
+ }
+ };
+
+ String[] ids;
+ try {
+ ids = task.getE();
+ } catch (Error e) {
+ String msg = "failed to get process list from target, caused by: " + e.getCause().getMessage();
+ if (!task.isCancelled()) {
+ EDCDebugger.getMessageLogger().logError(msg, e);
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), msg, e));
+ } else {
+ throw new CoreException(Status.CANCEL_STATUS);
+ }
+ }
+
+ if (ids.length == 0) {
+ String msg = "Failed to get running processes from target";
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), msg, null));
+ }
+ return ids;
+ }
+
+ /**
+ * TODO: This requires UI dialog. We need to either 1. make this abstract so
+ * that each EDC-based debugger can do its own implementation (pending
+ * unification of attach code for TRK & Windows), or 2. provide a common
+ * dialog as IStatusHandler extension.
+ *
+ * @param processes
+ * @param defaultSelection
+ * @return
+ * @throws CoreException
+ */
+ protected ChooseProcessItem chooseProcess(final ChooseProcessItem[] processes, String defaultSelection)
+ throws CoreException {
+ // temp stub.
+ return null;
+ }
+
+ /**
+ * Specify attributes the debugger requires from the underlying TCF peer.
+ * Only peers with those attributes will be considered for this debug
+ * session. This method is called by the abstract class during construction
+ * of the object. The implementation should add the required attributes to
+ * {@link #peerAttributes}
+ */
+ abstract protected void specifyRequiredPeer();
+
+ /**
+ * Return the single TCF peer associated with this debug session (ILaunch).
+ * EDC currently only supports the scenario where a single TCF peer provides
+ * all the services needed by a session. The peer is that from
+ * {@link #selectExplicitPeer()} or chosen from the Locator service
+ * using attributes from {@link #specifyRequiredPeer()} and filtered
+ * by {@link #selectPeer(IPeer[])}.
+ * <p>
+ *
+ * @return the peer. Will return null if called before the
+ * {@link #initFindPeerStep} step has executed.
+ * @since 2.0 a subclass can no longer override this method if it wants to explicitly provide the
+ * peer: override {@link #selectExplicitPeer()} instead.
+ */
+ protected final IPeer getTCFPeer() {
+ return tcfPeer;
+ }
+
+ /**
+ * Tell whether to match local peers or possibly remote peers.
+ * <p>
+ * This routine is called during the {@link #findPeer(RequestMonitor)}
+ * step to indicate whether this sequence will consider TCF peers detected on other
+ * machines. This check occurs only when no explicit peer is returned by {@link #selectExplicitPeer()}.
+ * @return <code>true</code> to filter remote agents (whose {@link IPeer#ATTR_IP_HOST} does not lie on
+ * a local network interface) or <code>false</code> to accept any peer matching {@link #peerAttributes}.
+ * @since 2.0
+ */
+ abstract protected boolean useLocalAgentOnly();
+
+ /**
+ * Default handler for connection errors that terminates the launch.
+ * This runs on the dispatch thread.
+ * @param channel the channel associated with the peer
+ * @param error error, possibly <code>null</code>
+ * @since 2.0
+ */
+ protected void handleConnectionClosed(IChannel channel, Throwable error) {
+ if (launch != null && !launch.isTerminated()) {
+ if (error != null) {
+ EDCDebugger.getMessageLogger().logError("Connection lost; terminating debug session", error);
+ }
+ launch.shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null));
+ }
+ }
+
+ /**
+ * @since 2.0
+ */
+ public EDCLaunch getLaunch() {
+ return launch;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.eclipse.cdt.debug.edc.tcf.extension.services.ISettings;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.util.TCFTask;
+
+/**
+ * @noimplement
+ * @noextend
+ */
+public class AgentSettings {
+
+ private final ISettings settingsService;
+
+ /**
+ * @since 2.0
+ */
+ public AgentSettings(ISettings settingsService) {
+ this.settingsService = settingsService;
+ }
+
+ public ISettings getSettingsService() {
+ return settingsService;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public String[] getSettingIds() throws IOException {
+ TCFTask<String[]> task = new TCFTask<String[]>() {
+
+ public void run() {
+
+ settingsService.getIds(new ISettings.DoneGetSettingIds() {
+
+ public void doneGetSettingIds(IToken token, Exception error, String[] ids) {
+ if (error != null) {
+ error(error);
+ } else {
+ if (ids.length > 0)
+ System.out.println(Arrays.deepToString(ids));
+ done(ids);
+ }
+ }
+ });
+ }
+
+ };
+ return task.getIO();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setSettings(final String context, final String[] ids, final Object[] values) throws IOException {
+
+ TCFTask<Object> task = new TCFTask<Object>() {
+
+ public void run() {
+
+ settingsService.setValues(context, ids, values,
+ new ISettings.DoneSetSettingValues() {
+
+ public void doneSetSettingValues(IToken token, Exception error) {
+ if (error != null)
+ error(error);
+ else
+ done(null);
+ }
+ });
+ }
+
+ };
+ task.getIO();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+/**
+ * Class to hold process information (ID, name) for the ChooseProcessDialog
+ */
+public class ChooseProcessItem {
+ public String processID;
+ public String processName;
+
+ public ChooseProcessItem(String id, String name) {
+ this.processID = id;
+ this.processName = name;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+import org.eclipse.cdt.debug.edc.internal.launch.CSourceLookup;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Breakpoints;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Noop;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Expressions;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.INoop;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Memory;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Modules;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Processes;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Signals;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Snapshots;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Symbols;
+import org.eclipse.cdt.debug.edc.services.Disassembly;
+import org.eclipse.cdt.debug.edc.services.ISnapshots;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.cdt.dsf.debug.service.AbstractDsfDebugServicesFactory;
+import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
+import org.eclipse.cdt.dsf.debug.service.IDisassembly;
+import org.eclipse.cdt.dsf.debug.service.IExpressions;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+import org.eclipse.cdt.dsf.debug.service.IModules;
+import org.eclipse.cdt.dsf.debug.service.IProcesses;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+import org.eclipse.cdt.dsf.debug.service.IRunControl;
+import org.eclipse.cdt.dsf.debug.service.ISignals;
+import org.eclipse.cdt.dsf.debug.service.ISourceLookup;
+import org.eclipse.cdt.dsf.debug.service.IStack;
+import org.eclipse.cdt.dsf.debug.service.ISymbols;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.debug.core.ILaunch;
+
+public abstract class DebugServicesFactory extends AbstractDsfDebugServicesFactory {
+
+ public DebugServicesFactory() {
+
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <V> V createService(Class<V> clazz, DsfSession session, Object... optionalArguments) {
+ if (ITargetEnvironment.class.isAssignableFrom(clazz)) {
+ for (Object arg : optionalArguments)
+ if (arg instanceof ILaunch)
+ return (V) createTargetEnvironmentService(session, ((ILaunch) arg));
+ }
+ if (Snapshots.class.isAssignableFrom(clazz)) {
+ return (V)createSnapshotsService(session);
+ }
+
+ // Used for testing
+ if (INoop.class.isAssignableFrom(clazz)) {
+ return (V) new Noop(session);
+ }
+
+ return super.createService(clazz, session, optionalArguments);
+ }
+
+ /**
+ * EDC requires adopters to provide an {@link ITargetEnvironment} service.
+ *
+ * @param session
+ * the DSF session the service will be used in
+ * @param launch
+ * the launch object which spawned the DSF session. It's likely
+ * the launch's configuration will be needed to service some of
+ * the ITargetEnvironment methods.
+ * @return the DSF service
+ */
+ abstract protected ITargetEnvironment createTargetEnvironmentService(DsfSession session, ILaunch launch);
+
+ /**
+ * EDC requires adopters to provide an {@link IStack} service.
+ *
+ * @param session
+ * the DSF session the service will be used in
+ * @return the DSF service
+ */
+ abstract protected IStack createStackService(DsfSession session);
+
+ /**
+ * EDC requires adopters to provide an {@link IRegisters} service.
+ *
+ * @param session
+ * the DSF session the service will be used in
+ * @return the DSF service
+ */
+ abstract protected IRegisters createRegistersService(DsfSession session);
+
+ @Override
+ protected ISourceLookup createSourceLookupService(DsfSession session) {
+ return new CSourceLookup(session);
+ }
+
+ @Override
+ protected IRunControl createRunControlService(DsfSession session) {
+ return new RunControl(session);
+ }
+
+ @Override
+ protected IMemory createMemoryService(DsfSession session) {
+ return new Memory(session);
+ }
+
+ @Override
+ protected IBreakpoints createBreakpointService(DsfSession session) {
+ return new Breakpoints(session);
+ }
+
+ @Override
+ protected IDisassembly createDisassemblyService(DsfSession session) {
+ return new Disassembly(session, new String[0]);
+ }
+
+ @Override
+ protected IExpressions createExpressionService(DsfSession session) {
+ return new Expressions(session);
+ }
+
+ @Override
+ protected IModules createModulesService(DsfSession session) {
+ return new Modules(session);
+ }
+
+ @Override
+ protected IProcesses createProcessesService(DsfSession session) {
+ return new Processes(session);
+ }
+
+ @Override
+ protected ISignals createSignalsService(DsfSession session) {
+ return new Signals(session);
+ }
+
+ @Override
+ protected ISymbols createSymbolsService(DsfSession session) {
+ return new Symbols(session);
+ }
+
+ protected ISnapshots createSnapshotsService(DsfSession session) {
+ return new Snapshots(session);
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.debug.core.sourcelookup.AbsolutePathSourceContainer;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.ExecutablesSourceContainer;
+import org.eclipse.cdt.debug.edc.internal.PathUtils;
+import org.eclipse.cdt.debug.edc.internal.launch.ShutdownSequence;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Processes;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExitedEvent;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.RootExecutionDMC;
+import org.eclipse.cdt.debug.edc.internal.snapshot.Album;
+import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotUtils;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
+import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
+import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.Sequence;
+import org.eclipse.cdt.dsf.concurrent.ThreadSafe;
+import org.eclipse.cdt.dsf.debug.model.DsfLaunch;
+import org.eclipse.cdt.dsf.debug.model.DsfMemoryBlockRetrieval;
+import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
+import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlShutdownDMEvent;
+import org.eclipse.cdt.dsf.debug.sourcelookup.DsfSourceLookupDirector;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.DsfServicesTracker;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.sourcelookup.ISourceContainer;
+import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer;
+import org.eclipse.tm.tcf.protocol.IChannel;
+import org.eclipse.tm.tcf.protocol.IPeer;
+import org.eclipse.tm.tcf.protocol.Protocol;
+
+/**
+ * The only object in the model that implements the traditional interfaces.
+ */
+@ThreadSafe
+abstract public class EDCLaunch extends DsfLaunch {
+ private DefaultDsfExecutor executor;
+ private final DsfSession session;
+ private DsfServicesTracker tracker;
+ private boolean initialized;
+ private boolean shutDown;
+ private boolean isLaunching;
+ private boolean isTerminating;
+
+ private DsfMemoryBlockRetrieval memRetrieval;
+ private IDsfDebugServicesFactory serviceFactory;
+ private final String debugModelID;
+ private String description;
+ private Album album;
+ private boolean snapshotSupportInitialized;
+ private boolean isFirstLaunch = true;
+ private ILaunchConfiguration activeLaunchConfiguration;
+ private List<ILaunchConfiguration> affiliatedLaunchConfigurations = Collections.synchronizedList(new ArrayList<ILaunchConfiguration>());
+ private boolean isTerminatedThanDisconnected = false;
+ private boolean shuttingDown;
+
+ private static final Map<EDCLaunch, List<IChannel>> launchChannels = Collections
+ .synchronizedMap(new HashMap<EDCLaunch, List<IChannel>>());
+
+ private static final Map<String, EDCLaunch> launchSessions = Collections
+ .synchronizedMap(new HashMap<String, EDCLaunch>());
+
+ /**
+ * Every EDC (DSF) session has a thread pool in which to delegate blocking
+ * code to. See AbstractEDCService.asyncExec()
+ */
+ private static final Map<String, ExecutorService> threadPools = Collections
+ .synchronizedMap(new HashMap<String, ExecutorService>());
+
+ public EDCLaunch(ILaunchConfiguration launchConfiguration, String mode, ISourceLocator locator, String ownerID) {
+ super(launchConfiguration, mode, locator);
+
+ debugModelID = ownerID;
+
+ // Create the dispatch queue to be used by debugger control and services
+ // that belong to this launch
+ final DefaultDsfExecutor dsfExecutor = new DefaultDsfExecutor("DSF executor - " + ownerID); //$NON-NLS-1$
+ dsfExecutor.prestartCoreThread();
+ executor = dsfExecutor;
+ session = DsfSession.startSession(executor, ownerID);
+ launchSessions.put(session.getId(), this);
+
+ threadPools.put(session.getId(), newThreadPool());
+ }
+
+ /**
+ * Obtains a new thread pool
+ */
+ private ThreadPoolExecutor newThreadPool() {
+ // Thread pools can be a tricky thing. The behaviors available for
+ // ThreadPoolExecutor are particularly so. Grab a handful of
+ // aspirin and read the class javadoc if you're up for it. Basically,
+ // what we're going to do here is create a thread pool that hopes to
+ // meet demand with three threads. It will use a queue to allow itself
+ // to be backlogged by a maximum of 10,000 requests. If the requests
+ // keep pouring in and the backlog limit is blown, the pool will
+ // dispatch up to three additional threads (for a total of six) to try
+ // to handle the load. If it still can't keep up after that, then it
+ // throws up its hands and starts rejecting requests. We need to protect
+ // ourselves from exhausting cpu/system resources (a million threads) or
+ // memory (millions of backlogged requests). A coding or runtime mishap
+ // could lead to either if we don't set limits on the thread pool. But
+ // what are reasonable limits? The best we can do is set some values we
+ // think will work, use checks to make us or the user aware when they
+ // don't, and give the user a backdoor mechanism to tweak the values
+ // until we can provide better default values.
+ //
+ // Also, keep in mind that the thread pool will try to trim down the
+ // number of threads if and when it has had to resort to creating
+ // additional ones to handle a heavy load. If a thread is idle for more
+ // than ten seconds, it will be shut down.
+
+ int coreThreadCount = getBackdoorValue("org.eclipse.cdt.edc.poolthread.coreThreadCount", 3);
+ int maxThreadCount = getBackdoorValue("org.eclipse.cdt.edc.poolthread.maxThreadCount", coreThreadCount + 3);
+ long idleLimit = getBackdoorValue("org.eclipse.cdt.edc.poolthread.idleLimit", 10);
+ int queueLimit = getBackdoorValue("org.eclipse.cdt.edc.poolthread.queueLimit", 10000);
+
+ return new ThreadPoolExecutor(coreThreadCount, maxThreadCount,
+ idleLimit, TimeUnit.SECONDS,
+ new ArrayBlockingQueue<Runnable>(queueLimit));
+ }
+
+ /**
+ * Provide a backdoor mechanism for tweaking the thread pool parameters on
+ * the field. Hopefully this will never be needed, but better safe than
+ * sorry.
+ */
+ private static int getBackdoorValue(String prop, int defaultVal) {
+ String value = System.getProperty(prop);
+ if (value != null) {
+ try {
+ return Integer.parseInt(value);
+ }
+ catch (NumberFormatException exc){}
+ }
+ return defaultVal;
+ }
+
+
+ public static EDCLaunch getLaunchForSession(String sessionID) {
+ return launchSessions.get(sessionID);
+ }
+
+ /**
+ * See {@link #threadPools}
+ * @since 2.0
+ */
+ public static ExecutorService getThreadPool(String sessionID) {
+ return threadPools.get(sessionID);
+ }
+
+ public DsfExecutor getDsfExecutor() {
+ return executor;
+ }
+
+ public IDsfDebugServicesFactory getServiceFactory() {
+ return serviceFactory;
+ }
+
+ public void initialize() {
+ Runnable initRunnable = new DsfRunnable() {
+ public void run() {
+ tracker = new DsfServicesTracker(EDCDebugger.getDefault().getBundle().getBundleContext(), session
+ .getId());
+ session.addServiceEventListener(EDCLaunch.this, null);
+ memRetrieval = null;
+ initialized = true;
+ fireChanged();
+ }
+ };
+
+ // Invoke the execution code and block waiting for the result.
+ try {
+ executor.submit(initRunnable).get();
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().log(
+ new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
+ "Error initializing launch", e)); //$NON-NLS-1$
+ }
+ }
+
+ public void initializeMemoryRetrieval() throws CoreException {
+ // Create a memory retrieval and register it with the session
+ try {
+ executor.submit(new Callable<Object>() {
+ public Object call() throws CoreException {
+ memRetrieval = new DsfMemoryBlockRetrieval(getDebugModelID(), getLaunchConfiguration(), session);
+ session.registerModelAdapter(IMemoryBlockRetrieval.class, memRetrieval);
+ return null;
+ }
+ }).get();
+ } catch (InterruptedException e) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, 0,
+ "Interrupted while waiting for get process callable.", e)); //$NON-NLS-1$
+ } catch (ExecutionException e) {
+ throw (CoreException) e.getCause();
+ } catch (RejectedExecutionException e) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, 0,
+ "Debugger shut down before launch was completed.", e)); //$NON-NLS-1$
+ }
+ }
+
+ public DsfSession getSession() {
+ return session;
+ }
+
+ public DsfMemoryBlockRetrieval getMemoryBlockRetrieval() {
+ return memRetrieval;
+ }
+
+ public void setServiceFactory(IDsfDebugServicesFactory factory) {
+ serviceFactory = factory;
+ }
+
+ // Event handler when a thread or a threadGroup exits
+ @DsfServiceEventHandler
+ public void eventDispatched(IExitedDMEvent e) {
+ // Only shutdown the session if the RootDMC is exited, namely all processDMCs
+ // are terminated or disconnected.
+ if (! (e instanceof ExitedEvent))
+ return;
+
+ // Synchronize here in case a launch delegate is trying to do more
+ // launching at the same time.
+ synchronized (this)
+ {
+ if (e.getDMContext() instanceof RootExecutionDMC && !isLaunching()) {
+ // Don't terminate the launch if a delegate has already started
+ // using it for new activity.
+ // The ExitedEvent tells us whether the last context in the launch
+ // is terminated or disconnected.
+ setTerminating(true);
+ isTerminatedThanDisconnected = ((ExitedEvent)e).isTerminatedThanDisconnected();
+ shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null));
+ }
+ }
+ }
+
+ // /////////////////////////////////////////////////////////////////////////
+ // IServiceEventListener
+ @DsfServiceEventHandler
+ public void eventDispatched(ICommandControlShutdownDMEvent event) {
+ shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null));
+ }
+
+ // /////////////////////////////////////////////////////////////////////////
+ // ITerminate
+ @Override
+ public boolean canTerminate() {
+ return initialized && !shutDown;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ // This return value is irrelevant to whether the session
+ // is terminated or disconnected.
+ return shutDown;
+ }
+
+ @Override
+ public void terminate() throws DebugException {
+ // Step one, tell the run control service to terminate everything
+ DsfExecutor dsfExecutor = getDsfExecutor();
+ if (dsfExecutor != null) {
+ setTerminating(true);
+ getDsfExecutor().execute(new Runnable() {
+ public void run() {
+ RunControl runControlService = tracker
+ .getService(RunControl.class);
+ if (runControlService != null)
+ runControlService.terminateAllContexts(null);
+ }
+ });
+ }
+ }
+
+ // ITerminate
+ // /////////////////////////////////////////////////////////////////////////
+
+ // /////////////////////////////////////////////////////////////////////////
+ // IDisconnect
+ @Override
+ public boolean canDisconnect() {
+ return !(snapshotSupportInitialized && isSnapshotLaunch()) && canTerminate();
+ }
+
+ @Override
+ public boolean isDisconnected() {
+ // Indicates whether the launch (session) is terminated
+ // by "disconnect" command.
+ return isTerminated() && ! isTerminatedThanDisconnected;
+ }
+
+ @Override
+ public void disconnect() throws DebugException {
+ DsfExecutor dsfExecutor = getDsfExecutor();
+ if (dsfExecutor != null) {
+ getDsfExecutor().execute(new Runnable() {
+ public void run() {
+ Processes procService = tracker.getService(Processes.class);
+ if (procService != null)
+ procService.detachDebuggerFromSession(null);
+ }
+ });
+ }
+ }
+
+ // IDisconnect
+ // /////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Shuts down the services, the session and the executor associated with
+ * this launch.
+ * <p>
+ * Note: The argument request monitor to this method should NOT use the
+ * executor that belongs to this launch. By the time the shutdown is
+ * complete, this executor will not be dispatching anymore and the request
+ * monitor will never be invoked. Instead callers should use the
+ * {@link ImmediateExecutor}.
+ * </p>
+ *
+ * @param rm
+ * The request monitor invoked when the shutdown is complete.
+ */
+ @ConfinedToDsfExecutor("getSession().getExecutor()")
+ public void shutdownSession(final RequestMonitor rm) {
+ if (shutDown || shuttingDown || executor == null) {
+ rm.done();
+ return;
+ }
+
+ shuttingDown = true;
+
+ // Shut down the thread pool, otherwise its core threads might hang
+ // around indefinitely.
+ ExecutorService pool = threadPools.get(session.getId());
+ if (pool != null) {
+ pool.shutdown();
+ try {
+ // We need to wait for the threads actively handling a task
+ // to complete. If we proceed with the session shutdown before
+ // that, the active threads are likely to encounter problem as
+ // they try to operate within a defunct session. Don't wait
+ // indefinitely, though. Note that this does not block the
+ // UI from showing the session as terminated. For the most part,
+ // things on the surface should look like the debug session
+ // ended. Obviously, cleanup will be pending.
+ pool.awaitTermination(15, TimeUnit.SECONDS);
+ } catch (InterruptedException exc) {
+ EDCDebugger.getMessageLogger().logException(exc);
+ }
+ }
+
+ Sequence shutdownSeq = new ShutdownSequence(getDsfExecutor(), session.getId(), new RequestMonitor(session
+ .getExecutor(), rm) {
+ @Override
+ public void handleCompleted() {
+ session.removeServiceEventListener(EDCLaunch.this);
+ if (!isSuccess()) {
+ EDCDebugger.getMessageLogger().log(
+ new MultiStatus(EDCDebugger.PLUGIN_ID, -1, new IStatus[] { getStatus() },
+ "Session shutdown failed", null)); //$NON-NLS-1$
+ }
+ // Last order of business, shutdown the dispatch queue.
+ if (tracker != null)
+ tracker.dispose();
+ tracker = null;
+ DsfSession.endSession(session);
+
+ // DsfMemoryBlockRetrieval.saveMemoryBlocks();
+ if (memRetrieval != null) {
+ memRetrieval.saveMemoryBlocks();
+ }
+
+ // endSession takes a full dispatch to distribute the
+ // session-ended event, finish step only after the dispatch.
+ executor.shutdown();
+ executor = null;
+ fireTerminate();
+
+ try {
+ closeUnusedChannels();
+ } catch (Throwable e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+
+ shutDown = true;
+
+ rm.setStatus(getStatus());
+ rm.done();
+ }
+ });
+ executor.execute(shutdownSeq);
+
+ try {
+ ILaunchConfiguration activeLaunchConfig = getLaunchConfiguration();
+
+ if (activeLaunchConfig.getAttribute(IEDCLaunchConfigurationConstants.ATTR_IS_ONE_USE, false))
+ activeLaunchConfig.delete();
+
+ if (isSnapshotLaunch()) {
+ // delete launch configuration
+ ILaunchConfiguration lc = SnapshotUtils.findExistingLaunchForAlbum(album);
+ if (lc != null){
+ lc.delete();
+ }
+ }
+ } catch (Throwable e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ protected void closeUnusedChannels() {
+ synchronized (launchChannels) {
+ List<IChannel> channelList = launchChannels.get(this);
+ launchChannels.remove(this);
+ if (channelList == null)
+ return;
+
+ Collection<List<IChannel>> remainingChannels = launchChannels.values();
+ for (List<IChannel> list : remainingChannels) {
+ channelList.removeAll(list);
+ }
+ for (final IChannel channel : channelList) {
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ channel.close();
+ }
+ });
+ }
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter) {
+ // Must force adapters to be loaded.
+ Platform.getAdapterManager().loadAdapter(this, adapter.getName());
+ return super.getAdapter(adapter);
+ }
+
+ public String getDebugModelID() {
+ return debugModelID;
+ }
+
+ public void usingTCFChannel(IChannel channel) {
+ synchronized (launchChannels) {
+ List<IChannel> channelList = launchChannels.get(this);
+ if (channelList == null) {
+ channelList = new ArrayList<IChannel>();
+ }
+ if (!channelList.contains(channel))
+ channelList.add(channel);
+ launchChannels.put(this, channelList);
+ }
+ }
+
+ public IAlbum getAlbum() {
+ return album;
+ }
+
+ public void setAlbum(IAlbum album) {
+ this.album = (Album) album;
+ }
+
+ public boolean isSnapshotLaunch() {
+ // this is not set in run mode
+ if (ILaunchManager.DEBUG_MODE.equals(getLaunchMode()))
+ assert snapshotSupportInitialized;
+ return album != null;
+ }
+
+ public ISourceLocator getExecutableLocator() {
+ CSourceLookupDirector director = new CSourceLookupDirector();
+ director.initializeParticipants();
+ try {
+ if (isSnapshotLaunch()) {
+ getAlbum().configureSourceLookupDirector(director);
+ } else {
+ String exePath = getLaunchConfiguration().getAttribute(
+ ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "");
+ director.setSourceContainers(createExecutableLocatorSourceContainers(exePath));
+ }
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ return director;
+ }
+
+ /**
+ * @since 2.0
+ */
+ protected ISourceContainer[] createExecutableLocatorSourceContainers(String exePath) {
+ List<ISourceContainer> containers = new ArrayList<ISourceContainer>();
+ containers.add(new AbsolutePathSourceContainer());
+ if (exePath.length() > 0)
+ {
+ IPath exeDirectory = new Path(exePath).removeLastSegments(1);
+ containers.add(new DirectorySourceContainer(exeDirectory, false));
+ }
+ containers.add(new ExecutablesSourceContainer());
+ return containers.toArray(new ISourceContainer[containers.size()]);
+ }
+
+ public void initializeSnapshotSupport() {
+ try {
+ String albumFile = getLaunchConfiguration().getAttribute(IEDCLaunchConfigurationConstants.ATTR_ALBUM_FILE,
+ "");
+ IPath albumPath = PathUtils.createPath(albumFile);
+ if (albumPath.toFile().exists()) {
+ album = Album.getAlbumByLocation(albumPath);
+ if (album == null) {
+ album = new Album();
+ album.setLocation(albumPath);
+ album.loadAlbum(false);
+ }
+ album.setSessionID(session.getId());
+
+ IAlbum album = Album.getAlbumBySession(session.getId());
+ DsfSourceLookupDirector director = (DsfSourceLookupDirector) getSourceLocator();
+ album.configureSourceLookupDirector(director);
+ } else {
+
+ }
+ } catch (Exception e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+
+ snapshotSupportInitialized = true;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public ILaunchConfiguration[] getAffiliatedLaunchConfigurations() {
+ return affiliatedLaunchConfigurations.toArray(new ILaunchConfiguration[affiliatedLaunchConfigurations.size()]);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void addAffiliatedLaunchConfiguration(
+ ILaunchConfiguration configuration) {
+ affiliatedLaunchConfigurations.add(configuration);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setActiveLaunchConfiguration(ILaunchConfiguration activeLaunchConfiguration) {
+ this.activeLaunchConfiguration = activeLaunchConfiguration;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public void setFirstLaunch(boolean isFirstLaunch) {
+ this.isFirstLaunch = isFirstLaunch;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public boolean isFirstLaunch() {
+ return isFirstLaunch;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public ISourceLocator createSourceLocator()
+ throws CoreException {
+ DsfSourceLookupDirector locator = new DsfSourceLookupDirector(session);
+ String memento = getLaunchConfiguration().getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO, (String) null);
+ if (memento == null) {
+ locator.initializeDefaults(getLaunchConfiguration());
+ } else {
+ locator.initializeFromMemento(memento, getLaunchConfiguration());
+ }
+ return locator;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static EDCLaunch[] findLaunchesUsingPeer(final IPeer ipeer) {
+ final List<EDCLaunch> results = new ArrayList<EDCLaunch>();
+ ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
+ final List<ILaunch> launchList = Arrays.asList(manager.getLaunches());
+
+ Protocol.invokeAndWait(new Runnable() {
+ public void run() {
+ for (ILaunch iLaunch : launchList) {
+ if (iLaunch instanceof EDCLaunch) {
+ EDCLaunch edcLaunch = (EDCLaunch) iLaunch;
+ List<IChannel> channels = launchChannels.get(edcLaunch);
+ if (channels != null)
+ {
+ for (IChannel iChannel : channels) {
+ if (iChannel.getRemotePeer().equals(ipeer)) {
+ results.add(edcLaunch);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ return results.toArray(new EDCLaunch[results.size()]);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @Override
+ public ILaunchConfiguration getLaunchConfiguration() {
+ if (activeLaunchConfiguration == null)
+ activeLaunchConfiguration = super.getLaunchConfiguration();
+ return activeLaunchConfiguration;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public String getCompilationPath(String filename) {
+ // TODO Use the source lookup service
+ IPath path = Path.EMPTY;
+ if (Path.EMPTY.isValidPath(filename)) {
+ filename = PathUtils.convertPathToNative(filename);
+ ISourceLocator sl = getSourceLocator();
+ if (sl instanceof CSourceLookupDirector) {
+ path = ((CSourceLookupDirector) sl).getCompilationPath(filename);
+ }
+ if (path == null) {
+ path = PathUtils.findExistingPathIfCaseSensitive(new Path(filename));
+ }
+ }
+ return path.toOSString();
+ }
+
+ /**
+ * @since 2.0
+ */
+ public String getStartupStopAtPoint() {
+ String ret = null;
+ try {
+ if (getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN,
+ false)) {
+ ret = getLaunchConfiguration().getAttribute(
+ ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL,
+ ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT);
+ }
+ } catch (CoreException e) {
+ // ignore
+ }
+
+ return ret;
+ }
+
+ /**
+ * Set a short description for this launch.
+ * The default is the name of the initial launch
+ * configuration but usually somewhere in the
+ * initial launch sequence a description of the
+ * debug target can be gathered, either from the
+ * TCF peer attributes or from the connection
+ * settings.
+ * @since 2.0
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Returns a short description of the launch.
+ * Used as the label for the launch in the
+ * Debug view.
+ * @since 2.0
+ */
+ public String getDescription() {
+ if (description == null)
+ return getLaunchConfiguration().getName();
+ return description;
+ }
+
+ /**
+ * Once a launch has been selected for use by a launch delegate
+ * the launching flag is set so clients can know the launch is
+ * in use. Once the launch process completes this launching flag
+ * will be reset.
+ * @since 2.0
+ */
+ public void setLaunching(boolean isLaunching) {
+ this.isLaunching = isLaunching;
+ }
+
+ /**
+ * Returns true if this launch being used by a delegate
+ * for new launch activity.
+ * @since 2.0
+ */
+ public boolean isLaunching() {
+ return isLaunching;
+ }
+
+ /**
+ * When the termination process begins this is called to flag
+ * the launch so clients can know it is shutting down.
+ * @since 2.0
+ */
+ public void setTerminating(boolean isTerminating) {
+ this.isTerminating = isTerminating;
+ }
+
+ /**
+ * Returns true if this launch is in the process of terminating.
+ * Termination is asynchronous and clients can call this to see
+ * if termination is in progress.
+ * @since 2.0
+ */
+ public boolean isTerminating() {
+ return isTerminating;
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.launch.ServicesLaunchSequence;
+import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotLaunchSequence;
+import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotUtils;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
+import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
+import org.eclipse.cdt.dsf.concurrent.Query;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.Sequence;
+import org.eclipse.cdt.dsf.debug.service.IDsfDebugServicesFactory;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.launch.AbstractCLaunchDelegate2;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+
+abstract public class EDCLaunchDelegate extends AbstractCLaunchDelegate2 {
+
+ class UncancelableMonitor extends SubProgressMonitor {
+
+ private boolean canceled;
+
+ public UncancelableMonitor(IProgressMonitor monitor, int ticks,
+ int style) {
+ super(monitor, ticks, style);
+ this.canceled = false;
+ }
+
+ @Override
+ public void setCanceled(boolean b) {
+ canceled = b;
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return canceled;
+ }
+
+ }
+
+ public EDCLaunchDelegate() {
+ super(false);
+ }
+
+ public EDCLaunchDelegate(boolean requireCProject) {
+ super(requireCProject);
+ }
+
+ public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)
+ throws CoreException {
+
+ org.eclipse.cdt.launch.LaunchUtils.enableActivity("org.eclipse.cdt.debug.edc.ui.edcActivity", true); //$NON-NLS-1$
+
+ if (monitor == null) {
+ monitor = new NullProgressMonitor();
+ }
+
+ monitor.beginTask("Launching...", 10);
+ if (monitor.isCanceled()) {
+ return;
+ }
+
+ try {
+ final EDCLaunch edcLaunch = (EDCLaunch) launch;
+ boolean forDebug = mode.equals(ILaunchManager.DEBUG_MODE);
+
+ monitor.worked(1);
+
+ if (edcLaunch.isFirstLaunch())
+ {
+ // First launch for this session, we need to create all of the services
+ edcLaunch.setServiceFactory(newServiceFactory());
+
+ if (forDebug) {
+ edcLaunch.initializeSnapshotSupport();
+ }
+ // Create and invoke the launch sequence to create the debug control and
+ // services
+ IProgressMonitor subMon1 = new SubProgressMonitor(monitor, 4, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
+ final ServicesLaunchSequence servicesLaunchSequence = new ServicesLaunchSequence(edcLaunch.getSession(), edcLaunch,
+ subMon1);
+
+ edcLaunch.getSession().getExecutor().execute(servicesLaunchSequence);
+ try {
+ getOrCancelSequence(servicesLaunchSequence, subMon1);
+ } catch (InterruptedException e1) {
+ throw new DebugException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, DebugException.INTERNAL_ERROR,
+ "Interrupted Exception in dispatch thread.\n" + e1.getLocalizedMessage(), e1)); //$NON-NLS-1$
+ } catch (CancellationException e) {
+ throw new CoreException(Status.CANCEL_STATUS);
+ } catch (ExecutionException e1) {
+ throw new DebugException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, DebugException.REQUEST_FAILED,
+ "Error in services launch sequence.", e1.getCause())); //$NON-NLS-1$
+ }
+
+ if (monitor.isCanceled())
+ return;
+
+ // The initializeControl method should be called after the
+ // ICommandControlService
+ // be initialized in the ServicesLaunchSequence above. This is because
+ // it is that
+ // service that will trigger the launch cleanup (if we need it during
+ // this launch)
+ // through an ICommandControlShutdownDMEvent
+ if (forDebug) {
+ edcLaunch.initializeMemoryRetrieval();
+ }
+
+ monitor.worked(1);
+
+ }
+
+
+ // Create and invoke the final launch sequence to setup the debugger
+ IProgressMonitor subMon2 = new UncancelableMonitor(monitor, 4, SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
+ final Sequence finalLaunchSequence = getFinalLaunchSequence(edcLaunch.getSession().getExecutor(), edcLaunch, subMon2);
+
+ edcLaunch.getSession().getExecutor().execute(finalLaunchSequence);
+ boolean succeed = false;
+ try {
+ getOrCancelSequence(finalLaunchSequence, subMon2);
+ succeed = true;
+ } catch (InterruptedException e1) {
+ IStatus exceptionStatus = new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, DebugException.INTERNAL_ERROR,
+ "Interrupted Exception in dispatch thread.\n" + e1.getLocalizedMessage(), e1);
+ if (edcLaunch.isFirstLaunch())
+ throw new DebugException(exceptionStatus); //$NON-NLS-1$
+ else
+ EDCDebugger.getMessageLogger().log(exceptionStatus);
+ } catch (CancellationException e) {
+ if (edcLaunch.isFirstLaunch())
+ throw new CoreException(Status.CANCEL_STATUS);
+ } catch (ExecutionException e1) {
+ Throwable cause = e1.getCause();
+ if (cause instanceof CoreException) {
+ IStatus s = ((CoreException) cause).getStatus();
+ if (s.getSeverity() == IStatus.CANCEL && edcLaunch.isFirstLaunch())
+ throw (CoreException) cause;
+ }
+ IStatus errorStatus = EDCDebugger.getMessageLogger().createStatus(IStatus.ERROR, null, e1.getCause());
+ if (edcLaunch.isFirstLaunch())
+ throw new DebugException(errorStatus);
+ else
+ EDCDebugger.getMessageLogger().log(errorStatus);
+ } finally {
+ if (!succeed && edcLaunch.isFirstLaunch()) {
+ Query<Object> launchShutdownQuery = new Query<Object>() {
+ @Override
+ protected void execute(DataRequestMonitor<Object> rm) {
+ edcLaunch.shutdownSession(rm);
+ }
+ };
+
+ edcLaunch.getSession().getExecutor().execute(launchShutdownQuery);
+
+ // Wait for the shutdown to finish. The Query.get() method is a
+ // synchronous call which blocks until the query completes.
+ try {
+ // not cancellable
+ launchShutdownQuery.get();
+ } catch (InterruptedException e) {
+ throw new DebugException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID,
+ DebugException.INTERNAL_ERROR,
+ "InterruptedException while shutting down debugger launch " + launch, e)); //$NON-NLS-1$
+ } catch (ExecutionException e) {
+ throw new DebugException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID,
+ DebugException.REQUEST_FAILED, "Error in shutting down debugger launch " + launch, e)); //$NON-NLS-1$
+ }
+ }
+
+ if (!forDebug) {
+ // just running, so go ahead and shutdown the session
+ edcLaunch.shutdownSession(new RequestMonitor(ImmediateExecutor.getInstance(), null));
+ }
+
+ edcLaunch.setLaunching(false);
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Wait for a sequence to finish, periodically checking whether
+ * it has been cancelled.
+ * @param sequence
+ * @param monitor
+ * @return the value of the sequence
+ * @throws ExecutionException
+ * @throws InterruptedException
+ * @throws CancellationException
+ */
+ private Object getOrCancelSequence(Sequence sequence,
+ IProgressMonitor monitor) throws InterruptedException, ExecutionException {
+ while (!monitor.isCanceled()) {
+ try {
+ return sequence.get(1, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ // fine, keep looping
+ }
+ }
+ // cancelled
+ sequence.cancel(true); /* flag is ignored */
+ throw new CancellationException();
+ }
+
+ @Override
+ public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException {
+ // Need to configure the source locator before creating the launch
+ // because once the launch is created and added to launch manager,
+ // the adapters will be created for the whole session, including
+ // the source lookup adapter.
+
+ EDCLaunch launch = findExistingLaunch(configuration, mode);
+ if (launch == null)
+ {
+ launch = createLaunch(configuration, mode);
+ launch.initialize();
+ launch.setSourceLocator(launch.createSourceLocator());
+ }
+ else
+ {
+ launch.addAffiliatedLaunchConfiguration(configuration);
+ launch.setFirstLaunch(false);
+ }
+
+ launch.setActiveLaunchConfiguration(configuration);
+
+ return launch;
+ }
+
+ /**
+ * @since 2.0
+ */
+ abstract public EDCLaunch createLaunch(ILaunchConfiguration configuration,
+ String mode);
+
+ private EDCLaunch findExistingLaunch(ILaunchConfiguration configuration,
+ String mode) {
+ if (!SnapshotUtils.isSnapshotLaunchConfig(configuration)) // Snapshot launches never join existing ones.
+ {
+ ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
+ List<ILaunch> launchList = Arrays.asList(manager.getLaunches());
+
+ for (ILaunch iLaunch : launchList) {
+ if (!iLaunch.isTerminated() && iLaunch instanceof EDCLaunch)
+ {
+ EDCLaunch edcLaunch = (EDCLaunch) iLaunch;
+ // The launch may be in the process of terminating. Test for that but
+ // first synchronize around the launch so it can't begin termination
+ // while we are checking here.
+
+ if (DsfSession.isSessionActive(edcLaunch.getSession().getId())
+ && isSameTarget(edcLaunch, configuration, mode))
+ {
+ if (edcLaunch.isTerminating())
+ this.waitForLaunchToTerminate(edcLaunch);
+ synchronized (edcLaunch)
+ {
+ if (!edcLaunch.isTerminating())
+ {
+ edcLaunch.setLaunching(true);
+ return edcLaunch;
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private void waitForLaunchToTerminate(final EDCLaunch edcLaunch) {
+
+ Job waitForTerminate = new Job("Waiting for " + edcLaunch.getDescription() + " to terminate") {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ monitor.beginTask("Waiting for termination",
+ IProgressMonitor.UNKNOWN);
+ try {
+ while (!edcLaunch.isTerminated()) {
+ Thread.sleep(500);
+ if (monitor.isCanceled())
+ return Status.CANCEL_STATUS;
+ monitor.worked(1);
+ }
+ } catch (InterruptedException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ finally {
+ monitor.done();
+ }
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ waitForTerminate.schedule();
+ try {
+ waitForTerminate.join();
+ } catch (InterruptedException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ }
+
+ abstract public String getDebugModelID();
+
+ abstract protected Sequence getLiveLaunchSequence(DsfExecutor executor, EDCLaunch launch, IProgressMonitor pm);
+
+ protected Sequence getSnapshotLaunchSequence(DsfExecutor executor, EDCLaunch launch, IProgressMonitor pm) {
+ return new SnapshotLaunchSequence(executor, launch, pm);
+ };
+
+ protected Sequence getFinalLaunchSequence(DsfExecutor executor, EDCLaunch launch, IProgressMonitor pm) {
+ if (launch.isSnapshotLaunch())
+ return getSnapshotLaunchSequence(executor, launch, pm);
+ else
+ return getLiveLaunchSequence(executor, launch, pm);
+ };
+
+ abstract protected IDsfDebugServicesFactory newServiceFactory();
+
+
+ /**
+ * @param existingLaunch
+ * @since 2.0
+ */
+ abstract protected boolean isSameTarget(EDCLaunch existingLaunch, ILaunchConfiguration configuration, String mode);
+
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.launch;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.acpm.AvailableFormatsRequestCache;
+import org.eclipse.cdt.debug.edc.acpm.FormatedExpressionValueRequestCache;
+import org.eclipse.cdt.debug.edc.acpm.MemoryRangeCache;
+import org.eclipse.cdt.debug.edc.acpm.RegistersByNameRequestCache;
+import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
+
+/**
+ * Interface for an ACPM cache manager used within EDC. The cache manager is
+ * responsible for dishing out cache objects and managing their lifetimes.
+ * <p>
+ * In theory, every method of every available DSF service could be given an ACPM
+ * front end, making this a massive interface. In practice, though, ACPM is used
+ * selectively for situations where coding using asynchronous APIS becomes
+ * unwieldy. This interface will attempt to provide access to the service
+ * methods likely to be used in those situations. This interface will grow in
+ * time and as such is marked @noimplement and @noextend so the expansion can be
+ * done without breaking backward compatibility.
+ *
+ * TODO: extensibility; allow EDC adopters to integrate custom services
+ *
+ * @since 2.0
+ * @noimplement
+ * @noextend
+ */
+@ConfinedToDsfExecutor("") // any executor
+public interface ICacheManager {
+
+ /** See {@link IFormattedValues#getFormattedExpressionValue(FormattedValueDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)} */
+ public FormatedExpressionValueRequestCache getFormattedExpressionValue(IFormattedValues service, FormattedValueDMContext dmc);
+
+ /** See {@link IFormattedValues#getAvailableFormats(IFormattedDataDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)} */
+ public AvailableFormatsRequestCache getAvailableFormats(IFormattedValues service, IFormattedDataDMContext dmc);
+
+ /** Returns the collection of all available registers for the given context, indexed by name */
+ public RegistersByNameRequestCache getRegistersByName(IDMContext dmc);
+
+ /**
+ * Returns a range cache that can be used to get any range of memory
+ * relative to [address].
+ * {@link IMemory#getMemory(IMemoryDMContext, IAddress, long, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)}
+ */
+ public MemoryRangeCache getMemory(IMemoryDMContext dmc, IAddress address, int wordSize);
+
+ /**
+ * ACPM transactions that use this cache manager interface are required to
+ * start the transaction by calling this method, and then calling
+ * {@link #endTransaction(boolean)} at the end of the attempt (successful or
+ * not). The manager relies on this for tracking cache objects used in the
+ * current transaction (only one can run at any one time since ACPM
+ * transactions run on the DSF thread). That information is used in managing
+ * the lifetime of cache objects.
+ *
+ * <pre>
+ * class MyTransaction<Boolean> {
+ * protected Boolean process() throws InvalidCacheException, CoreException {
+ * boolean invalidCache = false;
+ * ICacheManager cacheMgr = ...
+ * cacheMgr.beginTransaction();
+ * try {
+ * Boolean result = false;
+ * // ... transaction logic
+ * return result
+ * }
+ * catch (InvalidCacheException exc) {
+ * invalidCache = true;
+ * throw exc;
+ * }
+ * finally {
+ * cacheMgr.endTransaction(invalidCache);
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ */
+ public void beginTransaction();
+
+ /**
+ * See {@link #beginTransaction()}
+ *
+ * @param failedInvalidCache
+ * indicates whether the transaction failed due to an invalid
+ * cache exception. One that fails in that way will run again
+ * once the required invalid cache objects become valid; it's an
+ * "ongoing" transaction. It's imperative for the cache manager
+ * to not discard cache objects involved in ongoing transactions.
+ */
+ public void endTransaction(boolean failedInvalidCache);
+
+ /**
+ * Tells the cache manager to discard all cache objects.
+ */
+ public void purgeAll();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+/**
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @since 2.0
+ */
+public interface IEDCLaunchConfigurationConstants {
+
+ public static final String ATTR_ALBUM_FILE = "org.eclipse.cdt.debug.edc.internal.launch.snapshotAlbum"; //$NON-NLS-1$
+
+ public static final String ATTR_USE_REMOTE_PEERS = "org.eclipse.cdt.debug.edc.useRemotePeers"; //$NON-NLS-1$
+
+ /**
+ * @since 2.0
+ */
+ public static final String ATTR_ATTACH_CONTEXT_NAME = "org.eclipse.cdt.debug.edc.attachContextName"; //$NON-NLS-1$
+
+ /**
+ * @since 2.0
+ */
+ public static final String ATTR_IS_ONE_USE = "org.eclipse.cdt.debug.edc.isOneUse"; //$NON-NLS-1$
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class LaunchMessages {
+
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.edc.launch.LaunchMessages";//$NON-NLS-1$
+
+ private static ResourceBundle RESOURCE_BUNDLE = null;
+
+ static {
+ try {
+ RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
+ } catch (MissingResourceException x) {
+ }
+ }
+
+ private LaunchMessages() {
+ }
+
+ public static String getFormattedString(String key, String arg) {
+ return MessageFormat.format(getString(key), new Object[] { arg });
+ }
+
+ public static String getFormattedString(String key, String[] args) {
+ return MessageFormat.format(getString(key), (Object[]) args);
+ }
+
+ public static String getString(String key) {
+ if (RESOURCE_BUNDLE == null)
+ return '!' + key + '!';
+ return RESOURCE_BUNDLE.getString(key);
+ }
+}
--- /dev/null
+###############################################################################
+# Copyright (c) 2008, 2009 QNX Software 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:
+# QNX Software Systems - Initial API and implementation
+# Monta Vista - Joanne Woo - Bug 87556
+# Nokia - Ken Ryall - Bug 118894
+# Nokia - Ling Wang - tailored for EDC
+###############################################################################
+
+LaunchUtils.C_Project_not_specified=C Project not specified
+LaunchUtils.Not_a_C_CPP_project=Project is not a C/C++ project
+LaunchUtils.Program_file_not_specified=Program file not specified
+LaunchUtils.Program_file_does_not_exist=Program file does not exist
+LaunchUtils.PROGRAM_PATH_not_found={0} not found
+LaunchUtils.Project_NAME_does_not_exist=Project {0} does not exist
+LaunchUtils.Project_NAME_is_closed=Project {0} is closed
+
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.launch;
+
+import java.io.FileNotFoundException;
+import java.util.Map;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.model.ICProject;
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.variables.IStringVariableManager;
+import org.eclipse.core.variables.VariablesPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+
+public class LaunchUtils {
+
+ /**
+ * Verify the following things about the project: - is a valid project name
+ * given - does the project exist - is the project open - is the project a
+ * C/C++ project
+ */
+ public static ICProject verifyCProject(ILaunchConfiguration configuration) throws CoreException {
+ String name = getProjectName(configuration);
+ if (name == null) {
+ abort(LaunchMessages.getString("LaunchUtils.C_Project_not_specified"), null, //$NON-NLS-1$
+ ICDTLaunchConfigurationConstants.ERR_UNSPECIFIED_PROJECT);
+ return null;
+ }
+ ICProject cproject = getCProject(configuration);
+ if (cproject == null && name.length() > 0) {
+ IProject proj = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
+ if (!proj.exists()) {
+ abort(LaunchMessages.getFormattedString("LaunchUtils.Project_NAME_does_not_exist", name), null, //$NON-NLS-1$
+ ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT);
+ } else if (!proj.isOpen()) {
+ abort(LaunchMessages.getFormattedString("LaunchUtils.Project_NAME_is_closed", name), null, //$NON-NLS-1$
+ ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT);
+ }
+ abort(LaunchMessages.getString("LaunchUtils.Not_a_C_CPP_project"), null, //$NON-NLS-1$
+ ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT);
+ }
+ return cproject;
+ }
+
+ /**
+ * Verify that program name of the configuration can be found as a file.
+ *
+ * @return Absolute path of the program location
+ */
+ public static IPath verifyProgramPath(ILaunchConfiguration configuration, ICProject cproject) throws CoreException {
+ // Note this assumes CDT launch configuration main tab is used.
+ String programName = configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
+ (String) null);
+ if (programName == null) {
+ abort(LaunchMessages.getString("LaunchUtils.Program_file_not_specified"), null, //$NON-NLS-1$
+ ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT);
+ }
+
+ IPath programPath = new Path(programName);
+ if (programPath.isEmpty()) {
+ abort(LaunchMessages.getString("LaunchUtils.Program_file_does_not_exist"), null, //$NON-NLS-1$
+ ICDTLaunchConfigurationConstants.ERR_NOT_A_C_PROJECT);
+ }
+
+ if (!programPath.isAbsolute() && cproject != null) {
+ // Find the specified program within the specified project
+ IFile wsProgramPath = cproject.getProject().getFile(programPath);
+ programPath = wsProgramPath.getLocation();
+ }
+
+ if (!programPath.toFile().exists()) {
+ abort(LaunchMessages.getString("LaunchUtils.Program_file_does_not_exist"), //$NON-NLS-1$
+ new FileNotFoundException(LaunchMessages.getFormattedString("LaunchUtils.PROGRAM_PATH_not_found", //$NON-NLS-1$
+ programPath.toOSString())), ICDTLaunchConfigurationConstants.ERR_PROGRAM_NOT_EXIST);
+ }
+
+ return programPath;
+ }
+
+ /**
+ * Throws a core exception with an error status object built from the given
+ * message, lower level exception, and error code.
+ *
+ * @param message
+ * the status message
+ * @param exception
+ * lower level exception associated with the error, or
+ * <code>null</code> if none
+ * @param code
+ * error code
+ */
+ private static void abort(String message, Throwable exception, int code) throws CoreException {
+ MultiStatus status = new MultiStatus(EDCDebugger.getUniqueIdentifier(), code, message, exception);
+ status.add(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), code,
+ exception == null ? "" : exception.getLocalizedMessage(), //$NON-NLS-1$
+ exception));
+ throw new CoreException(status);
+ }
+
+ /**
+ * Returns an ICProject based on the project name provided in the
+ * configuration. First look for a project by name, and then confirm it is a
+ * C/C++ project.
+ */
+ public static ICProject getCProject(ILaunchConfiguration configuration) throws CoreException {
+ String projectName = getProjectName(configuration);
+ if (projectName != null) {
+ projectName = projectName.trim();
+ if (projectName.length() > 0) {
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ ICProject cProject = CCorePlugin.getDefault().getCoreModel().create(project);
+ if (cProject != null && cProject.exists()) {
+ return cProject;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String getProjectName(ILaunchConfiguration configuration) throws CoreException {
+ return configuration.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
+ }
+
+ /**
+ * Convenience method.
+ */
+ public static IStringVariableManager getStringVariableManager() {
+ return VariablesPlugin.getDefault().getStringVariableManager();
+ }
+
+ public static String getWorkingDirectoryPath(ILaunchConfiguration config) throws CoreException {
+ String location = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY, "");
+ if (location != null) {
+ String expandedLocation = LaunchUtils.getStringVariableManager().performStringSubstitution(location);
+ if (expandedLocation.length() > 0) {
+ return expandedLocation;
+ }
+ }
+ return "";
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static String[] getProgramArgumentsArray(ILaunchConfiguration config) throws CoreException {
+ return org.eclipse.cdt.launch.LaunchUtils.getProgramArgumentsArray(config);
+ }
+
+ /**
+ * @since 2.0
+ */
+ @SuppressWarnings("unchecked")
+ public static Map<String, String> getEnvironmentVariables(ILaunchConfiguration config) throws CoreException {
+ return config.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map<?,?>) null);
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.snapshot.Album;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.service.AbstractDsfService;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This is abstract DSF service with some APIs specific to EDC.
+ */
+public abstract class AbstractEDCService extends AbstractDsfService implements IEDCService {
+
+ private final String[] classNames;
+ private ITargetEnvironment targetEnvironmentService = null;
+ private final boolean snapshot;
+ private EDCServicesTracker fEDCTracker;
+
+ public AbstractEDCService(DsfSession session, String[] classNames) {
+ super(session);
+ this.classNames = classNames;
+ this.snapshot = Album.isSnapshotSession(session.getId());
+ }
+
+ public boolean isSnapshot() {
+ return snapshot;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public EDCServicesTracker getEDCServicesTracker() {
+ return fEDCTracker;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public <V> V getService(Class<V> serviceClass) {
+ if (IEDCService.class.isAssignableFrom(serviceClass))
+ return fEDCTracker.getService(serviceClass);
+ return getServicesTracker().getService(serviceClass);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.service.AbstractDsfService#initialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ @Override
+ public void initialize(final RequestMonitor requestMonitor) {
+ fEDCTracker = new EDCServicesTracker(getBundleContext(), getSession().getId());
+ super.initialize(new RequestMonitor(getExecutor(), requestMonitor) {
+ @Override
+ public void handleSuccess() {
+ doInitialize(requestMonitor);
+ }
+ });
+ }
+
+ @Override
+ public void shutdown(RequestMonitor rm) {
+ fEDCTracker.dispose();
+ fEDCTracker = null;
+ super.shutdown(rm);
+ }
+
+ protected void doInitialize(RequestMonitor requestMonitor) {
+ register(classNames, new Hashtable<String, String>());
+
+ if (targetEnvironmentService == null)
+ targetEnvironmentService = getServicesTracker().getService(ITargetEnvironment.class);
+
+ requestMonitor.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.service.AbstractDsfService#getBundleContext()
+ */
+ @Override
+ protected BundleContext getBundleContext() {
+ return EDCDebugger.getBundleContext();
+ }
+
+ public ITargetEnvironment getTargetEnvironmentService() {
+ return targetEnvironmentService;
+ }
+
+ /**
+ * Add implicit service class names
+ *
+ * @param classNames
+ * the class names our derivative gave us. Must not be null, but
+ * can be empty.
+ * @param implicitClassNames
+ * the implicit class names that should be specified. Must not be
+ * null, but can be empty.
+ * @return a new collection of class names that is [classNames] plus any
+ * missing implicit ones. The implicit ones will appear first in the
+ * list
+ */
+ /*package*/ static String[] massageClassNames(String[] classNames, String[] implicitClassNames) {
+ List<String> newClassNames = new ArrayList<String>(Arrays.asList(implicitClassNames));
+ for (String className : classNames) {
+ if (!newClassNames.contains(className)) {
+ newClassNames.add(className);
+ }
+ }
+ return newClassNames.toArray(new String[newClassNames.size()]);
+ }
+
+ /**
+ * EDC services can use this method to execute blocking <i>thread-safe</i>
+ * code without blocking the DSF thread. The runnable is exercised on a
+ * separate thread obtained from a thread pool. This mechanism was
+ * introduced to allow an EDC service's logic to avoid bogging down the DSF
+ * thread in cases where it cannot reasonably avoid making a blocking call.
+ * One example is when a TCF service has to be invoked synchronously (the
+ * calling thread waits for the request monitor to complete). Such things
+ * should not be done on the DSF thread since that thread is meant to be
+ * readily available to handle a queue of requests, much like a UI thread
+ * is.
+ *
+ * In the event of an uncaught exception in the given code, the request
+ * monitor is automatically completed and given an error status.
+ *
+ * @throws RejectedExecutionException
+ * if the thread pool has been overwhelmed and given code cannot
+ * be scheduled to run
+ *
+ * @since 2.0
+ */
+ protected void asyncExec(Runnable runnable, RequestMonitor rm) {
+ try {
+ ExecutorService executor = EDCLaunch.getThreadPool(getSession().getId());
+ if (executor.isShutdown())
+ {
+ rm.setStatus(new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, "Session has been shutdown.", null));
+ rm.done();
+ }
+ else
+ executor.execute(new SafeRunner(runnable, rm));
+ }
+ catch (RejectedExecutionException exc) {
+ // See EDCLaunch.newThreadPool()
+ String msg = Messages.AbstractEDCService_0;
+ EDCDebugger.getMessageLogger().log(IStatus.WARNING, msg, exc);
+ rm.setStatus(new Status(Status.ERROR, EDCDebugger.PLUGIN_ID, msg, exc));
+ rm.done();
+ throw exc;
+ }
+ }
+
+ /**
+ * A safe runner used by {@link AbstractEDCService#asyncExec(Runnable)} to
+ * ensure an uncaught exception does not leave the request monitor hanging.
+ */
+ private class SafeRunner implements Runnable {
+ private Runnable fCode;
+ private RequestMonitor fRm;
+
+ SafeRunner(Runnable code, RequestMonitor rm) {
+ fCode = code;
+ fRm = rm;
+ Assert.isNotNull(code);
+ }
+
+ public void run() {
+ try {
+ fCode.run();
+ } catch (Exception e) {
+ handleException(fCode, e);
+ } catch (LinkageError e) {
+ handleException(fCode, e);
+ } catch (AssertionError e) {
+ handleException(fCode, e);
+ }
+ }
+
+ private void handleException(Runnable code, Throwable e) {
+ IStatus status;
+ if (!(e instanceof OperationCanceledException)) {
+ // try to obtain the correct plug-in id for the bundle providing the safe runnable
+ if (e instanceof CoreException) {
+ status = new MultiStatus(EDCDebugger.PLUGIN_ID, -1, Messages.AbstractEDCService_1, e);
+ ((MultiStatus)status).merge(((CoreException) e).getStatus());
+ } else {
+ status = new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, -1, Messages.AbstractEDCService_2, e);
+ }
+ EDCDebugger.getMessageLogger().log(status);
+ }
+ else {
+ status = new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, -1, Messages.AbstractEDCService_3, e);
+ }
+ if (fRm != null) {
+ fRm.setStatus(status);
+ fRm.done();
+ }
+ }
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+
+/**
+ * Common base for ITargetEnvironment services.
+ *
+ */
+public abstract class AbstractTargetEnvironment extends AbstractEDCService implements ITargetEnvironment {
+ private ILaunch launch;
+
+ /**
+ * @param session
+ * @param classNames
+ * the type names the service will be registered under. See
+ * AbstractDsfService#register for details. We tack on
+ * ITargetEnvironment if not provided.
+ *
+ * @param launch
+ * must be non-null
+ */
+ public AbstractTargetEnvironment(DsfSession session, String[] classNames, ILaunch launch) {
+ super(session,
+ massageClassNames(classNames, new String[] {ITargetEnvironment.class.getName()}));
+ assert launch != null;
+ this.launch = launch;
+ }
+
+ public ILaunchConfiguration getLaunchConfiguration() {
+ return launch.getLaunchConfiguration();
+ }
+
+ public ILaunch getLaunch() {
+ return launch;
+ }
+
+ /*
+ * This implementation works for most CDT debug sessions.<br> If your
+ * debugger is using different preference UI, please override this method.
+ */
+ public String getStartupStopAtPoint() {
+ String ret = null;
+ try {
+ if (getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN,
+ false)) {
+ ret = getLaunchConfiguration().getAttribute(
+ ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL,
+ ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT);
+ }
+ } catch (CoreException e) {
+ // ignore
+ }
+
+ return ret;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public boolean needStartupBreakpointInExecutable(String exeName) {
+ // By default EDC will try to install startup breakpoint in
+ // any loaded module until it succeeds.
+ return true;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.service.IDsfService;
+
+public abstract class DMContext extends AbstractDMContext implements IEDCDMContext {
+
+ protected Map<String, Object> properties = Collections.synchronizedMap(new HashMap<String, Object>());
+ private String id;
+
+ public DMContext(IDsfService service, IDMContext[] parents, String name, String id) {
+ super(service, parents);
+ properties.put(PROP_NAME, name);
+ properties.put(PROP_ID, id);
+ this.id = id;
+ }
+
+ public DMContext(String sessionId, IDMContext[] parents, String id) {
+ super(sessionId, parents);
+ properties.put(PROP_NAME, id);
+ properties.put(PROP_ID, id);
+ this.id = id;
+ }
+
+ public DMContext(IDsfService service, IDMContext[] parents, String id, Map<String, Object> props) {
+ super(service, parents);
+ if (props != null) {
+ properties.putAll(props);
+ this.id = id;
+ properties.put(PROP_ID, id);
+ }
+ }
+
+ public DMContext(String sessionId, IDMContext[] parents, Map<String, Object> props) {
+ super(sessionId, parents);
+ if (props != null) {
+ properties.putAll(props);
+ id = (String) properties.get(PROP_ID);
+ }
+ }
+
+ public DMContext(IDsfService service, IDMContext[] parents, Map<String, Object> props) {
+ super(service, parents);
+ if (props != null) {
+ properties.putAll(props);
+ id = (String) properties.get(PROP_ID);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCDMContext#getProperty(java.lang.String)
+ */
+ public Object getProperty(String key) {
+ synchronized (properties) {
+ return properties.get(key);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCDMContext#getProperties()
+ */
+ public Map<String, Object> getProperties() {
+ Map<String, Object> result = new HashMap<String, Object>();
+ synchronized (properties) {
+ result.putAll(properties);
+ }
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCDMContext#getName()
+ */
+ public String getName() {
+ return (String) getProperty(PROP_NAME);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCDMContext#setName(java.lang.String)
+ */
+ public void setName(String name) {
+ synchronized (properties) {
+ properties.put(PROP_NAME, name);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCDMContext#setProperty(java.lang.String, java.lang.Object)
+ */
+ public void setProperty(String name, Object object) {
+ synchronized (properties) {
+ properties.put(name, object);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.internal.services.dsf.IEDCDMContext#getID()
+ */
+ public String getID() {
+ return id;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof DMContext)
+ return super.baseEquals(obj) && this.getID().equals(((IEDCDMContext) obj).getID());
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.baseHashCode() ^ getID().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("DMContext [");
+ if (id != null) {
+ builder.append("id=");
+ builder.append(id);
+ builder.append(", ");
+ }
+ if (properties != null) {
+ builder.append("properties=");
+ builder.append(properties);
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.disassembler.CodeBufferUnderflowException;
+import org.eclipse.cdt.debug.edc.disassembler.DisassembledInstruction;
+import org.eclipse.cdt.debug.edc.disassembler.EDCInstruction;
+import org.eclipse.cdt.debug.edc.disassembler.EDCInstructionFunctionInfo;
+import org.eclipse.cdt.debug.edc.disassembler.EDCMixedInstruction;
+import org.eclipse.cdt.debug.edc.disassembler.IDisassembledInstruction;
+import org.eclipse.cdt.debug.edc.disassembler.IDisassembler;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.EDCServicesMessages;
+import org.eclipse.cdt.debug.edc.launch.EDCLaunch;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+import org.eclipse.cdt.dsf.datamodel.DMContexts;
+import org.eclipse.cdt.dsf.debug.service.IDisassembly;
+import org.eclipse.cdt.dsf.debug.service.IInstruction;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
+import org.eclipse.cdt.dsf.debug.service.IMixedInstruction;
+import org.eclipse.cdt.dsf.debug.service.IModules;
+import org.eclipse.cdt.dsf.debug.service.IModules.AddressRange;
+import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
+import org.eclipse.cdt.dsf.service.DsfServicesTracker;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.model.MemoryByte;
+import org.eclipse.tm.tcf.services.IMemory.MemoryError;
+
+
+public class Disassembly extends AbstractEDCService implements IDisassembly {
+
+ /**
+ * @param classNames
+ * the type names the service will be registered under. See
+ * AbstractDsfService#register for details. We tack on base DSF's
+ * IDisassembly and this class to the list if not provided.
+ * @since 2.0
+ */
+ public Disassembly(DsfSession session, String[] classNames) {
+ super(session,
+ massageClassNames(classNames,
+ new String[] { IDisassembly.class.getName(), Disassembly.class.getName() }));
+ }
+
+ /**
+ * @return IStatus.ERROR containing NLS string indicating disassembler not yet available
+ * @since 2.0
+ */
+ public static IStatus statusNoDisassembler() {
+ return new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED,
+ EDCServicesMessages.Disassembly_NoDisassemblerYet, null);
+ }
+
+ /**
+ * @param memoryAt 1st location of unreadable memory
+ * @param memoryLength length of unreadable memory
+ * @return IStatus.ERROR containing formatted message with location & length
+ */
+ private static IStatus statusCannotReadMemory(String memoryAt, Integer memoryLength) {
+ return new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED,
+ cantReadMemory(memoryAt, memoryLength.toString()), null);
+ }
+
+ /**
+ * string for use in both status for error return and in
+ * pseudo-instruction used in error recovery mode for disassembly view
+ * @param memoryAt 1st location of unreadable memory
+ * @param memoryLength length of unreadable memory (incoming null will "unknown" for length)
+ * @return formatted message with location & length
+ */
+ private static String cantReadMemory(String memoryAt, String memoryLength) {
+ memoryLength = (memoryLength == null) ? "unknown" : memoryLength;
+ return MessageFormat.format(EDCServicesMessages.Disassembly_CannotReadMemoryAt,
+ memoryAt, memoryLength);
+ }
+
+ /**
+ * check each byte of incoming MemoryByte[] array to see if readable;
+ * fills the RequestMonitor status in case of unreadable bytes.
+ * @param memBytes data to translate
+ * @param start address of first byte of memBytes
+ * @param rm with which to set status in case of error
+ * @return code buffer translated from incoming memByte array, null if any are unreadable
+ * @since 2.0
+ */
+ public static ByteBuffer translateMemoryBytes(MemoryByte[] memBytes,
+ IAddress start, RequestMonitor rm) {
+ byte[] bytes = new byte[memBytes.length];
+ for (int i = 0; i < memBytes.length; i++) {
+ // check each byte
+ if (!memBytes[i].isReadable()) {
+ rm.setStatus(statusCannotReadMemory(start.add(i).toHexAddressString(),
+ memBytes.length-i));
+ rm.done();
+ return null;
+ }
+ bytes[i] = memBytes[i].getValue();
+ }
+ return ByteBuffer.wrap(bytes);
+ }
+
+ /**
+ * return a buffer of instruction code whose readability matches the
+ * first byte of the data for as long as all such bytes are the same
+ * @param memBytes data to translate
+ * @return a code buffer either full of readable bytes
+ * or empty representing the unreadable region
+ */
+ private ByteBuffer translateMemoryBytes(List<MemoryByte> memBytes) {
+ byte[] bytes = new byte[memBytes.size()];
+ boolean firstIsReadable = memBytes.get(0).isReadable();
+ int count = 0;
+ for (MemoryByte memByte : memBytes) {
+ // check each byte
+ if (memByte.isReadable() != firstIsReadable)
+ break;
+ bytes[count++] = firstIsReadable ? memByte.getValue() : 0;
+ }
+
+ return ByteBuffer.wrap(bytes, 0, count);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.math.BigInteger, java.math.BigInteger, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getInstructions(final IDisassemblyDMContext context, BigInteger startAddress, BigInteger endAddress,
+ final DataRequestMonitor<IInstruction[]> drm) {
+
+ // FIXME: ignoring null startAddress and null endAddress semantics
+
+ ITargetEnvironment env = getTargetEnvironmentService();
+ final IDisassembler disassembler = (env != null) ? env.getDisassembler() : null;
+ if (disassembler == null) {
+ drm.setStatus(statusNoDisassembler());
+ drm.done();
+ return;
+ }
+
+ DsfServicesTracker services = getServicesTracker();
+ if (services == null) // could be null if async invoked as or after debug session ends
+ return;
+
+ IMemory memoryService = services.getService(IMemory.class);
+ if (memoryService == null) // could be null if async invoked as or after debug session ends
+ return;
+
+ final int size = endAddress.intValue() - startAddress.intValue() + 16;
+
+ final IMemoryDMContext mem_dmc = DMContexts.getAncestorOfType(context, IMemoryDMContext.class);
+ final IAddress start = new Addr64(startAddress);
+
+ memoryService.getMemory(mem_dmc, start, 0, 1, size,
+ new DataRequestMonitor<MemoryByte[]>(getExecutor(), drm) {
+ /**
+ * overridden to create a non-error status plus pseudoInstruction data-set
+ * in the DRM requested by DisassemblyBackendDsf, where a DRM.status of
+ * ERROR is turned into an "invalid" block in its document map. Such
+ * blocks are repeatedly re-requested ... causing scrolling oddities & performance issues.
+ * @see failedMemoryDsfPseudoInstructions
+ */
+ @Override
+ protected void handleError() {
+ IStatus s = getStatus();
+ Throwable e = s.getException();
+ if (e instanceof MemoryError && s.getMessage().contains("Fail to read memory")) {
+ drm.setData(failedMemoryDsfPseudoInstructions(start, size, s.getMessage()));
+ drm.done();
+ } else {
+ super.handleError();
+ }
+
+ }
+
+ @Override
+ protected void handleSuccess() {
+ List<MemoryByte> memBytes = Arrays.asList(getData());
+ Map<String, Object> options = new HashMap<String, Object>();
+ try {
+ ArrayList<IInstruction> instrs
+ = fillDisassemblyViewInstructions(memBytes, start, context,
+ disassembler, options);
+ drm.setData(instrs.toArray(new IInstruction[instrs.size()]));
+ } catch (CoreException e) {
+ drm.setStatus(e.getStatus());
+ }
+ drm.done();
+ }
+ });
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.lang.String, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getInstructions(final IDisassemblyDMContext context, String filename, int linenum, final int lines,
+ final DataRequestMonitor<IInstruction[]> drm) {
+
+ // FIXME: ignoring "lines" semantics
+
+ IModules modulesService = getServicesTracker().getService(IModules.class);
+
+ ISymbolDMContext sym_dmc = DMContexts.getAncestorOfType(context, ISymbolDMContext.class);
+
+ filename = EDCLaunch.getLaunchForSession(getSession().getId()).getCompilationPath(filename);
+
+ modulesService.calcAddressInfo(sym_dmc, filename, linenum, 0,
+ new DataRequestMonitor<AddressRange[]>(getExecutor(), drm) {
+ @Override
+ protected void handleSuccess() {
+ AddressRange[] addr_ranges = getData();
+
+ IAddress start = addr_ranges[0].getStartAddress();
+ IAddress end = start.add(lines * 4); // kind of arbitrary end
+ // address hint.
+
+ getInstructions(context, start.getValue(), end.getValue(), drm);
+ }
+ });
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getMixedInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.math.BigInteger, java.math.BigInteger, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getMixedInstructions(final IDisassemblyDMContext context, BigInteger startAddress, BigInteger endAddress,
+ final DataRequestMonitor<IMixedInstruction[]> drm) {
+
+ // FIXME: ignoring null startAddress and null endAddress semantics
+
+ DsfServicesTracker services = getServicesTracker();
+ if (services == null) // could be null if async invoked as or after debug session ends
+ return;
+
+ IEDCSymbols symbolsService = services.getService(IEDCSymbols.class);
+ if (symbolsService == null) // could be null if async invoked as or after debug session ends
+ return;
+
+ IEDCModules modulesService = services.getService(IEDCModules.class);
+ if (modulesService == null) // could be null if async invoked as or after debug session ends
+ return;
+
+ final ISymbolDMContext sym_dmc = DMContexts.getAncestorOfType(context, ISymbolDMContext.class);
+
+ // These are absolute runtime addresses.
+ final IAddress end = new Addr64(endAddress);
+ final IAddress start
+ = getStartAddressForLineEntryContainingAddress(symbolsService, modulesService, sym_dmc,
+ new Addr64(startAddress), end);
+
+ ILineEntry startEntry = symbolsService.getLineEntryForAddress(sym_dmc, start);
+
+ if (startEntry == null) {
+ // startAddress has no source
+ getInstructions(context, startAddress, endAddress,
+ new DataRequestMonitor<IInstruction[]>(getExecutor(), drm) {
+ @Override
+ protected void handleSuccess() {
+ IMixedInstruction[] ret = new IMixedInstruction[1];
+ ret[0] = new EDCMixedInstruction("unknown", 0, getData()); //$NON-NLS-1$
+ drm.setData(ret);
+ drm.done();
+ }
+ });
+ } else { // there is source for start address.
+
+ final IEDCModuleDMContext module = modulesService.getModuleByAddress(sym_dmc, start);
+ final List<ILineEntry> codeLines = symbolsService.getLineEntriesForAddressRange(sym_dmc, start, end);
+
+ getInstructions(context, startAddress, endAddress,
+ new DataRequestMonitor<IInstruction[]>(getExecutor(), drm) {
+ @Override
+ protected void handleSuccess() {
+ List<IMixedInstruction> mixedInstructions = new ArrayList<IMixedInstruction>();
+
+ mixSource(mixedInstructions, null, module, codeLines, getData());
+
+ drm.setData(mixedInstructions.toArray(new IMixedInstruction[mixedInstructions.size()]));
+ drm.done();
+ }
+ });
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getMixedInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.lang.String, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getMixedInstructions(final IDisassemblyDMContext context, String filename, final int linenum,
+ final int lines, final DataRequestMonitor<IMixedInstruction[]> drm) {
+
+ // FIXME: ignoring "lines" semantics
+
+ final ITargetEnvironment env = getTargetEnvironmentService();
+ final IDisassembler disassembler = (env != null) ? env.getDisassembler() : null;
+ if (disassembler == null) {
+ drm.setStatus(statusNoDisassembler());
+ drm.done();
+ return;
+ }
+
+ IModules modulesService = getServicesTracker().getService(IModules.class);
+
+ ISymbolDMContext sym_dmc = DMContexts.getAncestorOfType(context, ISymbolDMContext.class);
+
+ filename = EDCLaunch.getLaunchForSession(getSession().getId()).getCompilationPath(filename);
+
+ modulesService.calcAddressInfo(sym_dmc, filename, linenum, 0,
+ new DataRequestMonitor<AddressRange[]>(getExecutor(), drm) {
+ @Override
+ protected void handleSuccess() {
+ AddressRange[] addr_ranges = getData();
+
+ IAddress start = addr_ranges[0].getStartAddress();
+ IAddress end = start.add(lines * 4); // kind of arbitrary end address hint.
+ getMixedInstructions(context, start.getValue(), end.getValue(), drm);
+ }
+ });
+ }
+
+ private static EDCInstruction pseudoInstruction(IAddress address, int size, String pseudoMnemonic) {
+ DisassembledInstruction pseudoInstruction = new DisassembledInstruction();
+ pseudoInstruction.setAddress(address);
+ pseudoInstruction.setSize(size);
+ pseudoInstruction.setMnemonics(pseudoMnemonic);
+ return new EDCInstruction(pseudoInstruction);
+ }
+
+ /**
+ * used in failedMemoryDsfPseudoInstructions() below as chunk boundary for
+ * each of the pseudoInstructions in a larger chunk of retrieved memory.
+ */
+ private static final int asmFence = 0x20;
+
+ /**
+ * this utility function creates pseudo-mnemonics indicating failed memory
+ * read. it was refactored from memoryService.getMemory().handleError() so
+ * that it could be utilized by subclass override getInstructions() methods
+ * making the same memoryService.getMemory() call.
+ * <p>
+ * <i>background:</i>
+ * <p>
+ * as of 2010.oct.01, EDC memoryService no longer caches blocks of memory that
+ * cannot be read (a correct change, given that this was blocking the caching
+ * of good memory on the boundaries of such blocks, causing other problems).
+ * <p>
+ * when this change was made, Disassembly#fillDisassemblyViewInstructions()
+ * stopped getting reached through memoryService.getMemory().handleSuccess() .
+ * therefore, actual bad blocks of memory were not getting filled with pseudo
+ * mnemonics indicating bad memory . in other words, when "Fail to read memory"
+ * errors from TCF caused invocation of memoryService.getMemory().handleFailure()
+ * ... and thus eventually also handleError() ... the result was that
+ * DsfBackendDisassembly would fill the DisassemblyDocument with "invalid"
+ * sections based upon address but no size. it's algorithm then later attempts
+ * to fill any missing/invalid sections corresponding with the document. this
+ * results in a visual anomaly where "Unable to retrieve disassembly" would be
+ * populated in the DisassemblyView one block at a time, until there would be a
+ * large, mostly useless portion of the document view populated with the same
+ * message repeated once for every byte the user had attempted to scroll to.
+ * <p>
+ * the handleError() override implementations that call this utility function
+ * solve the problem whereby DisassemblyBackendDsf interprets DRM.status as
+ * "invalid" sections in its the DisassemblyDocument it is associated with.
+ * <p>
+ * the point of this re-factored code is to create "fences" on regular
+ * boundaries so that chunks of failed memory always get placed on similar
+ * boundaries, thus drastically ameliorating the occurrence of small "invalid"
+ * chunks in the DisassemblyDocument map between chunks of pseudoInstructions
+ * that DisassemblyBackendDsf considers "valid".
+ * <p>
+ * in user terms, this means that scrolling in the view is more consistent
+ * and even, with better performance thanks to fewer attempts to re-retrieve
+ * memory for small "invalid" sections in its map at the boundaries of
+ * previously inserted pseudo-instructions.
+ *
+ * @param size size of the chunk to break up
+ * @param start location of memory chunk
+ * @param msg message from the target agent
+ * @return array containing 1 or more pseudo-instructions, mostly on asmFence boundaries
+ * @since 2.0
+ */
+ protected static IInstruction[] failedMemoryDsfPseudoInstructions(
+ IAddress start, final int size, String msg) {
+ ArrayList<IInstruction> pseudoInstr = new ArrayList<IInstruction>();
+ int offset = 0, chunkSize = Math.min(size, asmFence - start.getValue().intValue() % asmFence);
+ do {
+ pseudoInstr.add(pseudoInstruction(start.add(offset), chunkSize,
+ msg + "..[length=" + chunkSize + ']'));
+ offset += chunkSize;
+ chunkSize = Math.min(asmFence, size-offset);
+ } while (offset < size);
+ return pseudoInstr.toArray(new IInstruction[pseudoInstr.size()]);
+ }
+
+ /**
+ * Creates the array of instructions to be used to fill the disassembly view.
+ * for a range containing any unreadable instructions, it will create a
+ * fake instruction consisting of the address, the entire unreadable range,
+ * and a message to fill in the mnemonics section about the unreadable range.
+ * @param memBytes the buffer containing the bytes to be disassembled
+ * @param start starting address corresponding to the buffer
+ * @param context context for disassembly
+ * @param disassembler the disassembler object to use
+ * @param options to be passed to the disassembleInstructions() call
+ * @return array of IInstruction
+ * @throws CoreException can be thrown by disassembleInstructions().
+ * @since 2.0
+ */
+ protected ArrayList<IInstruction> fillDisassemblyViewInstructions(
+ final List<MemoryByte> memBytes, final IAddress start,
+ final IDisassemblyDMContext context, final IDisassembler disassembler,
+ Map<String, Object> options)
+ throws CoreException {
+ ArrayList<IInstruction> ret = new ArrayList<IInstruction>();
+ for (int offset = 0, last = memBytes.size(); offset < last ;) {
+ ByteBuffer codeBuf = translateMemoryBytes(memBytes.subList(offset, last));
+ int codeBufSize = codeBuf.limit();
+ IAddress block = start.add(offset);
+ if (memBytes.get(offset).isReadable()) {
+ try {
+ List<IDisassembledInstruction> insts
+ = disassembler.disassembleInstructions(block, block.add(codeBufSize),
+ codeBuf, options, context);
+ if (insts.size() == 0)
+ break;
+ for (int i = 0; i < insts.size(); i++) {
+ ret.add(new EDCInstruction(insts.get(i)));
+ offset += insts.get(i).getSize();
+ }
+ } catch (CodeBufferUnderflowException e) {
+ if (offset == 0 && codeBufSize == last) {
+ // nothing in entire block can be disassembled;
+ // at least tell the Disassembly view code this much
+ ret.add(pseudoInstruction(start, codeBufSize,
+ "Buffer Underflow during disassembly")); //$NON-NLS-1$
+ }
+ offset += codeBufSize;
+ }
+ } else { // this will only occur when the target supports partial bad blocks
+ ret.add(pseudoInstruction(block, codeBufSize,
+ cantReadMemory(block.toHexAddressString(),
+ ((Integer)codeBufSize).toString())));
+ offset += codeBufSize;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * whereas the default implementation of getMixedInstructions() gets and
+ * processes all the disassembly in one pass, some variants are known to need
+ * to override the default implementation to break the retrieval into chunks.
+ * <p>
+ * this portion of the mixing is still the same within those chunks, though,
+ * and so has been extracted and protected for use by extending variant classes.
+ * @param mixedInstructions the growing list of instructions being processed
+ * @param wholeFunctionName name for whole function; if null, will be calculated per line
+ * @param module the module for this block of code
+ * @param codeLines list of ILineEntry containing info for mixing
+ * @param instructions the instructions to be mixed with the source
+ * @since 2.0
+ */
+ protected void mixSource(List<IMixedInstruction> mixedInstructions,
+ final EDCInstructionFunctionInfo wholeBlockInfo,
+ final IEDCModuleDMContext module,
+ final List<ILineEntry> codeLines,
+ final IInstruction[] instructions) {
+
+ List<IInstruction> instsForLine = new ArrayList<IInstruction>();
+
+ IPath filePath = null;
+ String osString = null;
+
+ EDCInstructionFunctionInfo functionInfo = wholeBlockInfo;
+ int k = 0, instsCnt = instructions.length, lineCnt = codeLines.size();
+ for (int i = 0; i < lineCnt && k < instsCnt; i++) {
+ // Now map the instructions to source lines to generate
+ // MixedInstructions.
+ instsForLine.clear();
+ ILineEntry line = codeLines.get(i);
+
+ if (wholeBlockInfo == null && module != null) {
+ functionInfo = new EDCInstructionFunctionInfo(module, line);
+ }
+
+ while (k < instsCnt) {
+ EDCInstruction inst = (EDCInstruction)instructions[k];
+ IAddress linkAddress = module.toLinkAddress(new Addr64(inst.getAdress()));
+ if (linkAddress.compareTo(line.getHighAddress()) >= 0)
+ break;
+
+ if (functionInfo != null) {
+ inst.setFunctionName(functionInfo.getFunctionName());
+ IAddress functionBase = functionInfo.getFunctionStartAddress();
+ if (functionBase != null) {
+ inst.setOffset(functionBase.distanceTo(linkAddress).intValue());
+ }
+ }
+ instsForLine.add(inst);
+ k++;
+ }
+ if (line.getFilePath() != filePath) {
+ filePath = line.getFilePath();
+ osString = (filePath != null) ? filePath.toOSString() : null;
+ }
+ mixedInstructions.add(new EDCMixedInstruction(osString, line.getLineNumber(),
+ instsForLine.toArray(new IInstruction[instsForLine.size()])));
+ }
+ }
+
+ /**
+ * disassembly utility function to find the first address for a line-entry
+ * for an address contained by that line-entry
+ * @param symbolsService
+ * @param modulesService
+ * @param sym_dmc symbol context used to retrieve LineEntry for address
+ * @param initialStartAddress the address of interest
+ * @param endAddress the last address of a range to search
+ * @return the first address of an associated LineEntry if it can be found, else the initialStartAddress
+ * @since 2.0
+ */
+ protected static IAddress getStartAddressForLineEntryContainingAddress(final IEDCSymbols symbolsService,
+ final IEDCModules modulesService, final ISymbolDMContext sym_dmc, final IAddress initialStartAddress,
+ final IAddress endAddress) {
+ assert symbolsService != null && modulesService != null;
+ if (symbolsService == null || modulesService == null)
+ return initialStartAddress;
+
+ if (sym_dmc != null) {
+ // in case the caller requested a start that falls somewhere other than the
+ // boundary of an instruction, back up to that boundary for the first instruction
+ if (symbolsService.getLineEntryForAddress(sym_dmc, initialStartAddress) != null) {
+ IEDCModuleDMContext module = modulesService.getModuleByAddress(sym_dmc, initialStartAddress);
+ if (module != null) {
+ List<ILineEntry> allLines
+ = symbolsService.getLineEntriesForAddressRange(sym_dmc, initialStartAddress, endAddress);
+ if (allLines != null && !allLines.isEmpty())
+ {
+ return module.toRuntimeAddress(allLines.get(0).getLowAddress());
+ }
+ }
+ }
+ }
+ return initialStartAddress;
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 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.debug.edc.services;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.dsf.service.IDsfService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Convenience class to help track DSF services that a given
+ * client needs to use. This class is based on the DsfServicesTracker
+ * but is designed to be thread safe so clients can use it to get
+ * a service reference from any thread. This is important for EDC
+ * services because they are not restricted to the Dsf thread.
+ *
+ * @since 2.0
+ */
+public class EDCServicesTracker {
+
+ private static String getServiceFilter(String sessionId) {
+ return ("(" + IDsfService.PROP_SESSION_ID + "=" + sessionId + ")").intern(); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ final private static class ServiceKey
+ {
+ private final String fClassName;
+ private final String fFilter;
+ private final int fHashCode;
+ private final String fHashString;
+
+
+ public ServiceKey(Class<?> clazz, String filter) {
+ fClassName = clazz != null ? clazz.getName() : null;
+ fFilter = filter;
+ fHashString = 'C' + (fClassName == null ? "" : fClassName) + //$NON-NLS-1$
+ 'F' + (fFilter == null ? "" : fFilter); //$NON-NLS-1$
+ fHashCode = fHashString.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // hashcodes are not guaranteed to be unique, but objects that are equal must have the same hashcode
+ // thus we can optimize by first comparing hashcodes
+ return other instanceof ServiceKey &&
+ ((((ServiceKey)other).fHashCode == this.fHashCode) && (((ServiceKey)other).fHashString.equals(this.fHashString)));
+ }
+
+ @Override
+ public int hashCode() {
+ return fHashCode;
+ }
+ }
+
+ private final String fSessionId;
+ private volatile boolean fDisposed = false;
+ private final BundleContext fBundleContext;
+
+ @SuppressWarnings("rawtypes")
+ private final Map<ServiceKey,ServiceReference> fServiceReferences = Collections.synchronizedMap(new HashMap<ServiceKey,ServiceReference>());
+ @SuppressWarnings("rawtypes")
+ private final Map<ServiceReference,Object> fServices = Collections.synchronizedMap(new HashMap<ServiceReference,Object>());
+ private final String fServiceFilter;
+
+ private final ServiceListener fListner = new ServiceListener() {
+ public void serviceChanged(final ServiceEvent event) {
+ // Only listen to unregister events.
+ if (event.getType() != ServiceEvent.UNREGISTERING) {
+ return;
+ }
+
+ // If session is not active anymore, just exit. The tracker should
+ // soon be disposed.
+ DsfSession session = DsfSession.getSession(fSessionId);
+ if (session == null) {
+ return;
+ }
+
+ handleUnregisterEvent(event);
+ }
+ };
+
+ @SuppressWarnings("rawtypes")
+ private void handleUnregisterEvent(ServiceEvent event) {
+ synchronized (fServiceReferences)
+ {
+ for (Iterator<Map.Entry<ServiceKey, ServiceReference>> itr = fServiceReferences.entrySet().iterator(); itr.hasNext();) {
+ Map.Entry<ServiceKey, ServiceReference> entry = itr.next();
+ if ( entry.getValue().equals(event.getServiceReference()) ) {
+ itr.remove();
+ }
+ }
+ if (fServices.remove(event.getServiceReference()) != null) {
+ fBundleContext.ungetService(event.getServiceReference());
+ }
+ }
+ }
+
+ /**
+ * Only constructor.
+ * @param bundleContext Context of the plugin that the client lives in.
+ * @param sessionId The DSF session that this tracker will be used for.
+ */
+ public EDCServicesTracker(BundleContext bundleContext, String sessionId) {
+ fSessionId = sessionId;
+ fBundleContext = bundleContext;
+ fServiceFilter = getServiceFilter(sessionId);
+ try {
+ fBundleContext.addServiceListener(fListner, fServiceFilter);
+ } catch (InvalidSyntaxException e) {
+ assert false : "Invalid session ID syntax"; //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Retrieves a service reference for given service class and optional filter.
+ * Filter should be used if there are multiple instances of the desired service
+ * running within the same session.
+ * @param serviceClass class of the desired service
+ * @param custom filter to use when searching for the service, this filter will
+ * be used instead of the standard filter so it should also specify the desired
+ * session-ID
+ * @return OSGI service reference object to the desired service, null if not found
+ */
+ @SuppressWarnings("rawtypes")
+ public ServiceReference getServiceReference(Class serviceClass, String filter) {
+ if (fDisposed) {
+ return null;
+ }
+
+ // If the session is not active, all of its services are gone.
+ DsfSession session = DsfSession.getSession(fSessionId);
+ if (session == null) {
+ return null;
+ }
+
+ ServiceKey key = new ServiceKey(serviceClass, filter != null ? filter : fServiceFilter);
+ if (fServiceReferences.containsKey(key)) {
+ return fServiceReferences.get(key);
+ }
+
+ try {
+ ServiceReference[] references = fBundleContext.getServiceReferences(key.fClassName, key.fFilter);
+ assert references == null || references.length <= 1;
+ if (references == null || references.length == 0) {
+ return null;
+ } else {
+ fServiceReferences.put(key, references[0]);
+ return references[0];
+ }
+ } catch(InvalidSyntaxException e) {
+ assert false : "Invalid session ID syntax"; //$NON-NLS-1$
+ } catch(IllegalStateException e) {
+ // Can occur when plugin is shutting down.
+ }
+ return null;
+ }
+
+ /**
+ * Convenience class to retrieve a service based on class name only.
+ * @param serviceClass class of the desired service
+ * @return instance of the desired service, null if not found
+ */
+ public <V> V getService(Class<V> serviceClass) {
+ return getService(serviceClass, null);
+ }
+
+ /**
+ * Retrieves the service given service class and optional filter.
+ * Filter should be used if there are multiple instances of the desired service
+ * running within the same session.
+ * @param serviceClass class of the desired service
+ * @param custom filter to use when searching for the service, this filter will
+ * be used instead of the standard filter so it should also specify the desired
+ * session-ID
+ * @return instance of the desired service, null if not found
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public <V> V getService(Class<V> serviceClass, String filter) {
+ ServiceReference serviceRef = getServiceReference(serviceClass, filter);
+ if (serviceRef == null) {
+ return null;
+ } else {
+ if (fServices.containsKey(serviceRef)) {
+ return (V)fServices.get(serviceRef);
+ } else {
+ V service = (V)fBundleContext.getService(serviceRef);
+ fServices.put(serviceRef, service);
+ return service;
+ }
+ }
+ }
+
+ /**
+ * Un-gets all the references held by this tracker. Must be called
+ * to avoid leaking OSGI service references.
+ */
+ public void dispose() {
+ assert !fDisposed;
+ fDisposed = true;
+ doDispose();
+ }
+
+ @SuppressWarnings("rawtypes")
+ private void doDispose() {
+ synchronized (fServices)
+ {
+ try {
+ fBundleContext.removeServiceListener(fListner);
+ for (Iterator<ServiceReference> itr = fServices.keySet().iterator(); itr.hasNext();) {
+ fBundleContext.ungetService(itr.next());
+ }
+ } catch (IllegalStateException e) {
+ // May be thrown during shutdown (bug 293049).
+ }
+ }
+ fServices.clear();
+ fServiceReferences.clear();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ assert fDisposed;
+ super.finalize();
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.dsf.service.IDsfService;
+import org.eclipse.tm.tcf.protocol.IService;
+
+/**
+ * This is used to link a TCF service to the DSF service that needs it. Objects
+ * that implement {@link IDsfService} using TCF should implement this as well.
+ *
+ * @author LWang
+ *
+ */
+public interface IDSFServiceUsingTCF {
+
+ /**
+ * Tells this DSF service that the TCF service it uses is ready for action.
+ *
+ * @param service the TCF service
+ */
+ public void tcfServiceReady(IService service);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.util.Map;
+
+public interface IEDCDMContext {
+
+ /**
+ * Context property description.
+ */
+ public static final String PROP_DESCRIPTION = "Description";
+
+ /**
+ * Context property id.
+ */
+ public static final String PROP_ID = "ID";
+
+ /**
+ * Context property name.
+ */
+ public static final String PROP_NAME = "Name";
+
+ /**
+ * Context property value.
+ */
+ public static final String PROP_VALUE = "Value";
+
+ public Object getProperty(String key);
+
+ public Map<String, Object> getProperties();
+
+ public String getName();
+
+ public void setName(String name);
+
+ public void setProperty(String name, Object object);
+
+ public String getID();
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
+import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
+
+public interface IEDCExecutionDMC extends IExecutionDMContext,IMemoryDMContext, IEDCDMContext {
+
+ public boolean isSuspended();
+
+ public ISymbolDMContext getSymbolDMContext();
+
+ /**
+ * Does the context (usually a thread) want to be auto-selected/focused in
+ * Eclipse Debug View on suspend ? When this is true, EDC will try to honor
+ * it, but not guaranteed. If multiple contexts ask for focus, EDC will
+ * choose one based on some other standards. See where this is invoked for
+ * more.
+ *
+ * @since 2.0
+ */
+ public boolean wantFocusInUI();
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.cdt.debug.edc.symbols.IType;
+import org.eclipse.cdt.debug.edc.symbols.IVariableLocation;
+import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
+import org.eclipse.cdt.dsf.debug.service.IExpressions2.CastInfo;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext;
+import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+import org.eclipse.core.runtime.IStatus;
+
+public interface IEDCExpression extends IExpressionDMContext, IEDCDMContext {
+
+ public Executor getExecutor();
+ /**
+ * @since 2.0
+ */
+ public IEDCExpressions getExpressionsService();
+
+ /**
+ * Change the name of the expression that appears in the Variables view Name
+ * column or the Expressions view Expression column. This is typically
+ * used to make the subexpressions of an expression show only the
+ * suffix of the expression relative to the parent expression and
+ * to differentiates children from each other (though it is not intended
+ * to have any syntactic significance when catenated to the parent).
+ *
+ * Note: {@link #getExpression()} is always the full expression.
+ */
+ public void setName(String name);
+
+ public IFrameDMContext getFrame();
+
+ public void evaluateExpression();
+
+ public FormattedValueDMData getFormattedValue(FormattedValueDMContext dmc);
+
+ public IVariableLocation getValueLocation();
+
+ /** Get error during evaluation, or <code>null</code> if no error, or {@link #evaluateExpression()} has not been called */
+ public IStatus getEvaluationError();
+ /** Get numeric value after {@link #evaluateExpression()} */
+ public Number getEvaluatedValue();
+ /** Get string value after {@link #evaluateExpression()}, or the string value for
+ * a string expression, or the formatted value set by {@link #setEvaluatedValueString(String)} */
+ public String getEvaluatedValueString();
+
+ public void setEvaluatedValue(Number value);
+ public void setEvaluatedValueString(String string);
+
+ public IVariableLocation getEvaluatedLocation();
+
+ public IType getEvaluatedType();
+
+ public String getTypeName();
+
+ public boolean hasChildren();
+
+ /** Get any casting in effect. May be <code>null</code>. */
+ public CastInfo getCastInfo();
+
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.dsf.debug.service.IExpressions2;
+
+/**
+ * Interface to the EDC Expressions service.
+ * @since 2.0
+ */
+public interface IEDCExpressions extends IExpressions2 {
+
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.util.ArrayList;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.dsf.debug.service.IMemory;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.core.model.MemoryByte;
+
+/**
+ * Interface to the EDC memory service. Used for synchronous access to
+ * target memory.
+ */
+public interface IEDCMemory extends IMemory, IEDCService {
+
+ /**
+ * Fills a buffer with the contents of a range of memory.
+ *
+ * @param context the executable context
+ * @param address the starting address of the range of memory
+ * @param memBuffer the buffer to be filled with the memory contents
+ * @param count the number of words to read
+ * @param word_size the word_size
+ * @return status of the memory request
+ */
+ public IStatus getMemory(IEDCExecutionDMC context, IAddress address,
+ final ArrayList<MemoryByte> memBuffer, int count, int word_size);
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
+import org.eclipse.cdt.dsf.debug.service.IModules.IModuleDMContext;
+
+public interface IEDCModuleDMContext extends IModuleDMContext, IEDCDMContext {
+
+ /**
+ * Convert link address to runtime address.
+ *
+ * @param linkAddress
+ * @return null if the given link address is not in the module.
+ */
+ public IAddress toRuntimeAddress(IAddress linkAddress);
+
+ /**
+ * Convert runtime address to link address.
+ *
+ * @param runtimeAddress
+ * @return null if the given runtime address is not in the module.
+ */
+ public IAddress toLinkAddress(IAddress runtimeAddress);
+
+ /**
+ * Gets the symbol reader used to read symbols for this module.
+ *
+ * @return the symbol reader
+ */
+ public IEDCSymbolReader getSymbolReader();
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
+
+public interface IEDCModules extends IEDCService {
+
+ /**
+ * get module that contains the given runtime address.
+ *
+ * @param symCtxs
+ * @param instructionAddress
+ * runtime absolute address.
+ * @return null if not found.
+ */
+ public IEDCModuleDMContext getModuleByAddress(ISymbolDMContext symCtx,
+ IAddress instructionAddress);
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.dsf.service.IDsfService;
+
+/**
+ * @since 2.0
+ */
+public interface IEDCService extends IDsfService {
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
+
+public interface IEDCSymbols extends IEDCService {
+ /**
+ * Preference to show all variables that are defined as global by the current source file
+ */
+ public static final String SHOW_ALL_VARIABLES_ENABLED = "show_all_variables_enabled";
+
+ /**
+ * Get the function at the given runtime address
+ *
+ * @param context
+ * the context
+ * @param runtimeAddress
+ * the runtime address
+ * @return the function containing the given address, or null if none found
+ */
+ public IFunctionScope getFunctionAtAddress(ISymbolDMContext context,
+ IAddress runtimeAddress);
+
+
+ /**
+ * Get the name of any arbitrary symbol at the given runtime address
+ *
+ * @param context
+ * the context
+ * @param runtimeAddress
+ * the runtime address
+ * @return the name of the symbol the given address, or null if none found
+ * @since 2.0
+ */
+ public String getSymbolNameAtAddress(ISymbolDMContext context,
+ IAddress runtimeAddress);
+
+ /**
+ * Get the line entry at the given runtime address
+ *
+ * @param context
+ * the context
+ * @param runtimeAddress
+ * the runtime address
+ * @return the line entry for the given address, or null if none found
+ */
+ public ILineEntry getLineEntryForAddress(ISymbolDMContext context,
+ IAddress runtimeAddress);
+
+ /**
+ * <p>
+ * Get source line entries with code that are between the given start and
+ * end startAddress.
+ * <p>
+ * This method is created mainly for supporting disassembly service.
+ *
+ * @param context
+ * @param start
+ * start runtime address
+ * @param end
+ * end runtime address (exclusive).
+ * @return list of source line entries which may or may not be in the same
+ * source file (note that even one compile unit may have code from
+ * different source files). It's empty if the start address has no
+ * source line.
+ */
+ public List<ILineEntry> getLineEntriesForAddressRange(
+ ISymbolDMContext context, IAddress start, IAddress end);
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Provide access to reading values of registers from other stack frames.
+ */
+public interface IFrameRegisterProvider {
+
+ /**
+ * Get the registers available at the given frame and address.
+ * @param session
+ * @param tracker
+ * @param context the frame
+ * @return {@link IFrameRegisters} or <code>null</code> if no information found
+ * @throws CoreException if fatal error handling the symbolics for the frame
+ * @since 2.0
+ */
+ IFrameRegisters getFrameRegisters(DsfSession session, EDCServicesTracker tracker, IFrameDMContext context) throws CoreException;
+
+ void dispose();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.services;
+
+import java.math.BigInteger;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Provide values of registers which may be in other stack frames.
+ * This instance is only valid for a given stack trace, since the current
+ * PC at each stack level is used to precisely determine the state of
+ * stored registers.
+ */
+public interface IFrameRegisters {
+ /**
+ * Get the value of the register.
+ * @param regnum common register #
+ * @param bytes size of register to read (starting from least significant byte)
+ * @return value, never <code>null</code>
+ * @throws CoreException if cannot read
+ */
+ BigInteger getRegister(int regnum, int bytes) throws CoreException;
+
+ /**
+ * Write the value into the register or its location in the frame.
+ * @param regnum <code>int</code> common register #
+ * @param bytes <code>int</code> size of register to write (starting from least significant byte)
+ * @param value <code>BigInteger</code>
+ * @throws CoreException if cannot write
+ */
+ void writeRegister(int regnum, int bytes, BigInteger value) throws CoreException;
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.cdt.dsf.service.IDsfService;
+
+public interface ISnapshots extends IDsfService {
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.services;
+
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.IAddressExpressionEvaluator;
+import org.eclipse.cdt.debug.edc.disassembler.IDisassembler;
+import org.eclipse.cdt.debug.edc.tcf.extension.services.ISimpleRegisters;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.cdt.dsf.service.IDsfService;
+import org.eclipse.tm.tcf.services.IRegisters;
+
+/**
+ * DSF service that provides data peculiar to the environment the debugger is
+ * targeting. The environment here includes such things as target hardware, OS
+ * if any, and UI preferences for the debugger. <br>
+ * This service is supposed to be called by other DSF services so that they can
+ * be target independent as much as possible.
+ */
+public interface ITargetEnvironment extends IDsfService {
+
+ public final static String ARCH_X86 = "x86";
+ public final static String ARCH_ARM = "ARM";
+
+ public final static String OS_UNKNOWN = "unknown";
+ public final static String OS_WIN32 = "win32";
+ public final static String OS_LINUX = "linux";
+ public final static String OS_SYMBIAN = "symbian";
+
+ /**
+ * Get the string name of the target system architecture.
+ *
+ * @return string name which is one of the predefined ARCH_xx string in
+ * {@link ITargetEnvironment}. Cannot be null.
+ */
+ public String getArchitecture();
+
+ /**
+ * Get the string name of the target operating system (if any).
+ *
+ * @return string name which is one of the predefined OS_xx string in
+ * {@link ITargetEnvironment}. Cannot be null.
+ * {@link ITargetEnvironment#OS_UNKNOWN} if the OS is unknown. Empty
+ * string if there is no OS running.
+ */
+ public String getOS();
+
+ /**
+ * Get sizes of all basic C/C++ data types.
+ *
+ * @return list of sizes, in bytes, of C/C++ data types. The returned map
+ * should have TypeUtils#BASIC_TYPE_XXX constants for its keys, and
+ * type sizes for their values
+ */
+ public Map<Integer, Integer> getBasicTypeSizes();
+
+ /**
+ * Get size of pointer data type
+ *
+ * @return list of sizes, in bytes, of C/C++ data types
+ */
+ public int getPointerSize();
+
+ /**
+ * Get size of an enumeration data type
+ *
+ * @return list of sizes, in bytes, of C/C++ data types
+ */
+ public int getEnumSize();
+
+ /**
+ * Get whether plain "char" is signed
+ *
+ * @return whether "plain" char is signed
+ */
+ public boolean isCharSigned();
+
+ /**
+ * Get ID of program counter (PC) register. E.g. for X86, it could be "EIP". <br>
+ * <br>
+ * (This is temporarily needed with our current TCF {@link ISimpleRegisters}
+ * service. After we implement the TCF {@link IRegisters} service, this can
+ * be removed.)
+ *
+ * @return string representation of the register ID.
+ */
+ public String getPCRegisterID();
+
+ /**
+ * Get length in bytes of the longest instruction in target architecture.
+ * This return value does not have to be precise, but must be larger than
+ * size of the longest instruction.
+ *
+ * @return
+ */
+ public int getLongestInstructionLength();
+
+ /**
+ * Get breakpoint instruction that is used to set software breakpoint.<br>
+ * <br>
+ * For architecture like x86 the breakpoint instruction is invariant
+ * ("int 3" or "0xcc") thus the arguments are ignored. But for processor
+ * like ARM the instruction varies depending on processor mode (ARM or
+ * THUMB) in give context.
+ *
+ * @param context
+ * the runtime context, usually a process.
+ * @param address
+ * runtime absolute address where to set a breakpoint.
+ *
+ * @return byte array of the instruction.
+ */
+ public byte[] getBreakpointInstruction(IDMContext context, IAddress address);
+
+ /**
+ * Allows for modification or addition of target specific breakpoint
+ * properties before breakpoints are set. Note that this applies to TCF
+ * breakpoint service breakpoints.
+ *
+ * @param context
+ * the runtime context, usually a process.
+ * @param address
+ * runtime absolute address where to set a breakpoint.
+ * @param properties
+ * properties map
+ */
+ public void updateBreakpointProperties(IDMContext context, IAddress address, Map<String, Object> properties);
+
+ /**
+ * Is the target processor in little-endian ?
+ *
+ * @param context
+ * context for which the check is based on. For most cases this
+ * argument can be ignored.
+ * @return
+ */
+ public boolean isLittleEndian(IDMContext context);
+
+ /**
+ * Get disassembler for the target.
+ *
+ * @return {@link IDisassembler} object. Can be null which means
+ * disassembler is not implemented yet for the target.
+ */
+ public IDisassembler getDisassembler();
+
+ /**
+ * Get address expression evaluator for the target.
+ *
+ * @return {@link IAddressExpressionEvaluator} object. null means it's not
+ * available yet for the target.
+ */
+ public IAddressExpressionEvaluator getAddressExpressionEvaluator();
+
+ /**
+ * In some target environments, user may specify two or more executables
+ * (including things like DLL) to debug in one debug session. This API
+ * allows the target environment to tell EDC in which executable(s) it wants
+ * EDC to try to set startup breakpoint.
+ *
+ * @param exeName
+ * a name with or without path.
+ * @return true if the environment wants EDC to try setting startup
+ * breakpoint in the executable. False otherwise.
+ * @since 2.0
+ */
+ public boolean needStartupBreakpointInExecutable(String exeName);
+
+ /**
+ * Get minimum size of a memory block that is read and stored in memory
+ * cache. For instance, if the size is 64 bytes, then for a memory read
+ * request that requests 4 bytes, the debugger will read and cache 64 bytes.
+ * Different targets may prefer different size.
+ *
+ * @return size in bytes. zero (0) means to just cache the number of bytes
+ * actually requested.
+ */
+ public int getMemoryCacheMinimumBlockSize();
+
+ /**
+ * Get value of any given property.<br>
+ * <br>
+ * This generic API allows getting any new target property without adding
+ * new API that breaks backward compatibility.
+ *
+ * @param propertyKey
+ * @return
+ */
+ public String getProperty(String propertyKey);
+}
--- /dev/null
+package org.eclipse.cdt.debug.edc.services;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @since 2.0
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.edc.services.messages"; //$NON-NLS-1$
+ public static String AbstractEDCService_0;
+ public static String AbstractEDCService_1;
+ public static String AbstractEDCService_2;
+ public static String AbstractEDCService_3;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.cdt.debug.edc.MemoryUtils;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.NumberFormatUtils;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExecutionDMC;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.SuspendedEvent;
+import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotUtils;
+import org.eclipse.cdt.debug.edc.services.Stack.StackFrameDMC;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+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.IFormattedValues;
+import org.eclipse.cdt.dsf.debug.service.IRegisters;
+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.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tm.tcf.protocol.IService;
+import org.eclipse.tm.tcf.protocol.IToken;
+import org.eclipse.tm.tcf.services.IRegisters.RegistersContext;
+import org.eclipse.tm.tcf.util.TCFTask;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * The Registers service provides information about the target processor
+ * registers.
+ */
+public abstract class Registers extends AbstractEDCService implements IRegisters, ICachingService, IDSFServiceUsingTCF {
+
+ /**
+ * Cache register groups per context.
+ * Keyed on context ID.
+ */
+ private Map<String, List<RegisterGroupDMC>> registerGroupsPerContext =
+ Collections.synchronizedMap(new HashMap<String, List<RegisterGroupDMC>>());
+
+ /** The TCF registers service. */
+ protected org.eclipse.tm.tcf.services.IRegisters tcfRegistersService = null;
+
+ /**
+ * Register value cache per execution context.
+ * Keyed on context ID.
+ */
+ private Map<String, Map<String, BigInteger>> registerValueCache =
+ Collections.synchronizedMap(new HashMap<String, Map<String, BigInteger>>());
+
+ /** Iimeout value in milliseconds when waiting for a response from the TCF service. */
+ private long tcfTimeout;
+
+ /**
+ * A hex string indicating error in register read.
+ * See where this is used for more.
+ */
+ protected static final String REGISTER_VALUE_ERROR = "badbadba";
+
+ public static final String PROP_EXECUTION_CONTEXT_ID = "Context_ID";
+
+ private static final String REGISTER = "register";
+
+ /**
+ * Represents a group of registers.
+ */
+ public class RegisterGroupDMC extends DMContext implements IRegisterGroupDMContext, ISnapshotContributor {
+
+ private static final String REGISTER_GROUP = "register_group";
+
+ /** The registers in this group. */
+ private List<RegisterDMC> registers = Collections.synchronizedList(new ArrayList<RegisterDMC>());
+
+ /** The executable context. */
+ private final IEDCExecutionDMC exeContext;
+
+ /**
+ * Instantiates a new register group dmc.
+ *
+ * @param service the service
+ * @param executionDMC the execution context
+ * @param groupName the group name
+ * @param groupDescription the group description
+ * @param groupID the group id
+ */
+ public RegisterGroupDMC(Registers service, IEDCExecutionDMC executionDMC, String groupName, String groupDescription,
+ String groupID) {
+ super(service, new IDMContext[] { executionDMC }, groupName, groupID);
+ exeContext = executionDMC;
+ properties.put(PROP_DESCRIPTION, groupDescription);
+ properties.put(PROP_EXECUTION_CONTEXT_ID, executionDMC.getID());
+ }
+
+ /**
+ * Instantiates a new register group dmc.
+ *
+ * @param service the service
+ * @param executionDmc the execution dmc
+ * @param props the props
+ */
+ public RegisterGroupDMC(Registers service, IEDCExecutionDMC executionDmc,
+ Map<String, Object> props) {
+ super(service, new IDMContext[] { executionDmc }, props);
+ exeContext = executionDmc;
+ properties.put(PROP_EXECUTION_CONTEXT_ID, exeContext.getID());
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.DMContext#toString()
+ */
+ @Override
+ public String toString() {
+ return baseToString() + ".group[" + getName() + "]";} //$NON-NLS-1$ //$NON-NLS-2$
+
+ /**
+ * Gets the registers for this group.
+ *
+ * @return array of register contexts for this group
+ * @throws CoreException the core exception
+ */
+ public RegisterDMC[] getRegisters() throws CoreException {
+ RegisterDMC[] result = new RegisterDMC[0];
+ synchronized (registers) {
+ if (registers.size() == 0) {
+ registers = Registers.this.createRegistersForGroup(this);
+ }
+ result = registers.toArray(new RegisterDMC[registers.size()]);
+ }
+ return result;
+ }
+
+ /**
+ * Take a snapshot of this group of registers.
+ *
+ * @param album the snapshot album
+ * @param document the XML document
+ * @param monitor the progress monitor
+ * @return the XML element
+ * @throws Exception the exception if anything goes wrong
+ * @since 2.0
+ */
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor)throws Exception {
+ Element contextElement = document.createElement(REGISTER_GROUP);
+ contextElement.setAttribute(PROP_ID, this.getID());
+
+ Element propsElement = SnapshotUtils.makeXMLFromProperties(document, getProperties());
+ contextElement.appendChild(propsElement);
+
+ RegisterDMC[] allRegisters = getRegisters();
+ SubMonitor progress = SubMonitor.convert(monitor, allRegisters.length * 1000);
+ progress.subTask("Registers");
+ for (RegisterDMC registerDMC : allRegisters) {
+ Element dmcElement = registerDMC.takeSnapshot(album, document, progress.newChild(1000));
+ contextElement.appendChild(dmcElement);
+ }
+ return contextElement;
+ }
+
+ /**
+ * Gets the execution dmc.
+ *
+ * @return the execution dmc
+ */
+ public IEDCExecutionDMC getExecutionDMC() {
+ return exeContext;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor#loadSnapshot(org.w3c.dom.Element)
+ */
+ public void loadSnapshot(Element element) throws Exception {
+ NodeList registerElement = element.getElementsByTagName(REGISTER);
+
+ int numRegisters = registerElement.getLength();
+ for (int i = 0; i < numRegisters; i++) {
+ Element regElement = (Element) registerElement.item(i);
+ Element propElement = (Element) regElement.getElementsByTagName(SnapshotUtils.PROPERTIES).item(0);
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ SnapshotUtils.initializeFromXML(propElement, properties);
+
+ RegisterDMC regdmc = new RegisterDMC(this, exeContext, properties);
+ regdmc.loadSnapshot(regElement);
+ registers.add(regdmc);
+ }
+
+ }
+
+ }
+
+ /**
+ * Represents the context for a single register.
+ */
+ public class RegisterDMC extends DMContext implements IRegisterDMContext, ISnapshotContributor {
+
+ /** The context used by the TCF agent. */
+ private org.eclipse.tm.tcf.services.IRegisters.RegistersContext tcfContext = null;
+
+ /**
+ * Instantiates a new register dmc.
+ *
+ * @param executableDMC the executable context
+ * @param name the register name
+ * @param description the register description
+ * @param id the register id
+ */
+ public RegisterDMC(IEDCExecutionDMC executableDMC, String name, String description, String id) {
+ super(Registers.this, new IDMContext[] { executableDMC }, name, id);
+ properties.put(PROP_EXECUTION_CONTEXT_ID, executableDMC.getID());
+ }
+
+ /**
+ * Instantiates a new register dmc.
+ *
+ * @param registerGroupDmc the register group dmc
+ * @param executableDMC the executable context
+ * @param properties the properties
+ */
+ public RegisterDMC(RegisterGroupDMC registerGroupDmc, IEDCExecutionDMC executableDMC,
+ Map<String, Object> properties) {
+ super(Registers.this, new IDMContext[] { executableDMC }, properties);
+ this.properties.put(PROP_EXECUTION_CONTEXT_ID, executableDMC.getID());
+ }
+
+ /**
+ * Construct based on underlying context from TCF IRegisters service.
+ *
+ * @param registerGroupDMC the register group dmc
+ * @param executableDMC the executable context
+ * @param tcfContext the tcf context
+ */
+ public RegisterDMC(RegisterGroupDMC registerGroupDMC, IEDCExecutionDMC executableDMC, RegistersContext tcfContext) {
+ super(Registers.this, new IDMContext[] { registerGroupDMC }, tcfContext.getProperties());
+ this.properties.put(PROP_EXECUTION_CONTEXT_ID, executableDMC.getID());
+
+ this.tcfContext = tcfContext;
+ }
+
+ /**
+ * Get the underlying TCF context.
+ * @return may be null.
+ */
+ public RegistersContext getTCFContext() {
+ return tcfContext;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.DMContext#toString()
+ */
+ @Override
+ public String toString() {
+ return baseToString() + ".register[" + getName() + "]";} //$NON-NLS-1$ //$NON-NLS-2$
+
+ /**
+ * Take a snapshot of this register.
+ *
+ * @param album the snapshot album
+ * @param document the XML document
+ * @param monitor the progress monitor
+ * @return the XML element
+ * @throws Exception the exception if anything goes wrong
+ * @since 2.0
+ */
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor) throws Exception {
+ Element registerElement = document.createElement(REGISTER);
+ registerElement.setAttribute(PROP_ID, this.getID());
+ registerElement.setAttribute(PROP_VALUE, getRegisterValueAsHexString(this));
+ Element propsElement = SnapshotUtils.makeXMLFromProperties(document, getProperties());
+ registerElement.appendChild(propsElement);
+ return registerElement;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor#loadSnapshot(org.w3c.dom.Element)
+ */
+ public void loadSnapshot(Element element) throws Exception {
+ String registerValue = element.getAttribute(PROP_VALUE);
+ String contextID = (String) getProperties().get(PROP_EXECUTION_CONTEXT_ID);
+
+ synchronized (registerValueCache) {
+ Map<String, BigInteger> exeDMCRegisters = registerValueCache.get(contextID);
+ if (exeDMCRegisters == null) {
+ exeDMCRegisters = new HashMap<String, BigInteger>();
+ registerValueCache.put(contextID, exeDMCRegisters);
+ }
+ exeDMCRegisters.put(getID(), new BigInteger(registerValue, 16));
+ }
+ }
+ }
+
+ class RegisterData implements IRegisterDMData {
+
+ private final HashMap<String, Object> properties = new HashMap<String, Object>();
+
+ public RegisterData(Map<String, Object> properties) {
+ this.properties.putAll(properties);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#isReadable()
+ */
+ public boolean isReadable() {
+ Boolean n = (Boolean)properties.get(org.eclipse.tm.tcf.services.IRegisters.PROP_READBLE);
+ if (n == null)
+ return true;
+ return n.booleanValue();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#isReadOnce()
+ */
+ public boolean isReadOnce() {
+ Boolean n = (Boolean)properties.get(org.eclipse.tm.tcf.services.IRegisters.PROP_READ_ONCE);
+ if (n == null)
+ return false;
+ return n.booleanValue();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#isWriteable()
+ */
+ public boolean isWriteable() {
+ Boolean n = (Boolean)properties.get(org.eclipse.tm.tcf.services.IRegisters.PROP_WRITEABLE);
+ if (n == null)
+ return true;
+ return n.booleanValue();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#isWriteOnce()
+ */
+ public boolean isWriteOnce() {
+ Boolean n = (Boolean)properties.get(org.eclipse.tm.tcf.services.IRegisters.PROP_WRITE_ONCE);
+ if (n == null)
+ return false;
+ return n.booleanValue();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#hasSideEffects()
+ */
+ public boolean hasSideEffects() {
+ Boolean n = (Boolean)properties.get(org.eclipse.tm.tcf.services.IRegisters.PROP_SIDE_EFFECTS);
+ if (n == null)
+ return false;
+ return n.booleanValue();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#isVolatile()
+ */
+ public boolean isVolatile() {
+ Boolean n = (Boolean)properties.get(org.eclipse.tm.tcf.services.IRegisters.PROP_VOLATILE);
+ if (n == null)
+ return false;
+ return n.booleanValue();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#isFloat()
+ */
+ public boolean isFloat() {
+ Boolean n = (Boolean)properties.get(org.eclipse.tm.tcf.services.IRegisters.PROP_FLOAT);
+ if (n == null)
+ return false;
+ return n.booleanValue();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#getName()
+ */
+ public String getName() {
+ return (String) properties.get(IEDCDMContext.PROP_NAME);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData#getDescription()
+ */
+ public String getDescription() {
+ return (String) properties.get(IEDCDMContext.PROP_DESCRIPTION);
+ }
+
+ }
+
+ /**
+ * Event class to notify register value is changed
+ */
+ public static class RegisterChangedDMEvent implements IRegisters.IRegisterChangedDMEvent {
+
+ /** The register dmc. */
+ private final IRegisterDMContext fRegisterDMC;
+
+ /**
+ * Instantiates a new register changed dm event.
+ *
+ * @param registerDMC the register dmc
+ */
+ RegisterChangedDMEvent(IRegisterDMContext registerDMC) {
+ fRegisterDMC = registerDMC;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.datamodel.IDMEvent#getDMContext()
+ */
+ public IRegisterDMContext getDMContext() {
+ return fRegisterDMC;
+ }
+ }
+
+ /**
+ * Instantiates a new Registers service.
+ *
+ * @param session the session
+ * @param classNames the type names the service will be registered under. See
+ * AbstractDsfService#register for details. We tack on base DSF's
+ * IRegisters and this class to the list if missing.
+ */
+ public Registers(DsfSession session, String[] classNames) {
+ super(session,
+ massageClassNames(classNames,
+ new String[] {IRegisters.class.getName(), Registers.class.getName()}));
+ setTCFTimeout(15 * 1000); // Fifteen seconds
+ }
+
+ /**
+ * Find register DMC by register name. <br>
+ *
+ * It's required the register name be known/recognizable to TCF agent,
+ * meaning host debugger still cannot be totally target neutral on register
+ * access. TCF IRegisters service allows us to access common registers such
+ * as PC, LP and SP in a target-independent way (using Role property). But
+ * debugger need to access other registers (e.g. R0, R1, CPSR on ARM) for
+ * stack crawl and variable evaluation.
+ *
+ * @param exeDMC the exe dmc
+ * @param name the name
+ * @return the register dmc
+ * @throws CoreException the core exception
+ * @since 2.0
+ */
+ public RegisterDMC findRegisterDMCByName(IEDCExecutionDMC exeDMC, String name) throws CoreException {
+ assert RunControl.isNonContainer(exeDMC);
+
+ // this will create the reg groups for the exeDMC if not yet.
+ IRegisterGroupDMContext[] regGroups = getGroupsForContext(exeDMC);
+
+ for (IRegisterGroupDMContext g : regGroups) {
+ // Note the getRegisters() will create registerDMCs for the group if not yet.
+ for (RegisterDMC reg : ((RegisterGroupDMC)g).getRegisters()) {
+ String n = (String)reg.getProperties().get(org.eclipse.tm.tcf.services.IRegisters.PROP_NAME);
+ if (name.equals(n))
+ return reg;
+ }
+ }
+
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.AbstractEDCService#doInitialize(org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ @Override
+ protected void doInitialize(RequestMonitor requestMonitor) {
+ super.doInitialize(requestMonitor);
+ getSession().addServiceEventListener(this, null);
+ }
+
+ /**
+ * Gets the groups for context.
+ *
+ * @param executableContext the executable context
+ * @return the groups for context
+ * @throws CoreException the core exception
+ */
+ public IRegisterGroupDMContext[] getGroupsForContext(IEDCExecutionDMC executableContext) throws CoreException {
+ String contextID = executableContext.getID();
+ List<RegisterGroupDMC> groupsForContext = registerGroupsPerContext.get(contextID);
+ if (groupsForContext == null) {
+ groupsForContext = createGroupsForContext(executableContext);
+ synchronized (registerGroupsPerContext) {
+ registerGroupsPerContext.put(contextID, groupsForContext);
+ }
+ }
+ return groupsForContext.toArray(new IRegisterGroupDMContext[groupsForContext.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#writeBitField(org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext, java.lang.String, java.lang.String, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ public void writeBitField(IBitFieldDMContext bitFieldCtx, String bitFieldValue, String formatId, RequestMonitor rm) {
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#writeBitField(org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext, org.eclipse.cdt.dsf.debug.service.IRegisters.IMnemonic, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ public void writeBitField(IBitFieldDMContext bitFieldCtx, IMnemonic mnemonic, RequestMonitor rm) {
+ rm.done();
+ }
+
+ /**
+ * Writes a value to a register.
+ *
+ * @param context the context
+ * @param regID register name.
+ * @param regValue big-endian hex string representation of the value to write.
+ * @throws CoreException the core exception
+ */
+ public void writeRegister(IEDCExecutionDMC context, String regID, String regValue) throws CoreException {
+ RegisterDMC regDMC;
+
+ regDMC = findRegisterDMCByName(context, regID);
+ assert regDMC != null;
+
+ writeRegister(regDMC, regValue, IFormattedValues.HEX_FORMAT,
+ new RequestMonitor(getExecutor(), null));
+ }
+
+ /**
+ * Writes a value to a register
+ * @throws CoreException
+ * @since 2.0
+ */
+ public void writeRegister(IRegisterDMContext regCtx, String regValue, String formatID) throws CoreException {
+ assert (regCtx instanceof RegisterDMC);
+
+ final RegisterDMC regDMC = (RegisterDMC) regCtx;
+ IExecutionDMContext exeDMC = DMContexts.getAncestorOfType(regDMC, IExecutionDMContext.class);
+ if (exeDMC == null || !(exeDMC instanceof IEDCDMContext)) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(),
+ "No valid execution context for finding the register ID"));
+ }
+
+ final String exeDMCID = ((IEDCDMContext) exeDMC).getID();
+
+ // Put the incoming value into hex
+ if (formatID.equals(IFormattedValues.OCTAL_FORMAT) || formatID.equals(IFormattedValues.BINARY_FORMAT) ||
+ formatID.equals(IFormattedValues.DECIMAL_FORMAT))
+ {
+ BigInteger bigRegValue = null;
+
+ try {
+ bigRegValue = NumberFormatUtils.parseIntegerByFormat(regValue, formatID);
+ } catch (NumberFormatException e) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(),
+ "Cannot change register to invalid value \"" + regValue + "\""));
+ }
+ // if bigRegValue is negative, using bigRegValue.toString(16) directly gives values such as '-af'
+ regValue = Long.toHexString(bigRegValue.longValue());
+ }
+
+ // if register value string is too long, truncate to register size (2 hex chars per byte)
+ if (tcfRegistersService != null) { // TCF IRegisters service available)
+ int regSize = regDMC.getTCFContext().getSize();
+ if (regValue.length() > regSize * 2)
+ regValue = regValue.substring(regValue.length() - regSize * 2);
+ }
+
+ if (tcfRegistersService != null) { // TCF IRegisters service available
+ final RegistersContext tcfReg = regDMC.getTCFContext();
+ byte[] bv = null;
+ try {
+ bv = MemoryUtils.convertHexStringToByteArray(regValue, tcfReg.getSize(), 2);
+ } catch (NumberFormatException e) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(),
+ "Cannot change register to invalid value \"" + regValue + "\""));
+ }
+
+ final byte[] byteVal = bv;
+
+ TCFTask<Object> tcfTask = new TCFTask<Object>() {
+ public void run() {
+ tcfReg.set(byteVal, new org.eclipse.tm.tcf.services.IRegisters.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ if (error == null) {
+ generateRegisterChangedEvent(regDMC);
+ done(null);
+ } else {
+ done(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
+ "Error writing register.", error));
+ }
+ }
+ });
+ }
+ };
+
+ try {
+ Object result = tcfTask.get(getTCFTimeout(), TimeUnit.MILLISECONDS);
+ if (result != null && result instanceof IStatus)
+ throw new CoreException((IStatus) result);
+ } catch (Exception e) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
+ "Error writing register.", e));
+ }
+ }
+
+ // Update cached register values if register write succeeds
+ Map<String, BigInteger> exeDMCRegisters = registerValueCache.get(exeDMCID);
+ if (exeDMCRegisters != null) {
+ exeDMCRegisters.put(regDMC.getID(), new BigInteger(regValue, 16));
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IFormattedValues#getAvailableFormats(org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getAvailableFormats(IFormattedDataDMContext dmc, DataRequestMonitor<String[]> rm) {
+ rm.setData(new String[] { HEX_FORMAT, DECIMAL_FORMAT, OCTAL_FORMAT, BINARY_FORMAT, NATURAL_FORMAT });
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IFormattedValues#getFormattedExpressionValue(org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getFormattedExpressionValue(FormattedValueDMContext dmc, DataRequestMonitor<FormattedValueDMData> rm) {
+ if (dmc.getParents().length == 1 && dmc.getParents()[0] instanceof RegisterDMC) {
+ getRegisterDataValue((RegisterDMC) dmc.getParents()[0], dmc.getFormatID(), rm);
+ } else {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Unknown DMC type", null)); //$NON-NLS-1$
+ rm.done();
+ }
+ }
+
+ /**
+ * Read register with given ID, usually a name that's recognizable by TCF agent.
+ *
+ * @param context the context
+ * @param id the id
+ * @return a hex string on success, and {@link #REGISTER_VALUE_ERROR} on error.
+ * @throws CoreException the core exception
+ */
+ public String getRegisterValue(IExecutionDMContext context, String id) throws CoreException {
+ RegisterDMC regDMC;
+
+ regDMC = findRegisterDMCByName((IEDCExecutionDMC) context, id);
+ assert regDMC != null;
+
+ return getRegisterValueAsHexString(regDMC);
+ }
+
+ /**
+ * Gets the register value as hex string.
+ *
+ * @param registerDMC the register dmc
+ * @return the register value as hex string
+ * @throws CoreException the core exception
+ * @since 2.0
+ */
+ public String getRegisterValueAsHexString(RegisterDMC registerDMC) throws CoreException {
+ return getRegisterValue(registerDMC).toString(16);
+ }
+
+ /**
+ * Gets the register value as a big integer.
+ *
+ * @param registerDMC the register dmc
+ * @return the register value
+ * @throws CoreException the core exception
+ * @since 2.0
+ */
+ public BigInteger getRegisterValue(RegisterDMC registerDMC) throws CoreException {
+
+ IExecutionDMContext exeDMC = DMContexts.getAncestorOfType(registerDMC, IExecutionDMContext.class);
+ if (exeDMC == null || !(exeDMC instanceof IEDCDMContext)) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "No valid executionDMC for the register."));
+ }
+
+ final String exeDMCID = ((IEDCDMContext) exeDMC).getID();
+ final String registerDMCID = registerDMC.getID();
+
+ synchronized (registerValueCache) {
+
+ Map<String, BigInteger> exeDMCRegisters = registerValueCache.get(exeDMCID);
+ if (exeDMCRegisters != null) {
+ BigInteger cachedValue = exeDMCRegisters.get(registerDMC.getID());
+ if (cachedValue != null) {
+ return cachedValue;
+ }
+ }
+ }
+
+ if (tcfRegistersService != null) { // TCF IRegisters service available
+ final RegistersContext tcfReg = registerDMC.getTCFContext();
+
+ if (tcfReg == null) {
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "RegisterDMC " + registerDMC.getID() + " has no underlying TCF register context."));
+ }
+
+ TCFTask<byte[]> tcfTask = new TCFTask<byte[]>() {
+
+ public void run() {
+ tcfReg.get(new org.eclipse.tm.tcf.services.IRegisters.DoneGet() {
+
+ public void doneGet(IToken token, Exception error, byte[] value) {
+ done(value);
+ }
+ });
+ }
+ };
+
+ try {
+ byte[] value = tcfTask.get(getTCFTimeout(), TimeUnit.MILLISECONDS); // ignore the return
+ String strVal = MemoryUtils.convertByteArrayToHexString(value);
+ BigInteger biValue = new BigInteger(strVal, 16);
+ synchronized (registerValueCache) {
+ Map<String, BigInteger> exeDMCRegisters = registerValueCache.get(exeDMCID);
+ if (exeDMCRegisters == null) {
+ exeDMCRegisters = new HashMap<String, BigInteger>();
+ registerValueCache.put(exeDMCID, exeDMCRegisters);
+ }
+ exeDMCRegisters.put(registerDMCID, biValue);
+ }
+ return biValue;
+ } catch (Throwable e) {
+ throw new CoreException(EDCDebugger.dsfRequestFailedStatus("Exception reading register " + registerDMC.getName(), e));
+ }
+ }
+ throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "No data for register " + registerDMC.getName()));
+ }
+
+ /**
+ * Generate a register changed event.
+ *
+ * @param dmc the register dmc
+ */
+ private void generateRegisterChangedEvent(IRegisterDMContext dmc) {
+ getSession().dispatchEvent(new RegisterChangedDMEvent(dmc), getProperties());
+
+ // need to notify listeners via suspended event if the PC has changed
+ RegisterDMC regdmc = (RegisterDMC) dmc;
+ if (regdmc.getName().equals(getTargetEnvironmentService().getPCRegisterID())) {
+ IExecutionDMContext exeDMC = DMContexts.getAncestorOfType(dmc, IExecutionDMContext.class);
+ getSession().dispatchEvent(new SuspendedEvent(exeDMC, StateChangeReason.USER_REQUEST, new HashMap<String, Object>()), getProperties());
+ }
+ }
+
+ /**
+ * Gets the register data value.
+ *
+ * @param registerDMC the register dmc
+ * @param formatID the format id
+ * @param rm the request monitor
+ * @return the register data value
+ */
+ private void getRegisterDataValue(RegisterDMC registerDMC, final String formatID,
+ final DataRequestMonitor<FormattedValueDMData> rm) {
+ try {
+ BigInteger bigIntValue = getRegisterValue(registerDMC);
+
+ String formattedValue = bigIntValue.toString(16);
+
+ if (formatID.equals(IFormattedValues.OCTAL_FORMAT))
+ formattedValue = NumberFormatUtils.toOctalString(bigIntValue);
+ if (formatID.equals(IFormattedValues.BINARY_FORMAT))
+ formattedValue = NumberFormatUtils.asBinary(bigIntValue);
+ if (formatID.equals(IFormattedValues.DECIMAL_FORMAT))
+ formattedValue = bigIntValue.toString();
+
+ rm.setData(new FormattedValueDMData(formattedValue));
+
+ } catch (CoreException e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), "Error in getRegisterDataValue.", e);
+ EDCDebugger.getMessageLogger().log(s);
+ rm.setStatus(s);
+ }
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IFormattedValues#getFormattedValueContext(org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext, java.lang.String)
+ */
+ public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId) {
+ if (dmc instanceof RegisterDMC) {
+ return new FormattedValueDMContext(Registers.this, dmc, formatId);
+ }
+ return null;
+ }
+
+ /**
+ * Gets the model data for a register.
+ *
+ * @param dmc the dmc
+ * @param rm the request monitor
+ * @return the model data
+ */
+ @SuppressWarnings("unchecked")
+ public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) {
+
+ if (dmc instanceof RegisterGroupDMC)
+ getRegisterGroupData((IRegisterGroupDMContext) dmc, (DataRequestMonitor<IRegisterGroupDMData>) rm);
+ else if (dmc instanceof RegisterDMC)
+ getRegisterData((IRegisterDMContext) dmc, (DataRequestMonitor<IRegisterDMData>) rm);
+ else if (dmc instanceof FormattedValueDMContext)
+ getFormattedExpressionValue((FormattedValueDMContext) dmc, (DataRequestMonitor<FormattedValueDMData>) rm);
+ else
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.ICachingService#flushCache(org.eclipse.cdt.dsf.datamodel.IDMContext)
+ */
+ public void flushCache(IDMContext context) {
+ if (isSnapshot())
+ return;
+ // Why flush this static info ?
+ // registerGroupsPerThread.clear();
+
+ registerValueCache.clear();
+ }
+
+ /**
+ * Load register groups for an executable context.
+ *
+ * @param executionDmc the execution dmc
+ * @param element the element
+ * @throws Exception the exception
+ */
+ public void loadGroupsForContext(IEDCExecutionDMC executionDmc, Element element) throws Exception {
+ // Can't call flushCache here because it does nothing for snapshot
+ // services.
+ String cxtID = ((IEDCDMContext)executionDmc).getID();
+ // It does not hurt if the context is not in the caches.
+ registerGroupsPerContext.remove(cxtID);
+ registerValueCache.remove(cxtID);
+
+ NodeList registerGroups = element.getElementsByTagName(RegisterGroupDMC.REGISTER_GROUP);
+
+ List<RegisterGroupDMC> regGroups = Collections.synchronizedList(new ArrayList<RegisterGroupDMC>());
+
+ int numGroups = registerGroups.getLength();
+ for (int i = 0; i < numGroups; i++) {
+ Element groupElement = (Element) registerGroups.item(i);
+ Element propElement = (Element) groupElement.getElementsByTagName(SnapshotUtils.PROPERTIES).item(0);
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ SnapshotUtils.initializeFromXML(propElement, properties);
+
+ RegisterGroupDMC regdmc = new RegisterGroupDMC(this, executionDmc, properties);
+ regdmc.loadSnapshot(groupElement);
+ regGroups.add(regdmc);
+ }
+ registerGroupsPerContext.put(((IEDCDMContext) executionDmc).getID(), regGroups);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.debug.edc.services.IDSFServiceUsingTCF#tcfServiceReady(org.eclipse.tm.tcf.protocol.IService)
+ */
+ public void tcfServiceReady(IService service) {
+ tcfRegistersService = (org.eclipse.tm.tcf.services.IRegisters)service;
+ }
+
+ /**
+ * Gets the register value.
+ *
+ * @param executionDMC the execution dmc
+ * @param id the register id
+ * @return the register value
+ * @throws CoreException the core exception
+ */
+ public String getRegisterValue(IEDCExecutionDMC executionDMC, int id) throws CoreException {
+ String name = getRegisterNameFromCommonID(id);
+ if (name != null) {
+ return getRegisterValue(executionDMC, name);
+ }
+ return null;
+ }
+
+ /**
+ * Get TCF child registers contexts for the given parent.
+ * If parent is a thread, the registers contexts are register groups.
+ * If parent is a register group, the contexts returned are registers.
+ *
+ * @param parentID thread ID or register group ID.
+ * @return the tCF registers contexts
+ * @throws CoreException the core exception
+ */
+ protected List<RegistersContext> getTCFRegistersContexts(final String parentID) throws CoreException {
+ List<RegistersContext> tcfRegContexts = new ArrayList<RegistersContext>();
+
+ TCFTask<String[]> getChildIDTask = new TCFTask<String[]>() {
+ public void run() {
+ tcfRegistersService.getChildren(parentID, new org.eclipse.tm.tcf.services.IRegisters.DoneGetChildren() {
+
+ public void doneGetChildren(IToken token, Exception error, String[] contextIds) {
+ if (error == null)
+ done(contextIds);
+ else
+ error(error);
+ }});
+ }
+ };
+
+ String[] childIDs;
+ try {
+ childIDs = getChildIDTask.get(getTCFTimeout(), TimeUnit.MILLISECONDS);
+ } catch (Throwable e) {
+ throw new CoreException(EDCDebugger.dsfRequestFailedStatus("Fail to get TCF context for: " + parentID, e));
+ }
+
+ for (String gid: childIDs) {
+ final String id = gid;
+ TCFTask<RegistersContext> getGroupContextTask = new TCFTask<RegistersContext>() {
+ public void run() {
+ tcfRegistersService.getContext(id, new org.eclipse.tm.tcf.services.IRegisters.DoneGetContext(){
+ public void doneGetContext(IToken token, Exception error, RegistersContext context) {
+ if (error == null)
+ done(context);
+ else
+ error(error);
+ }});
+ }
+ };
+
+ RegistersContext rgc = null;
+ try {
+ rgc = getGroupContextTask.get(getTCFTimeout(), TimeUnit.MILLISECONDS);
+ } catch (Throwable e) {
+ throw new CoreException(EDCDebugger.dsfRequestFailedStatus("Fail to get TCF context for: " + parentID, e));
+ }
+
+ if (rgc != null)
+ tcfRegContexts.add(rgc);
+ }
+
+ return tcfRegContexts;
+ }
+
+ /**
+ * Handle a suspended event by flushing the cache.
+ *
+ * @param e the event
+ */
+ @DsfServiceEventHandler
+ public void eventDispatched(ISuspendedDMEvent e) {
+ flushCache(null);
+ }
+
+ /**
+ * Handle a resumed event by flushing the cache.
+ *
+ * @param e the event
+ */
+ @DsfServiceEventHandler
+ public void eventDispatched(IResumedDMEvent e) {
+ flushCache(null);
+ }
+
+ /**
+ * When a context (e.g. a thread) is killed/detached, we should forget
+ * cached register info & values for it so that we can properly access
+ * registers when we re-attach to it.
+ *
+ * @param e the event
+ * @since 2.0
+ */
+ @DsfServiceEventHandler
+ public void eventDispatched(IExitedDMEvent e) {
+ IExecutionDMContext cxt = e.getDMContext();
+ if (cxt != null && cxt instanceof IEDCDMContext) {
+ String cxtID = ((IEDCDMContext)cxt).getID();
+ // It does not hurt if the context is not in the caches.
+ registerGroupsPerContext.remove(cxtID);
+ registerValueCache.remove(cxtID);
+ }
+ }
+
+ /**
+ * Creates the registers for group.
+ *
+ * @param registerGroupDMC the register group dmc
+ * @return the list
+ * @throws CoreException the core exception
+ */
+ protected List<RegisterDMC> createRegistersForGroup(RegisterGroupDMC registerGroupDMC) throws CoreException {
+ ArrayList<RegisterDMC> registers = new ArrayList<RegisterDMC>();
+
+ if (tcfRegistersService != null) {
+ List<RegistersContext> tcfRegs = getTCFRegistersContexts(registerGroupDMC.getID());
+
+ for (RegistersContext rg: tcfRegs) {
+ registers.add(new RegisterDMC(registerGroupDMC, registerGroupDMC.getExecutionDMC(), rg));
+ }
+ }
+
+ return registers;
+ }
+
+ /**
+ * Creates the groups for context.
+ *
+ * @param ctx the ctx
+ * @return the list
+ * @throws CoreException the core exception
+ */
+ protected List<RegisterGroupDMC> createGroupsForContext(IEDCExecutionDMC ctx) throws CoreException {
+
+ List<RegisterGroupDMC> groups = Collections.synchronizedList(new ArrayList<RegisterGroupDMC>());
+
+ if (RunControl.isNonContainer(ctx)) {
+ if (tcfRegistersService != null) {
+ List<RegistersContext> tcfRegGroups = getTCFRegistersContexts(ctx.getID());
+
+ for (RegistersContext rg: tcfRegGroups) {
+ groups.add(new RegisterGroupDMC(this, ctx, rg.getProperties()));
+ }
+ }
+ }
+
+ return groups;
+ }
+
+ /**
+ * Given a common general purpose register id (e.g. from symbolics), get the
+ * corresponding register name.
+ *
+ * @param id
+ * the common general purpose register id (0-31)
+ * @return the corresponding register name, or null of n/a
+ */
+ public abstract String getRegisterNameFromCommonID(int id);
+
+ /**
+ * Sets the TCF timeout.
+ *
+ * @param msecs the new TCF timeout
+ * @since 2.0
+ */
+ public void setTCFTimeout(long msecs) {
+ tcfTimeout = msecs;
+ }
+
+ /**
+ * Gets the TCF timeout.
+ *
+ * @return the TCF timeout
+ * @since 2.0
+ */
+ public long getTCFTimeout() {
+ return tcfTimeout;
+ }
+
+ // Implementation of org.eclipse.cdt.dsf.debug.service.IRegisters
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getRegisterGroups(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getRegisterGroups(final IDMContext ctx, final DataRequestMonitor<IRegisterGroupDMContext[]> rm) {
+
+ asyncExec(new Runnable() {
+
+ public void run() {
+ IEDCExecutionDMC execDmc = DMContexts.getAncestorOfType(ctx, IEDCExecutionDMC.class);
+ if (execDmc != null && RunControl.isNonContainer(execDmc)) {
+ try {
+ rm.setData(getGroupsForContext(execDmc));
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ rm.setStatus(e.getStatus());
+ }
+ rm.done();
+ return;
+ }
+
+ StackFrameDMC frameDmc = DMContexts.getAncestorOfType(ctx, StackFrameDMC.class);
+ if (frameDmc != null) {
+ try {
+ rm.setData(getGroupsForContext(frameDmc.getExecutionDMC()));
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ rm.setStatus(e.getStatus());
+ }
+ rm.done();
+ return;
+ }
+
+ rm.setData(new IRegisterGroupDMContext[0]);
+ rm.done();
+ }
+ }, rm);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getRegisters(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getRegisters(final IDMContext ctx, final DataRequestMonitor<IRegisterDMContext[]> rm) {
+
+ asyncExec(new Runnable() {
+
+ public void run() {
+ RegisterGroupDMC groupContext = DMContexts.getAncestorOfType(ctx, RegisterGroupDMC.class);
+ IEDCExecutionDMC executionContext = DMContexts.getAncestorOfType(ctx, IEDCExecutionDMC.class);
+ RegisterDMC[] allRegisters;
+ try {
+ if (groupContext != null && executionContext != null) {
+ allRegisters = groupContext.getRegisters();
+ }
+ else {
+ allRegisters = new RegisterDMC[0];
+ }
+ rm.setData(allRegisters);
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ rm.setStatus(e.getStatus());
+ }
+ rm.done();
+ }
+ }, rm);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getBitFields(org.eclipse.cdt.dsf.datamodel.IDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getBitFields(IDMContext ctx, DataRequestMonitor<IBitFieldDMContext[]> rm) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED, "BitField not supported", null)); //$NON-NLS-1$
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#findRegisterGroup(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void findRegisterGroup(IDMContext ctx, String name, DataRequestMonitor<IRegisterGroupDMContext> rm) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED, "findRegisterGroup not supported", null)); //$NON-NLS-1$
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#findRegister(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void findRegister(IDMContext ctx, String name, DataRequestMonitor<IRegisterDMContext> rm) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED, "findRegister not supported", null)); //$NON-NLS-1$
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#findBitField(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void findBitField(IDMContext ctx, String name, DataRequestMonitor<IBitFieldDMContext> rm) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED, "findBitField not supported", null)); //$NON-NLS-1$
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getRegisterGroupData(org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getRegisterGroupData(IRegisterGroupDMContext dmc, DataRequestMonitor<IRegisterGroupDMData> rm) {
+
+ class RegisterGroupData implements IRegisterGroupDMData {
+ private final String name;
+ private final String description;
+
+ public RegisterGroupData(RegisterGroupDMC dmc) {
+ this.name = dmc.getName();
+ this.description = (String) dmc.getProperty(IEDCDMContext.PROP_DESCRIPTION);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+ }
+
+ rm.setData(new RegisterGroupData((RegisterGroupDMC) dmc));
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getRegisterData(org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getRegisterData(IRegisterDMContext dmc, DataRequestMonitor<IRegisterDMData> rm) {
+ RegisterDMC regdmc = (RegisterDMC) dmc;
+ rm.setData(new RegisterData(regdmc.getProperties()));
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#getBitFieldData(org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getBitFieldData(IBitFieldDMContext dmc, DataRequestMonitor<IBitFieldDMData> rm) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED,
+ "Bit fields not yet supported", null)); //$NON-NLS-1$
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IRegisters#writeRegister(org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext, java.lang.String, java.lang.String, org.eclipse.cdt.dsf.concurrent.RequestMonitor)
+ */
+ public void writeRegister(final IRegisterDMContext regCtx, final String regValue, final String formatID, final RequestMonitor rm) {
+
+ asyncExec(new Runnable() {
+
+ public void run() {
+ try{
+ writeRegister(regCtx, regValue, formatID);
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ rm.setStatus(e.getStatus());
+ }
+ rm.done();
+ }
+ }, rm);
+
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.services;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.EDCTrace;
+import org.eclipse.cdt.debug.edc.internal.launch.CSourceLookup;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.RunControl.ExecutionDMC;
+import org.eclipse.cdt.debug.edc.internal.services.dsf.Symbols;
+import org.eclipse.cdt.debug.edc.internal.snapshot.SnapshotUtils;
+import org.eclipse.cdt.debug.edc.internal.symbols.MemoryVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.EDCSymbolReader;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglerEABI;
+import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglingException;
+import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
+import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
+import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
+import org.eclipse.cdt.debug.edc.symbols.IDebugInfoProvider;
+import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
+import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
+import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
+import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
+import org.eclipse.cdt.debug.edc.symbols.IModuleLineEntryProvider;
+import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
+import org.eclipse.cdt.debug.edc.symbols.IScope;
+import org.eclipse.cdt.debug.edc.symbols.IUnmangler;
+import org.eclipse.cdt.debug.edc.symbols.IVariable;
+import org.eclipse.cdt.debug.edc.symbols.TypeEngine;
+import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector;
+import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
+import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
+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.IResumedDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
+import org.eclipse.cdt.dsf.debug.service.IStack;
+import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
+import org.eclipse.cdt.dsf.service.DsfSession;
+import org.eclipse.cdt.dsf.service.IDsfService;
+import org.eclipse.cdt.utils.Addr64;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public abstract class Stack extends AbstractEDCService implements IStack, ICachingService {
+
+ public static final String STACK_FRAME = "stack_frame";
+
+ public Boolean showAllVariablesEnabled = null;
+
+ private final Map<String, List<StackFrameDMC>> stackFrames = Collections
+ .synchronizedMap(new HashMap<String, List<StackFrameDMC>>());
+ private final Map<String, Boolean> allFramesCached = Collections
+ .synchronizedMap(new HashMap<String, Boolean>());
+
+
+
+ public static class StackFrameData implements IFrameDMData {
+
+ public final IAddress address;
+ public final int level;
+ public final String function;
+ public final String module;
+ private final String file;
+ private final int lineNumber;
+
+ StackFrameData(StackFrameDMC dmc) {
+ level = dmc.getLevel();
+ address = dmc.getInstructionPtrAddress();
+ module = dmc.getModuleName();
+ file = dmc.getSourceFile(); // "" instead of null if no file.
+ lineNumber = dmc.getLineNumber();
+ function = dmc.getFunctionName();
+ }
+
+ public IAddress getAddress() {
+ return address;
+ }
+
+ public String getFunction() {
+ return function;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ // DSF requires non-null return value.
+ public String getFile() {
+ return file;
+ }
+
+ public int getLine() {
+ return lineNumber;
+ }
+
+ public int getColumn() {
+ return 0;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return
+ this == other
+ || (other != null && other instanceof StackFrameData
+ && getAddress().equals(((StackFrameData)other).getAddress())
+ && getFunction().equals(((StackFrameData)other).getFunction())
+ && getLevel() == ((StackFrameData)other).getLevel()
+ && getFile().equals(((StackFrameData)other).getFile())
+ && getLine() == ((StackFrameData)other).getLine()
+ && getColumn() == ((StackFrameData)other).getColumn()
+ && getModule().equals(((StackFrameData)other).getModule()));
+ }
+ }
+
+ /**
+ * Variable or enumerator context. This interface provides a wrapper
+ * for treating variables and enumerators the same when needed.
+ **/
+ public interface IVariableEnumeratorContext {}
+
+ /**
+ * Enumerator context.
+ **/
+ public interface IEnumeratorDMContext {}
+
+ public static final class CurrentFrameRegisters implements IFrameRegisters {
+ private final Registers registers;
+ private final IEDCExecutionDMC executionDMC;
+
+ public CurrentFrameRegisters(IEDCExecutionDMC executionDMC, Registers registers) {
+ this.executionDMC = executionDMC;
+ this.registers = registers;
+ }
+
+ public BigInteger getRegister(int regnum, int bytes) throws CoreException {
+ String value = registers.getRegisterValue(executionDMC, regnum);
+ if (value == null || value.equals(Registers.REGISTER_VALUE_ERROR))
+ throw EDCDebugger.newCoreException("failed to read register");
+ return new BigInteger(value, 16);
+ }
+
+ public void writeRegister(int regnum, int bytes, BigInteger value) throws CoreException {
+ String id = registers.getRegisterNameFromCommonID(regnum);
+ if (id != null) {
+ // if value is negative, using value.toString(16) directly gives values such as '-af'
+ registers.writeRegister(executionDMC, id, Long.toHexString(value.longValue()));
+ } else
+ throw EDCDebugger.newCoreException(MessageFormat.format("could not find register number {0}", regnum));
+ }
+ }
+
+ /**
+ * Frame registers read from preserved registers on the stack frame.
+ */
+ public static class PreservedFrameRegisters implements IFrameRegisters {
+ private final Map<Integer, BigInteger> preservedRegisters;
+ private final EDCServicesTracker dsfServicesTracker;
+ private final StackFrameDMC context;
+
+ /**
+ * @param preservedRegisters map of register number to the address
+ * where the register is saved
+ * @since 2.0
+ */
+ public PreservedFrameRegisters(EDCServicesTracker dsfServicesTracker,
+ StackFrameDMC context,
+ Map<Integer, BigInteger> preservedRegisters) {
+ this.dsfServicesTracker = dsfServicesTracker;
+ this.context = context;
+ this.preservedRegisters = preservedRegisters;
+ }
+
+ public BigInteger getRegister(int regnum, int bytes) throws CoreException {
+ BigInteger addrVal = preservedRegisters.get(regnum);
+ if (addrVal != null) {
+ MemoryVariableLocation location = new MemoryVariableLocation(
+ dsfServicesTracker, context,
+ addrVal, true);
+ return location.readValue(bytes);
+ }
+ throw EDCDebugger.newCoreException("cannot read $R" + regnum + " from frame");
+ }
+
+ public void writeRegister(int regnum, int bytes, BigInteger value) throws CoreException {
+ BigInteger addrVal = preservedRegisters.get(regnum);
+ if (addrVal != null) {
+ MemoryVariableLocation location = new MemoryVariableLocation(
+ dsfServicesTracker, context,
+ addrVal, true);
+ location.writeValue(bytes, value);
+ }
+ }
+ }
+
+ /**
+ * Frame registers which always throws an exception.
+ */
+ public static class AlwaysFailingFrameRegisters implements IFrameRegisters {
+ private final CoreException e;
+
+ public AlwaysFailingFrameRegisters(CoreException e) {
+ this.e = e;
+ }
+
+ public BigInteger getRegister(int regnum, int bytes) throws CoreException {
+ throw e;
+ }
+
+ public void writeRegister(int regnum, int bytes, BigInteger value)
+ throws CoreException {
+ throw e;
+ }
+ }
+
+ public class StackFrameDMC extends DMContext implements IFrameDMContext, Comparable<StackFrameDMC>,
+ ISnapshotContributor {
+
+ /**
+ * Stack frame level. Zero is used for the first frame, where the PC is.
+ */
+ public static final String LEVEL_INDEX = "Level";
+ /**
+ * If set and True, tells that this frame is the topmost that we can fetch.
+ */
+ public static final String ROOT_FRAME = "root_frame";
+ public static final String BASE_ADDR = "Base_address";
+ /**
+ * @since 2.0 - previously "IP_ADDR"
+ */
+ public static final String INSTRUCTION_PTR_ADDR = "Instruction_address";
+ public static final String MODULE_NAME = "module_name";
+ public static final String SOURCE_FILE = "source_file";
+ public static final String FUNCTION_NAME = "function_name";
+ public static final String LINE_NUMBER = "line_number";
+ /**
+ * For LEVEL_INDEX == 0, if set and True, this tells us that this frame
+ * is not "authentic" yet, e.g., that the frame still represents the caller's
+ * state. This means we cannot trust the parameters and locals,
+ * and must resolve variables from other frames differently.
+ */
+ public static final String IN_PROLOGUE = "in_prologue"; // Boolean
+ /**
+ * Provides a Map<Integer, BigInteger> instance which can yield addresses of
+ * registers pushed into the stack frame if debug info does not provide it.
+ */
+ public static final String PRESERVED_REGISTERS = "preserved_registers";
+ private static final String FRAME_PROPERTY_CACHE = "_frame_properties";
+ /**
+ * @since 2.0 The id of the owning execution dmc
+ */
+ public static final String EXECUTION_DMC_ID = "execution_dmc_id";
+
+ private final EDCServicesTracker dsfServicesTracker = Stack.this.getEDCServicesTracker();
+ private final IEDCExecutionDMC executionDMC;
+ private final int level;
+ private IAddress baseAddress;
+ private IAddress instructionPtrAddress;
+
+ private String moduleName = "";
+ private String sourceFile = "";
+ private String functionName = "";
+ private int lineNumber;
+ private IScope variableScope = null;
+ private List<VariableDMC> locals;
+ private List<EnumeratorDMC> enumerators;
+ private final Map<String, VariableDMC> localsByName = Collections
+ .synchronizedMap(new HashMap<String, VariableDMC>());
+ private final Map<String, EnumeratorDMC> enumeratorsByName = Collections
+ .synchronizedMap(new HashMap<String, EnumeratorDMC>());
+ private final Map<String, IVariable> thisPtrs = Collections
+ .synchronizedMap(new LinkedHashMap<String, IVariable>());
+ private IFunctionScope functionScope;
+ private IFrameRegisters frameRegisters;
+ public StackFrameDMC calledFrame;
+ private TypeEngine typeEngine;
+ private IEDCModuleDMContext module;
+
+ // additional items may be null but are usually set early and used repeatedly
+ private IAddress instrPtrLinkAddr = null;
+ private IEDCSymbolReader reader = null;
+ private IModuleLineEntryProvider provider = null;
+ private IDebugInfoProvider debugInfoProvider = null;
+ private IPath symbolFile = null;
+
+ /**
+ * @since 2.0
+ */
+ @SuppressWarnings("unchecked")
+ public StackFrameDMC(final IEDCExecutionDMC executionDMC, EdcStackFrame edcFrame) {
+ super(Stack.this, new IDMContext[] { executionDMC }, createFrameID(executionDMC, edcFrame), edcFrame.props);
+
+ Map<String, Object> frameProperties = edcFrame.props;
+
+ this.executionDMC = executionDMC;
+ frameProperties.put(EXECUTION_DMC_ID, executionDMC.getID());
+
+ this.level = (Integer) frameProperties.get(LEVEL_INDEX);
+ this.moduleName = (String) frameProperties.get(MODULE_NAME);
+ this.baseAddress = address(frameProperties.get(BASE_ADDR));
+ this.instructionPtrAddress = address(frameProperties.get(INSTRUCTION_PTR_ADDR));
+
+ // compute the source location
+ IEDCSymbols symbolsService = getService(Symbols.class);
+ functionScope = symbolsService.getFunctionAtAddress(executionDMC.getSymbolDMContext(),
+ instructionPtrAddress);
+
+ boolean usingCachedProperties = false;
+ IEDCModules modules = dsfServicesTracker.getService(IEDCModules.class);
+ Map<IAddress, Map<String, Object>> cachedFrameProperties
+ = new HashMap<IAddress, Map<String, Object>>();
+ if (modules != null) {
+ module = modules.getModuleByAddress(executionDMC.getSymbolDMContext(), instructionPtrAddress);
+ if (module != null) {
+ instrPtrLinkAddr = module.toLinkAddress(instructionPtrAddress);
+ reader = module.getSymbolReader();
+ if (reader != null) {
+ symbolFile = this.reader.getSymbolFile();
+ if (symbolFile != null) {
+ // Check the persistent cache
+ String cacheKey = reader.getSymbolFile().toOSString() + FRAME_PROPERTY_CACHE;
+ Map<IAddress, Map<String, Object>> cachedData
+ = EDCDebugger.getDefault().getCache().getCachedData(cacheKey, Map.class,
+ reader.getModificationDate());
+ if (cachedData != null) {
+ cachedFrameProperties = cachedData;
+ Map<String, Object> cachedProperties
+ = cachedFrameProperties.get(instrPtrLinkAddr);
+ if (cachedProperties != null) {
+ if (cachedProperties.containsKey(SOURCE_FILE))
+ frameProperties.put(SOURCE_FILE, cachedProperties.get(SOURCE_FILE));
+
+ boolean cachedPropertiesHasFunctionName = false;
+ if (cachedProperties.containsKey(FUNCTION_NAME)) {
+ Object fnObj = cachedProperties.get(FUNCTION_NAME);
+ if (fnObj != null
+ && fnObj instanceof String
+ && ((String)fnObj).length() != 0) {
+ frameProperties.put(FUNCTION_NAME, fnObj);
+ cachedPropertiesHasFunctionName = true;
+ } }
+
+ if (!cachedPropertiesHasFunctionName) {
+ setFunctionName(executionDMC, frameProperties, symbolsService);
+ cachedProperties.put(FUNCTION_NAME, functionName);
+ }
+
+ if (cachedProperties.containsKey(LINE_NUMBER))
+ frameProperties.put(LINE_NUMBER, cachedProperties.get(LINE_NUMBER));
+ usingCachedProperties = true;
+ } } } } } } // null-checks on cachedProperties <= cachedData <= symbolFile
+
+ if (frameProperties.containsKey(SOURCE_FILE)) {
+ sourceFile = (String) frameProperties.get(SOURCE_FILE);
+ functionName = (String) frameProperties.get(FUNCTION_NAME);
+ lineNumber = (Integer) frameProperties.get(LINE_NUMBER);
+ } else if (frameProperties.containsKey(FUNCTION_NAME)) {
+ functionName = (String) frameProperties.get(FUNCTION_NAME);
+ } else if (!usingCachedProperties) {
+ ILineEntry line
+ = symbolsService.getLineEntryForAddress(executionDMC.getSymbolDMContext(),
+ instructionPtrAddress);
+ if (line != null)
+ setSourceProperties(frameProperties, line);
+
+ setFunctionName(executionDMC, frameProperties, symbolsService);
+ }
+ properties.putAll(frameProperties);
+
+ if (symbolFile != null) {
+ String cacheKey = symbolFile.toOSString() + FRAME_PROPERTY_CACHE;
+ cachedFrameProperties.put(this.instrPtrLinkAddr, frameProperties);
+ EDCDebugger.getDefault().getCache().putCachedData(cacheKey,
+ (Serializable)cachedFrameProperties,
+ this.reader.getModificationDate());
+ }
+
+ if (reader instanceof EDCSymbolReader)
+ debugInfoProvider = ((EDCSymbolReader)reader).getDebugInfoProvider();
+ typeEngine = new TypeEngine(getTargetEnvironmentService(), debugInfoProvider);
+ }
+
+ private void setFunctionName(final IEDCExecutionDMC executionDMC,
+ Map<String, Object> frameProperties, IEDCSymbols symbolsService) {
+ if (functionScope != null) {
+ // ignore inlined functions
+ IFunctionScope containerScope = functionScope;
+ while (containerScope.getParent() instanceof IFunctionScope) {
+ containerScope = (IFunctionScope) containerScope.getParent();
+ }
+ functionName = unmangle(containerScope.getName());
+ adjustFunctionSourceInfo(containerScope, frameProperties);
+ } else {
+ functionName
+ = unmangle(symbolsService.getSymbolNameAtAddress(executionDMC.getSymbolDMContext(),
+ instructionPtrAddress));
+ }
+
+ frameProperties.put(FUNCTION_NAME, functionName);
+ }
+
+ /**
+ * Modify the name to refer to the inline function within the parent function.
+ * <p>
+ * However, ignore the inline function name if the pointer is on the first
+ * line of the inline function and the "previous" line is
+ * <br> (a) in the parent function; or
+ * <br> (b) not in the original inline (meaning it was part of a prior inline); or
+ * <br> (c) is nested in another inline
+ * @param container the ultimate function containing the inline(s)
+ * @param frameProperties so source-file and line-number can also be adjusted
+ */
+ private void adjustFunctionSourceInfo(IFunctionScope container,
+ Map<String, Object> frameProperties) {
+ if (functionScope.equals(container)) {
+ ILineEntry funcFirstEntry = this.getLineEntryInFunction(functionScope);
+ if (funcFirstEntry != null
+ && !instrPtrLinkAddr.equals(funcFirstEntry.getLowAddress())) {
+ // this case covers the compiler having inline LNT entries
+ // whose bounds are outside the DWARF function scope boundaries
+ // for the inlines
+ setSourceProperties(frameProperties, funcFirstEntry);
+ }
+ return; // i.e. never fall through to "inline" re-naming below
+ }
+
+ ILineEntry containerEntry = this.getLineEntryInFunction(container);
+ if (containerEntry != null && isInlineShouldBeHidden(containerEntry)) {
+ setSourceProperties(frameProperties, containerEntry);
+ return;
+ }
+
+ this.functionName
+ = unmangle(functionScope.getName()) + " inlined in " + this.functionName;
+ }
+
+ /**
+ * Attempt to determine if the frame's instruction pointer is
+ * <br>(a) at the first instruction of an inlined function; and
+ * <br>(b) coincidentally at the first instruction of the line
+ * entry corresponding to the line that caused the inline to
+ * be generated.<p>
+ * @param entry if null, will be calculated based on established
+ * frame instruction pointer and function scope; can be passed
+ * in if caller needs line entry for other usage
+ * @return true if it can be determined that the instruction pointer is
+ * the first instruction of an inline function and coincidentally the
+ * first instruction of the line entry for which the inline was generated
+ * @since 2.0
+ */
+ public boolean isInlineShouldBeHidden(ILineEntry entry) {
+ if (functionScope == null
+ || !(functionScope.getParent() instanceof IFunctionScope)
+ || !instrPtrLinkAddr.equals(functionScope.getLowAddress()))
+ return false;
+
+ if (entry == null) {
+ entry = getLineEntryInFunction(functionScope);
+ if (entry == null)
+ return false;
+ }
+
+ if (instrPtrLinkAddr.equals(entry.getLowAddress())) {
+ ILineEntry prevEntry = getPreviousLineEntry(entry, true);
+ if (prevEntry != null) {
+ ILineEntry testEntry = getNextLineEntry(prevEntry, true);
+ if (entry.equals(testEntry)) {
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Private utility function to call the module's reader's provider's interfaces
+ * @see org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider#getLineEntryInFunction
+ * @see IModuleScope#getModuleLineEntryProvider
+ */
+ private ILineEntry getLineEntryInFunction(IFunctionScope func) {
+ return getModuleLineEntryProvider().getLineEntryInFunction(instrPtrLinkAddr, func);
+ }
+
+ /**
+ * Private utility function to call the module's reader's provider's interfaces
+ * @see IModuleScope#getModuleLineEntryProvider
+ * @return {@link IModuleLineEntryProvider} never <code>null</code>
+ */
+ private IModuleLineEntryProvider getModuleLineEntryProvider() {
+ if (provider == null && reader != null) {
+ IModuleScope moduleScope = reader.getModuleScope();
+ if (moduleScope != null)
+ provider = moduleScope.getModuleLineEntryProvider();
+ }
+ return provider;
+ }
+
+ /**
+ * Private utility function to call the module's reader's provider's interfaces
+ * @see org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider#getNextLineEntry
+ * @see IModuleScope#getModuleLineEntryProvider
+ */
+ private ILineEntry getNextLineEntry(ILineEntry entry, boolean collapseInlineFunctions) {
+ return getModuleLineEntryProvider().getNextLineEntry(entry, collapseInlineFunctions);
+ }
+
+ /**
+ * Private utility function to call the module's reader's provider's interfaces
+ * @see org.eclipse.cdt.debug.edc.symbols.ILineEntryProvider#getPreviousLineEntry
+ * @see IModuleScope#getModuleLineEntryProvider
+ */
+ private ILineEntry getPreviousLineEntry(ILineEntry entry, boolean collapseInlineFunctions) {
+ return getModuleLineEntryProvider().getPreviousLineEntry(entry, collapseInlineFunctions);
+ }
+
+ private void setSourceProperties(Map<String, Object> frameProperties,
+ ILineEntry entry) {
+ frameProperties.put(SOURCE_FILE, (sourceFile = entry.getFilePath().toOSString()));
+ frameProperties.put(LINE_NUMBER, (lineNumber = entry.getLineNumber()));
+ }
+
+ private String unmangle(String name) {
+ if (name == null)
+ return null;
+
+ // unmangle the name
+ IUnmangler unmangler = null;
+ if (reader instanceof EDCSymbolReader) {
+ unmangler = ((EDCSymbolReader) reader).getUnmangler();
+ }
+ if (unmangler == null) {
+ unmangler = new UnmanglerEABI();
+ }
+
+ if (!unmangler.isMangled(name))
+ return name;
+
+ try {
+ return unmangler.unmangleWithoutArgs(name);
+ } catch (UnmanglingException e) {
+ return name;
+ }
+ }
+
+ private IAddress address(Object obj) {
+ if (obj instanceof Integer)
+ return new Addr64(obj.toString());
+ if (obj instanceof Long)
+ return new Addr64(obj.toString());
+ if (obj instanceof String) // the string should be hex string
+ return new Addr64((String) obj, 16);
+ return null;
+ }
+
+ private void setInstructionPtrAddress(IAddress ipAddrPtr) {
+ this.instructionPtrAddress = ipAddrPtr;
+ if (module != null)
+ this.instrPtrLinkAddr = module.toLinkAddress(instructionPtrAddress);
+ }
+
+ public IFunctionScope getFunctionScope() {
+ return functionScope;
+ }
+
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ /**
+ * Get source file name if any for the frame.
+ * @return valid file name or "" otherwise.
+ */
+ public String getSourceFile() {
+ return sourceFile;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public IEDCExecutionDMC getExecutionDMC() {
+ return executionDMC;
+ }
+
+ public IAddress getBaseAddress() {
+ return baseAddress;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public IAddress getInstructionPtrAddress() {
+ return instructionPtrAddress;
+ }
+
+ public int getLevel() {
+ return level;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public EDCServicesTracker getEDCServicesTracker() {
+ return Stack.this.getEDCServicesTracker();
+ }
+
+ public int compareTo(StackFrameDMC f) {
+ if (level < f.level)
+ return -1;
+ if (level > f.level)
+ return +1;
+ return 0;
+ }
+
+
+ @Override
+ public String toString() {
+ return "StackFrameDMC [baseAddress=" + baseAddress.toHexAddressString() + ", ipAddress="
+ + instructionPtrAddress.toHexAddressString() + ", sourceFile=" + sourceFile
+ + ", functionName=" + functionName + ", lineNumber="
+ + lineNumber + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + getOuterType().hashCode();
+ result = prime * result
+ + ((baseAddress == null) ? 0 : baseAddress.hashCode());
+ result = prime * result
+ + ((executionDMC == null) ? 0 : executionDMC.hashCode());
+ result = prime * result + level;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!super.equals(obj))
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+
+ StackFrameDMC other = (StackFrameDMC) obj;
+ if (!getOuterType().equals(other.getOuterType()))
+ return false;
+
+ if (baseAddress == null) {
+ if (other.baseAddress != null)
+ return false;
+ } else if (!baseAddress.equals(other.baseAddress))
+ return false;
+
+ if (executionDMC == null) {
+ if (other.executionDMC != null)
+ return false;
+ } else if (!executionDMC.equals(other.executionDMC))
+ return false;
+
+ if (level != other.level)
+ return false;
+ return true;
+ }
+
+ /**
+ * Finds a source file using the source lookup director.
+ *
+ * @param sourceFile the raw source file location, usually from the symbol data
+ *
+ * @return location of the source file
+ */
+ private String findSourceFile(String sourceFile) {
+ String result = "";
+ CSourceLookup lookup = getService(CSourceLookup.class);
+ RunControl runControl = getService(RunControl.class);
+ CSourceLookupDirector[] directors = lookup.getSourceLookupDirectors(runControl.getRootDMC());
+
+ for (CSourceLookupDirector cSourceLookupDirector : directors) {
+ try {
+ Object[] elements = cSourceLookupDirector.findSourceElements(sourceFile);
+ if (elements != null && elements.length > 0)
+ {
+ Object element = elements[0];
+ if (element instanceof File) {
+ try {
+ result = (((File) element).getCanonicalPath());
+ } catch (IOException e) {
+ EDCDebugger.getMessageLogger().logError(null, e);
+ }
+ } else if (element instanceof IFile) {
+ result = (((IFile) element).getLocation().toOSString());
+ } else if (element instanceof IStorage) {
+ result = (((IStorage) element).getFullPath().toOSString());
+ } else if (element instanceof ITranslationUnit) {
+ result =(((ITranslationUnit) element).getLocation().toOSString());
+ }
+ break;
+ }
+ } catch (CoreException e1) {
+ EDCDebugger.getMessageLogger().logError(sourceFile, e1);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @since 2.0
+ */
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor) {
+ Element contextElement = document.createElement(STACK_FRAME);
+ contextElement.setAttribute(PROP_ID, this.getID());
+
+ Element propsElement = SnapshotUtils.makeXMLFromProperties(document, getProperties());
+ contextElement.appendChild(propsElement);
+ // Locate the actual source file to be included in the album.
+ if (sourceFile.length() > 0) // No source file for this frame (just module/address)
+ album.addFile(new Path(findSourceFile(sourceFile)));
+ return contextElement;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void loadSnapshot(Element element) {
+ // fix up registers to use integers again
+ Map<String, String> preservedRegisters = (Map<String, String>) properties.get(PRESERVED_REGISTERS);
+ if (preservedRegisters != null) {
+ Map<Integer, BigInteger> newPreservedRegisters = new HashMap<Integer, BigInteger>();
+ for (Map.Entry<String, String> entry : preservedRegisters.entrySet()) {
+ newPreservedRegisters.put(Integer.valueOf(entry.getKey().toString()), new BigInteger(entry.getValue().toString()));
+ }
+ properties.put(PRESERVED_REGISTERS, newPreservedRegisters);
+ }
+ }
+
+ public IVariableDMContext[] getLocals() {
+ return getLocals(/* boolean useCachedVariables => */ true);
+ }
+
+ private IVariableDMContext[] getLocals(boolean useCachedVariables) {
+ // may need to refresh the locals list because "Show All Variables"
+ // toggle has changed
+ if (showAllVariablesEnabled == null) {
+ IEclipsePreferences scope = InstanceScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
+ showAllVariablesEnabled = scope.getBoolean(IEDCSymbols.SHOW_ALL_VARIABLES_ENABLED, false);
+ }
+
+ boolean enabled = showAllVariablesEnabled.booleanValue();
+ if (locals != null) {
+ IEclipsePreferences scope = InstanceScope.INSTANCE.getNode(EDCDebugger.PLUGIN_ID);
+ enabled = scope.getBoolean(IEDCSymbols.SHOW_ALL_VARIABLES_ENABLED, showAllVariablesEnabled);
+ }
+
+ if (locals == null || !useCachedVariables || enabled != showAllVariablesEnabled) {
+ showAllVariablesEnabled = enabled;
+ locals = new ArrayList<VariableDMC>();
+ localsByName.clear();
+ thisPtrs.clear();
+ IEDCSymbols symbolsService = getService(IEDCSymbols.class);
+ IFunctionScope scope = symbolsService
+ .getFunctionAtAddress(executionDMC.getSymbolDMContext(), instructionPtrAddress);
+ if (scope != null) {
+ this.variableScope = scope;
+ }
+
+ while (scope != null && instrPtrLinkAddr != null) {
+ Collection<IVariable> scopedVariables = scope.getScopedVariables(instrPtrLinkAddr);
+ for (IVariable variable : scopedVariables) {
+ VariableDMC var = new VariableDMC(Stack.this, this, variable);
+ String name = variable.getName();
+ // because of inlined functions, debugger information may indicate that
+ // more than one "this" pointer is live at one time
+ if (name != null && name.equals("this")) {
+ thisPtrs.put(variable.getScope().getName(), variable);
+ } else {
+ // now that we've screened out compiler generated "this" variables,
+ // get rid of other compiler generated variables
+ // TODO: Allow user to choose whether to show compiler generated variables
+ if (var.getVariable().isDeclared()) {
+ VariableDMC haveLocal = localsByName.get(name);
+ if (haveLocal != null) {
+ localsByName.remove(name);
+ locals.remove(haveLocal);
+ }
+ locals.add(var);
+ localsByName.put(name, var);
+ }
+ }
+ }
+
+ // if requesting to show all variables, add file-scope globals too
+ // (this isn't nearly sufficient since globals can show up
+ // in a header while all code is in the source file)
+ IScope parentScope = null;
+ if (showAllVariablesEnabled)
+ parentScope = scope.getParent();
+ while (parentScope != null) {
+ if (parentScope instanceof ICompileUnitScope) {
+ ICompileUnitScope cuScope = ((ICompileUnitScope) parentScope);
+
+ List<ICompileUnitScope> cuScopes = null;
+ if (this.debugInfoProvider != null) {
+ cuScopes = debugInfoProvider.getCompileUnitsForFile(cuScope.getFilePath());
+ } else {
+ cuScopes = new ArrayList<ICompileUnitScope>(1);
+ cuScopes.add(cuScope);
+ }
+
+ // add the globals of all compile unit scopes for the source file
+ String cuFile = ((ICompileUnitScope) parentScope).getFilePath().toOSString();
+ for (ICompileUnitScope nextCuScope : cuScopes) {
+ Collection<IVariable> globals = nextCuScope.getVariables();
+ if (globals != null) {
+ for (IVariable variable : globals) {
+ IPath varFile = variable.getDefiningFile();
+ if (varFile != null && !varFile.toOSString().equalsIgnoreCase(cuFile))
+ continue;
+
+ VariableDMC var = new VariableDMC(Stack.this, this, variable);
+ String name = var.getName();
+ VariableDMC haveLocal = localsByName.get(name);
+ if (haveLocal != null) {
+ localsByName.remove(name);
+ locals.remove(haveLocal);
+ }
+ locals.add(var);
+ localsByName.put(name, var);
+ }
+ }
+ }
+ }
+ parentScope = parentScope.getParent();
+ }
+
+ if (!(scope.getParent() instanceof IFunctionScope))
+ break;
+ scope = (IFunctionScope) scope.getParent();
+ }
+ }
+
+ // start with "this" pointers, if any
+ VariableDMC[] localsArray = new VariableDMC[(thisPtrs.isEmpty() ? 0 : 1) + locals.size()];
+ int i = 0;
+ if (!thisPtrs.isEmpty())
+ localsArray[i++] = new VariableDMC(Stack.this, this, getOuterThis());
+ // TODO For now, turn off ability to see multiple this pointers
+ // of the form "this$ScopeName"
+// for (IVariable variable : thisPtrs.values()) {
+// VariableDMC var = new VariableDMC(Stack.this, this, variable);
+// var.setName("this$" + variable.getScope().getName());
+// localsArray[i++] = var;
+// }
+ for (VariableDMC var : locals)
+ localsArray[i++] = var;
+ return localsArray;
+ }
+
+ /**
+ * From a list of "this" pointers in scope, return the one from the outermost scope
+ * @return this pointer from the outermost scope
+ */
+ private IVariable getOuterThis() {
+ if (thisPtrs.isEmpty())
+ return null;
+
+ if (thisPtrs.size() == 1)
+ return thisPtrs.values().iterator().next();
+
+ IVariable outer = null;
+ for (IVariable variable : thisPtrs.values()) {
+ if (outer == null)
+ outer = variable;
+ else {
+ IScope outerScope = outer.getScope();
+ IScope variableScope = variable.getScope();
+ if ( variableScope.getLowAddress().compareTo(outerScope.getLowAddress()) < 0
+ || variableScope.getHighAddress().compareTo(outerScope.getHighAddress()) > 0)
+ outer = variable;
+ }
+ }
+ return outer;
+ }
+
+
+ /**
+ * Find a variable or enumerator by name
+ *
+ * @param name required name of the variable or enumerator
+ * @param qualifiedName optional fully qualified name of the variable or enumerator
+ * @param localsOnly whether to restrict search to local variables and enumerators only
+ * @return variable or enumerator, if found; otherwise, null
+ * @since 2.0
+ */
+ public IVariableEnumeratorContext findVariableOrEnumeratorByName(String name, String qualifiedName, boolean localsOnly) {
+ if (name == null)
+ return null;
+
+ if (locals == null)
+ getLocals();
+
+ // quickly check for a local variable or enumerator
+ IVariableEnumeratorContext variableOrEnumerator;
+
+ if (qualifiedName != null) {
+ variableOrEnumerator = localsByName.get(qualifiedName);
+ if (variableOrEnumerator != null)
+ return variableOrEnumerator;
+ }
+
+ variableOrEnumerator = localsByName.get(name);
+ if (variableOrEnumerator != null)
+ return variableOrEnumerator;
+
+ if (enumerators == null)
+ getEnumerators();
+
+ if (qualifiedName != null) {
+ variableOrEnumerator = enumeratorsByName.get(qualifiedName);
+ if (variableOrEnumerator != null)
+ return variableOrEnumerator;
+ }
+
+ variableOrEnumerator = enumeratorsByName.get(name);
+ if (variableOrEnumerator != null)
+ return variableOrEnumerator;
+
+ if (name.equals("this")) {
+ if (thisPtrs.isEmpty())
+ return null;
+ return new VariableDMC(Stack.this, this, getOuterThis());
+ }
+
+ // TODO For now, turn off ability to see multiple this pointers
+ // of the form "this$ScopeName"
+// if (name.startsWith("this$")) {
+// // return the one with the right scope
+// if (thisPtrs.isEmpty())
+// return null;
+// IVariable variable = thisPtrs.get(name.substring("this$".length()));
+// if (variable == null)
+// return null;
+// return new VariableDMC(Stack.this, this, variable);
+// }
+
+ if (localsOnly || this.getVariableScope() == null)
+ return null;
+
+ // if there is no local variable or enumerator with this name, not very
+ // efficiently check enclosing scopes for a variable or enumerator
+ IScope variableScope = this.getVariableScope().getParent();
+
+ // to find file scope variables, we may need to check several compile units
+ // associated with one file
+ ArrayList<IScope> scopes = new ArrayList<IScope>();
+
+ while (variableOrEnumerator == null && variableScope != null) {
+ // At the module level, match against globals across the entire symbol
+ // file, even for big symbol files.
+ if (variableScope instanceof IModuleScope) {
+ Collection<IVariable> variables = ((IModuleScope)variableScope).getVariablesByName(qualifiedName != null ? qualifiedName : name, true);
+ if (variables.size() > 0) {
+ // list may contain non-global variables, so return the first global
+ for (Object varObject : variables) {
+ if (varObject instanceof IVariable) {
+ IVariable variable = (IVariable)varObject;
+ if (variable.getScope() instanceof IModuleScope) {
+ variableOrEnumerator = new VariableDMC(Stack.this, this, variable);
+ break;
+ }
+ }
+ }
+ }
+ // module scope has no matching global variables
+ break;
+ }
+
+ scopes.clear();
+
+ if (variableScope instanceof ICompileUnitScope) {
+ // there may be several compile units for a file
+
+ // find the module scope parent of the compile unit
+ IScope parent = variableScope.getParent();
+ while (parent != null && !(parent instanceof IModuleScope))
+ parent = parent.getParent();
+
+ // find all compile units for the file
+ if (parent != null) {
+ IPath currentFile = ((ICompileUnitScope)variableScope).getFilePath();
+ if (currentFile != null)
+ for (ICompileUnitScope cu : ((IModuleScope)parent).getCompileUnitsForFile(currentFile))
+ scopes.add(cu);
+ }
+ }
+
+ if (scopes.isEmpty())
+ scopes.add(variableScope);
+
+ for (IScope scope : scopes) {
+ for (IVariable scopeVariable : scope.getVariables()) {
+ String scopeVariableName = scopeVariable.getName();
+ if (qualifiedName != null && scopeVariableName.equals(qualifiedName)) {
+ variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
+ break;
+ }
+
+ if (scopeVariableName.equals(name)) {
+ variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
+ break;
+ }
+ }
+
+ if (variableOrEnumerator == null && scope instanceof IFunctionScope) {
+ IFunctionScope functionScope = (IFunctionScope)scope;
+ for (IVariable scopeVariable : functionScope.getParameters()) {
+ String scopeVariableName = scopeVariable.getName();
+ if (qualifiedName != null && scopeVariableName.equals(qualifiedName)) {
+ variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
+ break;
+ }
+
+ if (scopeVariableName.equals(name)) {
+ variableOrEnumerator = new VariableDMC(Stack.this, this, scopeVariable);
+ break;
+ }
+ }
+ }
+
+ if (variableOrEnumerator == null) {
+ for (IEnumerator scopeEnumerator : scope.getEnumerators()) {
+ String scopeEnumeratorName = scopeEnumerator.getName();
+ if (qualifiedName != null && scopeEnumeratorName.equals(qualifiedName)) {
+ variableOrEnumerator = new EnumeratorDMC(this, scopeEnumerator);
+ break;
+ }
+
+ if (scopeEnumeratorName.equals(name)) {
+ variableOrEnumerator = new EnumeratorDMC(this, scopeEnumerator);
+ break;
+ }
+ }
+ }
+ }
+
+ variableScope = variableScope.getParent();
+ }
+
+ return variableOrEnumerator;
+ }
+
+ public IScope getVariableScope() {
+ return variableScope;
+ }
+
+ public EnumeratorDMC[] getEnumerators() {
+ if (enumerators == null) {
+ enumerators = new ArrayList<EnumeratorDMC>();
+ if (getServicesTracker() != null) {
+ IEDCSymbols symbolsService = getService(Symbols.class);
+ if (executionDMC != null && symbolsService != null) {
+ IFunctionScope scope = symbolsService.getFunctionAtAddress(executionDMC.getSymbolDMContext(),
+ instructionPtrAddress);
+ while (scope != null) {
+ Collection<IEnumerator> localEnumerators = scope.getEnumerators();
+ for (IEnumerator enumerator : localEnumerators) {
+ EnumeratorDMC enumeratorDMC = new EnumeratorDMC(this, enumerator);
+ enumerators.add(enumeratorDMC);
+ enumeratorsByName.put(enumerator.getName(), enumeratorDMC);
+ }
+ if (!(scope.getParent() instanceof IFunctionScope))
+ break;
+ scope = (IFunctionScope) scope.getParent();
+ }
+ }
+ }
+ }
+ return enumerators.toArray(new EnumeratorDMC[enumerators.size()]);
+ }
+
+ public EnumeratorDMC findEnumeratorbyName(String name) {
+ if (enumerators == null)
+ getEnumerators();
+ return enumeratorsByName.get(name);
+ }
+
+ /**
+ * Get the view onto registers for this stack frame. For the top stack frame, this
+ * forwards to the {@link Registers} service. Otherwise, this information
+ * is synthesized from unwind information in the debug information.
+ * @return {@link IFrameRegisters}, never <code>null</code>
+ */
+ @SuppressWarnings("unchecked")
+ public IFrameRegisters getFrameRegisters() {
+ if (frameRegisters == null) {
+ if (level == 0) {
+ // for top of stack, the registers service does the work
+ final Registers registers = getEDCServicesTracker().getService(Registers.class);
+ frameRegisters = new CurrentFrameRegisters(executionDMC, registers);
+ } else {
+ // see if symbolics can provide unwinding support
+ if (module != null) {
+ Symbols symbolsService = getService(Symbols.class);
+ IFrameRegisterProvider frameRegisterProvider = symbolsService.getFrameRegisterProvider(
+ executionDMC.getSymbolDMContext(), instructionPtrAddress);
+ if (frameRegisterProvider != null) {
+ try {
+ frameRegisters = frameRegisterProvider.getFrameRegisters(
+ getSession(), getEDCServicesTracker(), this);
+ } catch (CoreException e) {
+ // debug info failure; we should report this
+ frameRegisters = new AlwaysFailingFrameRegisters(e);
+ }
+ }
+ }
+
+ if (frameRegisters == null) {
+ // no information from symbolics; see if the stack unwinder found anything
+ final Map<Integer, BigInteger> preservedRegisters = (Map<Integer,BigInteger>) properties.get(
+ PRESERVED_REGISTERS);
+ if (preservedRegisters != null) {
+ frameRegisters = new PreservedFrameRegisters(dsfServicesTracker, StackFrameDMC.this, preservedRegisters);
+ }
+ }
+
+ if (frameRegisters == null) {
+ frameRegisters = new AlwaysFailingFrameRegisters(
+ EDCDebugger.newCoreException("cannot read variables in this frame"));
+ }
+ }
+ }
+ return frameRegisters;
+ }
+
+ /**
+ * Get the frame this one has called.
+ * @return StackFrameDMC or <code>null</code> for top of stack
+ */
+ public StackFrameDMC getCalledFrame() throws CoreException {
+ return calledFrame;
+ }
+
+ /**
+ * Get a type engine (which holds cached information about types for use by expressions)
+ * @return TypeEngine instance
+ */
+ public TypeEngine getTypeEngine() {
+ return typeEngine;
+ }
+
+ public IEDCModuleDMContext getModule() {
+ return module;
+ }
+
+ private Stack getOuterType() {
+ return Stack.this;
+ }
+
+ }
+
+ public class VariableData implements IVariableDMData {
+
+ private final String name;
+
+ public VariableData(VariableDMC variableDMC) {
+ name = variableDMC.getName();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return "0";
+ }
+
+ }
+
+ public class VariableDMC extends DMContext implements IVariableDMContext, IVariableEnumeratorContext {
+
+ public static final String PROP_LOCATION = "Location";
+ private final IVariable variable;
+
+ public VariableDMC(IDsfService service, StackFrameDMC frame, IVariable variable) {
+ super(Stack.this, new IDMContext[] { frame }, variable.getName(), variable.getName());
+ this.variable = variable;
+ }
+
+ public IVariable getVariable() {
+ return variable;
+ }
+ }
+
+ public class EnumeratorDMC extends DMContext implements IEnumeratorDMContext, IVariableEnumeratorContext {
+
+ private final IEnumerator enumerator;
+
+ public EnumeratorDMC(StackFrameDMC frame, IEnumerator enumerator) {
+ super(Stack.this, new IDMContext[] { frame }, enumerator.getName(), enumerator.getName());
+ this.enumerator = enumerator;
+ }
+
+ public IEnumerator getEnumerator() {
+ return enumerator;
+ }
+ }
+
+ /**
+ * @param classNames
+ * the type names the service will be registered under. See
+ * AbstractDsfService#register for details. We tack on base DSF's
+ * IStack and this class to the list if not provided.
+ */
+ public Stack(DsfSession session, String[] classNames) {
+ super(session,
+ massageClassNames(classNames,
+ new String[] { IStack.class.getName(), Stack.class.getName() }));
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static String createFrameID(IEDCExecutionDMC executionDMC, EdcStackFrame edcFrame) {
+ int level = (Integer) edcFrame.props.get(StackFrameDMC.LEVEL_INDEX);
+ String parentID = executionDMC.getID();
+ return parentID + ".frame[" + level + "]";
+ }
+
+ @Override
+ protected void doInitialize(RequestMonitor requestMonitor) {
+ super.doInitialize(requestMonitor);
+ getSession().addServiceEventListener(this, null);
+ }
+
+ public void getArguments(IFrameDMContext frameCtx, DataRequestMonitor<IVariableDMContext[]> rm) {
+ // never called by DSF. it expects arguments to be lumped in with
+ // locals.
+ rm.done();
+ }
+
+ public void getFrameData(IFrameDMContext frameDmc, DataRequestMonitor<IFrameDMData> rm) {
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(frameDmc)); }
+ rm.setData(new StackFrameData((StackFrameDMC) frameDmc));
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(rm.getData())); }
+ rm.done();
+ }
+
+ public void getFrames(final IDMContext execContext, final DataRequestMonitor<IFrameDMContext[]> rm) {
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(execContext)); }
+
+ final ExecutionDMC execDmc = DMContexts.getAncestorOfType(execContext, ExecutionDMC.class);
+ if (execDmc != null)
+ {
+ if (!execDmc.isSuspended())
+ {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "Context is running: " + execDmc, null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ asyncExec(new Runnable() {
+ public void run() {
+ try {
+ rm.setData(getFramesForDMC((ExecutionDMC) execContext, 0, ALL_FRAMES));
+ if (rm.getData().length == 0)
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "No stack frame available for: " + execDmc, null)); //$NON-NLS-1$
+ } catch (CoreException e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
+ EDCDebugger.getMessageLogger().log(s);
+ rm.setStatus(s);
+ }
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(rm.getData())); }
+ rm.done();
+ }
+
+ }, rm);
+
+ }
+ else {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
+ rm.done();
+ }
+ }
+
+ public void getLocals(final IFrameDMContext frameCtx, final DataRequestMonitor<IVariableDMContext[]> rm) {
+ asyncExec(new Runnable() {
+ public void run() {
+ final StackFrameDMC frameContext = (StackFrameDMC) frameCtx;
+ IAddress contextIPAddress = frameContext.getInstructionPtrAddress();
+ boolean useVariableCache = false;
+ // the frame context passed in may be "stale". it may prove equal to the current frame,
+ // but if the instruction ptr address is different, then the locals won't be collected properly
+ try {
+ IFrameDMContext[] iFrames = getFramesForDMC(frameContext.getExecutionDMC(), 0, ALL_FRAMES);
+ for (IFrameDMContext iFrameDMC : iFrames) {
+ if (frameCtx == iFrameDMC) {
+ useVariableCache = true;
+ break;
+ }
+ if (frameContext.equals(iFrameDMC)) {
+ StackFrameDMC frameDMC = (StackFrameDMC)iFrameDMC;
+ IAddress stackFrameIPAddr = frameDMC.getInstructionPtrAddress();
+ if (contextIPAddress.equals(stackFrameIPAddr)) {
+ useVariableCache = true;
+ } else {
+ frameContext.setInstructionPtrAddress(stackFrameIPAddr);
+ }
+ break;
+ }
+ }
+
+ rm.setData(frameContext.getLocals(useVariableCache));
+ } catch (CoreException e) {
+ EDCDebugger.getMessageLogger().log(e.getStatus());
+ rm.setStatus(e.getStatus());
+ }
+ rm.done();
+ }
+ }, rm);
+ }
+
+ public void getStackDepth(IDMContext dmc, final int maxDepth, final DataRequestMonitor<Integer> rm) {
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { dmc, maxDepth })); }
+
+ final ExecutionDMC execDmc = DMContexts.getAncestorOfType(dmc, ExecutionDMC.class);
+ if (execDmc != null)
+ {
+ if (!execDmc.isSuspended())
+ {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "Context is running: " + execDmc, null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ asyncExec(new Runnable() {
+ public void run() {
+ int startFrame = 0;
+ int endFrame = ALL_FRAMES;
+ if (maxDepth > 0)
+ endFrame = maxDepth - 1;
+ try {
+ rm.setData(getFramesForDMC(execDmc, startFrame, endFrame).length);
+ if (rm.getData() == 0)
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "No stack frame available for: " + execDmc, null)); //$NON-NLS-1$
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, rm.getData()); }
+ } catch (CoreException e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
+ EDCDebugger.getMessageLogger().log(s);
+ rm.setStatus(s);
+ }
+ rm.done();
+ }
+ }, rm);
+ }
+ else {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
+ rm.done();
+ }
+ }
+
+ public void getTopFrame(final IDMContext execContext, final DataRequestMonitor<IFrameDMContext> rm) {
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(execContext)); }
+
+ asyncExec(new Runnable() {
+ public void run() {
+ try {
+ IFrameDMContext[] frames = getFramesForDMC((ExecutionDMC) execContext, 0, 0);
+ if (frames.length == 0) {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE,
+ "No top stack frame available", null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+ rm.setData(frames[0]);
+ } catch (CoreException e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
+ EDCDebugger.getMessageLogger().log(s);
+ rm.setStatus(s);
+ }
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(rm.getData())); }
+ rm.done();
+ }
+ }, rm);
+
+ }
+
+ public void getVariableData(IVariableDMContext variableDmc, DataRequestMonitor<IVariableDMData> rm) {
+ rm.setData(new VariableData((VariableDMC) variableDmc));
+ rm.done();
+ }
+
+ @SuppressWarnings("unchecked")
+ public void getModelData(IDMContext dmc, DataRequestMonitor<?> rm) {
+ if (dmc instanceof IFrameDMContext) {
+ getFrameData((IFrameDMContext) dmc, (DataRequestMonitor<IFrameDMData>) rm);
+ } else if (dmc instanceof IVariableDMContext) {
+ getVariableData((IVariableDMContext) dmc, (DataRequestMonitor<IVariableDMData>) rm);
+ } else
+ rm.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.cdt.dsf.debug.service.IStack#getFrames(org.eclipse.cdt.dsf.datamodel.IDMContext, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
+ */
+ public void getFrames(final IDMContext execContext, final int startIndex, final int endIndex, final DataRequestMonitor<IFrameDMContext[]> rm) {
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { execContext, startIndex, endIndex })); }
+ final ExecutionDMC execDmc = DMContexts.getAncestorOfType(execContext, ExecutionDMC.class);
+ if (execDmc != null)
+ {
+ if (!execDmc.isSuspended())
+ {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "Context is running: " + execDmc, null)); //$NON-NLS-1$
+ rm.done();
+ return;
+ }
+
+ asyncExec(new Runnable() {
+ public void run() {
+ try {
+ rm.setData(getFramesForDMC((ExecutionDMC) execContext, startIndex, endIndex));
+ if (rm.getData().length == 0)
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_STATE, "No stack frame available for: " + execContext, null)); //$NON-NLS-1$
+ } catch (CoreException e) {
+ Status s = new Status(IStatus.ERROR, EDCDebugger.getUniqueIdentifier(), null, e);
+ EDCDebugger.getMessageLogger().log(s);
+ rm.setStatus(s);
+ }
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArgs(rm.getData())); }
+ rm.done();
+ }
+
+ }, rm);
+
+ }
+ else {
+ rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INVALID_HANDLE, "Invalid context", null)); //$NON-NLS-1$
+ rm.done();
+ }
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArgs(rm.getData())); }
+ }
+
+ public IFrameDMContext[] getFramesForDMC(IEDCExecutionDMC context, int startIndex, int endIndex) throws CoreException {
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context, startIndex, endIndex })); }
+
+ if (!context.isSuspended() ||
+ ! RunControl.isNonContainer(context)) // no frames for container context.
+ {
+ return new IFrameDMContext[0];
+ }
+
+ boolean needsUpdate = false;
+ synchronized (stackFrames) {
+ List<StackFrameDMC> frames = stackFrames.get(context.getID());
+ // Need to update the frames if there is no cached list for this
+ // context or if the cached list does not include all of the
+ // requested frames.
+ if (frames == null) {
+ // nothing in the cache so need to update
+ needsUpdate = true;
+ } else if (allFramesCached.get(context.getID())) {
+ // all frames are cached
+ needsUpdate = false;
+ } else if (endIndex == ALL_FRAMES) {
+ // some but not all frames cached
+ needsUpdate = true;
+ } else {
+ // some but not all requested frames cached
+ needsUpdate = (frames.get(0).getLevel() > startIndex ||
+ frames.get(frames.size() - 1).getLevel() < endIndex);
+ }
+
+ if (needsUpdate)
+ updateFrames(context, startIndex, endIndex);
+
+ frames = stackFrames.get(context.getID());
+ // endIndex is inclusive and may be negative to fetch all frames
+ if (endIndex >= 0) {
+ if (startIndex < frames.size() && startIndex <= endIndex) {
+ frames = frames.subList(startIndex, Math.min(endIndex + 1, frames.size()));
+ } else {
+ frames = Collections.emptyList();
+ }
+ }
+ IFrameDMContext[] result = frames.toArray(new IFrameDMContext[frames.size()]);
+ if (EDCTrace.STACK_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArgs(result)); }
+ return result;
+ }
+ }
+
+ private void updateFrames(IEDCExecutionDMC context, int startIndex, int endIndex) throws CoreException {
+ ArrayList<StackFrameDMC> frames = new ArrayList<StackFrameDMC>();
+ List<EdcStackFrame> edcFrames = computeStackFrames(context, startIndex, endIndex);
+ StackFrameDMC previous = null;
+ for (EdcStackFrame edcFrame : edcFrames) {
+ StackFrameDMC frame = new StackFrameDMC(context, edcFrame);
+ if (previous != null) {
+ frame.calledFrame = previous;
+ // note: don't store "callerFrame" since this is missing if only a partial stack was fetched
+ }
+ frames.add(frame);
+ previous = frame;
+ }
+
+ stackFrames.put(context.getID(), frames);
+
+ // all frames are cached if we request all frames, or if the returned number of frames was less than
+ // the requested max number of frames. e.g. if we ask for 10 and they return 9, it's because there
+ // are only 9 frames. so we have calculated all of them.
+ allFramesCached.put(context.getID(), startIndex == 0 && ((endIndex == ALL_FRAMES) || (frames.size() <= endIndex)));
+ }
+
+ /**
+ * A stack frame described as one or more of the following properties, plus
+ * any additional custom ones.
+ *
+ * <ul>
+ * <li>{@link StackFrameDMC#LEVEL_INDEX}
+ * <li>{@link StackFrameDMC#ROOT_FRAME}
+ * <li>{@link StackFrameDMC#BASE_ADDR}
+ * <li>{@link StackFrameDMC#INSTRUCTION_PTR_ADDR}
+ * <li>{@link StackFrameDMC#MODULE_NAME}
+ * <li>{@link StackFrameDMC#SOURCE_FILE}
+ * <li>{@link StackFrameDMC#FUNCTION_NAME}
+ * <li>{@link StackFrameDMC#LINE_NUMBER}
+ * <li>{@link StackFrameDMC#IN_PROLOGUE}
+ * <li>{@link StackFrameDMC#PRESERVED_REGISTERS}
+ * </ul>
+ *
+ * @since 2.0
+ */
+ public class EdcStackFrame {
+ public EdcStackFrame(Map<String, Object> props) {
+ this.props = props;
+ }
+ public Map<String, Object> props;
+ }
+
+ protected abstract List<EdcStackFrame> computeStackFrames(IEDCExecutionDMC context, int startIndex, int endIndex) throws CoreException;
+
+ public void loadFramesForContext(IEDCExecutionDMC exeDmc, Element allFrames) throws Exception {
+ flushCache(null);
+ List<StackFrameDMC> frames = Collections.synchronizedList(new ArrayList<StackFrameDMC>());
+
+ NodeList frameElements = allFrames.getElementsByTagName(STACK_FRAME);
+
+ int numFrames = frameElements.getLength();
+ StackFrameDMC previousFrameDMC = null;
+
+ for (int i = 0; i < numFrames; i++) {
+ Element groupElement = (Element) frameElements.item(i);
+ Element propElement = (Element) groupElement.getElementsByTagName(SnapshotUtils.PROPERTIES).item(0);
+ HashMap<String, Object> properties = new HashMap<String, Object>();
+ SnapshotUtils.initializeFromXML(propElement, properties);
+
+ // ensure that stack level numbering is canonical:
+ // we expect level==0 to be the top, but it used to be 1
+ properties.put(StackFrameDMC.LEVEL_INDEX, i);
+
+ StackFrameDMC frameDMC = new StackFrameDMC(exeDmc, new EdcStackFrame(properties));
+ frameDMC.loadSnapshot(groupElement);
+ if (previousFrameDMC != null) {
+ frameDMC.calledFrame = previousFrameDMC;
+ }
+ frames.add(frameDMC);
+
+ previousFrameDMC = frameDMC;
+ }
+ stackFrames.put(exeDmc.getID(), frames);
+ allFramesCached.put(exeDmc.getID(), true);
+ }
+
+ public void flushCache(IDMContext context) {
+ if (isSnapshot())
+ return;
+ if (context != null && context instanceof IEDCDMContext) {
+ String contextID = ((IEDCDMContext) context).getID();
+ stackFrames.remove(contextID);
+ allFramesCached.remove(contextID);
+ } else {
+ stackFrames.clear();
+ allFramesCached.clear();
+ }
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(ISuspendedDMEvent e) {
+ flushCache(e.getDMContext());
+ }
+
+ @DsfServiceEventHandler
+ public void eventDispatched(IResumedDMEvent e) {
+ flushCache(e.getDMContext());
+ }
+
+}
--- /dev/null
+AbstractEDCService_0=The EDC session thread pool has been overwhelmed. This may be indicative of either a problem with the debugger or an unexpectedly high concentration of activity. The latter can be remedied. Contact the CDT development team for more info.
+AbstractEDCService_1=Unhandled exception
+AbstractEDCService_2=Unhandled exception
+AbstractEDCService_3=User cancelled operation
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.snapshot;
+
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.cdt.debug.core.sourcelookup.MappingSourceContainer;
+import org.eclipse.cdt.debug.edc.internal.snapshot.Snapshot;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
+
+public interface IAlbum {
+
+ public String getName();
+
+ public String getDisplayName();
+
+ public String getSessionID();
+
+ public String getRecordingSessionID();
+
+ public void openSnapshot(final int index);
+
+ /**
+ * Zero based index
+ *
+ * @return current index of snapshot being played
+ */
+ public int getCurrentSnapshotIndex();
+
+ public void openNextSnapshot() throws Exception;
+
+ public void openPreviousSnapshot() throws Exception;
+
+ /**
+ * Get the location of the album contents, extracted to disk in the workspace.
+ * @return path to the extracted files
+ */
+ public IPath getAlbumRootDirectory();
+
+ public String getLaunchTypeID();
+
+ public HashMap<String, Object> getLaunchProperties();
+
+ public String getLaunchName();
+
+
+ public void addFile(IPath path);
+
+ public IPath getLocation();
+
+ public void configureSourceLookupDirector(ISourceLookupDirector director);
+
+ public void configureMappingSourceContainer(
+ MappingSourceContainer mappingContainer);
+
+ public List<Snapshot> getSnapshots();
+
+ public boolean isLoaded();
+
+ public boolean isRecording();
+
+ /**
+ * @since 2.0
+ */
+ public void playSnapshots();
+
+ /**
+ * @since 2.0
+ */
+ public boolean isPlayingSnapshots();
+
+ /**
+ * @since 2.0
+ */
+ public void stopPlayingSnapshots();
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.snapshot;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * The Interface ISnapshotContributor provides a way for objects to contribute data to
+ * a debug snapshot.
+ */
+public interface ISnapshotContributor {
+
+ /**
+ * Take a snapshot of this object's data.
+ *
+ * @param album the owning snapshot album
+ * @param document the xml document used to store the data
+ * @param monitor the progress monitor to use for reporting progress to the user. It is the caller's responsibility
+ to call done() on the given monitor. Accepts null, indicating that no progress should be
+ reported and that the operation cannot be canceled.
+ * @return the xml element containing the saved data.
+ * @since 2.0
+ */
+ public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor) throws Exception;
+
+ /**
+ * Load an object's data from a previously saved snapshot.
+ *
+ * @param element the xml element containing the data
+ * @throws Exception if anything goes wrong
+ */
+ public void loadSnapshot(Element element) throws Exception;
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Collection;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Interface representing a compile unit scope. A compile unit is a source or
+ * header file which contains functions.
+ */
+public interface ICompileUnitScope extends IScope {
+
+ /**
+ * Get the path to the compile unit file (source/header)
+ *
+ * @return the file path as defined in the symbolics
+ */
+ IPath getFilePath();
+
+ /**
+ * Get the function at the given link address
+ *
+ * @param linkAddress
+ * the link address
+ * @return the function, or null if none found
+ */
+ IFunctionScope getFunctionAtAddress(IAddress linkAddress);
+
+
+ /**
+ * Get all the top-level functions in the compilation unit.
+ * (This ensures the CU is parsed, unlike {@link #getChildren()}.)
+ * @return the list of functions
+ * @return an unmodifiable list of functions, maybe empty
+ */
+ Collection<IFunctionScope> getFunctions();
+
+ /**
+ * Get the list of line table entries for the entire compile unit.
+ * These may represent contributions for multiple files (e.g.,
+ * sources and #included headers).
+ *
+ * @return unmodifiable list of line entries, which may be empty
+ */
+ Collection<ILineEntry> getLineEntries();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * This interface handles fetching debug information from a particular
+ * debug information format.
+ */
+public interface IDebugInfoProvider {
+ /**
+ * Dispose resources held by the provider
+ */
+ void dispose();
+
+ /**
+ * Get the absolute host path to the symbolics
+ * @return IPath, never <code>null</code>
+ */
+ IPath getSymbolFile();
+
+ /**
+ * Get the corresponding executable used for symbolics. This may be different from the
+ * running executable.
+ */
+ IExecutableSymbolicsReader getExecutableSymbolicsReader();
+
+ /**
+ * Get the module scope, which contains the unified set of global code
+ * and symbols (and, implicitly, the types they reference) accessible at runtime.
+ */
+ IModuleScope getModuleScope();
+
+ /**
+ * Get the paths to all source files known for the module.
+ * @return non-<code>null</code> array of build-time paths
+ */
+ String[] getSourceFiles(IProgressMonitor monitor);
+
+ /**
+ * Get all functions with the given name in any scope in the module
+ *
+ * @param name
+ * the function name
+ * @return unmodifiable list of functions, which may be empty
+ */
+ Collection<IFunctionScope> getFunctionsByName(String name);
+
+ /**
+ * Get all variables with the given name in any scope in the module
+ *
+ * @param name
+ * the variable name, or <code>null</code> to get all variables
+ * @param globalsOnly
+ * whether to assume local variables in scope have been recorded
+ * and only globally-visible variables remain to be found
+ * @return unmodifiable list of variables, which may be empty
+ */
+ Collection<IVariable> getVariablesByName(String name, boolean globalsOnly);
+
+ /**
+ * Get the compile unit at the given link address
+ *
+ * @param address
+ * the link address
+ * @return the compile unit at the address, or null if none
+ */
+ ICompileUnitScope getCompileUnitForAddress(IAddress linkAddress);
+
+ /**
+ * Get the compile unit for the given file
+ *
+ * @param filePath
+ * the file path as defined in the symbolics
+ * @return a list of the compile units for this file, or null if none
+ */
+ List<ICompileUnitScope> getCompileUnitsForFile(IPath filePath);
+
+ /**
+ * Get the type with the given name
+ * @param name
+ * canonical base type name (trimmed, multiple spaces replaced
+ * by single spaces, no "*" chars)
+ * @return unmodifiable list of types, which may be empty
+ */
+ Collection<IType> getTypesByName(String name);
+
+ /**
+ * Get all the types declared in the module (which may
+ * include forward definitions).
+ * @return unmodifiable list of types, which may be empty.
+ */
+ Collection<IType> getTypes();
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * This factory creates debug info providers for files.
+ */
+public interface IDebugInfoProviderFactory {
+ String EXTENSION_ID = "org.eclipse.cdt.debug.edc.debugInfoProviderFactory"; //$NON-NLS-1$
+
+ /**
+ * Create a debug info provider for the given executable reader. This can
+ * fetch information from the executable itself or reference an associated
+ * symbolics file (e.g. *.sym or *.dbg).
+ * @param binaryFile the host-side path of the binary
+ * @param reader the reader for the executable, or <code>null</code>
+ * @return a new {@link IDebugInfoProvider} or <code>null</code> if unsupported
+ */
+ IDebugInfoProvider createDebugInfoProvider(IPath binaryFile, IExecutableSymbolicsReader reader);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.cdt.core.ISymbolReader;
+
+/**
+ * Top-level interface for getting symbolics information. The executable
+ * symbolics reader and IDebugInfoProvider (behind the scenes) work together
+ * to provide the full information. The symbolics
+ * information includes debug information (e.g. DWARF, CODEVIEW) and symbol
+ * table data. A binary file may only have symbol table without debug
+ * information.
+ */
+public interface IEDCSymbolReader extends IExecutableSymbolicsReader, ISymbolReader {
+ /**
+ * Call when the reader is no longer needed and should free up any resources.
+ */
+ void shutDown();
+
+ /**
+ * Check whether the symbol file has debug information recognized by this
+ * reader. The debug information here means data in the binary file that's
+ * specially for debugger, such as DWARF and CODEVIEW. The symbol table
+ * section data in many types of binary files is not considered debug
+ * information here.
+ *
+ * @return true if the symbol reader has dedicated debug information
+ */
+ boolean hasRecognizedDebugInformation();
+
+ /**
+ * Get the module-level scope for the primary symbol file
+ * @return scope, never <code>null</code>
+ * @see #getSymbolFile()
+ */
+ IModuleScope getModuleScope();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+/**
+ * Interface representing an enumerator (what's inside enum {}). E.g.,
+* enum {Red, Green, Blue} has enumerators Red, Green, Blue
+ */
+public interface IEnumerator {
+
+ /**
+ * Get the name of the enumerator
+ *
+ * @return the enumerator name
+ */
+ String getName();
+
+ /**
+ * Get the value of the enumerator.
+ *
+ * @return the enumerator value
+ */
+ long getValue();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.cdt.debug.edc.IStreamBuffer;
+
+/**
+ * This abstracts access to a particular section in an executable
+ */
+public interface IExecutableSection {
+ /**
+ * Get the name of the section.
+ */
+ String getName();
+
+ /**
+ * Get the buffer for the section. This may be thrown away and reloaded on demand.
+ * The buffer has the correct endianness already set.
+ * @return buffer, or <code>null</code> if failed to load
+ */
+ IStreamBuffer getBuffer();
+
+ /**
+ * Free the buffer allocated for this section.
+ */
+ void dispose();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.nio.ByteOrder;
+import java.util.Collection;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISection;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Interface for handling different kinds of executables. This manages
+ * the mid-level access to different executable formats (ELF, PE-COFF, etc.).
+ */
+public interface IExecutableSymbolicsReader {
+ /**
+ * Dispose the reader and any files or memory it is holding
+ */
+ void dispose();
+
+ /**
+ * Get the file represented by this reader
+ */
+ IPath getSymbolFile();
+
+ /**
+ * Get the executable sections from the symbol file (names and buffers)
+ * @return unmodifiable list of sections
+ */
+ Collection<IExecutableSection> getExecutableSections();
+
+ /**
+ * Get a section with the given name
+ * @param sectionName
+ * @return {@link IExecutableSection} or <code>null</code>
+ */
+ IExecutableSection findExecutableSection(String sectionName);
+
+ /**
+ * Get the (low level) sections from the symbol file
+ *
+ * @return unmodifiable list of sections
+ */
+ Collection<ISection> getSections();
+
+ /**
+ * Get the base image address generated by the linker
+ *
+ * @return the base address, or null if unknown
+ */
+ IAddress getBaseLinkAddress();
+
+ /**
+ * Get the modification date of the symbol file when it was parsed. This is
+ * used for caching purposes.
+ *
+ * @return the modification date (e.g. file.lastModified())
+ */
+ long getModificationDate();
+
+ /**
+ * Find symbol(s) with the given name. If no exact
+ * matches are found, also search for a symbol whose
+ * undecorated variant matches the name (e.g. in Win32, "_main" -> "main",
+ * "__imp_foo@8" -> "foo").
+ * @param name name of the symbol
+ * @return collection of matching symbols, may be empty
+ */
+ Collection<ISymbol> findSymbols(String name);
+
+ /**
+ * Find symbol(s) with the given unmangled name.
+ * The name will be interpreted as if spaces are irrelevant.
+ * The name will be matched as if a fully qualified name (e.g.
+ * leading "::" may be missing).
+ * If a symbol is a function and the name does not include
+ * arguments, any function with the same qualified name matches
+ * (e.g. "foo(int)" for name="foo").
+ * @param name name of unmangled string (e.g. "Foo::Bar", "main", "::std::myfunc(int)")
+ * @return collection of matching symbols, may be empty
+ */
+ Collection<ISymbol> findUnmangledSymbols(String name);
+
+ /**
+ * Get the symbols from the symbol table
+ *
+ * @return unmodifiable list of symbols
+ */
+ Collection<ISymbol> getSymbols();
+
+
+ /**
+ * Get the symbol that contains the given link address
+ *
+ * @param linkAddress
+ * the link address
+ * @return the symbol containing this address, or null if none found
+ */
+ ISymbol getSymbolAtAddress(IAddress linkAddress);
+
+ /**
+ * Get the byte order of data in the executable
+ * @return {@link ByteOrder}
+ */
+ ByteOrder getByteOrder();
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.core.runtime.IPath;
+
+public interface IExecutableSymbolicsReaderFactory {
+
+ String EXTENSION_ID = "org.eclipse.cdt.debug.edc.executableSymbolicsReaderFactory"; //$NON-NLS-1$
+
+ /**
+ * @since 2.0
+ */
+ static final int NO_CONFIDENCE = 0;
+ /**
+ * @since 2.0
+ */
+ static final int LOW_CONFIDENCE = 25;
+ /**
+ * @since 2.0
+ */
+ static final int NORMAL_CONFIDENCE = 50;
+ /**
+ * @since 2.0
+ */
+ static final int HIGH_CONFIDENCE = 75;
+
+ /**
+ * Used to help determine if this factory can read the given binary file. If so,
+ * the confidence returned is used to help determine which factory to use if more
+ * than one can read this file.
+ * @param binaryFile
+ * @return NO_CONFIDENCE if it cannot read the binary, otherwise one of LOW_CONFIDENCE,
+ * NORMAL_CONFIDENCE or HIGH_CONFIDENCE.
+ * @since 2.0
+ */
+ int getConfidence(IPath binaryFile);
+
+ /**
+ * Create an executable symbolics reader for the given binary file.
+ * @param binaryFile
+ * @return a new {@link IExecutableSymbolicsReader} or <code>null</code> if unsupported
+ */
+ IExecutableSymbolicsReader createExecutableSymbolicsReader(IPath binaryFile);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Collection;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Interface representing a function scope. A function is a block of code
+ * (function/method) which may have parameters and lexical blocks.
+ * <p>
+ * TODO: Note: a function's scope is either {@link IFunctionScope} or {@link ICompileUnitScope}.
+ * This does <b>not</b> reflect language scoping. Thus, there is currently no way to
+ * find out if a function is a method of a given class (except, perhaps, by looking
+ * at a 'this' parameter and introspecting its type). (But nothing currently handles
+ * namespaces.)
+ */
+public interface IFunctionScope extends IScope {
+
+ /**
+ * Gets the list of variables in this scope and any child scopes
+ * (for functions and lexical blocks)
+ *
+ * @return unmodifiable list of variables which may be empty
+ */
+ Collection<IVariable> getVariablesInTree();
+
+
+ /**
+ * Get function parameters
+ *
+ * @return unmodifiable list of parameters which may be empty
+ */
+ Collection<IVariable> getParameters();
+
+ /**
+ * Get the variables live at the given address, by using ILexicalBlockScope
+ * and embedded IFunctionScopes, as well as using {@link IVariable#getLocationProvider()}
+ * information.
+ * @param linkAddress the link-time address
+ * @return unmodifiable list of locals which may be empty
+ */
+ Collection<IVariable> getScopedVariables(IAddress linkAddress);
+
+ /**
+ * Get the location provider for the frame base.
+ *
+ * @return the location provider, or null if none
+ */
+ ILocationProvider getFrameBaseLocation();
+
+ /**
+ * Get the file where the function was declared, if known.
+ * @return IPath or <code>null</code>
+ */
+ IPath getDeclFile();
+
+ /**
+ * Get the line number where the function was declared, if known.
+ * @return line number, 1-based (0 if unknown)
+ */
+ int getDeclLine();
+
+ /**
+ * Get the column number where the function was declared, if known.
+ * @return column number, 1-based (0 if unknown)
+ */
+ int getDeclColumn();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+
+/**
+ * Interface representing that a variable location is invalid
+ */
+public interface IInvalidVariableLocation extends IVariableLocation {
+
+ /**
+ * Set message
+ *
+ * @param message
+ * description of why the location is not valid
+ */
+ public void setMessage(String message);
+
+ /**
+ * Get message
+ *
+ * @return description of why the location is not valid
+ */
+ public String getMessage();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Interface that represents a line table entry
+ */
+public interface ILineEntry extends Comparable<Object> {
+
+ /**
+ * Get the file path that the line entry applies to
+ *
+ * @return the file path as defined in the symbolics
+ */
+ IPath getFilePath();
+
+ /**
+ * Get the line number in the file
+ *
+ * @return the line number
+ */
+ int getLineNumber();
+
+ /**
+ * Get the column number in the line
+ *
+ * @return
+ */
+ int getColumnNumber();
+
+ /**
+ * Get the low link address of the line entry
+ *
+ * @return the low address
+ */
+ IAddress getLowAddress();
+
+ /**
+ * Get the high link address of the line entry
+ *
+ * @return the high address
+ */
+ IAddress getHighAddress();
+
+ /**
+ * Set the high link address of the line entry
+ */
+ void setHighAddress(IAddress highAddress);
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Provides line table lookup support.
+ */
+public interface ILineEntryProvider {
+
+ /**
+ * A source line and address(es) mapped to it. The address here may be
+ * runtime address or link address, depending on context in which the
+ * objects are used.
+ *
+ * @since 2.0
+ */
+ public interface ILineAddresses {
+ public int getLineNumber(); // line number
+ public IAddress[] getAddress(); // addresses mapped to the line
+ }
+
+ /**
+ * Get the line table entry for the given link address
+ *
+ * @param linkAddress
+ * the link address
+ * @return the line table entry, or null if none found
+ */
+ ILineEntry getLineEntryAtAddress(IAddress linkAddress);
+
+ /**
+ * Get the list of line table entries for the given sequence of line
+ * numbers. startLineNumber and endLineNumber can be the same if you're only
+ * interested in one source line.
+ * <p>
+ * Note that there can be multiple line entries for a single source line,
+ * due to multiple statements per line, template expansion, static functions
+ * in headers, etc.
+ *
+ * @param file
+ * the file to examine (source or header); as a full path.
+ * @param startLineNumber
+ * the first line number
+ * @param endLineNumber
+ * the last line number. if -1, the line entries for the
+ * remainder of the file will be returned
+ * @return unmodifiable list of line entries, which may be empty
+ */
+ Collection<ILineEntry> getLineEntriesForLines(IPath file, int startLineNumber, int endLineNumber);
+
+ /**
+ * Gets the next line table entry in the same scope by line number that also
+ * has a higher address (useful for source level stepping)
+ *
+ * @param entry
+ * the current entry
+ * @param collapseInlineFunctions
+ * treat inline code as though it were a function to be stepped over
+ * @return the next entry, or null if none
+ * @since 2.0
+ */
+ ILineEntry getNextLineEntry(ILineEntry entry, boolean collapseInlineFunctions);
+
+ /**
+ * Gets the previous line table entry in the same scope by line number that also
+ * has a lower address (useful for source level stepping)
+ *
+ * @param entry
+ * the current entry
+ * @param collapseInlineFunctions
+ * treat inline code as though it were a function to be stepped over
+ * @return the next entry, or null if none
+ * @since 2.0
+ */
+ ILineEntry getPreviousLineEntry(ILineEntry entry, boolean collapseInlineFunctions);
+
+ /**
+ * Gets the line entry for the given link address within a given function.
+ * (Useful for inline stepping and stack-crawl function name determination.)
+ *
+ * @param linkAddress
+ * @param parentFunction
+ * @return
+ * @since 2.0
+ */
+ public ILineEntry getLineEntryInFunction(IAddress linkAddress, IFunctionScope parentFunction);
+
+ /**
+ * Given a source line (let's call it anchor), find the line(s) closest to
+ * the anchor in the neighborhood (including the anchor itself) that has
+ * machine code. <br>
+ * <br>
+ * The search is done in the context of this line provider which is usually
+ * a compile unit (CU) or a module (one executable file).<br>
+ * <br>
+ * In the context of a CU, only one code line will be returned. If the
+ * closest line above the anchor and the closest line below the anchor have
+ * the same distance from the anchor, the one below will be selected.<br>
+ * <br>
+ * In the context of a module, more than one code lines may be found. For
+ * instance, one source line of an inline function in a header may have code
+ * in one CU but not in another where a neighboring code line may be found.
+ *
+ * @param sourceFile
+ * the file that contains the source lines in question.
+ * @param anchorLine
+ * line number of the anchor source line.
+ * @param neighborLimit
+ * specify the limit of the neighborhood: up to this number of
+ * lines above the anchor and up to this number of lines below
+ * the anchor will be checked if needed. But the check will never
+ * go beyond the source file. When the limit is zero, no neighbor
+ * lines will be checked. If the limit has value of -1, it means
+ * the actual limit is the source file.
+ *
+ * @return List of {@link ILineAddresses} objects containing link addresses.
+ * Empty list if no code line is found.
+ *
+ * @since 2.0
+ */
+ List<ILineAddresses> findClosestLineWithCode(IPath sourceFile, int anchorLine,
+ int neighborLimit);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
+
+/**
+ * Interface to interpret the location of a variable at runtime.
+ */
+public interface ILocationProvider {
+
+ /**
+ * Get the location of the variable for the given execution address
+ *
+ * @param tracker
+ * DSF services tracker to obtain needed services (registers,
+ * memory, etc)
+ * @param stack
+ * frame context to use for register based operations
+ * @param forLinkAddress
+ * the link-time address for use if the variable can live at
+ * different location depending on the execution context
+ * @param isNonLocalConstVariable
+ * whether variable is a global or static (non-local) constant
+ * @return the variable location
+ * @since 2.0
+ */
+ IVariableLocation getLocation(EDCServicesTracker tracker, IFrameDMContext context,
+ IAddress forLinkAddress, boolean isNonLocalConstVariable);
+
+ /**
+ * Tell if the variable has a known location at this address.
+ * @param forLinkAddress
+ * the link-time address for use if the variable
+ * @return true if a location is known for the variable at this address, or false if not.
+ * Note: if a variable has a static address, always return true.
+ */
+ boolean isLocationKnown(IAddress forLinkAddress);
+
+ /**
+ * Tell if the variable's lifetime must be the same as its enclosing scope.
+ * @return true if a lifetime range must match its enclosing scope's, or false if not.
+ * Note: If a variable has a static address, always return true.
+ * @since 2.0
+ */
+ boolean lifetimeMustMatchScope();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+/**
+ * Interface representing a variable value located in memory.
+ */
+public interface IMemoryVariableLocation extends IVariableLocation {
+ /**
+ * Is this address a runtime address or a link address?
+ * @return true if runtime address, false if link address
+ */
+ boolean isRuntimeAddress();
+
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Collection;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Added functionality for finding line information across a module.
+ */
+public interface IModuleLineEntryProvider extends ILineEntryProvider {
+ /**
+ * Get the line entry providers for the given source file.
+ * @path sourceFile the absolute path to the source file
+ * @return the unmodifiable list of providers for the file, possibly empty.
+ */
+ Collection<ILineEntryProvider> getLineEntryProvidersForFile(IPath sourceFile);
+
+ /**
+ * Check if the module uses the given source file.<br>
+ * Note that line table has more complete list of files (esp. headers) used
+ * in a compile unit than debug_info section of Dwarf.
+ *
+ * @since 2.0
+ */
+ boolean hasSourceFile(IPath sourceFile);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.services.IFrameRegisterProvider;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Interface representing a module scope. A module is a symbol file which
+ * contains compile units.
+ */
+public interface IModuleScope extends IScope {
+
+ /**
+ * Get the module described
+ */
+ IPath getSymbolFile();
+
+ /**
+ * Get the compile unit at the given link address
+ *
+ * @param address
+ * the link address
+ * @return the compile unit at the address, or null if none
+ */
+ ICompileUnitScope getCompileUnitForAddress(IAddress linkAddress);
+
+ /**
+ * Get the compile unit(s) for the given file
+ *
+ * @param filePath
+ * the file path as defined in the symbolics
+ * @return the compile unit for this file, possibly empty
+ */
+ List<ICompileUnitScope> getCompileUnitsForFile(IPath filePath);
+
+ /**
+ * Get all functions with the given name in any scope in the module
+ *
+ * @param name
+ * the function name, or <code>null</code> for all functions
+ * @return unmodifiable list of functions, which may be empty
+ */
+ Collection<IFunctionScope> getFunctionsByName(String name);
+
+ /**
+ * Get all variables with the given name in any scope in the module
+ *
+ * @param name
+ * the variable name, or <code>null</code> to get all variables
+ * @param globalsOnly
+ * whether to assume local variables in scope have been recorded
+ * and only globally-visible variables remain to be found
+ * @return unmodifiable list of variables, which may be empty
+ */
+ Collection<IVariable> getVariablesByName(String name, boolean globalsOnly);
+
+
+ /**
+ * Get all the types declared in the module.
+ * <p>
+ * This does not load types on demand; each IType instance may be a proxy for a
+ * type loaded once methods are called on it. Thus, do not use "instanceof" to
+ * check the type.
+ * @return unmodifiable list of types, which may be empty.
+ */
+ Collection<IType> getTypes();
+
+ /**
+ * Get the line entry provider for the module. This aggregates the
+ * information for any source or header file referenced in the module.
+ * @return {@link IModuleLineEntryProvider}, never <code>null</code>
+ */
+ IModuleLineEntryProvider getModuleLineEntryProvider();
+
+ /**
+ * Get a provider that allows access to registers stored in other stack frames.
+ * @return {@link IFrameRegisterProvider} or <code>null</code>
+ */
+ IFrameRegisterProvider getFrameRegisterProvider();
+
+}
--- /dev/null
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Iterator;
+
+/**
+ * This describes a range of non-contiguous addresses.
+ *
+ * Note: this uses long instead of IAddress for efficiency.
+ */
+public interface IRangeList extends Iterable<IRangeList.Entry> {
+
+ static class Entry implements Comparable<Entry> {
+ public Entry(long low, long high) {
+ this.low = low;
+ this.high = high;
+ }
+
+ final public long low, high;
+
+ @Override
+ public String toString() {
+ return "[" + Long.toHexString(low) + "-" + Long.toHexString(high) + ")";
+ }
+ /* (non-Javadoc)
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo(Entry o) {
+ if (low < o.low)
+ return -1;
+ if (low > o.low && high >= o.high)
+ return 1;
+ if (low == o.low && high > o.high)
+ return 1;
+ return 0;
+ }
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (high ^ (high >>> 32));
+ result = prime * result + (int) (low ^ (low >>> 32));
+ return result;
+ }
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Entry other = (Entry) obj;
+ if (high != other.high)
+ return false;
+ if (low != other.low)
+ return false;
+ return true;
+ }
+
+
+ }
+
+ /** Get absolute low address for the range */
+ long getLowAddress();
+ /** Get absolute high address for the range */
+ long getHighAddress();
+
+ /** Iterate over every portion of the range */
+ Iterator<IRangeList.Entry> iterator();
+
+ /**
+ * Tell if an address is in the range.
+ * @param addr
+ */
+ boolean isInRange(long addr);
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+
+/**
+ * Interface representing a variable value located at an address specified
+ * as [register + offset].
+ * <p>
+ * This may refer to a register that is saved inside a caller's stack frame
+ * and not necessarily to a current living register.
+ */
+public interface IRegisterOffsetVariableLocation extends IRegisterVariableLocation {
+ /**
+ * Get the offset from the register containing the variable value
+ *
+ * @return the offset in bytes
+ */
+ long getOffset();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+
+/**
+ * Interface representing a variable value located in a register.
+ * <p>
+ * This may refer to a register that is saved inside a caller's stack frame
+ * and not necessarily to a current living register.
+ */
+public interface IRegisterVariableLocation extends IVariableLocation {
+
+ /**
+ * Get the name of the register containing the variable value
+ *
+ * @return the register name if known, null otherwise
+ */
+ String getRegisterName();
+
+ /**
+ * Get the id of the register containing the variable value
+ *
+ * @return the register id if known, -1 otherwise
+ */
+ int getRegisterID();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Collection;
+
+import org.eclipse.cdt.core.IAddress;
+
+/**
+ * Generic symbolic scope interface.
+ */
+public interface IScope extends Comparable<Object> {
+
+ /**
+ * Get the name of the scope
+ *
+ * @return the name
+ */
+ String getName();
+
+ /**
+ * Tell whether the scope has an empty address range (either unset or unspecified)
+ */
+ boolean hasEmptyRange();
+
+ /**
+ * Get the low link address of this scope (absolute or synthesized from a range list)
+ *
+ * @return low address, or null if unknown
+ */
+ IAddress getLowAddress();
+
+ /**
+ * Get the high link address of this scope (absolute or synthesized from a range list)
+ *
+ * @return high address, or null if unknown
+ */
+ IAddress getHighAddress();
+
+ /**
+ * Get the list of non-consecutive ranges for the scope.
+ * @return list or <code>null</code> if the low and high addresses specify a contiguous range
+ */
+ IRangeList getRangeList();
+
+ /**
+ * Get the parent of this scope
+ *
+ * @return the parent scope, or null if the highest level scope
+ */
+ IScope getParent();
+
+ /**
+ * Gets the list of child scopes (if any)
+ *
+ * @return unmodifiable list of child scopes which may be empty
+ */
+ Collection<IScope> getChildren();
+
+ /**
+ * Gets the list of variables in this scope only
+ *
+ * @return unmodifiable list of variables which may be empty
+ */
+ Collection<IVariable> getVariables();
+
+ /**
+ * Gets the list of enumerators in this scope (if any)
+ *
+ * @return unmodifiable list of enumerators which may be empty
+ */
+ Collection<IEnumerator> getEnumerators();
+
+ /**
+ * Find the smallest scope at the given link address
+ *
+ * @param address
+ * the link address
+ * @return the smallest scope containing the given address, or null if none
+ * found
+ */
+ IScope getScopeAtAddress(IAddress linkAddress);
+
+ /**
+ *
+ */
+ void dispose();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.cdt.core.IAddress;
+
+/**
+ * Interface representing a symbol from the symbol table
+ */
+public interface ISymbol extends Comparable<Object> {
+
+ /**
+ * Get the symbol name
+ *
+ * @return the symbol name
+ */
+ String getName();
+
+ /**
+ * Get the address of the symbol
+ *
+ * @return the symbol address
+ */
+ IAddress getAddress();
+
+ /**
+ * Get the size of the symbol
+ *
+ * @return the symbol size
+ */
+ long getSize();
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * This interface detects a specific executable format and yields a symbol
+ * reader for it.
+ */
+public interface ISymbolReaderFactory {
+ /**
+ * Create a symbol reader for the given executable. The symbols do
+ * not have to live in the provided executable, but may be in associated files (e.g. exeFile + .sym).
+ * @param exeFile start point for searching
+ * @return a symbol reader, or <code>null</code> if not compatible
+ * @throws IOException if the reader fails to handle the file after detecting its type
+ */
+ IEDCSymbolReader createSymbolReader(IPath exeFile) throws IOException;
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Map;
+
+
+/**
+ * Interface for types.
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IType {
+
+ /**
+ * Get type name
+ *
+ * @return type name
+ */
+ public String getName();
+
+ /**
+ * Get type scope
+ *
+ * @return scope
+ */
+ public IScope getScope();
+
+ /**
+ * Get size of data type in bytes
+ *
+ * @return size in bytes of the effective type (e.g. skipping qualifiers, typedefs, etc.)
+ */
+ public int getByteSize();
+
+ /**
+ * Get properties
+ *
+ * @return general map of type properties
+ */
+ public Map<Object, Object> getProperties();
+
+ /**
+ * Get type pointed to, accessed, qualified, etc. by this type
+ */
+ public IType getType();
+
+ /**
+ * Set type pointed to, accessed, qualified, etc. by this type
+ *
+ * @param type
+ * type pointed to, accessed, qualified, etc.
+ */
+ public void setType(IType type);
+
+ public void dispose();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglingException;
+
+/**
+ * This interface supports unmangling symbols from executables
+ * or debug information into the high-level language format.
+ */
+public interface IUnmangler {
+ /**
+ * Undecorate a symbol which may have architecture or OS specific prefixes
+ * or suffixes independent of the actual mangling scheme underneath.
+ * @param symbol
+ * @return undecorated variant, possibly same as symbol
+ */
+ String undecorate(String symbol);
+
+ /**
+ * Tell if a symbol looks mangled
+ * @param symbol undecorated symbol
+ * @return true if it might be mangled, false if the symbol mangling is not recognized
+ * (or, it is the same as an extern "C" name)
+ */
+ boolean isMangled(String symbol);
+
+ /**
+ * Unmangle the symbol
+ * @param symbol undecorated symbol
+ * @return unmangled symbol
+ * @throws UnmanglingException if symbol cannot be mangled
+ */
+ String unmangle(String symbol) throws UnmanglingException;
+
+ /**
+ * Unmangle the symbol without args
+ * @param symbol
+ * @return the function name without args
+ * @throws UnmanglingException
+ * @since 2.0
+ */
+ String unmangleWithoutArgs(String symbol) throws UnmanglingException;
+
+
+ /**
+ * Unmangle a type, which may start at a different point in the BNF tree
+ * @param symbol
+ * @return the unmangled type name
+ * @throws UnmanglingException
+ * @since 2.0
+ */
+ String unmangleType(String symbol) throws UnmanglingException;
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.symbols;
+
+/**
+ * This represents a synthetic location where the value is stored directly.
+ */
+public interface IValueVariableLocation extends IVariableLocation {
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.ILexicalBlockScope;
+import org.eclipse.core.runtime.IPath;
+
+/**
+ * Interface representing a variable or parameter.
+ */
+public interface IVariable {
+
+ /**
+ * Get the name of the variable
+ *
+ * @return the variable name
+ */
+ String getName();
+
+ /**
+ * Get the scope that the variable belongs to
+ *
+ * @return the variable scope
+ */
+ IScope getScope();
+
+ /**
+ * Get the type of the variable
+ *
+ * @return the variable type
+ */
+ IType getType();
+
+ /**
+ * Get the location provider for the variable
+ *
+ * @return the location provider
+ */
+ ILocationProvider getLocationProvider();
+
+ /**
+ * A variable's lifetime may start somewhere inside its parent scope (without being
+ * inside an {@link ILexicalBlockScope}). This provides the offset from the
+ * start address of the parent scope at which time the variable is considered
+ * live.
+ * <p>
+ * This scope may be narrower than the scope implied by {@link ILocationProvider#isLocationKnown(org.eclipse.cdt.core.IAddress)}.
+ * <p>
+ * Note: a variable is always considered to be live until the end of the parent scope.
+ * @return offset in bytes (0 means the lifetime is the same as the parent scope)
+ */
+ long getStartScope();
+
+ /**
+ * Whether a variable was explicitly declared in the source, rather than generated
+ * by a tool such as a compiler
+ *
+ * @return whether the variable was explicitly declared
+ * @since 2.0
+ */
+ boolean isDeclared();
+
+ /**
+ * Get the path of the file in which this variable is defined
+ *
+ * @return file in which this variable is defined, or null if unknown
+ * @since 2.0
+ */
+ IPath getDefiningFile();
+
+ /**
+ *
+ */
+ void dispose();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.core.IAddress;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Interface representing a variable value location.
+ */
+public interface IVariableLocation {
+
+ /**
+ * Read the item's value
+ * @param bytes number of bytes to fetch
+ * @return value, never <code>null</code>
+ * @throws CoreException on error
+ */
+ BigInteger readValue(int bytes) throws CoreException;
+
+
+ /**
+ * Writes the item's value
+ * @param bytes <code>int</code> number of bytes to write
+ * @param value <code>BigInteger</code>
+ * @throws CoreException
+ */
+ void writeValue(int bytes, BigInteger value) throws CoreException;
+
+ /**
+ * Create another location at the given offset from this location.
+ * @param offset
+ * @return {@link IVariableLocation}
+ */
+ IVariableLocation addOffset(long offset);
+
+ /**
+ * Get the name for the location, to show in the UI
+ * @return name
+ */
+ String getLocationName();
+
+ /**
+ * Get the address of the location, if the location has an address.
+ * @return address or <code>null</code>
+ */
+ IAddress getAddress();
+
+ /**
+ * Get the context for the location, if the location has a context
+ * @return context or <code>null</code>
+ * @since 2.0
+ */
+ public IDMContext getContext();
+
+ /**
+ * Get the services tracker for the location, if the location has a services tracker
+ * @return services tracker or <code>null</code>
+ * @since 2.0
+ */
+ public EDCServicesTracker getServicesTracker();
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import org.eclipse.osgi.util.NLS;
+
+public class SymbolsMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.cdt.debug.edc.symbols.SymbolsMessages"; //$NON-NLS-1$
+
+ public static String TypeEngine_CannotResolveBaseType;
+ public static String TypeEngine_CannotResolveType;
+ public static String TypeEngine_ExpectedIntegerConstant;
+ public static String TypeEngine_NoDecltypeSupport;
+ public static String TypeEngine_NoTypeToCast;
+ public static String TypeEngine_UnhandledType;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, SymbolsMessages.class);
+ }
+
+ private SymbolsMessages() {
+ }
+}
--- /dev/null
+###############################################################################
+# Copyright (c) 2010 Nokia 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:
+# Nokia - Initial API and implementation
+###############################################################################
+TypeEngine_CannotResolveBaseType=cannot resolve base type of
+TypeEngine_CannotResolveType=cannot resolve type
+TypeEngine_ExpectedIntegerConstant=expected an integer constant, got
+TypeEngine_NoDecltypeSupport=decltype() not implemented
+TypeEngine_NoTypeToCast=no type to cast
+TypeEngine_UnhandledType=unhandled type:
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil;
+import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
+import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
+import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier;
+import org.eclipse.cdt.core.dom.ast.IASTExpression;
+import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
+import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
+import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
+import org.eclipse.cdt.core.dom.ast.IASTProblemTypeId;
+import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
+import org.eclipse.cdt.core.dom.ast.IASTTypeId;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTArrayDeclarator;
+import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
+import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.PushLongOrBigInteger;
+import org.eclipse.cdt.debug.edc.internal.symbols.ArrayBoundType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.CPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayBoundType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.PointerType;
+import org.eclipse.cdt.debug.edc.services.ITargetEnvironment;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This class manages the {@link IType} instances relevant to a given target.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+@SuppressWarnings("deprecation")
+public class TypeEngine {
+ private Map<Integer, Integer> typeSizeMap;
+ private Map<Object, IType> typeMap = new HashMap<Object, IType>();
+ private int addressSize;
+ private Map<IType, String> typeNameMap = new HashMap<IType, String>();
+ private final IDebugInfoProvider debugInfoProvider;
+ /** map of type signature to IType or CoreException */
+ private Map<String, Object> typeIdToTypeMap = new HashMap<String, Object>();
+ private Map<String, IArrayType> typeToArrayTypeMap = new HashMap<String, IArrayType>();
+ private boolean charIsSigned;
+
+ /**
+ * @since 2.0
+ */
+ public TypeEngine(ITargetEnvironment targetEnvironment, IDebugInfoProvider debugInfoProvider) {
+ this.debugInfoProvider = debugInfoProvider;
+ if (targetEnvironment != null) {
+ typeSizeMap = targetEnvironment.getBasicTypeSizes();
+ addressSize = targetEnvironment.getPointerSize();
+ charIsSigned = targetEnvironment.isCharSigned();
+ } else {
+ typeSizeMap = Collections.emptyMap();
+ addressSize = 4;
+ charIsSigned = true;
+ }
+ }
+
+ /**
+ * Get the target's basic type for an integer of the given size
+ * @param size
+ * @param isSigned
+ * @return type or <code>null</code> if no match
+ */
+ public IType getIntegerTypeOfSize(int size, boolean isSigned) {
+ int basicType;
+ int flags;
+
+ if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_CHAR) == size) {
+ basicType = ICPPBasicType.t_char;
+ flags = ICPPBasicType.IS_SIGNED;
+ } else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_CHAR) == size) {
+ basicType = ICPPBasicType.t_char;
+ flags = ICPPBasicType.IS_UNSIGNED;
+ } else if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_SHORT) == size) {
+ basicType = ICPPBasicType.t_int;
+ flags = ICPPBasicType.IS_SHORT + ICPPBasicType.IS_SIGNED;
+ } else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_SHORT) == size) {
+ basicType = ICPPBasicType.t_int;
+ flags = ICPPBasicType.IS_SHORT + ICPPBasicType.IS_UNSIGNED;
+ } else if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_INT) == size) {
+ basicType = ICPPBasicType.t_int;
+ flags = ICPPBasicType.IS_SIGNED;
+ } else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_INT) == size) {
+ basicType = ICPPBasicType.t_int;
+ flags = ICPPBasicType.IS_UNSIGNED;
+ } else if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG) == size) {
+ basicType = ICPPBasicType.t_int;
+ flags = ICPPBasicType.IS_LONG + ICPPBasicType.IS_SIGNED;
+ } else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG) == size) {
+ basicType = ICPPBasicType.t_int;
+ flags = ICPPBasicType.IS_LONG + ICPPBasicType.IS_UNSIGNED;
+ } else if (isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG_LONG) == size) {
+ basicType = ICPPBasicType.t_int;
+ flags = ICPPBasicType.IS_LONG_LONG + ICPPBasicType.IS_SIGNED;
+ } else if (!isSigned && typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG_LONG) == size) {
+ basicType = ICPPBasicType.t_int;
+ flags = ICPPBasicType.IS_LONG_LONG + ICPPBasicType.IS_UNSIGNED;
+ } else {
+ return null;
+ }
+
+ return getBasicType(basicType, flags, size);
+ }
+
+ /**
+ * Get the target's basic type for an integer of the given kind
+ * @param typeUtilsType from TypeUtils#BASIC_TYPE
+ * @param isSigned
+ * @return type or <code>null</code> if no match
+ */
+ public IType getIntegerTypeFor(int typeUtilsBasicType, boolean isSigned) {
+ return getIntegerTypeOfSize(typeSizeMap.get(typeUtilsBasicType), isSigned);
+ }
+
+ /**
+ * Get a cached ICPPBasicType instance
+ * @param basicType
+ * @param flags
+ * @param size
+ * @return IType
+ */
+ public IType getBasicType(int basicType, int flags, int size) {
+ return getBasicType(null, basicType, flags, size);
+ }
+
+ /**
+ * Get a cached ICPPBasicType instance, using a custom name.
+ * @param name
+ * @param basicType
+ * @param flags
+ * @param size
+ * @return IType
+ */
+ public IType getBasicType(String name, int basicType, int flags, int size) {
+ Object typeCode = (flags << 16) + (basicType << 8) + size;
+ if (name != null)
+ typeCode = name + ":" + typeCode; //$NON-NLS-1$
+ IType type = typeMap.get(typeCode);
+ if (type == null) {
+ if (name == null)
+ name = getBasicTypeName(basicType, flags);
+ type = new CPPBasicType(name, basicType, flags, size);
+ typeMap.put(typeCode, type);
+ }
+ return type;
+ }
+
+ /**
+ * @param basicType
+ * @param flags
+ * @return
+ */
+ private String getBasicTypeName(int basicType, int flags) {
+ String name;
+ switch (basicType) {
+ case ICPPBasicType.t_bool:
+ name = "bool"; break; //$NON-NLS-1$
+ case ICPPBasicType.t_wchar_t:
+ name = "wchar_t"; break; //$NON-NLS-1$
+ case ICPPBasicType.t_char:
+ name = "char"; break; //$NON-NLS-1$
+ case ICPPBasicType.t_int:
+ if ((flags & ICPPBasicType.IS_SHORT) != 0)
+ name = "short"; //$NON-NLS-1$
+ else if ((flags & ICPPBasicType.IS_LONG) != 0)
+ name = "long"; //$NON-NLS-1$
+ else if ((flags & ICPPBasicType.IS_LONG_LONG) != 0)
+ name = "long long"; //$NON-NLS-1$
+ else
+ name = "int"; //$NON-NLS-1$
+ break;
+ case ICPPBasicType.t_float:
+ name = "float"; break; //$NON-NLS-1$
+ case ICPPBasicType.t_double:
+ if ((flags & ICPPBasicType.IS_LONG) != 0)
+ name = "long double"; //$NON-NLS-1$
+ else
+ name = "double"; //$NON-NLS-1$
+ break;
+ case ICPPBasicType.t_unspecified:
+ name = "<<unknown>>"; //$NON-NLS-1$
+ break;
+ case ICPPBasicType.t_void:
+ name = "void"; //$NON-NLS-1$
+ break;
+ default:
+ assert(false);
+ name = ""; //$NON-NLS-1$
+ break;
+ }
+
+ if ((flags & ICPPBasicType.IS_SIGNED) != 0)
+ name = "signed " + name; //$NON-NLS-1$
+ else if ((flags & ICPPBasicType.IS_UNSIGNED) != 0)
+ name = "unsigned " + name; //$NON-NLS-1$
+ if ((flags & ICPPBasicType.IS_COMPLEX) != 0)
+ name = "complex " + name; //$NON-NLS-1$
+ if ((flags & ICPPBasicType.IS_IMAGINARY) != 0)
+ name = "imaginary " + name; //$NON-NLS-1$
+ return name;
+ }
+
+ /**
+ * Get the target's basic type for a float of the given size
+ * @param size
+ * @param isSigned
+ * @return type or <code>null</code> if no match
+ */
+ public IType getFloatTypeOfSize(int size) {
+ if (typeSizeMap.get(TypeUtils.BASIC_TYPE_FLOAT) == size) {
+ return getBasicType(ICPPBasicType.t_float, 0, size);
+ }
+ if (typeSizeMap.get(TypeUtils.BASIC_TYPE_DOUBLE) == size) {
+ return getBasicType(ICPPBasicType.t_double, 0, size);
+ }
+ if (typeSizeMap.get(TypeUtils.BASIC_TYPE_LONG_DOUBLE) == size) {
+ return getBasicType(ICPPBasicType.t_double, ICPPBasicType.IS_LONG, size);
+ }
+ return null;
+ }
+
+ /**
+ * Get the basic type for a character of a given size.
+ * @param size
+ * @return IType
+ */
+ public IType getCharacterType(int size) {
+ if (typeSizeMap.get(TypeUtils.BASIC_TYPE_CHAR) == size) {
+ return getBasicType(ICPPBasicType.t_char, 0, size);
+ }
+ return getBasicType(ICPPBasicType.t_wchar_t, 0, size);
+ }
+
+ /**
+ * Get the basic type for a character of a given size.
+ * @param size
+ * @return IType
+ */
+ public IType getWideCharacterType(int size) {
+ return getBasicType(ICPPBasicType.t_wchar_t, 0, size);
+ }
+
+ public IType getBooleanType(int size) {
+ return getBasicType(ICPPBasicType.t_bool, 0, size);
+ }
+
+ public IType getCharArrayType(IType charType, int length) {
+ IArrayBoundType bounds = new ArrayBoundType(null, length);
+ IArrayType array = new ArrayType(charType.getName() + "[" + length + "]", null, length, null); //$NON-NLS-1$ //$NON-NLS-2$
+ array.addBound(bounds);
+ array.setType(charType);
+ return array;
+ }
+
+ /**
+ * Get the integral type the same size as a pointer.
+ * @return IType or <code>null</code>
+ */
+ public IType getPointerSizeType() {
+ int size = getPointerSize();
+ return getBasicType("ptrsize_t", ICPPBasicType.t_int, 0, size); //$NON-NLS-1$
+ }
+
+ private int getPointerSize() {
+ return addressSize;
+ }
+
+ /**
+ * Get the byte size of a type
+ * @param basicType (TypeUtils#BASIC_TYPE_xxx)
+ * @return type, or 0 if unknown
+ */
+ public int getTypeSize(int basicType) {
+ Integer size = typeSizeMap.get(basicType);
+ return size != null ? size.intValue() : 0;
+ }
+
+ /**
+ * @param valueType
+ * @return
+ */
+ public String getTypeName(IType valueType) {
+ if (valueType == null)
+ return ""; //$NON-NLS-1$
+
+ String typeName = typeNameMap.get(valueType);
+ if (typeName == null) {
+ typeName = TypeUtils.getFullTypeName(valueType);
+ typeNameMap.put(valueType, typeName);
+ }
+ return typeName;
+ }
+
+ /**
+ * Convert an AST type ID into an EDC IType.
+ * @param typeId
+ * @return IType
+ * @throws CoreException if the IType cannot be created
+ */
+ public IType getTypeForTypeId(IASTTypeId typeId) throws CoreException {
+ if (typeId == null)
+ throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_NoTypeToCast);
+
+ if (typeId instanceof IASTProblemTypeId)
+ throw EDCDebugger.newCoreException(((IASTProblemTypeId) typeId).getProblem().getMessage());
+
+ String typeSignature = ASTSignatureUtil.getSignature(typeId);
+
+ Object obj = typeIdToTypeMap.get(typeSignature);
+ if (obj instanceof CoreException)
+ throw (CoreException) obj;
+
+ obj = null; //HACK
+ IType type = null;
+ if (!(obj instanceof IType)) {
+ try {
+ type = createTypeForTypeId(typeId, typeSignature, type);
+ typeIdToTypeMap.put(typeSignature, type);
+ } catch (CoreException e) {
+ typeIdToTypeMap.put(typeSignature, e);
+ throw e;
+ }
+ } else {
+ type = (IType) obj;
+ }
+
+ return type;
+ }
+
+ /**
+ * Create an IType mapping to IASTTypeId
+ * @param typeId
+ * @param typeSignature
+ * @param type
+ * @return new IType
+ * @throws CoreException
+ */
+ @SuppressWarnings("unused")
+ private IType createTypeForTypeId(IASTTypeId typeId, String typeSignature, IType type) throws CoreException {
+ IASTDeclSpecifier declSpec = typeId.getDeclSpecifier();
+ if (declSpec instanceof IASTSimpleDeclSpecifier) {
+ type = getTypeForDeclSpecifier((IASTSimpleDeclSpecifier) declSpec);
+ } else if (declSpec instanceof IASTNamedTypeSpecifier || declSpec instanceof IASTElaboratedTypeSpecifier) {
+ String typeName;
+
+ int elaboration = -1;
+
+ if (declSpec instanceof IASTNamedTypeSpecifier) {
+ typeName = ((IASTNamedTypeSpecifier) declSpec).getName().toString();
+ } else /*if (declSpec instanceof IASTElaboratedTypeSpecifier)*/ {
+ // note: ignore the elaboration (class/struct/etc) since compilers are
+ // inconsistent with how they do this, and furthermore, only one name
+ // should be visible at a time, usually, anyway
+ elaboration = ((IASTElaboratedTypeSpecifier) declSpec).getKind();
+ typeName = ((IASTElaboratedTypeSpecifier) declSpec).getName().toString();
+ }
+
+ if (debugInfoProvider != null) {
+ Collection<IType> types;
+ IType aMatch = null;
+ types = debugInfoProvider.getTypesByName(typeName);
+
+ // try to find one matching struct/class/etc
+ for (IType aType : types) {
+ aMatch = aType;
+ if (elaboration < 0 ||
+ (type instanceof ICompositeType && ((ICompositeType) type).getKey() == elaboration)) {
+ type = aType;
+ break;
+ }
+ }
+
+ // if no match, just take a matching name
+ if (type == null && aMatch != null) {
+ type = aMatch;
+ }
+
+ // fall through to check type != null
+ }
+ }
+
+ if (type == null) {
+ throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_CannotResolveType + typeSignature);
+ }
+
+ if (typeId.getAbstractDeclarator() instanceof ICPPASTDeclarator) {
+ ICPPASTDeclarator declarator = (ICPPASTDeclarator) typeId.getAbstractDeclarator();
+ for (IASTPointerOperator pointer : declarator.getPointerOperators()) {
+ IType ptr = new PointerType(type.getName()+"*", type.getScope(), getPointerSize(), null); //$NON-NLS-1$
+ ptr.setType(type);
+ type = ptr;
+ }
+
+ if (declarator instanceof ICPPASTArrayDeclarator) {
+ ICPPASTArrayDeclarator arrayDeclarator = (ICPPASTArrayDeclarator) declarator;
+ IArrayType arrayType = new ArrayType(type.getName()+"[]", type.getScope(), 0, null); //$NON-NLS-1$
+ for (IASTArrayModifier arrayMod : arrayDeclarator.getArrayModifiers()) {
+ long elementCount = 1;
+ if (arrayMod.getConstantExpression() != null) {
+ elementCount = getConstantValue(arrayMod.getConstantExpression());
+ }
+ IArrayBoundType bound = new ArrayBoundType(arrayType.getScope(), elementCount);
+ arrayType.addBound(bound);
+ }
+ arrayType.setType(type);
+ type = arrayType;
+ }
+ }
+ return type;
+ }
+
+ private long getConstantValue(IASTExpression constantExpression) throws CoreException {
+ if (constantExpression instanceof IASTLiteralExpression) {
+ if (((IASTLiteralExpression) constantExpression).getKind() == IASTLiteralExpression.lk_integer_constant) {
+ // HACK, use more generic utilities
+ PushLongOrBigInteger pusher = new PushLongOrBigInteger(constantExpression.toString());
+ return pusher.getLong();
+ }
+ }
+ throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_ExpectedIntegerConstant +
+ ASTSignatureUtil.getExpressionString(constantExpression));
+ }
+
+ private IType getTypeForDeclSpecifier(IASTSimpleDeclSpecifier simpleDeclSpec) throws CoreException {
+ int baseType = 0;
+ int basicType = 0;
+ switch (simpleDeclSpec.getType()) {
+ case IASTSimpleDeclSpecifier.t_bool:
+ baseType = ICPPBasicType.t_bool;
+ basicType = TypeUtils.BASIC_TYPE_BOOL;
+ break;
+ case IASTSimpleDeclSpecifier.t_char:
+ baseType = ICPPBasicType.t_char;
+ basicType = TypeUtils.BASIC_TYPE_CHAR;
+ break;
+ case IASTSimpleDeclSpecifier.t_wchar_t:
+ baseType = ICPPBasicType.t_wchar_t;
+ basicType = TypeUtils.BASIC_TYPE_WCHAR_T;
+ break;
+ case IASTSimpleDeclSpecifier.t_double:
+ baseType = ICPPBasicType.t_double;
+ basicType = TypeUtils.BASIC_TYPE_DOUBLE;
+ break;
+ case IASTSimpleDeclSpecifier.t_float:
+ baseType = ICPPBasicType.t_float;
+ basicType = TypeUtils.BASIC_TYPE_FLOAT;
+ break;
+ case IASTSimpleDeclSpecifier.t_int:
+ baseType = ICPPBasicType.t_int;
+ basicType = TypeUtils.BASIC_TYPE_INT;
+ break;
+ case IASTSimpleDeclSpecifier.t_void:
+ baseType = ICPPBasicType.t_void;
+ break;
+ case IASTSimpleDeclSpecifier.t_typeof:
+ // we'd need to parse the subexpression then get its type
+ case IASTSimpleDeclSpecifier.t_decltype:
+ // not sure about this one
+ throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_NoDecltypeSupport);
+ case IASTSimpleDeclSpecifier.t_unspecified:
+ baseType = ICPPBasicType.t_int;
+ break;
+ default:
+ throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_UnhandledType + simpleDeclSpec);
+ }
+
+ int flags = 0;
+ if (simpleDeclSpec.isComplex()) flags |= ICPPBasicType.IS_COMPLEX;
+ if (simpleDeclSpec.isImaginary()) flags |= ICPPBasicType.IS_IMAGINARY;
+ if (simpleDeclSpec.isLong()) {
+ flags |= ICPPBasicType.IS_LONG;
+ if (basicType == 0)
+ basicType = TypeUtils.BASIC_TYPE_LONG;
+ }
+ if (simpleDeclSpec.isLongLong()) {
+ flags |= ICPPBasicType.IS_LONG_LONG;
+ if (basicType == 0)
+ basicType = TypeUtils.BASIC_TYPE_LONG_LONG;
+ }
+ if (simpleDeclSpec.isShort()) {
+ flags |= ICPPBasicType.IS_SHORT;
+ if (basicType == 0)
+ basicType = TypeUtils.BASIC_TYPE_SHORT;
+ }
+ if (simpleDeclSpec.isUnsigned()) flags |= ICPPBasicType.IS_UNSIGNED;
+ if (simpleDeclSpec.isSigned()) flags |= ICPPBasicType.IS_SIGNED;
+
+ if (!simpleDeclSpec.isUnsigned() && !simpleDeclSpec.isSigned()) {
+ if (baseType == ICPPBasicType.t_char)
+ flags |= charIsSigned ? ICPPBasicType.IS_SIGNED : ICPPBasicType.IS_UNSIGNED;
+ else if (baseType == ICPPBasicType.t_int)
+ flags |= ICPPBasicType.IS_SIGNED;
+ }
+
+ int size = getTypeSize(basicType);
+ return getBasicType(baseType, flags, size);
+ }
+
+ /**
+ * Convert a given type to an array type
+ * @param exprType
+ * @return array type
+ * @throws CoreException if not a sensible conversion
+ */
+ public IType convertToArrayType(IType type, int count) throws CoreException {
+ String typeSig = getTypeName(type);
+
+ IArrayType arrayType = typeToArrayTypeMap.get(typeSig);
+ if (arrayType == null) {
+ type = TypeUtils.getStrippedType(type);
+
+ IType baseType = type;
+
+ if (type instanceof IPointerType || type instanceof IArrayType) {
+ baseType = type.getType();
+ }
+
+ if (baseType == null)
+ throw EDCDebugger.newCoreException(SymbolsMessages.TypeEngine_CannotResolveBaseType + typeSig);
+
+ arrayType = new ArrayType(baseType.getName()+"[]", baseType.getScope(), //$NON-NLS-1$
+ baseType.getByteSize() * count, null);
+ IArrayBoundType bound = new ArrayBoundType(arrayType.getScope(), count);
+ arrayType.addBound(bound);
+ arrayType.setType(baseType);
+
+ typeToArrayTypeMap.put(typeSig, arrayType);
+ }
+
+ return arrayType;
+ }
+
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.eval.ast.engine.instructions.IArrayDimensionType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IAggregate;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayBoundType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IConstType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IForwardTypeReference;
+import org.eclipse.cdt.debug.edc.internal.symbols.IPointerType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IQualifierType;
+import org.eclipse.cdt.debug.edc.internal.symbols.IReferenceType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ISubroutineType;
+import org.eclipse.cdt.debug.edc.internal.symbols.ITypedef;
+
+
+/*
+ * Various utility routines for Type objects
+ */
+public class TypeUtils {
+
+ // type IDs for basic C and C++ types
+ static public final int BASIC_TYPE_CHAR = 1;
+ static public final int BASIC_TYPE_CHAR_UNSIGNED = 2;
+ static public final int BASIC_TYPE_CHAR_SIGNED = 3;
+ static public final int BASIC_TYPE_SHORT = 4;
+ static public final int BASIC_TYPE_SHORT_UNSIGNED = 5;
+ static public final int BASIC_TYPE_INT = 6;
+ static public final int BASIC_TYPE_INT_UNSIGNED = 7;
+ static public final int BASIC_TYPE_LONG = 8;
+ static public final int BASIC_TYPE_LONG_UNSIGNED = 9;
+ static public final int BASIC_TYPE_LONG_LONG = 10;
+ static public final int BASIC_TYPE_LONG_LONG_UNSIGNED = 11;
+ static public final int BASIC_TYPE_FLOAT = 12;
+ static public final int BASIC_TYPE_FLOAT_COMPLEX = 13;
+ static public final int BASIC_TYPE_DOUBLE = 14;
+ static public final int BASIC_TYPE_DOUBLE_COMPLEX = 15;
+ static public final int BASIC_TYPE_LONG_DOUBLE = 16;
+ static public final int BASIC_TYPE_LONG_DOUBLE_COMPLEX = 17;
+ static public final int BASIC_TYPE_BOOL = 18;
+ static public final int BASIC_TYPE_BOOL_C9X = 19;
+ static public final int BASIC_TYPE_WCHAR_T = 20;
+ static public final int BASIC_TYPE_POINTER = 21; // not technically a basic type
+
+ // is a type a pointer "*" type?
+ public static boolean isPointerType(IType type) {
+ return getStrippedType(type) instanceof IPointerType;
+ }
+
+ // is a type a reference "&" type?
+ public static boolean isReferenceType(IType type) {
+ return getStrippedType(type) instanceof IReferenceType;
+ }
+
+ // is a type an aggregate (composite or array) type?
+ public static boolean isAggregateType(IType type) {
+ return getStrippedType(type) instanceof IAggregate;
+ }
+
+ // is a type a composite (class, struct, or union) type?
+ public static boolean isCompositeType(IType type) {
+ return getStrippedType(type) instanceof ICompositeType;
+ }
+
+ /**
+ * is a type a constant type?
+ * @since 2.0
+ */
+ public static boolean isConstType(IType type) {
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+
+ while ( type instanceof ITypedef || type instanceof IQualifierType || type instanceof IReferenceType
+ || type instanceof IArrayType) {
+ if (type instanceof IConstType)
+ return true;
+
+ type = type.getType();
+
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+ }
+
+ return false;
+ }
+
+ // return the type with no typedefs, consts, or volatiles
+ public static IType getStrippedType(IType type) {
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+
+ while (type instanceof ITypedef || type instanceof IQualifierType) {
+ type = type.getType();
+
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+ }
+
+ return type;
+ }
+
+ /**
+ * Return the type with no typedefs, consts, volatiles, or references
+ * @since 2.0
+ */
+ public static IType getUnRefStrippedType(IType type) {
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+
+ while (type instanceof ITypedef || type instanceof IQualifierType || type instanceof IReferenceType) {
+ type = type.getType();
+
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+ }
+
+ return type;
+ }
+
+ // return base type with no typedefs, consts, volatiles, or pointer types
+ // removing array types messes up formatters because they are assumed to act on the array
+ // but code creating expressions ignores the array syntax
+ // unlike with pointer types where -> is used instead of .
+ public static IType getBaseType(Object type) {
+ if (!(type instanceof IType))
+ return null;
+
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+
+ while (type instanceof ITypedef || type instanceof IQualifierType
+ || type instanceof IPointerType) {
+ type = ((IType) type).getType();
+
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+ }
+
+ return (IType) type;
+ }
+
+ // return base type with no consts, volatiles, pointer types, or array types - but preserving typedefs
+ public static IType getBaseTypePreservingTypedef(IType type) {
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+
+ while (type instanceof IQualifierType
+ || type instanceof IPointerType || type instanceof IArrayType) {
+ type = type.getType();
+
+ if (type instanceof IForwardTypeReference)
+ type = ((IForwardTypeReference) type).getReferencedType();
+ }
+
+ return type;
+ }
+
+ // shift, mask, and extend an extracted bit-field
+ // NOTE: this may need to be endianness aware
+ public static Number extractBitField(Number value, int byteSize, int bitSize, int bitOffset, boolean isSignedInt) {
+ if (bitSize <= 0 || value == null
+ || (!(value instanceof Long) && !(value instanceof Integer) && !(value instanceof BigInteger))) {
+ return value;
+ }
+
+ // TODO: Need to get default type sizes from the ITargetEnvironment
+ // This assumes long and long long are 64 bits, and int is 32 bits
+ if (value instanceof Long) {
+ long longValue = (Long) value;
+
+ longValue >>= (byteSize * 8) - (bitOffset + bitSize);
+ longValue &= (-1) >>> (64 - bitSize);
+
+ if (isSignedInt) {
+ if ((longValue & (1 << (bitSize - 1))) != 0) {
+ longValue |= ((-1) >>> bitSize) << bitSize;
+ }
+ }
+ return new Long(longValue);
+ }
+
+ if (value instanceof Integer) {
+ int intValue = (Integer) value;
+
+ intValue >>= (byteSize * 8) - (bitOffset + bitSize);
+ intValue &= ((-1) >>> (32 - bitSize));
+
+ if (isSignedInt) {
+ if ((intValue & (1 << (bitSize - 1))) != 0) {
+ intValue |= ((-1) >>> bitSize) << bitSize;
+ }
+ }
+ return new Integer(intValue);
+ }
+
+ if (value instanceof BigInteger) {
+ BigInteger bigValue = (BigInteger) value;
+ bigValue = bigValue.shiftRight((byteSize * 8) - (bitOffset + bitSize));
+ byte[] bytes = new byte[8];
+ int mask;
+ BigInteger bigMask;
+
+ mask = ((-1) >>> (32 - bitSize));
+ for (int i = 0; i < 8; i++) {
+ bytes[i] = (byte) ((mask >>> ((7 - i) * 8)) & 0xff);
+ }
+
+ bigMask = new BigInteger(bytes);
+ bigValue = bigValue.and(bigMask);
+
+ if (isSignedInt) {
+ // NOTE: for variable values, we use BigInteger ONLY for
+ // unsigned numbers
+ if (bigValue.testBit(bitSize - 1)) {
+ mask = (((-1) >>> bitSize) << bitSize);
+ for (int i = 0; i < 8; i++) {
+ bytes[i] = (byte) ((mask >>> ((7 - i) * 8)) & 0xff);
+ }
+
+ bigMask = new BigInteger(bytes);
+ bigValue = bigValue.or(bigMask);
+ }
+ }
+
+ return bigValue;
+ }
+
+ return value;
+ }
+
+ /**
+ * Get the full name of a type.
+ *
+ * {@link TypeEngine#getTypeName(IType)} caches the full name returned by this routine and associates it
+ * with the type passed in.
+ *
+ * @param type type whose full name is desired
+ * @return full name of the type, with all qualifiers, array bounds, etc.
+ *
+ * @since 2.0
+ */
+ public static String getFullTypeName(IType type) {
+ if (type == null)
+ return ""; //$NON-NLS-1$
+ if (type instanceof IReferenceType)
+ return getFullTypeName(((IReferenceType) type).getType()) + " &"; //$NON-NLS-1$
+ if (type instanceof IPointerType) {
+ IType pointedTo = ((IPointerType) type).getType();
+ if (pointedTo instanceof ISubroutineType)
+ // TODO: get parameters instead of saying "..."
+ return "(*)(...)"; //$NON-NLS-1$
+ else
+ return getFullTypeName(pointedTo) + " *"; //$NON-NLS-1$
+ }
+ if (type instanceof IArrayType) {
+ IArrayType arrayType = (IArrayType) type;
+
+ IType subtype = null;
+ String dimensions = "";
+ do {
+ for (IArrayBoundType bound : arrayType.getBounds())
+ dimensions += "[" + bound.getBoundCount() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ subtype = TypeUtils.getStrippedType(arrayType.getType());
+ if (subtype instanceof IArrayType)
+ arrayType = (IArrayType)subtype;
+ } while (subtype instanceof IArrayType);
+
+ return getFullTypeName(arrayType.getType()) + dimensions;
+ }
+ if (type instanceof IArrayDimensionType) {
+ IArrayDimensionType arrayDimensionType = (IArrayDimensionType) type;
+ IArrayType arrayType = arrayDimensionType.getArrayType();
+ String returnType = getFullTypeName(arrayType.getType());
+
+ IArrayBoundType[] bounds = arrayType.getBounds();
+ for (int i = arrayDimensionType.getDimensionCount(); i < arrayType.getBoundsCount(); i++) {
+ returnType += "[" + bounds[i].getBoundCount() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return returnType;
+ }
+ if (type instanceof ITypedef)
+ return ((ITypedef) type).getName();
+ if (type instanceof ICompositeType)
+ return ((ICompositeType) type).getName();
+ if (type instanceof IQualifierType)
+ return ((IQualifierType) type).getName()
+ + " " + getFullTypeName(((IQualifierType) type).getType()); //$NON-NLS-1$
+ if (type instanceof ISubroutineType) {
+ // TODO: real stuff once we parse parameters
+ return getFullTypeName(((ISubroutineType) type).getType());
+ }
+ return type.getName() + getFullTypeName(type.getType());
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2010 Nokia 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:
+ * Nokia - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.cdt.debug.edc.symbols;
+
+import java.math.BigInteger;
+
+import org.eclipse.cdt.debug.edc.internal.symbols.InvalidVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.MemoryVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.RegisterVariableLocation;
+import org.eclipse.cdt.debug.edc.internal.symbols.ValueVariableLocation;
+import org.eclipse.cdt.debug.edc.services.EDCServicesTracker;
+import org.eclipse.cdt.dsf.datamodel.IDMContext;
+
+/**
+ * Create {@link IVariableLocation} instances
+ */
+public final class VariableLocationFactory {
+ protected VariableLocationFactory() { }
+
+ /**
+ * @since 2.0
+ */
+ public static IMemoryVariableLocation createMemoryVariableLocation(EDCServicesTracker tracker,
+ IDMContext context, BigInteger addressValue, boolean isRuntimeAddress) {
+ return new MemoryVariableLocation(tracker, context, addressValue, isRuntimeAddress);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static IMemoryVariableLocation createMemoryVariableLocation(EDCServicesTracker tracker,
+ IDMContext context, BigInteger addressValue) {
+ return new MemoryVariableLocation(tracker, context, addressValue, true);
+ }
+ /**
+ * @since 2.0
+ */
+ public static IMemoryVariableLocation createMemoryVariableLocation(EDCServicesTracker tracker,
+ IDMContext context, long addressValue) {
+ return new MemoryVariableLocation(tracker, context, BigInteger.valueOf(addressValue), true);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static IMemoryVariableLocation createMemoryVariableLocation(
+ EDCServicesTracker tracker, IDMContext context,
+ Number addressValue) {
+ BigInteger addr;
+ if (addressValue instanceof BigInteger)
+ addr = (BigInteger) addressValue;
+ else
+ addr = BigInteger.valueOf(addressValue.longValue());
+ return new MemoryVariableLocation(tracker, context, addr, true);
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static IRegisterVariableLocation createRegisterVariableLocation(
+ EDCServicesTracker tracker, IDMContext context, String name, int id) {
+ return new RegisterVariableLocation(tracker, context, name, id);
+ }
+ /**
+ * @since 2.0
+ */
+ public static IRegisterVariableLocation createRegisterVariableLocation(
+ EDCServicesTracker tracker, IDMContext context, int id) {
+ return new RegisterVariableLocation(tracker, context, null, id);
+ }
+
+ public static IInvalidVariableLocation createInvalidVariableLocation(String message) {
+ return new InvalidVariableLocation(message);
+ }
+
+ public static IValueVariableLocation createValueVariableLocation(BigInteger value) {
+ return new ValueVariableLocation(value);
+ }
+
+}
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry exported="true" kind="lib" path="lib/org.eclipse.nebula.widgets.grid_1.0.0.jar"/>
- <classpathentry exported="true" kind="lib" path="lib/org.eclipse.cdt.core_5.3.1.201109151620.jar"/>
- <classpathentry exported="true" kind="lib" path="lib/org.eclipse.cdt.debug.edc_2.0.0.201109151658.jar"/>
- <classpathentry exported="true" kind="lib" path="lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar"/>
<classpathentry kind="lib" path="lib/hsqldb.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
org.tizen.dynamicanalyzer.common;bundle-version="1.0.0",
org.tizen.dynamicanalyzer.appearance;bundle-version="1.0.0",
org.tizen.dynamicanalyzer.widgets;bundle-version="1.0.0",
- org.tizen.dynamicanalyzer.workbench;bundle-version="1.0.0"
+ org.tizen.dynamicanalyzer.workbench;bundle-version="1.0.0",
+ org.eclipse.cdt.debug.edc;bundle-version="1.0.0",
+ org.eclipse.cdt.core;bundle-version="5.3.2"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-Activator: org.tizen.dynamicanalyzer.common.AnalyzerPlugin
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .,
- lib/org.eclipse.cdt.core_5.3.1.201109151620.jar,
- lib/org.eclipse.cdt.debug.edc_2.0.0.201109151658.jar,
- lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar,
lib/org.eclipse.nebula.widgets.grid_1.0.0.jar,
lib/hsqldb.jar,
lib/json-simple-1.1.1.jar,