mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
wip
This commit is contained in:
parent
592606e9cf
commit
be59b7dece
@ -61,6 +61,11 @@
|
||||
char txt[1024]; \
|
||||
stringStream ss(txt, sizeof(txt)); \
|
||||
o->klass()->name()->print_value_on(&ss); \
|
||||
if (o->is_objArray()) { \
|
||||
const objArrayOop pointee_oa = (objArrayOop) o; \
|
||||
const int array_len = pointee_oa->length(); \
|
||||
ss.print(" [%d]", array_len); \
|
||||
} \
|
||||
printf("%s: " PTR_FORMAT " %s ", prefix, p2i(o), txt); \
|
||||
printf("\n"); \
|
||||
fflush(stdout); \
|
||||
@ -72,6 +77,11 @@
|
||||
char txt[1024]; \
|
||||
stringStream ss(txt, sizeof(txt)); \
|
||||
o->klass()->name()->print_value_on(&ss); \
|
||||
if (o->is_objArray()) { \
|
||||
const objArrayOop pointee_oa = (objArrayOop) o; \
|
||||
const int array_len = pointee_oa->length(); \
|
||||
ss.print(" [%d]", array_len); \
|
||||
} \
|
||||
printf("%s: " PTR_FORMAT " %s ", prefix, p2i(o), txt); \
|
||||
printf(format, __VA_ARGS__); \
|
||||
printf("\n"); \
|
||||
@ -101,7 +111,9 @@ void DFSClosure::find_leaks_from_root_set(EdgeStore* edge_store,
|
||||
|
||||
TRC("SCANNING ROOTS");
|
||||
|
||||
// Mark root set, to avoid going sideways
|
||||
// Mark root set, to avoid going sideways. The intent here is to prevent
|
||||
// long reference chains that would be caused by tracing through multiple root
|
||||
// objects.
|
||||
DFSClosure dfs(edge_store, mark_bits, nullptr);
|
||||
dfs._max_depth = 1;
|
||||
RootSetClosure<DFSClosure> rs(&dfs);
|
||||
@ -138,7 +150,10 @@ DFSClosure::DFSClosure(EdgeStore* edge_store, JFRBitSet* mark_bits, const Edge*
|
||||
_max_depth(max_dfs_depth), _ignore_root_set(false),
|
||||
// _probe_stack(1024, 4, max_probe_stack_elems),
|
||||
_probe_stack(),
|
||||
_current_item(nullptr)
|
||||
_current_ref(UnifiedOopRef::encode_null()),
|
||||
_current_pointee(nullptr),
|
||||
_current_depth(0),
|
||||
_current_chunkindex(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -150,10 +165,11 @@ DFSClosure::~DFSClosure() {
|
||||
}
|
||||
#endif // ASSERT
|
||||
|
||||
void DFSClosure::push_to_probe_stack(UnifiedOopRef ref, oop pointee, size_t depth, int chunkindex) {
|
||||
void DFSClosure::probe_stack_push(UnifiedOopRef ref, oop pointee, size_t depth) {
|
||||
|
||||
assert(!ref.is_null(), "invariant");
|
||||
|
||||
if (pointee == nullptr) {
|
||||
// Don't push null references
|
||||
return;
|
||||
}
|
||||
|
||||
@ -167,74 +183,100 @@ void DFSClosure::push_to_probe_stack(UnifiedOopRef ref, oop pointee, size_t dept
|
||||
return;
|
||||
}
|
||||
|
||||
ProbeStackItem item { ref, checked_cast<unsigned>(depth), chunkindex };
|
||||
ProbeStackItem item { ref, checked_cast<unsigned>(depth), 0 };
|
||||
_probe_stack.push(item);
|
||||
|
||||
TRCOOPFMT("pushed", pointee, "path depth %u, probestack depth %zu", item.depth, _probe_stack.size());
|
||||
|
||||
}
|
||||
|
||||
void DFSClosure::probe_stack_push_followup_chunk(UnifiedOopRef ref, oop pointee, size_t depth, int chunkindex) {
|
||||
|
||||
assert(!ref.is_null(), "invariant");
|
||||
assert(pointee != nullptr, "invariant");
|
||||
assert(chunkindex > 0, "invariant");
|
||||
|
||||
if (_probe_stack.is_full()) {
|
||||
// Probe stack exhausted; see remarks about probe stack max depth above.
|
||||
return;
|
||||
}
|
||||
|
||||
ProbeStackItem item { ref, checked_cast<unsigned>(depth), chunkindex };
|
||||
_probe_stack.push(item);
|
||||
|
||||
TRCOOPFMT("pushed", pointee, "path depth %u, probestack depth %zu, chunkindex %d (follow-up)", item.depth, _probe_stack.size(), chunkindex);
|
||||
|
||||
}
|
||||
|
||||
bool DFSClosure::probe_stack_pop() {
|
||||
|
||||
if (_probe_stack.is_empty()) {
|
||||
_current_ref = UnifiedOopRef::encode_null();
|
||||
_current_pointee = nullptr;
|
||||
_current_depth = 0;
|
||||
_current_chunkindex = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
ProbeStackItem item = _probe_stack.pop();
|
||||
_current_ref = item.r;
|
||||
assert(!_current_ref.is_null(), "invariant");
|
||||
_current_depth = item.depth;
|
||||
assert(_current_depth < _max_depth, "invariant");
|
||||
_current_chunkindex = item.chunkindex;
|
||||
assert(_current_chunkindex >= 0, "invariant");
|
||||
|
||||
_current_pointee = _current_ref.dereference();
|
||||
|
||||
TRCOOPFMT("popped", _current_pointee, "path depth %zu, probestack depth %zu, chunkindex %d%s", _current_depth, _probe_stack.size(), _current_chunkindex, _current_chunkindex > 0 ? " (followup)" : "");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DFSClosure::handle_oop() {
|
||||
assert(_current_item != nullptr, "Sanity");
|
||||
assert(_current_item->chunkindex == 0, "Sanity");
|
||||
|
||||
const oop pointee = current_pointee();
|
||||
const size_t depth = current_depth();
|
||||
assert(depth < _max_depth, "Sanity (%zu)", depth);
|
||||
|
||||
|
||||
TRCOOPFMT("popped", pointee, "path depth %zu, probestack depth %zu", depth, _probe_stack.size());
|
||||
|
||||
if (depth == 0 && _ignore_root_set) {
|
||||
assert(pointee_was_visited(pointee), "We should have already visited roots");
|
||||
_reference_stack[depth] = _current_item->r;
|
||||
if (_current_depth == 0 && _ignore_root_set) {
|
||||
assert(pointee_was_visited(_current_pointee), "We should have already visited roots");
|
||||
_reference_stack[_current_depth] = _current_ref;
|
||||
// continue since we want to process children, too
|
||||
} else {
|
||||
if (pointee_was_visited(pointee)) {
|
||||
if (pointee_was_visited(_current_pointee)) {
|
||||
return; // already processed
|
||||
}
|
||||
mark_pointee_as_visited(pointee);
|
||||
_reference_stack[depth] = _current_item->r;
|
||||
if (pointee_was_sampled(pointee)) {
|
||||
TRC("=> SAMPLE OBJECT FOUND");
|
||||
mark_pointee_as_visited(_current_pointee);
|
||||
_reference_stack[_current_depth] = _current_ref;
|
||||
if (pointee_was_sampled(_current_pointee)) {
|
||||
TRC("=> SAMPLE OBJECT FOUND (handle_oop)");
|
||||
add_chain();
|
||||
}
|
||||
}
|
||||
|
||||
// trace children if needed
|
||||
if (depth == _max_depth - 1) {
|
||||
if (_current_depth == _max_depth - 1) {
|
||||
return; // stop following this chain
|
||||
}
|
||||
|
||||
pointee->oop_iterate(this);
|
||||
_current_pointee->oop_iterate(this);
|
||||
}
|
||||
|
||||
void DFSClosure::handle_objarrayoop() {
|
||||
|
||||
assert(_current_item != nullptr, "Sanity");
|
||||
ShouldNotReachHere();
|
||||
const oop pointee = current_pointee();
|
||||
const size_t depth = current_depth();
|
||||
assert(depth < _max_depth, "Sanity");
|
||||
|
||||
const int chunkindex = _current_item->chunkindex;
|
||||
assert(chunkindex >= 0, "Sanity");
|
||||
|
||||
if (current_depth() == 0 && _ignore_root_set) {
|
||||
assert(pointee_was_visited(pointee), "We should have already visited roots");
|
||||
_reference_stack[depth] = _current_item->r;
|
||||
if (_current_depth == 0 && _ignore_root_set) {
|
||||
assert(pointee_was_visited(_current_pointee), "We should have already visited roots");
|
||||
_reference_stack[_current_depth] = _current_ref;
|
||||
// continue since we want to process children, too
|
||||
} else {
|
||||
|
||||
if (chunkindex == 0) {
|
||||
if (_current_chunkindex == 0) {
|
||||
// For the first chunk only, check, process and mark the array oop itself.
|
||||
if (pointee_was_visited(pointee)) {
|
||||
if (pointee_was_visited(_current_pointee)) {
|
||||
return; // already processed
|
||||
}
|
||||
mark_pointee_as_visited(pointee);
|
||||
_reference_stack[depth] = _current_item->r;
|
||||
mark_pointee_as_visited(_current_pointee);
|
||||
_reference_stack[_current_depth] = _current_ref;
|
||||
|
||||
if (pointee_was_sampled(pointee)) {
|
||||
if (pointee_was_sampled(_current_pointee)) {
|
||||
TRC("=> SAMPLE OBJECT FOUND (handle_objarrayoop)");
|
||||
add_chain();
|
||||
}
|
||||
}
|
||||
@ -242,62 +284,49 @@ ShouldNotReachHere();
|
||||
|
||||
// trace children if needed
|
||||
|
||||
if (depth == _max_depth - 1) {
|
||||
if (_current_depth == _max_depth - 1) {
|
||||
return; // stop following this chain
|
||||
}
|
||||
|
||||
const objArrayOop pointee_oa = (objArrayOop) pointee;
|
||||
const objArrayOop pointee_oa = (objArrayOop) _current_pointee;
|
||||
const int array_len = pointee_oa->length();
|
||||
const int begidx = chunkindex * array_chunk_size;
|
||||
const int endidx = MIN2(array_len, (chunkindex + 1) * array_chunk_size);
|
||||
if (array_len == 0) {
|
||||
return;
|
||||
}
|
||||
const int begidx = _current_chunkindex * array_chunk_size;
|
||||
const int endidx = MIN2(array_len, (_current_chunkindex + 1) * array_chunk_size);
|
||||
assert(begidx < endidx, "invariant");
|
||||
|
||||
// Push follow-up chunk: same reference, same depth, next chunk index.
|
||||
// Do this before pushing the child references to preserve depth-first
|
||||
// traversal.
|
||||
// Push follow-up chunk
|
||||
if (endidx < array_len) {
|
||||
push_to_probe_stack(_current_item->r, pointee, depth, chunkindex + 1);
|
||||
probe_stack_push_followup_chunk(_current_ref, _current_pointee, _current_depth, _current_chunkindex + 1);
|
||||
}
|
||||
|
||||
// push child references
|
||||
if (begidx < endidx) {
|
||||
pointee_oa->oop_iterate_range(this, begidx, endidx);
|
||||
}
|
||||
pointee_oa->oop_iterate_range(this, begidx, endidx);
|
||||
}
|
||||
|
||||
void DFSClosure::drain_probe_stack() {
|
||||
|
||||
DEBUG_ONLY(unsigned last_depth = 0;)
|
||||
|
||||
while (!_probe_stack.is_empty() &&
|
||||
!GranularTimer::is_finished()) {
|
||||
while (probe_stack_pop() && !GranularTimer::is_finished()) {
|
||||
|
||||
const ProbeStackItem item = _probe_stack.pop();
|
||||
assert(item.depth <= (last_depth + 1), "jumping nodes?");
|
||||
// We should not dive downward more than 1 indirection.
|
||||
assert(_current_depth <= (last_depth + 1), "invariant");
|
||||
|
||||
// anchor current item
|
||||
_current_item = &item;
|
||||
|
||||
assert(!_current_item->r.is_null(), "invariant");
|
||||
assert(current_pointee() != nullptr, "invariant");
|
||||
|
||||
//if (current_pointee()->is_objArray()) {
|
||||
if (false) {
|
||||
if (_current_pointee->is_objArray()) {
|
||||
handle_objarrayoop();
|
||||
} else {
|
||||
handle_oop();
|
||||
}
|
||||
|
||||
DEBUG_ONLY(last_depth = item.depth;)
|
||||
|
||||
// reset current item
|
||||
_current_item = nullptr;
|
||||
|
||||
DEBUG_ONLY(last_depth = _current_depth;)
|
||||
}
|
||||
}
|
||||
|
||||
void DFSClosure::add_chain() {
|
||||
const size_t depth = current_depth();
|
||||
const size_t array_length = depth + 2;
|
||||
const size_t array_length = _current_depth + 2;
|
||||
|
||||
ResourceMark rm;
|
||||
Edge* const chain = NEW_RESOURCE_ARRAY(Edge, array_length);
|
||||
@ -305,7 +334,7 @@ void DFSClosure::add_chain() {
|
||||
|
||||
if (UseNewCode) {
|
||||
TRC("---- reference stack ----");
|
||||
for (size_t i = 0; i <= depth; i++) {
|
||||
for (size_t i = 0; i <= _current_depth; i++) {
|
||||
const oop pointee = _reference_stack[i].dereference();
|
||||
TRCOOP("", pointee);
|
||||
}
|
||||
@ -313,12 +342,12 @@ if (UseNewCode) {
|
||||
}
|
||||
|
||||
// aggregate from depth-first search
|
||||
for (size_t i = 0; i <= depth; i++) {
|
||||
for (size_t i = 0; i <= _current_depth; i++) {
|
||||
const size_t next = idx + 1;
|
||||
const size_t d = depth - i;
|
||||
const size_t d = _current_depth - i;
|
||||
chain[idx++] = Edge(&chain[next], _reference_stack[d]);
|
||||
}
|
||||
assert(depth + 1 == idx, "invariant");
|
||||
assert(_current_depth + 1 == idx, "invariant");
|
||||
assert(array_length == idx + 1, "invariant");
|
||||
|
||||
// aggregate from breadth-first search
|
||||
@ -334,20 +363,19 @@ void DFSClosure::do_oop(oop* ref) {
|
||||
assert(ref != nullptr, "invariant");
|
||||
assert(is_aligned(ref, HeapWordSize), "invariant");
|
||||
const oop pointee = HeapAccess<AS_NO_KEEPALIVE>::oop_load(ref);
|
||||
push_to_probe_stack(UnifiedOopRef::encode_in_heap(ref), pointee, current_depth() + 1, 0);
|
||||
probe_stack_push(UnifiedOopRef::encode_in_heap(ref), pointee, _current_depth + 1);
|
||||
}
|
||||
|
||||
void DFSClosure::do_oop(narrowOop* ref) {
|
||||
assert(ref != nullptr, "invariant");
|
||||
assert(is_aligned(ref, sizeof(narrowOop)), "invariant");
|
||||
const oop pointee = HeapAccess<AS_NO_KEEPALIVE>::oop_load(ref);
|
||||
push_to_probe_stack(UnifiedOopRef::encode_in_heap(ref), pointee, current_depth() + 1, 0);
|
||||
probe_stack_push(UnifiedOopRef::encode_in_heap(ref), pointee, _current_depth + 1);
|
||||
}
|
||||
|
||||
void DFSClosure::do_root(UnifiedOopRef ref) {
|
||||
assert(!ref.is_null(), "invariant");
|
||||
const oop pointee = ref.dereference();
|
||||
assert(pointee != nullptr, "invariant");
|
||||
assert(_current_item == nullptr, "invariant");
|
||||
push_to_probe_stack(ref, pointee, 0, 0);
|
||||
probe_stack_push(ref, pointee, 0);
|
||||
}
|
||||
|
||||
@ -61,9 +61,11 @@ class DFSClosure : public BasicOopIterateClosure {
|
||||
};
|
||||
Stack<ProbeStackItem, mtTracing> _probe_stack;
|
||||
|
||||
const ProbeStackItem* _current_item;
|
||||
oop current_pointee() const { return _current_item->r.dereference(); }
|
||||
size_t current_depth() const { return _current_item == nullptr ? 0 : _current_item->depth; }
|
||||
// Walkstate
|
||||
UnifiedOopRef _current_ref;
|
||||
oop _current_pointee;
|
||||
size_t _current_depth;
|
||||
int _current_chunkindex;
|
||||
|
||||
bool pointee_was_visited(const oop pointee) const { return _mark_bits->is_marked(pointee); }
|
||||
void mark_pointee_as_visited(const oop pointee) { _mark_bits->mark_obj(pointee); }
|
||||
@ -73,7 +75,9 @@ class DFSClosure : public BasicOopIterateClosure {
|
||||
void handle_oop();
|
||||
void handle_objarrayoop();
|
||||
|
||||
void push_to_probe_stack(UnifiedOopRef ref, oop pointee, size_t depth, int chunkindex);
|
||||
void probe_stack_push_followup_chunk(UnifiedOopRef ref, oop pointee, size_t depth, int chunkindex);
|
||||
void probe_stack_push(UnifiedOopRef ref, oop pointee, size_t depth);
|
||||
bool probe_stack_pop();
|
||||
|
||||
public:
|
||||
virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS_EXCEPT_REFERENT; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user