Skip to content

Commit 20e0055

Browse files
committed
8344165: Trace exceptions with a complete call-stack
Reviewed-by: coleenp, dholmes
1 parent 83fe688 commit 20e0055

File tree

5 files changed

+117
-18
lines changed

5 files changed

+117
-18
lines changed

src/hotspot/share/interpreter/interpreterRuntime.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ JRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThrea
500500
h_method->print_value_string(), current_bci, p2i(current), current->name());
501501
Exceptions::log_exception(h_exception, tempst.as_string());
502502
}
503+
if (log_is_enabled(Info, exceptions, stacktrace)) {
504+
Exceptions::log_exception_stacktrace(h_exception, h_method, current_bci);
505+
}
506+
503507
// Don't go paging in something which won't be used.
504508
// else if (extable->length() == 0) {
505509
// // disabled for now - interpreter is not using shortcut yet

src/hotspot/share/utilities/exceptions.cpp

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,17 @@ bool Exceptions::special_exception(JavaThread* thread, const char* file, int lin
114114
#endif // ASSERT
115115

116116
if (h_exception.is_null() && !thread->can_call_java()) {
117-
ResourceMark rm(thread);
118-
const char* exc_value = h_name != nullptr ? h_name->as_C_string() : "null";
119-
log_info(exceptions)("Thread cannot call Java so instead of throwing exception <%.*s%s%.*s> (" PTR_FORMAT ") \n"
120-
"at [%s, line %d]\nfor thread " PTR_FORMAT ",\n"
121-
"throwing pre-allocated exception: %s",
122-
MAX_LEN, exc_value, message ? ": " : "",
123-
MAX_LEN, message ? message : "",
124-
p2i(h_exception()), file, line, p2i(thread),
125-
Universe::vm_exception()->print_value_string());
117+
if (log_is_enabled(Info, exceptions)) {
118+
ResourceMark rm(thread);
119+
const char* exc_value = h_name != nullptr ? h_name->as_C_string() : "null";
120+
log_info(exceptions)("Thread cannot call Java so instead of throwing exception <%.*s%s%.*s> (" PTR_FORMAT ") \n"
121+
"at [%s, line %d]\nfor thread " PTR_FORMAT ",\n"
122+
"throwing pre-allocated exception: %s",
123+
MAX_LEN, exc_value, message ? ": " : "",
124+
MAX_LEN, message ? message : "",
125+
p2i(h_exception()), file, line, p2i(thread),
126+
Universe::vm_exception()->print_value_string());
127+
}
126128
// We do not care what kind of exception we get for a thread which
127129
// is compiling. We just install a dummy exception object
128130
thread->set_pending_exception(Universe::vm_exception(), file, line);
@@ -152,6 +154,9 @@ void Exceptions::_throw(JavaThread* thread, const char* file, int line, Handle h
152154
message ? ": " : "",
153155
MAX_LEN, message ? message : "",
154156
p2i(h_exception()), file, line, p2i(thread));
157+
if (log_is_enabled(Info, exceptions, stacktrace)) {
158+
log_exception_stacktrace(h_exception);
159+
}
155160

156161
// for AbortVMOnException flag
157162
Exceptions::debug_check_abort(h_exception, message);
@@ -609,3 +614,42 @@ void Exceptions::log_exception(Handle exception, const char* message) {
609614
MAX_LEN, message);
610615
}
611616
}
617+
618+
// This is called from InterpreterRuntime::exception_handler_for_exception(), which is the only
619+
// easy way to be notified in the VM that an _athrow bytecode has been executed. (The alternative
620+
// would be to add hooks into the interpreter and compiler, for all platforms ...).
621+
//
622+
// Unfortunately, InterpreterRuntime::exception_handler_for_exception() is called for every level
623+
// of the Java stack when looking for an exception handler. To avoid excessive output,
624+
// we print the stack only when the bci points to an _athrow bytecode.
625+
//
626+
// NOTE: exceptions that are NOT thrown by _athrow are handled by Exceptions::special_exception()
627+
// and Exceptions::_throw()).
628+
void Exceptions::log_exception_stacktrace(Handle exception, methodHandle method, int bci) {
629+
if (!method->is_native() && (Bytecodes::Code) *method->bcp_from(bci) == Bytecodes::_athrow) {
630+
// TODO: try to find a way to avoid repeated stacktraces when an exception gets re-thrown
631+
// by a finally block
632+
log_exception_stacktrace(exception);
633+
}
634+
}
635+
636+
// This should be called only from a live Java thread.
637+
void Exceptions::log_exception_stacktrace(Handle exception) {
638+
LogStreamHandle(Info, exceptions, stacktrace) st;
639+
ResourceMark rm;
640+
const char* detail_message = java_lang_Throwable::message_as_utf8(exception());
641+
if (detail_message != nullptr) {
642+
st.print_cr("Exception <%.*s: %.*s>",
643+
MAX_LEN, exception->print_value_string(),
644+
MAX_LEN, detail_message);
645+
} else {
646+
st.print_cr("Exception <%.*s>",
647+
MAX_LEN, exception->print_value_string());
648+
}
649+
JavaThread* t = JavaThread::current();
650+
if (t->has_last_Java_frame()) {
651+
t->print_active_stack_on(&st);
652+
} else {
653+
st.print_cr("(Cannot print stracktrace)");
654+
}
655+
}

src/hotspot/share/utilities/exceptions.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -190,6 +190,8 @@ class Exceptions {
190190

191191
// for logging exceptions
192192
static void log_exception(Handle exception, const char* message);
193+
static void log_exception_stacktrace(Handle exception);
194+
static void log_exception_stacktrace(Handle exception, methodHandle method, int bci);
193195
};
194196

195197

test/hotspot/jtreg/runtime/logging/ExceptionsTest.java

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
2323

2424
/*
2525
* @test
26-
* @bug 8141211 8147477
26+
* @bug 8141211 8147477 8358080
2727
* @summary exceptions=info output should have an exception message for interpreter methods
2828
* @requires vm.flagless
2929
* @library /test/lib
@@ -34,6 +34,8 @@
3434

3535
import java.io.File;
3636
import java.util.Map;
37+
import java.util.regex.Matcher;
38+
import java.util.regex.Pattern;
3739
import jdk.test.lib.process.OutputAnalyzer;
3840
import jdk.test.lib.process.ProcessTools;
3941

@@ -44,10 +46,24 @@ static void updateEnvironment(ProcessBuilder pb, String environmentVariable, Str
4446
}
4547

4648
static void analyzeOutputOn(ProcessBuilder pb) throws Exception {
47-
OutputAnalyzer output = new OutputAnalyzer(pb.start());
49+
OutputAnalyzer output = ProcessTools.executeProcess(pb);
4850
output.shouldContain("<a 'java/lang/RuntimeException'").shouldContain(": Test exception 1 for logging>");
4951
output.shouldContain(" thrown in interpreter method ");
50-
output.shouldHaveExitValue(0);
52+
output.shouldMatch("info..exceptions,stacktrace.*at ExceptionsTest[$]InternalClass.bar[(]ExceptionsTest.java:[0-9]+" +
53+
".*\n.*" +
54+
"info..exceptions,stacktrace.*at ExceptionsTest[$]InternalClass.foo[(]ExceptionsTest.java:[0-9]+" +
55+
".*\n.*" +
56+
"info..exceptions,stacktrace.*at ExceptionsTest[$]InternalClass.main[(]ExceptionsTest.java:[0-9]+");
57+
58+
// Note: "(?s)" means that the "." in the regexp can match the newline character.
59+
// To avoid verbosity, stack trace for bar2()->baz2() should be printed only once:
60+
// - It should be printed when the exception is thrown inside bzz2()
61+
// - It should not be printed when the interpreter is looking for an exception handler inside bar2()
62+
output.shouldMatch("(?s)baz2.*bar2");
63+
output.shouldNotMatch("(?s)baz2.*bar2,*baz2.*bar2");
64+
65+
// Two stack traces should include main()->foo2(), as an exception is thrown at two different BCIs in bar2().
66+
output.shouldMatch("(?s)foo2.*main.*foo2.*main");
5167
}
5268

5369
static void analyzeOutputOff(ProcessBuilder pb) throws Exception {
@@ -57,7 +73,7 @@ static void analyzeOutputOff(ProcessBuilder pb) throws Exception {
5773
}
5874

5975
public static void main(String[] args) throws Exception {
60-
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:exceptions=info",
76+
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:exceptions,exceptions+stacktrace",
6177
InternalClass.class.getName());
6278
analyzeOutputOn(pb);
6379

@@ -66,7 +82,7 @@ public static void main(String[] args) throws Exception {
6682
analyzeOutputOff(pb);
6783

6884
pb = ProcessTools.createLimitedTestJavaProcessBuilder(InternalClass.class.getName());
69-
updateEnvironment(pb, "_JAVA_OPTIONS", "-Xlog:exceptions=info");
85+
updateEnvironment(pb, "_JAVA_OPTIONS", "-Xlog:exceptions,exceptions+stacktrace");
7086
analyzeOutputOn(pb);
7187

7288
pb = ProcessTools.createLimitedTestJavaProcessBuilder(InternalClass.class.getName());
@@ -80,12 +96,45 @@ public static void main(String[] args) throws Exception {
8096
}
8197

8298
public static class InternalClass {
83-
public static void main(String[] args) throws Exception {
99+
public static void main(String[] args) {
100+
foo();
101+
foo2();
102+
}
103+
104+
static void foo() {
105+
bar();
106+
}
107+
108+
static void bar() {
84109
try {
85110
throw new RuntimeException("Test exception 1 for logging");
86111
} catch (Exception e) {
87112
System.out.println("Exception 1 caught.");
88113
}
89114
}
115+
116+
static void foo2() {
117+
try {
118+
bar2();
119+
} catch (Exception e) {
120+
System.out.println("Exception 2 caught.");
121+
}
122+
}
123+
124+
static void bar2() {
125+
try {
126+
baz2();
127+
} catch (RuntimeException e) {
128+
throw e; // Rethrow -- should print a new callstack.
129+
}
130+
}
131+
132+
static void baz2() {
133+
bzz2();
134+
}
135+
136+
static void bzz2() {
137+
throw new RuntimeException("Test exception 2 for logging");
138+
}
90139
}
91140
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
-Xlog:exceptions=info
1+
-Xlog:exceptions,exceptions+stacktrace

0 commit comments

Comments
 (0)