This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
[RFA] Fix JVMTI/JDWP Exception Handling
- From: Keith Seitz <keiths at redhat dot com>
- To: Java Patch List <java-patches at gcc dot gnu dot org>
- Date: Tue, 01 May 2007 16:33:12 -0700
- Subject: [RFA] Fix JVMTI/JDWP Exception Handling
Hi,
The following patch is a follow-on from Kyle's original patch.
Unfortunately, I discovered while playing with the original patch that
there is a small problem with Threads not being removed from the Map in
ExceptionEvent. So I've removed this and implemented the rest in
interpret.cc.
Also we were missing the JDWP side of this. I have implemented that,
too. So, as far as I can tell, JVMTI/JDWP Exception notifications work
(both interpreted and native).
QCC? (Questions/comments/concerns?)
Keith
ChangeLog
2007-05-01 Keith Seitz <keiths@redhat.com>
* include/jvmti-int.h (_Jv_ReportJVMTIExceptionThrow):
Declare.
* interpret.cc (_Jv_ReportJVMTIExceptionThrow): New function.
(find_catch_location): New function.
(REPORT_EXCEPTION): New macro.
(throw_internal_error): Use REPORT_EXCEPTION.
(throw_incompatible_class_change_error): Likewise.
(throw_null_pointer_exception): Likewise.
(throw_class_format_error): Likewise.
* interpret-run.cc (INTERP_REPORT_EXCEPTION)[DEBUG]: Set
to REAL_REPORT_EXCEPTION.
(INTERP_REPORT_EXCEPTION)[!DEBUG]: Make nop.
(insn_new): Use INTERP_REPORT_EXCEPTION.
(insn_athrow): Likewise.
Remove previous JVMTI exception notifications.
Add JVMTI ExceptionCatch notificatin.
* jni.cc (_Jv_PopSystemFrame): Notify JVMTI clients of
exception throw.
* gnu/gcj/jvmti/ExceptionEvent.java: Removed.
* gnu/gcj/jvmti/ExceptionEvent.h: Removed.
* gnu/classpath/jdwp/natVMVirtualMachine.cc
(jdwpExceptionCB): New function.
(jdwpVMInitCB): Set Exception event handler and enable.
* sources.am: Regenerated.
* Makefile.in: Regenerated.
Index: include/jvmti-int.h
===================================================================
--- include/jvmti-int.h (revision 124312)
+++ include/jvmti-int.h (working copy)
@@ -89,4 +89,6 @@
// Returns the jvmtiEnv used by the JDWP backend
extern jvmtiEnv *_Jv_GetJDWP_JVMTIEnv (void);
+// Reports JVMTI excpetions
+extern void _Jv_ReportJVMTIExceptionThrow (jthrowable);
#endif /* __GCJ_JVMTI_INT_H__ */
Index: interpret.cc
===================================================================
--- interpret.cc (revision 124312)
+++ interpret.cc (working copy)
@@ -40,7 +40,6 @@
#include <jvmti.h>
#include "jvmti-int.h"
-#include <gnu/classpath/jdwp/Jdwp.h>
#include <gnu/gcj/jvmti/Breakpoint.h>
#include <gnu/gcj/jvmti/BreakpointManager.h>
#include <gnu/gcj/jvmti/ExceptionEvent.h>
@@ -66,6 +65,17 @@
static void throw_class_format_error (const char *msg)
__attribute__ ((__noreturn__));
+static void find_catch_location (jthrowable, jthread, jmethodID *, jlong *);
+
+// A macro to facilitate JVMTI exception reporting
+#define REAL_REPORT_EXCEPTION(Jthrowable) \
+ do { \
+ if (JVMTI_REQUESTED_EVENT (Exception)) \
+ _Jv_ReportJVMTIExceptionThrow (Jthrowable); \
+ } \
+ while (0)
+#define REPORT_EXCEPTION(Jthrowable) REAL_REPORT_EXCEPTION (Jthrowable)
+
#ifdef DIRECT_THREADED
// Lock to ensure that methods are not compiled concurrently.
// We could use a finer-grained lock here, however it is not safe to use
@@ -956,19 +966,25 @@
static void
throw_internal_error (const char *msg)
{
- throw new java::lang::InternalError (JvNewStringLatin1 (msg));
+ jthrowable t = new java::lang::InternalError (JvNewStringLatin1 (msg));
+ REPORT_EXCEPTION (t);
+ throw t;
}
static void
throw_incompatible_class_change_error (jstring msg)
{
- throw new java::lang::IncompatibleClassChangeError (msg);
+ jthrowable t = new java::lang::IncompatibleClassChangeError (msg);
+ REPORT_EXCEPTION (t);
+ throw t;
}
static void
throw_null_pointer_exception ()
{
- throw new java::lang::NullPointerException;
+ jthrowable t = new java::lang::NullPointerException;
+ REPORT_EXCEPTION (t);
+ throw t;
}
/* Look up source code line number for given bytecode (or direct threaded
@@ -1613,9 +1629,11 @@
static void
throw_class_format_error (jstring msg)
{
- throw (msg
+ jthrowable t = (msg
? new java::lang::ClassFormatError (msg)
: new java::lang::ClassFormatError);
+ REPORT_EXCEPTION (t);
+ throw t;
}
static void
@@ -1624,6 +1642,70 @@
throw_class_format_error (JvNewStringLatin1 (msg));
}
+/* This function finds the method and location where the exception EXC
+ is caught in the stack frame. On return, it sets CATCH_METHOD and
+ CATCH_LOCATION with the method and location where the catch will
+ occur. If the exception is not caught, these are set to 0.
+
+ This function should only be used with the DEBUG interpreter. */
+static void
+find_catch_location (::java::lang::Throwable *exc, jthread thread,
+ jmethodID *catch_method, jlong *catch_loc)
+{
+ *catch_method = 0;
+ *catch_loc = 0;
+
+ _Jv_InterpFrame *frame
+ = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
+ while (frame != NULL)
+ {
+ pc_t pc = frame->get_pc ();
+ _Jv_InterpMethod *imeth
+ = reinterpret_cast<_Jv_InterpMethod *> (frame->self);
+ if (imeth->check_handler (&pc, imeth, exc))
+ {
+ // This method handles the exception.
+ *catch_method = imeth->get_method ();
+ *catch_loc = imeth->insn_index (pc);
+ return;
+ }
+
+ frame = frame->next_interp;
+ }
+}
+
+/* This method handles JVMTI notifications of thrown exceptions. It
+ calls find_catch_location to figure out where the exception is
+ caught (if it is caught).
+
+ Like find_catch_location, this should only be called with the
+ DEBUG interpreter. Since a few exceptions occur outside the
+ interpreter proper, it is important to not call this function
+ without checking JVMTI_REQUESTED_EVENT(Exception) first. */
+void
+_Jv_ReportJVMTIExceptionThrow (jthrowable ex)
+{
+ jthread thread = ::java::lang::Thread::currentThread ();
+ _Jv_Frame *frame = reinterpret_cast<_Jv_Frame *> (thread->frame);
+ jmethodID throw_meth = frame->self->get_method ();
+ jlocation throw_loc = -1;
+ if (frame->frame_type == frame_interpreter)
+ {
+ _Jv_InterpFrame * iframe
+ = reinterpret_cast<_Jv_InterpFrame *> (frame);
+ _Jv_InterpMethod *imeth
+ = reinterpret_cast<_Jv_InterpMethod *> (frame->self);
+ throw_loc = imeth->insn_index (iframe->get_pc ());
+ }
+
+ jlong catch_loc;
+ jmethodID catch_method;
+ find_catch_location (ex, thread, &catch_method, &catch_loc);
+ _Jv_JVMTI_PostEvent (JVMTI_EVENT_EXCEPTION, thread,
+ _Jv_GetCurrentJNIEnv (), throw_meth, throw_loc,
+ ex, catch_method, catch_loc);
+}
+
void
Index: interpret-run.cc
===================================================================
--- interpret-run.cc (revision 124313)
+++ interpret-run.cc (working copy)
@@ -377,7 +377,9 @@
} \
while (0)
-#else
+#undef REPORT_EXCEPTION
+#define REPORT_EXCEPTION(Jthrowable) REAL_REPORT_EXCEPTION (Jthrowable)
+#else // !DEBUG
#undef NEXT_INSN
#define NEXT_INSN goto *((pc++)->insn)
#define REWRITE_INSN(INSN,SLOT,VALUE) \
@@ -386,8 +388,11 @@
pc[-1].SLOT = VALUE; \
} \
while (0)
-#endif
+#undef REPORT_EXCEPTION
+#define REPORT_EXCEPTION(Jthrowable) /* not needed when not debugging */
+#endif // !DEBUG
+
#define INTVAL() ((pc++)->int_val)
#define AVAL() ((pc++)->datum)
@@ -2355,7 +2360,11 @@
/* VM spec, section 3.11.5 */
if ((klass->getModifiers() & Modifier::ABSTRACT)
|| klass->isInterface())
- throw new java::lang::InstantiationException;
+ {
+ jthrowable t = new java::lang::InstantiationException;
+ REPORT_EXCEPTION (t);
+ throw t;
+ }
jobject res = _Jv_AllocObject (klass);
PUSHA (res);
@@ -2422,7 +2431,9 @@
insn_athrow:
{
jobject value = POPA();
- throw static_cast<jthrowable>(value);
+ jthrowable t = static_cast<jthrowable> (value);
+ REPORT_EXCEPTION (t);
+ throw t;
}
NEXT_INSN;
@@ -2639,10 +2650,6 @@
}
catch (java::lang::Throwable *ex)
{
-#ifdef DEBUG
- // This needs to be done before the pc is changed.
- jlong throw_loc = meth->insn_index (pc);
-#endif
// Check if the exception is handled and, if so, set the pc to the start
// of the appropriate catch block.
if (meth->check_handler (&pc, meth, ex))
@@ -2650,27 +2657,19 @@
sp = stack;
sp++->o = ex; // Push exception.
#ifdef DEBUG
- if (JVMTI_REQUESTED_EVENT (Exception))
+ if (JVMTI_REQUESTED_EVENT (ExceptionCatch))
{
using namespace gnu::gcj::jvmti;
- jlong throw_meth = reinterpret_cast<jlong> (meth->get_method ());
+ jlong catch_meth = reinterpret_cast<jlong> (meth->get_method ());
jlong catch_loc = meth->insn_index (pc);
- ExceptionEvent::postExceptionEvent (thread, throw_meth,
- throw_loc, ex, throw_meth,
- catch_loc);
+ _Jv_JVMTI_PostEvent (JVMTI_EVENT_EXCEPTION_CATCH, thread,
+ _Jv_GetCurrentJNIEnv (), catch_meth,
+ catch_loc, ex);
}
#endif
NEXT_INSN;
}
-#ifdef DEBUG
- if (JVMTI_REQUESTED_EVENT (Exception))
- {
- using namespace gnu::gcj::jvmti;
- jlong throw_meth = reinterpret_cast<jlong> (meth->get_method ());
- ExceptionEvent::postExceptionEvent (thread, throw_meth, throw_loc,
- ex, NULL, NULL);
- }
-#endif
+
// No handler, so re-throw.
throw ex;
}
Index: jni.cc
===================================================================
--- jni.cc (revision 124312)
+++ jni.cc (working copy)
@@ -23,6 +23,7 @@
#include <jvmpi.h>
#endif
#include <jvmti.h>
+#include "jvmti-int.h"
#include <java/lang/Class.h>
#include <java/lang/ClassLoader.h>
@@ -456,6 +457,8 @@
{
jthrowable t = env->ex;
env->ex = NULL;
+ if (JVMTI_REQUESTED_EVENT (Exception))
+ _Jv_ReportJVMTIExceptionThrow (t);
throw t;
}
}
Index: gnu/classpath/jdwp/natVMVirtualMachine.cc
===================================================================
--- gnu/classpath/jdwp/natVMVirtualMachine.cc (revision 124312)
+++ gnu/classpath/jdwp/natVMVirtualMachine.cc (working copy)
@@ -21,6 +21,7 @@
#include <java/lang/String.h>
#include <java/lang/StringBuilder.h>
#include <java/lang/Thread.h>
+#include <java/lang/Throwable.h>
#include <java/nio/ByteBuffer.h>
#include <java/nio/ByteBufferImpl.h>
#include <java/util/ArrayList.h>
@@ -37,6 +38,7 @@
#include <gnu/classpath/jdwp/VMVirtualMachine.h>
#include <gnu/classpath/jdwp/event/BreakpointEvent.h>
#include <gnu/classpath/jdwp/event/ClassPrepareEvent.h>
+#include <gnu/classpath/jdwp/event/ExceptionEvent.h>
#include <gnu/classpath/jdwp/event/EventManager.h>
#include <gnu/classpath/jdwp/event/EventRequest.h>
#include <gnu/classpath/jdwp/event/SingleStepEvent.h>
@@ -82,6 +84,9 @@
static void JNICALL jdwpBreakpointCB (jvmtiEnv *, JNIEnv *, jthread,
jmethodID, jlocation);
static void JNICALL jdwpClassPrepareCB (jvmtiEnv *, JNIEnv *, jthread, jclass);
+static void JNICALL jdwpExceptionCB (jvmtiEnv *, JNIEnv *jni_env, jthread,
+ jmethodID, jlocation, jobject,
+ jmethodID, jlocation);
static void JNICALL jdwpSingleStepCB (jvmtiEnv *, JNIEnv *, jthread,
jmethodID, jlocation);
static void JNICALL jdwpThreadEndCB (jvmtiEnv *, JNIEnv *, jthread);
@@ -933,6 +938,56 @@
}
static void JNICALL
+jdwpExceptionCB (jvmtiEnv *env, MAYBE_UNUSED JNIEnv *jni_env, jthread thread,
+ jmethodID method, jlocation location, jobject exception,
+ jmethodID catch_method, jlocation catch_location)
+{
+ using namespace gnu::classpath::jdwp;
+ jclass throw_klass;
+ jvmtiError err = env->GetMethodDeclaringClass (method, &throw_klass);
+ if (err != JVMTI_ERROR_NONE)
+ {
+ fprintf (stderr, "libgcj: internal error: could not find class for ");
+ fprintf (stderr, "method throwing exception -- continuing\n");
+ return;
+ }
+
+ VMMethod *vmmethod = new VMMethod (throw_klass,
+ reinterpret_cast<jlong> (method));
+ Location *throw_loc = new Location (vmmethod, location);
+ Location *catch_loc = NULL;
+ if (catch_method == 0)
+ catch_loc = Location::getEmptyLocation ();
+ else
+ {
+ jclass catch_klass;
+ err = env->GetMethodDeclaringClass (catch_method, &catch_klass);
+ if (err != JVMTI_ERROR_NONE)
+ {
+ fprintf (stderr,
+ "libgcj: internal error: could not find class for ");
+ fprintf (stderr,
+ "method catching exception -- ignoring\n");
+ }
+ else
+ {
+ vmmethod = new VMMethod (catch_klass,
+ reinterpret_cast<jlong> (catch_method));
+ catch_loc = new Location (vmmethod, catch_location);
+ }
+ }
+
+ _Jv_InterpFrame *iframe
+ = reinterpret_cast<_Jv_InterpFrame *> (thread->interp_frame);
+ jobject instance = (iframe == NULL) ? NULL : iframe->get_this_ptr ();
+ Throwable *throwable = reinterpret_cast<Throwable *> (exception);
+ event::ExceptionEvent *e = new ExceptionEvent (throwable, thread,
+ throw_loc, catch_loc,
+ throw_klass, instance);
+ Jdwp::notify (e);
+}
+
+static void JNICALL
jdwpSingleStepCB (jvmtiEnv *env, JNIEnv *jni_env, jthread thread,
jmethodID method, jlocation location)
{
@@ -1034,6 +1089,7 @@
jvmtiEventCallbacks callbacks;
DEFINE_CALLBACK (callbacks, Breakpoint);
DEFINE_CALLBACK (callbacks, ClassPrepare);
+ DEFINE_CALLBACK (callbacks, Exception);
DEFINE_CALLBACK (callbacks, SingleStep);
DEFINE_CALLBACK (callbacks, ThreadEnd);
DEFINE_CALLBACK (callbacks, ThreadStart);
@@ -1043,6 +1099,7 @@
// Enable callbacks
ENABLE_EVENT (BREAKPOINT, NULL);
ENABLE_EVENT (CLASS_PREPARE, NULL);
+ ENABLE_EVENT (EXCEPTION, NULL);
// SingleStep is enabled only when needed
ENABLE_EVENT (THREAD_END, NULL);
ENABLE_EVENT (THREAD_START, NULL);