/*
* 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.util.*;
import com.sun.max.tele.*;
import com.sun.max.tele.debug.TeleStackFrameWalker.TruncatedStackFrame;
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.*;
/**
* Hierarchy of classes that act as wrappers for VM {@linkplain StackFrame stack frames}, with additional
* contextual information added for the benefits of clients. The hierarchy also includes the subclasses
* {@link TruncatedFrame}, which is <em>synthetic</em>: it
* corresponds to no VM frame type, but is rather used to as a marker by the stack walker for
* communicating truncated stack walks.
*/
public abstract class TeleStackFrame<StackFrame_Type extends StackFrame> extends AbstractVmHolder implements MaxStackFrame {
private static final int TRACE_LEVEL = 2;
/**
* Description of the memory region occupied by a {@linkplain MaxStackFrame stack frame} in the VM.
* <br>
* The parent of this region is the {@linkplain MaxStack stack} in which it is contained.
* <br>
* This region has no children (although it could if we chose to decompose it into slots).
*/
private static final class StackFrameMemoryRegion extends TeleFixedMemoryRegion implements MaxEntityMemoryRegion<MaxStackFrame> {
private static final List<MaxEntityMemoryRegion< ? extends MaxEntity>> EMPTY = Collections.emptyList();
private final TeleStackFrame teleStackFrame;
private StackFrameMemoryRegion(MaxVM vm, TeleStackFrame teleStackFrame, String regionName, Address start, long nBytes) {
super(vm, regionName, start, nBytes);
this.teleStackFrame = teleStackFrame;
}
public MaxEntityMemoryRegion< ? extends MaxEntity> parent() {
return teleStackFrame.stack().memoryRegion();
}
public List<MaxEntityMemoryRegion< ? extends MaxEntity>> children() {
return EMPTY;
}
public MaxStackFrame owner() {
return teleStackFrame;
}
}
/**
* Factory method for wrapping VM (and synthetic) stack frames with additional information, in a type
* hierarchy that partially mirrors the types of frames.
*
* @param vm the VM
* @param teleStack the stack containing the frame
* @param position the position in the stack of the frame; position 0 is the top
* @param stackFrame the frame to be wrapped
* @return a newly created instance of {@link TeleStackFrame}
*/
public static TeleStackFrame createFrame(TeleVM vm, TeleStack teleStack, int position, StackFrame stackFrame) {
if (stackFrame instanceof VMStackFrame) {
final VMStackFrame compiledStackFrame = (VMStackFrame) stackFrame;
return new TeleVMFrame(vm, teleStack, position, compiledStackFrame);
}
if (stackFrame instanceof NativeStackFrame) {
final NativeStackFrame nativeStackFrame = (NativeStackFrame) stackFrame;
return new NativeFrame(vm, teleStack, position, nativeStackFrame);
}
if (stackFrame instanceof TruncatedStackFrame) {
final TruncatedStackFrame errorStackFrame = (TruncatedStackFrame) stackFrame;
return new TruncatedFrame(vm, teleStack, position, errorStackFrame);
}
TeleError.unexpected("Unknown stack frame kind");
return null;
}
protected final StackFrame_Type stackFrame;
private final TeleStack teleStack;
private final int position;
private final CodeLocation codeLocation;
protected TeleStackFrame(TeleVM vm, TeleStack teleStack, int position, StackFrame_Type stackFrame) {
super(vm);
this.stackFrame = stackFrame;
this.teleStack = teleStack;
this.position = position;
CodeLocation location = null;
if (stackFrame.ip.isNotZero()) {
final StackFrame callee = stackFrame.calleeFrame();
if (callee == null) {
// Top frame
try {
location = vm.codeLocations().createMachineCodeLocation(stackFrame.ip, "top stack frame IP");
} catch (InvalidCodeAddressException e) {
TeleWarning.message("Bad IP address " + e.getAddressString() + " for top frame in thread " + teleStack.thread().entityName() + ": " + e.getMessage());
}
} else {
// Call frame: records the return location, the next to be executed upon 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 {
location = vm.codeLocations().createMachineCodeLocation(stackFrame.ip, "stack frame return");
} catch (InvalidCodeAddressException e) {
TeleWarning.message("Bad IP address " + e.getAddressString() + " for trap stub frame in thread " + teleStack.thread().entityName() + ": " + e.getMessage());
}
} else {
// Ordinary call: apply a platform-specific adjustment to get the real return address.
final int offsetToReturnPC = platform().isa.offsetToReturnPC;
try {
location = vm.codeLocations().createMachineCodeLocation(stackFrame.ip.plus(offsetToReturnPC), "stack frame return");
} catch (InvalidCodeAddressException e) {
TeleWarning.message("Bad IP address " + e.getAddressString() + " for call frame in thread " + teleStack.thread().entityName() + ": " + e.getMessage());
}
}
}
}
this.codeLocation = location;
}
public final TeleObject representation() {
// No distinguished object in VM runtime represents a stack frame.
return null;
}
public final TeleStack stack() {
return teleStack;
}
public final int position() {
return position;
}
public final boolean isTop() {
return position == 0;
}
public Pointer ip() {
return stackFrame.ip;
}
public Pointer sp() {
return stackFrame.sp;
}
public Pointer fp() {
return stackFrame.fp;
}
public final MaxCodeLocation codeLocation() {
return codeLocation;
}
public boolean isSameFrame(MaxStackFrame maxStackFrame) {
if (maxStackFrame instanceof TeleStackFrame) {
// By default, delegate definition of "same" to the wrapped frames.
final TeleStackFrame otherStackFrame = (TeleStackFrame) maxStackFrame;
return this.stackFrame.isSameFrame(otherStackFrame.stackFrame);
}
return false;
}
@Override
public String toString() {
return Integer.toString(position) + ": " + entityName();
}
static final class TeleVMFrame extends TeleStackFrame<VMStackFrame> implements MaxStackFrame.Compiled {
private final StackFrameMemoryRegion stackFrameMemoryRegion;
private final String entityDescription;
private TeleCompilation teleCompiledCode;
protected TeleVMFrame(TeleVM vm, TeleStack teleStack, int position, VMStackFrame compiledStackFrame) {
super(vm, teleStack, position, compiledStackFrame);
final String description = teleStack.thread().entityName() + " frame(" + position() + ")";
this.stackFrameMemoryRegion = new StackFrameMemoryRegion(vm, this, description, stackFrame.slotBase(), layout().maximumSlotOffset());
this.entityDescription = "Stack frame " + position() + " in the " + vm().entityName() + " for " + teleStack.thread().entityName();
this.teleCompiledCode = vm.machineCode().findCompilation(stackFrame.ip);
}
public String entityName() {
return "<" + stackFrame.getClass().getSimpleName() + "> " + stackFrame.toString();
}
public String entityDescription() {
return entityDescription;
}
public MaxEntityMemoryRegion<MaxStackFrame> memoryRegion() {
return stackFrameMemoryRegion;
}
public boolean contains(Address address) {
return stackFrameMemoryRegion.contains(address);
}
public MaxMachineCodeRoutine machineCode() {
return compilation();
}
public TeleCompilation compilation() {
if (teleCompiledCode == null) {
teleCompiledCode = vm().machineCode().findCompilation(stackFrame.ip);
}
return teleCompiledCode;
}
public VMFrameLayout layout() {
return stackFrame.layout;
}
public Pointer slotBase() {
return stackFrame.slotBase();
}
public int biasedFPOffset(int offset) {
return stackFrame.biasedFPOffset(Offset.fromInt(offset)).toInt();
}
public StackBias bias() {
return stackFrame.bias();
}
public String sourceVariableName(int slot) {
final TeleCompilation compilation = compilation();
return compilation == null ? null : compilation.sourceVariableName(this, slot);
}
}
static final class NativeFrame extends TeleStackFrame<NativeStackFrame> implements MaxStackFrame.Native {
private TeleNativeFunction nativeFunction = null;
private NativeFrame(TeleVM vm, TeleStack teleStack, int position, NativeStackFrame nativeStackFrame) {
super(vm, teleStack, position, nativeStackFrame);
}
public String entityName() {
return "Frame: " + stackFrame.toString();
}
public String entityDescription() {
return "A stack frame discovered running unknown native code together with the " + vm().entityName();
}
public MaxEntityMemoryRegion<MaxStackFrame> memoryRegion() {
// Don't know enough
return null;
}
public boolean contains(Address address) {
return false;
}
public TeleCompilation compilation() {
return null;
}
@Override
public MaxMachineCodeRoutine machineCode() {
if (nativeFunction == null) {
nativeFunction = vm().machineCode().findNativeFunction(stackFrame.ip);
}
return nativeFunction;
}
}
static final class TruncatedFrame extends TeleStackFrame<TruncatedStackFrame> implements MaxStackFrame.Truncated {
private TruncatedFrame(TeleVM vm, TeleStack teleStack, int position, TruncatedStackFrame pseudoStackFrame) {
super(vm, teleStack, position, pseudoStackFrame);
}
public String entityName() {
return stackFrame.toString();
}
public String entityDescription() {
return "A frame denoting a truncated stack walk the " + vm().entityName();
}
public MaxEntityMemoryRegion<MaxStackFrame> memoryRegion() {
return null;
}
public boolean contains(Address address) {
return false;
}
@Override
public Pointer ip() {
return Pointer.zero();
}
@Override
public Pointer sp() {
return Pointer.zero();
}
@Override
public Pointer fp() {
return Pointer.zero();
}
public MaxMachineCodeRoutine machineCode() {
return null;
}
public TeleCompilation compilation() {
return null;
}
public Throwable error() {
return stackFrame.error;
}
public int omitted() {
return stackFrame.omitted;
}
}
}