/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm;
import org.jnode.annotation.Internal;
import org.jnode.annotation.KernelSpace;
import org.jnode.annotation.MagicPermission;
import org.jnode.vm.classmgr.VmCompiledCode;
import org.jnode.vm.classmgr.VmMethod;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.facade.VmUtils;
import org.jnode.vm.objects.VmSystemObject;
import org.jnode.vm.scheduler.VmThread;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
/**
* Abstract class for reading information from stack frames.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@MagicPermission
public abstract class VmStackReader extends VmSystemObject {
/**
* Gets the previous frame (if any)
*
* @param sf The stackframe to get the previous frame from.
* @return The previous frame or null.
*/
@KernelSpace
@Internal
public final Address getPrevious(Address sf) {
if (isValid(sf)) {
return sf.loadAddress(getPreviousOffset(sf));
} else {
return null;
}
}
/**
* Gets the method of a given stackframe.
*
* @param sf Stackframe pointer
* @return The method
*/
@KernelSpace
final VmMethod getMethod(Address sf) {
final int ccid = sf.loadInt(getMethodIdOffset(sf));
if (ccid == 0) {
return null;
} else {
final VmCompiledCode cc = VmUtils.getVm().getCompiledMethods().get(ccid);
if (cc == null) {
// (This can happen if an exception is thrown while a frame
// for a 'native' method call is on the stack. A panic is not a
// good idea. It is better to generate a stack trace with a missing
// method name.)
// Unsafe.die("Unknown ccid found on stack");
return null;
} else {
return cc.getMethod();
}
}
}
/**
* Gets the compiled code of a given stackframe.
*
* @param sf Stackframe pointer
* @return The compiled code
*/
final VmCompiledCode getCompiledCode(Address sf) {
final int ccid = sf.loadInt(getMethodIdOffset(sf));
if (ccid == 0) {
return null;
} else {
return VmUtils.getVm().getCompiledMethods().get(ccid);
}
}
/**
* Gets the return address of a given stackframe.
*
* @param sf Stackframe pointer
* @return The address
*/
@Internal
public final Address getReturnAddress(Address sf) {
return sf.loadAddress(getReturnAddressOffset(sf));
}
/**
* Is a given stackframe valid?
*
* @param sf
* @return boolean
*/
@KernelSpace
final boolean isValid(Address sf) {
if (sf == null) {
return false;
}
if (getMethod(sf) == null) {
return false;
}
return true;
}
/**
* Is the given frame a bottom of stack marker?
*
* @param sf
* @return boolean
*/
final boolean isStackBottom(Address sf) {
if (sf == null) {
return true;
}
return (getMethod(sf) == null)
&& (getPrevious(sf) == null);
}
/**
* Gets the stacktrace for a given current frame.
*
* @param frame The address of the current frame.
* @param ip The instruction pointer of the given frame
* @param limit Maximum length of returned array.
* @return VmStackFrame[]
*/
@Internal
public final VmStackFrame[] getVmStackTrace(Address frame, Address ip, int limit) {
final VmStackFrameEnumerator sfEnum = new VmStackFrameEnumerator(this, frame, ip);
int count = 0;
while (sfEnum.isValid() && (count < limit)) {
count++;
sfEnum.next();
}
// if ((f != null) && !isStackBottom(f) && (count < limit)) {
// Unsafe.debug("Corrupted stack!, st.length=");
// Unsafe.debug(count);
// Unsafe.debug(" f.magic=");
// //Unsafe.die();
// }
final VmStackFrame[] stack = new VmStackFrame[count];
sfEnum.reset(frame, ip);
for (int i = 0; i < count; i++) {
stack[i] = new VmStackFrame(sfEnum.getMethod(), sfEnum.getProgramCounter());
sfEnum.next();
}
return stack;
}
/**
* Count the number of stackframe from a given frame.
*
* @param sf
* @return int
*/
@Internal
public final int countStackFrames(Address sf) {
int count = 0;
while (isValid(sf)) {
count++;
sf = getPrevious(sf);
}
return count;
}
/**
* Show the current stacktrace using Screen.debug.
*/
@KernelSpace
public final void debugStackTrace() {
debugStackTrace(25);
}
/**
* Show the current stacktrace using Screen.debug.
*/
@KernelSpace
public final void debugStackTrace(int max) {
Address f = VmMagic.getCurrentFrame();
Unsafe.debug("\nDebug stacktrace: ");
boolean first = true;
while (isValid(f) && (max > 0)) {
if (first) {
first = false;
} else {
Unsafe.debug(", ");
}
final VmMethod method = getMethod(f);
final VmType<?> vmClass = method.getDeclaringClass();
Unsafe.debug(vmClass.getName());
Unsafe.debug("::");
Unsafe.debug(method.getName());
Unsafe.debug('\n');
f = getPrevious(f);
max--;
}
if (isValid(f)) {
Unsafe.debug("...");
}
}
/**
* Show the current stacktrace using Screen.debug.
* TODO that method only exist to have line numbers : find a way to add line numbers to debugStackTrace(max)
*/
@KernelSpace
public final void debugStackTraceWithLineNumbers(int max) {
final VmThread current = VmThread.currentThread();
final VmStackFrame[] frames = (VmStackFrame[]) VmThread.getStackTrace(current);
if (frames == null) {
Unsafe.debug("Debug stacktrace:<no stack trace>\n");
} else {
Unsafe.debug("Debug stacktrace: ");
for (VmStackFrame frame : frames) {
final VmStackFrame s = (VmStackFrame) frame;
Unsafe.debug(s.getMethod().getDeclaringClass().getName());
Unsafe.debug("::");
Unsafe.debug(s.getMethod().getName());
Unsafe.debug(":");
Unsafe.debug(s.getLocationInfo());
Unsafe.debug('\n');
}
}
}
/**
* Show the stacktrace of the given thread using Screen.debug.
*/
@KernelSpace
public final void debugStackTrace(VmThread thread) {
Address f = thread.getStackFrame();
Unsafe.debug("Debug stacktrace: ");
boolean first = true;
int max = 20;
while (isValid(f) && (max > 0)) {
if (first) {
first = false;
} else {
Unsafe.debug(", ");
}
final VmMethod method = getMethod(f);
final VmType vmClass = method.getDeclaringClass();
Unsafe.debug(vmClass.getName());
Unsafe.debug("::");
Unsafe.debug(method.getName());
f = getPrevious(f);
max--;
}
if (isValid(f)) {
Unsafe.debug("...");
}
}
/**
* Gets the offset within the frame of previous frame (if any)
*
* @param sf The stackframe to get the previous frame from.
* @return The previous frame or null.
*/
@KernelSpace
protected abstract Offset getPreviousOffset(Address sf);
/**
* Gets the offset within the stackframe of compiled method id.
*
* @param sf Stackframe pointer
* @return The method id offset
*/
@KernelSpace
protected abstract Offset getMethodIdOffset(Address sf);
/**
* Gets the offset within the stackframe of the return address.
*
* @param sf Stackframe pointer
* @return The return address offset
*/
@KernelSpace
protected abstract Offset getReturnAddressOffset(Address sf);
}