mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-23 04:24:49 +02:00
8013651: NMT: reserve/release sequence id's in incorrect order due to race
Fixed NMT race condition for realloc, uncommit and release Reviewed-by: coleenp, ccheung
This commit is contained in:
parent
87505d60f6
commit
61a30f035b
14 changed files with 464 additions and 252 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2013, 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
|
||||
|
@ -54,6 +54,18 @@ class MemTracker : AllStatic {
|
|||
NMT_sequence_overflow // overflow the sequence number
|
||||
};
|
||||
|
||||
class Tracker {
|
||||
public:
|
||||
void discard() { }
|
||||
|
||||
void record(address addr, size_t size = 0, MEMFLAGS flags = mtNone, address pc = NULL) { }
|
||||
void record(address old_addr, address new_addr, size_t size,
|
||||
MEMFLAGS flags, address pc = NULL) { }
|
||||
};
|
||||
|
||||
private:
|
||||
static Tracker _tkr;
|
||||
|
||||
|
||||
public:
|
||||
static inline void init_tracking_options(const char* option_line) { }
|
||||
|
@ -68,19 +80,18 @@ class MemTracker : AllStatic {
|
|||
static inline void record_malloc(address addr, size_t size, MEMFLAGS flags,
|
||||
address pc = 0, Thread* thread = NULL) { }
|
||||
static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) { }
|
||||
static inline void record_realloc(address old_addr, address new_addr, size_t size,
|
||||
MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { }
|
||||
static inline void record_arena_size(address addr, size_t size) { }
|
||||
static inline void record_virtual_memory_reserve(address addr, size_t size,
|
||||
address pc = 0, Thread* thread = NULL) { }
|
||||
MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { }
|
||||
static inline void record_virtual_memory_reserve_and_commit(address addr, size_t size,
|
||||
MEMFLAGS flags, address pc = 0, Thread* thread = NULL) { }
|
||||
static inline void record_virtual_memory_commit(address addr, size_t size,
|
||||
address pc = 0, Thread* thread = NULL) { }
|
||||
static inline void record_virtual_memory_uncommit(address addr, size_t size,
|
||||
Thread* thread = NULL) { }
|
||||
static inline void record_virtual_memory_release(address addr, size_t size,
|
||||
Thread* thread = NULL) { }
|
||||
static inline void record_virtual_memory_type(address base, MEMFLAGS flags,
|
||||
Thread* thread = NULL) { }
|
||||
static inline Tracker get_realloc_tracker() { return _tkr; }
|
||||
static inline Tracker get_virtual_memory_uncommit_tracker() { return _tkr; }
|
||||
static inline Tracker get_virtual_memory_release_tracker() { return _tkr; }
|
||||
static inline bool baseline() { return false; }
|
||||
static inline bool has_baseline() { return false; }
|
||||
|
||||
|
@ -164,6 +175,45 @@ class MemTracker : AllStatic {
|
|||
NMT_shutdown // shutdown
|
||||
};
|
||||
|
||||
public:
|
||||
class Tracker : public StackObj {
|
||||
friend class MemTracker;
|
||||
public:
|
||||
enum MemoryOperation {
|
||||
NoOp, // no op
|
||||
Malloc, // malloc
|
||||
Realloc, // realloc
|
||||
Free, // free
|
||||
Reserve, // virtual memory reserve
|
||||
Commit, // virtual memory commit
|
||||
ReserveAndCommit, // virtual memory reserve and commit
|
||||
StackAlloc = ReserveAndCommit, // allocate thread stack
|
||||
Type, // assign virtual memory type
|
||||
Uncommit, // virtual memory uncommit
|
||||
Release, // virtual memory release
|
||||
ArenaSize, // set arena size
|
||||
StackRelease // release thread stack
|
||||
};
|
||||
|
||||
|
||||
protected:
|
||||
Tracker(MemoryOperation op, Thread* thr = NULL);
|
||||
|
||||
public:
|
||||
void discard();
|
||||
|
||||
void record(address addr, size_t size = 0, MEMFLAGS flags = mtNone, address pc = NULL);
|
||||
void record(address old_addr, address new_addr, size_t size,
|
||||
MEMFLAGS flags, address pc = NULL);
|
||||
|
||||
private:
|
||||
bool _need_thread_critical_lock;
|
||||
JavaThread* _java_thread;
|
||||
MemoryOperation _op; // memory operation
|
||||
jint _seq; // reserved sequence number
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
// native memory tracking level
|
||||
enum NMTLevel {
|
||||
|
@ -276,109 +326,74 @@ class MemTracker : AllStatic {
|
|||
// record a 'malloc' call
|
||||
static inline void record_malloc(address addr, size_t size, MEMFLAGS flags,
|
||||
address pc = 0, Thread* thread = NULL) {
|
||||
if (is_on() && NMT_CAN_TRACK(flags)) {
|
||||
assert(size > 0, "Sanity check");
|
||||
create_memory_record(addr, (flags|MemPointerRecord::malloc_tag()), size, pc, thread);
|
||||
}
|
||||
Tracker tkr(Tracker::Malloc, thread);
|
||||
tkr.record(addr, size, flags, pc);
|
||||
}
|
||||
// record a 'free' call
|
||||
static inline void record_free(address addr, MEMFLAGS flags, Thread* thread = NULL) {
|
||||
if (is_on() && NMT_CAN_TRACK(flags)) {
|
||||
create_memory_record(addr, MemPointerRecord::free_tag(), 0, 0, thread);
|
||||
}
|
||||
}
|
||||
// record a 'realloc' call
|
||||
static inline void record_realloc(address old_addr, address new_addr, size_t size,
|
||||
MEMFLAGS flags, address pc = 0, Thread* thread = NULL) {
|
||||
if (is_on() && NMT_CAN_TRACK(flags)) {
|
||||
assert(size > 0, "Sanity check");
|
||||
record_free(old_addr, flags, thread);
|
||||
record_malloc(new_addr, size, flags, pc, thread);
|
||||
}
|
||||
Tracker tkr(Tracker::Free, thread);
|
||||
tkr.record(addr, 0, flags, DEBUG_CALLER_PC);
|
||||
}
|
||||
|
||||
// record arena memory size
|
||||
static inline void record_arena_size(address addr, size_t size) {
|
||||
// we add a positive offset to arena address, so we can have arena memory record
|
||||
// sorted after arena record
|
||||
if (is_on() && !UseMallocOnly) {
|
||||
assert(addr != NULL, "Sanity check");
|
||||
create_memory_record((addr + sizeof(void*)), MemPointerRecord::arena_size_tag(), size,
|
||||
DEBUG_CALLER_PC, NULL);
|
||||
}
|
||||
Tracker tkr(Tracker::ArenaSize);
|
||||
tkr.record(addr, size);
|
||||
}
|
||||
|
||||
// record a virtual memory 'reserve' call
|
||||
static inline void record_virtual_memory_reserve(address addr, size_t size,
|
||||
address pc = 0, Thread* thread = NULL) {
|
||||
if (is_on()) {
|
||||
assert(size > 0, "Sanity check");
|
||||
create_memory_record(addr, MemPointerRecord::virtual_memory_reserve_tag(),
|
||||
size, pc, thread);
|
||||
}
|
||||
MEMFLAGS flags, address pc = 0, Thread* thread = NULL) {
|
||||
assert(size > 0, "Sanity check");
|
||||
Tracker tkr(Tracker::Reserve, thread);
|
||||
tkr.record(addr, size, flags, pc);
|
||||
}
|
||||
|
||||
static inline void record_thread_stack(address addr, size_t size, Thread* thr,
|
||||
address pc = 0) {
|
||||
if (is_on()) {
|
||||
assert(size > 0 && thr != NULL, "Sanity check");
|
||||
create_memory_record(addr, MemPointerRecord::virtual_memory_reserve_tag() | mtThreadStack,
|
||||
size, pc, thr);
|
||||
create_memory_record(addr, MemPointerRecord::virtual_memory_commit_tag() | mtThreadStack,
|
||||
size, pc, thr);
|
||||
}
|
||||
Tracker tkr(Tracker::StackAlloc, thr);
|
||||
tkr.record(addr, size, mtThreadStack, pc);
|
||||
}
|
||||
|
||||
static inline void release_thread_stack(address addr, size_t size, Thread* thr) {
|
||||
if (is_on()) {
|
||||
assert(size > 0 && thr != NULL, "Sanity check");
|
||||
assert(!thr->is_Java_thread(), "too early");
|
||||
create_memory_record(addr, MemPointerRecord::virtual_memory_uncommit_tag() | mtThreadStack,
|
||||
size, DEBUG_CALLER_PC, thr);
|
||||
create_memory_record(addr, MemPointerRecord::virtual_memory_release_tag() | mtThreadStack,
|
||||
size, DEBUG_CALLER_PC, thr);
|
||||
}
|
||||
Tracker tkr(Tracker::StackRelease, thr);
|
||||
tkr.record(addr, size, mtThreadStack, DEBUG_CALLER_PC);
|
||||
}
|
||||
|
||||
// record a virtual memory 'commit' call
|
||||
static inline void record_virtual_memory_commit(address addr, size_t size,
|
||||
address pc, Thread* thread = NULL) {
|
||||
if (is_on()) {
|
||||
assert(size > 0, "Sanity check");
|
||||
create_memory_record(addr, MemPointerRecord::virtual_memory_commit_tag(),
|
||||
size, pc, thread);
|
||||
}
|
||||
Tracker tkr(Tracker::Commit, thread);
|
||||
tkr.record(addr, size, mtNone, pc);
|
||||
}
|
||||
|
||||
// record a virtual memory 'uncommit' call
|
||||
static inline void record_virtual_memory_uncommit(address addr, size_t size,
|
||||
Thread* thread = NULL) {
|
||||
if (is_on()) {
|
||||
assert(size > 0, "Sanity check");
|
||||
create_memory_record(addr, MemPointerRecord::virtual_memory_uncommit_tag(),
|
||||
size, DEBUG_CALLER_PC, thread);
|
||||
}
|
||||
static inline void record_virtual_memory_reserve_and_commit(address addr, size_t size,
|
||||
MEMFLAGS flags, address pc, Thread* thread = NULL) {
|
||||
Tracker tkr(Tracker::ReserveAndCommit, thread);
|
||||
tkr.record(addr, size, flags, pc);
|
||||
}
|
||||
|
||||
// record a virtual memory 'release' call
|
||||
static inline void record_virtual_memory_release(address addr, size_t size,
|
||||
Thread* thread = NULL) {
|
||||
if (is_on()) {
|
||||
assert(size > 0, "Sanity check");
|
||||
create_memory_record(addr, MemPointerRecord::virtual_memory_release_tag(),
|
||||
size, DEBUG_CALLER_PC, thread);
|
||||
}
|
||||
}
|
||||
|
||||
// record memory type on virtual memory base address
|
||||
static inline void record_virtual_memory_type(address base, MEMFLAGS flags,
|
||||
Thread* thread = NULL) {
|
||||
if (is_on()) {
|
||||
assert(base > 0, "wrong base address");
|
||||
assert((flags & (~mt_masks)) == 0, "memory type only");
|
||||
create_memory_record(base, (flags | MemPointerRecord::virtual_memory_type_tag()),
|
||||
0, DEBUG_CALLER_PC, thread);
|
||||
}
|
||||
Tracker tkr(Tracker::Type);
|
||||
tkr.record(base, 0, flags);
|
||||
}
|
||||
|
||||
// Get memory trackers for memory operations that can result race conditions.
|
||||
// The memory tracker has to be obtained before realloc, virtual memory uncommit
|
||||
// and virtual memory release, and call tracker.record() method if operation
|
||||
// succeeded, or tracker.discard() to abort the tracking.
|
||||
static inline Tracker get_realloc_tracker() {
|
||||
return Tracker(Tracker::Realloc);
|
||||
}
|
||||
|
||||
static inline Tracker get_virtual_memory_uncommit_tracker() {
|
||||
return Tracker(Tracker::Uncommit);
|
||||
}
|
||||
|
||||
static inline Tracker get_virtual_memory_release_tracker() {
|
||||
return Tracker(Tracker::Release);
|
||||
}
|
||||
|
||||
|
||||
|
@ -444,6 +459,31 @@ class MemTracker : AllStatic {
|
|||
static MemRecorder* get_pending_recorders();
|
||||
static void delete_all_pending_recorders();
|
||||
|
||||
// write a memory tracking record in recorder
|
||||
static void write_tracking_record(address addr, MEMFLAGS type,
|
||||
size_t size, jint seq, address pc, JavaThread* thread);
|
||||
|
||||
static bool is_single_threaded_bootstrap() {
|
||||
return _state == NMT_bootstrapping_single_thread;
|
||||
}
|
||||
|
||||
static void check_NMT_load(Thread* thr) {
|
||||
assert(thr != NULL, "Sanity check");
|
||||
if (_slowdown_calling_thread && thr != _worker_thread) {
|
||||
os::yield_all();
|
||||
}
|
||||
}
|
||||
|
||||
static void inc_pending_op_count() {
|
||||
Atomic::inc(&_pending_op_count);
|
||||
}
|
||||
|
||||
static void dec_pending_op_count() {
|
||||
Atomic::dec(&_pending_op_count);
|
||||
assert(_pending_op_count >= 0, "Sanity check");
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
// retrieve a pooled memory record or create new one if there is not
|
||||
// one available
|
||||
|
@ -522,6 +562,12 @@ class MemTracker : AllStatic {
|
|||
// if NMT should slow down calling thread to allow
|
||||
// worker thread to catch up
|
||||
static volatile bool _slowdown_calling_thread;
|
||||
|
||||
// pending memory op count.
|
||||
// Certain memory ops need to pre-reserve sequence number
|
||||
// before memory operation can happen to avoid race condition.
|
||||
// See MemTracker::Tracker for detail
|
||||
static volatile jint _pending_op_count;
|
||||
};
|
||||
|
||||
#endif // !INCLUDE_NMT
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue