This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
[RFA] JVMTI Stack Tracing
- From: Kyle Galloway <kgallowa at redhat dot com>
- To: Java Patches <java-patches at gcc dot gnu dot org>
- Date: Thu, 25 Jan 2007 10:20:24 -0500
- Subject: [RFA] JVMTI Stack Tracing
This patch implments the JVMTI methods GetStackTrace and GetFrameCount.
It is fairly complicated, so it warrants some explanation.
This method of tracing the java stack relies on a frame list that is
created as methods are called. Is extends the interpreted frame list
already used, and creates a composite stack of all interpreted and JNI
methods in the stack. To get a stack trace, JVMTI runs through this
composite stack counting up the frames and extracting their info.
_Jv_NativeFrame class is mostly empty, but could be expanded to contain
more information if desired. I have also maintained compatability with
the current exception stack tracing mechanism by leaving the purely
interpreted list intact. I added a second variable to Thread.java
called frame (as opposed to interp_frame) which is the first frame in
the composite list.
This method, however, has some limitations, most notably CNI. Since CNI
is not called through the VM in the way that JNI is, CNI methods will
not show up in this stack trace. As I understand it, however, this is
not a problem since CNI cannot be used when running interpreted, but I
may be wrong on this point.
I have included a test case for the jvmti-interp.exp file I added
yesterday, which shows GetStackTrace in action.
What does everyone think? I am very open to suggestions, but given the
nature of CNI and our current goals of interpreted only debugging this
seems like a reasonable solution.
Thanks,
Kyle
2007-01-24 Kyle Galloway <kgallowa@redhat.com>
* include/java-interp.h: Added _Jv_Frame class and its two
subclasses _Jv_InterpFrame and _Jv_NativeFrame. Also moved
_Jv_FrameType from java-stack.h.
* include/java-stack.h: Removed _Jv_FrameType.
* java/lang/Thread.java: Added frame member to hold new
composite frame stack.
* java/lang/Thread.h: Regenerated.
* java/lang/Thread.class: Rebuilt.
* jni.cc (_Jv_JNIMethod::call): Push a frame onto the stack when
calling a JNI method.
* jvmti.cc (_Jv_JVMTI_GetStackTrace): New Method.
(_Jv_JVMTI_GetFrameCount): New method.
* stacktrace.cc (UnwindTraceFn): Modified to use new _Jv_Frame
classes.
* testsuite/libjava.jvmti/interp/getstacktrace.jar: New test.
* testsuite/libjava.jvmti/interp/natgetstacktrace.cc: New test.
* testsuite/libjava.jvmti/interp/getstacktrace.h: New test.
* testsuite/libjava.jvmti/interp/getstacktrace.jar: New test.
* testsuite/libjava.jvmti/interp/getstacktrace.out: Output file
for test.
Index: libjava/stacktrace.cc
===================================================================
--- libjava/stacktrace.cc (revision 121173)
+++ libjava/stacktrace.cc (working copy)
@@ -131,9 +131,11 @@
if (func_addr == UNWRAP_FUNCTION_DESCRIPTOR (interp_run))
{
state->frames[pos].type = frame_interpreter;
- state->frames[pos].interp.meth = state->interp_frame->self;
+ _Jv_Frame *frame = static_cast<_Jv_Frame *> (state->interp_frame);
+ state->frames[pos].interp.meth
+ = static_cast<_Jv_InterpMethod *> (frame->self);
state->frames[pos].interp.pc = state->interp_frame->pc;
- state->interp_frame = state->interp_frame->next;
+ state->interp_frame = state->interp_frame->next_interp;
}
else
#endif
@@ -143,7 +145,7 @@
state->frames[pos].type = frame_proxy;
state->frames[pos].proxyClass = state->interp_frame->proxyClass;
state->frames[pos].proxyMethod = state->interp_frame->proxyMethod;
- state->interp_frame = state->interp_frame->next;
+ state->interp_frame = state->interp_frame->next_interp;
}
else
{
Index: libjava/classpath/lib/java/lang/Thread.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: libjava/classpath/lib/java/lang/Thread$State.class
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: libjava/include/java-interp.h
===================================================================
--- libjava/include/java-interp.h (revision 121173)
+++ libjava/include/java-interp.h (working copy)
@@ -205,11 +205,11 @@
// number info is unavailable.
int get_source_line(pc_t mpc);
+ public:
+
// Convenience function for indexing bytecode PC/insn slots in
// line tables for JDWP
jlong insn_index (pc_t pc);
-
- public:
/* Get the line table for this method.
* start is the lowest index in the method
@@ -315,38 +315,89 @@
}
};
-// The interpreted call stack, represented by a linked list of frames.
-struct _Jv_InterpFrame
+enum _Jv_FrameType
{
+ frame_native,
+ frame_interpreter,
+ frame_proxy
+};
+
+// The composite call stack as represented by a linked list of frames
+class _Jv_Frame
+{
+public:
+ java::lang::Thread *thread;
+
union
{
+ _Jv_MethodBase *self;
void *meth;
- _Jv_InterpMethod *self;
_Jv_Method *proxyMethod;
};
- java::lang::Thread *thread;
- _Jv_InterpFrame *next;
+
+ //The full list of frames, JNI and interpreted
+ _Jv_Frame *next;
+ _Jv_FrameType frame_type;
+
+ _Jv_Frame (_Jv_MethodBase *s, java::lang::Thread *thr, _Jv_FrameType type)
+ {
+ self = s;
+ frame_type = type;
+ next = (_Jv_Frame *) thr->frame;
+ thr->frame = (gnu::gcj::RawData *) this;
+ thread = thr;
+ }
+
+ ~_Jv_Frame ()
+ {
+ thread->frame = (gnu::gcj::RawData *) next;
+ }
+};
+
+// An interpreted frame in the call stack
+class _Jv_InterpFrame : public _Jv_Frame
+{
+public:
+
+ // Keep the purely interpreted list around so as not to break backtraces
+ _Jv_InterpFrame *next_interp;
+
union
{
pc_t pc;
jclass proxyClass;
};
-
- _Jv_InterpFrame (void *meth, java::lang::Thread *thr, jclass proxyClass = NULL)
+
+ //Debug info for local variables.
+ _Jv_word *locals;
+ char *locals_type;
+
+ _Jv_InterpFrame (void *meth, java::lang::Thread *thr, jclass proxyCls = NULL)
+ : _Jv_Frame (reinterpret_cast<_Jv_MethodBase *> (meth), thr,
+ frame_interpreter)
{
- this->meth = meth;
- thread = thr;
- next = (_Jv_InterpFrame *) thr->interp_frame;
+ next_interp = (_Jv_InterpFrame *) thr->interp_frame;
+ proxyClass = proxyCls;
thr->interp_frame = (gnu::gcj::RawData *) this;
- this->proxyClass = proxyClass;
}
~_Jv_InterpFrame ()
{
- thread->interp_frame = (gnu::gcj::RawData *) next;
+ thread->interp_frame = (gnu::gcj::RawData *) next_interp;
}
};
+// A native frame in the call stack really just a placeholder
+class _Jv_NativeFrame : public _Jv_Frame
+{
+public:
+
+ _Jv_NativeFrame (_Jv_JNIMethod *s, java::lang::Thread *thr)
+ : _Jv_Frame (s, thr, frame_native)
+ {
+ }
+};
+
#endif /* INTERPRETER */
#endif /* __JAVA_INTERP_H__ */
Index: libjava/include/java-stack.h
===================================================================
--- libjava/include/java-stack.h (revision 121173)
+++ libjava/include/java-stack.h (working copy)
@@ -41,13 +41,6 @@
}
}
-enum _Jv_FrameType
-{
- frame_native,
- frame_interpreter,
- frame_proxy
-};
-
#ifdef INTERPRETER
struct _Jv_InterpFrameInfo
{
Index: libjava/testsuite/libjava.jvmti/interp/getstacktrace.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: libjava/testsuite/libjava.jvmti/interp/getstacktrace.jar
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Index: libjava/testsuite/libjava.jvmti/interp/getstacktrace.java
===================================================================
--- libjava/testsuite/libjava.jvmti/interp/getstacktrace.java (revision 0)
+++ libjava/testsuite/libjava.jvmti/interp/getstacktrace.java (revision 0)
@@ -0,0 +1,88 @@
+public class getstacktrace
+ extends Thread
+{
+ public boolean done = false;
+
+ // num_frames is the number of frames > the original run () call so if
+ // num_frames = 1, the thread will have 2 frames, the original Thread.run
+ // call, plus one additional
+ public int num_frames, thread_num;
+
+ public static int num_threads = 1;
+
+ static
+ {
+ System.loadLibrary("natgetstacktrace");
+ }
+
+ public void run ()
+ {
+ thread_num = num_threads++;
+ num_frames = thread_num;
+
+ if (num_frames <= 1)
+ {
+ natRunner ();
+ }
+ else
+ {
+ if (thread_num % 2 == 0)
+ natPlaceholder ();
+ else
+ placeholder ();
+ }
+ }
+
+ public void placeholder ()
+ {
+ num_frames--;
+ if (num_frames <= 1)
+ {
+ if (thread_num % 2 == 1)
+ natRunner ();
+ else
+ runner ();
+ }
+ else
+ {
+ if (thread_num % 2 == 0)
+ natPlaceholder ();
+ else
+ placeholder ();
+ }
+ }
+
+ public void runner ()
+ {
+ done = true;
+ while (done)
+ yield ();
+ }
+
+ public native void natPlaceholder ();
+ public native void natRunner ();
+
+ public static native int do_getstacktrace_tests (Thread[] threads);
+
+ public static void main (String[] args)
+ {
+ System.out.println ("JVMTI GetStackTrace Interpreted Test");
+
+ getstacktrace[] threads = new getstacktrace[10];
+
+ for (int i = 0; i < threads.length; i++)
+ {
+ threads[i] = new getstacktrace ();
+ threads[i].start ();
+ while (!threads[i].done)
+ yield ();
+ }
+
+ do_getstacktrace_tests (threads);
+
+ for (int i = 0; i < threads.length; i++)
+ {
+ threads[i].done = false;
+ }
+ }
+}
Index: libjava/testsuite/libjava.jvmti/interp/getstacktrace.h
===================================================================
--- libjava/testsuite/libjava.jvmti/interp/getstacktrace.h (revision 0)
+++ libjava/testsuite/libjava.jvmti/interp/getstacktrace.h (revision 0)
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+
+#ifndef __getstacktrace__
+#define __getstacktrace__
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+JNIEXPORT void JNICALL Java_getstacktrace_natPlaceholder (JNIEnv *env, jobject);
+JNIEXPORT void JNICALL Java_getstacktrace_natRunner (JNIEnv *env, jobject);
+JNIEXPORT jint JNICALL Java_getstacktrace_do_1getstacktrace_1tests (JNIEnv *env, jclass, jobjectArray);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __getstacktrace__ */
Index: libjava/testsuite/libjava.jvmti/interp/getstacktrace.out
===================================================================
--- libjava/testsuite/libjava.jvmti/interp/getstacktrace.out (revision 0)
+++ libjava/testsuite/libjava.jvmti/interp/getstacktrace.out (revision 0)
@@ -0,0 +1,76 @@
+JVMTI GetStackTrace Interpreted Test
+Thread has 2 frames
+Frame 0 is native
+Frame 1 is interpreted
+Thread has 3 frames
+Frame 0 is interpreted
+Frame 1 is native
+Frame 2 is interpreted
+Thread has 4 frames
+Frame 0 is native
+Frame 1 is interpreted
+Frame 2 is interpreted
+Frame 3 is interpreted
+Thread has 5 frames
+Frame 0 is interpreted
+Frame 1 is native
+Frame 2 is native
+Frame 3 is native
+Frame 4 is interpreted
+Thread has 6 frames
+Frame 0 is native
+Frame 1 is interpreted
+Frame 2 is interpreted
+Frame 3 is interpreted
+Frame 4 is interpreted
+Frame 5 is interpreted
+Thread has 7 frames
+Frame 0 is interpreted
+Frame 1 is native
+Frame 2 is native
+Frame 3 is native
+Frame 4 is native
+Frame 5 is native
+Frame 6 is interpreted
+Thread has 8 frames
+Frame 0 is native
+Frame 1 is interpreted
+Frame 2 is interpreted
+Frame 3 is interpreted
+Frame 4 is interpreted
+Frame 5 is interpreted
+Frame 6 is interpreted
+Frame 7 is interpreted
+Thread has 9 frames
+Frame 0 is interpreted
+Frame 1 is native
+Frame 2 is native
+Frame 3 is native
+Frame 4 is native
+Frame 5 is native
+Frame 6 is native
+Frame 7 is native
+Frame 8 is interpreted
+Thread has 10 frames
+Frame 0 is native
+Frame 1 is interpreted
+Frame 2 is interpreted
+Frame 3 is interpreted
+Frame 4 is interpreted
+Frame 5 is interpreted
+Frame 6 is interpreted
+Frame 7 is interpreted
+Frame 8 is interpreted
+Frame 9 is interpreted
+Thread has 11 frames
+Frame 0 is interpreted
+Frame 1 is native
+Frame 2 is native
+Frame 3 is native
+Frame 4 is native
+Frame 5 is native
+Frame 6 is native
+Frame 7 is native
+Frame 8 is native
+Frame 9 is native
+Frame 10 is interpreted
Index: libjava/testsuite/libjava.jvmti/interp/natgetstacktrace.cc
===================================================================
--- libjava/testsuite/libjava.jvmti/interp/natgetstacktrace.cc (revision 0)
+++ libjava/testsuite/libjava.jvmti/interp/natgetstacktrace.cc (revision 0)
@@ -0,0 +1,144 @@
+#include <jni.h>
+
+#include <jvmti.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <unistd.h>
+
+#include "getstacktrace.h"
+
+void
+printStackTrace (jvmtiFrameInfo *frames, jint frame_cnt)
+{
+ printf ("Thread has %d frames\n", static_cast<int> (frame_cnt));
+
+ for (int i = 0; i < frame_cnt; i++)
+ {
+ jmethodID method = frames[i].method;
+ jlocation location = frames[i].location;
+
+ if (location == -1)
+ {
+ printf ("Frame %d is native\n", i);
+ }
+ else
+ {
+ printf ("Frame %d is interpreted\n", i);
+ }
+ }
+}
+
+
+JNIEXPORT void JNICALL Java_getstacktrace_natPlaceholder (JNIEnv *env, jobject obj)
+{
+ jclass klass = env->GetObjectClass (obj);
+ jfieldID done_id = env->GetFieldID (klass, "done", "Z");
+ jfieldID num_frames_id = env->GetFieldID (klass, "num_frames", "I");
+ jfieldID thread_num_id = env->GetFieldID (klass, "thread_num", "I");
+
+ // num_frames--
+ jint n_frames = env->GetIntField (obj, num_frames_id);
+ n_frames--;
+ env->SetIntField (obj, num_frames_id, n_frames);
+
+ jint t_num = env->GetIntField (obj, thread_num_id);
+
+ if (n_frames <= 1)
+ {
+ if (t_num % 2 == 1)
+ {
+ jmethodID natRunner_id = env->GetMethodID (klass, "natRunner", "()V");
+ env->CallVoidMethod (obj, natRunner_id);
+ }
+ else
+ {
+ jmethodID runner_id = env->GetMethodID (klass, "runner", "()V");
+ env->CallVoidMethod (obj, runner_id);
+ }
+ }
+ else
+ {
+ if (t_num % 2 == 0)
+ {
+ jmethodID natPlaceholder_id = env->GetMethodID (klass,
+ "natPlaceholder",
+ "()V");
+ env->CallVoidMethod (obj, natPlaceholder_id);
+ }
+ else
+ {
+ jmethodID placeholder_id = env->GetMethodID (klass, "placeholder",
+ "()V");
+ env->CallVoidMethod (obj, placeholder_id);
+ }
+ }
+}
+
+JNIEXPORT void JNICALL Java_getstacktrace_natRunner (JNIEnv *env, jobject obj)
+{
+ jclass klass = env->GetObjectClass (obj);
+ jfieldID done_id = env->GetFieldID (klass, "done", "Z");
+
+
+ jboolean done;
+ done = true;
+ env->SetBooleanField (obj, done_id, done);
+
+ do
+ {
+ done = env->GetBooleanField (obj, done_id);
+ if (done == false)
+ break;
+ usleep (10);
+ }
+ while (done != false);
+}
+
+JNIEXPORT jint JNICALL Java_getstacktrace_do_1getstacktrace_1tests
+(JNIEnv *env, jclass klass, jobjectArray thr_arr)
+{
+ JavaVM *vm;
+ jint err = env->GetJavaVM (&vm);
+ if (err < 0)
+ {
+ fprintf (stderr, "error getting VM\n");
+ exit (1);
+ }
+
+ jvmtiEnv *jvmti = NULL;
+ vm->GetEnv ((void **) &jvmti, JVMTI_VERSION_1_0);
+
+ if (jvmti == NULL)
+ {
+ fprintf (stderr, "error getting jvmti environment\n");
+ exit (1);
+ }
+
+ jint frame_cnt;
+ jvmtiFrameInfo frames[30];
+
+ jvmtiError jerr;
+ jthread thr;
+
+ jsize num_threads = env->GetArrayLength (thr_arr);
+
+ for (int i = 0; i < num_threads; i++)
+ {
+ thr = reinterpret_cast<jthread>
+ (env->GetObjectArrayElement (thr_arr, static_cast<jsize> (i)));
+ fflush (stdout);
+ jerr = jvmti->GetStackTrace (thr, 0, 30, frames, &frame_cnt);
+ if (jerr != JVMTI_ERROR_NONE)
+ {
+ char *error_name;
+ jvmti->GetErrorName (jerr, &error_name);
+ fprintf (stderr, "JVMTI Error: %s\n", error_name);
+ jvmti->Deallocate (reinterpret_cast<unsigned char *> (error_name));
+ }
+ else
+ {
+ printStackTrace (frames, frame_cnt);
+ }
+ }
+}
Index: libjava/jni.cc
===================================================================
--- libjava/jni.cc (revision 121173)
+++ libjava/jni.cc (working copy)
@@ -2339,6 +2339,10 @@
// Copy over passed-in arguments.
memcpy (&real_args[offset], args, _this->args_raw_size);
+
+ // Add a frame to the composite (interpreted + JNI) call stack
+ java::lang::Thread *thread = java::lang::Thread::currentThread();
+ _Jv_NativeFrame nat_frame (_this, thread);
// The actual call to the JNI function.
#if FFI_NATIVE_RAW_API
Index: libjava/jvmti.cc
===================================================================
--- libjava/jvmti.cc (revision 121174)
+++ libjava/jvmti.cc (working copy)
@@ -242,6 +242,34 @@
}
static jvmtiError JNICALL
+_Jv_JVMTI_GetFrameCount (MAYBE_UNUSED jvmtiEnv *env, jthread thread,
+ jint* frame_count)
+{
+ REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+
+ NULL_CHECK (frame_count);
+
+ using namespace java::lang;
+
+ THREAD_DEFAULT_TO_CURRENT (thread);
+
+ Thread *thr = reinterpret_cast<Thread *> (thread);
+ THREAD_CHECK_VALID (thr);
+ THREAD_CHECK_IS_ALIVE (thr);
+
+ _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thr->frame);
+ (*frame_count) = 0;
+
+ while (frame != NULL)
+ {
+ (*frame_count)++;
+ frame = frame->next;
+ }
+
+ return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
_Jv_JVMTI_CreateRawMonitor (MAYBE_UNUSED jvmtiEnv *env, const char *name,
jrawMonitorID *result)
{
@@ -711,6 +739,82 @@
}
static jvmtiError JNICALL
+_Jv_JVMTI_GetStackTrace (MAYBE_UNUSED jvmtiEnv *env, jthread thread,
+ jint start_depth, jint max_frames,
+ jvmtiFrameInfo *frames, jint *frame_count)
+{
+ REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
+
+ ILLEGAL_ARGUMENT (max_frames < 0);
+
+ NULL_CHECK (frames);
+ NULL_CHECK (frame_count);
+
+ using namespace java::lang;
+
+ THREAD_DEFAULT_TO_CURRENT (thread);
+
+ Thread *thr = reinterpret_cast<Thread *> (thread);
+ THREAD_CHECK_VALID (thr);
+ THREAD_CHECK_IS_ALIVE (thr);
+
+ jvmtiError jerr = env->GetFrameCount (thread, frame_count);
+ if (jerr != JVMTI_ERROR_NONE)
+ return jerr;
+
+ // start_depth can be either a positive number, indicating the depth of the
+ // stack at which to begin the trace, or a negative number indicating the
+ // number of frames at the bottom of the stack to exclude. These checks
+ // ensure that it is a valid value in either case
+
+ ILLEGAL_ARGUMENT (start_depth >= (*frame_count));
+ ILLEGAL_ARGUMENT (start_depth < (-(*frame_count)));
+
+ _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thr->frame);
+
+ // If start_depth is negative use this to determine at what depth to start
+ // the trace by adding it to the length of the call stack. This allows the
+ // use of the same frame "discarding" mechanism as for a positive start_depth
+ if (start_depth < 0)
+ start_depth = *frame_count + start_depth;
+
+ // If start_depth > 0 "remove" start_depth frames from the beginning
+ // of the stack before beginning the trace by moving along the frame list.
+ while (start_depth > 0)
+ {
+ frame = frame->next;
+ start_depth--;
+ (*frame_count)--;
+ }
+
+ // Now check to see if the array supplied by the agent is large enough to
+ // hold frame_count frames, after adjustment for start_depth.
+ if ((*frame_count) > max_frames)
+ (*frame_count) = max_frames;
+
+ for (int i = 0; i < (*frame_count); i++)
+ {
+ frames[i].method = frame->self->get_method ();
+
+ // Set the location in the frame, native frames have location = -1
+ if (frame->frame_type == frame_interpreter)
+ {
+ _Jv_InterpMethod *imeth
+ = static_cast<_Jv_InterpMethod *> (frame->self);
+ _Jv_InterpFrame *interp_frame
+ = static_cast<_Jv_InterpFrame *> (frame);
+ frames[i].location = imeth->insn_index (interp_frame->pc);
+ }
+ else
+ frames[i].location = -1;
+
+ frame = frame->next;
+ }
+
+ return JVMTI_ERROR_NONE;
+}
+
+static jvmtiError JNICALL
_Jv_JVMTI_ForceGarbageCollection (MAYBE_UNUSED jvmtiEnv *env)
{
REQUIRE_PHASE (env, JVMTI_PHASE_LIVE);
@@ -1449,7 +1553,7 @@
UNIMPLEMENTED, // GetTopThreadGroups
UNIMPLEMENTED, // GetThreadGroupInfo
UNIMPLEMENTED, // GetThreadGroupChildren
- UNIMPLEMENTED, // GetFrameCount
+ _Jv_JVMTI_GetFrameCount, // GetFrameCount
UNIMPLEMENTED, // GetThreadState
RESERVED, // reserved18
UNIMPLEMENTED, // GetFrameLocation
@@ -1537,7 +1641,7 @@
UNIMPLEMENTED, // GetThreadListStackTraces
UNIMPLEMENTED, // GetThreadLocalStorage
UNIMPLEMENTED, // SetThreadLocalStorage
- UNIMPLEMENTED, // GetStackTrace
+ _Jv_JVMTI_GetStackTrace, // GetStackTrace
RESERVED, // reserved105
UNIMPLEMENTED, // GetTag
UNIMPLEMENTED, // SetTag
Index: libjava/java/lang/Thread.h
===================================================================
--- libjava/java/lang/Thread.h (revision 121173)
+++ libjava/java/lang/Thread.h (working copy)
@@ -144,6 +144,7 @@
static const jbyte THREAD_PARK_DEAD = 3;
::java::lang::Object * accessControlState;
::gnu::gcj::RawData * interp_frame;
+ ::gnu::gcj::RawData * frame;
volatile jint state;
::gnu::gcj::RawDataManaged * data;
public:
Index: libjava/java/lang/Thread.java
===================================================================
--- libjava/java/lang/Thread.java (revision 121173)
+++ libjava/java/lang/Thread.java (working copy)
@@ -182,6 +182,9 @@
// This describes the top-most interpreter frame for this thread.
RawData interp_frame;
+
+ // This describes the top most frame in the composite (interp + JNI) stack
+ RawData frame;
// Current state.
volatile int state;