/*
* Copyright (c) 2010, 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.vm.compiler.target.Stub.Type.*;
import java.io.*;
import java.lang.management.*;
import java.util.*;
import com.sun.max.tele.*;
import com.sun.max.tele.memory.*;
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.compiler.target.*;
import com.sun.max.vm.stack.*;
/**
* Access to the state of a stack in the VM.
*/
public class TeleStack extends AbstractVmHolder implements MaxStack {
private static final int TRACE_LEVEL = 2;
/**
* Description of the memory region occupied by a {@linkplain MaxStack teleStack}.
* <br>
* This region has no parent; it is allocated from the OS.
* <br>
* This region's children are the individual {@linkplain MaxStackFrame teleStack frames}
* contained in the teleStack.
*/
private static final class StackMemoryRegion extends TeleFixedMemoryRegion implements MaxEntityMemoryRegion<MaxStack> {
private TeleStack teleStack;
private MaxVMState lastUpdatedState = null;
private StackMemoryRegion(MaxVM vm, TeleStack owner, String regionName, Address start, long nBytes) {
super(vm, regionName, start, nBytes);
this.teleStack = owner;
}
@Override
public MemoryUsage getUsage() {
if (start().isNotZero() && nBytes() != 0L) {
try {
return new MemoryUsage(-1L, end().minus(teleStack.thread().registers().stackPointer()).toLong(), nBytes(), -1L);
} catch (IllegalArgumentException e) {
}
}
return MaxMemoryRegion.Util.NULL_MEMORY_USAGE;
}
public MaxEntityMemoryRegion< ? extends MaxEntity> parent() {
// Stack memory is allocated from the OS, not part of any other region
return null;
}
public List<MaxEntityMemoryRegion< ? extends MaxEntity>> children() {
final List<MaxStackFrame> frames = teleStack.frames(Integer.MAX_VALUE);
final List<MaxEntityMemoryRegion<? extends MaxEntity>> regions =
new ArrayList<MaxEntityMemoryRegion<? extends MaxEntity>>(frames.size());
for (MaxStackFrame stackFrame : frames) {
regions.add(stackFrame.memoryRegion());
}
return Collections.unmodifiableList(regions);
}
public MaxStack owner() {
return teleStack;
}
}
private final TeleNativeThread teleNativeThread;
private final String entityDescription;
/**
* The region of VM memory occupied by this teleStack.
*/
private final StackMemoryRegion stackMemoryRegion;
/**
* Location of the caller return address relative to the saved location in a teleStack frame, usually 0 but see SPARC.
*/
private final int offsetToReturnPC;
/**
* VM state the last time we updated the frames.
*/
private volatile TeleVMState lastUpdatedState = null;
/**
* VM state the last time the teleStack changed "structurally".
*/
private volatile TeleVMState lastChangedState = null;
/**
* Most recently updated teleStack frames; may be empty, but non-null.
*/
private volatile List<MaxStackFrame> maxStackFrames = Collections.emptyList();
/**
* Creates an object that models a teleStack in the VM.
*
* @param teleNativeThread the thread that owns the teleStack
* @param name name of the stack
* @param start starting location in VM memory
* @param nBytes size of the stack region in bytes
*/
public TeleStack(TeleVM vm, TeleNativeThread teleNativeThread, String name, Address start, long nBytes) {
super(vm);
this.teleNativeThread = teleNativeThread;
this.entityDescription = "The stack owned by VM thread " + teleNativeThread.entityName();
this.stackMemoryRegion = new StackMemoryRegion(vm, this, name, start, nBytes);
this.offsetToReturnPC = platform().isa.offsetToReturnPC;
}
public String entityName() {
return stackMemoryRegion.regionName();
}
public String entityDescription() {
return entityDescription;
}
public MaxEntityMemoryRegion<MaxStack> memoryRegion() {
return stackMemoryRegion;
}
public boolean contains(Address address) {
return stackMemoryRegion.contains(address);
}
public TeleObject representation() {
// No distinguished object in VM runtime represents the stack.
return null;
}
public MaxThread thread() {
return teleNativeThread;
}
public MaxStackFrame top() {
if (vm().tryLock()) {
try {
return TeleStackFrame.createFrame(vm(), this, 0, teleNativeThread.top());
} finally {
vm().unlock();
}
}
return null;
}
public List<MaxStackFrame> frames(int maxDepth) {
final TeleVMState currentVmState = vm().state();
if (currentVmState.newerThan(lastUpdatedState) || teleNativeThread.framesMaxDepth() != maxDepth) {
if (vm().tryLock()) {
try {
final List<StackFrame> frames = teleNativeThread.frames(maxDepth);
final List<MaxStackFrame> maxStackFrames = new ArrayList<MaxStackFrame>(frames.size());
int position = 0;
for (StackFrame stackFrame : frames) {
maxStackFrames.add(TeleStackFrame.createFrame(vm(), this, position, stackFrame));
position++;
}
this.maxStackFrames = maxStackFrames;
lastUpdatedState = currentVmState;
final Long lastChangedEpoch = teleNativeThread.framesLastChangedEpoch();
lastChangedState = currentVmState;
while (lastChangedState.previous() != null && lastChangedEpoch <= lastChangedState.previous().epoch()) {
lastChangedState = (TeleVMState) lastChangedState.previous();
}
} finally {
vm().unlock();
}
}
}
return maxStackFrames;
}
public MaxStackFrame findStackFrame(Address address) {
for (MaxStackFrame stackFrame : frames(Integer.MAX_VALUE)) {
MaxEntityMemoryRegion<MaxStackFrame> memoryRegion = stackFrame.memoryRegion();
if (memoryRegion != null && memoryRegion.contains(address)) {
return stackFrame;
}
}
return null;
}
public TeleVMState lastUpdated() {
return lastUpdatedState;
}
public TeleVMState lastChanged() {
return lastChangedState;
}
public void writeSummary(PrintStream printStream) {
printStream.println("Stack frames :");
for (MaxStackFrame maxStackFrame : new ArrayList<MaxStackFrame>(frames(Integer.MAX_VALUE))) {
printStream.println(" " + maxStackFrame.toString());
}
}
public CodeLocation returnLocation() {
final StackFrame topFrameCaller = teleNativeThread.top().callerFrame();
if (topFrameCaller != null && topFrameCaller.ip.isNotZero()) {
final StackFrame callee = topFrameCaller.calleeFrame();
if (callee == null) {
// Top frame
try {
return codeLocations().createMachineCodeLocation(topFrameCaller.ip, "top teleStack frame IP");
} catch (InvalidCodeAddressException e) {
TeleWarning.message("Bad IP address " + e.getAddressString() + " for top frame in thread " + teleNativeThread.entityName() + ": " + e.getMessage());
}
} else {
// A call return: add a platform-specific offset from the stored code address to the actual call return site.
final TargetMethod calleeTargetMethod = callee.targetMethod();
if (calleeTargetMethod != null && calleeTargetMethod.is(TrapStub)) {
// Special case, where the IP caused a trap; no adjustment.
try {
return codeLocations().createMachineCodeLocation(topFrameCaller.ip, "teleStack frame return");
} catch (InvalidCodeAddressException e) {
TeleWarning.message("Bad IP address " + e.getAddressString() + " for trap stub frame in thread " + teleNativeThread.entityName() + ": " + e.getMessage());
}
} else {
// An ordinary call; apply a platform-specific adjustment to get the real return address.
try {
return codeLocations().createMachineCodeLocation(topFrameCaller.ip.plus(offsetToReturnPC), "teleStack frame return");
} catch (InvalidCodeAddressException e) {
TeleWarning.message("Bad IP address " + e.getAddressString() + " for frame in thread " + teleNativeThread.entityName() + ": " + e.getMessage());
}
}
}
}
return null;
}
}