8281015: Further simplify NMT backend
Reviewed-by: zgu, mbaesken
This commit is contained in:
parent
9471f24ca1
commit
b96b743727
@ -649,18 +649,14 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NMT_TrackingLevel level = MemTracker::tracking_level();
|
const size_t outer_size = size + MemTracker::overhead_per_malloc();
|
||||||
const size_t nmt_overhead =
|
|
||||||
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
|
|
||||||
|
|
||||||
const size_t outer_size = size + nmt_overhead;
|
void* const outer_ptr = ::malloc(outer_size);
|
||||||
|
|
||||||
void* const outer_ptr = (u_char*)::malloc(outer_size);
|
|
||||||
if (outer_ptr == NULL) {
|
if (outer_ptr == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* inner_ptr = MemTracker::record_malloc((address)outer_ptr, size, memflags, stack, level);
|
void* const inner_ptr = MemTracker::record_malloc((address)outer_ptr, size, memflags, stack);
|
||||||
|
|
||||||
DEBUG_ONLY(::memset(inner_ptr, uninitBlockPad, size);)
|
DEBUG_ONLY(::memset(inner_ptr, uninitBlockPad, size);)
|
||||||
DEBUG_ONLY(break_if_ptr_caught(inner_ptr);)
|
DEBUG_ONLY(break_if_ptr_caught(inner_ptr);)
|
||||||
@ -696,19 +692,17 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NMT_TrackingLevel level = MemTracker::tracking_level();
|
const size_t new_outer_size = size + MemTracker::overhead_per_malloc();
|
||||||
const size_t nmt_overhead =
|
|
||||||
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
|
|
||||||
|
|
||||||
const size_t new_outer_size = size + nmt_overhead;
|
|
||||||
|
|
||||||
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
|
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
|
||||||
void* const old_outer_ptr = MemTracker::record_free(memblock, level);
|
void* const old_outer_ptr = MemTracker::record_free(memblock);
|
||||||
|
|
||||||
void* const new_outer_ptr = ::realloc(old_outer_ptr, new_outer_size);
|
void* const new_outer_ptr = ::realloc(old_outer_ptr, new_outer_size);
|
||||||
|
if (new_outer_ptr == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
|
void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, memflags, stack);
|
||||||
void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, memflags, stack, level);
|
|
||||||
|
|
||||||
DEBUG_ONLY(break_if_ptr_caught(new_inner_ptr);)
|
DEBUG_ONLY(break_if_ptr_caught(new_inner_ptr);)
|
||||||
|
|
||||||
@ -728,10 +722,9 @@ void os::free(void *memblock) {
|
|||||||
|
|
||||||
DEBUG_ONLY(break_if_ptr_caught(memblock);)
|
DEBUG_ONLY(break_if_ptr_caught(memblock);)
|
||||||
|
|
||||||
const NMT_TrackingLevel level = MemTracker::tracking_level();
|
|
||||||
|
|
||||||
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
|
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
|
||||||
void* const old_outer_ptr = MemTracker::record_free(memblock, level);
|
void* const old_outer_ptr = MemTracker::record_free(memblock);
|
||||||
|
|
||||||
::free(old_outer_ptr);
|
::free(old_outer_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,13 +106,11 @@ bool MallocSiteTable::walk(MallocSiteWalker* walker) {
|
|||||||
* 2. Overflow hash bucket.
|
* 2. Overflow hash bucket.
|
||||||
* Under any of above circumstances, caller should handle the situation.
|
* Under any of above circumstances, caller should handle the situation.
|
||||||
*/
|
*/
|
||||||
MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, size_t* bucket_idx,
|
MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t* marker, MEMFLAGS flags) {
|
||||||
size_t* pos_idx, MEMFLAGS flags) {
|
|
||||||
assert(flags != mtNone, "Should have a real memory type");
|
assert(flags != mtNone, "Should have a real memory type");
|
||||||
const unsigned int hash = key.calculate_hash();
|
const unsigned int hash = key.calculate_hash();
|
||||||
const unsigned int index = hash_to_index(hash);
|
const unsigned int index = hash_to_index(hash);
|
||||||
*bucket_idx = (size_t)index;
|
*marker = 0;
|
||||||
*pos_idx = 0;
|
|
||||||
|
|
||||||
// First entry for this hash bucket
|
// First entry for this hash bucket
|
||||||
if (_table[index] == NULL) {
|
if (_table[index] == NULL) {
|
||||||
@ -122,41 +120,47 @@ MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, size_t* b
|
|||||||
|
|
||||||
// swap in the head
|
// swap in the head
|
||||||
if (Atomic::replace_if_null(&_table[index], entry)) {
|
if (Atomic::replace_if_null(&_table[index], entry)) {
|
||||||
|
*marker = build_marker(index, 0);
|
||||||
return entry->data();
|
return entry->data();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete entry;
|
delete entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned pos_idx = 0;
|
||||||
MallocSiteHashtableEntry* head = _table[index];
|
MallocSiteHashtableEntry* head = _table[index];
|
||||||
while (head != NULL && (*pos_idx) <= MAX_BUCKET_LENGTH) {
|
while (head != NULL && pos_idx < MAX_BUCKET_LENGTH) {
|
||||||
if (head->hash() == hash) {
|
if (head->hash() == hash) {
|
||||||
MallocSite* site = head->data();
|
MallocSite* site = head->data();
|
||||||
if (site->flag() == flags && site->equals(key)) {
|
if (site->flag() == flags && site->equals(key)) {
|
||||||
|
*marker = build_marker(index, pos_idx);
|
||||||
return head->data();
|
return head->data();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (head->next() == NULL && (*pos_idx) < MAX_BUCKET_LENGTH) {
|
if (head->next() == NULL && pos_idx < (MAX_BUCKET_LENGTH - 1)) {
|
||||||
MallocSiteHashtableEntry* entry = new_entry(key, flags);
|
MallocSiteHashtableEntry* entry = new_entry(key, flags);
|
||||||
// OOM check
|
// OOM check
|
||||||
if (entry == NULL) return NULL;
|
if (entry == NULL) return NULL;
|
||||||
if (head->atomic_insert(entry)) {
|
if (head->atomic_insert(entry)) {
|
||||||
(*pos_idx) ++;
|
pos_idx ++;
|
||||||
|
*marker = build_marker(index, pos_idx);
|
||||||
return entry->data();
|
return entry->data();
|
||||||
}
|
}
|
||||||
// contended, other thread won
|
// contended, other thread won
|
||||||
delete entry;
|
delete entry;
|
||||||
}
|
}
|
||||||
head = (MallocSiteHashtableEntry*)head->next();
|
head = (MallocSiteHashtableEntry*)head->next();
|
||||||
(*pos_idx) ++;
|
pos_idx ++;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access malloc site
|
// Access malloc site
|
||||||
MallocSite* MallocSiteTable::malloc_site(size_t bucket_idx, size_t pos_idx) {
|
MallocSite* MallocSiteTable::malloc_site(uint32_t marker) {
|
||||||
|
uint16_t bucket_idx = bucket_idx_from_marker(marker);
|
||||||
assert(bucket_idx < table_size, "Invalid bucket index");
|
assert(bucket_idx < table_size, "Invalid bucket index");
|
||||||
|
const uint16_t pos_idx = pos_idx_from_marker(marker);
|
||||||
MallocSiteHashtableEntry* head = _table[bucket_idx];
|
MallocSiteHashtableEntry* head = _table[bucket_idx];
|
||||||
for (size_t index = 0;
|
for (size_t index = 0;
|
||||||
index < pos_idx && head != NULL;
|
index < pos_idx && head != NULL;
|
||||||
|
@ -111,11 +111,22 @@ class MallocSiteTable : AllStatic {
|
|||||||
table_size = (table_base_size * NMT_TrackingStackDepth - 1)
|
table_size = (table_base_size * NMT_TrackingStackDepth - 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
// The table must not be wider than the maximum value the bucket_idx field
|
// Table cannot be wider than a 16bit bucket idx can hold
|
||||||
// in the malloc header can hold.
|
#define MAX_MALLOCSITE_TABLE_SIZE (USHRT_MAX - 1)
|
||||||
|
// Each bucket chain cannot be longer than what a 16 bit pos idx can hold (hopefully way shorter)
|
||||||
|
#define MAX_BUCKET_LENGTH (USHRT_MAX - 1)
|
||||||
|
|
||||||
STATIC_ASSERT(table_size <= MAX_MALLOCSITE_TABLE_SIZE);
|
STATIC_ASSERT(table_size <= MAX_MALLOCSITE_TABLE_SIZE);
|
||||||
|
|
||||||
|
static uint32_t build_marker(unsigned bucket_idx, unsigned pos_idx) {
|
||||||
|
assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE && pos_idx < MAX_BUCKET_LENGTH, "overflow");
|
||||||
|
return (uint32_t)bucket_idx << 16 | pos_idx;
|
||||||
|
}
|
||||||
|
static uint16_t bucket_idx_from_marker(uint32_t marker) { return marker >> 16; }
|
||||||
|
static uint16_t pos_idx_from_marker(uint32_t marker) { return marker & 0xFFFF; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static bool initialize();
|
static bool initialize();
|
||||||
|
|
||||||
// Number of hash buckets
|
// Number of hash buckets
|
||||||
@ -123,9 +134,8 @@ class MallocSiteTable : AllStatic {
|
|||||||
|
|
||||||
// Access and copy a call stack from this table. Shared lock should be
|
// Access and copy a call stack from this table. Shared lock should be
|
||||||
// acquired before access the entry.
|
// acquired before access the entry.
|
||||||
static inline bool access_stack(NativeCallStack& stack, size_t bucket_idx,
|
static inline bool access_stack(NativeCallStack& stack, uint32_t marker) {
|
||||||
size_t pos_idx) {
|
MallocSite* site = malloc_site(marker);
|
||||||
MallocSite* site = malloc_site(bucket_idx, pos_idx);
|
|
||||||
if (site != NULL) {
|
if (site != NULL) {
|
||||||
stack = *site->call_stack();
|
stack = *site->call_stack();
|
||||||
return true;
|
return true;
|
||||||
@ -134,23 +144,22 @@ class MallocSiteTable : AllStatic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Record a new allocation from specified call path.
|
// Record a new allocation from specified call path.
|
||||||
// Return true if the allocation is recorded successfully, bucket_idx
|
// Return true if the allocation is recorded successfully and updates marker
|
||||||
// and pos_idx are also updated to indicate the entry where the allocation
|
// to indicate the entry where the allocation information was recorded.
|
||||||
// information was recorded.
|
|
||||||
// Return false only occurs under rare scenarios:
|
// Return false only occurs under rare scenarios:
|
||||||
// 1. out of memory
|
// 1. out of memory
|
||||||
// 2. overflow hash bucket
|
// 2. overflow hash bucket
|
||||||
static inline bool allocation_at(const NativeCallStack& stack, size_t size,
|
static inline bool allocation_at(const NativeCallStack& stack, size_t size,
|
||||||
size_t* bucket_idx, size_t* pos_idx, MEMFLAGS flags) {
|
uint32_t* marker, MEMFLAGS flags) {
|
||||||
MallocSite* site = lookup_or_add(stack, bucket_idx, pos_idx, flags);
|
MallocSite* site = lookup_or_add(stack, marker, flags);
|
||||||
if (site != NULL) site->allocate(size);
|
if (site != NULL) site->allocate(size);
|
||||||
return site != NULL;
|
return site != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record memory deallocation. bucket_idx and pos_idx indicate where the allocation
|
// Record memory deallocation. marker indicates where the allocation
|
||||||
// information was recorded.
|
// information was recorded.
|
||||||
static inline bool deallocation_at(size_t size, size_t bucket_idx, size_t pos_idx) {
|
static inline bool deallocation_at(size_t size, uint32_t marker) {
|
||||||
MallocSite* site = malloc_site(bucket_idx, pos_idx);
|
MallocSite* site = malloc_site(marker);
|
||||||
if (site != NULL) {
|
if (site != NULL) {
|
||||||
site->deallocate(size);
|
site->deallocate(size);
|
||||||
return true;
|
return true;
|
||||||
@ -170,8 +179,8 @@ class MallocSiteTable : AllStatic {
|
|||||||
// Delete a bucket linked list
|
// Delete a bucket linked list
|
||||||
static void delete_linked_list(MallocSiteHashtableEntry* head);
|
static void delete_linked_list(MallocSiteHashtableEntry* head);
|
||||||
|
|
||||||
static MallocSite* lookup_or_add(const NativeCallStack& key, size_t* bucket_idx, size_t* pos_idx, MEMFLAGS flags);
|
static MallocSite* lookup_or_add(const NativeCallStack& key, uint32_t* marker, MEMFLAGS flags);
|
||||||
static MallocSite* malloc_site(size_t bucket_idx, size_t pos_idx);
|
static MallocSite* malloc_site(uint32_t marker);
|
||||||
static bool walk(MallocSiteWalker* walker);
|
static bool walk(MallocSiteWalker* walker);
|
||||||
|
|
||||||
static inline unsigned int hash_to_index(unsigned int hash) {
|
static inline unsigned int hash_to_index(unsigned int hash) {
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include "runtime/safefetch.inline.hpp"
|
#include "runtime/safefetch.inline.hpp"
|
||||||
#include "services/mallocSiteTable.hpp"
|
#include "services/mallocSiteTable.hpp"
|
||||||
#include "services/mallocTracker.hpp"
|
#include "services/mallocTracker.hpp"
|
||||||
#include "services/mallocTracker.inline.hpp"
|
|
||||||
#include "services/memTracker.hpp"
|
#include "services/memTracker.hpp"
|
||||||
#include "utilities/debug.hpp"
|
#include "utilities/debug.hpp"
|
||||||
#include "utilities/ostream.hpp"
|
#include "utilities/ostream.hpp"
|
||||||
@ -115,20 +114,6 @@ void MallocHeader::mark_block_as_dead() {
|
|||||||
set_footer(_footer_canary_dead_mark);
|
set_footer(_footer_canary_dead_mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocHeader::release() {
|
|
||||||
assert(MemTracker::enabled(), "Sanity");
|
|
||||||
|
|
||||||
assert_block_integrity();
|
|
||||||
|
|
||||||
MallocMemorySummary::record_free(size(), flags());
|
|
||||||
MallocMemorySummary::record_free_malloc_header(sizeof(MallocHeader));
|
|
||||||
if (MemTracker::tracking_level() == NMT_detail) {
|
|
||||||
MallocSiteTable::deallocation_at(size(), _bucket_idx, _pos_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
mark_block_as_dead();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MallocHeader::print_block_on_error(outputStream* st, address bad_address) const {
|
void MallocHeader::print_block_on_error(outputStream* st, address bad_address) const {
|
||||||
assert(bad_address >= (address)this, "sanity");
|
assert(bad_address >= (address)this, "sanity");
|
||||||
|
|
||||||
@ -233,13 +218,8 @@ bool MallocHeader::check_block_integrity(char* msg, size_t msglen, address* p_co
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MallocHeader::record_malloc_site(const NativeCallStack& stack, size_t size,
|
|
||||||
size_t* bucket_idx, size_t* pos_idx, MEMFLAGS flags) const {
|
|
||||||
return MallocSiteTable::allocation_at(stack, size, bucket_idx, pos_idx, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MallocHeader::get_stack(NativeCallStack& stack) const {
|
bool MallocHeader::get_stack(NativeCallStack& stack) const {
|
||||||
return MallocSiteTable::access_stack(stack, _bucket_idx, _pos_idx);
|
return MallocSiteTable::access_stack(stack, _mst_marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MallocTracker::initialize(NMT_TrackingLevel level) {
|
bool MallocTracker::initialize(NMT_TrackingLevel level) {
|
||||||
@ -255,29 +235,33 @@ bool MallocTracker::initialize(NMT_TrackingLevel level) {
|
|||||||
|
|
||||||
// Record a malloc memory allocation
|
// Record a malloc memory allocation
|
||||||
void* MallocTracker::record_malloc(void* malloc_base, size_t size, MEMFLAGS flags,
|
void* MallocTracker::record_malloc(void* malloc_base, size_t size, MEMFLAGS flags,
|
||||||
const NativeCallStack& stack, NMT_TrackingLevel level) {
|
const NativeCallStack& stack)
|
||||||
assert(level != NMT_off, "precondition");
|
{
|
||||||
void* memblock; // the address for user data
|
assert(MemTracker::enabled(), "precondition");
|
||||||
MallocHeader* header = NULL;
|
assert(malloc_base != NULL, "precondition");
|
||||||
|
|
||||||
if (malloc_base == NULL) {
|
MallocMemorySummary::record_malloc(size, flags);
|
||||||
return NULL;
|
MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader));
|
||||||
|
uint32_t mst_marker = 0;
|
||||||
|
if (MemTracker::tracking_level() == NMT_detail) {
|
||||||
|
MallocSiteTable::allocation_at(stack, size, &mst_marker, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses placement global new operator to initialize malloc header
|
// Uses placement global new operator to initialize malloc header
|
||||||
|
MallocHeader* const header = ::new (malloc_base)MallocHeader(size, flags, stack, mst_marker);
|
||||||
header = ::new (malloc_base)MallocHeader(size, flags, stack, level);
|
void* const memblock = (void*)((char*)malloc_base + sizeof(MallocHeader));
|
||||||
memblock = (void*)((char*)malloc_base + sizeof(MallocHeader));
|
|
||||||
|
|
||||||
// The alignment check: 8 bytes alignment for 32 bit systems.
|
// The alignment check: 8 bytes alignment for 32 bit systems.
|
||||||
// 16 bytes alignment for 64-bit systems.
|
// 16 bytes alignment for 64-bit systems.
|
||||||
assert(((size_t)memblock & (sizeof(size_t) * 2 - 1)) == 0, "Alignment check");
|
assert(((size_t)memblock & (sizeof(size_t) * 2 - 1)) == 0, "Alignment check");
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
if (level > NMT_off) {
|
// Read back
|
||||||
// Read back
|
{
|
||||||
assert(get_size(memblock) == size, "Wrong size");
|
MallocHeader* const header2 = malloc_header(memblock);
|
||||||
assert(get_flags(memblock) == flags, "Wrong flags");
|
assert(header2->size() == size, "Wrong size");
|
||||||
|
assert(header2->flags() == flags, "Wrong flags");
|
||||||
|
header2->assert_block_integrity();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -285,9 +269,20 @@ void* MallocTracker::record_malloc(void* malloc_base, size_t size, MEMFLAGS flag
|
|||||||
}
|
}
|
||||||
|
|
||||||
void* MallocTracker::record_free(void* memblock) {
|
void* MallocTracker::record_free(void* memblock) {
|
||||||
assert(MemTracker::tracking_level() != NMT_off && memblock != NULL, "precondition");
|
assert(MemTracker::enabled(), "Sanity");
|
||||||
MallocHeader* header = malloc_header(memblock);
|
assert(memblock != NULL, "precondition");
|
||||||
header->release();
|
|
||||||
|
MallocHeader* const header = malloc_header(memblock);
|
||||||
|
header->assert_block_integrity();
|
||||||
|
|
||||||
|
MallocMemorySummary::record_free(header->size(), header->flags());
|
||||||
|
MallocMemorySummary::record_free_malloc_header(sizeof(MallocHeader));
|
||||||
|
if (MemTracker::tracking_level() == NMT_detail) {
|
||||||
|
MallocSiteTable::deallocation_at(header->size(), header->mst_marker());
|
||||||
|
}
|
||||||
|
|
||||||
|
header->mark_block_as_dead();
|
||||||
|
|
||||||
return (void*)header;
|
return (void*)header;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +295,7 @@ bool MallocTracker::print_pointer_information(const void* p, outputStream* st) {
|
|||||||
assert(MemTracker::enabled(), "NMT must be enabled");
|
assert(MemTracker::enabled(), "NMT must be enabled");
|
||||||
if (CanUseSafeFetch32() && os::is_readable_pointer(p)) {
|
if (CanUseSafeFetch32() && os::is_readable_pointer(p)) {
|
||||||
const NMT_TrackingLevel tracking_level = MemTracker::tracking_level();
|
const NMT_TrackingLevel tracking_level = MemTracker::tracking_level();
|
||||||
const MallocHeader* mhdr = (const MallocHeader*)MallocTracker::get_base(const_cast<void*>(p), tracking_level);
|
const MallocHeader* mhdr = malloc_header(p);
|
||||||
char msg[256];
|
char msg[256];
|
||||||
address p_corrupted;
|
address p_corrupted;
|
||||||
if (os::is_readable_pointer(mhdr) &&
|
if (os::is_readable_pointer(mhdr) &&
|
||||||
|
@ -267,7 +267,7 @@ class MallocMemorySummary : AllStatic {
|
|||||||
*
|
*
|
||||||
* 8 9 10 11 12 13 14 15 16 ++
|
* 8 9 10 11 12 13 14 15 16 ++
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
||||||
* ... | bucket idx | pos idx | flags | unused | canary | ... User payload ....
|
* ... | malloc site table marker | flags | unused | canary | ... User payload ....
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
||||||
*
|
*
|
||||||
* Layout on 32-bit:
|
* Layout on 32-bit:
|
||||||
@ -279,7 +279,7 @@ class MallocMemorySummary : AllStatic {
|
|||||||
*
|
*
|
||||||
* 8 9 10 11 12 13 14 15 16 ++
|
* 8 9 10 11 12 13 14 15 16 ++
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
||||||
* ... | bucket idx | pos idx | flags | unused | canary | ... User payload ....
|
* ... | malloc site table marker | flags | unused | canary | ... User payload ....
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
||||||
*
|
*
|
||||||
* Notes:
|
* Notes:
|
||||||
@ -294,16 +294,12 @@ class MallocMemorySummary : AllStatic {
|
|||||||
class MallocHeader {
|
class MallocHeader {
|
||||||
|
|
||||||
NOT_LP64(uint32_t _alt_canary);
|
NOT_LP64(uint32_t _alt_canary);
|
||||||
size_t _size;
|
const size_t _size;
|
||||||
uint16_t _bucket_idx;
|
const uint32_t _mst_marker;
|
||||||
uint16_t _pos_idx;
|
const uint8_t _flags;
|
||||||
uint8_t _flags;
|
const uint8_t _unused;
|
||||||
uint8_t _unused;
|
|
||||||
uint16_t _canary;
|
uint16_t _canary;
|
||||||
|
|
||||||
#define MAX_MALLOCSITE_TABLE_SIZE (USHRT_MAX - 1)
|
|
||||||
#define MAX_BUCKET_LENGTH (USHRT_MAX - 1)
|
|
||||||
|
|
||||||
static const uint16_t _header_canary_life_mark = 0xE99E;
|
static const uint16_t _header_canary_life_mark = 0xE99E;
|
||||||
static const uint16_t _header_canary_dead_mark = 0xD99D;
|
static const uint16_t _header_canary_dead_mark = 0xD99D;
|
||||||
static const uint16_t _footer_canary_life_mark = 0xE88E;
|
static const uint16_t _footer_canary_life_mark = 0xE88E;
|
||||||
@ -314,11 +310,7 @@ class MallocHeader {
|
|||||||
// We discount sizes larger than these
|
// We discount sizes larger than these
|
||||||
static const size_t max_reasonable_malloc_size = LP64_ONLY(256 * G) NOT_LP64(3500 * M);
|
static const size_t max_reasonable_malloc_size = LP64_ONLY(256 * G) NOT_LP64(3500 * M);
|
||||||
|
|
||||||
// If block is broken, print out a report to tty (optionally with
|
|
||||||
// hex dump surrounding the broken block), then trigger a fatal error
|
|
||||||
void assert_block_integrity() const;
|
|
||||||
void print_block_on_error(outputStream* st, address bad_address) const;
|
void print_block_on_error(outputStream* st, address bad_address) const;
|
||||||
void mark_block_as_dead();
|
|
||||||
|
|
||||||
static uint16_t build_footer(uint8_t b1, uint8_t b2) { return ((uint16_t)b1 << 8) | (uint16_t)b2; }
|
static uint16_t build_footer(uint8_t b1, uint8_t b2) { return ((uint16_t)b1 << 8) | (uint16_t)b2; }
|
||||||
|
|
||||||
@ -328,51 +320,32 @@ class MallocHeader {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, NMT_TrackingLevel level) {
|
MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, uint32_t mst_marker)
|
||||||
|
: _size(size), _mst_marker(mst_marker), _flags(NMTUtil::flag_to_index(flags)),
|
||||||
|
_unused(0), _canary(_header_canary_life_mark)
|
||||||
|
{
|
||||||
assert(size < max_reasonable_malloc_size, "Too large allocation size?");
|
assert(size < max_reasonable_malloc_size, "Too large allocation size?");
|
||||||
|
|
||||||
_flags = NMTUtil::flag_to_index(flags);
|
|
||||||
set_size(size);
|
|
||||||
if (level == NMT_detail) {
|
|
||||||
size_t bucket_idx;
|
|
||||||
size_t pos_idx;
|
|
||||||
if (record_malloc_site(stack, size, &bucket_idx, &pos_idx, flags)) {
|
|
||||||
assert(bucket_idx <= MAX_MALLOCSITE_TABLE_SIZE, "Overflow bucket index");
|
|
||||||
assert(pos_idx <= MAX_BUCKET_LENGTH, "Overflow bucket position index");
|
|
||||||
_bucket_idx = (uint16_t)bucket_idx;
|
|
||||||
_pos_idx = (uint16_t)pos_idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_unused = 0;
|
|
||||||
_canary = _header_canary_life_mark;
|
|
||||||
// On 32-bit we have some bits more, use them for a second canary
|
// On 32-bit we have some bits more, use them for a second canary
|
||||||
// guarding the start of the header.
|
// guarding the start of the header.
|
||||||
NOT_LP64(_alt_canary = _header_alt_canary_life_mark;)
|
NOT_LP64(_alt_canary = _header_alt_canary_life_mark;)
|
||||||
set_footer(_footer_canary_life_mark); // set after initializing _size
|
set_footer(_footer_canary_life_mark); // set after initializing _size
|
||||||
|
|
||||||
MallocMemorySummary::record_malloc(size, flags);
|
|
||||||
MallocMemorySummary::record_new_malloc_header(sizeof(MallocHeader));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t size() const { return _size; }
|
inline size_t size() const { return _size; }
|
||||||
inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; }
|
inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; }
|
||||||
|
inline uint32_t mst_marker() const { return _mst_marker; }
|
||||||
bool get_stack(NativeCallStack& stack) const;
|
bool get_stack(NativeCallStack& stack) const;
|
||||||
|
|
||||||
// Cleanup tracking information and mark block as dead before the memory is released.
|
void mark_block_as_dead();
|
||||||
void release();
|
|
||||||
|
|
||||||
// If block is broken, fill in a short descriptive text in out,
|
// If block is broken, fill in a short descriptive text in out,
|
||||||
// an option pointer to the corruption in p_corruption, and return false.
|
// an option pointer to the corruption in p_corruption, and return false.
|
||||||
// Return true if block is fine.
|
// Return true if block is fine.
|
||||||
bool check_block_integrity(char* msg, size_t msglen, address* p_corruption) const;
|
bool check_block_integrity(char* msg, size_t msglen, address* p_corruption) const;
|
||||||
|
|
||||||
private:
|
// If block is broken, print out a report to tty (optionally with
|
||||||
inline void set_size(size_t size) {
|
// hex dump surrounding the broken block), then trigger a fatal error
|
||||||
_size = size;
|
void assert_block_integrity() const;
|
||||||
}
|
|
||||||
bool record_malloc_site(const NativeCallStack& stack, size_t size,
|
|
||||||
size_t* bucket_idx, size_t* pos_idx, MEMFLAGS flags) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This needs to be true on both 64-bit and 32-bit platforms
|
// This needs to be true on both 64-bit and 32-bit platforms
|
||||||
@ -385,15 +358,9 @@ class MallocTracker : AllStatic {
|
|||||||
// Initialize malloc tracker for specific tracking level
|
// Initialize malloc tracker for specific tracking level
|
||||||
static bool initialize(NMT_TrackingLevel level);
|
static bool initialize(NMT_TrackingLevel level);
|
||||||
|
|
||||||
// malloc tracking header size for specific tracking level
|
// The overhead that is incurred by switching on NMT (we need, per malloc allocation,
|
||||||
static inline size_t malloc_header_size(NMT_TrackingLevel level) {
|
// space for header and 16-bit footer)
|
||||||
return (level == NMT_off) ? 0 : sizeof(MallocHeader);
|
static const size_t overhead_per_malloc = sizeof(MallocHeader) + sizeof(uint16_t);
|
||||||
}
|
|
||||||
|
|
||||||
// malloc tracking footer size for specific tracking level
|
|
||||||
static inline size_t malloc_footer_size(NMT_TrackingLevel level) {
|
|
||||||
return (level == NMT_off) ? 0 : sizeof(uint16_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parameter name convention:
|
// Parameter name convention:
|
||||||
// memblock : the beginning address for user data
|
// memblock : the beginning address for user data
|
||||||
@ -405,30 +372,11 @@ class MallocTracker : AllStatic {
|
|||||||
|
|
||||||
// Record malloc on specified memory block
|
// Record malloc on specified memory block
|
||||||
static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags,
|
static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags,
|
||||||
const NativeCallStack& stack, NMT_TrackingLevel level);
|
const NativeCallStack& stack);
|
||||||
|
|
||||||
// Record free on specified memory block
|
// Record free on specified memory block
|
||||||
static void* record_free(void* memblock);
|
static void* record_free(void* memblock);
|
||||||
|
|
||||||
// Offset memory address to header address
|
|
||||||
static inline void* get_base(void* memblock);
|
|
||||||
static inline void* get_base(void* memblock, NMT_TrackingLevel level) {
|
|
||||||
if (memblock == NULL || level == NMT_off) return memblock;
|
|
||||||
return (char*)memblock - malloc_header_size(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get memory size
|
|
||||||
static inline size_t get_size(void* memblock) {
|
|
||||||
MallocHeader* header = malloc_header(memblock);
|
|
||||||
return header->size();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get memory type
|
|
||||||
static inline MEMFLAGS get_flags(void* memblock) {
|
|
||||||
MallocHeader* header = malloc_header(memblock);
|
|
||||||
return header->flags();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void record_new_arena(MEMFLAGS flags) {
|
static inline void record_new_arena(MEMFLAGS flags) {
|
||||||
MallocMemorySummary::record_new_arena(flags);
|
MallocMemorySummary::record_new_arena(flags);
|
||||||
}
|
}
|
||||||
@ -451,8 +399,11 @@ class MallocTracker : AllStatic {
|
|||||||
private:
|
private:
|
||||||
static inline MallocHeader* malloc_header(void *memblock) {
|
static inline MallocHeader* malloc_header(void *memblock) {
|
||||||
assert(memblock != NULL, "NULL pointer");
|
assert(memblock != NULL, "NULL pointer");
|
||||||
MallocHeader* header = (MallocHeader*)((char*)memblock - sizeof(MallocHeader));
|
return (MallocHeader*)((char*)memblock - sizeof(MallocHeader));
|
||||||
return header;
|
}
|
||||||
|
static inline const MallocHeader* malloc_header(const void *memblock) {
|
||||||
|
assert(memblock != NULL, "NULL pointer");
|
||||||
|
return (const MallocHeader*)((const char*)memblock - sizeof(MallocHeader));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2014, 2019, 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
|
|
||||||
* under the terms of the GNU General Public License version 2 only, as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* version 2 for more details (a copy is included in the LICENSE file that
|
|
||||||
* accompanied this code).
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License version
|
|
||||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
||||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
||||||
* or visit www.oracle.com if you need additional information or have any
|
|
||||||
* questions.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SHARE_SERVICES_MALLOCTRACKER_INLINE_HPP
|
|
||||||
#define SHARE_SERVICES_MALLOCTRACKER_INLINE_HPP
|
|
||||||
|
|
||||||
#include "services/mallocTracker.hpp"
|
|
||||||
|
|
||||||
#include "services/memTracker.hpp"
|
|
||||||
|
|
||||||
inline void* MallocTracker::get_base(void* memblock){
|
|
||||||
return get_base(memblock, MemTracker::tracking_level());
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SHARE_SERVICES_MALLOCTRACKER_INLINE_HPP
|
|
@ -33,7 +33,7 @@
|
|||||||
#include "runtime/vmOperations.hpp"
|
#include "runtime/vmOperations.hpp"
|
||||||
#include "services/memBaseline.hpp"
|
#include "services/memBaseline.hpp"
|
||||||
#include "services/memReporter.hpp"
|
#include "services/memReporter.hpp"
|
||||||
#include "services/mallocTracker.inline.hpp"
|
#include "services/mallocTracker.hpp"
|
||||||
#include "services/memTracker.hpp"
|
#include "services/memTracker.hpp"
|
||||||
#include "services/nmtCommon.hpp"
|
#include "services/nmtCommon.hpp"
|
||||||
#include "services/nmtPreInit.hpp"
|
#include "services/nmtPreInit.hpp"
|
||||||
@ -90,10 +90,6 @@ void MemTracker::initialize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* MemTracker::malloc_base(void* memblock) {
|
|
||||||
return MallocTracker::get_base(memblock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tracker::record(address addr, size_t size) {
|
void Tracker::record(address addr, size_t size) {
|
||||||
if (MemTracker::tracking_level() < NMT_summary) return;
|
if (MemTracker::tracking_level() < NMT_summary) return;
|
||||||
switch(_type) {
|
switch(_type) {
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "services/nmtCommon.hpp"
|
#include "services/nmtCommon.hpp"
|
||||||
#include "services/threadStackTracker.hpp"
|
#include "services/threadStackTracker.hpp"
|
||||||
#include "services/virtualMemoryTracker.hpp"
|
#include "services/virtualMemoryTracker.hpp"
|
||||||
|
#include "utilities/debug.hpp"
|
||||||
#include "utilities/nativeCallStack.hpp"
|
#include "utilities/nativeCallStack.hpp"
|
||||||
|
|
||||||
#define CURRENT_PC ((MemTracker::tracking_level() == NMT_detail) ? \
|
#define CURRENT_PC ((MemTracker::tracking_level() == NMT_detail) ? \
|
||||||
@ -87,38 +88,30 @@ class MemTracker : AllStatic {
|
|||||||
return _tracking_level > NMT_off;
|
return _tracking_level > NMT_off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per-malloc overhead incurred by NMT, depending on the current NMT level
|
||||||
|
static size_t overhead_per_malloc() {
|
||||||
|
return enabled() ? MallocTracker::overhead_per_malloc : 0;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void* record_malloc(void* mem_base, size_t size, MEMFLAGS flag,
|
static inline void* record_malloc(void* mem_base, size_t size, MEMFLAGS flag,
|
||||||
const NativeCallStack& stack, NMT_TrackingLevel level) {
|
const NativeCallStack& stack) {
|
||||||
if (level != NMT_off) {
|
assert(mem_base != NULL, "caller should handle NULL");
|
||||||
return MallocTracker::record_malloc(mem_base, size, flag, stack, level);
|
if (enabled()) {
|
||||||
|
return MallocTracker::record_malloc(mem_base, size, flag, stack);
|
||||||
}
|
}
|
||||||
return mem_base;
|
return mem_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t malloc_header_size(NMT_TrackingLevel level) {
|
|
||||||
return MallocTracker::malloc_header_size(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
// malloc tracking footer size for specific tracking level
|
|
||||||
static inline size_t malloc_footer_size(NMT_TrackingLevel level) {
|
|
||||||
return MallocTracker::malloc_footer_size(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
// To malloc base address, which is the starting address
|
|
||||||
// of malloc tracking header if tracking is enabled.
|
|
||||||
// Otherwise, it returns the same address.
|
|
||||||
static void* malloc_base(void* memblock);
|
|
||||||
|
|
||||||
// Record malloc free and return malloc base address
|
// Record malloc free and return malloc base address
|
||||||
static inline void* record_free(void* memblock, NMT_TrackingLevel level) {
|
static inline void* record_free(void* memblock) {
|
||||||
// Never turned on
|
// Never turned on
|
||||||
if (level == NMT_off || memblock == NULL) {
|
assert(memblock != NULL, "caller should handle NULL");
|
||||||
|
if (!enabled()) {
|
||||||
return memblock;
|
return memblock;
|
||||||
}
|
}
|
||||||
return MallocTracker::record_free(memblock);
|
return MallocTracker::record_free(memblock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Record creation of an arena
|
// Record creation of an arena
|
||||||
static inline void record_new_arena(MEMFLAGS flag) {
|
static inline void record_new_arena(MEMFLAGS flag) {
|
||||||
if (!enabled()) return;
|
if (!enabled()) return;
|
||||||
|
@ -87,7 +87,7 @@ class NMTUtil : AllStatic {
|
|||||||
|
|
||||||
// Map memory type to index
|
// Map memory type to index
|
||||||
static inline int flag_to_index(MEMFLAGS flag) {
|
static inline int flag_to_index(MEMFLAGS flag) {
|
||||||
assert(flag_is_valid(flag), "Invalid flag");
|
assert(flag_is_valid(flag), "Invalid flag (%u)", (unsigned)flag);
|
||||||
return static_cast<int>(flag);
|
return static_cast<int>(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class NMTUtil : AllStatic {
|
|||||||
|
|
||||||
// Map an index to memory type
|
// Map an index to memory type
|
||||||
static MEMFLAGS index_to_flag(int index) {
|
static MEMFLAGS index_to_flag(int index) {
|
||||||
assert(flag_index_is_valid(index), "Invalid flag");
|
assert(flag_index_is_valid(index), "Invalid flag index (%d)", index);
|
||||||
return static_cast<MEMFLAGS>(index);
|
return static_cast<MEMFLAGS>(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,6 +478,10 @@ extern "C" JNIEXPORT void verify() {
|
|||||||
extern "C" JNIEXPORT void pp(void* p) {
|
extern "C" JNIEXPORT void pp(void* p) {
|
||||||
Command c("pp");
|
Command c("pp");
|
||||||
FlagSetting fl(DisplayVMOutput, true);
|
FlagSetting fl(DisplayVMOutput, true);
|
||||||
|
if (p == NULL) {
|
||||||
|
tty->print_cr("NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Universe::heap()->is_in(p)) {
|
if (Universe::heap()->is_in(p)) {
|
||||||
oop obj = cast_to_oop(p);
|
oop obj = cast_to_oop(p);
|
||||||
obj->print();
|
obj->print();
|
||||||
|
Loading…
Reference in New Issue
Block a user