8334738: os::print_hex_dump should optionally print ASCII

Reviewed-by: dholmes, sgehwolf
This commit is contained in:
Thomas Stuefe 2024-07-04 06:20:03 +00:00
parent 7b894bc4af
commit 38a578d547
7 changed files with 145 additions and 93 deletions

View File

@ -1,6 +1,6 @@
/*
* Copyright (c) 2020, Microsoft Corporation. All rights reserved.
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2024, 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
@ -219,7 +219,7 @@ void os::print_tos_pc(outputStream *st, const void *context) {
// this at the end, and hope for the best.
address pc = (address)uc->Pc;
st->print_cr("Instructions: (pc=" PTR_FORMAT ")", pc);
print_hex_dump(st, pc - 32, pc + 32, sizeof(char));
print_hex_dump(st, pc - 32, pc + 32, sizeof(char), /* print_ascii=*/false);
st->cr();
}

View File

@ -1273,7 +1273,7 @@ class ArchiveBuilder::CDSMapLogger : AllStatic {
// longs and doubles will be split into two words.
unitsize = sizeof(narrowOop);
}
os::print_hex_dump(&lsh, base, top, unitsize, 32, requested_base);
os::print_hex_dump(&lsh, base, top, unitsize, /* print_ascii=*/true, /* bytes_per_line=*/32, requested_base);
}
}

View File

@ -938,56 +938,73 @@ bool os::print_function_and_library_name(outputStream* st,
return have_function_name || have_library_name;
}
ATTRIBUTE_NO_ASAN static bool read_safely_from(intptr_t* p, intptr_t* result) {
const intptr_t errval = 0x1717;
intptr_t i = SafeFetchN(p, errval);
ATTRIBUTE_NO_ASAN static bool read_safely_from(const uintptr_t* p, uintptr_t* result) {
DEBUG_ONLY(*result = 0xAAAA;)
const uintptr_t errval = 0x1717;
uintptr_t i = (uintptr_t)SafeFetchN((intptr_t*)p, errval);
if (i == errval) {
i = SafeFetchN(p, ~errval);
i = (uintptr_t)SafeFetchN((intptr_t*)p, ~errval);
if (i == ~errval) {
return false;
}
}
(*result) = i;
(*result) = (uintptr_t)i;
return true;
}
static void print_hex_location(outputStream* st, address p, int unitsize) {
// Helper for os::print_hex_dump
static void print_ascii_form(stringStream& ascii_form, uint64_t value, int unitsize) {
union {
uint64_t v;
uint8_t c[sizeof(v)];
} u = { value };
for (int i = 0; i < unitsize; i++) {
const int idx = LITTLE_ENDIAN_ONLY(i) BIG_ENDIAN_ONLY(sizeof(u.v) - 1 - i);
const uint8_t c = u.c[idx];
ascii_form.put(isprint(c) && isascii(c) ? c : '.');
}
}
// Helper for os::print_hex_dump
static void print_hex_location(outputStream* st, const_address p, int unitsize, stringStream& ascii_form) {
assert(is_aligned(p, unitsize), "Unaligned");
address pa = align_down(p, sizeof(intptr_t));
const uintptr_t* pa = (const uintptr_t*) align_down(p, sizeof(intptr_t));
#ifndef _LP64
// Special handling for printing qwords on 32-bit platforms
if (unitsize == 8) {
intptr_t i1, i2;
if (read_safely_from((intptr_t*)pa, &i1) &&
read_safely_from((intptr_t*)pa + 1, &i2)) {
uintptr_t i1 = 0, i2 = 0;
if (read_safely_from(pa, &i1) &&
read_safely_from(pa + 1, &i2)) {
const uint64_t value =
LITTLE_ENDIAN_ONLY((((uint64_t)i2) << 32) | i1)
BIG_ENDIAN_ONLY((((uint64_t)i1) << 32) | i2);
st->print("%016" FORMAT64_MODIFIER "x", value);
print_ascii_form(ascii_form, value, unitsize);
} else {
st->print_raw("????????????????");
}
return;
}
#endif // 32-bit, qwords
intptr_t i = 0;
if (read_safely_from((intptr_t*)pa, &i)) {
uintptr_t i = 0;
if (read_safely_from(pa, &i)) {
// bytes: CA FE BA BE DE AD C0 DE
// bytoff: 0 1 2 3 4 5 6 7
// LE bits: 0 8 16 24 32 40 48 56
// BE bits: 56 48 40 32 24 16 8 0
const int offset = (int)(p - (address)pa);
const int offset = (int)(p - (const_address)pa);
const int bitoffset =
LITTLE_ENDIAN_ONLY(offset * BitsPerByte)
BIG_ENDIAN_ONLY((int)((sizeof(intptr_t) - unitsize - offset) * BitsPerByte));
const int bitfieldsize = unitsize * BitsPerByte;
intptr_t value = bitfield(i, bitoffset, bitfieldsize);
uintptr_t value = bitfield(i, bitoffset, bitfieldsize);
switch (unitsize) {
case 1: st->print("%02x", (u1)value); break;
case 2: st->print("%04x", (u2)value); break;
case 4: st->print("%08x", (u4)value); break;
case 8: st->print("%016" FORMAT64_MODIFIER "x", (u8)value); break;
}
print_ascii_form(ascii_form, value, unitsize);
} else {
switch (unitsize) {
case 1: st->print_raw("??"); break;
@ -998,36 +1015,56 @@ static void print_hex_location(outputStream* st, address p, int unitsize) {
}
}
void os::print_hex_dump(outputStream* st, address start, address end, int unitsize,
int bytes_per_line, address logical_start) {
void os::print_hex_dump(outputStream* st, const_address start, const_address end, int unitsize,
bool print_ascii, int bytes_per_line, const_address logical_start) {
constexpr int max_bytes_per_line = 64;
assert(unitsize == 1 || unitsize == 2 || unitsize == 4 || unitsize == 8, "just checking");
assert(bytes_per_line > 0 && bytes_per_line <= max_bytes_per_line &&
is_power_of_2(bytes_per_line), "invalid bytes_per_line");
start = align_down(start, unitsize);
logical_start = align_down(logical_start, unitsize);
bytes_per_line = align_up(bytes_per_line, 8);
int cols = 0;
int cols_per_line = bytes_per_line / unitsize;
const int cols_per_line = bytes_per_line / unitsize;
address p = start;
address logical_p = logical_start;
const_address p = start;
const_address logical_p = logical_start;
stringStream ascii_form;
// Print out the addresses as if we were starting from logical_start.
st->print(PTR_FORMAT ": ", p2i(logical_p));
while (p < end) {
print_hex_location(st, p, unitsize);
if (cols == 0) {
st->print(PTR_FORMAT ": ", p2i(logical_p));
}
print_hex_location(st, p, unitsize, ascii_form);
p += unitsize;
logical_p += unitsize;
cols++;
if (cols >= cols_per_line && p < end) {
cols = 0;
if (cols >= cols_per_line) {
if (print_ascii && !ascii_form.is_empty()) {
st->print(" %s", ascii_form.base());
}
ascii_form.reset();
st->cr();
st->print(PTR_FORMAT ": ", p2i(logical_p));
cols = 0;
} else {
st->print(" ");
}
}
st->cr();
if (cols > 0) { // did not print a full line
if (print_ascii) {
// indent last ascii part to match that of full lines
const int size_of_printed_unit = unitsize * 2;
const int space_left = (cols_per_line - cols) * (size_of_printed_unit + 1);
st->sp(space_left);
st->print(" %s", ascii_form.base());
}
st->cr();
}
}
void os::print_dhm(outputStream* st, const char* startStr, long sec) {
@ -1045,7 +1082,7 @@ void os::print_tos(outputStream* st, address sp) {
void os::print_instructions(outputStream* st, address pc, int unitsize) {
st->print_cr("Instructions: (pc=" PTR_FORMAT ")", p2i(pc));
print_hex_dump(st, pc - 256, pc + 256, unitsize);
print_hex_dump(st, pc - 256, pc + 256, unitsize, /* print_ascii=*/false);
}
void os::print_environment_variables(outputStream* st, const char** env_list) {

View File

@ -856,10 +856,10 @@ class os: AllStatic {
// return current frame. pc() and sp() are set to null on failure.
static frame current_frame();
static void print_hex_dump(outputStream* st, address start, address end, int unitsize,
int bytes_per_line, address logical_start);
static void print_hex_dump(outputStream* st, address start, address end, int unitsize) {
print_hex_dump(st, start, end, unitsize, /*bytes_per_line=*/16, /*logical_start=*/start);
static void print_hex_dump(outputStream* st, const_address start, const_address end, int unitsize, bool print_ascii,
int bytes_per_line, const_address logical_start);
static void print_hex_dump(outputStream* st, const_address start, const_address end, int unitsize, bool print_ascii = true) {
print_hex_dump(st, start, end, unitsize, print_ascii, /*bytes_per_line=*/16, /*logical_start=*/start);
}
// returns a string to describe the exception/signal;

View File

@ -455,6 +455,7 @@ typedef unsigned int uint; NEEDS_CLEANUP
typedef signed char s_char;
typedef unsigned char u_char;
typedef u_char* address;
typedef const u_char* const_address;
// Pointer subtraction.
// The idea here is to avoid ptrdiff_t, which is signed and so doesn't have

View File

@ -267,6 +267,7 @@ class stringStream : public outputStream {
return _buffer;
};
void reset();
bool is_empty() const { return _buffer[0] == '\0'; }
// Copy to a resource, or C-heap, array as requested
char* as_string(bool c_heap = false) const;
};

View File

@ -167,82 +167,95 @@ TEST_VM_ASSERT_MSG(os, page_size_for_region_with_zero_min_pages,
}
#endif
static void do_test_print_hex_dump(address addr, size_t len, int unitsize, const char* expected) {
char buf[256];
#ifndef AIX
// Test relies on the ability to protect memory allocated with os::reserve_memory. AIX may not be able
// to do that (mprotect won't work on System V shm).
static void do_test_print_hex_dump(const_address from, const_address to, int unitsize, int bytes_per_line,
const_address logical_start, const char* expected) {
char buf[2048];
buf[0] = '\0';
stringStream ss(buf, sizeof(buf));
os::print_hex_dump(&ss, addr, addr + len, unitsize);
// tty->print_cr("expected: %s", expected);
// tty->print_cr("result: %s", buf);
EXPECT_THAT(buf, HasSubstr(expected));
os::print_hex_dump(&ss, from, to, unitsize, /* print_ascii=*/true, bytes_per_line, logical_start);
EXPECT_STREQ(buf, expected);
}
TEST_VM(os, test_print_hex_dump) {
const char* pattern [4] = {
#ifdef _LP64
#define ADDRESS1 "0x0000aaaaaaaaaa00"
#define ADDRESS2 "0x0000aaaaaaaaaa20"
#define ADDRESS3 "0x0000aaaaaaaaaa40"
#else
#define ADDRESS1 "0xaaaaaa00"
#define ADDRESS2 "0xaaaaaa20"
#define ADDRESS3 "0xaaaaaa40"
#endif
#define ASCII_1 "....#.jdk/internal/loader/Native"
#define ASCII_2 "Libraries......."
#define PAT_1 ADDRESS1 ": ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??\n" \
ADDRESS2 ": ff ff e0 dc 23 00 6a 64 6b 2f 69 6e 74 65 72 6e 61 6c 2f 6c 6f 61 64 65 72 2f 4e 61 74 69 76 65 " ASCII_1 "\n" \
ADDRESS3 ": 4c 69 62 72 61 72 69 65 73 00 00 00 00 00 00 00 " ASCII_2 "\n"
#ifdef VM_LITTLE_ENDIAN
"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f",
"0100 0302 0504 0706 0908 0b0a 0d0c 0f0e",
"03020100 07060504 0b0a0908 0f0e0d0c",
"0706050403020100 0f0e0d0c0b0a0908"
#define PAT_2 ADDRESS1 ": ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ????\n" \
ADDRESS2 ": ffff dce0 0023 646a 2f6b 6e69 6574 6e72 6c61 6c2f 616f 6564 2f72 614e 6974 6576 " ASCII_1 "\n" \
ADDRESS3 ": 694c 7262 7261 6569 0073 0000 0000 0000 " ASCII_2 "\n"
#define PAT_4 ADDRESS1 ": ???????? ???????? ???????? ???????? ???????? ???????? ???????? ????????\n" \
ADDRESS2 ": dce0ffff 646a0023 6e692f6b 6e726574 6c2f6c61 6564616f 614e2f72 65766974 " ASCII_1 "\n" \
ADDRESS3 ": 7262694c 65697261 00000073 00000000 " ASCII_2 "\n"
#define PAT_8 ADDRESS1 ": ???????????????? ???????????????? ???????????????? ????????????????\n" \
ADDRESS2 ": 646a0023dce0ffff 6e7265746e692f6b 6564616f6c2f6c61 65766974614e2f72 " ASCII_1 "\n" \
ADDRESS3 ": 656972617262694c 0000000000000073 " ASCII_2 "\n"
#else
"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f",
"0001 0203 0405 0607 0809 0a0b 0c0d 0e0f",
"00010203 04050607 08090a0b 0c0d0e0f",
"0001020304050607 08090a0b0c0d0e0f"
#define PAT_2 ADDRESS1 ": ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ???? ????\n" \
ADDRESS2 ": ffff e0dc 2300 6a64 6b2f 696e 7465 726e 616c 2f6c 6f61 6465 722f 4e61 7469 7665 " ASCII_1 "\n" \
ADDRESS3 ": 4c69 6272 6172 6965 7300 0000 0000 0000 " ASCII_2 "\n"
#define PAT_4 ADDRESS1 ": ???????? ???????? ???????? ???????? ???????? ???????? ???????? ????????\n" \
ADDRESS2 ": ffffe0dc 23006a64 6b2f696e 7465726e 616c2f6c 6f616465 722f4e61 74697665 " ASCII_1 "\n" \
ADDRESS3 ": 4c696272 61726965 73000000 00000000 " ASCII_2 "\n"
#define PAT_8 ADDRESS1 ": ???????????????? ???????????????? ???????????????? ????????????????\n" \
ADDRESS2 ": ffffe0dc23006a64 6b2f696e7465726e 616c2f6c6f616465 722f4e6174697665 " ASCII_1 "\n" \
ADDRESS3 ": 4c69627261726965 7300000000000000 " ASCII_2 "\n"
#endif
constexpr uint8_t bytes[] = {
0xff, 0xff, 0xe0, 0xdc, 0x23, 0x00, 0x6a, 0x64, 0x6b, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x61, 0x6c, 0x2f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65,
0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const char* pattern_not_readable [4] = {
"?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??",
"???? ???? ???? ???? ???? ???? ???? ????",
"???????? ???????? ???????? ????????",
"???????????????? ????????????????"
};
// two pages, first one protected.
const size_t ps = os::vm_page_size();
char* two_pages = os::reserve_memory(ps * 2, false, mtTest);
os::commit_memory(two_pages, ps * 2, false);
os::protect_memory(two_pages, ps, os::MEM_PROT_NONE, true);
// On AIX, zero page is readable.
address unreadable =
#ifdef AIX
(address) 0xFFFFFFFFFFFF0000ULL;
#else
(address) 0
#endif
;
memcpy(two_pages + ps, bytes, sizeof(bytes));
ResourceMark rm;
char buf[64];
stringStream ss(buf, sizeof(buf));
outputStream* out = &ss;
// outputStream* out = tty; // enable for printout
// print
const const_address from = (const_address) two_pages + ps - 32;
const const_address to = (const_address) from + 32 + sizeof(bytes);
const const_address logical_start = (const_address) LP64_ONLY(0xAAAAAAAAAA00ULL) NOT_LP64(0xAAAAAA00ULL);
// Test dumping unreadable memory
// Exclude test for Windows for now, since it needs SEH handling to work which cannot be
// guaranteed when we call directly into VM code. (see JDK-8220220)
#ifndef _WIN32
do_test_print_hex_dump(unreadable, 100, 1, pattern_not_readable[0]);
do_test_print_hex_dump(unreadable, 100, 2, pattern_not_readable[1]);
do_test_print_hex_dump(unreadable, 100, 4, pattern_not_readable[2]);
do_test_print_hex_dump(unreadable, 100, 8, pattern_not_readable[3]);
#endif
do_test_print_hex_dump(from, to, 1, 32, logical_start, PAT_1);
do_test_print_hex_dump(from, to, 2, 32, logical_start, PAT_2);
do_test_print_hex_dump(from, to, 4, 32, logical_start, PAT_4);
do_test_print_hex_dump(from, to, 8, 32, logical_start, PAT_8);
// Test dumping readable memory
address arr = (address)os::malloc(100, mtInternal);
for (u1 c = 0; c < 100; c++) {
arr[c] = c;
}
// unaligned printing, should align to next lower unitsize
do_test_print_hex_dump(from + 1, to, 2, 32, logical_start, PAT_2);
do_test_print_hex_dump(from + 1, to, 4, 32, logical_start, PAT_4);
do_test_print_hex_dump(from + 1, to, 8, 32, logical_start, PAT_8);
// properly aligned
do_test_print_hex_dump(arr, 100, 1, pattern[0]);
do_test_print_hex_dump(arr, 100, 2, pattern[1]);
do_test_print_hex_dump(arr, 100, 4, pattern[2]);
do_test_print_hex_dump(arr, 100, 8, pattern[3]);
// Not properly aligned. Should automatically down-align by unitsize
do_test_print_hex_dump(arr + 1, 100, 2, pattern[1]);
do_test_print_hex_dump(arr + 1, 100, 4, pattern[2]);
do_test_print_hex_dump(arr + 1, 100, 8, pattern[3]);
os::free(arr);
os::release_memory(two_pages, ps * 2);
}
#endif // not AIX
//////////////////////////////////////////////////////////////////////////////
// Test os::vsnprintf and friends.