Browse Source

HPCC-22580 Improve error reporting in embedded Java code

Dump stack traces for any Java exceptions to log file.

Also provide a way to output to standard HPCC log from within embedded java
code.

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 5 năm trước cách đây
mục cha
commit
1ce977a334

+ 1 - 2
plugins/javaembed/HpccUtils.java

@@ -30,8 +30,6 @@ public class HpccUtils  implements Iterator
         handle = _handle;
     }
     public native void remove();
-    {
-    }
     public boolean hasNext()
     {
         return _hasNext(handle);
@@ -43,4 +41,5 @@ public class HpccUtils  implements Iterator
            throw new NoSuchElementException();
         return ret;
     }
+    public native static void log(String msg);
 }

+ 68 - 1
plugins/javaembed/javaembed.cpp

@@ -86,6 +86,10 @@ static void UNSUPPORTED(const char *feature)
 namespace javaembed {
 
 static jmethodID throwable_toString;
+static jmethodID throwable_getStackTrace;
+static jmethodID throwable_getCause;
+static jmethodID frame_toString;
+
 
 static void forceGC(class CheckedJNIEnv* JNIenv);
 
@@ -120,12 +124,59 @@ public:
         }
         return ret;
     }
+
+    void checkUnexpectedException()
+    {
+        if (JNIEnv::ExceptionCheck())
+        {
+            DBGLOG("javaembed: Uunexpected java exception while processing exception");
+            JNIEnv::ExceptionDescribe();
+            JNIEnv::ExceptionClear();
+            throwUnexpected();
+        }
+    }
+
+    void traceException(jthrowable exception)
+    {
+        // Don't use auto-checking in here, as we are already inside exception-checking code
+        jstring msg = (jstring) JNIEnv::CallObjectMethod(exception, throwable_toString);
+        checkUnexpectedException();
+        const char *text = JNIEnv::GetStringUTFChars(msg, 0);
+        DBGLOG("javaembed: exception: %s", text);
+        JNIEnv::ReleaseStringUTFChars(msg, text);
+        checkUnexpectedException();
+        JNIEnv::DeleteLocalRef(msg);
+
+        jobjectArray frames = (jobjectArray) JNIEnv::CallObjectMethod(exception, throwable_getStackTrace);
+        checkUnexpectedException();
+        jsize length = JNIEnv::GetArrayLength(frames);
+        for (jsize i = 0; i < length; i++)
+        {
+            jobject frame = JNIEnv::GetObjectArrayElement(frames, i);
+            checkUnexpectedException();
+            msg = (jstring) JNIEnv::CallObjectMethod(frame, frame_toString);
+            text = JNIEnv::GetStringUTFChars(msg, 0);
+            DBGLOG("javaembed: exception: stack: %s", text);
+            JNIEnv::ReleaseStringUTFChars(msg, text);
+            checkUnexpectedException();
+            JNIEnv::DeleteLocalRef(msg);
+            JNIEnv::DeleteLocalRef(frame);
+        }
+        jthrowable cause = (jthrowable) JNIEnv::CallObjectMethod(exception, throwable_getCause);
+        if (cause && cause != exception)
+        {
+            DBGLOG("javaembed: exception: Caused by:");
+            traceException(cause);
+        }
+    }
+
     void checkException()
     {
         if (JNIEnv::ExceptionCheck())
         {
             jthrowable exception = JNIEnv::ExceptionOccurred();
             JNIEnv::ExceptionClear();
+            traceException(exception);
             jstring cause = (jstring) JNIEnv::CallObjectMethod(exception, throwable_toString);
             JNIEnv::ExceptionClear();
             const char *text = JNIEnv::GetStringUTFChars(cause, 0);
@@ -673,7 +724,8 @@ static jclass langStringClass;
 static jclass netURLClass;
 static jmethodID netURL_constructor;
 static jclass throwableClass;
-//static jmethodID throwable_toString;  declared above
+//static jmethodID throwable_toString; and others declared above
+static jclass stackTraceElementClass;
 static jclass langIllegalArgumentExceptionClass;
 
 static void forceGC(CheckedJNIEnv* JNIenv)
@@ -688,6 +740,10 @@ static void setupGlobals(CheckedJNIEnv *J)
         // Load this first as we can't report errors on the others sensibly if this one not loaded!
         throwableClass = J->FindGlobalClass("java/lang/Throwable");
         throwable_toString = J->GetMethodID(throwableClass, "toString", "()Ljava/lang/String;");
+        throwable_getStackTrace = J->GetMethodID(throwableClass, "getStackTrace", "()[Ljava/lang/StackTraceElement;");
+        throwable_getCause = J->GetMethodID(throwableClass, "getCause", "()Ljava/lang/Throwable;");
+        stackTraceElementClass = J->FindGlobalClass("java/lang/StackTraceElement");
+        frame_toString = J->GetMethodID(stackTraceElementClass, "toString", "()Ljava/lang/String;");
 
         systemClass = J->FindGlobalClass("java/lang/System");
         system_gc = J->GetStaticMethodID(systemClass, "gc", "()V");
@@ -4862,6 +4918,7 @@ extern "C" {
 JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1hasNext (JNIEnv *, jclass, jlong);
 JNIEXPORT jobject JNICALL Java_com_HPCCSystems_HpccUtils__1next (JNIEnv *, jclass, jlong);
 JNIEXPORT jclass JNICALL Java_com_HPCCSystems_HpccClassLoader_defineClassForEmbed(JNIEnv *env, jobject loader, jint bytecodeLen, jlong bytecode, jstring name);
+JNIEXPORT void JNICALL Java_com_HPCCSystems_HpccUtils_log(JNIEnv *JNIenv, jclass, jstring msg);
 }
 
 JNIEXPORT jboolean JNICALL Java_com_HPCCSystems_HpccUtils__1hasNext (JNIEnv *JNIenv, jclass, jlong proxy)
@@ -4929,6 +4986,16 @@ JNIEXPORT jclass JNICALL Java_com_HPCCSystems_HpccClassLoader_defineClassForEmbe
 
 }
 
+JNIEXPORT void JNICALL Java_com_HPCCSystems_HpccUtils_log(JNIEnv *JNIenv, jclass, jstring msg)
+{
+    if (msg)
+    {
+        const char *text = JNIenv->GetStringUTFChars(msg, 0);
+        DBGLOG("javaembed: user: %s", text);
+        JNIenv->ReleaseStringUTFChars(msg, text);
+    }
+}
+
 // Used for dynamically loading in ESDL
 
 extern "C" DECL_EXPORT IEmbedContext *getEmbedContextDynamic()

+ 5 - 0
testing/regress/ecl/javaembed.ecl

@@ -27,10 +27,15 @@ string jcat(string a, string b) := EMBED(java)
 ENDEMBED;
 
 integer jadd(integer a, integer b) := EMBED(java)
+import com.HPCCSystems.HpccUtils;
+class MyClass
+{
   public static int jadd(int a, int b)
   {
+    HpccUtils.log("In jadd");
     return a + b;
   }
+}
 ENDEMBED;
 integer jaddL(integer a, integer b) := EMBED(java)
   public static long jaddL(int a, int b)