From 38a578d547f39c3637d97f5e0242f4a69f3bbb31 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Thu, 4 Jul 2024 06:20:03 +0000 Subject: [PATCH] 8334738: os::print_hex_dump should optionally print ASCII Reviewed-by: dholmes, sgehwolf --- .../windows_aarch64/os_windows_aarch64.cpp | 4 +- src/hotspot/share/cds/archiveBuilder.cpp | 2 +- src/hotspot/share/runtime/os.cpp | 89 ++++++++---- src/hotspot/share/runtime/os.hpp | 8 +- .../share/utilities/globalDefinitions.hpp | 1 + src/hotspot/share/utilities/ostream.hpp | 1 + test/hotspot/gtest/runtime/test_os.cpp | 133 ++++++++++-------- 7 files changed, 145 insertions(+), 93 deletions(-) diff --git a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp index 78e98609b6b..722c3c8dd60 100644 --- a/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp +++ b/src/hotspot/os_cpu/windows_aarch64/os_windows_aarch64.cpp @@ -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(); } diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index a87a3ff042d..fdbb6605c13 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -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); } } diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index 97bf33fbaaa..d141ee24426 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -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) { diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index f3f44ddb2e6..af8eb8c8b9a 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -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; diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index a15a4de3e93..74817a35e77 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -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 diff --git a/src/hotspot/share/utilities/ostream.hpp b/src/hotspot/share/utilities/ostream.hpp index beb02309d3f..9faaf32fb6b 100644 --- a/src/hotspot/share/utilities/ostream.hpp +++ b/src/hotspot/share/utilities/ostream.hpp @@ -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; }; diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp index 55d30349ee5..654c2c60673 100644 --- a/test/hotspot/gtest/runtime/test_os.cpp +++ b/test/hotspot/gtest/runtime/test_os.cpp @@ -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.