mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 20:18:48 +00:00
825 lines
26 KiB
C++
825 lines
26 KiB
C++
/*
|
|
* Copyright (c) 1997, 2026, 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 "classfile/classPrinter.hpp"
|
|
#include "classfile/systemDictionary.hpp"
|
|
#include "code/codeCache.hpp"
|
|
#include "code/nmethod.hpp"
|
|
#include "code/vtableStubs.hpp"
|
|
#include "compiler/compileBroker.hpp"
|
|
#include "compiler/disassembler.hpp"
|
|
#include "cppstdlib/new.hpp"
|
|
#include "gc/shared/collectedHeap.hpp"
|
|
#include "interpreter/interpreter.hpp"
|
|
#include "jvm.h"
|
|
#include "memory/allocation.hpp"
|
|
#include "memory/resourceArea.hpp"
|
|
#include "memory/universe.hpp"
|
|
#include "nmt/mallocTracker.hpp"
|
|
#include "nmt/memTracker.hpp"
|
|
#include "oops/klass.inline.hpp"
|
|
#include "oops/oop.inline.hpp"
|
|
#include "runtime/atomicAccess.hpp"
|
|
#include "runtime/flags/flagSetting.hpp"
|
|
#include "runtime/frame.inline.hpp"
|
|
#include "runtime/handles.inline.hpp"
|
|
#include "runtime/java.hpp"
|
|
#include "runtime/javaThread.hpp"
|
|
#include "runtime/os.inline.hpp"
|
|
#include "runtime/safefetch.hpp"
|
|
#include "runtime/sharedRuntime.hpp"
|
|
#include "runtime/stubCodeGenerator.hpp"
|
|
#include "runtime/stubRoutines.hpp"
|
|
#include "runtime/threads.hpp"
|
|
#include "runtime/vframe.hpp"
|
|
#include "runtime/vm_version.hpp"
|
|
#include "services/heapDumper.hpp"
|
|
#include "utilities/defaultStream.hpp"
|
|
#include "utilities/events.hpp"
|
|
#include "utilities/formatBuffer.hpp"
|
|
#include "utilities/globalDefinitions.hpp"
|
|
#include "utilities/macros.hpp"
|
|
#include "utilities/nativeStackPrinter.hpp"
|
|
#include "utilities/unsigned5.hpp"
|
|
#include "utilities/vmError.hpp"
|
|
#if INCLUDE_JFR
|
|
#include "jfr/jfr.hpp"
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
// These functions needs to be exported on Windows
|
|
// On Linux it is also beneficial to export them to avoid
|
|
// losing them e.g. with linktime gc
|
|
#define DEBUGEXPORT JNIEXPORT
|
|
|
|
// Support for showing register content on asserts/guarantees.
|
|
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
|
|
static char g_dummy;
|
|
char* g_assert_poison = &g_dummy;
|
|
const char* g_assert_poison_read_only = &g_dummy;
|
|
static intx g_asserting_thread = 0;
|
|
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
|
|
|
|
int DebuggingContext::_enabled = 0; // Initially disabled.
|
|
|
|
DebuggingContext::DebuggingContext() {
|
|
_enabled += 1; // Increase nesting count.
|
|
}
|
|
|
|
DebuggingContext::~DebuggingContext() {
|
|
if (is_enabled()) {
|
|
_enabled -= 1; // Decrease nesting count.
|
|
} else {
|
|
fatal("Debugging nesting confusion");
|
|
}
|
|
}
|
|
|
|
#ifndef ASSERT
|
|
# ifdef _DEBUG
|
|
// NOTE: don't turn the lines below into a comment -- if you're getting
|
|
// a compile error here, change the settings to define ASSERT
|
|
ASSERT should be defined when _DEBUG is defined. It is not intended to be used for debugging
|
|
functions that do not slow down the system too much and thus can be left in optimized code.
|
|
On the other hand, the code should not be included in a production version.
|
|
# endif // _DEBUG
|
|
#endif // ASSERT
|
|
|
|
|
|
#ifdef _DEBUG
|
|
# ifndef ASSERT
|
|
configuration error: ASSERT must be defined in debug version
|
|
# endif // ASSERT
|
|
#endif // _DEBUG
|
|
|
|
|
|
#ifdef PRODUCT
|
|
# if -defined _DEBUG || -defined ASSERT
|
|
configuration error: ASSERT et al. must not be defined in PRODUCT version
|
|
# endif
|
|
#endif // PRODUCT
|
|
|
|
#ifdef ASSERT
|
|
// This is to test that error reporting works if we assert during dynamic
|
|
// initialization of the hotspot. See JDK-8214975.
|
|
struct Crasher {
|
|
Crasher() {
|
|
// Using getenv - no other mechanism would work yet.
|
|
const char* s = ::getenv("HOTSPOT_FATAL_ERROR_DURING_DYNAMIC_INITIALIZATION");
|
|
if (s != nullptr && ::strcmp(s, "1") == 0) {
|
|
fatal("HOTSPOT_FATAL_ERROR_DURING_DYNAMIC_INITIALIZATION");
|
|
}
|
|
}
|
|
};
|
|
static Crasher g_crasher;
|
|
#endif // ASSERT
|
|
|
|
ATTRIBUTE_PRINTF(1, 2)
|
|
void warning(const char* format, ...) {
|
|
if (PrintWarnings) {
|
|
FILE* const err = defaultStream::error_stream();
|
|
jio_fprintf(err, "%s warning: ", VM_Version::vm_name());
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
vfprintf(err, format, ap);
|
|
va_end(ap);
|
|
fputc('\n', err);
|
|
}
|
|
}
|
|
|
|
void report_vm_error(const char* file, int line, const char* error_msg)
|
|
{
|
|
report_vm_error(file, line, error_msg, "%s", "");
|
|
}
|
|
|
|
|
|
static void print_error_for_unit_test(const char* message, const char* detail_fmt, va_list detail_args) {
|
|
if (ExecutingUnitTests) {
|
|
char detail_msg[256];
|
|
if (detail_fmt != nullptr) {
|
|
// Special handling for the sake of gtest death tests which expect the assert
|
|
// message to be printed in one short line to stderr (see TEST_VM_ASSERT_MSG) and
|
|
// cannot be tweaked to accept our normal assert message.
|
|
va_list detail_args_copy;
|
|
va_copy(detail_args_copy, detail_args);
|
|
jio_vsnprintf(detail_msg, sizeof(detail_msg), detail_fmt, detail_args_copy);
|
|
|
|
// the VM assert tests look for "assert failed: "
|
|
if (message == nullptr) {
|
|
fprintf(stderr, "assert failed: %s", detail_msg);
|
|
} else {
|
|
if (strlen(detail_msg) > 0) {
|
|
fprintf(stderr, "assert failed: %s: %s", message, detail_msg);
|
|
} else {
|
|
fprintf(stderr, "assert failed: Error: %s", message);
|
|
}
|
|
}
|
|
::fflush(stderr);
|
|
va_end(detail_args_copy);
|
|
}
|
|
}
|
|
}
|
|
|
|
void report_vm_error(const char* file, int line, const char* error_msg, const char* detail_fmt, ...)
|
|
{
|
|
va_list detail_args;
|
|
va_start(detail_args, detail_fmt);
|
|
|
|
print_error_for_unit_test(error_msg, detail_fmt, detail_args);
|
|
|
|
const void* context = nullptr;
|
|
const void* siginfo = nullptr;
|
|
|
|
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
|
|
if (os::current_thread_id() == g_asserting_thread) {
|
|
context = os::get_saved_assert_context(&siginfo);
|
|
}
|
|
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
|
|
|
|
VMError::report_and_die(INTERNAL_ERROR, error_msg, detail_fmt, detail_args,
|
|
Thread::current_or_null(), nullptr, siginfo, context,
|
|
file, line, 0);
|
|
va_end(detail_args);
|
|
}
|
|
|
|
void report_vm_status_error(const char* file, int line, const char* error_msg,
|
|
int status, const char* detail) {
|
|
report_vm_error(file, line, error_msg, "error %s(%d), %s", os::errno_name(status), status, detail);
|
|
}
|
|
|
|
void report_fatal(VMErrorType error_type, const char* file, int line, const char* detail_fmt, ...) {
|
|
va_list detail_args;
|
|
va_start(detail_args, detail_fmt);
|
|
|
|
|
|
print_error_for_unit_test("fatal error", detail_fmt, detail_args);
|
|
|
|
const void* context = nullptr;
|
|
const void* siginfo = nullptr;
|
|
|
|
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
|
|
if (os::current_thread_id() == g_asserting_thread) {
|
|
context = os::get_saved_assert_context(&siginfo);
|
|
}
|
|
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
|
|
|
|
VMError::report_and_die(error_type, "fatal error", detail_fmt, detail_args,
|
|
Thread::current_or_null(), nullptr, siginfo, context,
|
|
file, line, 0);
|
|
va_end(detail_args);
|
|
}
|
|
|
|
void report_vm_out_of_memory(const char* file, int line, size_t size,
|
|
VMErrorType vm_err_type, const char* detail_fmt, ...) {
|
|
va_list detail_args;
|
|
va_start(detail_args, detail_fmt);
|
|
|
|
print_error_for_unit_test(nullptr, detail_fmt, detail_args);
|
|
|
|
VMError::report_and_die(Thread::current_or_null(), file, line, size, vm_err_type, detail_fmt, detail_args);
|
|
va_end(detail_args);
|
|
|
|
// The UseOSErrorReporting option in report_and_die() may allow a return
|
|
// to here. If so then we'll have to figure out how to handle it.
|
|
guarantee(false, "report_and_die() should not return here");
|
|
}
|
|
|
|
void report_should_not_call(const char* file, int line) {
|
|
report_vm_error(file, line, "ShouldNotCall()");
|
|
}
|
|
|
|
void report_should_not_reach_here(const char* file, int line) {
|
|
report_vm_error(file, line, "ShouldNotReachHere()");
|
|
}
|
|
|
|
void report_unimplemented(const char* file, int line) {
|
|
report_vm_error(file, line, "Unimplemented()");
|
|
}
|
|
|
|
void report_untested(const char* file, int line, const char* message) {
|
|
#ifndef PRODUCT
|
|
warning("Untested: %s in %s: %d\n", message, file, line);
|
|
#endif // !PRODUCT
|
|
}
|
|
|
|
void report_java_out_of_memory(const char* message) {
|
|
static int out_of_memory_reported = 0;
|
|
|
|
JFR_ONLY(Jfr::on_report_java_out_of_memory();)
|
|
|
|
// A number of threads may attempt to report OutOfMemoryError at around the
|
|
// same time. To avoid dumping the heap or executing the data collection
|
|
// commands multiple times we just do it once when the first threads reports
|
|
// the error.
|
|
if (AtomicAccess::cmpxchg(&out_of_memory_reported, 0, 1) == 0) {
|
|
// create heap dump before OnOutOfMemoryError commands are executed
|
|
if (HeapDumpOnOutOfMemoryError) {
|
|
tty->print_cr("java.lang.OutOfMemoryError: %s", message);
|
|
HeapDumper::dump_heap_from_oome();
|
|
}
|
|
|
|
if (OnOutOfMemoryError && OnOutOfMemoryError[0]) {
|
|
VMError::report_java_out_of_memory(message);
|
|
}
|
|
|
|
if (CrashOnOutOfMemoryError) {
|
|
tty->print_cr("Aborting due to java.lang.OutOfMemoryError: %s", message);
|
|
report_fatal(OOM_JAVA_HEAP_FATAL, __FILE__, __LINE__, "OutOfMemory encountered: %s", message);
|
|
}
|
|
|
|
if (ExitOnOutOfMemoryError) {
|
|
tty->print_cr("Terminating due to java.lang.OutOfMemoryError: %s", message);
|
|
os::_exit(3); // quick exit with no cleanup hooks run
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------ helper functions for debugging go here ------------
|
|
|
|
// All debug entries should be wrapped with a stack allocated
|
|
// Command object. It makes sure a resource mark is set and
|
|
// flushes the logfile to prevent file sharing problems.
|
|
|
|
class Command : public StackObj {
|
|
private:
|
|
static int level;
|
|
DebuggingContext _debugging;
|
|
bool _has_rm;
|
|
// Union members of class type are implicitly allocated but not constructed automatically.
|
|
// We therefore have to explicitly construct _rm with a placement new call (see 'onThread()') and
|
|
// clean it up afterwards with an explicit destructor call (see '~Command()').
|
|
union { ResourceMark _rm; };
|
|
public:
|
|
Command(const char* str) : _has_rm(false) {
|
|
if (level++ == 0) {
|
|
tty->cr();
|
|
tty->print_cr("\"Executing %s\"", str);
|
|
}
|
|
tty->flush();
|
|
}
|
|
~Command() {
|
|
if (_has_rm) _rm.~ResourceMark();
|
|
tty->flush();
|
|
level--;
|
|
}
|
|
|
|
bool onThread() {
|
|
Thread* thread = Thread::current_or_null();
|
|
if (thread == nullptr) {
|
|
tty->print_cr("Failed: Current thread is not attached");
|
|
return false;
|
|
}
|
|
|
|
if (!_has_rm) {
|
|
::new (&_rm) ResourceMark();
|
|
_has_rm = true;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
int Command::level = 0;
|
|
|
|
extern "C" DEBUGEXPORT void blob(CodeBlob* cb) {
|
|
Command c("blob");
|
|
cb->print();
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void dump_vtable(address p) {
|
|
Command c("dump_vtable");
|
|
Klass* k = (Klass*)p;
|
|
k->vtable().print();
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void nm(intptr_t p) {
|
|
// Actually we look through all CodeBlobs (the nm name has been kept for backwards compatibility)
|
|
Command c("nm");
|
|
CodeBlob* cb = CodeCache::find_blob((address)p);
|
|
if (cb == nullptr) {
|
|
tty->print_cr("null");
|
|
} else {
|
|
cb->print();
|
|
}
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void disnm(intptr_t p) {
|
|
Command c("disnm");
|
|
CodeBlob* cb = CodeCache::find_blob((address) p);
|
|
if (cb != nullptr) {
|
|
nmethod* nm = cb->as_nmethod_or_null();
|
|
if (nm != nullptr) {
|
|
nm->print();
|
|
} else {
|
|
cb->print();
|
|
}
|
|
Disassembler::decode(cb);
|
|
}
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void printnm(intptr_t p) {
|
|
char buffer[256];
|
|
os::snprintf_checked(buffer, sizeof(buffer), "printnm: " INTPTR_FORMAT, p);
|
|
Command c(buffer);
|
|
CodeBlob* cb = CodeCache::find_blob((address) p);
|
|
if (cb != nullptr && cb->is_nmethod()) {
|
|
nmethod* nm = (nmethod*)cb;
|
|
nm->print_nmethod(true);
|
|
} else {
|
|
tty->print_cr("Invalid address");
|
|
}
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void universe() {
|
|
Command c("universe");
|
|
if (!c.onThread()) return;
|
|
Universe::print_on(tty);
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void verify() {
|
|
// try to run a verify on the entire system
|
|
// note: this may not be safe if we're not at a safepoint; for debugging,
|
|
// this manipulates the safepoint settings to avoid assertion failures
|
|
Command c("universe verify");
|
|
if (!c.onThread()) return;
|
|
bool safe = SafepointSynchronize::is_at_safepoint();
|
|
if (!safe) {
|
|
tty->print_cr("warning: not at safepoint -- verify may fail");
|
|
SafepointSynchronize::set_is_at_safepoint();
|
|
}
|
|
// Ensure Eden top is correct before verification
|
|
Universe::heap()->prepare_for_verify();
|
|
Universe::verify();
|
|
if (!safe) SafepointSynchronize::set_is_not_at_safepoint();
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void pp(void* p) {
|
|
Command c("pp");
|
|
if (!c.onThread()) return;
|
|
FlagSetting fl(DisplayVMOutput, true);
|
|
if (p == nullptr) {
|
|
tty->print_cr("null");
|
|
return;
|
|
}
|
|
if (Universe::heap()->is_in(p)) {
|
|
oop obj = cast_to_oop(p);
|
|
obj->print();
|
|
} else {
|
|
// Ask NMT about this pointer.
|
|
// GDB note: We will be using SafeFetch to access the supposed malloc header. If the address is
|
|
// not readable, this will generate a signal. That signal will trip up the debugger: gdb will
|
|
// catch the signal and disable the pp() command for further use.
|
|
// In order to avoid that, switch off SIGSEGV handling with "handle SIGSEGV nostop" before
|
|
// invoking pp()
|
|
if (MemTracker::print_containing_region(p, tty)) {
|
|
return;
|
|
}
|
|
tty->print_cr(PTR_FORMAT, p2i(p));
|
|
}
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void ps() { // print stack
|
|
// Prints the stack of the current Java thread
|
|
Command c("ps");
|
|
if (!c.onThread()) return;
|
|
JavaThread* p = JavaThread::active();
|
|
if (p == nullptr) {
|
|
tty->print_cr("Failed: JavaThread::active is null");
|
|
return;
|
|
}
|
|
tty->print(" for thread: ");
|
|
p->print();
|
|
tty->cr();
|
|
|
|
if (p->has_last_Java_frame()) {
|
|
// If the last_Java_fp is set we are in C land and
|
|
// can call the standard stack_trace function.
|
|
p->print_stack();
|
|
#ifndef PRODUCT
|
|
if (Verbose) p->trace_stack();
|
|
} else {
|
|
frame f = os::current_frame();
|
|
RegisterMap reg_map(p,
|
|
RegisterMap::UpdateMap::include,
|
|
RegisterMap::ProcessFrames::include,
|
|
RegisterMap::WalkContinuation::skip);
|
|
f = f.sender(®_map);
|
|
tty->print("(guessing starting frame id=" PTR_FORMAT " based on current fp)\n", p2i(f.id()));
|
|
p->trace_stack_from(vframe::new_vframe(&f, ®_map, p));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
extern "C" DEBUGEXPORT void pfl() {
|
|
// print frame layout
|
|
Command c("pfl");
|
|
if (!c.onThread()) return;
|
|
JavaThread* p = JavaThread::active();
|
|
if (p == nullptr) {
|
|
tty->print_cr("Failed: JavaThread::active is null");
|
|
return;
|
|
}
|
|
tty->print(" for thread: ");
|
|
p->print();
|
|
tty->cr();
|
|
if (p->has_last_Java_frame()) {
|
|
p->print_frame_layout();
|
|
}
|
|
}
|
|
|
|
extern "C" DEBUGEXPORT void psf() { // print stack frames
|
|
Command c("psf");
|
|
if (!c.onThread()) return;
|
|
JavaThread* p = JavaThread::active();
|
|
if (p == nullptr) {
|
|
tty->print_cr("Failed: JavaThread::active is null");
|
|
return;
|
|
}
|
|
tty->print(" for thread: ");
|
|
p->print();
|
|
tty->cr();
|
|
if (p->has_last_Java_frame()) {
|
|
p->trace_frames();
|
|
}
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void threads() {
|
|
Command c("threads");
|
|
if (!c.onThread()) return;
|
|
Threads::print(false, true);
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void psd() {
|
|
Command c("psd");
|
|
if (!c.onThread()) return;
|
|
SystemDictionary::print();
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void pss() { // print all stacks
|
|
Command c("pss");
|
|
if (!c.onThread()) return;
|
|
Threads::print(true, PRODUCT_ONLY(false) NOT_PRODUCT(true));
|
|
}
|
|
|
|
// #ifndef PRODUCT
|
|
|
|
extern "C" DEBUGEXPORT void debug() { // to set things up for compiler debugging
|
|
Command c("debug");
|
|
NOT_PRODUCT(WizardMode = true;)
|
|
PrintCompilation = true;
|
|
PrintInlining = PrintAssembly = true;
|
|
tty->flush();
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void ndebug() { // undo debug()
|
|
Command c("ndebug");
|
|
PrintCompilation = false;
|
|
PrintInlining = PrintAssembly = false;
|
|
tty->flush();
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void flush() {
|
|
Command c("flush");
|
|
tty->flush();
|
|
}
|
|
|
|
extern "C" DEBUGEXPORT void events() {
|
|
Command c("events");
|
|
Events::print();
|
|
}
|
|
|
|
extern "C" DEBUGEXPORT Method* findm(intptr_t pc) {
|
|
Command c("findm");
|
|
nmethod* nm = CodeCache::find_nmethod((address)pc);
|
|
return (nm == nullptr) ? (Method*)nullptr : nm->method();
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT nmethod* findnm(intptr_t addr) {
|
|
Command c("findnm");
|
|
return CodeCache::find_nmethod((address)addr);
|
|
}
|
|
|
|
extern "C" DEBUGEXPORT void find(intptr_t x) {
|
|
Command c("find");
|
|
if (!c.onThread()) return;
|
|
os::print_location(tty, x, false);
|
|
}
|
|
|
|
|
|
extern "C" DEBUGEXPORT void findpc(intptr_t x) {
|
|
Command c("findpc");
|
|
if (!c.onThread()) return;
|
|
os::print_location(tty, x, true);
|
|
}
|
|
|
|
// For findmethod() and findclass():
|
|
// See comments in classPrinter.hpp about the meanings of class_name_pattern, method_pattern and flags.
|
|
// Examples (in gdb):
|
|
// call findclass("java/lang/Object", 0x3) -> find j.l.Object and disasm all of its methods
|
|
// call findmethod("*ang/Object*", "wait", 0xff) -> detailed disasm of all "wait" methods in j.l.Object
|
|
// call findmethod("*ang/Object*", "wait:(*J*)V", 0x1) -> list all "wait" methods in j.l.Object that have a long parameter
|
|
extern "C" DEBUGEXPORT void findclass(const char* class_name_pattern, int flags) {
|
|
Command c("findclass");
|
|
if (!c.onThread()) return;
|
|
ClassPrinter::print_flags_help(tty);
|
|
ClassPrinter::print_classes(class_name_pattern, flags, tty);
|
|
}
|
|
|
|
extern "C" DEBUGEXPORT void findmethod(const char* class_name_pattern,
|
|
const char* method_pattern, int flags) {
|
|
Command c("findmethod");
|
|
if (!c.onThread()) return;
|
|
ClassPrinter::print_flags_help(tty);
|
|
ClassPrinter::print_methods(class_name_pattern, method_pattern, flags, tty);
|
|
}
|
|
|
|
// Need method pointer to find bcp
|
|
extern "C" DEBUGEXPORT void findbcp(intptr_t method, intptr_t bcp) {
|
|
Command c("findbcp");
|
|
Method* mh = (Method*)method;
|
|
if (!mh->is_native()) {
|
|
tty->print_cr("bci_from(%p) = %d; print_codes():",
|
|
mh, mh->bci_from(address(bcp)));
|
|
mh->print_codes_on(tty);
|
|
}
|
|
}
|
|
|
|
// check and decode a single u5 value
|
|
extern "C" DEBUGEXPORT u4 u5decode(intptr_t addr) {
|
|
Command c("u5decode");
|
|
u1* arr = (u1*)addr;
|
|
size_t off = 0, lim = 5;
|
|
if (!UNSIGNED5::check_length(arr, off, lim)) {
|
|
return 0;
|
|
}
|
|
return UNSIGNED5::read_uint(arr, off, lim);
|
|
}
|
|
|
|
// Sets up a Reader from addr/limit and prints count items.
|
|
// A limit of zero means no set limit; stop at the first null
|
|
// or after count items are printed.
|
|
// A count of zero or less is converted to -1, which means
|
|
// there is no limit on the count of items printed; the
|
|
// printing stops when an null is printed or at limit.
|
|
// See documentation for UNSIGNED5::Reader::print(count).
|
|
extern "C" DEBUGEXPORT intptr_t u5p(intptr_t addr,
|
|
intptr_t limit,
|
|
int count) {
|
|
Command c("u5p");
|
|
u1* arr = (u1*)addr;
|
|
if (limit && limit < addr) limit = addr;
|
|
size_t lim = !limit ? 0 : (limit - addr);
|
|
size_t endpos = UNSIGNED5::print_count(count > 0 ? count : -1,
|
|
arr, (size_t)0, lim);
|
|
return addr + endpos;
|
|
}
|
|
|
|
|
|
// int versions of all methods to avoid having to type type casts in the debugger
|
|
|
|
void pp(intptr_t p) { pp((void*)p); }
|
|
void pp(oop p) { pp((void*)p); }
|
|
|
|
extern "C" DEBUGEXPORT void help() {
|
|
Command c("help");
|
|
tty->print_cr("basic");
|
|
tty->print_cr(" pp(void* p) - try to make sense of p");
|
|
tty->print_cr(" ps() - print current thread stack");
|
|
tty->print_cr(" pss() - print all thread stacks");
|
|
tty->print_cr(" findnm(intptr_t pc) - find nmethod*");
|
|
tty->print_cr(" findm(intptr_t pc) - find Method*");
|
|
tty->print_cr(" find(intptr_t x) - find & print nmethod/stub/bytecode/oop based on pointer into it");
|
|
tty->print_cr(" findpc(intptr_t x) - find & print nmethod/stub/bytecode/oop based on pointer into it (verbose)");
|
|
|
|
#ifndef PRODUCT
|
|
tty->print_cr(" pns(void* sp, void* fp, void* pc) - print native (i.e. mixed) stack trace, e.g.");
|
|
#ifdef LINUX
|
|
AMD64_ONLY( tty->print_cr(" pns($sp, $rbp, $pc) on Linux/amd64"));
|
|
AARCH64_ONLY(tty->print_cr(" pns($sp, $fp, $pc) on Linux/AArch64"));
|
|
RISCV_ONLY( tty->print_cr(" pns($sp, $fp, $pc) on Linux/RISC-V"));
|
|
PPC64_ONLY( tty->print_cr(" pns($sp, 0, $pc) on Linux/ppc64"));
|
|
#endif // LINUX
|
|
tty->print_cr(" - in gdb do 'set overload-resolution off' before calling pns()");
|
|
tty->print_cr(" - in dbx do 'frame 1' before calling pns()");
|
|
#endif // !PRODUCT
|
|
|
|
tty->print_cr("universe.");
|
|
tty->print_cr(" verify(intptr_t p) - run verify on Universe");
|
|
tty->print_cr(" threads() - print all threads");
|
|
tty->print_cr(" psd() - print system dictionary");
|
|
|
|
tty->print_cr("class metadata.");
|
|
tty->print_cr(" findclass(name_pattern, flags)");
|
|
tty->print_cr(" findmethod(class_name_pattern, method_pattern, flags)");
|
|
|
|
tty->print_cr("method metadata.");
|
|
tty->print_cr(" blob(CodeBlob* p) - print CodeBlob");
|
|
tty->print_cr(" dump_vtable(address p) - dump vtable of the Klass");
|
|
tty->print_cr(" nm(intptr_t p) - find & print CodeBlob details");
|
|
tty->print_cr(" disnm(intptr_t p) - find & print disassembly of CodeBlob");
|
|
tty->print_cr(" printnm(intptr_t p) - print nmethod details");
|
|
tty->print_cr(" findbcp(method, bcp) - find & prints bcp");
|
|
|
|
tty->print_cr("stack frame details.");
|
|
tty->print_cr(" pfl() - print frame layout");
|
|
tty->print_cr(" psf() - print stack frames");
|
|
|
|
tty->print_cr("misc.");
|
|
tty->print_cr(" flush() - flush the log file");
|
|
tty->print_cr(" events() - dump events from ring buffers");
|
|
tty->print_cr(" u5decode(intptr_t addr) - decode a single u5 value");
|
|
tty->print_cr(" u5p(intptr_t addr, intptr_t limit, int count) - decode u5 values");
|
|
|
|
tty->print_cr("compiler debugging");
|
|
tty->print_cr(" debug() - set things up for compiler debugging");
|
|
tty->print_cr(" ndebug() - undo debug");
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
extern "C" DEBUGEXPORT void pns(void* sp, void* fp, void* pc) { // print native stack
|
|
Command c("pns");
|
|
if (!c.onThread()) return;
|
|
static char buf[O_BUFLEN];
|
|
// Call generic frame constructor (certain arguments may be ignored)
|
|
frame fr(sp, fp, pc);
|
|
NativeStackPrinter nsp(Thread::current_or_null());
|
|
nsp.print_stack_from_frame(tty, fr, buf, sizeof(buf),
|
|
false /* print_source_info */, -1 /* max stack */);
|
|
}
|
|
|
|
//
|
|
// This version of pns() will not work when called from the debugger, but is
|
|
// useful when called from within hotspot code. The advantages over pns()
|
|
// are not having to pass in any arguments, and it will work on Windows/x64.
|
|
//
|
|
// WARNING: Only intended for use when debugging. Do not leave calls to
|
|
// pns2() in committed source (product or debug).
|
|
//
|
|
extern "C" DEBUGEXPORT void pns2() { // print native stack
|
|
Command c("pns2");
|
|
if (!c.onThread()) return;
|
|
static char buf[O_BUFLEN];
|
|
address lastpc = nullptr;
|
|
NativeStackPrinter nsp(Thread::current_or_null());
|
|
nsp.print_stack(tty, buf, sizeof(buf), lastpc,
|
|
false /* print_source_info */, -1 /* max stack */);
|
|
}
|
|
#endif
|
|
|
|
|
|
// Returns true iff the address p is readable and *(intptr_t*)p != errvalue
|
|
extern "C" bool dbg_is_safe(const void* p, intptr_t errvalue) {
|
|
return p != nullptr && SafeFetchN((intptr_t*)const_cast<void*>(p), errvalue) != errvalue;
|
|
}
|
|
|
|
extern "C" bool dbg_is_good_oop(oopDesc* o) {
|
|
return dbg_is_safe(o, -1) && dbg_is_safe(o->klass(), -1) && oopDesc::is_oop(o) && o->klass()->is_klass();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Test multiple STATIC_ASSERT forms in various scopes.
|
|
|
|
#ifndef PRODUCT
|
|
|
|
// namespace scope
|
|
STATIC_ASSERT(true);
|
|
STATIC_ASSERT(true);
|
|
STATIC_ASSERT(1 == 1);
|
|
STATIC_ASSERT(0 == 0);
|
|
|
|
void test_multiple_static_assert_forms_in_function_scope() {
|
|
STATIC_ASSERT(true);
|
|
STATIC_ASSERT(true);
|
|
STATIC_ASSERT(0 == 0);
|
|
STATIC_ASSERT(1 == 1);
|
|
}
|
|
|
|
// class scope
|
|
struct TestMultipleStaticAssertFormsInClassScope {
|
|
STATIC_ASSERT(true);
|
|
STATIC_ASSERT(true);
|
|
STATIC_ASSERT(0 == 0);
|
|
STATIC_ASSERT(1 == 1);
|
|
};
|
|
|
|
#endif // !PRODUCT
|
|
|
|
// Support for showing register content on asserts/guarantees.
|
|
#ifdef CAN_SHOW_REGISTERS_ON_ASSERT
|
|
void initialize_assert_poison() {
|
|
char* page = os::reserve_memory(os::vm_page_size(), mtInternal);
|
|
if (page) {
|
|
if (os::commit_memory(page, os::vm_page_size(), !ExecMem) &&
|
|
os::protect_memory(page, os::vm_page_size(), os::MEM_PROT_NONE)) {
|
|
g_assert_poison = page;
|
|
g_assert_poison_read_only = page;
|
|
}
|
|
}
|
|
}
|
|
|
|
void disarm_assert_poison() {
|
|
g_assert_poison = &g_dummy;
|
|
}
|
|
|
|
#ifdef ASSERT
|
|
static void print_unprotect_error() {
|
|
fprintf(stderr, "Assertion poison page cannot be unprotected - mprotect failed with %d (%s)",
|
|
errno, os::strerror(errno));
|
|
fflush(stderr);
|
|
}
|
|
#endif
|
|
|
|
// TOUCH_ASSERT_POISON writes to the protected g_assert_poison page, which faults
|
|
// and enters platform signal handlers which in turn invokes this routine.
|
|
bool handle_assert_poison_fault(const void* ucVoid) {
|
|
// Disarm poison page.
|
|
if (!os::protect_memory((char*)g_assert_poison, os::vm_page_size(), os::MEM_PROT_RWX)) {
|
|
DEBUG_ONLY(print_unprotect_error();)
|
|
return false; // unprotecting memory may fail in OOM situations, as surprising as this sounds.
|
|
}
|
|
if (ucVoid != nullptr) {
|
|
// Save context.
|
|
const intx my_tid = os::current_thread_id();
|
|
if (AtomicAccess::cmpxchg(&g_asserting_thread, (intx)0, my_tid) == 0) {
|
|
os::save_assert_context(ucVoid);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
#endif // CAN_SHOW_REGISTERS_ON_ASSERT
|