8258606: os::print_signal_handlers() should resolve the function name of the handlers

Reviewed-by: dholmes, coleenp, gziemski
This commit is contained in:
Thomas Stuefe 2021-01-11 09:06:40 +00:00
parent bd34418429
commit e0d748d56f
4 changed files with 184 additions and 27 deletions
src/hotspot
os/posix
share/runtime
test/hotspot/gtest/runtime

@ -650,29 +650,13 @@ static void UserHandler(int sig, void *siginfo, void *context) {
os::signal_notify(sig);
}
static const char* get_signal_handler_name(address handler,
char* buf, int buflen) {
int offset = 0;
bool found = os::dll_address_to_library_name(handler, buf, buflen, &offset);
if (found) {
// skip directory names
const char *p1, *p2;
p1 = buf;
size_t len = strlen(os::file_separator());
while ((p2 = strstr(p1, os::file_separator())) != NULL) p1 = p2 + len;
#if !defined(AIX)
jio_snprintf(buf, buflen, "%s+0x%x", p1, offset);
#else
// The way os::dll_address_to_library_name is implemented on Aix
// right now, it always returns -1 for the offset which is not
// terribly informative.
// Will fix that. For now, omit the offset.
jio_snprintf(buf, buflen, "%s", p1);
#endif
} else {
jio_snprintf(buf, buflen, PTR_FORMAT, handler);
}
return buf;
static void print_signal_handler_name(outputStream* os, address handler, char* buf, size_t buflen) {
// We demangle, but omit arguments - signal handlers should have always the same prototype.
os::print_function_and_library_name(os, handler, buf, buflen,
true, // shorten_path
true, // demangle
true // omit arguments
);
}
// Writes one-line description of a combination of sigaction.sa_flags into a user
@ -826,8 +810,10 @@ static void check_signal_handler(int sig) {
if (thisHandler != jvmHandler) {
tty->print("Warning: %s handler ", os::exception_name(sig, buf, O_BUFLEN));
tty->print("expected:%s", get_signal_handler_name(jvmHandler, buf, O_BUFLEN));
tty->print_cr(" found:%s", get_signal_handler_name(thisHandler, buf, O_BUFLEN));
tty->print_raw("expected:");
print_signal_handler_name(tty, jvmHandler, buf, O_BUFLEN);
tty->print_raw(" found:");
print_signal_handler_name(tty, thisHandler, buf, O_BUFLEN);
// No need to check this sig any longer
sigaddset(&check_signal_done, sig);
// Running under non-interactive shell, SHUTDOWN2_SIGNAL will be reassigned SIG_IGN
@ -1368,7 +1354,7 @@ void PosixSignals::print_signal_handler(outputStream* st, int sig,
// See comment for SA_RESTORER_FLAG_MASK
LINUX_ONLY(sa.sa_flags &= SA_RESTORER_FLAG_MASK;)
st->print("%s: ", os::exception_name(sig, buf, buflen));
st->print("%10s: ", os::exception_name(sig, buf, buflen));
address handler = get_signal_handler(&sa);
@ -1377,7 +1363,7 @@ void PosixSignals::print_signal_handler(outputStream* st, int sig,
} else if (handler == CAST_FROM_FN_PTR(address, SIG_IGN)) {
st->print("SIG_IGN");
} else {
st->print("[%s]", get_signal_handler_name(handler, buf, buflen));
print_signal_handler_name(st, handler, buf, O_BUFLEN);
}
st->print(", sa_mask[0]=");

@ -882,6 +882,60 @@ void os::abort(bool dump_core) {
//---------------------------------------------------------------------------
// Helper functions for fatal error handler
bool os::print_function_and_library_name(outputStream* st,
address addr,
char* buf, int buflen,
bool shorten_paths,
bool demangle,
bool strip_arguments) {
// If no scratch buffer given, allocate one here on stack.
// (used during error handling; its a coin toss, really, if on-stack allocation
// is worse than (raw) C-heap allocation in that case).
char* p = buf;
if (p == NULL) {
p = (char*)::alloca(O_BUFLEN);
buflen = O_BUFLEN;
}
int offset = 0;
const bool have_function_name = dll_address_to_function_name(addr, p, buflen,
&offset, demangle);
if (have_function_name) {
// Print function name, optionally demangled
if (demangle && strip_arguments) {
char* args_start = strchr(p, '(');
if (args_start != NULL) {
*args_start = '\0';
}
}
// Print offset. Omit printing if offset is zero, which makes the output
// more readable if we print function pointers.
if (offset == 0) {
st->print("%s", p);
} else {
st->print("%s+%d", p, offset);
}
} else {
st->print(PTR_FORMAT, p2i(addr));
}
offset = 0;
const bool have_library_name = dll_address_to_library_name(addr, p, buflen, &offset);
if (have_library_name) {
// Cut path parts
if (shorten_paths) {
char* p2 = strrchr(p, os::file_separator()[0]);
if (p2 != NULL) {
p = p2 + 1;
}
}
st->print(" in %s", p);
if (!have_function_name) { // Omit offset if we already printed the function offset
st->print("+%d", offset);
}
}
return have_function_name || have_library_name;
}
void os::print_hex_dump(outputStream* st, address start, address end, int unitsize,
int bytes_per_line, address logical_start) {
assert(unitsize == 1 || unitsize == 2 || unitsize == 4 || unitsize == 8, "just checking");

@ -589,6 +589,24 @@ class os: AllStatic {
static bool dll_address_to_library_name(address addr, char* buf,
int buflen, int* offset);
// Given an address, attempt to locate both the symbol and the library it
// resides in. If at least one of these steps was successful, prints information
// and returns true.
// - if no scratch buffer is given, stack is used
// - shorten_paths: path is omitted from library name
// - demangle: function name is demangled
// - strip_arguments: arguments are stripped (requires demangle=true)
// On success prints either one of:
// "<function name>+<offset> in <library>"
// "<function name>+<offset>"
// "<address> in <library>+<offset>"
static bool print_function_and_library_name(outputStream* st,
address addr,
char* buf = NULL, int buflen = 0,
bool shorten_paths = true,
bool demangle = true,
bool strip_arguments = false);
// Find out whether the pc is in the static code for jvm.dll/libjvm.so.
static bool address_is_in_vm(address addr);

@ -25,6 +25,7 @@
#include "memory/allocation.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
@ -696,3 +697,101 @@ TEST_VM(os, pagesizes_test_print) {
pss.print_on(&ss);
ASSERT_EQ(strcmp(expected, buffer), 0);
}
TEST_VM(os, dll_address_to_function_and_library_name) {
char tmp[1024];
char output[1024];
stringStream st(output, sizeof(output));
#define EXPECT_CONTAINS(haystack, needle) \
EXPECT_NE(::strstr(haystack, needle), (char*)NULL)
#define EXPECT_DOES_NOT_CONTAIN(haystack, needle) \
EXPECT_EQ(::strstr(haystack, needle), (char*)NULL)
// #define LOG(...) tty->print_cr(__VA_ARGS__); // enable if needed
#define LOG(...)
// Invalid addresses
address addr = (address)(intptr_t)-1;
EXPECT_FALSE(os::print_function_and_library_name(&st, addr));
addr = NULL;
EXPECT_FALSE(os::print_function_and_library_name(&st, addr));
// Valid addresses
// Test with or without shorten-paths, demangle, and scratch buffer
for (int i = 0; i < 16; i++) {
const bool shorten_paths = (i & 1) != 0;
const bool demangle = (i & 2) != 0;
const bool strip_arguments = (i & 4) != 0;
const bool provide_scratch_buffer = (i & 8) != 0;
LOG("shorten_paths=%d, demangle=%d, strip_arguments=%d, provide_scratch_buffer=%d",
shorten_paths, demangle, strip_arguments, provide_scratch_buffer);
// Should show os::min_page_size in libjvm
addr = CAST_FROM_FN_PTR(address, Threads::create_vm);
st.reset();
EXPECT_TRUE(os::print_function_and_library_name(&st, addr,
provide_scratch_buffer ? tmp : NULL,
sizeof(tmp),
shorten_paths, demangle,
strip_arguments));
EXPECT_CONTAINS(output, "Threads");
EXPECT_CONTAINS(output, "create_vm");
EXPECT_CONTAINS(output, "jvm"); // "jvm.dll" or "libjvm.so" or similar
#ifndef _WIN32 // Demangler gives us no arguments on Windows
if (demangle) {
if (strip_arguments) {
EXPECT_DOES_NOT_CONTAIN(output, "(");
} else {
EXPECT_CONTAINS(output, "(");
}
}
#endif // _WIN32
LOG("%s", output);
// Test truncation on scratch buffer
if (provide_scratch_buffer) {
st.reset();
tmp[10] = 'X';
EXPECT_TRUE(os::print_function_and_library_name(&st, addr, tmp, 10,
shorten_paths, demangle));
EXPECT_EQ(tmp[10], 'X');
LOG("%s", output);
}
// Pointer (probably) outside function, should show at least the library name
addr -= 10;
st.reset();
EXPECT_TRUE(os::print_function_and_library_name(&st, addr,
provide_scratch_buffer ? tmp : NULL,
sizeof(tmp),
shorten_paths, demangle));
EXPECT_CONTAINS(output, "jvm"); // "jvm.dll" or "libjvm.so" or similar
LOG("%s", output);
// Pointer into system library
#ifndef _WIN32
addr = CAST_FROM_FN_PTR(address, ::malloc);
st.reset();
EXPECT_TRUE(os::print_function_and_library_name(&st, addr,
provide_scratch_buffer ? tmp : NULL,
sizeof(tmp),
shorten_paths, demangle));
EXPECT_CONTAINS(output, "malloc");
LINUX_ONLY(EXPECT_CONTAINS(output, "libc"));
MACOS_ONLY(EXPECT_CONTAINS(output, "libsystem"));
LOG("%s", output);
#else
addr = CAST_FROM_FN_PTR(address, CreateFileA);
st.reset(); // this also zero-terminates start of output
EXPECT_TRUE(os::print_function_and_library_name(&st, addr,
provide_scratch_buffer ? tmp : NULL,
sizeof(tmp),
shorten_paths, demangle));
for (char* p = output; *p; p++) {
*p = ::toupper(*p);
}
EXPECT_CONTAINS(output, "KERNEL32.DLL");
LOG("%s", output);
#endif
}
}