apetushkov@9858: /* ddong@9885: * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. apetushkov@9858: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. apetushkov@9858: * apetushkov@9858: * This code is free software; you can redistribute it and/or modify it apetushkov@9858: * under the terms of the GNU General Public License version 2 only, as apetushkov@9858: * published by the Free Software Foundation. apetushkov@9858: * apetushkov@9858: * This code is distributed in the hope that it will be useful, but WITHOUT apetushkov@9858: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or apetushkov@9858: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License apetushkov@9858: * version 2 for more details (a copy is included in the LICENSE file that apetushkov@9858: * accompanied this code). apetushkov@9858: * apetushkov@9858: * You should have received a copy of the GNU General Public License version apetushkov@9858: * 2 along with this work; if not, write to the Free Software Foundation, apetushkov@9858: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. apetushkov@9858: * apetushkov@9858: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA apetushkov@9858: * or visit www.oracle.com if you need additional information or have any apetushkov@9858: * questions. apetushkov@9858: * apetushkov@9858: */ apetushkov@9858: #include "precompiled.hpp" apetushkov@9858: #include "jfr/jfrEvents.hpp" apetushkov@9858: #include "jfr/leakprofiler/sampling/objectSample.hpp" apetushkov@9858: #include "jfr/leakprofiler/sampling/objectSampler.hpp" apetushkov@9858: #include "jfr/leakprofiler/sampling/sampleList.hpp" apetushkov@9858: #include "jfr/leakprofiler/sampling/samplePriorityQueue.hpp" apetushkov@9858: #include "jfr/recorder/jfrEventSetting.inline.hpp" apetushkov@9858: #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" apetushkov@9858: #include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" apetushkov@9858: #include "jfr/support/jfrThreadLocal.hpp" apetushkov@9858: #include "jfr/utilities/jfrTryLock.hpp" apetushkov@9858: #include "memory/universe.hpp" apetushkov@9858: #include "oops/oop.inline.hpp" ddong@9885: #include "runtime/atomic.hpp" ddong@9885: #include "runtime/orderAccess.hpp" ddong@9885: #include "runtime/safepoint.hpp" apetushkov@9858: #include "runtime/thread.hpp" apetushkov@9858: ddong@9885: static ObjectSampler* _instance = NULL; ddong@9885: ddong@9885: static ObjectSampler& instance() { ddong@9885: assert(_instance != NULL, "invariant"); ddong@9885: return *_instance; ddong@9885: } ddong@9885: apetushkov@9858: ObjectSampler::ObjectSampler(size_t size) : apetushkov@9858: _priority_queue(new SamplePriorityQueue(size)), apetushkov@9858: _list(new SampleList(size)), apetushkov@9858: _last_sweep(JfrTicks::now()), apetushkov@9858: _total_allocated(0), apetushkov@9858: _threshold(0), apetushkov@9858: _size(size), apetushkov@9858: _dead_samples(false) {} apetushkov@9858: apetushkov@9858: ObjectSampler::~ObjectSampler() { apetushkov@9858: delete _priority_queue; apetushkov@9858: _priority_queue = NULL; apetushkov@9858: delete _list; apetushkov@9858: _list = NULL; apetushkov@9858: } apetushkov@9858: ddong@9885: bool ObjectSampler::create(size_t size) { ddong@9885: assert(SafepointSynchronize::is_at_safepoint(), "invariant"); ddong@9885: assert(_instance == NULL, "invariant"); ddong@9885: _instance = new ObjectSampler(size); ddong@9885: return _instance != NULL; ddong@9885: } ddong@9885: ddong@9885: bool ObjectSampler::is_created() { ddong@9885: return _instance != NULL; ddong@9885: } ddong@9885: ddong@9885: ObjectSampler* ObjectSampler::sampler() { ddong@9885: assert(is_created(), "invariant"); ddong@9885: return _instance; ddong@9885: } ddong@9885: ddong@9885: void ObjectSampler::destroy() { ddong@9885: assert(SafepointSynchronize::is_at_safepoint(), "invariant"); ddong@9885: if (_instance != NULL) { ddong@9885: ObjectSampler* const sampler = _instance; ddong@9885: _instance = NULL; ddong@9885: delete sampler; ddong@9885: } ddong@9885: } ddong@9885: ddong@9885: static volatile int _lock = 0; ddong@9885: ddong@9885: ObjectSampler* ObjectSampler::acquire() { ddong@9885: assert(is_created(), "invariant"); ddong@9885: while (Atomic::cmpxchg(1, &_lock, 0) == 1) {} ddong@9885: return _instance; ddong@9885: } ddong@9885: ddong@9885: void ObjectSampler::release() { ddong@9885: assert(is_created(), "invariant"); ddong@9885: OrderAccess::fence(); ddong@9885: _lock = 0; ddong@9885: } ddong@9885: ddong@9885: static traceid get_thread_id(JavaThread* thread) { apetushkov@9858: assert(thread != NULL, "invariant"); ddong@9885: if (thread->threadObj() == NULL) { ddong@9885: return 0; ddong@9885: } ddong@9885: const JfrThreadLocal* const tl = thread->jfr_thread_local(); ddong@9885: assert(tl != NULL, "invariant"); ddong@9885: if (!tl->has_thread_checkpoint()) { ddong@9885: JfrCheckpointManager::create_thread_checkpoint(thread); ddong@9885: } ddong@9885: assert(tl->has_thread_checkpoint(), "invariant"); ddong@9885: return tl->thread_id(); ddong@9885: } ddong@9885: ddong@9885: // Populates the thread local stack frames, but does not add them ddong@9885: // to the stacktrace repository (...yet, see stacktrace_id() below) ddong@9885: // ddong@9885: void ObjectSampler::fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread) { ddong@9885: assert(stacktrace != NULL, "invariant"); ddong@9885: assert(thread != NULL, "invariant"); ddong@9885: if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { ddong@9885: JfrStackTraceRepository::fill_stacktrace_for(thread, stacktrace, 0); ddong@9885: } ddong@9885: } ddong@9885: ddong@9885: // We were successful in acquiring the try lock and have been selected for adding a sample. ddong@9885: // Go ahead with installing our previously taken stacktrace into the stacktrace repository. ddong@9885: // ddong@9885: traceid ObjectSampler::stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread) { ddong@9885: assert(stacktrace != NULL, "invariant"); ddong@9885: assert(stacktrace->hash() != 0, "invariant"); ddong@9885: const traceid stacktrace_id = JfrStackTraceRepository::add(stacktrace, thread); ddong@9885: thread->jfr_thread_local()->set_cached_stack_trace_id(stacktrace_id, stacktrace->hash()); ddong@9885: return stacktrace_id; ddong@9885: } ddong@9885: ddong@9885: void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread) { ddong@9885: assert(thread != NULL, "invariant"); ddong@9885: assert(is_created(), "invariant"); ddong@9885: ddong@9885: const traceid thread_id = get_thread_id(thread); apetushkov@9858: if (thread_id == 0) { apetushkov@9858: return; apetushkov@9858: } ddong@9885: const JfrThreadLocal* const tl = thread->jfr_thread_local(); ddong@9885: JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth()); ddong@9885: fill_stacktrace(&stacktrace, thread); apetushkov@9858: ddong@9885: // try enter critical section ddong@9885: JfrTryLock tryLock(&_lock); apetushkov@9858: if (!tryLock.has_lock()) { apetushkov@9858: if (LogJFR && Verbose) tty->print_cr("Skipping old object sample due to lock contention"); apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: ddong@9885: instance().add(obj, allocated, thread_id, &stacktrace, thread); ddong@9885: } ddong@9885: ddong@9885: void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread) { ddong@9885: assert(stacktrace != NULL, "invariant"); ddong@9885: assert(thread_id != 0, "invariant"); ddong@9885: assert(thread != NULL, "invariant"); ddong@9885: assert(thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); ddong@9885: apetushkov@9858: if (_dead_samples) { apetushkov@9858: scavenge(); apetushkov@9858: assert(!_dead_samples, "invariant"); apetushkov@9858: } apetushkov@9858: apetushkov@9858: _total_allocated += allocated; apetushkov@9858: const size_t span = _total_allocated - _priority_queue->total(); apetushkov@9858: ObjectSample* sample; apetushkov@9858: if ((size_t)_priority_queue->count() == _size) { apetushkov@9858: assert(_list->count() == _size, "invariant"); apetushkov@9858: const ObjectSample* peek = _priority_queue->peek(); apetushkov@9858: if (peek->span() > span) { apetushkov@9858: // quick reject, will not fit apetushkov@9858: return; apetushkov@9858: } apetushkov@9858: sample = _list->reuse(_priority_queue->pop()); apetushkov@9858: } else { apetushkov@9858: sample = _list->get(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: assert(sample != NULL, "invariant"); apetushkov@9858: sample->set_thread_id(thread_id); apetushkov@9858: sample->set_thread_checkpoint(thread->jfr_thread_local()->thread_checkpoint()); apetushkov@9858: ddong@9885: const unsigned int stacktrace_hash = stacktrace->hash(); ddong@9885: if (stacktrace_hash != 0) { ddong@9885: sample->set_stack_trace_id(stacktrace_id(stacktrace, thread)); ddong@9885: sample->set_stack_trace_hash(stacktrace_hash); apetushkov@9858: } apetushkov@9858: apetushkov@9858: sample->set_span(allocated); apetushkov@9858: sample->set_object((oop)obj); apetushkov@9858: sample->set_allocated(allocated); apetushkov@9858: sample->set_allocation_time(JfrTicks::now()); apetushkov@9858: sample->set_heap_used_at_last_gc(Universe::get_heap_used_at_last_gc()); apetushkov@9858: _priority_queue->push(sample); apetushkov@9858: } apetushkov@9858: ddong@9885: void ObjectSampler::scavenge() { ddong@9885: ObjectSample* current = _list->last(); ddong@9885: while (current != NULL) { ddong@9885: ObjectSample* next = current->next(); ddong@9885: if (current->is_dead()) { ddong@9885: remove_dead(current); ddong@9885: } ddong@9885: current = next; ddong@9885: } ddong@9885: _dead_samples = false; ddong@9885: } ddong@9885: ddong@9885: void ObjectSampler::remove_dead(ObjectSample* sample) { ddong@9885: assert(sample != NULL, "invariant"); ddong@9885: assert(sample->is_dead(), "invariant"); ddong@9885: ObjectSample* const previous = sample->prev(); ddong@9885: // push span on to previous ddong@9885: if (previous != NULL) { ddong@9885: _priority_queue->remove(previous); ddong@9885: previous->add_span(sample->span()); ddong@9885: _priority_queue->push(previous); ddong@9885: } ddong@9885: _priority_queue->remove(sample); ddong@9885: _list->release(sample); ddong@9885: } ddong@9885: ddong@9885: void ObjectSampler::oops_do(BoolObjectClosure* is_alive, OopClosure* f) { ddong@9885: assert(is_created(), "invariant"); ddong@9885: assert(SafepointSynchronize::is_at_safepoint(), "invariant"); ddong@9885: ObjectSampler& sampler = instance(); ddong@9885: ObjectSample* current = sampler._list->last(); ddong@9885: while (current != NULL) { ddong@9885: ObjectSample* next = current->next(); ddong@9885: if (!current->is_dead()) { ddong@9885: if (is_alive->do_object_b(current->object())) { ddong@9885: // The weakly referenced object is alive, update pointer ddong@9885: f->do_oop(const_cast(current->object_addr())); ddong@9885: } else { ddong@9885: current->set_dead(); ddong@9885: sampler._dead_samples = true; ddong@9885: } ddong@9885: } ddong@9885: current = next; ddong@9885: } ddong@9885: sampler._last_sweep = JfrTicks::now(); ddong@9885: } ddong@9885: apetushkov@9858: const ObjectSample* ObjectSampler::last() const { apetushkov@9858: return _list->last(); apetushkov@9858: } apetushkov@9858: egahlin@9867: const ObjectSample* ObjectSampler::first() const { egahlin@9867: return _list->first(); egahlin@9867: } egahlin@9867: apetushkov@9858: const ObjectSample* ObjectSampler::last_resolved() const { apetushkov@9858: return _list->last_resolved(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: void ObjectSampler::set_last_resolved(const ObjectSample* sample) { apetushkov@9858: _list->set_last_resolved(sample); apetushkov@9858: } apetushkov@9858: apetushkov@9858: int ObjectSampler::item_count() const { apetushkov@9858: return _priority_queue->count(); apetushkov@9858: } apetushkov@9858: apetushkov@9858: const ObjectSample* ObjectSampler::item_at(int index) const { apetushkov@9858: return _priority_queue->item_at(index); apetushkov@9858: } apetushkov@9858: apetushkov@9858: ObjectSample* ObjectSampler::item_at(int index) { apetushkov@9858: return const_cast( apetushkov@9858: const_cast(this)->item_at(index) ddong@9885: ); apetushkov@9858: } apetushkov@9858: apetushkov@9858: const JfrTicks& ObjectSampler::last_sweep() const { apetushkov@9858: return _last_sweep; apetushkov@9858: }