New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8274986: max code printed in hs-err logs should be configurable #5875
Changes from all commits
077b16d
e15e855
70e344f
83e6abd
aecc2c0
29060fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -34,6 +34,7 @@ | ||
#include "gc/shared/referenceProcessor.hpp" | ||
#include "oops/markWord.hpp" | ||
#include "runtime/task.hpp" | ||
#include "utilities/vmError.hpp" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the definition of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm guessing there is some macro weirdness involved when defining the flag in globals.hpp. |
||
|
||
//---------------------------------------------------------------------- | ||
// Build flagLimitTable[] | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -245,7 +245,8 @@ void VMError::print_stack_trace(outputStream* st, JavaThread* jt, | ||
/** | ||
* Adds `value` to `list` iff it's not already present and there is sufficient | ||
* capacity (i.e. length(list) < `list_capacity`). The length of the list | ||
* is the index of the first nullptr entry. | ||
* is the index of the first nullptr entry or `list_capacity` if there are | ||
* no nullptr entries. | ||
* | ||
* @ return true if the value was added, false otherwise | ||
*/ | ||
@@ -910,25 +911,44 @@ void VMError::report(outputStream* st, bool _verbose) { | ||
STEP("printing code blobs if possible") | ||
|
||
if (_verbose && _context) { | ||
if (!_print_native_stack_used) { | ||
// Only try print code of the crashing frame since | ||
// we cannot walk the native stack using next_frame. | ||
const int printed_capacity = 1; | ||
address printed_singleton = nullptr; | ||
address* printed = &printed_singleton; | ||
print_code(st, _thread, _pc, true, printed, 1); | ||
} else { | ||
// Print up to the first 5 unique code units on the stack | ||
const int printed_capacity = 5; | ||
address printed[printed_capacity]; | ||
printed[0] = nullptr; // length(list) == index of first nullptr | ||
|
||
frame fr = os::fetch_frame_from_context(_context); | ||
for (int count = 0; count < printed_capacity && fr.pc() != nullptr; ) { | ||
if (print_code(st, _thread, fr.pc(), fr.pc() == _pc, printed, printed_capacity)) { | ||
count++; | ||
const int printed_capacity = max_error_log_print_code; | ||
address printed[printed_capacity]; | ||
printed[0] = nullptr; | ||
int printed_len = 0; | ||
// Even though ErrorLogPrintCodeLimit is ranged checked | ||
// during argument parsing, there's no way to prevent it | ||
// subsequently (i.e., after parsing) being set to a | ||
// value outside the range. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is overly defensive IMO. Flags should never be touched after initialization is complete and we assume we can trust ourselves. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general I agree but in error reporting code I get extra defensive plus the defensive code is small and not on a hot path. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought the point of MIN2 here is to handle ErrorLogPrintCodeLimit < max. IMHO ErrorLogPrintCodeLimit > max would be just an assert-worthy error, since as David wrote flag values should not be changed after initialization. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The point is to ensure that we don't run off the end of the stack allocated There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Recursive asserts would be caught by secondary error handling and show up as "Error occurred during error reporting" printout. Not ideal, but at least won't endanger the rest of the printing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but in a production scenario (which is where robust error reporting is critical), the assert is ignored and we end up with potential buffer overflow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be a guarantee then. |
||
int limit = MIN2(ErrorLogPrintCodeLimit, printed_capacity); | ||
if (limit > 0) { | ||
// Scan the native stack | ||
if (!_print_native_stack_used) { | ||
// Only try to print code of the crashing frame since | ||
// the native stack cannot be walked with next_frame. | ||
if (print_code(st, _thread, _pc, true, printed, printed_capacity)) { | ||
printed_len++; | ||
} | ||
} else { | ||
frame fr = os::fetch_frame_from_context(_context); | ||
while (printed_len < limit && fr.pc() != nullptr) { | ||
if (print_code(st, _thread, fr.pc(), fr.pc() == _pc, printed, printed_capacity)) { | ||
printed_len++; | ||
} | ||
fr = next_frame(fr, _thread); | ||
} | ||
} | ||
|
||
// Scan the Java stack | ||
if (_thread != nullptr && _thread->is_Java_thread()) { | ||
JavaThread* jt = JavaThread::cast(_thread); | ||
if (jt->has_last_Java_frame()) { | ||
for (StackFrameStream sfs(jt, true /* update */, true /* process_frames */); printed_len < limit && !sfs.is_done(); sfs.next()) { | ||
address pc = sfs.current()->pc(); | ||
if (print_code(st, _thread, pc, pc == _pc, printed, printed_capacity)) { | ||
printed_len++; | ||
} | ||
} | ||
} | ||
fr = next_frame(fr, _thread); | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be something like this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. As far as I can see, the intent is to have a single line of output for an nmethod followed by an optional " for ..." suffix when
verbose == true
.The
nm->print_nmethod(verbose)
call then prints extra multi-line detail for the nmethod with the number of lines printed governed byverbose
.This code seems like it went from being hard coded to always go to
tty
and was then parameterized to go to an arbitrary stream but the evolution accidentally overlooked some code that still goes totty
.I don't want to make extensive changes here as there really should be a single effort to rationalize all dumping to ensure it's parameterized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree you might not want to bite this off in this PR, but this piece of code is reason you commonly see random nmethods appearing on the tty just before a crash. verbose is only ever true when called from findpc in debug.cpp. All the other non-verbose work print_nmethod does is useless, like writing to the xtty, and otherwise boils down to nm->print_on(tty). But all these printing paths could use a rework so I'm fine if you skip it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you convinced me in a side conversation, your suggestion has the advantage that we now at least get this output in a hs_err file instead of loosing it altogether.