Skip to content

Commit

Permalink
JBR-7600 Provide ability to add messages to fatal error log
Browse files Browse the repository at this point in the history
Use JNU_LOG_EVENT(env, msg, ...) to save a message in the internal
JVM ring buffer that gets printed out to the Events section
of the fatal error log if JVM crashed.
  • Loading branch information
mkartashev authored and jbrbot committed Sep 14, 2024
1 parent 37a074e commit 31a2128
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 0 deletions.
1 change: 1 addition & 0 deletions make/modules/java.base/lib/CoreLibraries.gmk
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
-DARCHPROPNAME='"$(OPENJDK_TARGET_CPU_OSARCH)"', \
DISABLED_WARNINGS_gcc_ProcessImpl_md.c := unused-result, \
DISABLED_WARNINGS_clang_TimeZone_md.c := unused-variable, \
DISABLED_WARNINGS_clang_jni_util.c := format-nonliteral, \
JDK_LIBS := libjvm, \
LIBS_linux := $(LIBDL), \
LIBS_aix := $(LIBDL) $(LIBM), \
Expand Down
1 change: 1 addition & 0 deletions make/test/JtregNativeJdk.gmk
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libTracePinnedThreads := java.base:libjvm
BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libNewDirectByteBuffer := java.base:libjava
BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libGetXSpace := java.base:libjava
BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libFatalErrorTest := java.base:libjava
BUILD_JDK_JTREG_LIBRARIES_JDK_LIBS_libLogEventTest := java.base:libjava

# Platform specific setup
ifeq ($(call isTargetOs, windows), true)
Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/share/prims/jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@ JNI_ENTRY(jint, jni_ThrowNew(JNIEnv *env, jclass clazz, const char *message))
report_fatal(INTERNAL_ERROR, "<dummy>", 0, "%s", message);
ShouldNotReachHere();
return 0;
} else if (name->equals("java/lang/Exception$JB$$Event")) {
Events::log(THREAD, "%s", message);
return 0;
}
Handle class_loader (THREAD, k->class_loader());
Handle protection_domain (THREAD, k->protection_domain());
Expand Down
1 change: 1 addition & 0 deletions src/java.base/share/classes/java/lang/Exception.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,5 @@ protected Exception(String message, Throwable cause,
}

private static class JB$$Assertion {}
private static class JB$$Event {}
}
37 changes: 37 additions & 0 deletions src/java.base/share/native/libjava/jni_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1320,3 +1320,40 @@ JNU_Fatal(JNIEnv *env, const char *file, int line, const char *msg)
(*env)->ThrowNew(env, cls, real_msg);
}
}

JNIEXPORT void JNICALL
JNU_LogEvent(JNIEnv *env, const char *file, int line, const char *fmt, ...)
{
jclass cls = (*env)->FindClass(env, "java/lang/Exception$JB$$Event");
if (cls == 0) return;

va_list args;

va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
if (len < 0) return;

int suffix_len = (int) strlen(file) + 64;
len += suffix_len;
char * real_msg = malloc(len);
if (real_msg == NULL) return;

va_start(args, fmt);
vsnprintf(real_msg, len, fmt, args);
va_end(args);

char * suffix = malloc(suffix_len);
if (suffix == NULL) {
free(real_msg);
return;
}

snprintf(suffix, suffix_len, " (%s:%d)", file, line);
strncat(real_msg, suffix, suffix_len);
free(suffix);

// Throwing an exception by this name will result in Events::log() call
(*env)->ThrowNew(env, cls, real_msg);
free(real_msg);
}
7 changes: 7 additions & 0 deletions src/java.base/share/native/libjava/jni_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ JNU_GetStaticFieldByName(JNIEnv *env,
JNIEXPORT void JNICALL
JNU_Fatal(JNIEnv *env, const char *file, int line, const char *msg);

JNIEXPORT void JNICALL
JNU_LogEvent(JNIEnv *env, const char *file, int line, const char *fmt, ...);

/************************************************************************
* Miscellaneous utilities used by the class libraries
Expand Down Expand Up @@ -330,6 +332,11 @@ JNU_Fatal(JNIEnv *env, const char *file, int line, const char *msg);
} \
} while(0)

#define JNU_LOG_EVENT(env, fmt, ...) \
do { \
JNU_LogEvent((env), __FILE__, __LINE__, (fmt), ##__VA_ARGS__); \
} while(0)

/************************************************************************
* Debugging utilities
*/
Expand Down
80 changes: 80 additions & 0 deletions test/jdk/jb/LogEvent/LogEventTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2024 JetBrains s.r.o.
* 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
* @summary Verifies the error log does contain events previously
* logged with JNU_LOG_EVENT()
* @library /test/lib
* @run main/native LogEventTest
*/

import jdk.test.lib.Platform;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;

public class LogEventTest {

public static void main(String args[]) throws Exception {
if (args.length > 0 && args[0].equals("--test")) {
System.out.println("Proceeding to crash JVM with a simulated assertion failure");
String osDependentLibraryFileName = toPlatformLibraryName("LogEventTest");
String nativePathName = System.getProperty("test.nativepath");
Path nativePath = Paths.get(nativePathName).resolve(osDependentLibraryFileName);
System.out.println("Loading library from: " + nativePath);
System.load(String.valueOf(nativePath));
logEvent(42);
crashJVM();
System.out.println("...shouldn't reach here");
} else {
generateAndVerifyCrashLogContents();
}
}

public static void generateAndVerifyCrashLogContents() throws Exception {
String nativePathSetting = "-Dtest.nativepath=" + System.getProperty("test.nativepath");
ArrayList<String> opts = new ArrayList<>();
opts.add("-XX:-CreateCoredumpOnCrash");
opts.add("-XX:+ErrorFileToStdout");
opts.add("--enable-native-access=ALL-UNNAMED");
opts.add(nativePathSetting);
opts.add(LogEventTest.class.getName());
opts.add("--test");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(opts);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.outputTo(System.out);
output.shouldContain("LogEventTest: unique log message");
output.shouldContain("LogEventTest: 42");
}

private static String toPlatformLibraryName(String name) {
return (Platform.isWindows() ? "" : "lib") + name + "." + Platform.sharedLibraryExt();
}

static native void logEvent(int arg);
static native void crashJVM();
}
35 changes: 35 additions & 0 deletions test/jdk/jb/LogEvent/libLogEventTest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2024 JetBrains s.r.o.
* 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 <jni_util.h>

JNIEXPORT void JNICALL
Java_LogEventTest_logEvent(JNIEnv* env, jclass cls, jint arg) {
JNU_LOG_EVENT(env, "LogEventTest: unique log message");
JNU_LOG_EVENT(env, "LogEventTest: %d", arg);
}

JNIEXPORT void JNICALL
Java_LogEventTest_crashJVM(JNIEnv* env, jclass cls) {
JNU_RUNTIME_ASSERT(env, 0 == 1, "Java_LogEventTest_crashJVM: unique message");
}

0 comments on commit 31a2128

Please sign in to comment.