/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.tele.debug;
import static com.sun.max.platform.Platform.*;
import static com.sun.max.tele.MaxThreadState.*;
import java.util.*;
import java.util.logging.*;
import com.sun.cri.ci.*;
import com.sun.max.jdwp.vm.data.*;
import com.sun.max.jdwp.vm.proxy.*;
import com.sun.max.lang.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.data.*;
import com.sun.max.tele.memory.*;
import com.sun.max.tele.method.CodeLocation.MachineCodeLocation;
import com.sun.max.tele.method.*;
import com.sun.max.tele.object.*;
import com.sun.max.tele.util.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.actor.member.*;
import com.sun.max.vm.classfile.*;
import com.sun.max.vm.compiler.target.*;
import com.sun.max.vm.stack.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.value.*;
/**
* Represents a thread executing in a {@linkplain TeleProcess tele process}.
*/
public abstract class TeleNativeThread extends AbstractVmHolder
implements TeleVMCache, Comparable<TeleNativeThread>, MaxThread, VmAllocationHolder<MaxThread>, ThreadProvider {
@Override
protected String tracePrefix() {
return "[TeleNativeThread: " + Thread.currentThread().getName() + "] ";
}
private static final int TRACE_VALUE = 1;
private final TimedTrace updateTracer;
private long lastUpdateEpoch = -1L;
private static final Logger LOGGER = Logger.getLogger(TeleNativeThread.class.getName());
private final TeleProcess teleProcess;
private int suspendCount;
private final TeleRegisterSet teleRegisterSet;
private final TeleStack teleStack;
/**
* A cached stack trace for this thread, never null.
*/
private List<StackFrame> frames = Collections.emptyList();
/**
* The value of maxDepth when {@link #frames} was last updated.
*/
private int framesMaxDepth = -1;
/**
* Only if this value is less than the {@linkplain TeleProcess#epoch() epoch} of this thread's tele process, does
* the {@link #frames(int)} method do anything.
*/
private long framesRefreshedEpoch;
/**
* The last epoch at which the structure of the stack changed, even if the contents of the top frame may have.
*/
private long framesLastChangedEpoch;
/**
* Access to information about and contained in this thread's local storage area.
* Holds a dummy if no thread local information is available: i.e. either this is a non-Java thread, or
* the thread is very early in its creation sequence.
*/
private /*final*/ TeleThreadLocalsBlock threadLocalsBlock;
/**
* Access to associated {@link TeleThreadVMLog}, if assigned.
*/
private final TeleThreadVMLog vmLog;
private MaxThreadState state = SUSPENDED;
private VmTargetBreakpoint breakpoint;
private FrameProvider[] frameCache;
/**
* This thread's {@linkplain VmThread#id() identifier}.
*/
private final int id;
private final long localHandle;
private final long handle;
private final String entityName;
private final String entityDescription;
/**
* It seems impossible, experimentally, to identify the primordial thread by it's id or handle.
* However, the primordial thread is always the first thread gathered on startup.
* Need to be careful about ATTACH mode, however; in that case we never see the primordial thread.
*/
private static boolean seenPrimordial;
private final boolean isPrimordial;
/**
* The parameters accepted by {@link TeleNativeThread#TeleNativeThread(TeleProcess, Params)}.
*/
public static class Params {
public int id;
public long localHandle;
public long handle;
public TeleFixedMemoryRegion stackRegion;
public TeleFixedMemoryRegion threadLocalsRegion;
@Override
public String toString() {
return String.format("id=%d, localHandle=0x%08x, handle=%d, stackRegion=%s, tlasRegion=%s", id, localHandle, handle, MaxMemoryRegion.Util.asString(stackRegion), MaxMemoryRegion.Util.asString(threadLocalsRegion));
}
}
protected TeleNativeThread(TeleProcess teleProcess, Params params) {
super(teleProcess.vm());
final TimedTrace tracer = new TimedTrace(TRACE_VALUE, tracePrefix() + " creating id=" + params.id);
tracer.begin();
if (teleProcess.vm().inspectionMode() == MaxInspectionMode.ATTACH) {
this.isPrimordial = false;
} else {
this.isPrimordial = !seenPrimordial;
seenPrimordial = true;
}
this.teleProcess = teleProcess;
this.id = params.id;
this.localHandle = params.localHandle;
this.handle = params.handle;
this.entityName = "Thread-" + this.localHandle;
this.entityDescription = "The thread named " + this.entityName + " in the " + teleProcess.vm().entityName();
this.teleRegisterSet = new TeleRegisterSet(teleProcess.vm(), this);
if (params.threadLocalsRegion == null) {
final String name = this.entityName + " Locals (NULL, not allocated)";
this.threadLocalsBlock = new TeleThreadLocalsBlock(this, name);
} else {
final String name = this.entityName + " Locals";
this.threadLocalsBlock = new TeleThreadLocalsBlock(this, name, params.threadLocalsRegion.start(), params.threadLocalsRegion.nBytes());
}
if (teleProcess.vm().vmLog() instanceof TeleVMLogNative) {
this.vmLog = new TeleThreadVMLog(teleProcess.vm(), this);
} else {
this.vmLog = null;
}
this.breakpointIsAtInstructionPointer = platform().isa == ISA.SPARC;
final String stackName = this.entityName + " Stack";
this.teleStack = new TeleStack(teleProcess.vm(), this, stackName, params.stackRegion.start(), params.stackRegion.nBytes());
teleProcess.vm().addressSpace().add(this.teleStack.memoryRegion());
this.updateTracer = new TimedTrace(TRACE_VALUE, tracePrefix() + " updating");
tracer.end(null);
}
/**
* {@inheritDoc}
* <p>
* Memory-related updates get handled specially for threads, together with other state refresh.
*/
public final void updateMemoryStatus(long epoch) {
}
public final void updateCache(long epoch) {
if (epoch > lastUpdateEpoch) {
Trace.line(TRACE_VALUE + 1, tracePrefix() + "refresh thread=" + this);
if (state.allowsDataAccess()) {
refreshBreakpoint();
threadLocalsBlock.updateCache(epoch);
if (vmLog != null) {
vmLog.updateCache(epoch);
}
}
lastUpdateEpoch = epoch;
} else {
Trace.line(TRACE_VALUE, tracePrefix() + "redundant update epoch=" + epoch + ": " + this);
}
}
public final String entityName() {
return entityName;
}
public final String entityDescription() {
return entityDescription;
}
public final MaxEntityMemoryRegion<MaxThread> memoryRegion() {
// The thread has no VM memory allocated for itself; it allocates stack and locals spaces from the OS.
return null;
}
public final List<MaxEntityMemoryRegion<? extends MaxEntity> > memoryAllocations() {
final List<MaxEntityMemoryRegion<? extends MaxEntity> > allocations = new ArrayList<MaxEntityMemoryRegion<? extends MaxEntity> >(3);
if (teleStack.memoryRegion() != null) {
allocations.add(teleStack.memoryRegion());
}
if (threadLocalsBlock.memoryRegion() != null) {
allocations.add(threadLocalsBlock.memoryRegion());
}
if (vmLog != null && vmLog.memoryRegion() != null) {
allocations.add(vmLog.memoryRegion());
}
return allocations;
}
public boolean contains(Address address) {
return teleStack.contains(address) || (threadLocalsBlock != null && threadLocalsBlock.contains(address));
}
public final TeleObject representation() {
return teleVmThread();
}
public final MaxThreadVMLog vmLog() {
return vmLog;
}
public final int id() {
return id;
}
public final long handle() {
return handle;
}
public final String handleString() {
return "0x" + Long.toHexString(handle);
}
public final long localHandle() {
return localHandle;
}
public final boolean isPrimordial() {
return isPrimordial;
}
/**
* Determines if this thread is associated with a {@link VmThread} instance. Note that even if this method returns
* {@code true}, the {@link #teleVmThread()} method will return {@code null} if the thread has not reached the
* execution point in {@link VmThread#run} where the {@linkplain VmThreadLocal#VM_THREAD reference} to the
* {@link VmThread} object has been initialized.
*/
public final boolean isJava() {
return id > 0;
}
public final boolean isLive() {
return state != DEAD;
}
public final MaxThreadState state() {
return state;
}
public final VmTargetBreakpoint breakpoint() {
return breakpoint;
}
public final TeleThreadLocalsBlock localsBlock() {
return threadLocalsBlock;
}
public final TeleRegisterSet registers() {
return teleRegisterSet;
}
public final TeleStack stack() {
return teleStack;
}
public final MachineCodeLocation ipLocation() {
// No need to refresh registers: the instruction pointer is updated by updateAfterGather() which
// ensures that it is always in sync.
if (isLive()) {
try {
return codeLocations().createMachineCodeLocation(teleRegisterSet.instructionPointer(), "Instruction pointer");
} catch (InvalidCodeAddressException e) {
TeleWarning.message("Bad IP address " + e.getAddressString() + " in thread " + entityName() + ": " + e.getMessage());
}
}
return null;
}
public final TeleVmThread teleVmThread() {
return threadLocalsBlock.teleVmThread();
}
public final String vmThreadName() {
if (teleVmThread() != null) {
return teleVmThread().name();
}
return null;
}
/**
* @return a printable version of the thread's internal state that only shows key aspects
*/
public final String toShortString() {
final StringBuilder sb = new StringBuilder(100);
sb.append(isPrimordial() ? "primordial" : (teleVmThread() == null ? "native" : teleVmThread().name()));
sb.append("[id=").append(id());
sb.append(",handle=").append(handleString());
sb.append(",local handle=").append(localHandle());
sb.append(",type=").append(isPrimordial() ? "primordial" : (isJava() ? "Java" : "native"));
sb.append(",stat=").append(state.toString());
sb.append("]");
return sb.toString();
}
/**
* Imposes a total ordering between threads based on their {@linkplain #id() identifiers}.
*/
public final int compareTo(TeleNativeThread other) {
return Longs.compare(handle(), other.handle());
}
/**
* Immutable; thread-safe.
*
* @return the process in which this thread is running.
*/
public TeleProcess teleProcess() {
return teleProcess;
}
final StackFrame top() {
StackFrame top = new TeleStackFrameWalker(vm(), this).frames(2).get(0);
return top;
}
/**
* @param maxDepth the maximum length of the returned frame list
* @return the most currently refreshed frames on the thread's stack, never null.
*/
final List<StackFrame> frames(int maxDepth) {
final long epoch = teleProcess().epoch();
if (framesRefreshedEpoch < epoch || maxDepth != this.framesMaxDepth) {
Trace.line(TRACE_VALUE + 1, tracePrefix() + "refreshFrames (epoch=" + epoch + ") for " + this);
threadLocalsBlock.updateCache(epoch);
final List<StackFrame> newFrames = new TeleStackFrameWalker(vm(), this).frames(maxDepth);
framesMaxDepth = maxDepth;
assert !newFrames.isEmpty();
// See if the new stack is structurally equivalent to its predecessor, even if the contents of the top
// frame may have changed.
if (newFrames.size() != this.frames.size()) {
// Clear structural change; lengths are different
framesLastChangedEpoch = epoch;
} else {
// Lengths are the same; see if any frames differ.
final Iterator<StackFrame> oldFramesIterator = this.frames.iterator();
final Iterator<StackFrame> newFramesIterator = newFrames.iterator();
while (oldFramesIterator.hasNext()) {
final StackFrame oldFrame = oldFramesIterator.next();
final StackFrame newFrame = newFramesIterator.next();
if (!oldFrame.isSameFrame(newFrame)) {
framesLastChangedEpoch = epoch;
break;
}
}
}
framesRefreshedEpoch = epoch;
this.frames = newFrames;
}
return frames;
}
/**
* Tracks when the structure of the stack changes, in any respect other than
* the contents of the top frame.
*
* @return the last process epoch at which the structure of the stack changed.
*/
final Long framesLastChangedEpoch() {
return framesLastChangedEpoch;
}
/**
* Gets the value of maxDepth when the frames cache was last updated.
*/
final int framesMaxDepth() {
return framesMaxDepth;
}
/**
* Updates this thread with the information information made available while
* {@linkplain TeleProcess#gatherThreads(List) gathering} threads. This information is made available
* by the native tele layer as threads are discovered. Subsequent refreshing of cached thread state (such as
* {@linkplain TeleRegisterSet#updateCache(long) registers}, {@linkplain #frames(int) stack frames} and
* thread locals) depends on this information being available and up to date.
*
* @param state the state of the thread
* @param instructionPointer the current value of the instruction pointer for the thread
* @param threadLocalsRegion the memory region reported to be holding the thread local block; null if not available
* @param tlaSize the size of each Thread Locals Area in the thread local block.
*/
final void updateAfterGather(MaxThreadState state, Pointer instructionPointer, TeleFixedMemoryRegion threadLocalsRegion, int tlaSize) {
this.state = state;
teleRegisterSet.setInstructionPointer(instructionPointer);
if (isPrimordial && threadLocalsBlock.memoryRegion() == null) {
// primordial thread does have a thread locals block but not accessed in the usual way
Address tlBlock = vm().fields().MaxineVM_primordialTLBlock.readWord(vm()).asAddress();
if (tlBlock.isNotZero()) {
final String name = this.entityName + " Locals";
this.threadLocalsBlock = new TeleThreadLocalsBlock(this, name, tlBlock, vm().fields().MaxineVM_primordialTLBlockSize.readInt(vm()));
threadLocalsRegion = (TeleFixedMemoryRegion) threadLocalsBlock.memoryRegion();
}
}
threadLocalsBlock.updateAfterGather(threadLocalsRegion, tlaSize);
}
/**
* Marks the thread as having died in the process; flushes all state accordingly.
*/
final void setDead() {
vm().addressSpace().remove(teleStack.memoryRegion());
state = DEAD;
clearFrames();
breakpoint = null;
frameCache = null;
threadLocalsBlock.clear();
}
/**
* If this thread is currently at a {@linkplain #breakpoint() breakpoint} it is single stepped to the next
* instruction.
*/
void evadeBreakpoint() throws OSExecutionRequestException {
if (breakpoint != null && !breakpoint.isTransient()) {
assert !breakpoint.isActive() : "Cannot single step at an activated breakpoint";
Trace.line(TRACE_VALUE + 1, tracePrefix() + "single step to evade breakpoint=" + breakpoint);
teleProcess().singleStep(this, true);
}
}
/**
* Refreshes the information about the {@linkplain #breakpoint() breakpoint} this thread is currently stopped at (if
* any). If this thread is stopped at a breakpoint, its instruction pointer is adjusted so that it is at the
* instruction on which the breakpoint was set.
*/
private void refreshBreakpoint() {
VmTargetBreakpoint breakpoint = null;
try {
final Pointer breakpointAddress = breakpointAddressFromInstructionPointer();
RemoteCodePointer codePointer = null;
try {
codePointer = vm().machineCode().makeCodePointer(breakpointAddress);
} catch (InvalidCodeAddressException e) {
TeleWarning.message(tracePrefix() + "Invalid breakpoint address " + e.getAddressString() + ": " + e.getMessage());
}
if (codePointer != null) {
breakpoint = breakpointManager().targetBreakpoints().find(codePointer);
}
} catch (DataIOError dataIOError) {
// This is a catch for problems getting accurate state for threads that are not at breakpoints
}
if (breakpoint != null) {
Trace.line(TRACE_VALUE + 1, tracePrefix() + "refreshingBreakpoint (epoch=" + teleProcess().epoch() + ") for " + this);
state = BREAKPOINT;
this.breakpoint = breakpoint;
final Address address = this.breakpoint.codeLocation().address();
if (updateInstructionPointer(address)) {
teleRegisterSet.setInstructionPointer(address);
Trace.line(TRACE_VALUE + 1, tracePrefix() + "refreshingBreakpoint (epoch=" + teleProcess().epoch() + ") IP updated for " + this);
} else {
TeleError.unexpected("Error updating instruction pointer to adjust thread after breakpoint at " + address + " was hit: " + this);
}
} else {
this.breakpoint = null;
assert state != BREAKPOINT;
}
}
/**
* Clears the current list of frames.
*/
private synchronized void clearFrames() {
frames = Collections.emptyList();
framesLastChangedEpoch = teleProcess().epoch();
}
/**
* Specifies whether or not the instruction pointer needs to be adjusted when this thread hits a breakpoint to
* denote the instruction pointer for which the breakpoint was set. For example, on x86 architectures, the
* instruction pointer is at the instruction following the breakpoint instruction whereas on SPARC, it's
* at the instruction pointer for which the breakpoint was set.
*/
private boolean breakpointIsAtInstructionPointer;
/**
* Updates the current value of the instruction pointer for this thread.
*
* @param address the address to which the instruction should be set
* @return true if the instruction pointer was successfully updated, false otherwise
*/
protected abstract boolean updateInstructionPointer(Address address);
protected abstract boolean readRegisters(byte[] integerRegisters, byte[] floatingPointRegisters, byte[] stateRegisters);
/**
* Advances this thread to the next instruction. That is, makes this thread execute a single machine instruction.
* Note that this method does not block waiting for the tele process to complete the step.
*
* @return true if the single step was issued successfully, false otherwise
*/
protected abstract boolean singleStep();
protected abstract boolean threadResume();
protected abstract boolean threadSuspend();
/**
* Gets the address of the breakpoint instruction derived from the current instruction pointer. The current
* instruction pointer is assumed to be at the architecture dependent location immediately after a breakpoint
* instruction was executed.
*
* The implementation of this method in {@link TeleNativeThread} uses the convention for x86 architectures where the
* the instruction pointer is at the instruction following the breakpoint instruction.
*/
private Pointer breakpointAddressFromInstructionPointer() {
final Pointer instructionPointer = teleRegisterSet.instructionPointer();
if (breakpointIsAtInstructionPointer) {
return instructionPointer;
}
return instructionPointer.minus(breakpointManager().targetBreakpoints().codeSize());
}
@Override
public boolean equals(Object other) {
if (other instanceof TeleNativeThread) {
final TeleNativeThread teleNativeThread = (TeleNativeThread) other;
return localHandle() == teleNativeThread.localHandle() && id() == teleNativeThread.id();
}
return false;
}
@Override
public int hashCode() {
return (int) localHandle();
}
@Override
public final String toString() {
final StringBuilder sb = new StringBuilder(100);
sb.append(isPrimordial() ? "primordial" : (teleVmThread() == null ? "native" : teleVmThread().name()));
sb.append("[id=").append(id());
sb.append(",handle=").append(handleString());
sb.append(",local handle=").append(localHandle());
sb.append(",state=").append(state);
sb.append(",type=").append(isPrimordial() ? "primordial" : (isJava() ? "Java" : "native"));
if (isLive()) {
sb.append(",ip=0x").append(teleRegisterSet.instructionPointer().toHexString());
if (isJava()) {
sb.append(",stack_start=0x").append(stack().memoryRegion().start().toHexString());
sb.append(",stack_size=").append(stack().memoryRegion().nBytes());
if (vmLog != null && vmLog.memoryRegion() != null) {
sb.append(",vmlog_start=0x").append(vmLog.memoryRegion().start().toHexString());
sb.append(",vmlog_size=").append(vmLog.memoryRegion().nBytes());
}
}
}
sb.append("]");
return sb.toString();
}
/**
* Class representing a Java-level frame. It implements the interface the JDWP protocol is programmed against.
*/
private class FrameProviderImpl implements FrameProvider {
private CiCodePos codePos;
private ClassMethodActor classMethodActor;
private int position;
private StackFrame stackFrame;
private TeleTargetMethod targetMethod;
private long[] rawValues;
private VMValue[] vmValues;
private boolean isTopFrame;
public FrameProviderImpl(boolean isTopFrame, TeleTargetMethod targetMethod, StackFrame stackFrame, CiCodePos codePos) {
this(isTopFrame, targetMethod, stackFrame, codePos, (ClassMethodActor) codePos.method, 0); //descriptor.bytecodeLocation().());
}
public FrameProviderImpl(boolean isTopFrame, TeleTargetMethod targetMethod, StackFrame stackFrame, CiCodePos codePos, ClassMethodActor classMethodActor, int position) {
this.stackFrame = stackFrame;
this.codePos = codePos;
this.classMethodActor = classMethodActor;
this.position = position;
this.targetMethod = targetMethod;
this.isTopFrame = isTopFrame;
if (classMethodActor.codeAttribute().lineNumberTable().entries().length > 0) {
this.position = classMethodActor.codeAttribute().lineNumberTable().entries()[0].bci();
} else {
LOGGER.warning("No line number table information for method " + classMethodActor.name.toString());
this.position = -1;
}
}
private void initValues() {
final int length = classMethodActor.codeAttribute().localVariableTable().entries().length;
final long[] values = new long[length];
final VMValue[] vmValues = new VMValue[length];
for (LocalVariableTable.Entry entry : classMethodActor.codeAttribute().localVariableTable().entries()) {
final Value curValue = getValueImpl(entry.slot());
vmValues[entry.slot()] = vm().maxineValueToJDWPValue(curValue);
if (curValue.kind().isReference) {
values[entry.slot()] = curValue.asReference().toOrigin().toLong();
} else if (curValue.kind().isWord) {
values[entry.slot()] = curValue.asWord().asPointer().toLong();
}
}
this.vmValues = vmValues;
rawValues = values;
}
private Value getValueImpl(int slot) {
// TODO: Implement this!
return null;
}
public TargetMethodAccess getTargetMethodProvider() {
return targetMethod;
}
public JdwpCodeLocation getLocation() {
return vm().vmAccess().createCodeLocation(vm().findTeleMethodActor(TeleClassMethodActor.class, classMethodActor), position, false);
}
public long getInstructionPointer() {
// On top frame, the instruction pointer is incorrect, so take it from the thread!
if (isTopFrame) {
return teleRegisterSet.instructionPointer().asAddress().toLong();
}
return stackFrame.ip.asAddress().toLong();
}
public long getFramePointer() {
return stackFrame.fp.asAddress().toLong();
}
public long getStackPointer() {
return stackFrame.sp.asAddress().toLong();
}
public ThreadProvider getThread() {
return TeleNativeThread.this;
}
public VMValue getValue(int slot) {
if (vmValues == null) {
initValues();
}
return vmValues[slot];
}
public void setValue(int slot, VMValue value) {
final CiValue targetLocation = codePos instanceof CiFrame ? ((CiFrame) codePos).getLocalValue(slot) : null;
// TODO: Implement writing to stack frames.
LOGGER.warning("Stackframe write at " + slot + ", targetLocation=" + targetLocation + ", doing nothing");
}
public ObjectProvider thisObject() {
// TODO: Add a way to access the "this" object.
LOGGER.warning("Trying to access THIS object, returning null");
return null;
}
public long[] getRawValues() {
if (rawValues == null) {
initValues();
}
return rawValues;
}
}
public FrameProvider getFrame(int depth) {
return getFrames()[depth];
}
private List<StackFrame> oldFrames;
public synchronized FrameProvider[] getFrames() {
synchronized (teleProcess()) {
if (oldFrames != frames) {
oldFrames = frames;
} else {
return frameCache;
}
final List<FrameProvider> result = new LinkedList<FrameProvider>();
int z = 0;
for (final StackFrame stackFrame : frames(Integer.MAX_VALUE)) {
z++;
final Address address = stackFrame.ip;
TeleCompilation compilation = vm().machineCode().findCompilation(address);
if (compilation == null) {
if (stackFrame.targetMethod() == null) {
LOGGER.warning("Target method of stack frame (" + stackFrame + ") was null!");
continue;
}
final TargetMethod targetMethod = stackFrame.targetMethod();
final ClassMethodActor classMethodActor = targetMethod.classMethodActor();
final TeleClassMethodActor teleClassMethodActor = vm().findTeleMethodActor(TeleClassMethodActor.class, classMethodActor);
if (teleClassMethodActor == null) {
TeleWarning.message("Could not find tele class method actor for " + classMethodActor);
continue;
}
compilation = vm().machineCode().findCompilation(targetMethod.codeStart().toAddress());
if (compilation == null) {
TeleWarning.message("Could not find tele target method actor for " + classMethodActor);
continue;
}
}
LOGGER.info("Processing stackframe " + stackFrame);
int index = -1;
if (stackFrame.targetMethod() != null) {
index = stackFrame.targetMethod().findSafepointIndex(CodePointer.from(stackFrame.ip));
}
if (index != -1) {
final int stopIndex = index;
CiFrame frames = compilation.teleTargetMethod().getDebugInfoAtSafepointIndex(stopIndex).frame();
if (frames == null) {
LOGGER.info("WARNING: No Java frame descriptor found for Java stop " + stopIndex);
if (vm().findTeleMethodActor(TeleClassMethodActor.class, compilation.classMethodActor()) == null) {
LOGGER.warning("Could not find tele method!");
} else {
result.add(new FrameProviderImpl(z == 1, compilation.teleTargetMethod(), stackFrame, null, compilation.classMethodActor(), 0));
}
} else {
while (frames != null) {
final TeleClassMethodActor curTma = vm().findTeleMethodActor(TeleClassMethodActor.class, (ClassMethodActor) frames.method);
LOGGER.info("Found part frame " + frames + " tele method actor: " + curTma);
result.add(new FrameProviderImpl(z == 1, compilation.teleTargetMethod(), stackFrame, frames));
frames = frames.caller();
}
}
} else {
LOGGER.info("Not at Java stop!");
if (vm().findTeleMethodActor(TeleClassMethodActor.class, compilation.classMethodActor()) == null) {
LOGGER.warning("Could not find tele method!");
} else {
result.add(new FrameProviderImpl(z == 1, compilation.teleTargetMethod(), stackFrame, null, compilation.classMethodActor(), 0));
}
}
}
frameCache = result.toArray(new FrameProvider[result.size()]);
return frameCache;
}
}
public String getName() {
return toString();
}
public void interrupt() {
// TODO: Implement the possibility to interrupt threads.
LOGGER.warning("Thread " + this + " was asked to interrupt, doing nothing");
assert false : "Not implemented.";
}
public final void resume() {
if (suspendCount > 0) {
suspendCount--;
}
if (suspendCount == 0) {
LOGGER.info("Asked to RESUME THREAD " + this + " we are resuming silently the whole VM for now");
vm().vmAccess().resume();
}
}
public void stop(ObjectProvider exception) {
// TODO: Consider implementing stopping a thread by throwing an exception.
LOGGER.warning("A thread was asked to stop over JDWP with the exception " + exception + ", doing nothing.");
}
public final void suspend() {
suspendCount++;
}
public int suspendCount() {
// TODO: Implement the suspend count according to the JDWP rules. The current very simple implementation seems to work however fine with NetBeans.
if (teleProcess().processState() == ProcessState.STOPPED) {
return 1;
}
return suspendCount;
}
public ReferenceTypeProvider getReferenceType() {
return vm().vmAccess().getReferenceType(getClass());
}
public ThreadGroupProvider getThreadGroup() {
return isJava() ? vm().javaThreadGroupProvider() : vm().nativeThreadGroupProvider();
}
public void doSingleStep() {
LOGGER.info("Asked to do a single step!");
vm().registerSingleStepThread(this);
}
public void doStepOut() {
LOGGER.info("Asked to do a step out!");
vm().registerStepOutThread(this);
}
public VMAccess getVM() {
return vm().vmAccess();
}
public RegistersGroup getRegistersGroup() {
final Registers[] registers = new Registers[]{teleRegisterSet.teleIntegerRegisters().getRegisters("Integer Registers"),
teleRegisterSet.teleStateRegisters().getRegisters("State Registers"),
teleRegisterSet.teleFloatingPointRegisters().getRegisters("Floating Point Registers")};
return new RegistersGroup(registers);
}
}