Path: blob/master/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp
66645 views
/*1* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include "precompiled.hpp"25#include "jfr/jfrEvents.hpp"26#include "jfr/jni/jfrJavaSupport.hpp"27#include "jfr/leakprofiler/chains/edgeStore.hpp"28#include "jfr/leakprofiler/chains/objectSampleMarker.hpp"29#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp"30#include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp"31#include "jfr/leakprofiler/leakProfiler.hpp"32#include "jfr/leakprofiler/sampling/objectSample.hpp"33#include "jfr/leakprofiler/sampling/objectSampler.hpp"34#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp"35#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"36#include "jfr/recorder/service/jfrOptionSet.hpp"37#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp"38#include "jfr/support/jfrKlassUnloading.hpp"39#include "jfr/support/jfrMethodLookup.hpp"40#include "jfr/utilities/jfrHashtable.hpp"41#include "jfr/utilities/jfrPredicate.hpp"42#include "jfr/utilities/jfrRelation.hpp"43#include "memory/resourceArea.inline.hpp"44#include "oops/instanceKlass.inline.hpp"45#include "runtime/interfaceSupport.inline.hpp"46#include "runtime/mutexLocker.hpp"47#include "runtime/safepoint.hpp"48#include "runtime/thread.inline.hpp"4950const int initial_array_size = 64;5152template <typename T>53static GrowableArray<T>* c_heap_allocate_array(int size = initial_array_size) {54return new (ResourceObj::C_HEAP, mtTracing) GrowableArray<T>(size, mtTracing);55}5657static GrowableArray<traceid>* unloaded_thread_id_set = NULL;5859class ThreadIdExclusiveAccess : public StackObj {60private:61static Semaphore _mutex_semaphore;62public:63ThreadIdExclusiveAccess() { _mutex_semaphore.wait(); }64~ThreadIdExclusiveAccess() { _mutex_semaphore.signal(); }65};6667Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1);6869static bool has_thread_exited(traceid tid) {70assert(tid != 0, "invariant");71if (unloaded_thread_id_set == NULL) {72return false;73}74ThreadIdExclusiveAccess lock;75return JfrPredicate<traceid, compare_traceid>::test(unloaded_thread_id_set, tid);76}7778static void add_to_unloaded_thread_set(traceid tid) {79ThreadIdExclusiveAccess lock;80if (unloaded_thread_id_set == NULL) {81unloaded_thread_id_set = c_heap_allocate_array<traceid>();82}83JfrMutablePredicate<traceid, compare_traceid>::test(unloaded_thread_id_set, tid);84}8586void ObjectSampleCheckpoint::on_thread_exit(JavaThread* jt) {87assert(jt != NULL, "invariant");88if (LeakProfiler::is_running()) {89add_to_unloaded_thread_set(jt->jfr_thread_local()->thread_id());90}91}9293void ObjectSampleCheckpoint::clear() {94assert(SafepointSynchronize::is_at_safepoint(), "invariant");95if (unloaded_thread_id_set != NULL) {96delete unloaded_thread_id_set;97unloaded_thread_id_set = NULL;98}99assert(unloaded_thread_id_set == NULL, "invariant");100}101102template <typename Processor>103static void do_samples(ObjectSample* sample, const ObjectSample* end, Processor& processor) {104assert(sample != NULL, "invariant");105while (sample != end) {106processor.sample_do(sample);107sample = sample->next();108}109}110111template <typename Processor>112static void iterate_samples(Processor& processor, bool all = false) {113ObjectSampler* const sampler = ObjectSampler::sampler();114assert(sampler != NULL, "invariant");115ObjectSample* const last = sampler->last();116assert(last != NULL, "invariant");117do_samples(last, all ? NULL : sampler->last_resolved(), processor);118}119120class SampleMarker {121private:122ObjectSampleMarker& _marker;123jlong _last_sweep;124int _count;125public:126SampleMarker(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker), _last_sweep(last_sweep), _count(0) {}127void sample_do(ObjectSample* sample) {128if (sample->is_alive_and_older_than(_last_sweep)) {129_marker.mark(sample->object());130++_count;131}132}133int count() const {134return _count;135}136};137138int ObjectSampleCheckpoint::save_mark_words(const ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all) {139assert(sampler != NULL, "invariant");140if (sampler->last() == NULL) {141return 0;142}143SampleMarker sample_marker(marker, emit_all ? max_jlong : ObjectSampler::last_sweep());144iterate_samples(sample_marker, true);145return sample_marker.count();146}147148class BlobCache {149typedef HashTableHost<JfrBlobHandle, traceid, JfrHashtableEntry, BlobCache> BlobTable;150typedef BlobTable::HashEntry BlobEntry;151private:152BlobTable _table;153traceid _lookup_id;154public:155BlobCache(size_t size) : _table(this, size), _lookup_id(0) {}156JfrBlobHandle get(const ObjectSample* sample);157void put(const ObjectSample* sample, const JfrBlobHandle& blob);158// Hash table callbacks159void on_link(const BlobEntry* entry) const;160bool on_equals(uintptr_t hash, const BlobEntry* entry) const;161void on_unlink(BlobEntry* entry) const;162};163164JfrBlobHandle BlobCache::get(const ObjectSample* sample) {165assert(sample != NULL, "invariant");166_lookup_id = sample->stack_trace_id();167assert(_lookup_id != 0, "invariant");168BlobEntry* const entry = _table.lookup_only(sample->stack_trace_hash());169return entry != NULL ? entry->literal() : JfrBlobHandle();170}171172void BlobCache::put(const ObjectSample* sample, const JfrBlobHandle& blob) {173assert(sample != NULL, "invariant");174assert(_table.lookup_only(sample->stack_trace_hash()) == NULL, "invariant");175_lookup_id = sample->stack_trace_id();176assert(_lookup_id != 0, "invariant");177_table.put(sample->stack_trace_hash(), blob);178}179180inline void BlobCache::on_link(const BlobEntry* entry) const {181assert(entry != NULL, "invariant");182assert(entry->id() == 0, "invariant");183entry->set_id(_lookup_id);184}185186inline bool BlobCache::on_equals(uintptr_t hash, const BlobEntry* entry) const {187assert(entry != NULL, "invariant");188assert(entry->hash() == hash, "invariant");189return entry->id() == _lookup_id;190}191192inline void BlobCache::on_unlink(BlobEntry* entry) const {193assert(entry != NULL, "invariant");194}195196static GrowableArray<traceid>* id_set = NULL;197198static void prepare_for_resolution() {199id_set = new GrowableArray<traceid>(JfrOptionSet::old_object_queue_size());200}201202static bool stack_trace_precondition(const ObjectSample* sample) {203assert(sample != NULL, "invariant");204return sample->has_stack_trace_id() && !sample->is_dead();205}206207class StackTraceBlobInstaller {208private:209BlobCache _cache;210void install(ObjectSample* sample);211const JfrStackTrace* resolve(const ObjectSample* sample) const;212public:213StackTraceBlobInstaller() : _cache(JfrOptionSet::old_object_queue_size()) {214prepare_for_resolution();215}216~StackTraceBlobInstaller() {217JfrStackTraceRepository::clear_leak_profiler();218}219void sample_do(ObjectSample* sample) {220if (stack_trace_precondition(sample)) {221install(sample);222}223}224};225226#ifdef ASSERT227static void validate_stack_trace(const ObjectSample* sample, const JfrStackTrace* stack_trace) {228assert(!sample->has_stacktrace(), "invariant");229assert(stack_trace != NULL, "invariant");230assert(stack_trace->hash() == sample->stack_trace_hash(), "invariant");231assert(stack_trace->id() == sample->stack_trace_id(), "invariant");232}233#endif234235inline const JfrStackTrace* StackTraceBlobInstaller::resolve(const ObjectSample* sample) const {236return JfrStackTraceRepository::lookup_for_leak_profiler(sample->stack_trace_hash(), sample->stack_trace_id());237}238239void StackTraceBlobInstaller::install(ObjectSample* sample) {240JfrBlobHandle blob = _cache.get(sample);241if (blob.valid()) {242sample->set_stacktrace(blob);243return;244}245const JfrStackTrace* const stack_trace = resolve(sample);246DEBUG_ONLY(validate_stack_trace(sample, stack_trace));247JfrCheckpointWriter writer;248writer.write_type(TYPE_STACKTRACE);249writer.write_count(1);250ObjectSampleCheckpoint::write_stacktrace(stack_trace, writer);251blob = writer.copy();252_cache.put(sample, blob);253sample->set_stacktrace(blob);254}255256static void install_stack_traces(const ObjectSampler* sampler) {257assert(sampler != NULL, "invariant");258const ObjectSample* const last = sampler->last();259if (last != sampler->last_resolved()) {260ResourceMark rm;261JfrKlassUnloading::sort();262StackTraceBlobInstaller installer;263iterate_samples(installer);264}265}266267void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler) {268assert(sampler != NULL, "invariant");269assert(LeakProfiler::is_running(), "invariant");270JavaThread* const thread = JavaThread::current();271DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(thread);)272// can safepoint here273ThreadInVMfromNative transition(thread);274MutexLocker lock(ClassLoaderDataGraph_lock);275// the lock is needed to ensure the unload lists do not grow in the middle of inspection.276install_stack_traces(sampler);277}278279static bool is_klass_unloaded(traceid klass_id) {280assert(ClassLoaderDataGraph_lock->owned_by_self(), "invariant");281return JfrKlassUnloading::is_unloaded(klass_id);282}283284static bool is_processed(traceid method_id) {285assert(method_id != 0, "invariant");286assert(id_set != NULL, "invariant");287return JfrMutablePredicate<traceid, compare_traceid>::test(id_set, method_id);288}289290void ObjectSampleCheckpoint::add_to_leakp_set(const InstanceKlass* ik, traceid method_id) {291assert(ik != NULL, "invariant");292if (is_processed(method_id) || is_klass_unloaded(JfrMethodLookup::klass_id(method_id))) {293return;294}295const Method* const method = JfrMethodLookup::lookup(ik, method_id);296assert(method != NULL, "invariant");297assert(method->method_holder() == ik, "invariant");298JfrTraceId::load_leakp(ik, method);299}300301void ObjectSampleCheckpoint::write_stacktrace(const JfrStackTrace* trace, JfrCheckpointWriter& writer) {302assert(trace != NULL, "invariant");303// JfrStackTrace304writer.write(trace->id());305writer.write((u1)!trace->_reached_root);306writer.write(trace->_nr_of_frames);307// JfrStackFrames308for (u4 i = 0; i < trace->_nr_of_frames; ++i) {309const JfrStackFrame& frame = trace->_frames[i];310frame.write(writer);311add_to_leakp_set(frame._klass, frame._methodid);312}313}314315static void write_blob(const JfrBlobHandle& blob, JfrCheckpointWriter& writer, bool reset) {316if (reset) {317blob->reset_write_state();318return;319}320blob->exclusive_write(writer);321}322323static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {324if (sample->has_type_set()) {325write_blob(sample->type_set(), writer, reset);326}327}328329static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {330assert(sample->has_thread(), "invariant");331if (has_thread_exited(sample->thread_id())) {332write_blob(sample->thread(), writer, reset);333}334}335336static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {337if (sample->has_stacktrace()) {338write_blob(sample->stacktrace(), writer, reset);339}340}341342static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {343assert(sample != NULL, "invariant");344write_stacktrace_blob(sample, writer, reset);345write_thread_blob(sample, writer, reset);346write_type_set_blob(sample, writer, reset);347}348349class BlobWriter {350private:351const ObjectSampler* _sampler;352JfrCheckpointWriter& _writer;353const jlong _last_sweep;354bool _reset;355public:356BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) :357_sampler(sampler), _writer(writer), _last_sweep(last_sweep), _reset(false) {}358void sample_do(ObjectSample* sample) {359if (sample->is_alive_and_older_than(_last_sweep)) {360write_blobs(sample, _writer, _reset);361}362}363void set_reset() {364_reset = true;365}366};367368static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) {369// sample set is predicated on time of last sweep370const jlong last_sweep = emit_all ? max_jlong : ObjectSampler::last_sweep();371JfrCheckpointWriter writer(thread, false);372BlobWriter cbw(sampler, writer, last_sweep);373iterate_samples(cbw, true);374// reset blob write states375cbw.set_reset();376iterate_samples(cbw, true);377}378379void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {380assert(sampler != NULL, "invariant");381assert(edge_store != NULL, "invariant");382assert(thread != NULL, "invariant");383write_sample_blobs(sampler, emit_all, thread);384// write reference chains385if (!edge_store->is_empty()) {386JfrCheckpointWriter writer(thread);387ObjectSampleWriter osw(writer, edge_store);388edge_store->iterate(osw);389}390}391392// A linked list of saved type set blobs for the epoch.393// The link consist of a reference counted handle.394static JfrBlobHandle saved_type_set_blobs;395396static void release_state_for_previous_epoch() {397// decrements the reference count and the list is reinitialized398saved_type_set_blobs = JfrBlobHandle();399}400401class BlobInstaller {402public:403~BlobInstaller() {404release_state_for_previous_epoch();405}406void sample_do(ObjectSample* sample) {407if (!sample->is_dead()) {408sample->set_type_set(saved_type_set_blobs);409}410}411};412413static void install_type_set_blobs() {414BlobInstaller installer;415iterate_samples(installer);416}417418static void save_type_set_blob(JfrCheckpointWriter& writer, bool copy = false) {419assert(writer.has_data(), "invariant");420const JfrBlobHandle blob = copy ? writer.copy() : writer.move();421if (saved_type_set_blobs.valid()) {422saved_type_set_blobs->set_next(blob);423} else {424saved_type_set_blobs = blob;425}426}427428void ObjectSampleCheckpoint::on_type_set(JfrCheckpointWriter& writer) {429assert(LeakProfiler::is_running(), "invariant");430DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(JavaThread::current());)431const ObjectSample* last = ObjectSampler::sampler()->last();432if (writer.has_data() && last != NULL) {433save_type_set_blob(writer);434install_type_set_blobs();435ObjectSampler::sampler()->set_last_resolved(last);436}437}438439void ObjectSampleCheckpoint::on_type_set_unload(JfrCheckpointWriter& writer) {440assert_locked_or_safepoint(ClassLoaderDataGraph_lock);441assert(LeakProfiler::is_running(), "invariant");442if (writer.has_data() && ObjectSampler::sampler()->last() != NULL) {443save_type_set_blob(writer, true);444}445}446447448