8334738: os::print_hex_dump should optionally print ASCII
Reviewed-by: dholmes, sgehwolf
This commit is contained in:
parent
7b894bc4af
commit
38a578d547
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user