Path: blob/master/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp
41155 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");71return unloaded_thread_id_set != NULL && JfrPredicate<traceid, compare_traceid>::test(unloaded_thread_id_set, tid);72}7374static bool add(GrowableArray<traceid>* set, traceid id) {75assert(set != NULL, "invariant");76return JfrMutablePredicate<traceid, compare_traceid>::test(set, id);77}7879static void add_to_unloaded_thread_set(traceid tid) {80ThreadIdExclusiveAccess lock;81if (unloaded_thread_id_set == NULL) {82unloaded_thread_id_set = c_heap_allocate_array<traceid>();83}84add(unloaded_thread_id_set, tid);85}8687void ObjectSampleCheckpoint::on_thread_exit(JavaThread* jt) {88assert(jt != NULL, "invariant");89if (LeakProfiler::is_running()) {90add_to_unloaded_thread_set(jt->jfr_thread_local()->thread_id());91}92}9394template <typename Processor>95static void do_samples(ObjectSample* sample, const ObjectSample* end, Processor& processor) {96assert(sample != NULL, "invariant");97while (sample != end) {98processor.sample_do(sample);99sample = sample->next();100}101}102103template <typename Processor>104static void iterate_samples(Processor& processor, bool all = false) {105ObjectSampler* const sampler = ObjectSampler::sampler();106assert(sampler != NULL, "invariant");107ObjectSample* const last = sampler->last();108assert(last != NULL, "invariant");109do_samples(last, all ? NULL : sampler->last_resolved(), processor);110}111112class SampleMarker {113private:114ObjectSampleMarker& _marker;115jlong _last_sweep;116int _count;117public:118SampleMarker(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker), _last_sweep(last_sweep), _count(0) {}119void sample_do(ObjectSample* sample) {120if (sample->is_alive_and_older_than(_last_sweep)) {121_marker.mark(sample->object());122++_count;123}124}125int count() const {126return _count;127}128};129130int ObjectSampleCheckpoint::save_mark_words(const ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all) {131assert(sampler != NULL, "invariant");132if (sampler->last() == NULL) {133return 0;134}135SampleMarker sample_marker(marker, emit_all ? max_jlong : ObjectSampler::last_sweep());136iterate_samples(sample_marker, true);137return sample_marker.count();138}139140class BlobCache {141typedef HashTableHost<JfrBlobHandle, traceid, JfrHashtableEntry, BlobCache> BlobTable;142typedef BlobTable::HashEntry BlobEntry;143private:144BlobTable _table;145traceid _lookup_id;146public:147BlobCache(size_t size) : _table(this, size), _lookup_id(0) {}148JfrBlobHandle get(const ObjectSample* sample);149void put(const ObjectSample* sample, const JfrBlobHandle& blob);150// Hash table callbacks151void on_link(const BlobEntry* entry) const;152bool on_equals(uintptr_t hash, const BlobEntry* entry) const;153void on_unlink(BlobEntry* entry) const;154};155156JfrBlobHandle BlobCache::get(const ObjectSample* sample) {157assert(sample != NULL, "invariant");158_lookup_id = sample->stack_trace_id();159assert(_lookup_id != 0, "invariant");160BlobEntry* const entry = _table.lookup_only(sample->stack_trace_hash());161return entry != NULL ? entry->literal() : JfrBlobHandle();162}163164void BlobCache::put(const ObjectSample* sample, const JfrBlobHandle& blob) {165assert(sample != NULL, "invariant");166assert(_table.lookup_only(sample->stack_trace_hash()) == NULL, "invariant");167_lookup_id = sample->stack_trace_id();168assert(_lookup_id != 0, "invariant");169_table.put(sample->stack_trace_hash(), blob);170}171172inline void BlobCache::on_link(const BlobEntry* entry) const {173assert(entry != NULL, "invariant");174assert(entry->id() == 0, "invariant");175entry->set_id(_lookup_id);176}177178inline bool BlobCache::on_equals(uintptr_t hash, const BlobEntry* entry) const {179assert(entry != NULL, "invariant");180assert(entry->hash() == hash, "invariant");181return entry->id() == _lookup_id;182}183184inline void BlobCache::on_unlink(BlobEntry* entry) const {185assert(entry != NULL, "invariant");186}187188static GrowableArray<traceid>* id_set = NULL;189190static void prepare_for_resolution() {191id_set = new GrowableArray<traceid>(JfrOptionSet::old_object_queue_size());192}193194static bool stack_trace_precondition(const ObjectSample* sample) {195assert(sample != NULL, "invariant");196return sample->has_stack_trace_id() && !sample->is_dead();197}198199class StackTraceBlobInstaller {200private:201BlobCache _cache;202void install(ObjectSample* sample);203const JfrStackTrace* resolve(const ObjectSample* sample) const;204public:205StackTraceBlobInstaller() : _cache(JfrOptionSet::old_object_queue_size()) {206prepare_for_resolution();207}208~StackTraceBlobInstaller() {209JfrStackTraceRepository::clear_leak_profiler();210}211void sample_do(ObjectSample* sample) {212if (stack_trace_precondition(sample)) {213install(sample);214}215}216};217218#ifdef ASSERT219static void validate_stack_trace(const ObjectSample* sample, const JfrStackTrace* stack_trace) {220assert(!sample->has_stacktrace(), "invariant");221assert(stack_trace != NULL, "invariant");222assert(stack_trace->hash() == sample->stack_trace_hash(), "invariant");223assert(stack_trace->id() == sample->stack_trace_id(), "invariant");224}225#endif226227inline const JfrStackTrace* StackTraceBlobInstaller::resolve(const ObjectSample* sample) const {228return JfrStackTraceRepository::lookup_for_leak_profiler(sample->stack_trace_hash(), sample->stack_trace_id());229}230231void StackTraceBlobInstaller::install(ObjectSample* sample) {232JfrBlobHandle blob = _cache.get(sample);233if (blob.valid()) {234sample->set_stacktrace(blob);235return;236}237const JfrStackTrace* const stack_trace = resolve(sample);238DEBUG_ONLY(validate_stack_trace(sample, stack_trace));239JfrCheckpointWriter writer;240writer.write_type(TYPE_STACKTRACE);241writer.write_count(1);242ObjectSampleCheckpoint::write_stacktrace(stack_trace, writer);243blob = writer.copy();244_cache.put(sample, blob);245sample->set_stacktrace(blob);246}247248static void install_stack_traces(const ObjectSampler* sampler) {249assert(sampler != NULL, "invariant");250const ObjectSample* const last = sampler->last();251if (last != sampler->last_resolved()) {252ResourceMark rm;253JfrKlassUnloading::sort();254StackTraceBlobInstaller installer;255iterate_samples(installer);256}257}258259void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler) {260assert(sampler != NULL, "invariant");261assert(LeakProfiler::is_running(), "invariant");262JavaThread* const thread = JavaThread::current();263DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_native(thread);)264// can safepoint here265ThreadInVMfromNative transition(thread);266MutexLocker lock(ClassLoaderDataGraph_lock);267// the lock is needed to ensure the unload lists do not grow in the middle of inspection.268install_stack_traces(sampler);269}270271static bool is_klass_unloaded(traceid klass_id) {272assert(ClassLoaderDataGraph_lock->owned_by_self(), "invariant");273return JfrKlassUnloading::is_unloaded(klass_id);274}275276static bool is_processed(traceid method_id) {277assert(method_id != 0, "invariant");278assert(id_set != NULL, "invariant");279return JfrMutablePredicate<traceid, compare_traceid>::test(id_set, method_id);280}281282void ObjectSampleCheckpoint::add_to_leakp_set(const InstanceKlass* ik, traceid method_id) {283assert(ik != NULL, "invariant");284if (is_processed(method_id) || is_klass_unloaded(JfrMethodLookup::klass_id(method_id))) {285return;286}287const Method* const method = JfrMethodLookup::lookup(ik, method_id);288assert(method != NULL, "invariant");289assert(method->method_holder() == ik, "invariant");290JfrTraceId::load_leakp(ik, method);291}292293void ObjectSampleCheckpoint::write_stacktrace(const JfrStackTrace* trace, JfrCheckpointWriter& writer) {294assert(trace != NULL, "invariant");295// JfrStackTrace296writer.write(trace->id());297writer.write((u1)!trace->_reached_root);298writer.write(trace->_nr_of_frames);299// JfrStackFrames300for (u4 i = 0; i < trace->_nr_of_frames; ++i) {301const JfrStackFrame& frame = trace->_frames[i];302frame.write(writer);303add_to_leakp_set(frame._klass, frame._methodid);304}305}306307static void write_blob(const JfrBlobHandle& blob, JfrCheckpointWriter& writer, bool reset) {308if (reset) {309blob->reset_write_state();310return;311}312blob->exclusive_write(writer);313}314315static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {316if (sample->has_type_set()) {317write_blob(sample->type_set(), writer, reset);318}319}320321static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {322assert(sample->has_thread(), "invariant");323if (has_thread_exited(sample->thread_id())) {324write_blob(sample->thread(), writer, reset);325}326}327328static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {329if (sample->has_stacktrace()) {330write_blob(sample->stacktrace(), writer, reset);331}332}333334static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) {335assert(sample != NULL, "invariant");336write_stacktrace_blob(sample, writer, reset);337write_thread_blob(sample, writer, reset);338write_type_set_blob(sample, writer, reset);339}340341class BlobWriter {342private:343const ObjectSampler* _sampler;344JfrCheckpointWriter& _writer;345const jlong _last_sweep;346bool _reset;347public:348BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) :349_sampler(sampler), _writer(writer), _last_sweep(last_sweep), _reset(false) {}350void sample_do(ObjectSample* sample) {351if (sample->is_alive_and_older_than(_last_sweep)) {352write_blobs(sample, _writer, _reset);353}354}355void set_reset() {356_reset = true;357}358};359360static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) {361// sample set is predicated on time of last sweep362const jlong last_sweep = emit_all ? max_jlong : ObjectSampler::last_sweep();363JfrCheckpointWriter writer(thread, false);364BlobWriter cbw(sampler, writer, last_sweep);365iterate_samples(cbw, true);366// reset blob write states367cbw.set_reset();368iterate_samples(cbw, true);369}370371void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) {372assert(sampler != NULL, "invariant");373assert(edge_store != NULL, "invariant");374assert(thread != NULL, "invariant");375write_sample_blobs(sampler, emit_all, thread);376// write reference chains377if (!edge_store->is_empty()) {378JfrCheckpointWriter writer(thread);379ObjectSampleWriter osw(writer, edge_store);380edge_store->iterate(osw);381}382}383384// A linked list of saved type set blobs for the epoch.385// The link consist of a reference counted handle.386static JfrBlobHandle saved_type_set_blobs;387388static void release_state_for_previous_epoch() {389// decrements the reference count and the list is reinitialized390saved_type_set_blobs = JfrBlobHandle();391}392393class BlobInstaller {394public:395~BlobInstaller() {396release_state_for_previous_epoch();397}398void sample_do(ObjectSample* sample) {399if (!sample->is_dead()) {400sample->set_type_set(saved_type_set_blobs);401}402}403};404405static void install_type_set_blobs() {406BlobInstaller installer;407iterate_samples(installer);408}409410static void save_type_set_blob(JfrCheckpointWriter& writer, bool copy = false) {411assert(writer.has_data(), "invariant");412const JfrBlobHandle blob = copy ? writer.copy() : writer.move();413if (saved_type_set_blobs.valid()) {414saved_type_set_blobs->set_next(blob);415} else {416saved_type_set_blobs = blob;417}418}419420void ObjectSampleCheckpoint::on_type_set(JfrCheckpointWriter& writer) {421assert(LeakProfiler::is_running(), "invariant");422DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(JavaThread::current());)423const ObjectSample* last = ObjectSampler::sampler()->last();424if (writer.has_data() && last != NULL) {425save_type_set_blob(writer);426install_type_set_blobs();427ObjectSampler::sampler()->set_last_resolved(last);428}429}430431void ObjectSampleCheckpoint::on_type_set_unload(JfrCheckpointWriter& writer) {432assert_locked_or_safepoint(ClassLoaderDataGraph_lock);433assert(LeakProfiler::is_running(), "invariant");434if (writer.has_data() && ObjectSampler::sampler()->last() != NULL) {435save_type_set_blob(writer, true);436}437}438439440