Skip to content
This repository has been archived by the owner on May 29, 2024. It is now read-only.

Commit

Permalink
[GR-33902] Dump abstract disassembly in hs-err log for top N methods …
Browse files Browse the repository at this point in the history
…on stack.

PullRequest: labsjdk-ce-17/10
  • Loading branch information
dougxc committed Oct 12, 2021
2 parents 85c8df7 + c89bb0c commit 133a24a
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 49 deletions.
7 changes: 6 additions & 1 deletion src/hotspot/share/code/codeBlob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,12 @@ void CodeBlob::dump_for_addr(address addr, outputStream* st, bool verbose) const
nm->method()->print_value_on(st);
}
st->cr();
nm->print_nmethod(verbose);
if (verbose && st == tty) {
// verbose is only ever true when called from findpc in debug.cpp
nm->print_nmethod(true);
} else {
nm->print(st);
}
return;
}
st->print_cr(INTPTR_FORMAT " is at code_begin+%d in ", p2i(addr), (int)(addr - code_begin()));
Expand Down
11 changes: 9 additions & 2 deletions src/hotspot/share/code/nmethod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include "runtime/sharedRuntime.hpp"
#include "runtime/signature.hpp"
#include "runtime/sweeper.hpp"
#include "runtime/threadWXSetters.inline.hpp"
#include "runtime/vmThread.hpp"
#include "utilities/align.hpp"
#include "utilities/copy.hpp"
Expand Down Expand Up @@ -923,7 +924,7 @@ void nmethod::print_on(outputStream* st, const char* msg) const {
CompileTask::print(st, this, msg, /*short_form:*/ true);
st->print_cr(" (" INTPTR_FORMAT ")", p2i(this));
} else {
CompileTask::print(st, this, msg, /*short_form:*/ false);
CompileTask::print(st, this, msg, /*short_form:*/ false, /* cr */ true, /* timestamp */ false);
}
}
}
Expand Down Expand Up @@ -2518,7 +2519,7 @@ void nmethod::print(outputStream* st) const {
st->print("(n/a) ");
}

print_on(tty, NULL);
print_on(st, NULL);

if (WizardMode) {
st->print("((nmethod*) " INTPTR_FORMAT ") ", p2i(this));
Expand Down Expand Up @@ -2869,6 +2870,9 @@ void nmethod::decode2(outputStream* ost) const {
AbstractDisassembler::show_block_comment());
#endif

// Decoding an nmethod can write to a PcDescCache (see PcDescCache::add_pc_desc)
MACOS_AARCH64_ONLY(ThreadWXEnable wx(WXWrite, Thread::current());)

st->cr();
this->print(st);
st->cr();
Expand All @@ -2878,7 +2882,10 @@ void nmethod::decode2(outputStream* ost) const {
//---< Print real disassembly >---
//----------------------------------
if (! use_compressed_format) {
st->print_cr("[Disassembly]");
Disassembler::decode(const_cast<nmethod*>(this), st);
st->bol();
st->print_cr("[/Disassembly]");
return;
}
#endif
Expand Down
8 changes: 5 additions & 3 deletions src/hotspot/share/compiler/compileTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,13 @@ void CompileTask::print_tty() {
// CompileTask::print_impl
void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, int comp_level,
bool is_osr_method, int osr_bci, bool is_blocking,
const char* msg, bool short_form, bool cr,
const char* msg, bool short_form, bool cr, bool timestamp,
jlong time_queued, jlong time_started) {
if (!short_form) {
// Print current time
st->print("%7d ", (int)tty->time_stamp().milliseconds());
if (timestamp) {
// Print current time
st->print("%7d ", (int)tty->time_stamp().milliseconds());
}
if (Verbose && time_queued != 0) {
// Print time in queue and time being processed by compiler thread
jlong now = os::elapsed_counter();
Expand Down
6 changes: 3 additions & 3 deletions src/hotspot/share/compiler/compileTask.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,16 +187,16 @@ class CompileTask : public CHeapObj<mtCompiler> {
private:
static void print_impl(outputStream* st, Method* method, int compile_id, int comp_level,
bool is_osr_method = false, int osr_bci = -1, bool is_blocking = false,
const char* msg = NULL, bool short_form = false, bool cr = true,
const char* msg = NULL, bool short_form = false, bool cr = true, bool timestamp = true,
jlong time_queued = 0, jlong time_started = 0);

public:
void print(outputStream* st = tty, const char* msg = NULL, bool short_form = false, bool cr = true);
void print_ul(const char* msg = NULL);
static void print(outputStream* st, const nmethod* nm, const char* msg = NULL, bool short_form = false, bool cr = true) {
static void print(outputStream* st, const nmethod* nm, const char* msg = NULL, bool short_form = false, bool cr = true, bool timestamp = true) {
print_impl(st, nm->method(), nm->compile_id(), nm->comp_level(),
nm->is_osr_method(), nm->is_osr_method() ? nm->osr_entry_bci() : -1, /*is_blocking*/ false,
msg, short_form, cr);
msg, short_form, cr, timestamp);
}
static void print_ul(const nmethod* nm, const char* msg = NULL);

Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/compiler/disassembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ void Disassembler::decode(CodeBlob* cb, outputStream* st) {
if (cb->is_nmethod()) {
// If we have an nmethod at hand,
// call the specialized decoder directly.
decode((nmethod*)cb, st);
((nmethod*)cb)->decode2(st);
return;
}

Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/runtime/flags/jvmFlagLimit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "gc/shared/referenceProcessor.hpp"
#include "oops/markWord.hpp"
#include "runtime/task.hpp"
#include "utilities/vmError.hpp"

//----------------------------------------------------------------------
// Build flagLimitTable[]
Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/share/runtime/globals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,10 @@ const intx ObjectAlignmentInBytes = 8;
develop(intx, StackPrintLimit, 100, \
"number of stack frames to print in VM-level stack dump") \
\
product(int, ErrorLogPrintCodeLimit, 3, DIAGNOSTIC, \
"max number of compiled code units to print in error log") \
range(0, VMError::max_error_log_print_code) \
\
notproduct(intx, MaxElementPrintSize, 256, \
"maximum number of elements to print") \
\
Expand Down
182 changes: 143 additions & 39 deletions src/hotspot/share/utilities/vmError.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Thread* VMError::_thread;
address VMError::_pc;
void* VMError::_siginfo;
void* VMError::_context;
bool VMError::_print_native_stack_used = false;
const char* VMError::_filename;
int VMError::_lineno;
size_t VMError::_size;
Expand Down Expand Up @@ -241,6 +242,108 @@ void VMError::print_stack_trace(outputStream* st, JavaThread* jt,
#endif // ZERO
}

/**
* 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 or `list_capacity` if there are
* no nullptr entries.
*
* @ return true if the value was added, false otherwise
*/
static bool add_if_absent(address value, address* list, int list_capacity) {
for (int i = 0; i < list_capacity; i++) {
if (list[i] == value) {
return false;
}
if (list[i] == nullptr) {
list[i] = value;
if (i + 1 < list_capacity) {
list[i + 1] = nullptr;
}
return true;
}
}
return false;
}

/**
* Prints the VM generated code unit, if any, containing `pc` if it has not already
* been printed. If the code unit is an InterpreterCodelet or StubCodeDesc, it is
* only printed if `is_crash_pc` is true.
*
* @param printed array of code units that have already been printed (delimited by NULL entry)
* @param printed_capacity the capacity of `printed`
* @return true if the code unit was printed, false otherwise
*/
static bool print_code(outputStream* st, Thread* thread, address pc, bool is_crash_pc,
address* printed, int printed_capacity) {
if (Interpreter::contains(pc)) {
if (is_crash_pc) {
// The interpreter CodeBlob is very large so try to print the codelet instead.
InterpreterCodelet* codelet = Interpreter::codelet_containing(pc);
if (codelet != nullptr) {
if (add_if_absent((address) codelet, printed, printed_capacity)) {
codelet->print_on(st);
Disassembler::decode(codelet->code_begin(), codelet->code_end(), st);
return true;
}
}
}
} else {
StubCodeDesc* desc = StubCodeDesc::desc_for(pc);
if (desc != nullptr) {
if (is_crash_pc) {
if (add_if_absent((address) desc, printed, printed_capacity)) {
desc->print_on(st);
Disassembler::decode(desc->begin(), desc->end(), st);
return true;
}
}
} else if (thread != nullptr) {
CodeBlob* cb = CodeCache::find_blob(pc);
if (cb != nullptr && add_if_absent((address) cb, printed, printed_capacity)) {
// Disassembling nmethod will incur resource memory allocation,
// only do so when thread is valid.
ResourceMark rm(thread);
Disassembler::decode(cb, st);
st->cr();
return true;
}
}
}
return false;
}

/**
* Gets the caller frame of `fr`.
*
* @returns an invalid frame (i.e. fr.pc() === 0) if the caller cannot be obtained
*/
static frame next_frame(frame fr, Thread* t) {
// Compiled code may use EBP register on x86 so it looks like
// non-walkable C frame. Use frame.sender() for java frames.
frame invalid;
if (t != nullptr && t->is_Java_thread()) {
// Catch very first native frame by using stack address.
// For JavaThread stack_base and stack_size should be set.
if (!t->is_in_full_stack((address)(fr.real_fp() + 1))) {
return invalid;
}
if (fr.is_java_frame() || fr.is_native_frame() || fr.is_runtime_frame()) {
RegisterMap map(t->as_Java_thread(), false); // No update
return fr.sender(&map);
} else {
// is_first_C_frame() does only simple checks for frame pointer,
// it will pass if java compiled code has a pointer in EBP.
if (os::is_first_C_frame(&fr)) return invalid;
return os::get_sender_for_C_frame(&fr);
}
} else {
if (os::is_first_C_frame(&fr)) return invalid;
return os::get_sender_for_C_frame(&fr);
}
}

void VMError::print_native_stack(outputStream* st, frame fr, Thread* t, char* buf, int buf_size) {

// see if it's a valid frame
Expand All @@ -258,26 +361,9 @@ void VMError::print_native_stack(outputStream* st, frame fr, Thread* t, char* bu
}
}
st->cr();
// Compiled code may use EBP register on x86 so it looks like
// non-walkable C frame. Use frame.sender() for java frames.
if (t && t->is_Java_thread()) {
// Catch very first native frame by using stack address.
// For JavaThread stack_base and stack_size should be set.
if (!t->is_in_full_stack((address)(fr.real_fp() + 1))) {
break;
}
if (fr.is_java_frame() || fr.is_native_frame() || fr.is_runtime_frame()) {
RegisterMap map(t->as_Java_thread(), false); // No update
fr = fr.sender(&map);
} else {
// is_first_C_frame() does only simple checks for frame pointer,
// it will pass if java compiled code has a pointer in EBP.
if (os::is_first_C_frame(&fr)) break;
fr = os::get_sender_for_C_frame(&fr);
}
} else {
if (os::is_first_C_frame(&fr)) break;
fr = os::get_sender_for_C_frame(&fr);
fr = next_frame(fr, t);
if (fr.pc() == nullptr) {
break;
}
}

Expand Down Expand Up @@ -747,6 +833,7 @@ void VMError::report(outputStream* st, bool _verbose) {
: os::current_frame();

print_native_stack(st, fr, _thread, buf, sizeof(buf));
_print_native_stack_used = true;
}
}

Expand Down Expand Up @@ -821,29 +908,46 @@ void VMError::report(outputStream* st, bool _verbose) {
st->cr();
}

STEP("printing code blob if possible")
STEP("printing code blobs if possible")

if (_verbose && _context) {
CodeBlob* cb = CodeCache::find_blob(_pc);
if (cb != NULL) {
if (Interpreter::contains(_pc)) {
// The interpreter CodeBlob is very large so try to print the codelet instead.
InterpreterCodelet* codelet = Interpreter::codelet_containing(_pc);
if (codelet != NULL) {
codelet->print_on(st);
Disassembler::decode(codelet->code_begin(), codelet->code_end(), st);
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.
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 {
StubCodeDesc* desc = StubCodeDesc::desc_for(_pc);
if (desc != NULL) {
desc->print_on(st);
Disassembler::decode(desc->begin(), desc->end(), st);
} else if (_thread != NULL) {
// Disassembling nmethod will incur resource memory allocation,
// only do so when thread is valid.
ResourceMark rm(_thread);
Disassembler::decode(cb, st);
st->cr();
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 = _thread->as_Java_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++;
}
}
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/hotspot/share/utilities/vmError.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class VMError : public AllStatic {
static void* _context; // ContextRecord on Windows,
// ucontext_t on Solaris/Linux

// records if VMError::print_native_stack was used to
// print the native stack instead of os::platform_print_native_stack
static bool _print_native_stack_used;

// additional info for VM internal errors
static const char* _filename;
static int _lineno;
Expand Down Expand Up @@ -181,6 +185,9 @@ class VMError : public AllStatic {
// which is not NULL and contains bits in every word.
static const intptr_t segfault_address = LP64_ONLY(0xABC0000000000ABCULL) NOT_LP64(0x00000ABC);

// Max value for the ErrorLogPrintCodeLimit flag.
static const int max_error_log_print_code = 10;

// Needed when printing signal handlers.
NOT_WINDOWS(static const void* crash_handler_address;)

Expand Down
Loading

0 comments on commit 133a24a

Please sign in to comment.