8332237: [nmt] Remove the need for ThreadStackTracker::track_as_vm()

Reviewed-by: jsjolen, azafari
This commit is contained in:
Thomas Stuefe 2024-05-17 04:59:11 +00:00
parent 7c750fd95b
commit 9160ef8b9d
7 changed files with 56 additions and 183 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -151,11 +151,6 @@ bool MemBaseline::baseline_allocation_sites() {
return false; return false;
} }
// Walk simple thread stacks
if (!ThreadStackTracker::walk_simple_thread_stack_site(&malloc_walker)) {
return false;
}
_malloc_sites.move(malloc_walker.malloc_sites()); _malloc_sites.move(malloc_walker.malloc_sites());
// The malloc sites are collected in size order // The malloc sites are collected in size order
_malloc_sites_order = by_size; _malloc_sites_order = by_size;

View File

@ -193,17 +193,10 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
// Count thread's native stack in "Thread" category // Count thread's native stack in "Thread" category
if (flag == mtThread) { if (flag == mtThread) {
if (ThreadStackTracker::track_as_vm()) { const VirtualMemory* thread_stack_usage =
const VirtualMemory* thread_stack_usage = (const VirtualMemory*)_vm_snapshot->by_type(mtThreadStack);
(const VirtualMemory*)_vm_snapshot->by_type(mtThreadStack); reserved_amount += thread_stack_usage->reserved();
reserved_amount += thread_stack_usage->reserved(); committed_amount += thread_stack_usage->committed();
committed_amount += thread_stack_usage->committed();
} else {
const MallocMemory* thread_stack_usage =
(const MallocMemory*)_malloc_snapshot->by_type(mtThreadStack);
reserved_amount += thread_stack_usage->malloc_size();
committed_amount += thread_stack_usage->malloc_size();
}
} else if (flag == mtNMT) { } else if (flag == mtNMT) {
// Count malloc headers in "NMT" category // Count malloc headers in "NMT" category
reserved_amount += _malloc_snapshot->malloc_overhead(); reserved_amount += _malloc_snapshot->malloc_overhead();
@ -240,21 +233,12 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
out->print_cr("%27s ( instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")", out->print_cr("%27s ( instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")",
" ", _instance_class_count, _array_class_count); " ", _instance_class_count, _array_class_count);
} else if (flag == mtThread) { } else if (flag == mtThread) {
if (ThreadStackTracker::track_as_vm()) { const VirtualMemory* thread_stack_usage =
const VirtualMemory* thread_stack_usage = _vm_snapshot->by_type(mtThreadStack);
_vm_snapshot->by_type(mtThreadStack); // report thread count
// report thread count out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", ThreadStackTracker::thread_count());
out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", ThreadStackTracker::thread_count()); out->print("%27s (stack: ", " ");
out->print("%27s (stack: ", " "); print_total(thread_stack_usage->reserved(), thread_stack_usage->committed(), thread_stack_usage->peak_size());
print_total(thread_stack_usage->reserved(), thread_stack_usage->committed(), thread_stack_usage->peak_size());
} else {
MallocMemory* thread_stack_memory = _malloc_snapshot->by_type(mtThreadStack);
const char* scale = current_scale();
// report thread count
out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", thread_stack_memory->malloc_count());
out->print("%27s (Stack: " SIZE_FORMAT "%s", " ",
amount_in_current_scale(thread_stack_memory->malloc_size()), scale);
}
out->print_cr(")"); out->print_cr(")");
} }
@ -627,24 +611,15 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
out->print_cr(")"); out->print_cr(")");
out->print("%27s (stack: ", " "); out->print("%27s (stack: ", " ");
if (ThreadStackTracker::track_as_vm()) { // report thread stack
// report thread stack const VirtualMemory* current_thread_stack =
const VirtualMemory* current_thread_stack = _current_baseline.virtual_memory(mtThreadStack);
_current_baseline.virtual_memory(mtThreadStack); const VirtualMemory* early_thread_stack =
const VirtualMemory* early_thread_stack = _early_baseline.virtual_memory(mtThreadStack);
_early_baseline.virtual_memory(mtThreadStack);
print_virtual_memory_diff(current_thread_stack->reserved(), current_thread_stack->committed(), print_virtual_memory_diff(current_thread_stack->reserved(), current_thread_stack->committed(),
early_thread_stack->reserved(), early_thread_stack->committed()); early_thread_stack->reserved(), early_thread_stack->committed());
} else {
const MallocMemory* current_thread_stack =
_current_baseline.malloc_memory(mtThreadStack);
const MallocMemory* early_thread_stack =
_early_baseline.malloc_memory(mtThreadStack);
print_malloc_diff(current_thread_stack->malloc_size(), current_thread_stack->malloc_count(),
early_thread_stack->malloc_size(), early_thread_stack->malloc_count(), flag);
}
out->print_cr(")"); out->print_cr(")");
} }

View File

@ -67,8 +67,7 @@ void MemTracker::initialize() {
if (level > NMT_off) { if (level > NMT_off) {
if (!MallocTracker::initialize(level) || if (!MallocTracker::initialize(level) ||
!VirtualMemoryTracker::initialize(level) || !VirtualMemoryTracker::initialize(level)) {
!ThreadStackTracker::initialize(level)) {
assert(false, "NMT initialization failed"); assert(false, "NMT initialization failed");
level = NMT_off; level = NMT_off;
log_warning(nmt)("NMT initialization failed. NMT disabled."); log_warning(nmt)("NMT initialization failed. NMT disabled.");

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -43,13 +43,11 @@ NMTUsage::NMTUsage(NMTUsageOptions options) :
_usage_options(options) { } _usage_options(options) { }
void NMTUsage::walk_thread_stacks() { void NMTUsage::walk_thread_stacks() {
// If backed by virtual memory, snapping the thread stacks involves walking // Snapping the thread stacks involves walking the areas to figure out how
// them to to figure out how much memory is committed if they are backed by // much memory had been committed if they are backed by virtual memory. This
// virtual memory. This needs ot happen before we take the snapshot of the // needs to happen before we take the snapshot of the virtual memory since it
// virtual memory since it will update this information. // will update this information.
if (ThreadStackTracker::track_as_vm()) { VirtualMemoryTracker::snapshot_thread_stacks();
VirtualMemoryTracker::snapshot_thread_stacks();
}
} }
void NMTUsage::update_malloc_usage() { void NMTUsage::update_malloc_usage() {

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2019, 2021, Red Hat, Inc. All rights reserved. * Copyright (c) 2019, 2024, Red Hat, Inc. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -24,95 +25,46 @@
#include "precompiled.hpp" #include "precompiled.hpp"
#include "nmt/mallocTracker.hpp"
#include "nmt/memTracker.hpp" #include "nmt/memTracker.hpp"
#include "nmt/threadStackTracker.hpp" #include "nmt/threadStackTracker.hpp"
#include "nmt/virtualMemoryTracker.hpp" #include "nmt/virtualMemoryTracker.hpp"
#include "runtime/os.hpp"
#include "runtime/threadCritical.hpp" #include "runtime/threadCritical.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
volatile size_t ThreadStackTracker::_thread_count = 0; volatile size_t ThreadStackTracker::_thread_count = 0;
SortedLinkedList<SimpleThreadStackSite, ThreadStackTracker::compare_thread_stack_base>* ThreadStackTracker::_simple_thread_stacks = nullptr;
bool ThreadStackTracker::initialize(NMT_TrackingLevel level) { static void align_thread_stack_boundaries_inward(void*& base, size_t& size) {
if (level == NMT_detail && !track_as_vm()) { // Thread stack boundaries don't have to be aligned to page boundaries. For cases where they
_simple_thread_stacks = new (std::nothrow, mtNMT) // are not aligned (e.g. AIX, Alpine), this function corrects boundaries inward to the next
SortedLinkedList<SimpleThreadStackSite, ThreadStackTracker::compare_thread_stack_base>(); // page boundaries. This ensures that we can track thread stacks piggybacking on the virtual
return (_simple_thread_stacks != nullptr); // memory tracker.
} void* const base_aligned = align_up(base, os::vm_page_size());
return true; const size_t size_aligned = align_down(size, os::vm_page_size());
} assert(size_aligned > 0, "stack size less than a page?");
base = base_aligned;
int ThreadStackTracker::compare_thread_stack_base(const SimpleThreadStackSite& s1, const SimpleThreadStackSite& s2) { size = size_aligned;
return primitive_compare(s1.base(), s2.base());
} }
void ThreadStackTracker::new_thread_stack(void* base, size_t size, const NativeCallStack& stack) { void ThreadStackTracker::new_thread_stack(void* base, size_t size, const NativeCallStack& stack) {
assert(MemTracker::tracking_level() >= NMT_summary, "Must be"); assert(MemTracker::enabled(), "Must be");
assert(base != nullptr, "Should have been filtered"); assert(base != nullptr, "Should have been filtered");
align_thread_stack_boundaries_inward(base, size);
ThreadCritical tc; ThreadCritical tc;
if (track_as_vm()) { VirtualMemoryTracker::add_reserved_region((address)base, size, stack, mtThreadStack);
VirtualMemoryTracker::add_reserved_region((address)base, size, stack, mtThreadStack);
} else {
// Use a slot in mallocMemorySummary for thread stack bookkeeping
MallocMemorySummary::record_malloc(size, mtThreadStack);
if (MemTracker::tracking_level() == NMT_detail) {
assert(_simple_thread_stacks != nullptr, "Must be initialized");
SimpleThreadStackSite site((address)base, size, stack);
_simple_thread_stacks->add(site);
}
}
_thread_count++; _thread_count++;
} }
void ThreadStackTracker::delete_thread_stack(void* base, size_t size) { void ThreadStackTracker::delete_thread_stack(void* base, size_t size) {
assert(MemTracker::tracking_level() >= NMT_summary, "Must be"); assert(MemTracker::enabled(), "Must be");
assert(base != nullptr, "Should have been filtered"); assert(base != nullptr, "Should have been filtered");
align_thread_stack_boundaries_inward(base, size);
ThreadCritical tc; ThreadCritical tc;
if(track_as_vm()) { VirtualMemoryTracker::remove_released_region((address)base, size);
VirtualMemoryTracker::remove_released_region((address)base, size);
} else {
// Use a slot in mallocMemorySummary for thread stack bookkeeping
MallocMemorySummary::record_free(size, mtThreadStack);
if (MemTracker::tracking_level() == NMT_detail) {
assert(_simple_thread_stacks != nullptr, "Must be initialized");
SimpleThreadStackSite site((address)base, size, NativeCallStack::empty_stack()); // Fake object just to serve as compare target for delete
bool removed = _simple_thread_stacks->remove(site);
assert(removed, "Must exist");
}
}
_thread_count--; _thread_count--;
} }
bool ThreadStackTracker::walk_simple_thread_stack_site(MallocSiteWalker* walker) {
if (!track_as_vm()) {
LinkedListImpl<MallocSite> _sites;
{
ThreadCritical tc;
assert(_simple_thread_stacks != nullptr, "Must be initialized");
LinkedListIterator<SimpleThreadStackSite> itr(_simple_thread_stacks->head());
const SimpleThreadStackSite* ts = itr.next();
// Consolidate sites and convert to MallocSites, so we can piggyback into
// malloc snapshot
while (ts != nullptr) {
MallocSite site(*ts->call_stack(), mtThreadStack);
MallocSite* exist = _sites.find(site);
if (exist != nullptr) {
exist->allocate(ts->size());
} else {
site.allocate(ts->size());
_sites.add(site);
}
ts = itr.next();
}
}
// Piggyback to malloc snapshot
LinkedListIterator<MallocSite> site_itr(_sites.head());
const MallocSite* s = site_itr.next();
while (s != nullptr) {
walker->do_malloc_site(s);
s = site_itr.next();
}
}
return true;
}

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2019, 2021, Red Hat, Inc. All rights reserved. * Copyright (c) 2019, 2024, Red Hat, Inc. All rights reserved.
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,61 +26,17 @@
#ifndef SHARE_NMT_THREADSTACKTRACKER_HPP #ifndef SHARE_NMT_THREADSTACKTRACKER_HPP
#define SHARE_NMT_THREADSTACKTRACKER_HPP #define SHARE_NMT_THREADSTACKTRACKER_HPP
#include "nmt/allocationSite.hpp" #include "memory/allStatic.hpp"
#include "nmt/mallocSiteTable.hpp" #include "utilities/globalDefinitions.hpp"
#include "nmt/nmtCommon.hpp"
#include "utilities/linkedlist.hpp"
#include "utilities/nativeCallStack.hpp" #include "utilities/nativeCallStack.hpp"
class SimpleThreadStackSite : public AllocationSite {
const address _base;
const size_t _size;
public:
SimpleThreadStackSite(address base, size_t size, const NativeCallStack& stack) :
AllocationSite(stack, mtThreadStack),
_base(base),
_size(size) {}
bool equals(const SimpleThreadStackSite& mts) const {
bool eq = base() == mts.base();
assert(!eq || size() == mts.size(), "Must match");
return eq;
}
size_t size() const { return _size; }
address base() const { return _base; }
};
/*
* Most of platforms, that hotspot support, have their thread stacks backed by
* virtual memory by default. For these cases, thread stack tracker simply
* delegates tracking to virtual memory tracker.
* However, there are exceptions, (e.g. AIX), that platforms can provide stacks
* that are not page aligned. A hypothetical VM implementation, it can provide
* it own stacks. In these case, track_as_vm() should return false and manage
* stack tracking by this tracker internally.
* During memory snapshot, tracked thread stacks memory data is walked and stored
* along with malloc'd data inside baseline. The regions are not scanned and assumed
* all committed for now. Can add scanning phase when there is a need.
*/
class ThreadStackTracker : AllStatic { class ThreadStackTracker : AllStatic {
private: private:
static volatile size_t _thread_count; static volatile size_t _thread_count;
static int compare_thread_stack_base(const SimpleThreadStackSite& s1, const SimpleThreadStackSite& s2);
static SortedLinkedList<SimpleThreadStackSite, compare_thread_stack_base>* _simple_thread_stacks;
public: public:
static bool initialize(NMT_TrackingLevel level);
static void new_thread_stack(void* base, size_t size, const NativeCallStack& stack); static void new_thread_stack(void* base, size_t size, const NativeCallStack& stack);
static void delete_thread_stack(void* base, size_t size); static void delete_thread_stack(void* base, size_t size);
static bool track_as_vm() { return AIX_ONLY(false) NOT_AIX(true); }
static size_t thread_count() { return _thread_count; } static size_t thread_count() { return _thread_count; }
// Snapshot support. Piggyback thread stack data in malloc slot, NMT always handles
// thread stack slot specially since beginning.
static bool walk_simple_thread_stack_site(MallocSiteWalker* walker);
}; };
#endif // SHARE_NMT_THREADSTACKTRACKER_HPP #endif // SHARE_NMT_THREADSTACKTRACKER_HPP

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -47,11 +47,8 @@ void VirtualMemory::update_peak(size_t size) {
} }
void VirtualMemorySummary::snapshot(VirtualMemorySnapshot* s) { void VirtualMemorySummary::snapshot(VirtualMemorySnapshot* s) {
// Only if thread stack is backed by virtual memory // Snapshot current thread stacks
if (ThreadStackTracker::track_as_vm()) { VirtualMemoryTracker::snapshot_thread_stacks();
// Snapshot current thread stacks
VirtualMemoryTracker::snapshot_thread_stacks();
}
as_snapshot()->copy_to(s); as_snapshot()->copy_to(s);
} }