This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
RFC: stack trace info for interpreter frames
- From: Tom Tromey <tromey at redhat dot com>
- To: Java Patch List <java-patches at gcc dot gnu dot org>
- Date: 28 Aug 2002 14:20:09 -0600
- Subject: RFC: stack trace info for interpreter frames
- Reply-to: tromey at redhat dot com
This patch changes libgcj so that it prints info about the interpreted
function when an interpreted frame is found in a stack trace.
It works by keeping a chain of stack-allocated objects describing the
interpreted frames. Then for any frame found to be in the interpreter
we use this information to find the actual underlying method and print
it instead of `_Jv_InterpMethod::run'.
At this point I'm asking for opinions on a few topics. The code isn't
quite ready to go in (for one thing it doesn't handle the case where
there is no interpreter).
* Should I add a parallel array for the stack trace instead of what is
done now (allocating one array twice as long and then using the
second half for interpreter info)?
* Should the interpreter method info chain be kept somewhere other
than Thread? (I chose Thread since it is already conveniently
per-thread.)
* Is the technique of putting dummy functions before and after the
interpreter function portable enough? It works ok for me on my
Linux box, but that's hardly definitive. (If not this approach,
then what?)
* Anything else anyone cares to critique.
Tom
Index: ChangeLog
from Tom Tromey <tromey@redhat.com>
* gnu/gcj/runtime/natNameFinder.cc: Include StringBuffer.h,
java-interp.h.
(lookupInterp): New method.
* gnu/gcj/runtime/NameFinder.java (lookup): Added length
argument. Try to look up interpreted frame.
(lookupInterp): Declare.
* java/lang/natVMThrowable.cc: Include Thread.h, java-interp.h.
(fillInStackTrace): Collect information on interpreted frames.
* interpret.cc: Include Thread.h.
(run): Create and push _Jv_MethodChain object.
(_Jv_EndOfInterpreter): New global.
* java/lang/Thread.java (interp_frame): New field.
* include/java-interp.h (struct _Jv_MethodChain): New structure.
Include NameFinder.h.
Index: interpret.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/interpret.cc,v
retrieving revision 1.33
diff -u -r1.33 interpret.cc
--- interpret.cc 24 Jun 2002 20:38:45 -0000 1.33
+++ interpret.cc 28 Aug 2002 20:11:12 -0000
@@ -22,7 +22,6 @@
#include <jvm.h>
#include <java-cpool.h>
#include <java-interp.h>
-// #include <java/lang/fdlibm.h>
#include <java/lang/System.h>
#include <java/lang/String.h>
#include <java/lang/Integer.h>
@@ -36,6 +35,7 @@
#include <java/lang/NullPointerException.h>
#include <java/lang/ArithmeticException.h>
#include <java/lang/IncompatibleClassChangeError.h>
+#include <java/lang/Thread.h>
#include <java-insns.h>
#include <java-signal.h>
@@ -745,10 +745,20 @@
#endif /* DIRECT_THREADED */
void
+_Jv_StartOfInterpreter (void)
+{
+}
+
+void
_Jv_InterpMethod::run (void *retp, ffi_raw *args)
{
using namespace java::lang::reflect;
+ // Indicate that we're the top-most interpreter frame.
+ java::lang::Thread *thread = java::lang::Thread::currentThread();
+ _Jv_MethodChain frame_desc (this,
+ (_Jv_MethodChain **) &thread->interp_frame);
+
_Jv_word stack[max_stack];
_Jv_word *sp = stack;
@@ -3167,6 +3177,11 @@
// No handler, so re-throw.
throw ex;
}
+}
+
+void
+_Jv_EndOfInterpreter (void)
+{
}
static void
Index: include/java-interp.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/include/java-interp.h,v
retrieving revision 1.17
diff -u -r1.17 java-interp.h
--- include/java-interp.h 24 Jun 2002 20:38:46 -0000 1.17
+++ include/java-interp.h 28 Aug 2002 20:11:13 -0000
@@ -13,6 +13,7 @@
#include <jvm.h>
#include <java-cpool.h>
+#include <gnu/gcj/runtime/NameFinder.h>
#ifdef INTERPRETER
@@ -138,6 +139,7 @@
friend class _Jv_ClassReader;
friend class _Jv_BytecodeVerifier;
+ friend class gnu::gcj::runtime::NameFinder;
friend void _Jv_PrepareClass(jclass);
};
@@ -202,6 +204,28 @@
void set_function (void *f)
{
function = f;
+ }
+};
+
+// A structure of this type is used to link together interpreter
+// invocations on the stack.
+struct _Jv_MethodChain
+{
+ const _Jv_InterpMethod *self;
+ _Jv_MethodChain **ptr;
+ _Jv_MethodChain *next;
+
+ _Jv_MethodChain (const _Jv_InterpMethod *s, _Jv_MethodChain **n)
+ {
+ self = s;
+ ptr = n;
+ next = *n;
+ *n = this;
+ }
+
+ ~_Jv_MethodChain ()
+ {
+ *ptr = next;
}
};
Index: gnu/gcj/runtime/NameFinder.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/gcj/runtime/NameFinder.java,v
retrieving revision 1.1
diff -u -r1.1 NameFinder.java
--- gnu/gcj/runtime/NameFinder.java 24 Aug 2002 22:46:19 -0000 1.1
+++ gnu/gcj/runtime/NameFinder.java 28 Aug 2002 20:11:13 -0000
@@ -173,17 +173,27 @@
native private String getAddrAsString(RawData addrs, int n);
/**
+ * If nth element of stack is an interpreted frame, return the
+ * element representing the method being interpreted.
+ */
+ native private StackTraceElement lookupInterp(RawData addrs, int n,
+ int lenth);
+
+ /**
* Creates the nth StackTraceElement from the given native stacktrace.
*/
- private StackTraceElement lookup(RawData addrs, int n)
+ private StackTraceElement lookup(RawData addrs, int n, int length)
{
StackTraceElement result;
- result = dladdrLookup(addrs, n);
+ result = lookupInterp(addrs, n, length);
+ if (result == null)
+ result = dladdrLookup(addrs, n);
if (result == null)
{
String name = null;
String file = null;
+
String hex = getAddrAsString(addrs, n);
if (addr2line != null)
@@ -216,7 +226,7 @@
{
StackTraceElement[] elements = new StackTraceElement[length];
for (int i=0; i < length; i++)
- elements[i] = lookup(addrs, i);
+ elements[i] = lookup(addrs, i, length);
if (demangle && sanitize)
return sanitizeStack(elements, t);
Index: gnu/gcj/runtime/natNameFinder.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/gcj/runtime/natNameFinder.cc,v
retrieving revision 1.1
diff -u -r1.1 natNameFinder.cc
--- gnu/gcj/runtime/natNameFinder.cc 24 Aug 2002 22:46:19 -0000 1.1
+++ gnu/gcj/runtime/natNameFinder.cc 28 Aug 2002 20:11:13 -0000
@@ -1,4 +1,4 @@
-// natNameFinder.cc - native helper methods for NameFiner.java
+// natNameFinder.cc - native helper methods for NameFinder.java
/* Copyright (C) 2002 Free Software Foundation, Inc
@@ -19,6 +19,8 @@
#include <jvm.h>
#include <java/lang/String.h>
#include <java/lang/StackTraceElement.h>
+#include <java/lang/StringBuffer.h>
+#include <java-interp.h>
#include <gnu/gcj/runtime/NameFinder.h>
@@ -29,7 +31,7 @@
java::lang::String*
gnu::gcj::runtime::NameFinder::getExecutable (void)
{
- return JvNewStringLatin1 (_Jv_ThisExecutable ());
+ return JvNewStringLatin1 (_Jv_ThisExecutable ());
}
java::lang::String*
@@ -81,4 +83,26 @@
}
#endif
return NULL;
+}
+
+java::lang::StackTraceElement *
+gnu::gcj::runtime::NameFinder::lookupInterp(RawData* addrs, jint n, jint len)
+{
+ void **stack = (void **) addrs;
+ if (stack[n + len] == NULL)
+ return NULL;
+
+ _Jv_InterpMethod *meth
+ = reinterpret_cast<_Jv_InterpMethod *> (stack[n + len]);
+ // FIXME: demangle.
+ java::lang::StringBuffer *sb = new java::lang::StringBuffer();
+ sb->append(_Jv_NewStringUtf8Const(meth->self->name));
+ sb->append(_Jv_NewStringUtf8Const(meth->self->signature));
+ // FIXME: source file name and line number can be found from
+ // bytecode debug information. But currently we don't keep that
+ // around.
+ // FIXME: is using the defining class correct here?
+ return new java::lang::StackTraceElement(NULL, -1,
+ meth->defining_class->getName(),
+ sb->toString(), false);
}
Index: java/lang/Thread.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/Thread.java,v
retrieving revision 1.22
diff -u -r1.22 Thread.java
--- java/lang/Thread.java 18 Jun 2002 15:39:38 -0000 1.22
+++ java/lang/Thread.java 28 Aug 2002 20:11:13 -0000
@@ -10,6 +10,8 @@
package java.lang;
+import gnu.gcj.RawData;
+
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date August 24, 1998
@@ -310,6 +312,9 @@
private boolean alive_flag;
private boolean startable_flag;
private ClassLoader context_class_loader;
+
+ // This describes the top-most interpreter frame for this thread.
+ RawData interp_frame;
// Our native data - points to an instance of struct natThread.
private Object data;
Index: java/lang/natVMThrowable.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natVMThrowable.cc,v
retrieving revision 1.1
diff -u -r1.1 natVMThrowable.cc
--- java/lang/natVMThrowable.cc 24 Aug 2002 22:46:19 -0000 1.1
+++ java/lang/natVMThrowable.cc 28 Aug 2002 20:11:13 -0000
@@ -26,6 +26,8 @@
#include <java-threads.h>
#include <java/lang/Throwable.h>
#include <java/lang/VMThrowable.h>
+#include <java/lang/Thread.h>
+#include <java-interp.h>
#include <sys/types.h>
@@ -57,10 +59,26 @@
void **addrs;
if (n > 0)
{
+ extern void _Jv_StartOfInterpreter (void);
+ extern void _Jv_EndOfInterpreter (void);
+
+ java::lang::Thread *thread = java::lang::Thread::currentThread();
+ _Jv_MethodChain *interp_frame
+ = reinterpret_cast<_Jv_MethodChain *> (thread->interp_frame);
state->length = n;
- addrs = (void **) _Jv_Malloc (n * sizeof p[0]);
+ int len = n;
+ addrs = (void **) _Jv_Malloc (2 * n * sizeof p[0]);
while (n--)
- addrs[n] = p[n];
+ {
+ addrs[n] = p[n];
+ if (p[n] >= &_Jv_StartOfInterpreter && p[n] <= &_Jv_EndOfInterpreter)
+ {
+ addrs[len + n] = (void *) interp_frame->self;
+ interp_frame = interp_frame->next;
+ }
+ else
+ addrs[len + n] = 0;
+ }
}
else
addrs = NULL;