8295974: jni_FatalError and Xcheck:jni warnings should print the native stack when there are no Java frames
Reviewed-by: coleenp, rehn, sspitsyn
This commit is contained in:
parent
417d01ea63
commit
375743336d
@ -874,7 +874,7 @@ BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exesigtest := -ljvm
|
||||
|
||||
ifeq ($(call isTargetOs, windows), true)
|
||||
BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT
|
||||
BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libCompleteExit.c libTestPsig.c
|
||||
BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libCompleteExit.c libTestPsig.c libnativeStack.c
|
||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib
|
||||
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exedaemonDestroy := jvm.lib
|
||||
else
|
||||
@ -1515,6 +1515,7 @@ else
|
||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libterminatedThread += -lpthread
|
||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit += -ljvm
|
||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libCompleteExit += -lpthread
|
||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread
|
||||
endif
|
||||
|
||||
# This evaluation is expensive and should only be done if this target was
|
||||
|
@ -99,6 +99,7 @@
|
||||
#include "utilities/macros.hpp"
|
||||
#include "utilities/preserveException.hpp"
|
||||
#include "utilities/spinYield.hpp"
|
||||
#include "utilities/vmError.hpp"
|
||||
#if INCLUDE_JVMCI
|
||||
#include "jvmci/jvmci.hpp"
|
||||
#include "jvmci/jvmciEnv.hpp"
|
||||
@ -1673,14 +1674,23 @@ oop JavaThread::current_park_blocker() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Print stack trace for checked JNI warnings and JNI fatal errors.
|
||||
// This is the external format from above, but selecting the platform
|
||||
// or vthread as applicable.
|
||||
// Print current stack trace for checked JNI warnings and JNI fatal errors.
|
||||
// This is the external format, selecting the platform or vthread
|
||||
// as applicable, and allowing for a native-only stack.
|
||||
void JavaThread::print_jni_stack() {
|
||||
if (is_vthread_mounted()) {
|
||||
print_vthread_stack_on(tty);
|
||||
assert(this == JavaThread::current(), "Can't print stack of other threads");
|
||||
if (!has_last_Java_frame()) {
|
||||
ResourceMark rm(this);
|
||||
char* buf = NEW_RESOURCE_ARRAY_RETURN_NULL(char, O_BUFLEN);
|
||||
if (buf == nullptr) {
|
||||
tty->print_cr("Unable to print native stack - out of memory");
|
||||
return;
|
||||
}
|
||||
frame f = os::current_frame();
|
||||
VMError::print_native_stack(tty, f, this, true /*print_source_info */,
|
||||
-1 /* max stack */, buf, O_BUFLEN);
|
||||
} else {
|
||||
print_stack_on(tty);
|
||||
print_active_stack_on(tty);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -945,7 +945,7 @@ private:
|
||||
void print_vthread_stack_on(outputStream* st);
|
||||
// This prints the active stack: either carrier/platform or virtual.
|
||||
void print_active_stack_on(outputStream* st);
|
||||
// Print stack trace for checked JNI warnings and JNI fatal errors.
|
||||
// Print current stack trace for checked JNI warnings and JNI fatal errors.
|
||||
// This is the external format from above, but selecting the platform
|
||||
// or vthread as applicable.
|
||||
void print_jni_stack();
|
||||
|
@ -103,15 +103,6 @@ class VMError : public AllStatic {
|
||||
static void print_stack_trace(outputStream* st, JavaThread* jt,
|
||||
char* buf, int buflen, bool verbose = false);
|
||||
|
||||
// public for use by the internal non-product debugger.
|
||||
NOT_PRODUCT(public:)
|
||||
// print_source_info: if true, we try to resolve the source information on platforms that support it
|
||||
// (useful but may slow down, timeout or misfunction in error situations)
|
||||
// max_frames: if not -1, overrides StackPrintLimit
|
||||
static void print_native_stack(outputStream* st, frame fr, Thread* t, bool print_source_info,
|
||||
int max_frames, char* buf, int buf_size);
|
||||
NOT_PRODUCT(private:)
|
||||
|
||||
static const char* get_filename_only() {
|
||||
char separator = os::file_separator()[0];
|
||||
const char* p = strrchr(_filename, separator);
|
||||
@ -147,6 +138,12 @@ class VMError : public AllStatic {
|
||||
|
||||
public:
|
||||
|
||||
// print_source_info: if true, we try to resolve the source information on platforms that support it
|
||||
// (useful but may slow down, timeout or misfunction in error situations)
|
||||
// max_frames: if not -1, overrides StackPrintLimit
|
||||
static void print_native_stack(outputStream* st, frame fr, Thread* t, bool print_source_info,
|
||||
int max_frames, char* buf, int buf_size);
|
||||
|
||||
// return a string to describe the error
|
||||
static char* error_string(char* buf, int buflen);
|
||||
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8295974
|
||||
* @requires os.family != "windows"
|
||||
* @library /test/lib
|
||||
* @summary Generate a JNI Fatal error, or a warning, in a launched VM and check
|
||||
* the native stack is present as expected.
|
||||
* @comment The native code only supports POSIX so no windows testing
|
||||
* @run driver TestNativeStack
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
public class TestNativeStack {
|
||||
|
||||
/**
|
||||
* Create a native thread that will execute native code that
|
||||
* will either trigger a JNI warning (with -Xcheck:jni) or a JNI
|
||||
* error, depending on the value of `warning`.
|
||||
*/
|
||||
static native void triggerJNIStackTrace(boolean warning);
|
||||
|
||||
static {
|
||||
System.loadLibrary("nativeStack");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
// case 1: Trigger a JNI warning with Xcheck:jni
|
||||
OutputAnalyzer oa =
|
||||
ProcessTools.executeTestJvm("-Xcheck:jni",
|
||||
"-Djava.library.path=" + Utils.TEST_NATIVE_PATH,
|
||||
"TestNativeStack$Main");
|
||||
oa.shouldHaveExitValue(0);
|
||||
oa.shouldContain("WARNING in native method");
|
||||
oa.shouldContain("thread_start");
|
||||
oa.reportDiagnosticSummary();
|
||||
|
||||
// Case 2: Trigger a JNI FatalError call
|
||||
oa = ProcessTools.executeTestJvm("-XX:-CreateCoredumpOnCrash",
|
||||
"-Djava.library.path=" + Utils.TEST_NATIVE_PATH,
|
||||
"TestNativeStack$Main",
|
||||
"error");
|
||||
oa.shouldNotHaveExitValue(0);
|
||||
oa.shouldContain("FATAL ERROR in native method");
|
||||
oa.shouldContain("thread_start");
|
||||
oa.reportDiagnosticSummary();
|
||||
}
|
||||
|
||||
static class Main {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
boolean doWarning = args.length == 0;
|
||||
System.out.println("Triggering a JNI " +
|
||||
(doWarning ? "warning" : "fatal error"));
|
||||
triggerJNIStackTrace(doWarning);
|
||||
}
|
||||
}
|
||||
}
|
114
test/hotspot/jtreg/runtime/jni/nativeStack/libnativeStack.c
Normal file
114
test/hotspot/jtreg/runtime/jni/nativeStack/libnativeStack.c
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code 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 General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
JavaVM* jvm;
|
||||
jboolean warning = 0;
|
||||
|
||||
void generateWarning(JNIEnv *env) {
|
||||
jclass class_id;
|
||||
jmethodID method_id;
|
||||
|
||||
printf("About to trigger JNI Warning\n");
|
||||
|
||||
// Just call Thread.currentThread() twice in succession without checking
|
||||
// for an exception in between.
|
||||
|
||||
class_id = (*env)->FindClass (env, "java/lang/Thread");
|
||||
if (class_id == NULL) {
|
||||
fprintf(stderr, "Test ERROR. Can't load class Thread\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
method_id = (*env)->GetStaticMethodID(env, class_id, "currentThread",
|
||||
"()Ljava/lang/Thread;");
|
||||
if (method_id == NULL) {
|
||||
fprintf(stderr, "Test ERROR. Can't find method currentThread\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
jobject nativeThread = (*env)->CallStaticObjectMethod(env, class_id, method_id, NULL);
|
||||
nativeThread = (*env)->CallStaticObjectMethod(env, class_id, method_id, NULL);
|
||||
}
|
||||
|
||||
void generateError(JNIEnv *env) {
|
||||
printf("About to trigger JNI FatalError\n");
|
||||
(*env)->FatalError(env, "Fatal error generated in test code");
|
||||
}
|
||||
|
||||
static void * thread_start(void* unused) {
|
||||
JNIEnv *env;
|
||||
int res;
|
||||
|
||||
printf("Native thread is running and attaching as daemon ...\n");
|
||||
|
||||
res = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&env, NULL);
|
||||
if (res != JNI_OK) {
|
||||
fprintf(stderr, "Test ERROR. Can't attach current thread: %d\n", res);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (warning != 0) {
|
||||
generateWarning(env);
|
||||
} else {
|
||||
generateError(env);
|
||||
}
|
||||
|
||||
if ((*env)->ExceptionOccurred(env) != NULL) {
|
||||
(*env)->ExceptionDescribe(env);
|
||||
exit(1);
|
||||
}
|
||||
printf("Native thread terminating\n");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_TestNativeStack_triggerJNIStackTrace
|
||||
(JNIEnv *env, jclass cls, jboolean warn) {
|
||||
pthread_t thread;
|
||||
int res = (*env)->GetJavaVM(env, &jvm);
|
||||
if (res != JNI_OK) {
|
||||
fprintf(stderr, "Test ERROR. Can't extract JavaVM: %d\n", res);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
warning = warn;
|
||||
|
||||
if ((res = pthread_create(&thread, NULL, thread_start, NULL)) != 0) {
|
||||
fprintf(stderr, "TEST ERROR: pthread_create failed: %s (%d)\n", strerror(res), res);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((res = pthread_join(thread, NULL)) != 0) {
|
||||
fprintf(stderr, "TEST ERROR: pthread_join failed: %s (%d)\n", strerror(res), res);
|
||||
exit(1);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user