From: woojin Date: Fri, 19 Sep 2014 12:14:42 +0000 (+0900) Subject: SOURCE LINK: support DWARF4 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=bc572f27be84beff821dc03945051ad678554be6;p=sdk%2Ftools%2Fdynamic-analyzer.git SOURCE LINK: support DWARF4 import and modify eclipse cdt edc plugin to support DWARF4 Change-Id: Ia855410047276871358b9157525c6bb41405fcae Signed-off-by: woojin --- diff --git a/org.eclipse.cdt.debug.edc/.classpath b/org.eclipse.cdt.debug.edc/.classpath new file mode 100644 index 0000000..16f53cb --- /dev/null +++ b/org.eclipse.cdt.debug.edc/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/org.eclipse.cdt.debug.edc/.project b/org.eclipse.cdt.debug.edc/.project new file mode 100644 index 0000000..3f52031 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/.project @@ -0,0 +1,28 @@ + + + org.eclipse.cdt.debug.edc + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/org.eclipse.cdt.debug.edc/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.cdt.debug.edc/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..dc7828d --- /dev/null +++ b/org.eclipse.cdt.debug.edc/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +#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 diff --git a/org.eclipse.cdt.debug.edc/META-INF/MANIFEST.MF b/org.eclipse.cdt.debug.edc/META-INF/MANIFEST.MF new file mode 100644 index 0000000..8007ddc --- /dev/null +++ b/org.eclipse.cdt.debug.edc/META-INF/MANIFEST.MF @@ -0,0 +1,28 @@ +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 diff --git a/org.eclipse.cdt.debug.edc/build.properties b/org.eclipse.cdt.debug.edc/build.properties new file mode 100644 index 0000000..c34e114 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/build.properties @@ -0,0 +1,11 @@ +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 diff --git a/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.debug.core_7.1.0.201109151620.jar b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.debug.core_7.1.0.201109151620.jar new file mode 100644 index 0000000..1d61b19 Binary files /dev/null and b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.debug.core_7.1.0.201109151620.jar differ diff --git a/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.debug.edc.tcf.extension_2.0.0.201109151658.jar b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.debug.edc.tcf.extension_2.0.0.201109151658.jar new file mode 100644 index 0000000..bd617f9 Binary files /dev/null and b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.debug.edc.tcf.extension_2.0.0.201109151658.jar differ diff --git a/org.tizen.dynamicanalyzer/lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar similarity index 100% rename from org.tizen.dynamicanalyzer/lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar rename to org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.dsf_2.2.0.201109151620.jar diff --git a/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.launch_7.0.0.201109151620.jar b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.launch_7.0.0.201109151620.jar new file mode 100644 index 0000000..f38eecd Binary files /dev/null and b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.launch_7.0.0.201109151620.jar differ diff --git a/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.scripting_1.0.0.201109151658.jar b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.scripting_1.0.0.201109151658.jar new file mode 100644 index 0000000..78184d4 Binary files /dev/null and b/org.eclipse.cdt.debug.edc/lib/org.eclipse.cdt.scripting_1.0.0.201109151658.jar differ diff --git a/org.eclipse.cdt.debug.edc/lib/org.eclipse.tm.tcf.core_0.4.1.201109151255.jar b/org.eclipse.cdt.debug.edc/lib/org.eclipse.tm.tcf.core_0.4.1.201109151255.jar new file mode 100644 index 0000000..4893279 Binary files /dev/null and b/org.eclipse.cdt.debug.edc/lib/org.eclipse.tm.tcf.core_0.4.1.201109151255.jar differ diff --git a/org.eclipse.cdt.debug.edc/lib/truezip-6.jar b/org.eclipse.cdt.debug.edc/lib/truezip-6.jar new file mode 100644 index 0000000..92a8968 Binary files /dev/null and b/org.eclipse.cdt.debug.edc/lib/truezip-6.jar differ diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IAddressExpressionEvaluator.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IAddressExpressionEvaluator.java new file mode 100644 index 0000000..9d57203 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IAddressExpressionEvaluator.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.
+ *
+ * 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.
+ *
+ * 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; +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IEDCConstants.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IEDCConstants.java new file mode 100644 index 0000000..c4a0709 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IEDCConstants.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * 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"; +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IJumpToAddress.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IJumpToAddress.java new file mode 100644 index 0000000..1d3b4b9 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IJumpToAddress.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IStreamBuffer.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IStreamBuffer.java new file mode 100644 index 0000000..b2ffc2f --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/IStreamBuffer.java @@ -0,0 +1,116 @@ +/* +* 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); + + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFAgentLauncher.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFAgentLauncher.java new file mode 100644 index 0000000..0c18669 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFAgentLauncher.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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 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 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; + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFConnectionListener.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFConnectionListener.java new file mode 100644 index 0000000..3747971 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFConnectionListener.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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); +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFServiceManager.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFServiceManager.java new file mode 100644 index 0000000..1bde259 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/ITCFServiceManager.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * 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); +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/JumpToAddress.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/JumpToAddress.java new file mode 100644 index 0000000..0fe6766 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/JumpToAddress.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * 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.
+ * Contrast to: the address of the instruction right after this instruction in + * memory.
+ *
+ * Object of such class is supposed to be produced by disassembler and consumed + * by debugger for tasks such as stepping and stack crawling.
+ *
+ * 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 + "]"; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/MemoryUtils.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/MemoryUtils.java new file mode 100644 index 0000000..28ee2e5 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/MemoryUtils.java @@ -0,0 +1,710 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/MessageLogger.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/MessageLogger.java new file mode 100644 index 0000000..5440abd --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/MessageLogger.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * 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. + *

+ * The goals of the logging and the showing are different: + *

+ * A logged message should retain enough information so it's not useless, while + * not having empty top-level messages or unnecessary levels of nesting. + *

+ * 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 listeners = new ArrayList(); + + 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 null to use the + * exception message) + * @param exception + * the exception caused by the error (may be null) + */ + 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 null to use the + * exception message) + * @param exception + * the exception caused by the error (may be null) + */ + 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 null) + * @since 2.0 + */ + public void logException(Throwable t) { + log(IStatus.ERROR, null, t); + } + + public abstract Plugin getPlugin(); + + public abstract String getPluginID(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AbstractFormattedValuesRequestCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AbstractFormattedValuesRequestCache.java new file mode 100644 index 0000000..72f78d6 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AbstractFormattedValuesRequestCache.java @@ -0,0 +1,36 @@ +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 + * the specific type of information requested + * @since 2.0 + */ +public abstract class AbstractFormattedValuesRequestCache extends BaseRequestCache { + + /** 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AbstractRegisterRequestCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AbstractRegisterRequestCache.java new file mode 100644 index 0000000..99227a7 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AbstractRegisterRequestCache.java @@ -0,0 +1,33 @@ +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 + * the specific type of information requested + * @since 2.0 + */ +public abstract class AbstractRegisterRequestCache extends BaseRequestCache { + + /** 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 + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AvailableFormatsRequestCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AvailableFormatsRequestCache.java new file mode 100644 index 0000000..e85df53 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/AvailableFormatsRequestCache.java @@ -0,0 +1,33 @@ +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 + * the specific type of information requested + * @since 2.0 + */ +public class AvailableFormatsRequestCache extends AbstractFormattedValuesRequestCache { + + /** 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 rm) { + ((IFormattedValues)fService).getAvailableFormats((IFormattedDataDMContext)fCtx, new DataRequestMonitor(fService.getExecutor(), rm) { + public void handleSuccess() { + rm.setData(getData()); + rm.done(); + } + }); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/BaseRangeCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/BaseRangeCache.java new file mode 100644 index 0000000..0271eab --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/BaseRangeCache.java @@ -0,0 +1,113 @@ +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 extends RangeCache 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 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??? + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/BaseRequestCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/BaseRequestCache.java new file mode 100644 index 0000000..75ea084 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/BaseRequestCache.java @@ -0,0 +1,112 @@ +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 extends RequestCache 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/FormatedExpressionValueRequestCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/FormatedExpressionValueRequestCache.java new file mode 100644 index 0000000..ecafb72 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/FormatedExpressionValueRequestCache.java @@ -0,0 +1,30 @@ +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 { + + 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 rm) { + + ((IFormattedValues)fService).getFormattedExpressionValue((FormattedValueDMContext)fCtx, new DataRequestMonitor(fService.getExecutor(), rm) { + public void handleSuccess() { + rm.setData(getData()); + rm.done(); + } + }); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/GetRegistersRequestCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/GetRegistersRequestCache.java new file mode 100644 index 0000000..b69d355 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/GetRegistersRequestCache.java @@ -0,0 +1,24 @@ +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 { + + 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 rm) { + ((IRegisters)fService).getRegisters(fCtx, rm); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/ICacheEntry.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/ICacheEntry.java new file mode 100644 index 0000000..2ea2483 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/ICacheEntry.java @@ -0,0 +1,52 @@ +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(); +} + diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/MemoryRangeCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/MemoryRangeCache.java new file mode 100644 index 0000000..be41579 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/MemoryRangeCache.java @@ -0,0 +1,71 @@ +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 { + + /** 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> rm) { + ((IMemory)fService).getMemory((IMemoryDMContext)fCtx, fAddress, offset, fWordSize, count, new DataRequestMonitor(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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/RegisterGroupsRequestCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/RegisterGroupsRequestCache.java new file mode 100644 index 0000000..ee62b83 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/RegisterGroupsRequestCache.java @@ -0,0 +1,26 @@ +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 { + + 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 rm) { + ((IRegisters)fService).getRegisterGroups(fCtx, rm); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/RegistersByNameRequestCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/RegistersByNameRequestCache.java new file mode 100644 index 0000000..b29aab5 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/acpm/RegistersByNameRequestCache.java @@ -0,0 +1,63 @@ +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 > { + + 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 > rm) { + final Map registerMap = new HashMap(); + ((IRegisters)fService).getRegisterGroups(fCtx, new DataRequestMonitor(fService.getExecutor(), rm) { + public void handleSuccess() { + final List registers = new ArrayList(); + 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(fService.getExecutor(), crm) { + public void handleSuccess() { + IRegisterDMContext[] regs = getData(); + for (IRegisterDMContext reg : regs) { + registers.add(reg); + } + crm.done(); + } + }); + } + } + }); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/AbstractDisassembler.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/AbstractDisassembler.java new file mode 100644 index 0000000..81ba6ad --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/AbstractDisassembler.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * 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 disassembleInstructions(IAddress startAddress, IAddress endAddress, + ByteBuffer codeBuffer, Map options, IDisassemblyDMContext dmc) + throws CoreException { + IDisassembledInstruction inst = null; + ArrayList result = new ArrayList(); + 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/AssemblyFormatter.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/AssemblyFormatter.java new file mode 100644 index 0000000..162cf76 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/AssemblyFormatter.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * 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.
+ * E.g. 0x12 => "12" or "0x12"
+ * 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/CodeBufferUnderflowException.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/CodeBufferUnderflowException.java new file mode 100644 index 0000000..be4e7dc --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/CodeBufferUnderflowException.java @@ -0,0 +1,28 @@ +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.
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)); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/DisassembledInstruction.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/DisassembledInstruction.java new file mode 100644 index 0000000..ae2528f --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/DisassembledInstruction.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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() + "]" : ""); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCInstruction.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCInstruction.java new file mode 100644 index 0000000..f864d84 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCInstruction.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * 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(); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCInstructionFunctionInfo.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCInstructionFunctionInfo.java new file mode 100644 index 0000000..8780d9b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCInstructionFunctionInfo.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCMixedInstruction.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCMixedInstruction.java new file mode 100644 index 0000000..43a9d94 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/EDCMixedInstruction.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * 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(); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/IDisassembledInstruction.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/IDisassembledInstruction.java new file mode 100644 index 0000000..048dd06 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/IDisassembledInstruction.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/IDisassembler.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/IDisassembler.java new file mode 100644 index 0000000..32092c6 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/disassembler/IDisassembler.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * 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 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 disassembleInstructions(IAddress start_address, IAddress end_address, + ByteBuffer code_bytes, Map options, IDisassemblyDMContext dmc) throws CoreException; +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractCompositeFormatProvider.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractCompositeFormatProvider.java new file mode 100644 index 0000000..607b555 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractCompositeFormatProvider.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * 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 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 getChildIterator(IExpressionDMContext variable) throws CoreException { + List 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 childExpressions = getChildren(variable); + return childExpressions.size(); + } + + /** + * Gets the children. + * + * @param variable the variable + * @return the children + * @throws CoreException the core exception + */ + protected List getChildren(IExpressionDMContext variable) throws CoreException { + List childExpressions = new ArrayList(); + 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 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 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(); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractStringFormatter.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractStringFormatter.java new file mode 100644 index 0000000..5749848 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractStringFormatter.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractVariableConverter.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractVariableConverter.java new file mode 100644 index 0000000..12f86ba --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/AbstractVariableConverter.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultArrayFormatter.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultArrayFormatter.java new file mode 100644 index 0000000..dad0dae --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultArrayFormatter.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * 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 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultCStringFormatter.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultCStringFormatter.java new file mode 100644 index 0000000..09bf72a --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultCStringFormatter.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * 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 null 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultCompositeFormatter.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultCompositeFormatter.java new file mode 100644 index 0000000..8d3ef44 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/DefaultCompositeFormatter.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * 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 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/EDCFormatterMessages.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/EDCFormatterMessages.java new file mode 100644 index 0000000..496c3ca --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/EDCFormatterMessages.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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() { + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/EDCFormatterMessages.properties b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/EDCFormatterMessages.properties new file mode 100644 index 0000000..d5480c9 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/EDCFormatterMessages.properties @@ -0,0 +1,11 @@ +############################################################################### +# 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} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/FormatUtils.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/FormatUtils.java new file mode 100644 index 0000000..aeed5a6 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/FormatUtils.java @@ -0,0 +1,486 @@ +/******************************************************************************* + * 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 buffer = new ArrayList(); + 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 buffer = new ArrayList(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 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 getAllChildExpressions(IExpressionDMContext variable) { + + IEDCExpression variableDMC = (IEDCExpression) variable; + Expressions expressions = (Expressions) variableDMC.getExpressionsService(); + if (expressions == null) + return Collections.emptyList(); + + List kids = Arrays.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 null + */ + 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); + } + + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/ITypeContentProvider.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/ITypeContentProvider.java new file mode 100644 index 0000000..7daaa46 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/ITypeContentProvider.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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 + * @throws CoreException on errors + */ + Iterator 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; +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/IVariableFormatProvider.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/IVariableFormatProvider.java new file mode 100644 index 0000000..367f318 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/IVariableFormatProvider.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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); +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/IVariableValueConverter.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/IVariableValueConverter.java new file mode 100644 index 0000000..7a8ece0 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/formatter/IVariableValueConverter.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * 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; +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ByteBufferStreamBuffer.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ByteBufferStreamBuffer.java new file mode 100644 index 0000000..2a4c29a --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ByteBufferStreamBuffer.java @@ -0,0 +1,67 @@ +/* +* 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCApplication.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCApplication.java new file mode 100644 index 0000000..6920e72 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCApplication.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCDebugPreferenceInitializer.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCDebugPreferenceInitializer.java new file mode 100644 index 0000000..5a78b25 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCDebugPreferenceInitializer.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCDebugger.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCDebugger.java new file mode 100644 index 0000000..5494bc0 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCDebugger.java @@ -0,0 +1,249 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCTrace.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCTrace.java new file mode 100644 index 0000000..3bb5586 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/EDCTrace.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * 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; + *
see + * + * How do I use the Platform debug tracing facility? + */ +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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ExecutablesSourceContainer.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ExecutablesSourceContainer.java new file mode 100644 index 0000000..627df24 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ExecutablesSourceContainer.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * 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 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 ); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/FileStreamBuffer.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/FileStreamBuffer.java new file mode 100644 index 0000000..9c76fd5 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/FileStreamBuffer.java @@ -0,0 +1,73 @@ +/* +* 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/HostOS.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/HostOS.java new file mode 100644 index 0000000..3d3d4f9 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/HostOS.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * 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 null + */ + 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/IMemoryAccess.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/IMemoryAccess.java new file mode 100644 index 0000000..658ca69 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/IMemoryAccess.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * 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; +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/MemoryStreamBuffer.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/MemoryStreamBuffer.java new file mode 100644 index 0000000..9c581ec --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/MemoryStreamBuffer.java @@ -0,0 +1,61 @@ +/* +* 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/NumberFormatUtils.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/NumberFormatUtils.java new file mode 100644 index 0000000..17ff813 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/NumberFormatUtils.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/PathUtils.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/PathUtils.java new file mode 100644 index 0000000..31ec0f1 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/PathUtils.java @@ -0,0 +1,273 @@ +/******************************************************************************* + * 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. + *

+ * 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.

+ * 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.

+ *

+ * ("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 null for the system value + * @return array of IPath, never null + */ + 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/PersistentCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/PersistentCache.java new file mode 100644 index 0000000..4092cb6 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/PersistentCache.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * 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 getData(Class 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 caches = Collections.synchronizedMap(new HashMap()); + 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 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 getCachedData(String cacheIdentifier, Class 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 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/StreamBufferBase.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/StreamBufferBase.java new file mode 100644 index 0000000..e12852d --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/StreamBufferBase.java @@ -0,0 +1,251 @@ +/* +* 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/TCFServiceManager.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/TCFServiceManager.java new file mode 100644 index 0000000..d29e774 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/TCFServiceManager.java @@ -0,0 +1,618 @@ +/******************************************************************************* + * 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). + * + *

+ * 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 localIPAddresses; + + private List tcfAgentLaunchers; + + private static final String EXTENSION_POINT_NAME = "tcfAgentLauncher"; + + private List 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(); + launchedtcfAgentLaunchers = new ArrayList(); + + 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 all the attributes in [attributesToMatch] appear + * identically in [attributes] (keys and respective values). Basically, is + * [attributesToMatch] a subset of [attributes]? + */ + public static boolean matchesAllAttributes(Map attributes, Map 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 attributesToMatch, boolean localAgentsOnly) { + List registeredPeerLabels = new ArrayList(); + List registeredAgents = new ArrayList(); + + // 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 attributesToMatch, final boolean localAgentsOnly) throws CoreException { + // first find running peers with matching attributes + final List runningCandidates1 = new ArrayList(); + + Protocol.invokeAndWait(new Runnable() { + public void run() { + // This collection is only changed in TCF dispatcher thread. + // So don't worry about race condition. + Collection 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 runningCandidates2 = new ArrayList(); + final List runningLocalAgentPorts = new ArrayList(); + + for (final IPeer peer : runningCandidates1) { + + // wait up to 3 seconds for the asynchronous task. + TCFTask task = new TCFTask(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 waitForChannel = new WaitForResult(); + + 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 waitForService = new WaitForResult(); + + 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 peerAttrs) throws CoreException { + final WaitForResult waitForPeer = new WaitForResult() { + }; + + 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 getLocalIPAddresses() throws CoreException { + List ret = new ArrayList(); + + Enumeration 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 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); + } + } + } + }); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/WaitForResult.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/WaitForResult.java new file mode 100644 index 0000000..6b1dc41 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/WaitForResult.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * 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 implements Future { + + 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ZipFileUtils.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ZipFileUtils.java new file mode 100644 index 0000000..efd1a44 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/ZipFileUtils.java @@ -0,0 +1,347 @@ +/******************************************************************************* + * 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. + *

+ * 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. + *

+ * 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 zipFileExtensions = new ArrayList(); + 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 listZipContents(String zipFileName) throws IOException{ + List zipContents = new ArrayList(); + + ZipFile zipFile = new ZipFile(zipFileName); + Enumeration 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 unzipFiles(java.io.File zippedfile, String destPath, IProgressMonitor monitor) + throws FileNotFoundException, IOException { + ZipFile zipFile = new ZipFile(zippedfile); + + Enumeration entries = zipFile.entries(); + List outputFiles = new ArrayList(); + 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(); + } + } + + + + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvalMessages.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvalMessages.java new file mode 100644 index 0000000..0f8eda1 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvalMessages.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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() { + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvalMessages.properties b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvalMessages.properties new file mode 100644 index 0000000..e7d930a --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvalMessages.properties @@ -0,0 +1,55 @@ +############################################################################### +# 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvaluationEngine.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvaluationEngine.java new file mode 100644 index 0000000..b501681 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTEvaluationEngine.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * 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 = ""; //$NON-NLS-1$ + private final EDCServicesTracker tracker; + private final IDMContext context; + private final TypeEngine typeEngine; + + private static final Map compiledExpressionsCache = + Collections.synchronizedMap(new HashMap()); + + /** + * @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("", ("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("", ("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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTInstructionCompiler.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTInstructionCompiler.java new file mode 100644 index 0000000..6b0d3fe --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/ASTInstructionCompiler.java @@ -0,0 +1,522 @@ +/******************************************************************************* + * 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 stack; + private final InstructionSequence instructions; + private int counter; + private String errorMessage; + private boolean active = true; + + public ASTInstructionCompiler( String snippet) { + super(true); + stack = new Stack(); + 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)); + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/ArrayDimensionType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/ArrayDimensionType.java new file mode 100644 index 0000000..7897423 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/ArrayDimensionType.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/ArraySubscript.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/ArraySubscript.java new file mode 100644 index 0000000..f5971f5 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/ArraySubscript.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * 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); + } + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/BinaryLogicalOperator.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/BinaryLogicalOperator.java new file mode 100644 index 0000000..44911a5 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/BinaryLogicalOperator.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * 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; + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/BinaryOperator.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/BinaryOperator.java new file mode 100644 index 0000000..b289f63 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/BinaryOperator.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/CompoundInstruction.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/CompoundInstruction.java new file mode 100644 index 0000000..720631d --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/CompoundInstruction.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/EvaluateID.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/EvaluateID.java new file mode 100644 index 0000000..f9f97a3 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/EvaluateID.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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 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)); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/FieldReference.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/FieldReference.java new file mode 100644 index 0000000..fd3daa6 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/FieldReference.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/GetValue.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/GetValue.java new file mode 100644 index 0000000..fcaf336 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/GetValue.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * 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. + *

+ * 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(); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/IArrayDimensionType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/IArrayDimensionType.java new file mode 100644 index 0000000..bf50556 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/IArrayDimensionType.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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); +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/IInvalidExpression.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/IInvalidExpression.java new file mode 100644 index 0000000..684b7ea --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/IInvalidExpression.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/Instruction.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/Instruction.java new file mode 100644 index 0000000..87f5942 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/Instruction.java @@ -0,0 +1,521 @@ +/******************************************************************************* + * 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 + * null 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). + *

+ * 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 null 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). + *

+ * 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.) + *

+ * 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 }, + }; + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/InstructionSequence.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/InstructionSequence.java new file mode 100644 index 0000000..e417b20 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/InstructionSequence.java @@ -0,0 +1,259 @@ +/******************************************************************************* + * 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 fInstructions; + /** + * A collection of error messages (String) that occurred while + * creating this expression + */ + private List 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(10); + fErrors = new ArrayList(); + fSnippet = snippet; + } + + /** + * Copy constructor + * + * @param original - the instance to copy + */ + public InstructionSequence(InstructionSequence original) { + fInstructions = new ArrayList(Arrays.asList(original.getInstructions())); + fErrors = new ArrayList(Arrays.asList(original.getErrorMessages())); + fSnippet = original.getSnippet(); + fException = original.getException(); + } + + /** + * Get the runtime exception that occurred while evaluating this expression + * + * @param runtime + * exception, or null 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 iter = fInstructions.iterator(); iter.hasNext(); ) { + if (iter.next() instanceof NoOp) { + iter.remove(); + } + } + } + + /** + * Fixup instructions like: *(*)& + *

+ * 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. + *

+ * 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(); + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/Interpreter.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/Interpreter.java new file mode 100644 index 0000000..4ceae01 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/Interpreter.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * 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 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(); + 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 + * null or empty + */ + public OperandValue getResult() { + if (stack == null || stack.isEmpty()) { + return lastValue; + } + OperandValue top = stack.peek(); + return top; + } + + public TypeEngine getTypeEngine() { + return typeEngine; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/NoOp.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/NoOp.java new file mode 100644 index 0000000..e706b7e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/NoOp.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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 { + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperandValue.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperandValue.java new file mode 100644 index 0000000..56af32c --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperandValue.java @@ -0,0 +1,409 @@ +/******************************************************************************* + * 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 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorAddrOf.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorAddrOf.java new file mode 100644 index 0000000..9644e54 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorAddrOf.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryAnd.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryAnd.java new file mode 100644 index 0000000..91a8e83 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryAnd.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * 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 leftOperand & rightOperand + * @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 leftOperand & rightOperand + * @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 leftOperand & rightOperand + * @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 leftOperand & rightOperand + */ + @Override + protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) { + return leftOperand & rightOperand; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryOr.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryOr.java new file mode 100644 index 0000000..74c8f21 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryOr.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * 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 leftOperand | rightOperand + * @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 leftOperand | rightOperand + * @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 leftOperand | rightOperand + * @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 leftOperand | rightOperand + */ + @Override + protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) { + return leftOperand | rightOperand; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryXor.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryXor.java new file mode 100644 index 0000000..6cfbd77 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBinaryXor.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * 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 leftOperand ^ rightOperand + * @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 leftOperand ^ rightOperand + * @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 leftOperand ^ rightOperand + * @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 leftOperand ^ rightOperand + */ + @Override + protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) { + return leftOperand ^ rightOperand; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBitwiseNot.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBitwiseNot.java new file mode 100644 index 0000000..553bbc7 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorBitwiseNot.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * 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 ~operand + * @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 ~operand + * @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 ~operand + * @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 false + */ + @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 null + * @throws CoreException + */ + @Override + protected String getStringResult(String operand) throws CoreException { + throw EDCDebugger.newCoreException(ASTEvalMessages.UnsupportedStringOperation); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorCast.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorCast.java new file mode 100644 index 0000000..6d3880b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorCast.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorCastValue.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorCastValue.java new file mode 100644 index 0000000..fd09ba5 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorCastValue.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorDivide.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorDivide.java new file mode 100644 index 0000000..091ae94 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorDivide.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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 leftOperand / rightOperand + * @throws CoreException + * if rightOperand 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 leftOperand / rightOperand + * @throws CoreException + * if rightOperand 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 leftOperand / rightOperand + * @throws CoreException + * if rightOperand 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 leftOperand / rightOperand + */ + @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 leftOperand / rightOperand + */ + @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 false + */ + @Override + protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) { + return false; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorEquals.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorEquals.java new file mode 100644 index 0000000..f524b01 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorEquals.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * 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 leftOperand == rightOperand, + * 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 leftOperand == rightOperand, + * 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 leftOperand == rightOperand, + * 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 leftOperand == rightOperand, + * 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 leftOperand == rightOperand, + * 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 leftOperand == rightOperand, + * 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 leftOperand == rightOperand, + * and false otherwise + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException { + return leftOperand.equals(rightOperand); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorGreaterEqual.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorGreaterEqual.java new file mode 100644 index 0000000..20c9626 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorGreaterEqual.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * 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 leftOperand >= rightOperand, + * 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 leftOperand >= rightOperand, + * 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 leftOperand >= rightOperand, + * 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 leftOperand >= rightOperand, + * 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 leftOperand >= rightOperand, + * 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 false + */ + @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 leftOperand >= rightOperand, + * and false otherwise + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException { + return leftOperand.compareTo(rightOperand) >= 0; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorGreaterThan.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorGreaterThan.java new file mode 100644 index 0000000..2da911e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorGreaterThan.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * 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 leftOperand > rightOperand, 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 leftOperand > rightOperand, 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 leftOperand > rightOperand, 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 leftOperand > rightOperand, 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 leftOperand > rightOperand, 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 leftOperand > rightOperand, and + * false otherwise + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException { + return leftOperand.compareTo(rightOperand) > 0; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorIndirection.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorIndirection.java new file mode 100644 index 0000000..124e7ad --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorIndirection.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * 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")); + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLessEqual.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLessEqual.java new file mode 100644 index 0000000..42f4674 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLessEqual.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * 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 leftOperand <= rightOperand, + * 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 leftOperand <= rightOperand, + * 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 leftOperand <= rightOperand, + * 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 leftOperand <= rightOperand, + * 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 leftOperand <= rightOperand, + * 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 false + */ + @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 leftOperand <= rightOperand, + * and false otherwise + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException { + return leftOperand.compareTo(rightOperand) <= 0; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLessThan.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLessThan.java new file mode 100644 index 0000000..36bf245 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLessThan.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * 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 leftOperand < rightOperand, 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 leftOperand < rightOperand, 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 leftOperand < rightOperand, 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 leftOperand < rightOperand, 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 leftOperand < rightOperand, 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 false + */ + @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 leftOperand < rightOperand, and false otherwise + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException { + return leftOperand.compareTo(rightOperand) < 0; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalAnd.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalAnd.java new file mode 100644 index 0000000..ddbae53 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalAnd.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * 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 false + * @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 false + * @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 false + * @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 false + */ + @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 false + */ + @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 leftOperand && rightOperand 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 false + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException { + return true; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalNot.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalNot.java new file mode 100644 index 0000000..000a315 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalNot.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * 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 false + * @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 false + * @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 false + * @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 false + */ + @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 false + */ + @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 !operand + */ + @Override + protected boolean getBooleanResult(boolean leftOperand) { + return !leftOperand; + } + + /** + * Get boolean result of applying logical NOT "!" to a string + * + * @param operand + * - string operand + * @return false + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand) throws CoreException { + // not of address + return false; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalOr.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalOr.java new file mode 100644 index 0000000..4d97b43 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorLogicalOr.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * 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 false + * @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 false + * @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 false + * @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 false + */ + @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 false + */ + @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 leftOperand || rightOperand 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 false + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException { + // address or + return true; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorMinus.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorMinus.java new file mode 100644 index 0000000..e1b999e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorMinus.java @@ -0,0 +1,206 @@ +/******************************************************************************* + * 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 leftOperand - rightOperand + * @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 leftOperand - rightOperand + * @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 leftOperand - rightOperand, 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 leftOperand - rightOperand + */ + @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 leftOperand - rightOperand + */ + @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 false + */ + @Override + protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) { + return false; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorModulo.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorModulo.java new file mode 100644 index 0000000..85b96ef --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorModulo.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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 leftOperand % rightOperand + * @throws CoreException + * if rightOperand 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 leftOperand % rightOperand + * @throws CoreException + * if rightOperand 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 leftOperand % rightOperand + * @throws CoreException + * if rightOperand 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 false + */ + @Override + protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) { + return false; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorMultiply.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorMultiply.java new file mode 100644 index 0000000..40cb543 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorMultiply.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * 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 leftOperand * rightOperand + * @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 leftOperand * rightOperand + * @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 leftOperand * rightOperand, 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 leftOperand * rightOperand + */ + @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 leftOperand * rightOperand + */ + @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 false + */ + @Override + protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) { + return false; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorNotEquals.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorNotEquals.java new file mode 100644 index 0000000..748e7eb --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorNotEquals.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * 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 leftOperand != rightOperand, + * 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 leftOperand != rightOperand, + * 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 leftOperand != rightOperand, + * 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 leftOperand != rightOperand, + * 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 leftOperand != rightOperand, + * 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 leftOperand != rightOperand, + * 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 leftOperand != rightOperand, + * and false otherwise + * @throws CoreException + */ + @Override + protected boolean getStringResult(String leftOperand, String rightOperand) throws CoreException { + return !leftOperand.equals(rightOperand); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorPlus.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorPlus.java new file mode 100644 index 0000000..0fe52cb --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorPlus.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * 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 leftOperand + rightOperand + * @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 leftOperand + rightOperand + * @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 leftOperand + rightOperand, 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 leftOperand + rightOperand + */ + @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 leftOperand + rightOperand + */ + @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 false + */ + @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 leftOperand + rightOperand + * @throws CoreException + */ + @Override + protected String getStringResult(String leftOperand, String rightOperand) throws CoreException { + return leftOperand + rightOperand; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorShiftLeft.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorShiftLeft.java new file mode 100644 index 0000000..c61b040 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorShiftLeft.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * 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 leftOperand << rightOperand + * @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 leftOperand << rightOperand + * @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 leftOperand << rightOperand, 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorShiftRight.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorShiftRight.java new file mode 100644 index 0000000..8f36735 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorShiftRight.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * 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 leftOperand >> rightOperand + * @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 leftOperand >> rightOperand + * @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 leftOperand >> rightOperand + * @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 false + */ + @Override + protected boolean getBooleanResult(boolean leftOperand, boolean rightOperand) { + return false; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorUnaryMinus.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorUnaryMinus.java new file mode 100644 index 0000000..14c5e39 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorUnaryMinus.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * 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 -operand + * @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 -operand + * @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 -operand + * @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 -operand + */ + @Override + protected float getFloatResult(float operand) { + return -operand; + } + + /** + * Get double result of applying unary minus "-" to a double + * + * @param operand + * - double operand + * @return -operand + */ + @Override + protected double getDoubleResult(double operand) { + return -operand; + } + + /** + * Get boolean result of applying unary minus "-" to a boolean + * + * @param operand + * - boolean operand + * @return false + */ + @Override + protected boolean getBooleanResult(boolean operand) { + return false; + } + + /** + * Get string result of applying unary minus "-" to a string + * + * @param operand + * - string operand + * @return null + * @throws CoreException + */ + @Override + protected String getStringResult(String operand) throws CoreException { + throw EDCDebugger.newCoreException(ASTEvalMessages.UnsupportedStringOperation); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorUnaryPlus.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorUnaryPlus.java new file mode 100644 index 0000000..295ff1d --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/OperatorUnaryPlus.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * 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 operand + * @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 operand + * @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 operand + * @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 operand + */ + @Override + protected float getFloatResult(float operand) { + return operand; + } + + /** + * Get double result of applying unary plus "+" to a double + * + * @param operand + * - double operand + * @return operand + */ + @Override + protected double getDoubleResult(double operand) { + return operand; + } + + /** + * Get boolean result of applying unary plus "+" to a boolean + * + * @param operand + * - boolean operand + * @return false + */ + @Override + protected boolean getBooleanResult(boolean operand) { + return false; + } + + /** + * Get string result of applying unary plus "+" to a string + * + * @param operand + * - string operand + * @return null + * @throws CoreException + */ + @Override + protected String getStringResult(String operand) throws CoreException { + throw EDCDebugger.newCoreException(ASTEvalMessages.UnsupportedStringOperation); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushBoolean.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushBoolean.java new file mode 100644 index 0000000..5db6420 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushBoolean.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushChar.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushChar.java new file mode 100644 index 0000000..e043669 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushChar.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * 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); + } + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushDouble.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushDouble.java new file mode 100644 index 0000000..161481e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushDouble.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushFloat.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushFloat.java new file mode 100644 index 0000000..8e441f3 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushFloat.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushLongOrBigInteger.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushLongOrBigInteger.java new file mode 100644 index 0000000..494f210 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushLongOrBigInteger.java @@ -0,0 +1,347 @@ +/******************************************************************************* + * 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); + } + } + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushString.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushString.java new file mode 100644 index 0000000..c5d0a33 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/PushString.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/SimpleInstruction.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/SimpleInstruction.java new file mode 100644 index 0000000..aa08a80 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/SimpleInstruction.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/UnaryLogicalOperator.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/UnaryLogicalOperator.java new file mode 100644 index 0000000..1552729 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/UnaryLogicalOperator.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * 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; + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/UnaryOperator.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/UnaryOperator.java new file mode 100644 index 0000000..ebe5ba0 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/UnaryOperator.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * 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; + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/VariableWithValue.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/VariableWithValue.java new file mode 100644 index 0000000..61fe7f4 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/eval/ast/engine/instructions/VariableWithValue.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/FormatExtensionManager.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/FormatExtensionManager.java new file mode 100644 index 0000000..f18648f --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/FormatExtensionManager.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * 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 ids) { + return chooseAnyBeforeDefault(ids); + } + + public String chooseTypeContentProvider(IType type, Collection ids) { + return chooseAnyBeforeDefault(ids); + } + + public String chooseVariableValueConverter(IType type, Collection ids) { + return chooseAnyBeforeDefault(ids); + } + + private String chooseAnyBeforeDefault(Collection 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 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(); + formatProviders.put(id, new FormatProviderExtension(label, formatProvider)); + } catch (Exception e) { + EDCDebugger.getMessageLogger().logError("Could not create formatting extension", e); + } + } + } + + private interface Getter { + T get(IVariableFormatProvider fp, IType type); + String choose(IType type, Collection ids); + } + + private T getProvider(IType type, Getter getter) { + if (!enabled) + return null; + Map providers = new HashMap(); + for (Entry 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() { + public ITypeContentProvider get(IVariableFormatProvider fp, IType type) { + return fp.getTypeContentProvider(type); + } + public String choose(IType type, Collection ids) { + return chooser.chooseTypeContentProvider(type, ids); + } + }); + } + + public IVariableValueConverter getVariableValueConverter(IType type) { + return getProvider(type, new Getter() { + public IVariableValueConverter get(IVariableFormatProvider fp, IType type) { + return fp.getVariableValueConverter(type); + } + public String choose(IType type, Collection ids) { + return chooser.chooseVariableValueConverter(type, ids); + } + }); + } + + public IVariableValueConverter getDetailValueConverter(IType type) { + return getProvider(type, new Getter() { + public IVariableValueConverter get(IVariableFormatProvider fp, IType type) { + return fp.getDetailValueConverter(type); + } + public String choose(IType type, Collection ids) { + return chooser.chooseDetailValueConverter(type, ids); + } + }); + } + + public void setFormatProviderChooser(IVariableFormatProviderChooser chooser) { + this.chooser = chooser; + } + + public String[] getVariableFormatProviderIds() { + Set 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/IVariableFormatManager.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/IVariableFormatManager.java new file mode 100644 index 0000000..2f6640c --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/IVariableFormatManager.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/IVariableFormatProviderChooser.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/IVariableFormatProviderChooser.java new file mode 100644 index 0000000..9047349 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/formatter/IVariableFormatProviderChooser.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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 ids); + + String chooseVariableValueConverter(IType type, Collection ids); + + String chooseDetailValueConverter(IType type, Collection ids); +} \ No newline at end of file diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/CSourceLookup.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/CSourceLookup.java new file mode 100644 index 0000000..4973671 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/CSourceLookup.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * 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> directors = new HashMap>(); + + public CSourceLookup(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return EDCDebugger.getBundleContext(); + } + + public void addSourceLookupDirector(ISourceLookupDMContext ctx, CSourceLookupDirector director) { + List directorsInContext = directors.get(ctx); + if (directorsInContext == null) + directorsInContext = new ArrayList(); + directorsInContext.add(director); + directors.put(ctx, directorsInContext); + } + + public CSourceLookupDirector[] getSourceLookupDirectors(ISourceLookupDMContext ctx) { + List 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()); + + requestMonitor.done(); + } + + @Override + public void shutdown(final RequestMonitor requestMonitor) { + unregister(); + super.shutdown(requestMonitor); + } + + public void getDebuggerPath(ISourceLookupDMContext sourceLookupCtx, Object source, + final DataRequestMonitor 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 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(); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/ServicesLaunchSequence.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/ServicesLaunchSequence.java new file mode 100644 index 0000000..eeaeeec --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/ServicesLaunchSequence.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/ShutdownSequence.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/ShutdownSequence.java new file mode 100644 index 0000000..44ba7f8 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/ShutdownSequence.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * 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 void shutdownService(Class 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(); + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/SnapshotLaunch.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/SnapshotLaunch.java new file mode 100644 index 0000000..91864d6 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/SnapshotLaunch.java @@ -0,0 +1,14 @@ +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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/SnapshotLaunchDelegate.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/SnapshotLaunchDelegate.java new file mode 100644 index 0000000..9a7366a --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/launch/SnapshotLaunchDelegate.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * 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()); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/BreakpointAttributeTranslator.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/BreakpointAttributeTranslator.java new file mode 100644 index 0000000..0b7119b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/BreakpointAttributeTranslator.java @@ -0,0 +1,459 @@ +/******************************************************************************* + * 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 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> 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> retVal = new ArrayList>(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> bpsInfo, + BreakpointEventType eventType) { + for (IBreakpoint bp : bpsInfo.keySet()) { + if (! (bp instanceof ICBreakpoint)) // not C breakpoints, bail out. + return; + + final ICBreakpoint icbp = (ICBreakpoint) bp; + + Map 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 convertAttributes(Map platformBPAttrDelta) { + // For EDC, we don't need any conversion yet....11/08/09. + return new HashMap(platformBPAttrDelta); + } + + public void resolveBreakpoint(IBreakpointsTargetDMContext context, IBreakpoint breakpoint, + final Map attributes, final DataRequestMonitor>> drm) { + + final List> targetBPAttrs = new ArrayList>(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 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(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 addrs = symService.getFunctionAddress(module, function); + for (IAddress a : addrs) { + oneBPAttr = new HashMap(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(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 targetAttr = new HashMap(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 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 platformBpAttrs = marker.getAttributes(); + + // Just make a copy of the platform attributes. + // Add conversion or addition when needed. + Map attrs = new HashMap(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 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 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Breakpoints.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Breakpoints.java new file mode 100644 index 0000000..abfe6c0 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Breakpoints.java @@ -0,0 +1,1207 @@ +/******************************************************************************* + * 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 userBreakpoints = new HashMap(); + + /** + * Internal temporary breakpoints set by debugger for stepping. + */ + private final List tempBreakpoints = new ArrayList(); + + private org.eclipse.tm.tcf.services.IBreakpoints tcfBreakpointService; + + // Module in which startup breakpoint is installed for the debug session. + private Map startupBreakpointModule = new HashMap(); + + private ISourceLocator sourceLocator; + + private Map fBreakpointMarkers = new HashMap(); + + static private long nextBreakpointID = 1; + + // ///////////////////////////////////////////////////////////////////////// + // Breakpoint Events + // ///////////////////////////////////////////////////////////////////////// + + public class BreakpointsChangedEvent extends AbstractDMEvent 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 properties; + private int hitCount; + + public BreakpointDMData(long id, IBreakpointDMContext context, IAddress[] addresses, + Map properties) { + super(); + this.id = id; + this.context = context; + this.addresses = addresses; + this.originalInstruction = null; + this.properties = new HashMap(properties); // make a copy + } + + public BreakpointDMData(long id, IBreakpointDMContext context, IAddress[] addresses, + byte[] fOriginalInstruction, Map properties) { + super(); + this.id = id; + this.context = context; + this.addresses = addresses; + this.originalInstruction = fOriginalInstruction; + this.properties = new HashMap(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 getProperties() { + return properties; + } + + public IBreakpointDMContext getContext() { + return context; + } + + public void setProperties(Map props) { + properties = new HashMap(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 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 drm) { + Set 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 attributes, + DataRequestMonitor 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 attributes, + final DataRequestMonitor 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( + 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).
+ * 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(), new DataRequestMonitor( + 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 props, + final DataRequestMonitor 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 properties = new HashMap(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( + 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(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(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 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 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 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 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
+ * 1. the breakpoint has no condition, or
+ * 2. the breakpoint condition is invalid in syntax, or
+ * 3. the breakpoint condition cannot be resolved, or
+ * 4. the breakpoint condition is true.
+ * Otherwise the result in the drm is false. + * + */ + public void evaluateBreakpointCondition(IExecutionDMContext context, final BreakpointDMData bp, final DataRequestMonitor 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(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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/EDCServicesMessages.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/EDCServicesMessages.java new file mode 100644 index 0000000..1a90b90 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/EDCServicesMessages.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * 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() { + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/EDCServicesMessages.properties b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/EDCServicesMessages.properties new file mode 100644 index 0000000..7618d6e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/EDCServicesMessages.properties @@ -0,0 +1,18 @@ +############################################################################### +# 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. diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Expressions.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Expressions.java new file mode 100644 index 0000000..5e1a6ca --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Expressions.java @@ -0,0 +1,1428 @@ +/******************************************************************************* + * 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 null + */ + 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 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 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 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 rm) { + rm.setData(new IEDCExpression[0]); + rm.done(); + } + + public void getExpressionAddressData(final IExpressionDMContext exprContext, final DataRequestMonitor 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 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 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( + 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 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 rm) { + asyncExec(new Runnable() { + public void run() { + getSubExpressions(exprContext, new DataRequestMonitor(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 rm) { + + List children = new ArrayList(); + Iterator 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 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 arrayChildren = new ArrayList(); + 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 exprList = new ArrayList(); + 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 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 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 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(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(ImmediateExecutor.getInstance(), null) { + + @Override + protected void handleSuccess() { + depth[0] = depth[0] - 1; + IExpressions.IExpressionDMContext[] subExpressions = getData(); + for (IExpressionDMContext iExpressionDMContext : subExpressions) { + loadExpressionValues(iExpressionDMContext, depth); + } + }}); + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/INoop.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/INoop.java new file mode 100644 index 0000000..0d48b35 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/INoop.java @@ -0,0 +1,36 @@ +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 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 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/LineEntryMapper.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/LineEntryMapper.java new file mode 100644 index 0000000..932041d --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/LineEntryMapper.java @@ -0,0 +1,144 @@ +/* +* 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 getAddressRangesAtSource( + IModuleLineEntryProvider moduleLineEntryProvider, + IPath sourceFile, + int line) { + Collection 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 getAddressRangesAtSource( + IModuleLineEntryProvider moduleLineEntryProvider, + IPath sourceFile, + int startLine, + int endLine) { + Collection fileProviders = + moduleLineEntryProvider.getLineEntryProvidersForFile(sourceFile); + if (fileProviders.isEmpty()) + return null; + + int lastColumn = -1; + IPath lastFile = null; + int bestLine = endLine; + + List addrRanges = null; + for (ILineEntryProvider fileProvider : fileProviders) { + + Collection entries = fileProvider.getLineEntriesForLines(sourceFile, startLine, endLine); + if (addrRanges == null && !entries.isEmpty()) + addrRanges = new ArrayList(); + + 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 returnRanges = addrRanges; + return Collections.unmodifiableCollection(returnRanges); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Memory.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Memory.java new file mode 100644 index 0000000..5c0dffa --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Memory.java @@ -0,0 +1,471 @@ +/******************************************************************************* + * 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 memoryCaches; + private long tcfTimeout; + + private class MemoryChangedEvent extends AbstractDMEvent 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(); + + 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 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( + 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(); + 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 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); + + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/MemoryCache.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/MemoryCache.java new file mode 100644 index 0000000..e62250b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/MemoryCache.java @@ -0,0 +1,836 @@ +/******************************************************************************* + * 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 tcfMemoryContexts = Collections.synchronizedMap(new HashMap()); + 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 getListOfMissingBlocks(IAddress reqBlockStart, int count) { + if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { reqBlockStart.toHexAddressString(), count })); } + LinkedList list = new LinkedList(); + + synchronized (memoryBlockList) + { + ListIterator 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 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 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 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 tcfTask = new TCFTask(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 tcfTask = new TCFTask(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 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 tcfTask = new TCFTask(TIMEOUT) { + + public void run() { + final TCFTask 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 { + 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 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 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 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; + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Modules.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Modules.java new file mode 100644 index 0000000..0068959 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Modules.java @@ -0,0 +1,1188 @@ +/******************************************************************************* + * 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> modules = Collections + .synchronizedMap(new HashMap>()); + + 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 addresses; + + public EDCLineAddresses(int lineNumber, IAddress addr) { + super(); + this.lineNumber = lineNumber; + addresses = new ArrayList(); + addresses.add(addr); + } + + public EDCLineAddresses(int lineNumber, List addrs) { + super(); + this.lineNumber = lineNumber; + addresses = new ArrayList(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 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 runtimeSections = new ArrayList(); + + public ModuleDMC(ISymbolDMContext symbolContext, Map 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 properties = new HashMap(); + 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.
+ *
+ * The relocation handling is target environment dependent. + * Implementation here has been tested for debug applications on + * Windows, Linux and Symbian.
+ * + * @param props + * - runtime section properties from OS or from loader. + */ + public void relocateSections(Map 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 pp = new HashMap(); + 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 pp = new HashMap(); + + 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 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 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 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 moduleList = modules.get(symContextID); + if (moduleList == null) { + moduleList = Collections.synchronizedList(new ArrayList()); + 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 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 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 addrRanges = new ArrayList(1); + + for (IModuleDMContext module : moduleList) { + ModuleDMC mdmc = (ModuleDMC) module; + IEDCSymbolReader reader = mdmc.getSymbolReader(); + + if (reader != null) { + + Collection linkAddressRanges = null; + Map> cachedRanges = new HashMap>(); + // Check the persistent cache + String cacheKey = reader.getSymbolFile().toOSString() + ADDRESS_RANGE_CACHE; + String noFileCacheKey = reader.getSymbolFile().toOSString() + NO_FILE_CACHE; + Set 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> 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(); + 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 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 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 codeLines = null; + + Map> cache = new HashMap>(); + // Check the persistent cache + String cacheKey = reader.getSymbolFile().toOSString() + LINE_ADDRESSES_CACHE; + String noFileCacheKey = reader.getSymbolFile().toOSString() + NO_FILE_CACHE; + @SuppressWarnings("unchecked") + Set 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> 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(); + 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 runtimeCLs = new ArrayList(codeLines.size()); + for (ILineAddresses cl : codeLines) { + List rt_addrs = new ArrayList(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> drm) { + final List addrs = new ArrayList(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(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 rm) { + rm.setData(new ModuleDMData((ModuleDMC) dmc)); + rm.done(); + } + + public void getModules(ISymbolDMContext symCtx, DataRequestMonitor rm) { + String symContextID = ((IEDCDMContext) symCtx).getID(); + IModuleDMContext[] moduleList = getModulesForContext(symContextID); + rm.setData(moduleList); + rm.done(); + } + + public IModuleDMContext[] getModulesForContext(String symContextID) { + synchronized (modules) { + List 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 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 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 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 contextModules = Collections.synchronizedList(new ArrayList()); + + 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 properties = new HashMap(); + 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 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Noop.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Noop.java new file mode 100644 index 0000000..2c6fc0b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Noop.java @@ -0,0 +1,64 @@ +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 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 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; + } + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Processes.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Processes.java new file mode 100644 index 0000000..4137a1c --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Processes.java @@ -0,0 +1,303 @@ +/******************************************************************************* + * 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 rm) { + rm.done(); + } + + public void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor rm) { + ExecutionDMC edcDMC = (ExecutionDMC) dmc; + rm.setData(edcDMC.canDetach()); + rm.done(); + } + + public void canTerminate(IThreadDMContext thread, DataRequestMonitor rm) { + ExecutionDMC executionDmc = (ExecutionDMC) thread; + rm.setData(executionDmc.canTerminate()); + rm.done(); + } + + public void debugNewProcess(IDMContext dmc, String file, Map attributes, + DataRequestMonitor 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(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 rm) { + rm.done(); + } + + public void getExecutionData(IThreadDMContext dmc, DataRequestMonitor rm) { + if (dmc instanceof IEDCExecutionDMC) + rm.setData(new ExecutionDMData((ExecutionDMC) dmc)); + rm.done(); + } + + public void getProcessesBeingDebugged(IDMContext dmc, DataRequestMonitor 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 rm) { + rm.done(); + } + + public void isDebugNewProcessSupported(IDMContext dmc, DataRequestMonitor rm) { + rm.done(); + } + + public void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor rm) { + rm.done(); + } + + public void isRunNewProcessSupported(IDMContext dmc, DataRequestMonitor rm) { + rm.done(); + } + + public void runNewProcess(IDMContext dmc, String file, Map attributes, + DataRequestMonitor 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/RunControl.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/RunControl.java new file mode 100644 index 0000000..aee60e1 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/RunControl.java @@ -0,0 +1,2761 @@ +/******************************************************************************* + * 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 { + + private final StateChangeReason reason; + private final Map params; + + public DMCSuspendedEvent(IExecutionDMContext dmc, StateChangeReason reason, Map 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 getParams() { + return params; + } + + } + + public static class SuspendedEvent extends DMCSuspendedEvent implements ISuspendedDMEvent { + + public SuspendedEvent(IExecutionDMContext dmc, + StateChangeReason reason, Map params) { + super(dmc, reason, params); + } + + } + + public static class ContainerSuspendedEvent extends DMCSuspendedEvent implements IContainerSuspendedDMEvent { + + public ContainerSuspendedEvent(IExecutionDMContext dmc, + StateChangeReason reason, Map params) { + super(dmc, reason, params); + } + + public IExecutionDMContext[] getTriggeringContexts() { + return new IExecutionDMContext[]{getDMContext()}; + } + } + + public abstract static class DMCResumedEvent extends AbstractDMEvent { + + 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 children = Collections.synchronizedList(new ArrayList()); + 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 disabledRanges = Collections.synchronizedList(new ArrayList()); + private boolean suspendEventsEnabled = true; + + public ExecutionDMC(ExecutionDMC parent, Map 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 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 properties = new HashMap(); + 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()), + RunControl.this.getProperties()); + } + + protected void contextSuspended(String pc, String reason, final Map 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 preprocessDrm = new DataRequestMonitor(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 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()).
+ * 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 params = new HashMap(); + 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 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 properties, RunControlContext tcfContext) { + super(parent, properties, tcfContext); + } + + @Override + public ExecutionDMC contextAdded(Map 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 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()), + 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 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. + *

+ * 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 properties, RunControlContext tcfContext) { + super(parent, properties, tcfContext); + assert !RunControl.getProperty(properties, PROP_IS_CONTAINER, true); + } + + @Override + protected DMCSuspendedEvent createSuspendedEvent(StateChangeReason reason, Map 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 props) { + super(null, props, null); + } + + @Override + public ExecutionDMC contextAdded(Map 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 dmcsByID = new HashMap(); + + 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 props = new HashMap(); + props.put(IEDCDMContext.PROP_ID, "root"); + rootExecutionDMC = new RootExecutionDMC(props); + } + + public void canResume(IExecutionDMContext context, DataRequestMonitor rm) { + rm.setData(((ExecutionDMC) context).isSuspended() ? Boolean.TRUE : Boolean.FALSE); + rm.done(); + } + + public void canStep(IExecutionDMContext context, StepType stepType, DataRequestMonitor rm) { + rm.setData(((ExecutionDMC) context).canStep() ? Boolean.TRUE : Boolean.FALSE); + rm.done(); + } + + public void canSuspend(IExecutionDMContext context, DataRequestMonitor 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 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 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:
+ * 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.
+ * 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. + *

+ * Subclass can override this method to add their own special preprocessing, + * while calling super implementation to carry out the default common. + *

+ * 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 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(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
+ * - 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 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 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(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 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 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(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 options = new HashMap(); + 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(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 options = new HashMap(); + + List 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 stopPoints = new ArrayList(); + final List runToAndCheckPoints = new ArrayList(); + 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(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(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 implements IStartedDMEvent { + + public StartedEvent(IExecutionDMContext context) { + super(context); + } + } + + public static class ExitedEvent extends AbstractDMEvent 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 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 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 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>(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>(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 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(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 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 addrs = new ArrayList(1); + addrs.add(address); + runToAddresses(context, addrs, rm); + } + + public void canMoveToLine(IExecutionDMContext context, String sourceFile, + int lineNumber, boolean resume, final DataRequestMonitor 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>(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>(getExecutor(), rm){ + @Override + protected void handleCompleted() { + if (! isSuccess()) { + rm.setStatus(getStatus()); + rm.done(); + } + else { + List 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 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> drm) { + List addrs = new ArrayList(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 result = new ArrayList(); + synchronized (dmcsByID) + { + Collection 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 properties, String name, boolean defaultValue) { + Boolean b = (Boolean)properties.get(name); + return (b == null ? defaultValue : b); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Signals.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Signals.java new file mode 100644 index 0000000..4c16ce3 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Signals.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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 + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Snapshots.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Snapshots.java new file mode 100644 index 0000000..f1f73a7 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Snapshots.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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()); + } + } + } + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Symbols.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Symbols.java new file mode 100644 index 0000000..59d08dd --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/services/dsf/Symbols.java @@ -0,0 +1,307 @@ +/******************************************************************************* + * 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> readerCache = new HashMap>(); + 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> 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 getLineEntriesForAddressRange(ISymbolDMContext context, IAddress start, IAddress end) { + List lineEntries = new ArrayList(); + + 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. + *

+ * Note: this is not meaningful by itselfis typically used from {@link StackFrameDMC#getFrameRegisters()}. + * @param context + * @param runtimeAddress + * @return {@link IFrameRegisters} or null + */ + 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 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(reader)); + } + + return reader; + } + + @Override + public void shutdown(RequestMonitor rm) { + super.shutdown(rm); + } + + /** + * This is exposed only for testing. + */ + public static void releaseReaderCache() { + Collection> readers = readerCache.values(); + for (WeakReference 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 getFunctionAddress(IEDCModuleDMContext module, String functionName) { + + List ret = new ArrayList(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 functions = symReader.getModuleScope().getFunctionsByName(function); +// for (IFunctionScope f : functions) { +// IAddress breakAddr = f.getLowAddress(); +// ... + + // assume it's the human-readable name first + Collection 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/Album.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/Album.java new file mode 100644 index 0000000..3f8f3eb --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/Album.java @@ -0,0 +1,1325 @@ +/******************************************************************************* + * 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 snapshotList = new ArrayList(); + private String sessionID = ""; + private String recordingSessionID = ""; + private IPath albumRootDirectory; + private boolean launchConfigSaved; + private String launchType; + private HashMap launchProperties; + private String launchName = ""; + private String name; + private boolean loaded; + private boolean metaDataLoaded; + private final Set files = new HashSet(); + + 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 listeners = Collections.synchronizedList(new ArrayList()); + + private static Map albumsBySessionID = Collections.synchronizedMap(new HashMap()); + private static Map albumsRecordingBySessionID = Collections.synchronizedMap(new HashMap()); + private static Map albumsByLocation = Collections.synchronizedMap(new HashMap()); + private static Map launchNames = Collections.synchronizedMap(new HashMap()); + + 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(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 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 infoProps = new HashMap(); + Set 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 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(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(); + 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 getLaunchProperties() { + return launchProperties; + } + + public void setLaunchProperties(HashMap 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 containers = new ArrayList(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 containers = new ArrayList(); + Set devicesAlreadyAdded = new HashSet(); + 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 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 frameQuery = new Query() { + @Override + protected void execute( + DataRequestMonitor 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 query = new Query() { + @Override + protected void execute(final DataRequestMonitor drm) { + try { + Snapshot newSnapshot = finalAlbum.createSnapshot(session, stackFrame, monitor); + // Fire the event to anyone listening + for (ISnapshotAlbumEventListener l : new ArrayList(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. + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/AlbumSourceContainer.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/AlbumSourceContainer.java new file mode 100644 index 0000000..143f5c9 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/AlbumSourceContainer.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/AlbumSourceContainerType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/AlbumSourceContainerType.java new file mode 100644 index 0000000..289cefc --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/AlbumSourceContainerType.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/ISnapshotAlbumEventListener.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/ISnapshotAlbumEventListener.java new file mode 100644 index 0000000..0acbd7e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/ISnapshotAlbumEventListener.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/Snapshot.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/Snapshot.java new file mode 100644 index 0000000..2b44aa4 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/Snapshot.java @@ -0,0 +1,305 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/SnapshotLaunchSequence.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/SnapshotLaunchSequence.java new file mode 100644 index 0000000..d2b71ea --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/SnapshotLaunchSequence.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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. + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/SnapshotUtils.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/SnapshotUtils.java new file mode 100644 index 0000000..524c205 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/snapshot/SnapshotUtils.java @@ -0,0 +1,649 @@ +/******************************************************************************* + * 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 properties) { + + Element rootElement = doc.createElement(PROPERTIES); + Iterator 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) value); + } else if (value instanceof Map) { + element = createMapElement(doc, MAP_ATTRIBUTE, key, (Map) value); + } else if (value instanceof HashSet) { + element = createSetElement(doc, SET_ATTRIBUTE, key, (Set) 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 Element for the specified + * java.util.List + * + * @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 list) { + Element listElement = doc.createElement(elementType); + listElement.setAttribute(KEY, listKey); + Iterator 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 Element for the specified + * java.util.Set + * + * @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 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 Element for the specified + * java.util.Map + *

+ * NOTE: this creates a 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 map) { + Element mapElement = doc.createElement(elementType); + mapElement.setAttribute(KEY, mapKey); + for (Map.Entry 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 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 String 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 properties) throws CoreException { + properties.put(element.getAttribute(KEY), element.getAttribute(VALUE)); + } + + /** + * Loads an Integer 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 properties) throws CoreException { + properties.put(element.getAttribute(KEY), new Integer(element.getAttribute(VALUE))); + } + + /** + * Loads an Long 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 properties) throws CoreException { + properties.put(element.getAttribute(KEY), new Long(element.getAttribute(VALUE))); + } + + /** + * Loads a serialized Object 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 properties) throws CoreException, IOException, ClassNotFoundException, DecoderException { + properties.put(element.getAttribute(KEY), createSerializedObjectFromString(element.getFirstChild().getTextContent())); + } + + /** + * Loads an BigInteger 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 properties) throws CoreException { + properties.put(element.getAttribute(KEY), new BigInteger(element.getAttribute(VALUE))); + } + + /** + * Loads a Boolean 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 properties) throws CoreException { + properties.put(element.getAttribute(KEY), Boolean.valueOf(element.getAttribute(VALUE))); + } + + /** + * Reads a List 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 properties) throws CoreException { + String listKey = element.getAttribute(KEY); + NodeList nodeList = element.getChildNodes(); + int entryCount = nodeList.getLength(); + List list = new ArrayList(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 Set 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 properties) throws CoreException { + String setKey = element.getAttribute(KEY); + NodeList nodeList = element.getChildNodes(); + int entryCount = nodeList.getLength(); + Set set = new HashSet(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 Map attribute from the specified XML node and loads + * it into the mapping of attributes + *

+ * NOTE: this creates a 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 properties) throws CoreException { + String mapKey = element.getAttribute(KEY); + NodeList nodeList = element.getChildNodes(); + int entryCount = nodeList.getLength(); + Map map = new HashMap(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(); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ArrayBoundType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ArrayBoundType.java new file mode 100644 index 0000000..b995e37 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ArrayBoundType.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ArrayType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ArrayType.java new file mode 100644 index 0000000..1586c39 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ArrayType.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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 bounds = new ArrayList(); + + public ArrayType(String name, IScope scope, int byteSize, Map 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CPPBasicType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CPPBasicType.java new file mode 100644 index 0000000..4d0b445 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CPPBasicType.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ClassType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ClassType.java new file mode 100644 index 0000000..b852be1 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ClassType.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * 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 properties) { + super(name, scope, ICompositeType.k_class, byteSize, properties, "class"); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CompileUnitScope.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CompileUnitScope.java new file mode 100644 index 0000000..6c088ce --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CompileUnitScope.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * 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 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 getFunctions() { + List functions = new ArrayList(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 parseLineTable(); + + + public Collection 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(); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CompositeType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CompositeType.java new file mode 100644 index 0000000..e21f369 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/CompositeType.java @@ -0,0 +1,399 @@ +/******************************************************************************* + * 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 fields = new ArrayList(); + + // classes inherited from + protected ArrayList inheritances = null; + + // template parameters + protected ArrayList 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 unknownOffsetFields = null; + + protected static class OffsetAndLength { + public long offset; + public long length; + } + + public CompositeType(String name, IScope scope, int key, int byteSize, Map 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(); + unknownOffsetFields.add(field); + } else + fields.add(field); + } + + public IField[] getFields() { + if (unknownOffsetFields != null) + setAnonymousUnionOffsets(); + ArrayList fieldList = new ArrayList(fields); + + return fieldList.toArray(new IField[fields.size()]); + } + + public void addTemplateParam(ITemplateParam templateParam) { + if (templateParams == null) { + templateParams = new ArrayList(2); + } + templateParams.add(templateParam); + } + + public ITemplateParam[] getTemplateParams() { + if (templateParams == null) + return new ITemplateParam[0]; + + ArrayList templateParamList = new ArrayList(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. "") 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(); + 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 nameQualifiers = new ArrayList(); + + 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 matches = new ArrayList(); + + 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 pruneMatches(ArrayList nameQualifiers, ArrayList matches) { + if (nameQualifiers.size() == 0) + return matches; + + for (int i = 0; i < matches.size(); i++) { + ArrayList matchQualifiers = new ArrayList(); + 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 findInheritedByName(String name, ICompositeType composite, String prefix, long offset, ArrayList 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ConstType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ConstType.java new file mode 100644 index 0000000..6ad9b1e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ConstType.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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 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(); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/EDCSourceFilesProvider.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/EDCSourceFilesProvider.java new file mode 100644 index 0000000..8e8e38c --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/EDCSourceFilesProvider.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Enumeration.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Enumeration.java new file mode 100644 index 0000000..95f694e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Enumeration.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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 enumerators = new ArrayList(); + HashMap enumeratorsByConstant = new HashMap(); + + public Enumeration(String name, IScope scope, int byteSize, Map 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Enumerator.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Enumerator.java new file mode 100644 index 0000000..51e3b2d --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Enumerator.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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$ + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FieldType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FieldType.java new file mode 100644 index 0000000..3d5b7bf --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FieldType.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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 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$ + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FileLineEntryProvider.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FileLineEntryProvider.java new file mode 100644 index 0000000..324834f --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FileLineEntryProvider.java @@ -0,0 +1,737 @@ +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.
+ * This is an internal class used by {@linkplain ModuleLineEntryProvider}. + */ +class FileLineEntryProvider implements ILineEntryProvider { + + private List lineEntries = new ArrayList(); + private List 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> lineEntriesByLine = new TreeMap>(); + + private TreeMap lineEntriesByAddress = new TreeMap(); + + 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 entries) { + cuEntries = new ArrayList(entries); + } + + protected List 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 currentMappings = lineEntriesByLine.get(entry.getLineNumber()); + if (currentMappings == null) { + currentMappings = new ArrayList(); + 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 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 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 entries = new ArrayList(), 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 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 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 x is an ancestor of c + */ + 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> tailLines + = lineEntriesByLine.tailMap(entry.getLineNumber()); + if (tailLines != null) { + List 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 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> headLines + = inline + ? lineEntriesByLine.headMap(headAddrs.get(headAddrs.lastKey()).getLineNumber()+1) + : lineEntriesByLine.headMap(entry.getLineNumber()); + + while (!headLines.isEmpty()) { + List 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 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 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 findClosestLineWithCode(IPath sourceFile, + int anchorLine, int neighbor_limit) { + List ret = new ArrayList(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 line_addrs = new ArrayList(); + 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FunctionScope.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FunctionScope.java new file mode 100644 index 0000000..d91fca1 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/FunctionScope.java @@ -0,0 +1,197 @@ +/******************************************************************************* + * 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 parameters = new ArrayList(); + 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 getParameters() { + return Collections.unmodifiableCollection(parameters); + } + + public ILocationProvider getFrameBaseLocation() { + return frameBaseLocationProvider; + } + + public Collection getVariablesInTree() { + List variables = new ArrayList(); + 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 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 scoped = new ArrayList(); + List varNames = new ArrayList(); + + recurseGetScopedVariables(this, scoped, varNames, linkAddress); + + return scoped; + } + + protected static void recurseGetScopedVariables(IScope scope, List scoped, List 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 scoped, List 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IAggregate.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IAggregate.java new file mode 100644 index 0000000..36b5979 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IAggregate.java @@ -0,0 +1,14 @@ +/******************************************************************************* + * 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 { +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IArrayBoundType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IArrayBoundType.java new file mode 100644 index 0000000..c2aa3dc --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IArrayBoundType.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IArrayType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IArrayType.java new file mode 100644 index 0000000..bf0bc44 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IArrayType.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IBasicType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IBasicType.java new file mode 100644 index 0000000..f9bff25 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IBasicType.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * 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(); +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ICPPBasicType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ICPPBasicType.java new file mode 100644 index 0000000..0ff2fec --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ICPPBasicType.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * 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(); +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ICompositeType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ICompositeType.java new file mode 100644 index 0000000..62c8cb4 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ICompositeType.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * 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(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IConstType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IConstType.java new file mode 100644 index 0000000..cdd2051 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IConstType.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * 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 { + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IEnumeration.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IEnumeration.java new file mode 100644 index 0000000..254d26b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IEnumeration.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IField.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IField.java new file mode 100644 index 0000000..14fc09c --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IField.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * 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(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IForwardTypeReference.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IForwardTypeReference.java new file mode 100644 index 0000000..76bb601 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IForwardTypeReference.java @@ -0,0 +1,29 @@ +/* +* 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(); +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IInheritance.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IInheritance.java new file mode 100644 index 0000000..86fe9b8 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IInheritance.java @@ -0,0 +1,57 @@ +/******************************************************************************* + + * 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 getProperties(); + + /** + * Get type inherited from + * + * @return type + */ + public IType getType(); + + /** + * Set type inherited from + * + * @param type + * type inherited from + */ + public void setType(IType type); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ILexicalBlockScope.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ILexicalBlockScope.java new file mode 100644 index 0000000..c8e2d51 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ILexicalBlockScope.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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 getVariablesInTree(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IMayBeQualifedType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IMayBeQualifedType.java new file mode 100644 index 0000000..85d2397 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IMayBeQualifedType.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IPointerType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IPointerType.java new file mode 100644 index 0000000..119982f --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IPointerType.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * 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 { + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IQualifierType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IQualifierType.java new file mode 100644 index 0000000..5b68cab --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IQualifierType.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * 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 { + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IReferenceType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IReferenceType.java new file mode 100644 index 0000000..beea795 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IReferenceType.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * 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 { + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IRuntimeSection.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IRuntimeSection.java new file mode 100644 index 0000000..05eb66e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/IRuntimeSection.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ISection.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ISection.java new file mode 100644 index 0000000..ce9f748 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ISection.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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 getProperties(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ISubroutineType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ISubroutineType.java new file mode 100644 index 0000000..db9607b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ISubroutineType.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * 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) +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ITemplateParam.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ITemplateParam.java new file mode 100644 index 0000000..01ee044 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ITemplateParam.java @@ -0,0 +1,17 @@ +/******************************************************************************* + + * 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 { +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ITypedef.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ITypedef.java new file mode 100644 index 0000000..f7308e6 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ITypedef.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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(); + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/InheritanceType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/InheritanceType.java new file mode 100644 index 0000000..dfdc42e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/InheritanceType.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * 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 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(); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/InvalidVariableLocation.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/InvalidVariableLocation.java new file mode 100644 index 0000000..cb24243 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/InvalidVariableLocation.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/LexicalBlockScope.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/LexicalBlockScope.java new file mode 100644 index 0000000..5137664 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/LexicalBlockScope.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * 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 getVariablesInTree() { + List variables = new ArrayList(); + 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/LineEntry.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/LineEntry.java new file mode 100644 index 0000000..efdc4b2 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/LineEntry.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * 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$ + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/MayBeQualifiedType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/MayBeQualifiedType.java new file mode 100644 index 0000000..174dccf --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/MayBeQualifiedType.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * 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 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/MemoryVariableLocation.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/MemoryVariableLocation.java new file mode 100644 index 0000000..0418415 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/MemoryVariableLocation.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * 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 memBuffer = new ArrayList(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 memBuffer = new ArrayList(); + 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ModuleLineEntryProvider.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ModuleLineEntryProvider.java new file mode 100644 index 0000000..5dc14f8 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ModuleLineEntryProvider.java @@ -0,0 +1,434 @@ +/* +* 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 { + private static final long serialVersionUID = -2157263701372708990L; + } + + /** + * basically, a typedef of {@link HashMap}<{@link IPath},{@link FileLineEntryProvider}> + */ + private final class PathToLineEntryMap extends HashMap { + private static final long serialVersionUID = 7064789571684986782L; + } + + // CUs we've already considered + private Set parsedCUs = new HashSet(); + // 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 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 fileProviders = new HashMap(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 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 getLineEntryProvidersForFile(IPath sourceFile) { + List cus = pathToLineEntryMap.get(sourceFile); + if (cus != null) + return Collections.unmodifiableCollection(cus); + + for (Map.Entry 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 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 getLineEntriesForLines(IPath file, + int startLineNumber, int endLineNumber) { + FileLineEntryProviders matches = pathToLineEntryMap.get(file); + if (matches == null) + return Collections.emptyList(); + + List ret = null; + for (FileLineEntryProvider provider : matches) { + Collection entries + = provider.getLineEntriesForLines(file, startLineNumber, endLineNumber); + if (!entries.isEmpty()) { + if (ret == null) + ret = new ArrayList(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 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 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 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 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 findClosestLineWithCode(IPath sourceFile, + int anchorLine, int neighbor_limit) { + List ret = new ArrayList(1); + + /* Check all compile units in the module for code line. + */ + Collection fileProviders = + getLineEntryProvidersForFile(sourceFile); + if (fileProviders.isEmpty()) + return ret; + + for (ILineEntryProvider fileProvider : fileProviders) { + + // Find code line in one CU using the source file + List 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 fileProviders = + getLineEntryProvidersForFile(sourceFile); + return fileProviders != null && ! fileProviders.isEmpty(); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/PointerType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/PointerType.java new file mode 100644 index 0000000..4ba1294 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/PointerType.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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 properties) { + super(name, scope, byteSize, properties); + } + + // create an internal pointer for expression evaluation + public PointerType() { + super("", null, 0, null); //$NON-NLS-1$ + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ReferenceType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ReferenceType.java new file mode 100644 index 0000000..ce308d5 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ReferenceType.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * 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 properties) { + super(name, scope, byteSize, properties); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RegisterOffsetVariableLocation.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RegisterOffsetVariableLocation.java new file mode 100644 index 0000000..b5cfd3b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RegisterOffsetVariableLocation.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RegisterVariableLocation.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RegisterVariableLocation.java new file mode 100644 index 0000000..439b504 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RegisterVariableLocation.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RuntimeSection.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RuntimeSection.java new file mode 100644 index 0000000..887fd17 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/RuntimeSection.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * 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 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Scope.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Scope.java new file mode 100644 index 0000000..d404c3d --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Scope.java @@ -0,0 +1,340 @@ +/******************************************************************************* + * 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 children = new ArrayList(); + protected List variables = new ArrayList(); + protected List enumerators = new ArrayList(); + private TreeMap 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 null + */ + 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 getChildren() { + return Collections.unmodifiableCollection(children); + } + + public Collection getVariables() { + return Collections.unmodifiableCollection(variables); + } + + public Collection 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 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(); + + 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Section.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Section.java new file mode 100644 index 0000000..222be38 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Section.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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 properties; + + public Section(int id, long size, IAddress linkAddress, Map properties) { + this.id = id; + this.size = size; + this.linkAddress = linkAddress; + this.properties = new HashMap(properties); // make a + // copy + } + + public int getId() { + return id; + } + + public long getSize() { + return size; + } + + public IAddress getLinkAddress() { + return linkAddress; + } + + public Map 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/StructType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/StructType.java new file mode 100644 index 0000000..78c07b3 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/StructType.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * 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 properties) { + super(name, scope, ICompositeType.k_struct, byteSize, properties, "struct"); //$NON-NLS-1$ + } + +} \ No newline at end of file diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SubroutineType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SubroutineType.java new file mode 100644 index 0000000..37c1b2f --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SubroutineType.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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 properties) { + super("", scope, 0, properties); //$NON-NLS-1$ + } + + // create an internal pointer for expression evaluation + public SubroutineType() { + super("", null, 0, null); //$NON-NLS-1$ + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Symbol.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Symbol.java new file mode 100644 index 0000000..ed6de1b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Symbol.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * 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$ + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SymbolsMessages.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SymbolsMessages.java new file mode 100644 index 0000000..928d1cc --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SymbolsMessages.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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() { + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SymbolsMessages.properties b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SymbolsMessages.properties new file mode 100644 index 0000000..d0695c4 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/SymbolsMessages.properties @@ -0,0 +1,23 @@ +############################################################################### +# 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/TemplateParamType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/TemplateParamType.java new file mode 100644 index 0000000..1a549e7 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/TemplateParamType.java @@ -0,0 +1,72 @@ +/******************************************************************************* + + * 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 getProperties() { + return null; + } + + public void setType(IType type) { + } + + public void dispose() { + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Type.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Type.java new file mode 100644 index 0000000..5204c57 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Type.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * 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 properties; // may be null + + public Type(String name, IScope scope, int byteSize, Map 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 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/TypedefType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/TypedefType.java new file mode 100644 index 0000000..abb1f53 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/TypedefType.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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 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(); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/UnionType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/UnionType.java new file mode 100644 index 0000000..1719eb3 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/UnionType.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * 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 properties) { + super(name, scope, ICompositeType.k_union, byteSize, properties, "union"); //$NON-NLS-1$ + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ValueVariableLocation.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ValueVariableLocation.java new file mode 100644 index 0000000..2f00934 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/ValueVariableLocation.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Variable.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Variable.java new file mode 100644 index 0000000..f7d66db --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/Variable.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * 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(); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/VolatileType.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/VolatileType.java new file mode 100644 index 0000000..1757625 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/VolatileType.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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 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(); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfCompileUnit.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfCompileUnit.java new file mode 100644 index 0000000..203fb99 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfCompileUnit.java @@ -0,0 +1,291 @@ +/******************************************************************************* + * 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 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 parseLineTable() { + DwarfInfoReader reader = new DwarfInfoReader(provider); + fileList = new ArrayList(); + 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 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 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 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 null + */ + 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfConstants.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfConstants.java new file mode 100644 index 0000000..11512e1 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfConstants.java @@ -0,0 +1,636 @@ +/******************************************************************************* + * 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; + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfDebugInfoProvider.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfDebugInfoProvider.java new file mode 100644 index 0000000..7fb9bfa --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfDebugInfoProvider.java @@ -0,0 +1,1459 @@ +/******************************************************************************* + * 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 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 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 attributes; + boolean hasChildren; + + AbbreviationEntry(long code, short tag, boolean hasChildren) { + // abbreviation code not stored + this.tag = tag; + this.hasChildren = hasChildren; + attributes = new ArrayList(); + } + } + + 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 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 attributeMap; + + AttributeList(AbbreviationEntry entry, IStreamBuffer in, byte addressSize, IStreamBuffer debugStrings) { + + int len = entry.attributes.size(); + attributeMap = new HashMap(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. + *

+ * Note: a singular DW_AT_low_pc means an entry point + *

+ * 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> compileUnitsPerFile = new HashMap>(); + + // list of compile units in .debug_info order + protected ArrayList compileUnits = new ArrayList(); + + // list of compile units with code (non-zero high address), sorted by low address + protected ArrayList sortedCompileUnitsWithCode = new ArrayList(); + + // 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 functionsByOffset = new HashMap(); + protected Map typesByOffset = Collections.synchronizedMap(new HashMap()); + + // for casting to a type, keep certain types by name + protected Map> typesByName = new HashMap>(); + // 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 scopesByOffset = new HashMap(); + + // entry points for CUs in the .debug_info section, used to dynamically parse CUs as needed + protected TreeMap debugOffsetsToCompileUnits = new TreeMap(); + + // forward references for tags we have not parsed yet. (These will go into typesByOffset once handled) + //Map forwardDwarfDefinitions = new HashMap(); + + // these are just for faster lookups + protected Map> functionsByName = new HashMap>(); + protected Map> variablesByName = new HashMap>(); + protected Map> publicFunctions = new HashMap>(); + protected Map> publicVariables = new HashMap>(); + + // abbreviation tables (lists of abbrev entries), mapped by .debug_abbrev offset + protected Map> abbreviationMaps = new HashMap>(); + + // mapping of PC range to frame description entries + protected TreeMap frameDescEntries = new TreeMap(); + // mapping of CIE offsets to parsed common info entries + protected Map commonInfoEntries = new HashMap(); + + + protected Set referencedFiles = new HashSet(); + 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 cuWithGlobalsArray = new HashSet(publicVariables.size()); + for (List 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 getFunctionsByName(String name) { + List 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 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 cuList = new ArrayList(); + + 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(0); + } else { + result = new ArrayList(functionsByName.size()); // at least this big + for (List functions : functionsByName.values()) + result.addAll(functions); + ((ArrayList) result).trimToSize(); + } + return Collections.unmodifiableCollection(result); + } + + public Collection getVariablesByName(String name, boolean globalsOnly) { + List 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(variablesByName.size()); // at least this big + for (List 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 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 cuList = new ArrayList(); + + 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(0); + + return Collections.unmodifiableCollection(result); + } + + /** + * @return the publicFunctions + */ + public Map> getPublicFunctions() { + ensureParsedInitially(); + return publicFunctions; + } + /** + * @return the publicVariables + */ + public Map> 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 getCompileUnitsForFile(IPath filePath) { + ensureParsedInitially(); + + List 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> 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 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(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 getTypes() { + ensureParsedForTypes(); + + ArrayList types = new ArrayList(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 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 null + */ + public FrameDescriptionEntry findFrameDescriptionEntry(IAddress framePC) { + DwarfInfoReader reader = new DwarfInfoReader(this); + if (frameDescEntries.isEmpty()) { + reader.parseForFrameIndices(); + } + + long pc = framePC.getValue().longValue(); + SortedMap 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 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 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 matchingTypes = null; + for (Iterator 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(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(0); + } + } + + // screen out types whose template list does not match + if (templateName2 != null) { + ArrayList matchingTypes = new ArrayList(types.size()); + for (Iterator 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 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(0); + + return Collections.unmodifiableCollection(types); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfDebugInfoProviderFactory.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfDebugInfoProviderFactory.java new file mode 100644 index 0000000..6e42b72 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfDebugInfoProviderFactory.java @@ -0,0 +1,56 @@ +/* +* 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFileHelper.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFileHelper.java new file mode 100644 index 0000000..87ee718 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFileHelper.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * 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 compDirAndNameMap; + + private int hits; + private long nextDump; + + public DwarfFileHelper(IPath symbolFile) { + this.symbolFile = symbolFile; + this.compDirAndNameMap = new HashMap(); + } + + 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
+ * -- extra path delimiter are removed.
+ * -- "a/b/" and "../c/d" are combined to "/a/c/d".
+ * -- 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. . + * + */ + public IPath normalizeFilePath(String compDir, String name) { + if (name == null || name.length() == 0) + return null; + + // don't count the entry "" 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 null + */ + 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(); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFrameRegisterProvider.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFrameRegisterProvider.java new file mode 100644 index 0000000..1d79354 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFrameRegisterProvider.java @@ -0,0 +1,950 @@ +/******************************************************************************* + * 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 regRules; + /** Context for the current frame */ + public IFrameDMContext context; + /** Accessor for registers from child frame */ + public IFrameRegisters childRegisters; + /** Initial locations from CIE */ + public Map initialRules; + /** Implicit stack for DW_CFA_remember_state and DW_CFA_restore_state*/ + public Stack> 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(); + this.stateStack = new Stack>(); + this.initialRules = new TreeMap(); + + 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(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 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 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 parseInitialLocations(DwarfDebugInfoProvider provider) throws CoreException { + List instrs = new ArrayList(); + + 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> 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> 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> parseRules( + DwarfDebugInfoProvider provider) throws CoreException { + TreeMap> rules = new TreeMap>(); + + 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 row = new ArrayList(); + + 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(); + 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFrameRegisters.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFrameRegisters.java new file mode 100644 index 0000000..9b9f195 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFrameRegisters.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * 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 cachedRegisters = new TreeMap(); + private final FrameDescriptionEntry fde; + private final DwarfDebugInfoProvider provider; + private Map 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 null 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 calculateFrameRegisterRules(InstructionState state) throws CoreException { + if (fde.getCIE() == null) + throw EDCDebugger.newCoreException(MessageFormat.format(DwarfMessages.DwarfFrameRegisters_NoCommonInfoEntry, fde)); + + List initialLocationInstructions = + fde.getCIE().getInitialLocations(provider); + + for (AbstractInstruction instr : initialLocationInstructions) { + instr.applyInstruction(state); + } + + // Run through rules until we hit one past our range + TreeMap> fdeInstrs = fde.getInstructions(provider); + + long currentPC = context.getModule().toLinkAddress(context.getInstructionPtrAddress()).getValue().longValue(); + + for (Map.Entry> 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFunctionScope.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFunctionScope.java new file mode 100644 index 0000000..9317a68 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfFunctionScope.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfInfoReader.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfInfoReader.java new file mode 100644 index 0000000..6b48dce --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfInfoReader.java @@ -0,0 +1,3451 @@ +/** + * 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> locationEntriesByOffset = Collections.synchronizedMap(new HashMap>()); + + // 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 typeToParentMap = Collections.synchronizedMap(new HashMap()); + 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 sComparatorByLowAddress = new Comparator() { + 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 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 variables = provider.publicVariables.get(baseName); + if (variables == null) { + variables = new ArrayList(); + } + variables.add(new PublicNameInfo(name, header, entry.tag)); + provider.publicVariables.put(baseName, variables); + } else if (entry.tag == DwarfConstants.DW_TAG_subprogram) { + List functions = provider.publicFunctions.get(baseName); + if (functions == null) { + functions = new ArrayList(); + 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 arrayList = (ArrayList)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 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> namesStore, BaseAndScopedNames baseAndScopedNames, + CompilationUnitHeader cuHeader, short tag) { + + List currentNames = namesStore.get(baseAndScopedNames.baseName); + if (currentNames == null) { + currentNames = new ArrayList(); + 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 abbrevs = parseDebugAbbreviation(header.abbreviationOffset); + + parseForAddresses(data, abbrevs, header, new Stack(), 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 abbrevs = parseDebugAbbreviation(header.abbreviationOffset); + + parseForTypes(data, abbrevs, header, new Stack()); + } 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 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. + * + *

See Chapter 7.4 32-Bit and 64-Bit DWARF Formats

+ * @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 null + */ + 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 dirList = new ArrayList(); + + // 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 parseLineTable(IScope scope, AttributeList attributes, List fileList) { + synchronized (provider) { + List lineEntries = new ArrayList(); + 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 dirList = new ArrayList(); + + // 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., "") + 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., "") + 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 abbrevs, CompilationUnitHeader header, + Stack 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 abbrevs, CompilationUnitHeader header, + Stack 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 typeStack = new Stack(); + 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(). + *

+ * 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. + *

+ * 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 getLocationRecord(long offset) { + // first check the cache + Collection 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 entryMap = new TreeMap(); + + 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 parseDebugAbbreviation(int abbreviationOffset) throws IOException { + Integer key = Integer.valueOf(abbreviationOffset); + Map abbrevs = provider.abbreviationMaps.get(key); + if (abbrevs == null) { + abbrevs = new HashMap(); + 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. + *
+ * 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 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 matchingCompileUnits = provider.compileUnitsPerFile.get(filePath); + + if (matchingCompileUnits == null) { + // first one. create it now. + matchingCompileUnits = new ArrayList(); + } + + 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 null (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 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 functions = provider.functionsByName.get(name); + if (functions == null) { + functions = new ArrayList(); + 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 functions = provider.functionsByName.get(name); + if (functions == null) { + functions = new ArrayList(); + 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 + * 4-byte long long1 + * 4-byte union + * 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 + * type union + * 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 variables = provider.variablesByName.get(name); + if (variables == null) { + variables = new ArrayList(); + 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 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 null 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 typeList = provider.typesByName.get(name); + if (typeList == null) { + typeList = new ArrayList(); + + // 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfMessages.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfMessages.java new file mode 100644 index 0000000..d372bca --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfMessages.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * 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() { + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfMessages.properties b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfMessages.properties new file mode 100644 index 0000000..2fe9c42 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfMessages.properties @@ -0,0 +1,58 @@ +############################################################################### +# 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=<> +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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfModuleScope.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfModuleScope.java new file mode 100644 index 0000000..737f4b4 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfModuleScope.java @@ -0,0 +1,268 @@ +/* +* 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 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 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 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 getEnumerators() { + ensureParsed(); + return super.getEnumerators(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getChildren() + */ + @Override + public Collection getChildren() { + ensureParsed(); + return super.getChildren(); + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.debug.edc.internal.symbols.Scope#getVariables() + */ + @Override + public Collection 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 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(); + } +} + diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfVariable.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfVariable.java new file mode 100644 index 0000000..c99be39 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/DwarfVariable.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/EDCSymbolReader.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/EDCSymbolReader.java new file mode 100644 index 0000000..7df0952 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/EDCSymbolReader.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * 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 getExecutableSections() { + return exeSymReader.getExecutableSections(); + } + + public IExecutableSection findExecutableSection(String sectionName) { + return exeSymReader.findExecutableSection(sectionName); + } + + public Collection getSections() { + return exeSymReader.getSections(); + } + + public IAddress getBaseLinkAddress() { + return exeSymReader.getBaseLinkAddress(); + } + + public long getModificationDate() { + return exeSymReader.getModificationDate(); + } + + public Collection findSymbols(String name) { + return exeSymReader.findSymbols(name); + } + + public Collection findUnmangledSymbols(String name) { + return exeSymReader.findUnmangledSymbols(name); + } + + public Collection 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationEntry.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationEntry.java new file mode 100644 index 0000000..685dd7c --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationEntry.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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(); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationExpression.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationExpression.java new file mode 100644 index 0000000..0125fcc --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationExpression.java @@ -0,0 +1,323 @@ +/******************************************************************************* + * 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationList.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationList.java new file mode 100644 index 0000000..2d6b121 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/LocationList.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * 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 + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/RangeList.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/RangeList.java new file mode 100644 index 0000000..51f77c4 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/dwarf/RangeList.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * 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 ranges = new ArrayList(); + 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 iterator() { + return new Iterator() { + 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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/BufferedRandomReadAccessFile.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/BufferedRandomReadAccessFile.java new file mode 100644 index 0000000..ae5d7d9 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/BufferedRandomReadAccessFile.java @@ -0,0 +1,388 @@ +/******************************************************************************* + * 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)); + } + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/ERandomAccessFile.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/ERandomAccessFile.java new file mode 100644 index 0000000..d03dfd9 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/ERandomAccessFile.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * 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(); + } +} + + diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/Elf.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/Elf.java new file mode 100644 index 0000000..88759f2 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/Elf.java @@ -0,0 +1,1231 @@ +/******************************************************************************* + * 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 { + + /* 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 { + + 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 dynList = new ArrayList(); + 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
slist = new ArrayList
(); + 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 symList = new ArrayList(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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/IRandomReadAccessFile.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/IRandomReadAccessFile.java new file mode 100644 index 0000000..02fac0e --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/IRandomReadAccessFile.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 (not relative to {@link #setFileOffset(long)}). + * @param size the size in bytes; may not extend beyond EOF. + */ + ByteBuffer createReadByteBuffer(long offset, long size) throws IOException; +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/README.txt b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/README.txt new file mode 100644 index 0000000..df03177 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/elf/README.txt @@ -0,0 +1,6 @@ + +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 diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/BaseExecutableSymbolicsReader.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/BaseExecutableSymbolicsReader.java new file mode 100644 index 0000000..0f3ce6b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/BaseExecutableSymbolicsReader.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * 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 executableSections = new HashMap(); + protected List sections = new ArrayList(); + protected List symbols = new ArrayList(); + 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 getExecutableSections() { + return Collections.unmodifiableCollection(executableSections.values()); + } + + public IExecutableSection findExecutableSection(String sectionName) { + return executableSections.get(sectionName); + } + + public Collection getSections() { + return Collections.unmodifiableCollection(sections); + } + + public Collection 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 findSymbols(String name) { + List matchSymbols = new ArrayList(); + + // 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 findUnmangledSymbols(String name) { + List matchSymbols = new ArrayList(); + + 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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/DebugInfoProviderFactory.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/DebugInfoProviderFactory.java new file mode 100644 index 0000000..404e256 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/DebugInfoProviderFactory.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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 providerMap = new HashMap(); + + 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 null + * @return {@link IDebugInfoProvider} or null if nothing supports it + */ + public static IDebugInfoProvider createFor(IPath binaryPath, IExecutableSymbolicsReader exeReader) { + for (Map.Entry 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); + } + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ElfExecutableSymbolicsReader.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ElfExecutableSymbolicsReader.java new file mode 100644 index 0000000..901b5e6 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ElfExecutableSymbolicsReader.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * 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 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(); + + 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 symbolAddressSet = new TreeSet(); + + 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ElfExecutableSymbolicsReaderFactory.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ElfExecutableSymbolicsReaderFactory.java new file mode 100644 index 0000000..f43aef0 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ElfExecutableSymbolicsReaderFactory.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ExecutableSection.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ExecutableSection.java new file mode 100644 index 0000000..e626b89 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ExecutableSection.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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 ? " <>": ""); //$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; + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ExecutableSymbolicsReaderFactory.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ExecutableSymbolicsReaderFactory.java new file mode 100644 index 0000000..bbf9b7c --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ExecutableSymbolicsReaderFactory.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * 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 providerMap = new HashMap(); + + 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 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 null 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 candidates = new ArrayList(); + + 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() { + 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); + } + } + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/FileStatistics.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/FileStatistics.java new file mode 100644 index 0000000..c04d184 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/FileStatistics.java @@ -0,0 +1,64 @@ +/* +* 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); + } +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ISectionMapper.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ISectionMapper.java new file mode 100644 index 0000000..fcc11e8 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/ISectionMapper.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * 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(); +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/PEFileExecutableSymbolicsReader.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/PEFileExecutableSymbolicsReader.java new file mode 100644 index 0000000..55a2a8b --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/PEFileExecutableSymbolicsReader.java @@ -0,0 +1,276 @@ +/******************************************************************************* + * 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 sectionsByPEID = new HashMap(); + + 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 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(); + 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); + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/PEFileExecutableSymbolicsReaderFactory.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/PEFileExecutableSymbolicsReaderFactory.java new file mode 100644 index 0000000..0073026 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/PEFileExecutableSymbolicsReaderFactory.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * 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; + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/SectionInfo.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/SectionInfo.java new file mode 100644 index 0000000..2c59874 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/SectionInfo.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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; + } + + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/SectionMapper.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/SectionMapper.java new file mode 100644 index 0000000..9791720 --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/SectionMapper.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * 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 loadedSections; + /** subset of sections loaded into memory maps */ + private Map mappedBuffers; + + public SectionMapper(IPath hostFile, boolean isLE) { + this.hostFile = hostFile; + this.isLE = isLE; + this.efile = null; + this.loadedSections = new HashMap(); + this.mappedBuffers = new HashMap(); + } + + public void dispose() { + for (Map.Entry 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(); + } + } + } + +} diff --git a/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/UnmanglerEABI.java b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/UnmanglerEABI.java new file mode 100644 index 0000000..aedc1dc --- /dev/null +++ b/org.eclipse.cdt.debug.edc/src/org/eclipse/cdt/debug/edc/internal/symbols/files/UnmanglerEABI.java @@ -0,0 +1,1824 @@ +/******************************************************************************* + * 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) + *

+ * TODO: + */ +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 pushes ; // lengths of buffer when pushed + private List substitutions; + private Map substitutionTypes; + private int lastTypeNameIndex; + + private List templateArgs; + private int templateArgBase; + private Stack templateArgStack; // length of templateArgs when pushed + + private Stack 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(); + substitutions = new ArrayList(); + substitutionTypes = new HashMap(); + templateArgs = new ArrayList(); + templateArgStack = new Stack(); + backtracks = new Stack(); + lastTypeNameIndex = -1; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + String remaining = getRemaining(); + if (remaining.length() == 0) + remaining = "<>"; + 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 unmangledMap = new WeakHashMap(); + private static WeakHashMap withoutArgsMap = new WeakHashMap(); + + /* (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: + + + ::= _Z + ::= + ::= + ::= + */ + 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; + } + } + + /* + ::= + ::= + ::= + */ + private String unmangleEncoding(UnmangleState state) throws UnmanglingException { + state.push(); + + String name; + + // ferret out + 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 _ + int offset = doUnmangleNumber(state); + state.consume('_'); + state.buffer.append(" _ _ + int offset = doUnmangleNumber(state); + state.consume('_'); + int voffset = doUnmangleNumber(state); + state.consume('_'); + state.buffer.append(" ::= TV # virtual table + ::= TT # VTT structure (construction vtable index) + ::= TI # typeinfo structure + ::= TS # typeinfo name (null-terminated byte string) + ::= GV # Guard variable for one-time initialization + # No + ::= T + # base is the nominal target function of thunk + ::= h _ + ::= v _ + ::= + # non-virtual base override + ::= _ + # virtual base override, with vcall offset + + ::= Tc + # 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("'); + break; + case 'T': + type = unmangleType(state); + state.buffer.append("'); + break; + case 'I': + type = unmangleType(state); + state.buffer.append("'); + break; + case 'S': + type = unmangleType(state); + 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 + 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("'); + 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(); + } + + /* + ::= = N ... + ::= = number or St ... + ::= = unscoped | S ... | I ... + ::= # 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(); + } + + /* + := Z E [] + := Z E s [] + + := _ # when number < 10 + := __ _ # 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(); + } + + /* + ::= + ::= [n] + ::= + */ + 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] + */ + private int doUnmangleNumber(UnmangleState state) { + boolean neg = false; + if (state.peek() == 'n') { + state.skip(); + neg = true; + } + int number = doUnmangleNonNegativeNumber(state); + return neg ? -number : number; + } + + /* + ::= N [] E + ::= N [] 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(); + } + + + /* + ::= I + 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(); + } + + /* + ::= # type or template + ::= X E # expression + ::= # simple expressions ('L') + ::= I * E # argument pack + ::= sp # 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(); + } + + + /** + ::= L E # integer literal + ::= L E # floating literal + ::= L E # string literal + ::= L E # nullptr literal (i.e., "LDnE") + ::= L _ E # complex floating point literal (C 2000) + ::= L 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); + + } + /* + ::= T_ # first template parameter + ::= T _ + + */ + 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; + } + + /* + ::= S _ + ::= S_ + + ::= St # ::std:: + ::= Sa # ::std::allocator + ::= Sb # ::std::basic_string + ::= Ss # ::std::basic_string < char, + ::std::char_traits, + ::std::allocator > + ::= Si # ::std::basic_istream > + ::= So # ::std::basic_ostream > + ::= Sd # ::std::basic_iostream > + + */ + 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,::std::allocator >"); break; + case 'i': + state.buffer.append("::std::basic_istream >"); break; + case 'o': + state.buffer.append("::std::basic_ostream >"); break; + case 'd': + state.buffer.append("::std::basic_iostream >"); 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; + } + + /* + ::= # ... 0-9 + ::= --> template=T... args=I... + ::= --> T... + ::= # empty + ::= --> S... + ::= --> name M + + left-recursion elimination: + ::= + ::= + ::= + ::= # empty + ::= + ::= M + ::= #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); + } + + /* + ::=