--- /dev/null 2019-01-28 17:46:06.000000000 +0800 +++ new/src/share/vm/jfr/leakprofiler/checkpoint/objectSampleWriter.cpp 2019-01-28 17:46:05.000000000 +0800 @@ -0,0 +1,615 @@ +/* + * 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 + * 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/constant/jfrTagSet.hpp" +#include "jfr/recorder/checkpoint/constant/jfrTagSetWriter.hpp" +#include "jfr/leakprofiler/chains/edge.hpp" +#include "jfr/leakprofiler/chains/edgeStore.hpp" +#include "jfr/leakprofiler/chains/edgeUtils.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleDescription.hpp" +#include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp" +#include "jfr/leakprofiler/checkpoint/rootResolver.hpp" +#include "jfr/leakprofiler/sampling/objectSampler.hpp" +#include "jfr/leakprofiler/utilities/rootType.hpp" +#include "jfr/leakprofiler/utilities/unifiedOop.hpp" +#include "oops/oop.inline.hpp" +#include "oops/symbol.hpp" +#include "tracefiles/traceTypes.hpp" +#include "utilities/growableArray.hpp" + +template +class ObjectSampleAuxInfo : public ResourceObj { + public: + Data _data; + traceid _id; + ObjectSampleAuxInfo() : _data(), _id(0) {} +}; + +class ObjectSampleArrayData { + public: + int _array_size; + int _array_index; + ObjectSampleArrayData() : _array_size(0), _array_index(0) {} +}; + +class ObjectSampleFieldInfo : public ResourceObj { + public: + const Symbol* _field_name_symbol; + jshort _field_modifiers; + ObjectSampleFieldInfo() : _field_name_symbol(NULL), _field_modifiers(0) {} +}; + +class ObjectSampleRootDescriptionData { + public: + const Edge* _root_edge; + const char* _description; + OldObjectRoot::System _system; + OldObjectRoot::Type _type; + ObjectSampleRootDescriptionData() : _root_edge(NULL), + _description(NULL), + _system(OldObjectRoot::_system_undetermined), + _type(OldObjectRoot::_type_undetermined) {} +}; + +class OldObjectSampleData { + public: + oop _object; + traceid _reference_id; +}; + +class ReferenceData { + public: + traceid _field_info_id; + traceid _array_info_id; + traceid _old_object_sample_id; + size_t _skip; +}; + +static int initial_storage_size = 16; + +template +class SampleSet : public ResourceObj { + private: + GrowableArray* _storage; + public: + SampleSet() : _storage(NULL) {} + + traceid store(Data data) { + assert(data != NULL, "invariant"); + if (_storage == NULL) { + _storage = new GrowableArray(initial_storage_size); + } + assert(_storage != NULL, "invariant"); + assert(_storage->find(data) == -1, "invariant"); + _storage->append(data); + return data->_id; + } + + size_t size() const { + return _storage != NULL ? (size_t)_storage->length() : 0; + } + + template + void iterate(Functor& functor) { + if (_storage != NULL) { + for (int i = 0; i < _storage->length(); ++i) { + functor(_storage->at(i)); + } + } + } + + const GrowableArray& storage() const { + return *_storage; + } +}; + +typedef ObjectSampleAuxInfo ObjectSampleArrayInfo; +typedef ObjectSampleAuxInfo ObjectSampleRootDescriptionInfo; +typedef ObjectSampleAuxInfo OldObjectSampleInfo; +typedef ObjectSampleAuxInfo ReferenceInfo; + +class FieldTable : public ResourceObj { + template class, + typename, + size_t> + friend class HashTableHost; + typedef HashTableHost FieldInfoTable; + public: + typedef FieldInfoTable::HashEntry FieldInfoEntry; + + private: + static traceid _field_id_counter; + FieldInfoTable* _table; + + void assign_id(FieldInfoEntry* entry) { + assert(entry != NULL, "invariant"); + entry->set_id(++_field_id_counter); + } + + bool equals(const ObjectSampleFieldInfo* query, 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(const_cast(stored->_field_name_symbol)->identity_hash() == const_cast(query->_field_name_symbol)->identity_hash(), "invariant"); + return stored->_field_modifiers == query->_field_modifiers; + } + + public: + FieldTable() : _table(new FieldInfoTable(this)) {} + ~FieldTable() { + assert(_table != NULL, "invariant"); + delete _table; + } + + 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()); + return entry.id(); + } + + size_t size() const { + return _table->cardinality(); + } + + template + void iterate(T& functor) const { + _table->iterate_entry(functor); + } +}; + +traceid FieldTable::_field_id_counter = 0; + +typedef SampleSet SampleInfo; +typedef SampleSet RefInfo; +typedef SampleSet ArrayInfo; +typedef SampleSet RootDescriptionInfo; + +static SampleInfo* sample_infos = NULL; +static RefInfo* ref_infos = NULL; +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) { + assert(writer != NULL, "invariant"); + assert(si != NULL, "invariant"); + const OldObjectSampleInfo* const oosi = (const OldObjectSampleInfo*)si; + oop object = oosi->_data._object; + assert(object != NULL, "invariant"); + writer->write(oosi->_id); + writer->write((u8)(const HeapWord*)object); + writer->write(const_cast(object->klass())); + ObjectSampleDescription od(object); + writer->write(od.description()); + writer->write(oosi->_data._reference_id); + return 1; +} + +typedef JfrArtifactWriterImplHost SampleWriterImpl; +typedef JfrArtifactWriterHost SampleWriter; + +static void write_sample_infos(JfrCheckpointWriter& writer) { + if (sample_infos != NULL) { + SampleWriter sw(&writer, NULL, false); + sample_infos->iterate(sw); + } +} + +int __write_reference_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ri) { + assert(writer != NULL, "invariant"); + assert(ri != NULL, "invariant"); + const ReferenceInfo* const ref_info = (const ReferenceInfo*)ri; + writer->write(ref_info->_id); + writer->write(ref_info->_data._array_info_id); + writer->write(ref_info->_data._field_info_id); + writer->write(ref_info->_data._old_object_sample_id); + writer->write((s4)ref_info->_data._skip); + return 1; +} + +typedef JfrArtifactWriterImplHost ReferenceWriterImpl; +typedef JfrArtifactWriterHost ReferenceWriter; + +static void write_reference_infos(JfrCheckpointWriter& writer) { + if (ref_infos != NULL) { + ReferenceWriter rw(&writer, NULL, false); + ref_infos->iterate(rw); + } +} + +int __write_array_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ai) { + assert(writer != NULL, "invariant"); + assert(ai != NULL, "invariant"); + const ObjectSampleArrayInfo* const osai = (const ObjectSampleArrayInfo*)ai; + writer->write(osai->_id); + writer->write(osai->_data._array_size); + writer->write(osai->_data._array_index); + return 1; +} + +static traceid get_array_info_id(const Edge& edge, traceid id) { + if (edge.is_root() || !EdgeUtils::is_array_element(edge)) { + return 0; + } + if (array_infos == NULL) { + array_infos = new ArrayInfo(); + } + assert(array_infos != NULL, "invariant"); + + ObjectSampleArrayInfo* const osai = new ObjectSampleArrayInfo(); + assert(osai != NULL, "invariant"); + osai->_id = id; + osai->_data._array_size = EdgeUtils::array_size(edge); + osai->_data._array_index = EdgeUtils::array_index(edge); + return array_infos->store(osai); +} + +typedef JfrArtifactWriterImplHost ArrayWriterImpl; +typedef JfrArtifactWriterHost ArrayWriter; + +static void write_array_infos(JfrCheckpointWriter& writer) { + if (array_infos != NULL) { + ArrayWriter aw(&writer, NULL, false); + array_infos->iterate(aw); + } +} + +int __write_field_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* fi) { + assert(writer != NULL, "invariant"); + assert(fi != NULL, "invariant"); + const FieldTable::FieldInfoEntry* field_info_entry = (const FieldTable::FieldInfoEntry*)fi; + writer->write(field_info_entry->id()); + const ObjectSampleFieldInfo* const osfi = field_info_entry->literal(); + writer->write(osfi->_field_name_symbol->as_C_string()); + writer->write(osfi->_field_modifiers); + return 1; +} + +static traceid get_field_info_id(const Edge& edge) { + if (edge.is_root()) { + return 0; + } + + assert(!EdgeUtils::is_array_element(edge), "invariant"); + const Symbol* const field_name_symbol = EdgeUtils::field_name_symbol(edge); + if (field_name_symbol == NULL) { + return 0; + } + + if (field_infos == NULL) { + field_infos = new FieldTable(); + } + assert(field_infos != NULL, "invariant"); + + ObjectSampleFieldInfo* const osfi = new ObjectSampleFieldInfo(); + assert(osfi != NULL, "invariant"); + osfi->_field_name_symbol = field_name_symbol; + osfi->_field_modifiers = EdgeUtils::field_modifiers(edge); + return field_infos->store(osfi); +} + +typedef JfrArtifactWriterImplHost FieldWriterImpl; +typedef JfrArtifactWriterHost FieldWriter; + +static void write_field_infos(JfrCheckpointWriter& writer) { + if (field_infos != NULL) { + FieldWriter fw(&writer, NULL, false); + field_infos->iterate(fw); + } +} + +static const char* description(const ObjectSampleRootDescriptionInfo* osdi) { + assert(osdi != NULL, "invariant"); + + if (osdi->_data._description == NULL) { + return NULL; + } + + ObjectDescriptionBuilder description; + if (osdi->_data._system == OldObjectRoot::_threads) { + description.write_text("Thread Name: "); + } + description.write_text(osdi->_data._description); + return description.description(); +} + +int __write_root_description_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* di) { + assert(writer != NULL, "invariant"); + assert(di != NULL, "invariant"); + const ObjectSampleRootDescriptionInfo* const osdi = (const ObjectSampleRootDescriptionInfo*)di; + writer->write(osdi->_id); + writer->write(description(osdi)); + writer->write(osdi->_data._system); + writer->write(osdi->_data._type); + return 1; +} + +static traceid get_root_description_info_id(const Edge& edge, traceid id) { + assert(edge.is_root(), "invariant"); + if (EdgeUtils::is_leak_edge(edge)) { + return 0; + } + + if (root_infos == NULL) { + root_infos = new RootDescriptionInfo(); + } + assert(root_infos != NULL, "invariant"); + ObjectSampleRootDescriptionInfo* const oodi = new ObjectSampleRootDescriptionInfo(); + oodi->_id = id; + oodi->_data._root_edge = &edge; + return root_infos->store(oodi); +} + +typedef JfrArtifactWriterImplHost RootDescriptionWriterImpl; +typedef JfrArtifactWriterHost RootDescriptionWriter; + + +int _edge_reference_compare_(uintptr_t lhs, uintptr_t rhs) { + return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; +} + +int _root_desc_compare_(const ObjectSampleRootDescriptionInfo*const & lhs, const ObjectSampleRootDescriptionInfo* const& rhs) { + const uintptr_t lhs_ref = (uintptr_t)lhs->_data._root_edge->reference(); + const uintptr_t rhs_ref = (uintptr_t)rhs->_data._root_edge->reference(); + return _edge_reference_compare_(lhs_ref, rhs_ref); +} + +static int find_sorted(const RootCallbackInfo& callback_info, + const GrowableArray* arr, + int length, + bool& found) { + assert(arr != NULL, "invariant"); + assert(length >= 0, "invariant"); + assert(length <= arr->length(), "invariant"); + + found = false; + int min = 0; + int max = length; + while (max >= min) { + const int mid = (int)(((uint)max + min) / 2); + int diff = _edge_reference_compare_((uintptr_t)callback_info._high, + (uintptr_t)arr->at(mid)->_data._root_edge->reference()); + if (diff > 0) { + min = mid + 1; + } else if (diff < 0) { + max = mid - 1; + } else { + found = true; + return mid; + } + } + return min; +} + +class RootResolutionSet : public ResourceObj, public RootCallback { + private: + GrowableArray* _unresolved_roots; + + const uintptr_t high() const { + return (uintptr_t)_unresolved_roots->last()->_data._root_edge->reference(); + } + + const uintptr_t low() const { + return (uintptr_t)_unresolved_roots->first()->_data._root_edge->reference(); + } + + bool in_set_address_range(const RootCallbackInfo& callback_info) const { + assert(callback_info._low == NULL, "invariant"); + const uintptr_t addr = (uintptr_t)callback_info._high; + return low() <= addr && high() >= addr; + } + + int compare_to_range(const RootCallbackInfo& callback_info) const { + assert(callback_info._high != NULL, "invariant"); + assert(callback_info._low != NULL, "invariant"); + + for (int i = 0; i < _unresolved_roots->length(); ++i) { + const uintptr_t ref_addr = (uintptr_t)_unresolved_roots->at(i)->_data._root_edge->reference(); + if ((uintptr_t)callback_info._low <= ref_addr && (uintptr_t)callback_info._high >= ref_addr) { + return i; + } + } + return -1; + } + + int exact(const RootCallbackInfo& callback_info) const { + assert(callback_info._high != NULL, "invariant"); + assert(in_set_address_range(callback_info), "invariant"); + + bool found; + const int idx = find_sorted(callback_info, _unresolved_roots, _unresolved_roots->length(), found); + return found ? idx : -1; + } + + bool resolve_root(const RootCallbackInfo& callback_info, int idx) const { + assert(idx >= 0, "invariant"); + assert(idx < _unresolved_roots->length(), "invariant"); + + ObjectSampleRootDescriptionInfo* const desc = + const_cast(_unresolved_roots->at(idx)); + assert(desc != NULL, "invariant"); + assert((uintptr_t)callback_info._high == (uintptr_t)desc->_data._root_edge->reference(), "invariant"); + + desc->_data._system = callback_info._system; + desc->_data._type = callback_info._type; + + if (callback_info._system == OldObjectRoot::_threads) { + const JavaThread* jt = (const JavaThread*)callback_info._context; + assert(jt != NULL, "invariant"); + desc->_data._description = jt->name(); + } + + _unresolved_roots->remove_at(idx); + return _unresolved_roots->is_empty(); + } + + public: + RootResolutionSet(RootDescriptionInfo* info) : _unresolved_roots(NULL) { + assert(info != NULL, "invariant"); + // construct a sorted copy + const GrowableArray& info_storage = info->storage(); + const int length = info_storage.length(); + _unresolved_roots = new GrowableArray(length); + assert(_unresolved_roots != NULL, "invariant"); + + for (int i = 0; i < length; ++i) { + _unresolved_roots->insert_sorted<_root_desc_compare_>(info_storage.at(i)); + } + } + + bool process(const RootCallbackInfo& callback_info) { + if (NULL == callback_info._low) { + if (in_set_address_range(callback_info)) { + const int idx = exact(callback_info); + return idx == -1 ? false : resolve_root(callback_info, idx); + } + return false; + } + assert(callback_info._low != NULL, "invariant"); + const int idx = compare_to_range(callback_info); + return idx == -1 ? false : resolve_root(callback_info, idx); + } + + int entries() const { + return _unresolved_roots->length(); + } + + const void* at(int idx) const { + assert(idx >= 0, "invariant"); + assert(idx < _unresolved_roots->length(), "invariant"); + return _unresolved_roots->at(idx)->_data._root_edge->reference(); + } +}; + +static void write_root_descriptors(JfrCheckpointWriter& writer) { + if (root_infos != NULL) { + // resolve roots + RootResolutionSet rrs(root_infos); + RootResolver::resolve(rrs); + // write roots + RootDescriptionWriter rw(&writer, NULL, false); + root_infos->iterate(rw); + } +} + +static void add_old_object_sample_info(const Edge* current, traceid id) { + assert(current != NULL, "invariant"); + if (sample_infos == NULL) { + sample_infos = new SampleInfo(); + } + assert(sample_infos != NULL, "invariant"); + OldObjectSampleInfo* const oosi = new OldObjectSampleInfo(); + assert(oosi != NULL, "invariant"); + oosi->_id = id; + oosi->_data._object = current->pointee(); + oosi->_data._reference_id = current->is_root() ? (traceid)0 : id; + sample_infos->store(oosi); +} + +static void add_reference_info(const RoutableEdge* current, traceid id, traceid parent_id) { + assert(current != NULL, "invariant"); + if (ref_infos == NULL) { + ref_infos = new RefInfo(); + } + + assert(ref_infos != NULL, "invariant"); + ReferenceInfo* const ri = new ReferenceInfo(); + assert(ri != NULL, "invariant"); + + ri->_id = id; + ri->_data._array_info_id = !current->is_skip_edge() ? get_array_info_id(*current, id) : 0; + ri->_data._field_info_id = ri->_data._array_info_id == 0 && !current->is_skip_edge() ? + get_field_info_id(*current) : (traceid)0; + ri->_data._old_object_sample_id = parent_id; + ri->_data._skip = current->skip_length(); + ref_infos->store(ri); +} + +static traceid add_root_info(const Edge* root, traceid id) { + assert(root != NULL, "invariant"); + assert(root->is_root(), "invariant"); + return get_root_description_info_id(*root, id); +} + +void ObjectSampleWriter::write(const RoutableEdge* edge) { + assert(edge != NULL, "invariant"); + const traceid id = _store->get_id(edge); + add_old_object_sample_info(edge, id); + const RoutableEdge* parent = edge->logical_parent(); + if (parent != NULL) { + add_reference_info(edge, id, _store->get_id(parent)); + } else { + assert(edge->is_root(), "invariant"); + add_root_info(edge, id); + } +} + +ObjectSampleWriter::ObjectSampleWriter(JfrCheckpointWriter& writer, const EdgeStore* store) : + _writer(writer), + _store(store) { + assert(store != NULL, "invariant"); + assert(store->number_of_entries() > 0, "invariant"); + sample_infos = NULL; + ref_infos = NULL; + array_infos = NULL; + field_infos = NULL; + root_infos = NULL; +} + +ObjectSampleWriter::~ObjectSampleWriter() { + write_sample_infos(_writer); + write_reference_infos(_writer); + write_array_infos(_writer); + write_field_infos(_writer); + write_root_descriptors(_writer); +} + +void ObjectSampleWriter::write_chain(const RoutableEdge& edge) { + assert(EdgeUtils::is_leak_edge(edge), "invariant"); + if (edge.processed()) { + return; + } + EdgeUtils::collapse_chain(edge); + const RoutableEdge* current = &edge; + while (current != NULL) { + if (current->processed()) { + return; + } + write(current); + current->set_processed(); + current = current->logical_parent(); + } +} + +bool ObjectSampleWriter::operator()(const RoutableEdge& edge) { + if (EdgeUtils::is_leak_edge(edge)) { + write_chain(edge); + } + return true; +}