Skip to content

Commit b44a329

Browse files
committed
8256864: [windows] Improve tracing for mapping errors
Reviewed-by: iklam, rrich
1 parent ae1eb28 commit b44a329

File tree

4 files changed

+141
-20
lines changed

4 files changed

+141
-20
lines changed

src/hotspot/os/windows/os_windows.cpp

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3215,9 +3215,10 @@ void os::split_reserved_memory(char *base, size_t size, size_t split) {
32153215
(attempt_reserve_memory_at(base, split) != NULL) &&
32163216
(attempt_reserve_memory_at(split_address, size - split) != NULL);
32173217
if (!rc) {
3218-
log_warning(os)("os::split_reserved_memory failed for [" RANGE_FORMAT ")",
3218+
log_warning(os)("os::split_reserved_memory failed for " RANGE_FORMAT,
32193219
RANGE_FORMAT_ARGS(base, size));
3220-
assert(false, "os::split_reserved_memory failed for [" RANGE_FORMAT ")",
3220+
os::print_memory_mappings(base, size, tty);
3221+
assert(false, "os::split_reserved_memory failed for " RANGE_FORMAT,
32213222
RANGE_FORMAT_ARGS(base, size));
32223223
}
32233224

@@ -5989,19 +5990,55 @@ bool os::win32::find_mapping(address addr, mapping_info_t* mi) {
59895990
return rc;
59905991
}
59915992

5993+
// Helper for print_one_mapping: print n words, both as hex and ascii.
5994+
// Use Safefetch for all values.
5995+
static void print_snippet(const void* p, outputStream* st) {
5996+
static const int num_words = LP64_ONLY(3) NOT_LP64(6);
5997+
static const int num_bytes = num_words * sizeof(int);
5998+
intptr_t v[num_words];
5999+
const int errval = 0xDE210244;
6000+
for (int i = 0; i < num_words; i++) {
6001+
v[i] = SafeFetchN((intptr_t*)p + i, errval);
6002+
if (v[i] == errval &&
6003+
SafeFetchN((intptr_t*)p + i, ~errval) == ~errval) {
6004+
return;
6005+
}
6006+
}
6007+
st->put('[');
6008+
for (int i = 0; i < num_words; i++) {
6009+
st->print(INTPTR_FORMAT " ", v[i]);
6010+
}
6011+
const char* b = (char*)v;
6012+
st->put('\"');
6013+
for (int i = 0; i < num_bytes; i++) {
6014+
st->put(::isgraph(b[i]) ? b[i] : '.');
6015+
}
6016+
st->put('\"');
6017+
st->put(']');
6018+
}
6019+
59926020
// Helper function for print_memory_mappings:
59936021
// Given a MEMORY_BASIC_INFORMATION, containing information about a non-free region:
59946022
// print out all regions in that allocation. If any of those regions
59956023
// fall outside the given range [start, end), indicate that in the output.
59966024
// Return the pointer to the end of the allocation.
59976025
static address print_one_mapping(MEMORY_BASIC_INFORMATION* minfo, address start, address end, outputStream* st) {
5998-
assert(start != NULL && end != NULL && end > start, "Sanity");
6026+
// Print it like this:
6027+
//
6028+
// Base: <xxxxx>: [xxxx - xxxx], state=MEM_xxx, prot=x, type=MEM_xxx (region 1)
6029+
// [xxxx - xxxx], state=MEM_xxx, prot=x, type=MEM_xxx (region 2)
59996030
assert(minfo->State != MEM_FREE, "Not inside an allocation.");
60006031
address allocation_base = (address)minfo->AllocationBase;
6001-
address last_region_end = NULL;
6002-
st->print_cr("AllocationBase: " PTR_FORMAT ":", allocation_base);
60036032
#define IS_IN(p) (p >= start && p < end)
6033+
bool first_line = true;
6034+
bool is_dll = false;
60046035
for(;;) {
6036+
if (first_line) {
6037+
st->print("Base " PTR_FORMAT ": ", p2i(allocation_base));
6038+
} else {
6039+
st->print_raw(NOT_LP64 (" ")
6040+
LP64_ONLY(" "));
6041+
}
60056042
address region_start = (address)minfo->BaseAddress;
60066043
address region_end = region_start + minfo->RegionSize;
60076044
assert(region_end > region_start, "Sanity");
@@ -6014,19 +6051,39 @@ static address print_one_mapping(MEMORY_BASIC_INFORMATION* minfo, address start,
60146051
}
60156052
st->print("[" PTR_FORMAT "-" PTR_FORMAT "), state=", p2i(region_start), p2i(region_end));
60166053
switch (minfo->State) {
6017-
case MEM_COMMIT: st->print("MEM_COMMIT"); break;
6018-
case MEM_FREE: st->print("MEM_FREE"); break;
6019-
case MEM_RESERVE: st->print("MEM_RESERVE"); break;
6054+
case MEM_COMMIT: st->print_raw("MEM_COMMIT "); break;
6055+
case MEM_FREE: st->print_raw("MEM_FREE "); break;
6056+
case MEM_RESERVE: st->print_raw("MEM_RESERVE"); break;
60206057
default: st->print("%x?", (unsigned)minfo->State);
60216058
}
6022-
st->print(", prot=%x, type=", (unsigned)minfo->AllocationProtect);
6059+
st->print(", prot=%3x, type=", (unsigned)minfo->Protect);
60236060
switch (minfo->Type) {
6024-
case MEM_IMAGE: st->print("MEM_IMAGE"); break;
6025-
case MEM_MAPPED: st->print("MEM_MAPPED"); break;
6026-
case MEM_PRIVATE: st->print("MEM_PRIVATE"); break;
6061+
case MEM_IMAGE: st->print_raw("MEM_IMAGE "); break;
6062+
case MEM_MAPPED: st->print_raw("MEM_MAPPED "); break;
6063+
case MEM_PRIVATE: st->print_raw("MEM_PRIVATE"); break;
60276064
default: st->print("%x?", (unsigned)minfo->State);
60286065
}
6066+
// At the start of every allocation, print some more information about this mapping.
6067+
// Notes:
6068+
// - this could be beefed up a lot, similar to os::print_location
6069+
// - for now we just query the allocation start point. This may be confusing for cases where
6070+
// the kernel merges multiple mappings.
6071+
if (first_line) {
6072+
char buf[MAX_PATH];
6073+
if (os::dll_address_to_library_name(allocation_base, buf, sizeof(buf), nullptr)) {
6074+
st->print(", %s", buf);
6075+
is_dll = true;
6076+
}
6077+
}
6078+
// If memory is accessible, and we do not know anything else about it, print a snippet
6079+
if (!is_dll &&
6080+
minfo->State == MEM_COMMIT &&
6081+
!(minfo->Protect & PAGE_NOACCESS || minfo->Protect & PAGE_GUARD)) {
6082+
st->print_raw(", ");
6083+
print_snippet(region_start, st);
6084+
}
60296085
st->cr();
6086+
// Next region...
60306087
bool rc = checkedVirtualQuery(region_end, minfo);
60316088
if (rc == false || // VirtualQuery error, end of allocation?
60326089
(minfo->State == MEM_FREE) || // end of allocation, free memory follows
@@ -6035,6 +6092,7 @@ static address print_one_mapping(MEMORY_BASIC_INFORMATION* minfo, address start,
60356092
{
60366093
return region_end;
60376094
}
6095+
first_line = false;
60386096
}
60396097
#undef IS_IN
60406098
ShouldNotReachHere();
@@ -6046,7 +6104,14 @@ void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {
60466104
address start = (address)addr;
60476105
address end = start + bytes;
60486106
address p = start;
6049-
while (p < end) {
6107+
if (p == nullptr) { // Lets skip the zero pages.
6108+
p += os::vm_allocation_granularity();
6109+
}
6110+
address p2 = p; // guard against wraparounds
6111+
int fuse = 0;
6112+
6113+
while (p < end && p >= p2) {
6114+
p2 = p;
60506115
// Probe for the next mapping.
60516116
if (checkedVirtualQuery(p, &minfo)) {
60526117
if (minfo.State != MEM_FREE) {
@@ -6064,8 +6129,24 @@ void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {
60646129
p = region_end;
60656130
}
60666131
} else {
6067-
// advance probe pointer.
6068-
p += os::vm_allocation_granularity();
6132+
// MSDN doc on VirtualQuery is unclear about what it means if it returns an error.
6133+
// In particular, whether querying an address outside any mappings would report
6134+
// a MEM_FREE region or just return an error. From experiments, it seems to return
6135+
// a MEM_FREE region for unmapped areas in valid address space and an error if we
6136+
// are outside valid address space.
6137+
// Here, we advance the probe pointer by alloc granularity. But if the range to print
6138+
// is large, this may take a long time. Therefore lets stop right away if the address
6139+
// is outside of what we know are valid addresses on Windows. Also, add a loop fuse.
6140+
static const address end_virt = (address)(LP64_ONLY(0x7ffffffffffULL) NOT_LP64(3*G));
6141+
if (p >= end_virt) {
6142+
break;
6143+
} else {
6144+
// Advance probe pointer, but with a fuse to break long loops.
6145+
if (fuse++ == 100000) {
6146+
break;
6147+
}
6148+
p += os::vm_allocation_granularity();
6149+
}
60696150
}
60706151
}
60716152
}

src/hotspot/share/runtime/os.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,11 @@ bool os::release_memory(char* addr, size_t bytes) {
17371737
return res;
17381738
}
17391739

1740+
// Prints all mappings
1741+
void os::print_memory_mappings(outputStream* st) {
1742+
os::print_memory_mappings(nullptr, (size_t)-1, st);
1743+
}
1744+
17401745
void os::pretouch_memory(void* start, void* end, size_t page_size) {
17411746
for (volatile char *p = (char*)start; p < (char*)end; p += page_size) {
17421747
*p = 0;

src/hotspot/share/runtime/os.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,8 @@ class os: AllStatic {
349349

350350
// A diagnostic function to print memory mappings in the given range.
351351
static void print_memory_mappings(char* addr, size_t bytes, outputStream* st);
352+
// Prints all mappings
353+
static void print_memory_mappings(outputStream* st);
352354

353355
// Touch memory pages that cover the memory range from start to end (exclusive)
354356
// to make the OS back the memory range with actual memory.

test/hotspot/gtest/runtime/test_os.cpp

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*/
2323

2424
#include "precompiled.hpp"
25+
#include "memory/allocation.hpp"
2526
#include "memory/resourceArea.hpp"
2627
#include "runtime/os.hpp"
2728
#include "utilities/globalDefinitions.hpp"
@@ -494,11 +495,43 @@ TEST_VM(os, release_one_mapping_multi_commits) {
494495
PRINT_MAPPINGS("D");
495496
}
496497

497-
TEST_VM(os, show_mappings_1) {
498-
// Display an arbitrary large address range. Make this works, does not hang, etc.
499-
char dummy[16 * K]; // silent truncation is fine, we don't care.
500-
stringStream ss(dummy, sizeof(dummy));
501-
os::print_memory_mappings((char*)0x1000, LP64_ONLY(1024) NOT_LP64(3) * G, &ss);
498+
static void test_show_mappings(address start, size_t size) {
499+
// Note: should this overflow, thats okay. stream will silently truncate. Does not matter for the test.
500+
const size_t buflen = 4 * M;
501+
char* buf = NEW_C_HEAP_ARRAY(char, buflen, mtInternal);
502+
buf[0] = '\0';
503+
stringStream ss(buf, buflen);
504+
if (start != nullptr) {
505+
os::print_memory_mappings((char*)start, size, &ss);
506+
} else {
507+
os::print_memory_mappings(&ss); // prints full address space
508+
}
509+
// Still an empty implementation on MacOS and AIX
510+
#if defined(LINUX) || defined(_WIN32)
511+
EXPECT_NE(buf[0], '\0');
512+
#endif
513+
// buf[buflen - 1] = '\0';
514+
// tty->print_raw(buf);
515+
FREE_C_HEAP_ARRAY(char, buf);
516+
}
517+
518+
TEST_VM(os, show_mappings_small_range) {
519+
test_show_mappings((address)0x100000, 2 * G);
520+
}
521+
522+
TEST_VM(os, show_mappings_full_range) {
523+
// Reserve a small range and fill it with a marker string, should show up
524+
// on implementations displaying range snippets
525+
char* p = os::reserve_memory(1 * M, mtInternal);
526+
if (p != nullptr) {
527+
if (os::commit_memory(p, 1 * M, false)) {
528+
strcpy(p, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
529+
}
530+
}
531+
test_show_mappings(nullptr, 0);
532+
if (p != nullptr) {
533+
os::release_memory(p, 1 * M);
534+
}
502535
}
503536

504537
#ifdef _WIN32

0 commit comments

Comments
 (0)