diff --git a/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.cpp b/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.cpp index 9ffd8acfa10cc63801119b35cd3259f98b0d2d57..36945e3ad114f33ab550a667d7d13ec412664134 100644 --- a/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.cpp +++ b/src/share/vm/jfr/instrumentation/jfrEventClassTransformer.cpp @@ -1461,7 +1461,7 @@ static void copy_method_trace_flags(const InstanceKlass* the_original_klass, con assert(new_method != NULL, "invariant"); assert(new_method->name() == old_method->name(), "invariant"); assert(new_method->signature() == old_method->signature(), "invariant"); - *new_method->trace_flags_addr() = old_method->trace_flags(); + new_method->set_trace_flags(old_method->trace_flags()); assert(new_method->trace_flags() == old_method->trace_flags(), "invariant"); } } diff --git a/src/share/vm/jfr/jfr.cpp b/src/share/vm/jfr/jfr.cpp index ed5de6d950be51011cb2ac1b6d3532e0ddeee62f..06d3b79837e86ca737ae477a43c9b111aa6135be 100644 --- a/src/share/vm/jfr/jfr.cpp +++ b/src/share/vm/jfr/jfr.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 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 @@ -92,7 +92,9 @@ void Jfr::on_vm_shutdown(bool exception_handler) { } void Jfr::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) { - LeakProfiler::oops_do(is_alive, f); + if (LeakProfiler::is_running()) { + LeakProfiler::oops_do(is_alive, f); + } } void Jfr::weak_oops_do(OopClosure* f) { diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp b/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp index d18713151f370be08081b6fa607e5b113031a72a..ad549f4864a77f25b65b389241b726c4b1a24b2d 100644 --- a/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp +++ b/src/share/vm/jfr/leakprofiler/chains/edgeStore.cpp @@ -55,18 +55,23 @@ bool EdgeStore::is_empty() const { return !_edges->has_entries(); } -void EdgeStore::assign_id(EdgeEntry* entry) { +void EdgeStore::on_link(EdgeEntry* entry) { assert(entry != NULL, "invariant"); assert(entry->id() == 0, "invariant"); entry->set_id(++_edge_id_counter); } -bool EdgeStore::equals(const Edge& query, uintptr_t hash, const EdgeEntry* entry) { +bool EdgeStore::on_equals(uintptr_t hash, const EdgeEntry* entry) { assert(entry != NULL, "invariant"); assert(entry->hash() == hash, "invariant"); return true; } +void EdgeStore::on_unlink(EdgeEntry* entry) { + assert(entry != NULL, "invariant"); + // nothing +} + #ifdef ASSERT bool EdgeStore::contains(const oop* reference) const { return get(reference) != NULL; @@ -75,22 +80,21 @@ bool EdgeStore::contains(const oop* reference) const { StoredEdge* EdgeStore::get(const oop* reference) const { assert(reference != NULL, "invariant"); - const StoredEdge e(NULL, reference); - EdgeEntry* const entry = _edges->lookup_only(e, (uintptr_t)reference); + EdgeEntry* const entry = _edges->lookup_only((uintptr_t)reference); return entry != NULL ? entry->literal_addr() : NULL; } StoredEdge* EdgeStore::put(const oop* reference) { assert(reference != NULL, "invariant"); const StoredEdge e(NULL, reference); - assert(NULL == _edges->lookup_only(e, (uintptr_t)reference), "invariant"); - EdgeEntry& entry = _edges->put(e, (uintptr_t)reference); + assert(NULL == _edges->lookup_only((uintptr_t)reference), "invariant"); + EdgeEntry& entry = _edges->put((uintptr_t)reference, e); return entry.literal_addr(); } traceid EdgeStore::get_id(const Edge* edge) const { assert(edge != NULL, "invariant"); - EdgeEntry* const entry = _edges->lookup_only(*edge, (uintptr_t)edge->reference()); + EdgeEntry* const entry = _edges->lookup_only((uintptr_t)edge->reference()); assert(entry != NULL, "invariant"); return entry->id(); } diff --git a/src/share/vm/jfr/leakprofiler/chains/edgeStore.hpp b/src/share/vm/jfr/leakprofiler/chains/edgeStore.hpp index 7f8db737d48b86f9c50a9d1c5f1e26508b4b90f1..f9472b71f01775410940b1a291bbd12b36cff76e 100644 --- a/src/share/vm/jfr/leakprofiler/chains/edgeStore.hpp +++ b/src/share/vm/jfr/leakprofiler/chains/edgeStore.hpp @@ -58,7 +58,7 @@ class StoredEdge : public Edge { }; class EdgeStore : public CHeapObj { - typedef HashTableHost EdgeHashTable; + typedef HashTableHost EdgeHashTable; typedef EdgeHashTable::HashEntry EdgeEntry; template { EdgeHashTable* _edges; // Hash table callbacks - void assign_id(EdgeEntry* entry); - bool equals(const Edge& query, uintptr_t hash, const EdgeEntry* entry); + void on_link(EdgeEntry* entry); + bool on_equals(uintptr_t hash, const EdgeEntry* entry); + void on_unlink(EdgeEntry* entry); StoredEdge* get(const oop* reference) const; StoredEdge* put(const oop* reference); diff --git a/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp b/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp index a3f23f898f627367609995df206314368915ff32..fc5f90d611d1954770b70aca72e02c81d8b6cf61 100644 --- a/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp +++ b/src/share/vm/jfr/leakprofiler/chains/pathToGcRootsOperation.cpp @@ -42,7 +42,6 @@ #include "jfr/leakprofiler/sampling/objectSampler.hpp" #include "jfr/leakprofiler/utilities/granularTimer.hpp" #include "memory/universe.hpp" -#include "oops/markOop.hpp" #include "oops/oop.inline.hpp" #include "runtime/safepoint.hpp" #include "utilities/globalDefinitions.hpp" @@ -101,7 +100,7 @@ void PathToGcRootsOperation::doit() { // Save the original markWord for the potential leak objects, // to be restored on function exit ObjectSampleMarker marker; - if (ObjectSampleCheckpoint::mark(_sampler, marker, _emit_all) == 0) { + if (ObjectSampleCheckpoint::save_mark_words(_sampler, marker, _emit_all) == 0) { // no valid samples to process return; } diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp index 0fbccab78fceb3d3474fe5d48ae43e06427e1b7a..6c38cc67772e8d061144b4e0ae2f1512eedfdb08 100644 --- a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2019, 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 @@ -24,10 +24,6 @@ #include "precompiled.hpp" #include "jfr/jfrEvents.hpp" -#include "jfr/recorder/jfrRecorder.hpp" -#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" -#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" -#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" #include "jfr/leakprofiler/chains/edgeStore.hpp" #include "jfr/leakprofiler/chains/objectSampleMarker.hpp" #include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" @@ -35,259 +31,442 @@ #include "jfr/leakprofiler/leakProfiler.hpp" #include "jfr/leakprofiler/sampling/objectSample.hpp" #include "jfr/leakprofiler/sampling/objectSampler.hpp" -#include "jfr/leakprofiler/utilities/rootType.hpp" -#include "jfr/metadata/jfrSerializer.hpp" -#include "runtime/interfaceSupport.hpp" -#include "runtime/mutexLocker.hpp" -#include "runtime/thread.inline.hpp" - -template -static void do_samples(ObjectSample* sample, const ObjectSample* const end, SampleProcessor& processor) { - assert(sample != NULL, "invariant"); - while (sample != end) { - processor.sample_do(sample); - sample = sample->next(); - } +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" +#include "jfr/recorder/service/jfrOptionSet.hpp" +#include "jfr/recorder/stacktrace/jfrStackTraceRepository.hpp" +#include "jfr/utilities/jfrHashtable.hpp" +#include "jfr/utilities/jfrTypes.hpp" +#include "runtime/safepoint.hpp" +#include "runtime/semaphore.hpp" +#include "runtime/thread.hpp" +#include "utilities/growableArray.hpp" + +static bool predicate(GrowableArray* set, traceid id) { + assert(set != NULL, "invariant"); + bool found = false; + set->find_sorted(id, found); + return found; } -class RootSystemType : public JfrSerializer { - public: - void serialize(JfrCheckpointWriter& writer) { - const u4 nof_root_systems = OldObjectRoot::_number_of_systems; - writer.write_count(nof_root_systems); - for (u4 i = 0; i < nof_root_systems; ++i) { - writer.write_key(i); - writer.write(OldObjectRoot::system_description((OldObjectRoot::System)i)); - } +static bool mutable_predicate(GrowableArray* set, traceid id) { + assert(set != NULL, "invariant"); + bool found = false; + const int location = set->find_sorted(id, found); + if (!found) { + set->insert_before(location, id); } -}; + return found; +} -class RootType : public JfrSerializer { - public: - void serialize(JfrCheckpointWriter& writer) { - const u4 nof_root_types = OldObjectRoot::_number_of_types; - writer.write_count(nof_root_types); - for (u4 i = 0; i < nof_root_types; ++i) { - writer.write_key(i); - writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i)); - } - } -}; +static bool add(GrowableArray* set, traceid id) { + assert(set != NULL, "invariant"); + return mutable_predicate(set, id); +} -class CheckpointInstall { - private: - const JfrCheckpointBlobHandle& _cp; - public: - CheckpointInstall(const JfrCheckpointBlobHandle& cp) : _cp(cp) {} - void sample_do(ObjectSample* sample) { - assert(sample != NULL, "invariant"); - if (!sample->is_dead()) { - sample->set_klass_checkpoint(_cp); - } - } -}; +const int initial_array_size = 64; + +template +static GrowableArray* c_heap_allocate_array(int size = initial_array_size) { + return new (ResourceObj::C_HEAP, mtTracing) GrowableArray(size, true, mtTracing); +} -class CheckpointWrite { +static GrowableArray* unloaded_thread_id_set = NULL; + +class ThreadIdExclusiveAccess : public StackObj { private: - JfrCheckpointWriter& _writer; - const jlong _last_sweep; + static Semaphore _mutex_semaphore; public: - CheckpointWrite(JfrCheckpointWriter& writer, jlong last_sweep) : _writer(writer), _last_sweep(last_sweep) {} - void sample_do(ObjectSample* sample) { - assert(sample != NULL, "invariant"); - if (sample->is_alive_and_older_than(_last_sweep)) { - if (sample->has_thread_checkpoint()) { - const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint(); - thread_cp->exclusive_write(_writer); - } - if (sample->has_klass_checkpoint()) { - const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint(); - klass_cp->exclusive_write(_writer); - } - } - } + ThreadIdExclusiveAccess() { _mutex_semaphore.wait(); } + ~ThreadIdExclusiveAccess() { _mutex_semaphore.signal(); } }; -class CheckpointStateReset { - private: - const jlong _last_sweep; - public: - CheckpointStateReset(jlong last_sweep) : _last_sweep(last_sweep) {} - void sample_do(ObjectSample* sample) { - assert(sample != NULL, "invariant"); - if (sample->is_alive_and_older_than(_last_sweep)) { - if (sample->has_thread_checkpoint()) { - const JfrCheckpointBlobHandle& thread_cp = sample->thread_checkpoint(); - thread_cp->reset_write_state(); - } - if (sample->has_klass_checkpoint()) { - const JfrCheckpointBlobHandle& klass_cp = sample->klass_checkpoint(); - klass_cp->reset_write_state(); - } - } +Semaphore ThreadIdExclusiveAccess::_mutex_semaphore(1); + +static bool has_thread_exited(traceid tid) { + assert(tid != 0, "invariant"); + return unloaded_thread_id_set != NULL && predicate(unloaded_thread_id_set, tid); +} + +static void add_to_unloaded_thread_set(traceid tid) { + ThreadIdExclusiveAccess lock; + if (unloaded_thread_id_set == NULL) { + unloaded_thread_id_set = c_heap_allocate_array(); } -}; + add(unloaded_thread_id_set, tid); +} -class StackTraceWrite { - private: - JfrStackTraceRepository& _stack_trace_repo; - JfrCheckpointWriter& _writer; - int _count; - public: - StackTraceWrite(JfrStackTraceRepository& stack_trace_repo, JfrCheckpointWriter& writer) : - _stack_trace_repo(stack_trace_repo), _writer(writer), _count(0) { - JfrStacktrace_lock->lock(); +void ObjectSampleCheckpoint::on_thread_exit(JavaThread* jt) { + assert(jt != NULL, "invariant"); + if (LeakProfiler::is_running()) { + add_to_unloaded_thread_set(jt->jfr_thread_local()->thread_id()); } - ~StackTraceWrite() { - assert(JfrStacktrace_lock->owned_by_self(), "invariant"); - JfrStacktrace_lock->unlock(); +} + +// Track the set of unloaded klasses during a chunk / epoch. +// Methods in stacktraces belonging to unloaded klasses must not be accessed. +static GrowableArray* unloaded_klass_set = NULL; + +static void add_to_unloaded_klass_set(traceid klass_id) { + if (unloaded_klass_set == NULL) { + unloaded_klass_set = c_heap_allocate_array(); } + unloaded_klass_set->append(klass_id); +} - void sample_do(ObjectSample* sample) { - assert(sample != NULL, "invariant"); - if (!sample->is_dead()) { - if (sample->has_stack_trace()) { - JfrTraceId::use(sample->klass(), true); - _stack_trace_repo.write(_writer, sample->stack_trace_id(), sample->stack_trace_hash()); - ++_count; - } - } +static void sort_unloaded_klass_set() { + if (unloaded_klass_set != NULL && unloaded_klass_set->length() > 1) { + unloaded_klass_set->sort(sort_traceid); } +} - int count() const { - return _count; +void ObjectSampleCheckpoint::on_klass_unload(const Klass* k) { + assert(k != NULL, "invariant"); + add_to_unloaded_klass_set(TRACE_ID(k)); +} + +template +static void do_samples(ObjectSample* sample, const ObjectSample* end, Processor& processor) { + assert(sample != NULL, "invariant"); + while (sample != end) { + processor.sample_do(sample); + sample = sample->next(); } -}; +} -class SampleMark { +template +static void iterate_samples(Processor& processor, bool all = false) { + ObjectSampler* const sampler = ObjectSampler::sampler(); + assert(sampler != NULL, "invariant"); + ObjectSample* const last = sampler->last(); + assert(last != NULL, "invariant"); + do_samples(last, all ? NULL : sampler->last_resolved(), processor); +} + +class SampleMarker { private: ObjectSampleMarker& _marker; jlong _last_sweep; int _count; public: - SampleMark(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker), - _last_sweep(last_sweep), - _count(0) {} + SampleMarker(ObjectSampleMarker& marker, jlong last_sweep) : _marker(marker), _last_sweep(last_sweep), _count(0) {} void sample_do(ObjectSample* sample) { - assert(sample != NULL, "invariant"); if (sample->is_alive_and_older_than(_last_sweep)) { _marker.mark(sample->object()); ++_count; } } - int count() const { return _count; } }; -void ObjectSampleCheckpoint::install(JfrCheckpointWriter& writer, bool class_unload, bool type_set) { - if (!writer.has_data()) { - return; +int ObjectSampleCheckpoint::save_mark_words(const ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all) { + assert(sampler != NULL, "invariant"); + if (sampler->last() == NULL) { + return 0; } + SampleMarker sample_marker(marker, emit_all ? max_jlong : sampler->last_sweep().value()); + iterate_samples(sample_marker, true); + return sample_marker.count(); +} - assert(writer.has_data(), "invariant"); - const JfrCheckpointBlobHandle h_cp = writer.checkpoint_blob(); - CheckpointInstall install(h_cp); - - // Class unload implies a safepoint. - // Not class unload implies the object sampler is locked, because it was claimed exclusively earlier. - // Therefore: direct access the object sampler instance is safe. - ObjectSampler* const object_sampler = ObjectSampler::sampler(); - assert(object_sampler != NULL, "invariant"); - - ObjectSample* const last = const_cast(object_sampler->last()); - const ObjectSample* const last_resolved = object_sampler->last_resolved(); - - // install only to new samples since last resolved checkpoint - if (last != last_resolved) { - do_samples(last, last_resolved, install); - if (class_unload) { - return; - } - if (type_set) { - object_sampler->set_last_resolved(last); +class BlobCache { + typedef HashTableHost BlobTable; + typedef BlobTable::HashEntry BlobEntry; + private: + BlobTable _table; + traceid _lookup_id; + public: + BlobCache(size_t size) : _table(this, size), _lookup_id(0) {} + JfrBlobHandle get(const ObjectSample* sample); + void put(const ObjectSample* sample, const JfrBlobHandle& blob); + // Hash table callbacks + void on_link(const BlobEntry* entry) const; + bool on_equals(uintptr_t hash, const BlobEntry* entry) const; + void on_unlink(BlobEntry* entry) const; +}; + +JfrBlobHandle BlobCache::get(const ObjectSample* sample) { + assert(sample != NULL, "invariant"); + _lookup_id = sample->stack_trace_id(); + assert(_lookup_id != 0, "invariant"); + BlobEntry* const entry = _table.lookup_only(sample->stack_trace_hash()); + return entry != NULL ? entry->literal() : JfrBlobHandle(); +} + +void BlobCache::put(const ObjectSample* sample, const JfrBlobHandle& blob) { + assert(sample != NULL, "invariant"); + assert(_table.lookup_only(sample->stack_trace_hash()) == NULL, "invariant"); + _lookup_id = sample->stack_trace_id(); + assert(_lookup_id != 0, "invariant"); + _table.put(sample->stack_trace_hash(), blob); +} + +inline void BlobCache::on_link(const BlobEntry* entry) const { + assert(entry != NULL, "invariant"); + assert(entry->id() == 0, "invariant"); + entry->set_id(_lookup_id); +} + +inline bool BlobCache::on_equals(uintptr_t hash, const BlobEntry* entry) const { + assert(entry != NULL, "invariant"); + assert(entry->hash() == hash, "invariant"); + return entry->id() == _lookup_id; +} + +inline void BlobCache::on_unlink(BlobEntry* entry) const { + assert(entry != NULL, "invariant"); +} + +static GrowableArray* id_set = NULL; + +static void prepare_for_resolution() { + id_set = new GrowableArray(JfrOptionSet::old_object_queue_size()); + sort_unloaded_klass_set(); +} + +static bool stack_trace_precondition(const ObjectSample* sample) { + assert(sample != NULL, "invariant"); + return sample->has_stack_trace_id() && !sample->is_dead(); +} + +class StackTraceBlobInstaller { + private: + const JfrStackTraceRepository& _stack_trace_repo; + BlobCache _cache; + const JfrStackTrace* resolve(const ObjectSample* sample); + void install(ObjectSample* sample); + public: + StackTraceBlobInstaller(const JfrStackTraceRepository& stack_trace_repo); + void sample_do(ObjectSample* sample) { + if (stack_trace_precondition(sample)) { + install(sample); } } +}; + +StackTraceBlobInstaller::StackTraceBlobInstaller(const JfrStackTraceRepository& stack_trace_repo) : + _stack_trace_repo(stack_trace_repo), _cache(JfrOptionSet::old_object_queue_size()) { + prepare_for_resolution(); +} + +const JfrStackTrace* StackTraceBlobInstaller::resolve(const ObjectSample* sample) { + return _stack_trace_repo.lookup(sample->stack_trace_hash(), sample->stack_trace_id()); +} + +#ifdef ASSERT +static void validate_stack_trace(const ObjectSample* sample, const JfrStackTrace* stack_trace) { + assert(!sample->has_stacktrace(), "invariant"); + assert(stack_trace != NULL, "invariant"); + assert(stack_trace->hash() == sample->stack_trace_hash(), "invariant"); + assert(stack_trace->id() == sample->stack_trace_id(), "invariant"); +} +#endif + +void StackTraceBlobInstaller::install(ObjectSample* sample) { + JfrBlobHandle blob = _cache.get(sample); + if (blob.valid()) { + sample->set_stacktrace(blob); + return; + } + const JfrStackTrace* const stack_trace = resolve(sample); + DEBUG_ONLY(validate_stack_trace(sample, stack_trace)); + JfrCheckpointWriter writer(false, true, Thread::current()); + writer.write_type(TYPE_STACKTRACE); + writer.write_count(1); + ObjectSampleCheckpoint::write_stacktrace(stack_trace, writer); + blob = writer.move(); + _cache.put(sample, blob); + sample->set_stacktrace(blob); } -void ObjectSampleCheckpoint::write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { +static void install_stack_traces(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) { assert(sampler != NULL, "invariant"); - assert(edge_store != NULL, "invariant"); - assert(thread != NULL, "invariant"); + const ObjectSample* const last = sampler->last(); + if (last != sampler->last_resolved()) { + StackTraceBlobInstaller installer(stack_trace_repo); + iterate_samples(installer); + } +} + +// caller needs ResourceMark +void ObjectSampleCheckpoint::on_rotation(const ObjectSampler* sampler, JfrStackTraceRepository& stack_trace_repo) { + assert(sampler != NULL, "invariant"); + assert(LeakProfiler::is_running(), "invariant"); + install_stack_traces(sampler, stack_trace_repo); +} + +static traceid get_klass_id(traceid method_id) { + assert(method_id != 0, "invariant"); + return method_id >> TRACE_ID_SHIFT; +} - static bool types_registered = false; - if (!types_registered) { - JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType()); - JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType()); - types_registered = true; +static bool is_klass_unloaded(traceid method_id) { + return unloaded_klass_set != NULL && predicate(unloaded_klass_set, get_klass_id(method_id)); +} + +static bool is_processed(traceid id) { + assert(id != 0, "invariant"); + assert(id_set != NULL, "invariant"); + return mutable_predicate(id_set, id); +} + +void ObjectSampleCheckpoint::add_to_leakp_set(const Method* method, traceid method_id) { + if (is_processed(method_id) || is_klass_unloaded(method_id)) { + return; } + JfrTraceId::set_leakp(method); +} - const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value(); - ObjectSample* const last = const_cast(sampler->last()); - { - JfrCheckpointWriter writer(false, false, thread); - CheckpointWrite checkpoint_write(writer, last_sweep); - do_samples(last, NULL, checkpoint_write); +void ObjectSampleCheckpoint::write_stacktrace(const JfrStackTrace* trace, JfrCheckpointWriter& writer) { + assert(trace != NULL, "invariant"); + // JfrStackTrace + writer.write(trace->id()); + writer.write((u1)!trace->_reached_root); + writer.write(trace->_nr_of_frames); + // JfrStackFrames + for (u4 i = 0; i < trace->_nr_of_frames; ++i) { + const JfrStackFrame& frame = trace->_frames[i]; + frame.write(writer); + add_to_leakp_set(frame._method, frame._methodid); } +} - CheckpointStateReset state_reset(last_sweep); - do_samples(last, NULL, state_reset); +static void write_blob(const JfrBlobHandle& blob, JfrCheckpointWriter& writer, bool reset) { + if (reset) { + blob->reset_write_state(); + return; + } + blob->exclusive_write(writer); +} + +static void write_type_set_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) { + if (sample->has_type_set()) { + write_blob(sample->type_set(), writer, reset); + } +} +static void write_thread_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) { + assert(sample->has_thread(), "invariant"); + if (has_thread_exited(sample->thread_id())) { + write_blob(sample->thread(), writer, reset); + } +} + +static void write_stacktrace_blob(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) { + if (sample->has_stacktrace()) { + write_blob(sample->stacktrace(), writer, reset); + } +} + +static void write_blobs(const ObjectSample* sample, JfrCheckpointWriter& writer, bool reset) { + assert(sample != NULL, "invariant"); + write_stacktrace_blob(sample, writer, reset); + write_thread_blob(sample, writer, reset); + write_type_set_blob(sample, writer, reset); +} + +class BlobWriter { + private: + const ObjectSampler* _sampler; + JfrCheckpointWriter& _writer; + const jlong _last_sweep; + bool _reset; + public: + BlobWriter(const ObjectSampler* sampler, JfrCheckpointWriter& writer, jlong last_sweep) : + _sampler(sampler), _writer(writer), _last_sweep(last_sweep), _reset(false) {} + void sample_do(ObjectSample* sample) { + if (sample->is_alive_and_older_than(_last_sweep)) { + write_blobs(sample, _writer, _reset); + } + } + void set_reset() { + _reset = true; + } +}; + +static void write_sample_blobs(const ObjectSampler* sampler, bool emit_all, Thread* thread) { + // sample set is predicated on time of last sweep + const jlong last_sweep = emit_all ? max_jlong : sampler->last_sweep().value(); + JfrCheckpointWriter writer(false, false, thread); + BlobWriter cbw(sampler, writer, last_sweep); + iterate_samples(cbw, true); + // reset blob write states + cbw.set_reset(); + iterate_samples(cbw, true); +} + +void ObjectSampleCheckpoint::write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread) { + assert(sampler != NULL, "invariant"); + assert(edge_store != NULL, "invariant"); + assert(thread != NULL, "invariant"); + write_sample_blobs(sampler, emit_all, thread); + // write reference chains if (!edge_store->is_empty()) { - // java object and chain representations JfrCheckpointWriter writer(false, true, thread); ObjectSampleWriter osw(writer, edge_store); edge_store->iterate(osw); } } -int ObjectSampleCheckpoint::mark(ObjectSampler* object_sampler, ObjectSampleMarker& marker, bool emit_all) { - assert(object_sampler != NULL, "invariant"); - ObjectSample* const last = const_cast(object_sampler->last()); - if (last == NULL) { - return 0; +static void clear_unloaded_klass_set() { + if (unloaded_klass_set != NULL && unloaded_klass_set->is_nonempty()) { + unloaded_klass_set->clear(); } - const jlong last_sweep = emit_all ? max_jlong : object_sampler->last_sweep().value(); - SampleMark mark(marker, last_sweep); - do_samples(last, NULL, mark); - return mark.count(); } -WriteObjectSampleStacktrace::WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo) : - _sampler(sampler), _stack_trace_repo(repo) {} +// A linked list of saved type set blobs for the epoch. +// The link consist of a reference counted handle. +static JfrBlobHandle saved_type_set_blobs; -bool WriteObjectSampleStacktrace::process() { - assert(LeakProfiler::is_running(), "invariant"); - assert(_sampler != NULL, "invariant"); +static void release_state_for_previous_epoch() { + // decrements the reference count and the list is reinitialized + saved_type_set_blobs = JfrBlobHandle(); + clear_unloaded_klass_set(); +} - ObjectSample* const last = const_cast(_sampler->last()); - const ObjectSample* const last_resolved = _sampler->last_resolved(); - if (last == last_resolved) { - return true; +class BlobInstaller { + public: + ~BlobInstaller() { + release_state_for_previous_epoch(); } + void sample_do(ObjectSample* sample) { + if (!sample->is_dead()) { + sample->set_type_set(saved_type_set_blobs); + } + } +}; - JfrCheckpointWriter writer(false, true, Thread::current()); - const JfrCheckpointContext ctx = writer.context(); - - writer.write_type(TYPE_STACKTRACE); - const jlong count_offset = writer.reserve(sizeof(u4)); +static void install_type_set_blobs() { + BlobInstaller installer; + iterate_samples(installer); +} - int count = 0; - { - StackTraceWrite stack_trace_write(_stack_trace_repo, writer); // JfrStacktrace_lock - do_samples(last, last_resolved, stack_trace_write); - count = stack_trace_write.count(); +static void save_type_set_blob(JfrCheckpointWriter& writer, bool copy = false) { + assert(writer.has_data(), "invariant"); + const JfrBlobHandle blob = copy ? writer.copy() : writer.move(); + if (saved_type_set_blobs.valid()) { + saved_type_set_blobs->set_next(blob); + } else { + saved_type_set_blobs = blob; } - if (count == 0) { - writer.set_context(ctx); - return true; +} + +void ObjectSampleCheckpoint::on_type_set(JfrCheckpointWriter& writer) { + assert(LeakProfiler::is_running(), "invariant"); + const ObjectSample* last = ObjectSampler::sampler()->last(); + if (writer.has_data() && last != NULL) { + save_type_set_blob(writer); + install_type_set_blobs(); + ObjectSampler::sampler()->set_last_resolved(last); } - assert(count > 0, "invariant"); - writer.write_count((u4)count, count_offset); - JfrStackTraceRepository::write_metadata(writer); +} - // install the stacktrace checkpoint information to the candidates - ObjectSampleCheckpoint::install(writer, false, false); - return true; +void ObjectSampleCheckpoint::on_type_set_unload(JfrCheckpointWriter& writer) { + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); + assert(LeakProfiler::is_running(), "invariant"); + if (writer.has_data() && ObjectSampler::sampler()->last() != NULL) { + save_type_set_blob(writer, true); + } } diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp index 86725a4977b50052e79fbbf83a66962080d39732..cdd9e090ac60a2c476af0c5967e1dea2b1b4d789 100644 --- a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp @@ -26,27 +26,35 @@ #define SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP #include "memory/allocation.hpp" +#include "jfr/utilities/jfrTypes.hpp" class EdgeStore; +class JavaThread; class JfrCheckpointWriter; +class JfrStackTrace; class JfrStackTraceRepository; +class Klass; +class Method; +class ObjectSample; class ObjectSampleMarker; class ObjectSampler; +class Thread; class ObjectSampleCheckpoint : AllStatic { - public: - static void install(JfrCheckpointWriter& writer, bool class_unload, bool type_set); - static void write(ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread); - static int mark(ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all); -}; - -class WriteObjectSampleStacktrace : public StackObj { + friend class EventEmitter; + friend class PathToGcRootsOperation; + friend class StackTraceBlobInstaller; private: - ObjectSampler* const _sampler; - JfrStackTraceRepository& _stack_trace_repo; + static void add_to_leakp_set(const Method* method, traceid method_id); + static int save_mark_words(const ObjectSampler* sampler, ObjectSampleMarker& marker, bool emit_all); + static void write_stacktrace(const JfrStackTrace* trace, JfrCheckpointWriter& writer); + static void write(const ObjectSampler* sampler, EdgeStore* edge_store, bool emit_all, Thread* thread); public: - WriteObjectSampleStacktrace(ObjectSampler* sampler, JfrStackTraceRepository& repo); - bool process(); + static void on_klass_unload(const Klass* k); + static void on_type_set(JfrCheckpointWriter& writer); + static void on_type_set_unload(JfrCheckpointWriter& writer); + static void on_thread_exit(JavaThread* jt); + static void on_rotation(const ObjectSampler* sampler, JfrStackTraceRepository& repo); }; #endif // SHARE_VM_LEAKPROFILER_CHECKPOINT_OBJECTSAMPLECHECKPOINT_HPP diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp index 59553a95371cb47284d40771218d432665a4d94f..3cfd560116ce4d283ec60d37ca1ff0748c69e2d3 100644 --- a/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp +++ b/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp @@ -33,8 +33,8 @@ #include "jfr/leakprofiler/sampling/objectSampler.hpp" #include "jfr/leakprofiler/utilities/rootType.hpp" #include "jfr/leakprofiler/utilities/unifiedOop.hpp" -#include "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp" -#include "jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp" +#include "jfr/metadata/jfrSerializer.hpp" +#include "jfr/writers/jfrTypeWriterHost.hpp" #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" #include "utilities/growableArray.hpp" @@ -137,30 +137,33 @@ class FieldTable : public ResourceObj { typename, size_t> friend class HashTableHost; - typedef HashTableHost FieldInfoTable; + typedef HashTableHost FieldInfoTable; public: typedef FieldInfoTable::HashEntry FieldInfoEntry; private: static traceid _field_id_counter; FieldInfoTable* _table; + const ObjectSampleFieldInfo* _lookup; - void assign_id(FieldInfoEntry* entry) { + void on_link(FieldInfoEntry* entry) { assert(entry != NULL, "invariant"); entry->set_id(++_field_id_counter); } - bool equals(const ObjectSampleFieldInfo* query, uintptr_t hash, const FieldInfoEntry* entry) { + bool on_equals(uintptr_t hash, const FieldInfoEntry* entry) { assert(hash == entry->hash(), "invariant"); - assert(query != NULL, "invariant"); - const ObjectSampleFieldInfo* stored = entry->literal(); - assert(stored != NULL, "invariant"); - assert(((Symbol*)stored->_field_name_symbol)->identity_hash() == ((Symbol*)query->_field_name_symbol)->identity_hash(), "invariant"); - return stored->_field_modifiers == query->_field_modifiers; + assert(_lookup != NULL, "invariant"); + return entry->literal()->_field_modifiers == _lookup->_field_modifiers; + } + + void on_unlink(FieldInfoEntry* entry) { + assert(entry != NULL, "invariant"); + // nothing } public: - FieldTable() : _table(new FieldInfoTable(this)) {} + FieldTable() : _table(new FieldInfoTable(this)), _lookup(NULL) {} ~FieldTable() { assert(_table != NULL, "invariant"); delete _table; @@ -168,8 +171,8 @@ class FieldTable : public ResourceObj { traceid store(const ObjectSampleFieldInfo* field_info) { assert(field_info != NULL, "invariant"); - const FieldInfoEntry& entry =_table->lookup_put(field_info, - ((Symbol*)field_info->_field_name_symbol)->identity_hash()); + _lookup = field_info; + const FieldInfoEntry& entry = _table->lookup_put(((Symbol*)field_info->_field_name_symbol)->identity_hash(), field_info); return entry.id(); } @@ -196,7 +199,7 @@ static ArrayInfo* array_infos = NULL; static FieldTable* field_infos = NULL; static RootDescriptionInfo* root_infos = NULL; -int __write_sample_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* si) { +int __write_sample_info__(JfrCheckpointWriter* writer, const void* si) { assert(writer != NULL, "invariant"); assert(si != NULL, "invariant"); const OldObjectSampleInfo* const oosi = (const OldObjectSampleInfo*)si; @@ -211,17 +214,17 @@ int __write_sample_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, c return 1; } -typedef JfrArtifactWriterImplHost SampleWriterImpl; -typedef JfrArtifactWriterHost SampleWriter; +typedef JfrTypeWriterImplHost SampleWriterImpl; +typedef JfrTypeWriterHost SampleWriter; static void write_sample_infos(JfrCheckpointWriter& writer) { if (sample_infos != NULL) { - SampleWriter sw(&writer, NULL, false); + SampleWriter sw(&writer); sample_infos->iterate(sw); } } -int __write_reference_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ri) { +int __write_reference_info__(JfrCheckpointWriter* writer, const void* ri) { assert(writer != NULL, "invariant"); assert(ri != NULL, "invariant"); const ReferenceInfo* const ref_info = (const ReferenceInfo*)ri; @@ -233,17 +236,17 @@ int __write_reference_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused return 1; } -typedef JfrArtifactWriterImplHost ReferenceWriterImpl; -typedef JfrArtifactWriterHost ReferenceWriter; +typedef JfrTypeWriterImplHost ReferenceWriterImpl; +typedef JfrTypeWriterHost ReferenceWriter; static void write_reference_infos(JfrCheckpointWriter& writer) { if (ref_infos != NULL) { - ReferenceWriter rw(&writer, NULL, false); + ReferenceWriter rw(&writer); ref_infos->iterate(rw); } } -int __write_array_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ai) { +int __write_array_info__(JfrCheckpointWriter* writer, const void* ai) { assert(writer != NULL, "invariant"); assert(ai != NULL, "invariant"); const ObjectSampleArrayInfo* const osai = (const ObjectSampleArrayInfo*)ai; @@ -270,17 +273,17 @@ static traceid get_array_info_id(const Edge& edge, traceid id) { return array_infos->store(osai); } -typedef JfrArtifactWriterImplHost ArrayWriterImpl; -typedef JfrArtifactWriterHost ArrayWriter; +typedef JfrTypeWriterImplHost ArrayWriterImpl; +typedef JfrTypeWriterHost ArrayWriter; static void write_array_infos(JfrCheckpointWriter& writer) { if (array_infos != NULL) { - ArrayWriter aw(&writer, NULL, false); + ArrayWriter aw(&writer); array_infos->iterate(aw); } } -int __write_field_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* fi) { +int __write_field_info__(JfrCheckpointWriter* writer, const void* fi) { assert(writer != NULL, "invariant"); assert(fi != NULL, "invariant"); const FieldTable::FieldInfoEntry* field_info_entry = (const FieldTable::FieldInfoEntry*)fi; @@ -314,12 +317,12 @@ static traceid get_field_info_id(const Edge& edge) { return field_infos->store(osfi); } -typedef JfrArtifactWriterImplHost FieldWriterImpl; -typedef JfrArtifactWriterHost FieldWriter; +typedef JfrTypeWriterImplHost FieldWriterImpl; +typedef JfrTypeWriterHost FieldWriter; static void write_field_infos(JfrCheckpointWriter& writer) { if (field_infos != NULL) { - FieldWriter fw(&writer, NULL, false); + FieldWriter fw(&writer); field_infos->iterate(fw); } } @@ -339,7 +342,7 @@ static const char* description(const ObjectSampleRootDescriptionInfo* osdi) { return description.description(); } -int __write_root_description_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* di) { +int __write_root_description_info__(JfrCheckpointWriter* writer, const void* di) { assert(writer != NULL, "invariant"); assert(di != NULL, "invariant"); const ObjectSampleRootDescriptionInfo* const osdi = (const ObjectSampleRootDescriptionInfo*)di; @@ -366,8 +369,8 @@ static traceid get_gc_root_description_info_id(const Edge& edge, traceid id) { return root_infos->store(oodi); } -typedef JfrArtifactWriterImplHost RootDescriptionWriterImpl; -typedef JfrArtifactWriterHost RootDescriptionWriter; +typedef JfrTypeWriterImplHost RootDescriptionWriterImpl; +typedef JfrTypeWriterHost RootDescriptionWriter; int _edge_reference_compare_(uintptr_t lhs, uintptr_t rhs) { @@ -513,7 +516,7 @@ static void write_root_descriptors(JfrCheckpointWriter& writer) { RootResolutionSet rrs(root_infos); RootResolver::resolve(rrs); // write roots - RootDescriptionWriter rw(&writer, NULL, false); + RootDescriptionWriter rw(&writer); root_infos->iterate(rw); } } @@ -576,11 +579,45 @@ void ObjectSampleWriter::write(const StoredEdge* edge) { } } +class RootSystemType : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer) { + const u4 nof_root_systems = OldObjectRoot::_number_of_systems; + writer.write_count(nof_root_systems); + for (u4 i = 0; i < nof_root_systems; ++i) { + writer.write_key(i); + writer.write(OldObjectRoot::system_description((OldObjectRoot::System)i)); + } + } +}; + +class RootType : public JfrSerializer { + public: + void serialize(JfrCheckpointWriter& writer) { + const u4 nof_root_types = OldObjectRoot::_number_of_types; + writer.write_count(nof_root_types); + for (u4 i = 0; i < nof_root_types; ++i) { + writer.write_key(i); + writer.write(OldObjectRoot::type_description((OldObjectRoot::Type)i)); + } + } +}; + +static void register_serializers() { + static bool is_registered = false; + if (!is_registered) { + JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTSYSTEM, false, true, new RootSystemType()); + JfrSerializer::register_serializer(TYPE_OLDOBJECTROOTTYPE, false, true, new RootType()); + is_registered = true; + } +} + ObjectSampleWriter::ObjectSampleWriter(JfrCheckpointWriter& writer, EdgeStore* store) : _writer(writer), _store(store) { assert(store != NULL, "invariant"); assert(!store->is_empty(), "invariant"); + register_serializers(); sample_infos = NULL; ref_infos = NULL; array_infos = NULL; diff --git a/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp b/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp index 90f61bea3d43181d4c9f2e4f480aa6f6532603aa..e3a56afd0c525714a3fd787696e4a3e6bd6e3353 100644 --- a/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp +++ b/src/share/vm/jfr/leakprofiler/checkpoint/rootResolver.cpp @@ -29,7 +29,6 @@ #include "jfr/leakprofiler/checkpoint/rootResolver.hpp" #include "memory/iterator.hpp" #include "oops/klass.hpp" -#include "oops/markOop.hpp" #include "oops/oop.hpp" #include "prims/jvmtiThreadState.hpp" #include "prims/privilegedStack.hpp" diff --git a/src/share/vm/jfr/leakprofiler/sampling/objectSample.hpp b/src/share/vm/jfr/leakprofiler/sampling/objectSample.hpp index 48baea52dd4196d04a6ef387c686d2b24f7c24f3..6394e8c6c89bf41bdbccbeaa5b9f2b3eb86b41d3 100644 --- a/src/share/vm/jfr/leakprofiler/sampling/objectSample.hpp +++ b/src/share/vm/jfr/leakprofiler/sampling/objectSample.hpp @@ -25,13 +25,14 @@ #ifndef SHARE_VM_JFR_LEAKPROFILER_SAMPLING_OBJECTSAMPLE_HPP #define SHARE_VM_JFR_LEAKPROFILER_SAMPLING_OBJECTSAMPLE_HPP -#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp" #include "jfr/utilities/jfrAllocation.hpp" +#include "jfr/utilities/jfrBlob.hpp" #include "jfr/utilities/jfrTime.hpp" #include "jfr/utilities/jfrTypes.hpp" #include "memory/allocation.hpp" #include "oops/oop.hpp" #include "utilities/ticks.hpp" + /* * Handle for diagnosing Java memory leaks. * @@ -44,8 +45,9 @@ class ObjectSample : public JfrCHeapObj { private: ObjectSample* _next; ObjectSample* _previous; - JfrCheckpointBlobHandle _thread_cp; - JfrCheckpointBlobHandle _klass_cp; + JfrBlobHandle _stacktrace; + JfrBlobHandle _thread; + JfrBlobHandle _type_set; oop _object; Ticks _allocation_time; traceid _stack_trace_id; @@ -62,17 +64,14 @@ class ObjectSample : public JfrCHeapObj { } void release_references() { - if (_thread_cp.valid()) { - _thread_cp.~JfrCheckpointBlobHandle(); - } - if (_klass_cp.valid()) { - _klass_cp.~JfrCheckpointBlobHandle(); - } + _stacktrace.~JfrBlobHandle(); + _thread.~JfrBlobHandle(); + _type_set.~JfrBlobHandle(); } void reset() { set_stack_trace_id(0); - set_stack_trace_hash(0), + set_stack_trace_hash(0); release_references(); _dead = false; } @@ -80,8 +79,9 @@ class ObjectSample : public JfrCHeapObj { public: ObjectSample() : _next(NULL), _previous(NULL), - _thread_cp(), - _klass_cp(), + _stacktrace(), + _thread(), + _type_set(), _object(NULL), _allocation_time(), _stack_trace_id(0), @@ -174,7 +174,7 @@ class ObjectSample : public JfrCHeapObj { return _heap_used_at_last_gc; } - bool has_stack_trace() const { + bool has_stack_trace_id() const { return stack_trace_id() != 0; } @@ -194,10 +194,6 @@ class ObjectSample : public JfrCHeapObj { _stack_trace_hash = hash; } - bool has_thread() const { - return _thread_id != 0; - } - traceid thread_id() const { return _thread_id; } @@ -211,37 +207,51 @@ class ObjectSample : public JfrCHeapObj { _allocation_time.ft_value() : _allocation_time.value()) < time_stamp; } - const JfrCheckpointBlobHandle& thread_checkpoint() const { - return _thread_cp; + const JfrBlobHandle& stacktrace() const { + return _stacktrace; } - bool has_thread_checkpoint() const { - return _thread_cp.valid(); + bool has_stacktrace() const { + return _stacktrace.valid(); } - // JfrCheckpointBlobHandle assignment operator + // JfrBlobHandle assignment operator // maintains proper reference counting - void set_thread_checkpoint(const JfrCheckpointBlobHandle& ref) { - if (_thread_cp != ref) { - _thread_cp = ref; + void set_stacktrace(const JfrBlobHandle& ref) { + if (_stacktrace != ref) { + _stacktrace = ref; + } + } + + const JfrBlobHandle& thread() const { + return _thread; + } + + bool has_thread() const { + return _thread.valid(); + } + + void set_thread(const JfrBlobHandle& ref) { + if (_thread != ref) { + _thread = ref; } } - const JfrCheckpointBlobHandle& klass_checkpoint() const { - return _klass_cp; + const JfrBlobHandle& type_set() const { + return _type_set; } - bool has_klass_checkpoint() const { - return _klass_cp.valid(); + bool has_type_set() const { + return _type_set.valid(); } - void set_klass_checkpoint(const JfrCheckpointBlobHandle& ref) { - if (_klass_cp != ref) { - if (_klass_cp.valid()) { - _klass_cp->set_next(ref); + void set_type_set(const JfrBlobHandle& ref) { + if (_type_set != ref) { + if (_type_set.valid()) { + _type_set->set_next(ref); return; } - _klass_cp = ref; + _type_set = ref; } } }; diff --git a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp index 90f78bad940b77fe99c0e7398aa18931f3539871..89653a6f6e04997803ae6faddcd7b5cc30078b69 100644 --- a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp +++ b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, 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 @@ -108,47 +108,28 @@ static traceid get_thread_id(JavaThread* thread) { } const JfrThreadLocal* const tl = thread->jfr_thread_local(); assert(tl != NULL, "invariant"); - if (!tl->has_thread_checkpoint()) { - JfrCheckpointManager::create_thread_checkpoint(thread); + if (!tl->has_thread_blob()) { + JfrCheckpointManager::create_thread_blob(thread); } - assert(tl->has_thread_checkpoint(), "invariant"); + assert(tl->has_thread_blob(), "invariant"); return tl->thread_id(); } -// Populates the thread local stack frames, but does not add them -// to the stacktrace repository (...yet, see stacktrace_id() below) -// -void ObjectSampler::fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread) { - assert(stacktrace != NULL, "invariant"); +static void record_stacktrace(JavaThread* thread) { assert(thread != NULL, "invariant"); if (JfrEventSetting::has_stacktrace(EventOldObjectSample::eventId)) { - JfrStackTraceRepository::fill_stacktrace_for(thread, stacktrace, 0, WALK_BY_DEFAULT); + JfrStackTraceRepository::record_and_cache(thread, 0, WALK_BY_DEFAULT); } } -// We were successful in acquiring the try lock and have been selected for adding a sample. -// Go ahead with installing our previously taken stacktrace into the stacktrace repository. -// -traceid ObjectSampler::stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread) { - assert(stacktrace != NULL, "invariant"); - assert(stacktrace->hash() != 0, "invariant"); - const traceid stacktrace_id = JfrStackTraceRepository::add(stacktrace, thread); - thread->jfr_thread_local()->set_cached_stack_trace_id(stacktrace_id, stacktrace->hash()); - return stacktrace_id; -} - void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread) { assert(thread != NULL, "invariant"); assert(is_created(), "invariant"); - const traceid thread_id = get_thread_id(thread); if (thread_id == 0) { return; } - const JfrThreadLocal* const tl = thread->jfr_thread_local(); - JfrStackTrace stacktrace(tl->stackframes(), tl->stackdepth()); - fill_stacktrace(&stacktrace, thread); - + record_stacktrace(thread); // try enter critical section JfrTryLock tryLock(&_lock); if (!tryLock.has_lock()) { @@ -156,14 +137,14 @@ void ObjectSampler::sample(HeapWord* obj, size_t allocated, JavaThread* thread) return; } - instance().add(obj, allocated, thread_id, &stacktrace, thread); + instance().add(obj, allocated, thread_id, thread); } -void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread) { - assert(stacktrace != NULL, "invariant"); +void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JavaThread* thread) { + assert(obj != NULL, "invariant"); assert(thread_id != 0, "invariant"); assert(thread != NULL, "invariant"); - assert(thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); + assert(thread->jfr_thread_local()->has_thread_blob(), "invariant"); if (_dead_samples) { scavenge(); @@ -187,11 +168,13 @@ void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, JfrS assert(sample != NULL, "invariant"); sample->set_thread_id(thread_id); - sample->set_thread_checkpoint(thread->jfr_thread_local()->thread_checkpoint()); - const unsigned int stacktrace_hash = stacktrace->hash(); + const JfrThreadLocal* const tl = thread->jfr_thread_local(); + sample->set_thread(tl->thread_blob()); + + const unsigned int stacktrace_hash = tl->cached_stack_trace_hash(); if (stacktrace_hash != 0) { - sample->set_stack_trace_id(stacktrace_id(stacktrace, thread)); + sample->set_stack_trace_id(tl->cached_stack_trace_id()); sample->set_stack_trace_hash(stacktrace_hash); } @@ -250,7 +233,7 @@ void ObjectSampler::oops_do(BoolObjectClosure* is_alive, OopClosure* f) { sampler._last_sweep = JfrTicks::now(); } -const ObjectSample* ObjectSampler::last() const { +ObjectSample* ObjectSampler::last() const { return _list->last(); } diff --git a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp index dceb1350aa06996009d9f50fcf54545097345fb3..c7b66079c147d6f27be095a843865d0a4a03ccd9 100644 --- a/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp +++ b/src/share/vm/jfr/leakprofiler/sampling/objectSampler.hpp @@ -31,25 +31,19 @@ typedef u8 traceid; class BoolObjectClosure; -class JfrStackTrace; +class JavaThread; class OopClosure; class ObjectSample; -class ObjectSampler; class SampleList; class SamplePriorityQueue; -class Thread; // Class reponsible for holding samples and // making sure the samples are evenly distributed as // new entries are added and removed. class ObjectSampler : public CHeapObj { - friend class EventEmitter; - friend class JfrRecorderService; friend class LeakProfiler; friend class StartOperation; friend class StopOperation; - friend class ObjectSampleCheckpoint; - friend class WriteObjectSampleStacktrace; private: SamplePriorityQueue* _priority_queue; SampleList* _list; @@ -64,20 +58,11 @@ class ObjectSampler : public CHeapObj { ~ObjectSampler(); static bool create(size_t size); static bool is_created(); - static ObjectSampler* sampler(); static void destroy(); - // For operations that require exclusive access (non-safepoint) - static ObjectSampler* acquire(); - static void release(); - - // Stacktrace - static void fill_stacktrace(JfrStackTrace* stacktrace, JavaThread* thread); - traceid stacktrace_id(const JfrStackTrace* stacktrace, JavaThread* thread); - // Sampling static void sample(HeapWord* object, size_t size, JavaThread* thread); - void add(HeapWord* object, size_t size, traceid thread_id, JfrStackTrace* stacktrace, JavaThread* thread); + void add(HeapWord* object, size_t size, traceid thread_id, JavaThread* thread); void scavenge(); void remove_dead(ObjectSample* sample); @@ -87,8 +72,15 @@ class ObjectSampler : public CHeapObj { const ObjectSample* item_at(int index) const; ObjectSample* item_at(int index); int item_count() const; + + public: + static ObjectSampler* sampler(); + // For operations that require exclusive access (non-safepoint) + static ObjectSampler* acquire(); + static void release(); + const ObjectSample* first() const; - const ObjectSample* last() const; + ObjectSample* last() const; const ObjectSample* last_resolved() const; void set_last_resolved(const ObjectSample* sample); const JfrTicks& last_sweep() const; diff --git a/src/share/vm/jfr/leakprofiler/sampling/sampleList.hpp b/src/share/vm/jfr/leakprofiler/sampling/sampleList.hpp index 6754ed3ba9fbfc5bd7b9259900b6f4f7c961ac53..766c7bc262f8284036b5b94961f517f6a1060d24 100644 --- a/src/share/vm/jfr/leakprofiler/sampling/sampleList.hpp +++ b/src/share/vm/jfr/leakprofiler/sampling/sampleList.hpp @@ -50,12 +50,12 @@ class SampleList : public JfrCHeapObj { SampleList(size_t limit, size_t cache_size = 0); ~SampleList(); - void set_last_resolved(const ObjectSample* sample); ObjectSample* get(); - ObjectSample* last() const; ObjectSample* first() const; - void release(ObjectSample* sample); + ObjectSample* last() const; const ObjectSample* last_resolved() const; + void set_last_resolved(const ObjectSample* sample); + void release(ObjectSample* sample); ObjectSample* reuse(ObjectSample* sample); bool is_full() const; size_t count() const; diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp deleted file mode 100644 index aaf3352f3e3d795483757891c3174ad8d61a3cbd..0000000000000000000000000000000000000000 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointBlob.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2017, 2018, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp" -#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" - -JfrCheckpointBlob::JfrCheckpointBlob(const u1* checkpoint, size_t size) : - _checkpoint(JfrCHeapObj::new_array(size)), - _size(size), - _next(), - _written(false) { - assert(checkpoint != NULL, "invariant"); - assert(_checkpoint != NULL, "invariant"); - memcpy(const_cast(_checkpoint), checkpoint, size); -} - -JfrCheckpointBlob::~JfrCheckpointBlob() { - JfrCHeapObj::free(const_cast(_checkpoint), _size); -} - -const JfrCheckpointBlobHandle& JfrCheckpointBlob::next() const { - return _next; -} - -void JfrCheckpointBlob::write_this(JfrCheckpointWriter& writer) const { - writer.bytes(_checkpoint, _size); -} - -void JfrCheckpointBlob::exclusive_write(JfrCheckpointWriter& writer) const { - if (!_written) { - write_this(writer); - _written = true; - } - if (_next.valid()) { - _next->exclusive_write(writer); - } -} - -void JfrCheckpointBlob::write(JfrCheckpointWriter& writer) const { - write_this(writer); - if (_next.valid()) { - _next->write(writer); - } -} - -void JfrCheckpointBlob::reset_write_state() const { - if (_written) { - _written = false; - } - if (_next.valid()) { - _next->reset_write_state(); - } -} - -void JfrCheckpointBlob::set_next(const JfrCheckpointBlobHandle& ref) { - if (_next == ref) { - return; - } - assert(_next != ref, "invariant"); - if (_next.valid()) { - _next->set_next(ref); - return; - } - _next = ref; -} - -JfrCheckpointBlobHandle JfrCheckpointBlob::make(const u1* checkpoint, size_t size) { - const JfrCheckpointBlob* cp_blob = new JfrCheckpointBlob(checkpoint, size); - assert(cp_blob != NULL, "invariant"); - return JfrCheckpointBlobReference::make(cp_blob); -} diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp index 69a4ccfef8dd74194bafee8492fe92a3a8bbad96..28c9a940249bd0fcd668791ab54920fb0f3eb5ab 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.cpp @@ -36,6 +36,7 @@ #include "jfr/utilities/jfrBigEndian.hpp" #include "jfr/utilities/jfrTypes.hpp" #include "memory/resourceArea.hpp" +#include "runtime/handles.inline.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/orderAccess.inline.hpp" #include "runtime/os.hpp" @@ -86,22 +87,18 @@ static const size_t unlimited_mspace_size = 0; static const size_t checkpoint_buffer_cache_count = 2; static const size_t checkpoint_buffer_size = 512 * K; -static JfrCheckpointMspace* create_mspace(size_t buffer_size, size_t limit, size_t cache_count, JfrCheckpointManager* system) { - JfrCheckpointMspace* mspace = new JfrCheckpointMspace(buffer_size, limit, cache_count, system); - if (mspace != NULL) { - mspace->initialize(); - } - return mspace; +static JfrCheckpointMspace* allocate_mspace(size_t size, size_t limit, size_t cache_count, JfrCheckpointManager* mgr) { + return create_mspace(size, limit, cache_count, mgr); } bool JfrCheckpointManager::initialize() { assert(_free_list_mspace == NULL, "invariant"); - _free_list_mspace = create_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this); + _free_list_mspace = allocate_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this); if (_free_list_mspace == NULL) { return false; } assert(_epoch_transition_mspace == NULL, "invariant"); - _epoch_transition_mspace = create_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this); + _epoch_transition_mspace = allocate_mspace(checkpoint_buffer_size, unlimited_mspace_size, checkpoint_buffer_cache_count, this); if (_epoch_transition_mspace == NULL) { return false; } @@ -113,22 +110,6 @@ bool JfrCheckpointManager::initialize() { return JfrTypeManager::initialize(); } -bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const { - return _service_thread != thread && OrderAccess::load_acquire((u1*)&_checkpoint_epoch_state) != JfrTraceIdEpoch::current(); -} - -void JfrCheckpointManager::synchronize_epoch() { - assert(_checkpoint_epoch_state != JfrTraceIdEpoch::current(), "invariant"); - OrderAccess::storestore(); - _checkpoint_epoch_state = JfrTraceIdEpoch::current(); -} - -void JfrCheckpointManager::shift_epoch() { - debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();) - JfrTraceIdEpoch::shift_epoch(); - assert(current_epoch != JfrTraceIdEpoch::current(), "invariant"); -} - void JfrCheckpointManager::register_service_thread(const Thread* thread) { _service_thread = thread; } @@ -150,7 +131,6 @@ void JfrCheckpointManager::unlock() { } #ifdef ASSERT - bool JfrCheckpointManager::is_locked() const { return _lock->owned_by_self(); } @@ -166,7 +146,6 @@ static void assert_release(const BufferPtr buffer) { assert(buffer->lease(), "invariant"); assert(buffer->acquired_by_self(), "invariant"); } - #endif // ASSERT static BufferPtr lease_free(size_t size, JfrCheckpointMspace* mspace, size_t retry_count, Thread* thread) { @@ -184,6 +163,10 @@ static BufferPtr lease_free(size_t size, JfrCheckpointMspace* mspace, size_t ret return buffer; } +bool JfrCheckpointManager::use_epoch_transition_mspace(const Thread* thread) const { + return _service_thread != thread && OrderAccess::load_acquire((u1*)&_checkpoint_epoch_state) != JfrTraceIdEpoch::epoch(); +} + static const size_t lease_retry = 10; BufferPtr JfrCheckpointManager::lease_buffer(Thread* thread, size_t size /* 0 */) { @@ -255,33 +238,33 @@ static juint number_of_types(const u1* data) { return read_data(data + types_offset); } -static void write_checkpoint_header(JfrChunkWriter& cw, intptr_t offset_prev_cp_event, const u1* data) { +static void write_checkpoint_header(JfrChunkWriter& cw, int64_t offset_prev_cp_event, const u1* data) { cw.reserve(sizeof(u4)); - cw.write((u8)EVENT_CHECKPOINT); + cw.write(EVENT_CHECKPOINT); cw.write(starttime(data)); cw.write(duration(data)); - cw.write((jlong)offset_prev_cp_event); + cw.write(offset_prev_cp_event); cw.write(is_flushpoint(data)); cw.write(number_of_types(data)); } static void write_checkpoint_content(JfrChunkWriter& cw, const u1* data, size_t size) { assert(data != NULL, "invariant"); - cw.write_unbuffered(data + payload_offset, size); + cw.write_unbuffered(data + payload_offset, size - sizeof(JfrCheckpointEntry)); } static size_t write_checkpoint_event(JfrChunkWriter& cw, const u1* data) { assert(data != NULL, "invariant"); - const intptr_t previous_checkpoint_event = cw.previous_checkpoint_offset(); - const intptr_t event_begin = cw.current_offset(); - const intptr_t offset_to_previous_checkpoint_event = 0 == previous_checkpoint_event ? 0 : previous_checkpoint_event - event_begin; - const jlong total_checkpoint_size = total_size(data); - write_checkpoint_header(cw, offset_to_previous_checkpoint_event, data); - write_checkpoint_content(cw, data, total_checkpoint_size - sizeof(JfrCheckpointEntry)); - const jlong checkpoint_event_size = cw.current_offset() - event_begin; - cw.write_padded_at_offset(checkpoint_event_size, event_begin); - cw.set_previous_checkpoint_offset(event_begin); - return (size_t)total_checkpoint_size; + const int64_t event_begin = cw.current_offset(); + const int64_t last_checkpoint_event = cw.last_checkpoint_offset(); + const int64_t delta = last_checkpoint_event == 0 ? 0 : last_checkpoint_event - event_begin; + const int64_t checkpoint_size = total_size(data); + write_checkpoint_header(cw, delta, data); + write_checkpoint_content(cw, data, checkpoint_size); + const int64_t event_size = cw.current_offset() - event_begin; + cw.write_padded_at_offset(event_size, event_begin); + cw.set_last_checkpoint_offset(event_begin); + return (size_t)checkpoint_size; } static size_t write_checkpoints(JfrChunkWriter& cw, const u1* data, size_t size) { @@ -289,14 +272,14 @@ static size_t write_checkpoints(JfrChunkWriter& cw, const u1* data, size_t size) assert(data != NULL, "invariant"); assert(size > 0, "invariant"); const u1* const limit = data + size; - const u1* next_entry = data; + const u1* next = data; size_t processed = 0; - while (next_entry < limit) { - const size_t checkpoint_size = write_checkpoint_event(cw, next_entry); + while (next < limit) { + const size_t checkpoint_size = write_checkpoint_event(cw, next); processed += checkpoint_size; - next_entry += checkpoint_size; + next += checkpoint_size; } - assert(next_entry == limit, "invariant"); + assert(next == limit, "invariant"); return processed; } @@ -331,6 +314,12 @@ static size_t write_mspace_exclusive(JfrCheckpointMspace* mspace, JfrChunkWriter return wo.processed(); } +void JfrCheckpointManager::synchronize_epoch() { + assert(_checkpoint_epoch_state != JfrTraceIdEpoch::epoch(), "invariant"); + OrderAccess::storestore(); + _checkpoint_epoch_state = JfrTraceIdEpoch::epoch(); +} + size_t JfrCheckpointManager::write() { const size_t processed = write_mspace_exclusive(_free_list_mspace, _chunkwriter); synchronize_epoch(); @@ -372,10 +361,16 @@ void JfrCheckpointManager::write_type_set_for_unloaded_classes() { JfrTypeManager::write_type_set_for_unloaded_classes(); } -void JfrCheckpointManager::create_thread_checkpoint(JavaThread* jt) { - JfrTypeManager::create_thread_checkpoint(jt); +void JfrCheckpointManager::create_thread_blob(JavaThread* jt) { + JfrTypeManager::create_thread_blob(jt); } void JfrCheckpointManager::write_thread_checkpoint(JavaThread* jt) { JfrTypeManager::write_thread_checkpoint(jt); } + +void JfrCheckpointManager::shift_epoch() { + debug_only(const u1 current_epoch = JfrTraceIdEpoch::current();) + JfrTraceIdEpoch::shift_epoch(); + assert(current_epoch != JfrTraceIdEpoch::current(), "invariant"); +} diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp index e7d4d81f72e989ea9f3119f6e375dffc289d9502..f90cdc9af38c21450925d713a773deac36953b80 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointManager.hpp @@ -92,7 +92,7 @@ class JfrCheckpointManager : public JfrCHeapObj { public: void register_service_thread(const Thread* t); static void write_type_set_for_unloaded_classes(); - static void create_thread_checkpoint(JavaThread* jt); + static void create_thread_blob(JavaThread* jt); static void write_thread_checkpoint(JavaThread* jt); friend class JfrRecorder; diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp index 1fc884780b2e79217982b3b3d61f3e3fb60d5939..fb2bd1bb0c1e1ecb913856065ddad46c7501ef88 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, 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 @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/utilities/jfrBlob.hpp" #include "jfr/writers/jfrBigEndianWriter.hpp" JfrCheckpointFlush::JfrCheckpointFlush(Type* old, size_t used, size_t requested, Thread* t) : @@ -126,7 +127,7 @@ void JfrCheckpointWriter::write_count(u4 nof_entries, jlong offset) { write_padded_at_offset(nof_entries, offset); } -const u1* JfrCheckpointWriter::session_data(size_t* size, const JfrCheckpointContext* ctx /* 0 */) { +const u1* JfrCheckpointWriter::session_data(size_t* size, bool move /* false */, const JfrCheckpointContext* ctx /* 0 */) { assert(this->is_acquired(), "wrong state!"); if (!this->is_valid()) { *size = 0; @@ -140,8 +141,10 @@ const u1* JfrCheckpointWriter::session_data(size_t* size, const JfrCheckpointCon *size = this->used_size(); assert(this->start_pos() + *size == this->current_pos(), "invariant"); write_checkpoint_header(const_cast(this->start_pos()), this->used_offset(), _time, is_flushpoint(), count()); - this->seek(_offset + (_header ? sizeof(JfrCheckpointEntry) : 0)); - set_count(0); + _header = false; // the header was just written + if (move) { + this->seek(_offset); + } return this->start_pos(); } @@ -160,26 +163,19 @@ bool JfrCheckpointWriter::has_data() const { return this->used_size() > sizeof(JfrCheckpointEntry); } -JfrCheckpointBlobHandle JfrCheckpointWriter::checkpoint_blob() { +JfrBlobHandle JfrCheckpointWriter::copy(const JfrCheckpointContext* ctx /* 0 */) { size_t size = 0; - const u1* data = session_data(&size); - return JfrCheckpointBlob::make(data, size); + const u1* data = session_data(&size, false, ctx); + return JfrBlob::make(data, size); } -JfrCheckpointBlobHandle JfrCheckpointWriter::copy(const JfrCheckpointContext* ctx /* 0 */) { - if (ctx == NULL) { - return checkpoint_blob(); - } +JfrBlobHandle JfrCheckpointWriter::move(const JfrCheckpointContext* ctx /* 0 */) { size_t size = 0; - const u1* data = session_data(&size, ctx); - return JfrCheckpointBlob::make(data, size); -} - -JfrCheckpointBlobHandle JfrCheckpointWriter::move(const JfrCheckpointContext* ctx /* 0 */) { - JfrCheckpointBlobHandle data = copy(ctx); + const u1* data = session_data(&size, true, ctx); + JfrBlobHandle blob = JfrBlob::make(data, size); if (ctx != NULL) { const_cast(ctx)->count = 0; set_context(*ctx); } - return data; + return blob; } diff --git a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp index bb01cb56e4d16b805237a328e70b6ff6ef2935a2..efdfefd14732650f9a3d5b3f2cab03710c06438b 100644 --- a/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/jfrCheckpointWriter.hpp @@ -25,8 +25,8 @@ #ifndef SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP #define SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP -#include "jfr/recorder/checkpoint/jfrCheckpointBlob.hpp" #include "jfr/recorder/storage/jfrBuffer.hpp" +#include "jfr/utilities/jfrBlob.hpp" #include "jfr/utilities/jfrTime.hpp" #include "jfr/utilities/jfrTypes.hpp" #include "jfr/writers/jfrEventWriterHost.inline.hpp" @@ -67,9 +67,8 @@ class JfrCheckpointWriter : public JfrCheckpointWriterBase { void increment(); void set_flushpoint(bool flushpoint); bool is_flushpoint() const; - const u1* session_data(size_t* size, const JfrCheckpointContext* ctx = NULL); + const u1* session_data(size_t* size, bool move = false, const JfrCheckpointContext* ctx = NULL); void release(); - public: JfrCheckpointWriter(bool flushpoint, bool header, Thread* thread); ~JfrCheckpointWriter(); @@ -80,9 +79,8 @@ class JfrCheckpointWriter : public JfrCheckpointWriterBase { const JfrCheckpointContext context() const; void set_context(const JfrCheckpointContext ctx); bool has_data() const; - JfrCheckpointBlobHandle checkpoint_blob(); - JfrCheckpointBlobHandle copy(const JfrCheckpointContext* ctx = NULL); - JfrCheckpointBlobHandle move(const JfrCheckpointContext* ctx = NULL); + JfrBlobHandle copy(const JfrCheckpointContext* ctx = NULL); + JfrBlobHandle move(const JfrCheckpointContext* ctx = NULL); }; #endif // SHARE_VM_JFR_RECORDER_CHECKPOINT_JFRCHECKPOINTWRITER_HPP diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp index 668083c7b03d2768163588bc33ae355f044da55b..e513abc63976e93570909ac09d7ed596071c961b 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.cpp @@ -30,7 +30,6 @@ #include "gc_interface/gcName.hpp" #include "gc_implementation/shared/gcTrace.hpp" #include "gc_implementation/shared/gcWhen.hpp" -#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" #include "jfr/leakprofiler/leakProfiler.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointManager.hpp" #include "jfr/recorder/checkpoint/types/jfrType.hpp" @@ -295,34 +294,26 @@ void VMOperationTypeConstant::serialize(JfrCheckpointWriter& writer) { class TypeSetSerialization { private: + JfrCheckpointWriter* _leakp_writer; bool _class_unload; public: - explicit TypeSetSerialization(bool class_unload) : _class_unload(class_unload) {} - void write(JfrCheckpointWriter& writer, JfrCheckpointWriter* leakp_writer) { - JfrTypeSet::serialize(&writer, leakp_writer, _class_unload); + TypeSetSerialization(bool class_unload, JfrCheckpointWriter* leakp_writer = NULL) : + _leakp_writer(leakp_writer), _class_unload(class_unload) {} + void write(JfrCheckpointWriter& writer) { + JfrTypeSet::serialize(&writer, _leakp_writer, _class_unload); } }; void ClassUnloadTypeSet::serialize(JfrCheckpointWriter& writer) { TypeSetSerialization type_set(true); - if (LeakProfiler::is_running()) { - JfrCheckpointWriter leakp_writer(false, true, Thread::current()); - type_set.write(writer, &leakp_writer); - ObjectSampleCheckpoint::install(leakp_writer, true, true); - return; - } - type_set.write(writer, NULL); + type_set.write(writer); }; +TypeSet::TypeSet(JfrCheckpointWriter* leakp_writer) : _leakp_writer(leakp_writer) {} + void TypeSet::serialize(JfrCheckpointWriter& writer) { - TypeSetSerialization type_set(false); - if (LeakProfiler::is_running()) { - JfrCheckpointWriter leakp_writer(false, true, Thread::current()); - type_set.write(writer, &leakp_writer); - ObjectSampleCheckpoint::install(leakp_writer, false, true); - return; - } - type_set.write(writer, NULL); + TypeSetSerialization type_set(false, _leakp_writer); + type_set.write(writer); }; void ThreadStateConstant::serialize(JfrCheckpointWriter& writer) { @@ -333,7 +324,6 @@ void JfrThreadConstant::serialize(JfrCheckpointWriter& writer) { assert(_thread != NULL, "invariant"); assert(_thread == Thread::current(), "invariant"); assert(_thread->is_Java_thread(), "invariant"); - assert(!_thread->jfr_thread_local()->has_thread_checkpoint(), "invariant"); ResourceMark rm(_thread); const oop threadObj = _thread->threadObj(); assert(threadObj != NULL, "invariant"); diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp index 5f91819b9e17016c92062460f8c5378f1809e286..a33fe39819f6283bfbedad30fee5d5bf6cffc52e 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrType.hpp @@ -118,7 +118,10 @@ class VMOperationTypeConstant : public JfrSerializer { }; class TypeSet : public JfrSerializer { + private: + JfrCheckpointWriter* _leakp_writer; public: + explicit TypeSet(JfrCheckpointWriter* leakp_writer = NULL); void serialize(JfrCheckpointWriter& writer); }; diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp index 0520478f4782e39ff6e64b347b947971b70c797f..916956457c50d46dc6718f469b39a7b4cf2d257a 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, 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 @@ -23,12 +23,17 @@ */ #include "precompiled.hpp" +#include "jfr/jfr.hpp" +#include "jfr/leakprofiler/leakProfiler.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" #include "jfr/metadata/jfrSerializer.hpp" #include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" #include "jfr/recorder/checkpoint/types/jfrType.hpp" #include "jfr/recorder/checkpoint/types/jfrTypeManager.hpp" #include "jfr/utilities/jfrDoublyLinkedList.hpp" #include "jfr/utilities/jfrIterator.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/handles.inline.hpp" #include "runtime/safepoint.hpp" #include "runtime/thread.inline.hpp" #include "utilities/exceptions.hpp" @@ -39,7 +44,7 @@ class JfrSerializerRegistration : public JfrCHeapObj { JfrSerializerRegistration* _next; JfrSerializerRegistration* _prev; JfrSerializer* _serializer; - mutable JfrCheckpointBlobHandle _cache; + mutable JfrBlobHandle _cache; JfrTypeId _id; bool _permit_cache; @@ -148,43 +153,58 @@ void JfrTypeManager::write_safepoint_types(JfrCheckpointWriter& writer) { } void JfrTypeManager::write_type_set() { - // can safepoint here because of PackageTable_lock - MutexLockerEx lock(SafepointSynchronize::is_at_safepoint() ? NULL : PackageTable_lock); - JfrCheckpointWriter writer(true, true, Thread::current()); - TypeSet set; + assert(!SafepointSynchronize::is_at_safepoint(), "invariant"); + // can safepoint here + MutexLocker module_lock(PackageTable_lock); + if (!LeakProfiler::is_running()) { + JfrCheckpointWriter writer(true, true, Thread::current()); + TypeSet set; + set.serialize(writer); + return; + } + JfrCheckpointWriter leakp_writer(false, true, Thread::current()); + JfrCheckpointWriter writer(false, true, Thread::current()); + TypeSet set(&leakp_writer); set.serialize(writer); + ObjectSampleCheckpoint::on_type_set(leakp_writer); } void JfrTypeManager::write_type_set_for_unloaded_classes() { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); JfrCheckpointWriter writer(false, true, Thread::current()); + const JfrCheckpointContext ctx = writer.context(); ClassUnloadTypeSet class_unload_set; class_unload_set.serialize(writer); + if (LeakProfiler::is_running()) { + ObjectSampleCheckpoint::on_type_set_unload(writer); + } + if (!Jfr::is_recording()) { + // discard anything written + writer.set_context(ctx); + } } -void JfrTypeManager::create_thread_checkpoint(JavaThread* jt) { +void JfrTypeManager::create_thread_blob(JavaThread* jt) { assert(jt != NULL, "invariant"); + ResourceMark rm(jt); + HandleMark hm(jt); JfrThreadConstant type_thread(jt); JfrCheckpointWriter writer(false, true, jt); writer.write_type(TYPE_THREAD); type_thread.serialize(writer); // create and install a checkpoint blob - jt->jfr_thread_local()->set_thread_checkpoint(writer.checkpoint_blob()); - assert(jt->jfr_thread_local()->has_thread_checkpoint(), "invariant"); + jt->jfr_thread_local()->set_thread_blob(writer.move()); + assert(jt->jfr_thread_local()->has_thread_blob(), "invariant"); } void JfrTypeManager::write_thread_checkpoint(JavaThread* jt) { assert(jt != NULL, "JavaThread is NULL!"); ResourceMark rm(jt); - if (jt->jfr_thread_local()->has_thread_checkpoint()) { - JfrCheckpointWriter writer(false, false, jt); - jt->jfr_thread_local()->thread_checkpoint()->write(writer); - } else { - JfrThreadConstant type_thread(jt); - JfrCheckpointWriter writer(false, true, jt); - writer.write_type(TYPE_THREAD); - type_thread.serialize(writer); - } + HandleMark hm(jt); + JfrThreadConstant type_thread(jt); + JfrCheckpointWriter writer(false, true, jt); + writer.write_type(TYPE_THREAD); + type_thread.serialize(writer); } #ifdef ASSERT diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp index a37d54711a26079d121ad73fb06a43ca21019f18..0bd195d7211f3f0b299e28b7c253066dd92450b6 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeManager.hpp @@ -37,7 +37,7 @@ class JfrTypeManager : public AllStatic { static void write_safepoint_types(JfrCheckpointWriter& writer); static void write_type_set(); static void write_type_set_for_unloaded_classes(); - static void create_thread_checkpoint(JavaThread* jt); + static void create_thread_blob(JavaThread* jt); static void write_thread_checkpoint(JavaThread* jt); }; diff --git a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp index 05f430c94f5c9b4258b93286f7c5c8a7c492c340..81bb4107f9b89ed1f237f642677d42e515742dba 100644 --- a/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrTypeSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, 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 @@ -23,37 +23,26 @@ */ #include "precompiled.hpp" -#include "classfile/classLoaderData.inline.hpp" +#include "classfile/classLoaderData.hpp" #include "classfile/javaClasses.hpp" // XXX #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" -#include "classfile/systemDictionary.hpp" #include "jfr/jfr.hpp" #include "jfr/jni/jfrGetAllEventClasses.hpp" -#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleCheckpoint.hpp" #include "jfr/recorder/checkpoint/types/jfrTypeSet.hpp" #include "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp" -#include "jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp" #include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp" -#include "jfr/recorder/storage/jfrBuffer.hpp" #include "jfr/utilities/jfrHashtable.hpp" #include "jfr/utilities/jfrTypes.hpp" +#include "jfr/writers/jfrTypeWriterHost.hpp" #include "memory/iterator.hpp" #include "memory/resourceArea.hpp" #include "oops/instanceKlass.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" -#include "memory/resourceArea.hpp" #include "utilities/accessFlags.hpp" -// incremented on each checkpoint -static u8 checkpoint_id = 0; - -// creates a unique id by combining a checkpoint relative symbol id (2^24) -// with the current checkpoint id (2^40) -#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id))) -#define CREATE_PACKAGE_ID(pkg_id) (((u8)((checkpoint_id << 24) | pkg_id))) - typedef const Klass* KlassPtr; // XXX typedef const PackageEntry* PkgPtr; typedef const ClassLoaderData* CldPtr; @@ -62,6 +51,55 @@ typedef const Symbol* SymbolPtr; typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr; typedef const JfrSymbolId::CStringEntry* CStringEntryPtr; +static JfrCheckpointWriter* _writer = NULL; +static JfrCheckpointWriter* _leakp_writer = NULL; +static JfrArtifactSet* _artifacts = NULL; +static JfrArtifactClosure* _subsystem_callback = NULL; +static bool _class_unload = false; +static bool _flushpoint = false; + +// incremented on each rotation +static u8 checkpoint_id = 1; + +// creates a unique id by combining a checkpoint relative symbol id (2^24) +// with the current checkpoint id (2^40) +#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id))) +#define CREATE_PACKAGE_ID(pkg_id) (((u8)((checkpoint_id << 24) | pkg_id))) + +static traceid create_symbol_id(traceid artifact_id) { + return artifact_id != 0 ? CREATE_SYMBOL_ID(artifact_id) : 0; +} + +static bool current_epoch() { + return _class_unload; +} + +static bool previous_epoch() { + return !current_epoch(); +} + +static bool is_complete() { + return !_artifacts->has_klass_entries() && current_epoch(); +} + +static traceid mark_symbol(KlassPtr klass, bool leakp) { + return klass != NULL ? create_symbol_id(_artifacts->mark(klass, leakp)) : 0; +} + +static traceid mark_symbol(Symbol* symbol, bool leakp) { + return symbol != NULL ? create_symbol_id(_artifacts->mark(symbol, leakp)) : 0; +} + +static traceid get_bootstrap_name(bool leakp) { + return create_symbol_id(_artifacts->bootstrap_name(leakp)); +} + +template +static traceid artifact_id(const T* ptr) { + assert(ptr != NULL, "invariant"); + return TRACE_ID(ptr); +} + inline uintptr_t package_name_hash(const char *s) { uintptr_t val = 0; while (*s != 0) { @@ -70,6 +108,7 @@ inline uintptr_t package_name_hash(const char *s) { return val; } +/** static traceid package_id(KlassPtr klass, JfrArtifactSet* artifacts) { assert(klass != NULL, "invariant"); char* klass_name = klass->name()->as_C_string(); // uses ResourceMark declared in JfrTypeSet::serialize() @@ -79,43 +118,38 @@ static traceid package_id(KlassPtr klass, JfrArtifactSet* artifacts) { } return CREATE_PACKAGE_ID(artifacts->markPackage(pkg_name, package_name_hash(pkg_name))); } +*/ -static traceid cld_id(CldPtr cld) { - assert(cld != NULL, "invariant"); - return cld->is_anonymous() ? 0 : TRACE_ID(cld); +static traceid method_id(KlassPtr klass, MethodPtr method) { + assert(klass != NULL, "invariant"); + assert(method != NULL, "invariant"); + return METHOD_ID(klass, method); } - -static void tag_leakp_klass_artifacts(KlassPtr k, bool class_unload) { - assert(k != NULL, "invariant"); - // XXX - // PkgPtr pkg = k->package(); - // if (pkg != NULL) { - // tag_leakp_artifact(pkg, class_unload); - // } - CldPtr cld = k->class_loader_data(); +static traceid cld_id(CldPtr cld, bool leakp) { assert(cld != NULL, "invariant"); - if (!cld->is_anonymous()) { - tag_leakp_artifact(cld, class_unload); + if (cld->is_anonymous()) { + return 0; + } + if (leakp) { + SET_LEAKP(cld); + } else { + SET_TRANSIENT(cld); } + return artifact_id(cld); } -class TagLeakpKlassArtifact { - bool _class_unload; - public: - TagLeakpKlassArtifact(bool class_unload) : _class_unload(class_unload) {} - bool operator()(KlassPtr klass) { - if (_class_unload) { - if (LEAKP_USED_THIS_EPOCH(klass)) { - tag_leakp_klass_artifacts(klass, _class_unload); - } - } else { - if (LEAKP_USED_PREV_EPOCH(klass)) { - tag_leakp_klass_artifacts(klass, _class_unload); - } - } - return true; - } -}; +template +static s4 get_flags(const T* ptr) { + assert(ptr != NULL, "invariant"); + return ptr->access_flags().get_flags(); +} + +template +static void set_serialized(const T* ptr) { + assert(ptr != NULL, "invariant"); + SET_SERIALIZED(ptr); + assert(IS_SERIALIZED(ptr), "invariant"); +} /* * In C++03, functions used as template parameters must have external linkage; @@ -125,11 +159,10 @@ class TagLeakpKlassArtifact { * The weird naming is an effort to decrease the risk of name clashes. */ -int write__artifact__klass(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) { +static int write_klass(JfrCheckpointWriter* writer, KlassPtr klass, bool leakp) { assert(writer != NULL, "invariant"); - assert(artifacts != NULL, "invariant"); - assert(k != NULL, "invariant"); - KlassPtr klass = (KlassPtr)k; + assert(_artifacts != NULL, "invariant"); + assert(klass != NULL, "invariant"); traceid pkg_id = 0; KlassPtr theklass = klass; if (theklass->oop_is_objArray()) { @@ -137,17 +170,16 @@ int write__artifact__klass(JfrCheckpointWriter* writer, JfrArtifactSet* artifact theklass = obj_arr_klass->bottom_klass(); } if (theklass->oop_is_instance()) { - pkg_id = package_id(theklass, artifacts); + // pkg_id = package_id(theklass, _artifacts); + pkg_id = 0; } else { assert(theklass->oop_is_typeArray(), "invariant"); } - const traceid symbol_id = artifacts->mark(klass); - assert(symbol_id > 0, "need to have an address for symbol!"); - writer->write(TRACE_ID(klass)); - writer->write(cld_id(klass->class_loader_data())); - writer->write((traceid)CREATE_SYMBOL_ID(symbol_id)); + writer->write(artifact_id(klass)); + writer->write(cld_id(klass->class_loader_data(), leakp)); + writer->write(mark_symbol(klass, leakp)); writer->write(pkg_id); - writer->write((s4)klass->access_flags().get_flags()); + writer->write(get_flags(klass)); if (klass->oop_is_array()) { // The object array size can not be determined statically from klass. // It is determined by the elements length in object layout. @@ -161,43 +193,114 @@ int write__artifact__klass(JfrCheckpointWriter* writer, JfrArtifactSet* artifact return 1; } -typedef LeakPredicate LeakKlassPredicate; -typedef JfrPredicatedArtifactWriterImplHost LeakKlassWriterImpl; -typedef JfrArtifactWriterHost LeakKlassWriter; -typedef JfrArtifactWriterImplHost KlassWriterImpl; -typedef JfrArtifactWriterHost KlassWriter; +int write__klass(JfrCheckpointWriter* writer, const void* k) { + assert(k != NULL, "invariant"); + KlassPtr klass = (KlassPtr)k; + set_serialized(klass); + return write_klass(writer, klass, false); +} -int write__artifact__method(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* m) { - assert(writer != NULL, "invariant"); - assert(artifacts != NULL, "invariant"); - assert(m != NULL, "invariant"); - MethodPtr method = (MethodPtr)m; - const traceid method_name_symbol_id = artifacts->mark(method->name()); - assert(method_name_symbol_id > 0, "invariant"); - const traceid method_sig_symbol_id = artifacts->mark(method->signature()); - assert(method_sig_symbol_id > 0, "invariant"); - KlassPtr klass = method->method_holder(); +int write__klass__leakp(JfrCheckpointWriter* writer, const void* k) { + assert(k != NULL, "invariant"); + KlassPtr klass = (KlassPtr)k; + return write_klass(writer, klass, true); +} + +static void do_implied(Klass* klass) { assert(klass != NULL, "invariant"); - assert(METHOD_USED_ANY_EPOCH(klass), "invariant"); - writer->write((u8)METHOD_ID(klass, method)); - writer->write((u8)TRACE_ID(klass)); - writer->write((u8)CREATE_SYMBOL_ID(method_name_symbol_id)); - writer->write((u8)CREATE_SYMBOL_ID(method_sig_symbol_id)); - writer->write((u2)method->access_flags().get_flags()); - writer->write(const_cast(method)->is_hidden() ? (u1)1 : (u1)0); - return 1; + if (klass->is_subclass_of(SystemDictionary::ClassLoader_klass()) || klass == SystemDictionary::Object_klass()) { + if (_leakp_writer != NULL) { + SET_LEAKP(klass); + } + _subsystem_callback->do_artifact(klass); + } +} + +static void do_unloaded_klass(Klass* klass) { + assert(klass != NULL, "invariant"); + assert(_subsystem_callback != NULL, "invariant"); + if (IS_JDK_JFR_EVENT_SUBKLASS(klass)) { + JfrEventClasses::increment_unloaded_event_class(); + } + if (USED_THIS_EPOCH(klass)) { + ObjectSampleCheckpoint::on_klass_unload(klass); + _subsystem_callback->do_artifact(klass); + return; + } + do_implied(klass); } -typedef JfrArtifactWriterImplHost MethodWriterImplTarget; -typedef JfrArtifactWriterHost MethodWriterImpl; +static void do_klass(Klass* klass) { + assert(klass != NULL, "invariant"); + assert(_subsystem_callback != NULL, "invariant"); + if (current_epoch()) { + if (USED_THIS_EPOCH(klass)) { + _subsystem_callback->do_artifact(klass); + return; + } + } else { + if (USED_PREV_EPOCH(klass)) { + _subsystem_callback->do_artifact(klass); + return; + } + } + do_implied(klass); +} -int write__artifact__package(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* p) { +static void do_klasses() { + if (_class_unload) { + ClassLoaderDataGraph::classes_unloading_do(&do_unloaded_klass); + return; + } + ClassLoaderDataGraph::classes_do(&do_klass); +} + +typedef SerializePredicate KlassPredicate; +typedef JfrPredicatedTypeWriterImplHost KlassWriterImpl; +typedef JfrTypeWriterHost KlassWriter; +typedef CompositeFunctor KlassWriterRegistration; +typedef JfrArtifactCallbackHost KlassCallback; + +typedef LeakPredicate LeakKlassPredicate; +typedef JfrPredicatedTypeWriterImplHost LeakKlassWriterImpl; +typedef JfrTypeWriterHost LeakKlassWriter; + +typedef CompositeFunctor CompositeKlassWriter; +typedef CompositeFunctor CompositeKlassWriterRegistration; +typedef JfrArtifactCallbackHost CompositeKlassCallback; + +static bool write_klasses() { + assert(!_artifacts->has_klass_entries(), "invariant"); + assert(_writer != NULL, "invariant"); + KlassArtifactRegistrator reg(_artifacts); + KlassWriter kw(_writer, _class_unload); + KlassWriterRegistration kwr(&kw, ®); + if (_leakp_writer == NULL) { + KlassCallback callback(&kwr); + _subsystem_callback = &callback; + do_klasses(); + } else { + LeakKlassWriter lkw(_leakp_writer, _artifacts, _class_unload); + CompositeKlassWriter ckw(&lkw, &kw); + CompositeKlassWriterRegistration ckwr(&ckw, ®); + CompositeKlassCallback callback(&ckwr); + _subsystem_callback = &callback; + do_klasses(); + } + if (is_complete()) { + return false; + } + _artifacts->tally(kw); + return true; +} + +int write__artifact__package(JfrCheckpointWriter* writer, const void* p) { assert(writer != NULL, "invariant"); - assert(artifacts != NULL, "invariant"); + assert(_artifacts != NULL, "invariant"); assert(p != NULL, "invariant"); CStringEntryPtr entry = (CStringEntryPtr)p; - const traceid package_name_symbol_id = artifacts->mark(entry->value(), package_name_hash(entry->value())); + const traceid package_name_symbol_id = _artifacts->mark(package_name_hash(entry->value()), entry->value(), false); assert(package_name_symbol_id > 0, "invariant"); writer->write((traceid)CREATE_PACKAGE_ID(entry->id())); writer->write((traceid)CREATE_SYMBOL_ID(package_name_symbol_id)); @@ -205,628 +308,400 @@ int write__artifact__package(JfrCheckpointWriter* writer, JfrArtifactSet* artifa return 1; } -int write__artifact__classloader(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* c) { - assert(c != NULL, "invariant"); - CldPtr cld = (CldPtr)c; +/** +typedef JfrTypeWriterImplHost PackageEntryWriterImpl; +typedef JfrTypeWriterHost PackageEntryWriter; + +void write_packages() { + // below jdk9 there is no oop for packages, so nothing to do with leakp_writer + // just write packages + PackageEntryWriter pw(_writer, _class_unload); + _artifacts->iterate_packages(pw); +} +*/ + +template +static void do_previous_epoch_artifact(JfrArtifactClosure* callback, T* value) { + assert(callback != NULL, "invariant"); + assert(value != NULL, "invariant"); + if (USED_PREV_EPOCH(value)) { + callback->do_artifact(value); + assert(IS_NOT_SERIALIZED(value), "invariant"); + return; + } + if (IS_SERIALIZED(value)) { + CLEAR_SERIALIZED(value); + } + assert(IS_NOT_SERIALIZED(value), "invariant"); +} + +static int write_classloader(JfrCheckpointWriter* writer, CldPtr cld, bool leakp) { + assert(cld != NULL, "invariant"); assert(!cld->is_anonymous(), "invariant"); - const traceid cld_id = TRACE_ID(cld); // class loader type const Klass* class_loader_klass = cld->class_loader() != NULL ? cld->class_loader()->klass() : NULL; if (class_loader_klass == NULL) { // (primordial) boot class loader - writer->write(cld_id); // class loader instance id + writer->write(artifact_id(cld)); // class loader instance id writer->write((traceid)0); // class loader type id (absence of) - writer->write((traceid)CREATE_SYMBOL_ID(1)); // 1 maps to synthetic name -> "bootstrap" + writer->write(get_bootstrap_name(leakp)); // maps to synthetic name -> "bootstrap" } else { - Symbol* symbol_name = class_loader_klass->name(); - const traceid symbol_name_id = symbol_name != NULL ? artifacts->mark(symbol_name) : 0; - writer->write(cld_id); // class loader instance id - writer->write(TRACE_ID(class_loader_klass)); // class loader type id - writer->write(symbol_name_id == 0 ? (traceid)0 : - (traceid)CREATE_SYMBOL_ID(symbol_name_id)); // class loader instance name + writer->write(artifact_id(cld)); // class loader instance id + writer->write(artifact_id(class_loader_klass)); // class loader type id + writer->write(mark_symbol(class_loader_klass->name(), leakp)); // class loader instance name } return 1; } -typedef LeakPredicate LeakCldPredicate; -int _compare_cld_ptr_(CldPtr const& lhs, CldPtr const& rhs) { return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; } -typedef UniquePredicate CldPredicate; -typedef JfrPredicatedArtifactWriterImplHost LeakCldWriterImpl; -typedef JfrPredicatedArtifactWriterImplHost CldWriterImpl; -typedef JfrArtifactWriterHost LeakCldWriter; -typedef JfrArtifactWriterHost CldWriter; - -typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr; - -static int write__artifact__symbol__entry__(JfrCheckpointWriter* writer, - SymbolEntryPtr entry) { - assert(writer != NULL, "invariant"); - assert(entry != NULL, "invariant"); - ResourceMark rm; - writer->write(CREATE_SYMBOL_ID(entry->id())); - writer->write(entry->value()->as_C_string()); - return 1; -} - -int write__artifact__symbol__entry(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* e) { - assert(e != NULL, "invariant"); - return write__artifact__symbol__entry__(writer, (SymbolEntryPtr)e); -} - -typedef JfrArtifactWriterImplHost SymbolEntryWriterImpl; -typedef JfrArtifactWriterHost SymbolEntryWriter; - -typedef const JfrSymbolId::CStringEntry* CStringEntryPtr; - -static int write__artifact__cstring__entry__(JfrCheckpointWriter* writer, CStringEntryPtr entry) { - assert(writer != NULL, "invariant"); - assert(entry != NULL, "invariant"); - writer->write(CREATE_SYMBOL_ID(entry->id())); - writer->write(entry->value()); - return 1; -} - -int write__artifact__cstring__entry(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* e) { - assert(e != NULL, "invariant"); - return write__artifact__cstring__entry__(writer, (CStringEntryPtr)e); +int write__classloader(JfrCheckpointWriter* writer, const void* c) { + assert(c != NULL, "invariant"); + CldPtr cld = (CldPtr)c; + set_serialized(cld); + return write_classloader(writer, cld, false); } -typedef JfrArtifactWriterImplHost CStringEntryWriterImpl; -typedef JfrArtifactWriterHost CStringEntryWriter; - -int write__artifact__klass__symbol(JfrCheckpointWriter* writer, JfrArtifactSet* artifacts, const void* k) { - assert(writer != NULL, "invariant"); - assert(artifacts != NULL, "invaiant"); - assert(k != NULL, "invariant"); - const InstanceKlass* const ik = (const InstanceKlass*)k; - if (ik->is_anonymous()) { - CStringEntryPtr entry = - artifacts->map_cstring(JfrSymbolId::anonymous_klass_name_hash_code(ik)); - assert(entry != NULL, "invariant"); - return write__artifact__cstring__entry__(writer, entry); - } - - SymbolEntryPtr entry = artifacts->map_symbol(JfrSymbolId::regular_klass_name_hash_code(ik)); - return write__artifact__symbol__entry__(writer, entry); +int write__classloader__leakp(JfrCheckpointWriter* writer, const void* c) { + assert(c != NULL, "invariant"); + CldPtr cld = (CldPtr)c; + CLEAR_LEAKP(cld); + return write_classloader(writer, cld, true); } -int _compare_traceid_(const traceid& lhs, const traceid& rhs) { - return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; +static void do_class_loader_data(ClassLoaderData* cld) { + do_previous_epoch_artifact(_subsystem_callback, cld); } -template