Mon, 12 Aug 2019 18:30:40 +0300
8223147: JFR Backport
8199712: Flight Recorder
8203346: JFR: Inconsistent signature of jfr_add_string_constant
8195817: JFR.stop should require name of recording
8195818: JFR.start should increase autogenerated name by one
8195819: Remove recording=x from jcmd JFR.check output
8203921: JFR thread sampling is missing fixes from JDK-8194552
8203929: Limit amount of data for JFR.dump
8203664: JFR start failure after AppCDS archive created with JFR StartFlightRecording
8003209: JFR events for network utilization
8207392: [PPC64] Implement JFR profiling
8202835: jfr/event/os/TestSystemProcess.java fails on missing events
Summary: Backport JFR from JDK11. Initial integration
Reviewed-by: neugens
apetushkov@9858 | 1 | /* |
apetushkov@9858 | 2 | * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. |
apetushkov@9858 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
apetushkov@9858 | 4 | * |
apetushkov@9858 | 5 | * This code is free software; you can redistribute it and/or modify it |
apetushkov@9858 | 6 | * under the terms of the GNU General Public License version 2 only, as |
apetushkov@9858 | 7 | * published by the Free Software Foundation. |
apetushkov@9858 | 8 | * |
apetushkov@9858 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
apetushkov@9858 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
apetushkov@9858 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
apetushkov@9858 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
apetushkov@9858 | 13 | * accompanied this code). |
apetushkov@9858 | 14 | * |
apetushkov@9858 | 15 | * You should have received a copy of the GNU General Public License version |
apetushkov@9858 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
apetushkov@9858 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
apetushkov@9858 | 18 | * |
apetushkov@9858 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
apetushkov@9858 | 20 | * or visit www.oracle.com if you need additional information or have any |
apetushkov@9858 | 21 | * questions. |
apetushkov@9858 | 22 | * |
apetushkov@9858 | 23 | */ |
apetushkov@9858 | 24 | |
apetushkov@9858 | 25 | #include "precompiled.hpp" |
apetushkov@9858 | 26 | #include "jfrfiles/jfrTypes.hpp" |
apetushkov@9858 | 27 | #include "jfr/leakprofiler/chains/edge.hpp" |
apetushkov@9858 | 28 | #include "jfr/leakprofiler/chains/edgeStore.hpp" |
apetushkov@9858 | 29 | #include "jfr/leakprofiler/chains/edgeUtils.hpp" |
apetushkov@9858 | 30 | #include "jfr/leakprofiler/checkpoint/objectSampleDescription.hpp" |
apetushkov@9858 | 31 | #include "jfr/leakprofiler/checkpoint/objectSampleWriter.hpp" |
apetushkov@9858 | 32 | #include "jfr/leakprofiler/checkpoint/rootResolver.hpp" |
apetushkov@9858 | 33 | #include "jfr/leakprofiler/sampling/objectSampler.hpp" |
apetushkov@9858 | 34 | #include "jfr/leakprofiler/utilities/rootType.hpp" |
apetushkov@9858 | 35 | #include "jfr/leakprofiler/utilities/unifiedOop.hpp" |
apetushkov@9858 | 36 | #include "jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp" |
apetushkov@9858 | 37 | #include "jfr/recorder/checkpoint/types/jfrTypeSetWriter.hpp" |
apetushkov@9858 | 38 | #include "oops/oop.inline.hpp" |
apetushkov@9858 | 39 | #include "oops/symbol.hpp" |
apetushkov@9858 | 40 | #include "utilities/growableArray.hpp" |
apetushkov@9858 | 41 | |
apetushkov@9858 | 42 | template <typename Data> |
apetushkov@9858 | 43 | class ObjectSampleAuxInfo : public ResourceObj { |
apetushkov@9858 | 44 | public: |
apetushkov@9858 | 45 | Data _data; |
apetushkov@9858 | 46 | traceid _id; |
apetushkov@9858 | 47 | ObjectSampleAuxInfo() : _data(), _id(0) {} |
apetushkov@9858 | 48 | }; |
apetushkov@9858 | 49 | |
apetushkov@9858 | 50 | class ObjectSampleArrayData { |
apetushkov@9858 | 51 | public: |
apetushkov@9858 | 52 | int _array_size; |
apetushkov@9858 | 53 | int _array_index; |
apetushkov@9858 | 54 | ObjectSampleArrayData() : _array_size(0), _array_index(0) {} |
apetushkov@9858 | 55 | }; |
apetushkov@9858 | 56 | |
apetushkov@9858 | 57 | class ObjectSampleFieldInfo : public ResourceObj { |
apetushkov@9858 | 58 | public: |
apetushkov@9858 | 59 | const Symbol* _field_name_symbol; |
apetushkov@9858 | 60 | jshort _field_modifiers; |
apetushkov@9858 | 61 | ObjectSampleFieldInfo() : _field_name_symbol(NULL), _field_modifiers(0) {} |
apetushkov@9858 | 62 | }; |
apetushkov@9858 | 63 | |
apetushkov@9858 | 64 | class ObjectSampleRootDescriptionData { |
apetushkov@9858 | 65 | public: |
apetushkov@9858 | 66 | const Edge* _root_edge; |
apetushkov@9858 | 67 | const char* _description; |
apetushkov@9858 | 68 | OldObjectRoot::System _system; |
apetushkov@9858 | 69 | OldObjectRoot::Type _type; |
apetushkov@9858 | 70 | ObjectSampleRootDescriptionData() : _root_edge(NULL), |
apetushkov@9858 | 71 | _description(NULL), |
apetushkov@9858 | 72 | _system(OldObjectRoot::_system_undetermined), |
apetushkov@9858 | 73 | _type(OldObjectRoot::_type_undetermined) {} |
apetushkov@9858 | 74 | }; |
apetushkov@9858 | 75 | |
apetushkov@9858 | 76 | class OldObjectSampleData { |
apetushkov@9858 | 77 | public: |
apetushkov@9858 | 78 | oop _object; |
apetushkov@9858 | 79 | traceid _reference_id; |
apetushkov@9858 | 80 | }; |
apetushkov@9858 | 81 | |
apetushkov@9858 | 82 | class ReferenceData { |
apetushkov@9858 | 83 | public: |
apetushkov@9858 | 84 | traceid _field_info_id; |
apetushkov@9858 | 85 | traceid _array_info_id; |
apetushkov@9858 | 86 | traceid _old_object_sample_id; |
apetushkov@9858 | 87 | size_t _skip; |
apetushkov@9858 | 88 | }; |
apetushkov@9858 | 89 | |
apetushkov@9858 | 90 | static int initial_storage_size = 16; |
apetushkov@9858 | 91 | |
apetushkov@9858 | 92 | template <typename Data> |
apetushkov@9858 | 93 | class SampleSet : public ResourceObj { |
apetushkov@9858 | 94 | private: |
apetushkov@9858 | 95 | GrowableArray<Data>* _storage; |
apetushkov@9858 | 96 | public: |
apetushkov@9858 | 97 | SampleSet() : _storage(NULL) {} |
apetushkov@9858 | 98 | |
apetushkov@9858 | 99 | traceid store(Data data) { |
apetushkov@9858 | 100 | assert(data != NULL, "invariant"); |
apetushkov@9858 | 101 | if (_storage == NULL) { |
apetushkov@9858 | 102 | _storage = new GrowableArray<Data>(initial_storage_size); |
apetushkov@9858 | 103 | } |
apetushkov@9858 | 104 | assert(_storage != NULL, "invariant"); |
apetushkov@9858 | 105 | assert(_storage->find(data) == -1, "invariant"); |
apetushkov@9858 | 106 | _storage->append(data); |
apetushkov@9858 | 107 | return data->_id; |
apetushkov@9858 | 108 | } |
apetushkov@9858 | 109 | |
apetushkov@9858 | 110 | size_t size() const { |
apetushkov@9858 | 111 | return _storage != NULL ? (size_t)_storage->length() : 0; |
apetushkov@9858 | 112 | } |
apetushkov@9858 | 113 | |
apetushkov@9858 | 114 | template <typename Functor> |
apetushkov@9858 | 115 | void iterate(Functor& functor) { |
apetushkov@9858 | 116 | if (_storage != NULL) { |
apetushkov@9858 | 117 | for (int i = 0; i < _storage->length(); ++i) { |
apetushkov@9858 | 118 | functor(_storage->at(i)); |
apetushkov@9858 | 119 | } |
apetushkov@9858 | 120 | } |
apetushkov@9858 | 121 | } |
apetushkov@9858 | 122 | |
apetushkov@9858 | 123 | const GrowableArray<Data>& storage() const { |
apetushkov@9858 | 124 | return *_storage; |
apetushkov@9858 | 125 | } |
apetushkov@9858 | 126 | }; |
apetushkov@9858 | 127 | |
apetushkov@9858 | 128 | typedef ObjectSampleAuxInfo<ObjectSampleArrayData> ObjectSampleArrayInfo; |
apetushkov@9858 | 129 | typedef ObjectSampleAuxInfo<ObjectSampleRootDescriptionData> ObjectSampleRootDescriptionInfo; |
apetushkov@9858 | 130 | typedef ObjectSampleAuxInfo<OldObjectSampleData> OldObjectSampleInfo; |
apetushkov@9858 | 131 | typedef ObjectSampleAuxInfo<ReferenceData> ReferenceInfo; |
apetushkov@9858 | 132 | |
apetushkov@9858 | 133 | class FieldTable : public ResourceObj { |
apetushkov@9858 | 134 | template <typename, |
apetushkov@9858 | 135 | typename, |
apetushkov@9858 | 136 | template<typename, typename> class, |
apetushkov@9858 | 137 | typename, |
apetushkov@9858 | 138 | size_t> |
apetushkov@9858 | 139 | friend class HashTableHost; |
apetushkov@9858 | 140 | typedef HashTableHost<const ObjectSampleFieldInfo*, traceid, Entry, FieldTable, 109> FieldInfoTable; |
apetushkov@9858 | 141 | public: |
apetushkov@9858 | 142 | typedef FieldInfoTable::HashEntry FieldInfoEntry; |
apetushkov@9858 | 143 | |
apetushkov@9858 | 144 | private: |
apetushkov@9858 | 145 | static traceid _field_id_counter; |
apetushkov@9858 | 146 | FieldInfoTable* _table; |
apetushkov@9858 | 147 | |
apetushkov@9858 | 148 | void assign_id(FieldInfoEntry* entry) { |
apetushkov@9858 | 149 | assert(entry != NULL, "invariant"); |
apetushkov@9858 | 150 | entry->set_id(++_field_id_counter); |
apetushkov@9858 | 151 | } |
apetushkov@9858 | 152 | |
apetushkov@9858 | 153 | bool equals(const ObjectSampleFieldInfo* query, uintptr_t hash, const FieldInfoEntry* entry) { |
apetushkov@9858 | 154 | assert(hash == entry->hash(), "invariant"); |
apetushkov@9858 | 155 | assert(query != NULL, "invariant"); |
apetushkov@9858 | 156 | const ObjectSampleFieldInfo* stored = entry->literal(); |
apetushkov@9858 | 157 | assert(stored != NULL, "invariant"); |
apetushkov@9858 | 158 | assert(((Symbol*)stored->_field_name_symbol)->identity_hash() == ((Symbol*)query->_field_name_symbol)->identity_hash(), "invariant"); |
apetushkov@9858 | 159 | return stored->_field_modifiers == query->_field_modifiers; |
apetushkov@9858 | 160 | } |
apetushkov@9858 | 161 | |
apetushkov@9858 | 162 | public: |
apetushkov@9858 | 163 | FieldTable() : _table(new FieldInfoTable(this)) {} |
apetushkov@9858 | 164 | ~FieldTable() { |
apetushkov@9858 | 165 | assert(_table != NULL, "invariant"); |
apetushkov@9858 | 166 | delete _table; |
apetushkov@9858 | 167 | } |
apetushkov@9858 | 168 | |
apetushkov@9858 | 169 | traceid store(const ObjectSampleFieldInfo* field_info) { |
apetushkov@9858 | 170 | assert(field_info != NULL, "invariant"); |
apetushkov@9858 | 171 | const FieldInfoEntry& entry =_table->lookup_put(field_info, |
apetushkov@9858 | 172 | ((Symbol*)field_info->_field_name_symbol)->identity_hash()); |
apetushkov@9858 | 173 | return entry.id(); |
apetushkov@9858 | 174 | } |
apetushkov@9858 | 175 | |
apetushkov@9858 | 176 | size_t size() const { |
apetushkov@9858 | 177 | return _table->cardinality(); |
apetushkov@9858 | 178 | } |
apetushkov@9858 | 179 | |
apetushkov@9858 | 180 | template <typename T> |
apetushkov@9858 | 181 | void iterate(T& functor) const { |
apetushkov@9858 | 182 | _table->iterate_entry<T>(functor); |
apetushkov@9858 | 183 | } |
apetushkov@9858 | 184 | }; |
apetushkov@9858 | 185 | |
apetushkov@9858 | 186 | traceid FieldTable::_field_id_counter = 0; |
apetushkov@9858 | 187 | |
apetushkov@9858 | 188 | typedef SampleSet<const OldObjectSampleInfo*> SampleInfo; |
apetushkov@9858 | 189 | typedef SampleSet<const ReferenceInfo*> RefInfo; |
apetushkov@9858 | 190 | typedef SampleSet<const ObjectSampleArrayInfo*> ArrayInfo; |
apetushkov@9858 | 191 | typedef SampleSet<const ObjectSampleRootDescriptionInfo*> RootDescriptionInfo; |
apetushkov@9858 | 192 | |
apetushkov@9858 | 193 | static SampleInfo* sample_infos = NULL; |
apetushkov@9858 | 194 | static RefInfo* ref_infos = NULL; |
apetushkov@9858 | 195 | static ArrayInfo* array_infos = NULL; |
apetushkov@9858 | 196 | static FieldTable* field_infos = NULL; |
apetushkov@9858 | 197 | static RootDescriptionInfo* root_infos = NULL; |
apetushkov@9858 | 198 | |
apetushkov@9858 | 199 | int __write_sample_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* si) { |
apetushkov@9858 | 200 | assert(writer != NULL, "invariant"); |
apetushkov@9858 | 201 | assert(si != NULL, "invariant"); |
apetushkov@9858 | 202 | const OldObjectSampleInfo* const oosi = (const OldObjectSampleInfo*)si; |
apetushkov@9858 | 203 | oop object = oosi->_data._object; |
apetushkov@9858 | 204 | assert(object != NULL, "invariant"); |
apetushkov@9858 | 205 | writer->write(oosi->_id); |
apetushkov@9858 | 206 | writer->write((u8)(const HeapWord*)object); |
apetushkov@9858 | 207 | writer->write(const_cast<const Klass*>(object->klass())); |
apetushkov@9858 | 208 | ObjectSampleDescription od(object); |
apetushkov@9858 | 209 | writer->write(od.description()); |
apetushkov@9858 | 210 | writer->write(oosi->_data._reference_id); |
apetushkov@9858 | 211 | return 1; |
apetushkov@9858 | 212 | } |
apetushkov@9858 | 213 | |
apetushkov@9858 | 214 | typedef JfrArtifactWriterImplHost<const OldObjectSampleInfo*, __write_sample_info__> SampleWriterImpl; |
apetushkov@9858 | 215 | typedef JfrArtifactWriterHost<SampleWriterImpl, TYPE_OLDOBJECT> SampleWriter; |
apetushkov@9858 | 216 | |
apetushkov@9858 | 217 | static void write_sample_infos(JfrCheckpointWriter& writer) { |
apetushkov@9858 | 218 | if (sample_infos != NULL) { |
apetushkov@9858 | 219 | SampleWriter sw(&writer, NULL, false); |
apetushkov@9858 | 220 | sample_infos->iterate(sw); |
apetushkov@9858 | 221 | } |
apetushkov@9858 | 222 | } |
apetushkov@9858 | 223 | |
apetushkov@9858 | 224 | int __write_reference_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ri) { |
apetushkov@9858 | 225 | assert(writer != NULL, "invariant"); |
apetushkov@9858 | 226 | assert(ri != NULL, "invariant"); |
apetushkov@9858 | 227 | const ReferenceInfo* const ref_info = (const ReferenceInfo*)ri; |
apetushkov@9858 | 228 | writer->write(ref_info->_id); |
apetushkov@9858 | 229 | writer->write(ref_info->_data._array_info_id); |
apetushkov@9858 | 230 | writer->write(ref_info->_data._field_info_id); |
apetushkov@9858 | 231 | writer->write(ref_info->_data._old_object_sample_id); |
apetushkov@9858 | 232 | writer->write<s4>((s4)ref_info->_data._skip); |
apetushkov@9858 | 233 | return 1; |
apetushkov@9858 | 234 | } |
apetushkov@9858 | 235 | |
apetushkov@9858 | 236 | typedef JfrArtifactWriterImplHost<const ReferenceInfo*, __write_reference_info__> ReferenceWriterImpl; |
apetushkov@9858 | 237 | typedef JfrArtifactWriterHost<ReferenceWriterImpl, TYPE_REFERENCE> ReferenceWriter; |
apetushkov@9858 | 238 | |
apetushkov@9858 | 239 | static void write_reference_infos(JfrCheckpointWriter& writer) { |
apetushkov@9858 | 240 | if (ref_infos != NULL) { |
apetushkov@9858 | 241 | ReferenceWriter rw(&writer, NULL, false); |
apetushkov@9858 | 242 | ref_infos->iterate(rw); |
apetushkov@9858 | 243 | } |
apetushkov@9858 | 244 | } |
apetushkov@9858 | 245 | |
apetushkov@9858 | 246 | int __write_array_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* ai) { |
apetushkov@9858 | 247 | assert(writer != NULL, "invariant"); |
apetushkov@9858 | 248 | assert(ai != NULL, "invariant"); |
apetushkov@9858 | 249 | const ObjectSampleArrayInfo* const osai = (const ObjectSampleArrayInfo*)ai; |
apetushkov@9858 | 250 | writer->write(osai->_id); |
apetushkov@9858 | 251 | writer->write(osai->_data._array_size); |
apetushkov@9858 | 252 | writer->write(osai->_data._array_index); |
apetushkov@9858 | 253 | return 1; |
apetushkov@9858 | 254 | } |
apetushkov@9858 | 255 | |
apetushkov@9858 | 256 | static traceid get_array_info_id(const Edge& edge, traceid id) { |
apetushkov@9858 | 257 | if (edge.is_root() || !EdgeUtils::is_array_element(edge)) { |
apetushkov@9858 | 258 | return 0; |
apetushkov@9858 | 259 | } |
apetushkov@9858 | 260 | if (array_infos == NULL) { |
apetushkov@9858 | 261 | array_infos = new ArrayInfo(); |
apetushkov@9858 | 262 | } |
apetushkov@9858 | 263 | assert(array_infos != NULL, "invariant"); |
apetushkov@9858 | 264 | |
apetushkov@9858 | 265 | ObjectSampleArrayInfo* const osai = new ObjectSampleArrayInfo(); |
apetushkov@9858 | 266 | assert(osai != NULL, "invariant"); |
apetushkov@9858 | 267 | osai->_id = id; |
apetushkov@9858 | 268 | osai->_data._array_size = EdgeUtils::array_size(edge); |
apetushkov@9858 | 269 | osai->_data._array_index = EdgeUtils::array_index(edge); |
apetushkov@9858 | 270 | return array_infos->store(osai); |
apetushkov@9858 | 271 | } |
apetushkov@9858 | 272 | |
apetushkov@9858 | 273 | typedef JfrArtifactWriterImplHost<const ObjectSampleArrayInfo*, __write_array_info__> ArrayWriterImpl; |
apetushkov@9858 | 274 | typedef JfrArtifactWriterHost<ArrayWriterImpl, TYPE_OLDOBJECTARRAY> ArrayWriter; |
apetushkov@9858 | 275 | |
apetushkov@9858 | 276 | static void write_array_infos(JfrCheckpointWriter& writer) { |
apetushkov@9858 | 277 | if (array_infos != NULL) { |
apetushkov@9858 | 278 | ArrayWriter aw(&writer, NULL, false); |
apetushkov@9858 | 279 | array_infos->iterate(aw); |
apetushkov@9858 | 280 | } |
apetushkov@9858 | 281 | } |
apetushkov@9858 | 282 | |
apetushkov@9858 | 283 | int __write_field_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* fi) { |
apetushkov@9858 | 284 | assert(writer != NULL, "invariant"); |
apetushkov@9858 | 285 | assert(fi != NULL, "invariant"); |
apetushkov@9858 | 286 | const FieldTable::FieldInfoEntry* field_info_entry = (const FieldTable::FieldInfoEntry*)fi; |
apetushkov@9858 | 287 | writer->write(field_info_entry->id()); |
apetushkov@9858 | 288 | const ObjectSampleFieldInfo* const osfi = field_info_entry->literal(); |
apetushkov@9858 | 289 | writer->write(osfi->_field_name_symbol->as_C_string()); |
apetushkov@9858 | 290 | writer->write(osfi->_field_modifiers); |
apetushkov@9858 | 291 | return 1; |
apetushkov@9858 | 292 | } |
apetushkov@9858 | 293 | |
apetushkov@9858 | 294 | static traceid get_field_info_id(const Edge& edge) { |
apetushkov@9858 | 295 | if (edge.is_root()) { |
apetushkov@9858 | 296 | return 0; |
apetushkov@9858 | 297 | } |
apetushkov@9858 | 298 | |
apetushkov@9858 | 299 | assert(!EdgeUtils::is_array_element(edge), "invariant"); |
apetushkov@9858 | 300 | const Symbol* const field_name_symbol = EdgeUtils::field_name_symbol(edge); |
apetushkov@9858 | 301 | if (field_name_symbol == NULL) { |
apetushkov@9858 | 302 | return 0; |
apetushkov@9858 | 303 | } |
apetushkov@9858 | 304 | |
apetushkov@9858 | 305 | if (field_infos == NULL) { |
apetushkov@9858 | 306 | field_infos = new FieldTable(); |
apetushkov@9858 | 307 | } |
apetushkov@9858 | 308 | assert(field_infos != NULL, "invariant"); |
apetushkov@9858 | 309 | |
apetushkov@9858 | 310 | ObjectSampleFieldInfo* const osfi = new ObjectSampleFieldInfo(); |
apetushkov@9858 | 311 | assert(osfi != NULL, "invariant"); |
apetushkov@9858 | 312 | osfi->_field_name_symbol = field_name_symbol; |
apetushkov@9858 | 313 | osfi->_field_modifiers = EdgeUtils::field_modifiers(edge); |
apetushkov@9858 | 314 | return field_infos->store(osfi); |
apetushkov@9858 | 315 | } |
apetushkov@9858 | 316 | |
apetushkov@9858 | 317 | typedef JfrArtifactWriterImplHost<const FieldTable::FieldInfoEntry*, __write_field_info__> FieldWriterImpl; |
apetushkov@9858 | 318 | typedef JfrArtifactWriterHost<FieldWriterImpl, TYPE_OLDOBJECTFIELD> FieldWriter; |
apetushkov@9858 | 319 | |
apetushkov@9858 | 320 | static void write_field_infos(JfrCheckpointWriter& writer) { |
apetushkov@9858 | 321 | if (field_infos != NULL) { |
apetushkov@9858 | 322 | FieldWriter fw(&writer, NULL, false); |
apetushkov@9858 | 323 | field_infos->iterate(fw); |
apetushkov@9858 | 324 | } |
apetushkov@9858 | 325 | } |
apetushkov@9858 | 326 | |
apetushkov@9858 | 327 | static const char* description(const ObjectSampleRootDescriptionInfo* osdi) { |
apetushkov@9858 | 328 | assert(osdi != NULL, "invariant"); |
apetushkov@9858 | 329 | |
apetushkov@9858 | 330 | if (osdi->_data._description == NULL) { |
apetushkov@9858 | 331 | return NULL; |
apetushkov@9858 | 332 | } |
apetushkov@9858 | 333 | |
apetushkov@9858 | 334 | ObjectDescriptionBuilder description; |
apetushkov@9858 | 335 | if (osdi->_data._system == OldObjectRoot::_threads) { |
apetushkov@9858 | 336 | description.write_text("Thread Name: "); |
apetushkov@9858 | 337 | } |
apetushkov@9858 | 338 | description.write_text(osdi->_data._description); |
apetushkov@9858 | 339 | return description.description(); |
apetushkov@9858 | 340 | } |
apetushkov@9858 | 341 | |
apetushkov@9858 | 342 | int __write_root_description_info__(JfrCheckpointWriter* writer, JfrArtifactSet* unused, const void* di) { |
apetushkov@9858 | 343 | assert(writer != NULL, "invariant"); |
apetushkov@9858 | 344 | assert(di != NULL, "invariant"); |
apetushkov@9858 | 345 | const ObjectSampleRootDescriptionInfo* const osdi = (const ObjectSampleRootDescriptionInfo*)di; |
apetushkov@9858 | 346 | writer->write(osdi->_id); |
apetushkov@9858 | 347 | writer->write(description(osdi)); |
apetushkov@9858 | 348 | writer->write<u8>(osdi->_data._system); |
apetushkov@9858 | 349 | writer->write<u8>(osdi->_data._type); |
apetushkov@9858 | 350 | return 1; |
apetushkov@9858 | 351 | } |
apetushkov@9858 | 352 | |
apetushkov@9858 | 353 | static traceid get_root_description_info_id(const Edge& edge, traceid id) { |
apetushkov@9858 | 354 | assert(edge.is_root(), "invariant"); |
apetushkov@9858 | 355 | if (EdgeUtils::is_leak_edge(edge)) { |
apetushkov@9858 | 356 | return 0; |
apetushkov@9858 | 357 | } |
apetushkov@9858 | 358 | |
apetushkov@9858 | 359 | if (root_infos == NULL) { |
apetushkov@9858 | 360 | root_infos = new RootDescriptionInfo(); |
apetushkov@9858 | 361 | } |
apetushkov@9858 | 362 | assert(root_infos != NULL, "invariant"); |
apetushkov@9858 | 363 | ObjectSampleRootDescriptionInfo* const oodi = new ObjectSampleRootDescriptionInfo(); |
apetushkov@9858 | 364 | oodi->_id = id; |
apetushkov@9858 | 365 | oodi->_data._root_edge = &edge; |
apetushkov@9858 | 366 | return root_infos->store(oodi); |
apetushkov@9858 | 367 | } |
apetushkov@9858 | 368 | |
apetushkov@9858 | 369 | typedef JfrArtifactWriterImplHost<const ObjectSampleRootDescriptionInfo*, __write_root_description_info__> RootDescriptionWriterImpl; |
apetushkov@9858 | 370 | typedef JfrArtifactWriterHost<RootDescriptionWriterImpl, TYPE_OLDOBJECTGCROOT> RootDescriptionWriter; |
apetushkov@9858 | 371 | |
apetushkov@9858 | 372 | |
apetushkov@9858 | 373 | int _edge_reference_compare_(uintptr_t lhs, uintptr_t rhs) { |
apetushkov@9858 | 374 | return lhs > rhs ? 1 : (lhs < rhs) ? -1 : 0; |
apetushkov@9858 | 375 | } |
apetushkov@9858 | 376 | |
apetushkov@9858 | 377 | int _root_desc_compare_(const ObjectSampleRootDescriptionInfo*const & lhs, const ObjectSampleRootDescriptionInfo* const& rhs) { |
apetushkov@9858 | 378 | const uintptr_t lhs_ref = (uintptr_t)lhs->_data._root_edge->reference(); |
apetushkov@9858 | 379 | const uintptr_t rhs_ref = (uintptr_t)rhs->_data._root_edge->reference(); |
apetushkov@9858 | 380 | return _edge_reference_compare_(lhs_ref, rhs_ref); |
apetushkov@9858 | 381 | } |
apetushkov@9858 | 382 | |
apetushkov@9858 | 383 | static int find_sorted(const RootCallbackInfo& callback_info, |
apetushkov@9858 | 384 | const GrowableArray<const ObjectSampleRootDescriptionInfo*>* arr, |
apetushkov@9858 | 385 | int length, |
apetushkov@9858 | 386 | bool& found) { |
apetushkov@9858 | 387 | assert(arr != NULL, "invariant"); |
apetushkov@9858 | 388 | assert(length >= 0, "invariant"); |
apetushkov@9858 | 389 | assert(length <= arr->length(), "invariant"); |
apetushkov@9858 | 390 | |
apetushkov@9858 | 391 | found = false; |
apetushkov@9858 | 392 | int min = 0; |
apetushkov@9858 | 393 | int max = length; |
apetushkov@9858 | 394 | while (max >= min) { |
apetushkov@9858 | 395 | const int mid = (int)(((uint)max + min) / 2); |
apetushkov@9858 | 396 | int diff = _edge_reference_compare_((uintptr_t)callback_info._high, |
apetushkov@9858 | 397 | (uintptr_t)arr->at(mid)->_data._root_edge->reference()); |
apetushkov@9858 | 398 | if (diff > 0) { |
apetushkov@9858 | 399 | min = mid + 1; |
apetushkov@9858 | 400 | } else if (diff < 0) { |
apetushkov@9858 | 401 | max = mid - 1; |
apetushkov@9858 | 402 | } else { |
apetushkov@9858 | 403 | found = true; |
apetushkov@9858 | 404 | return mid; |
apetushkov@9858 | 405 | } |
apetushkov@9858 | 406 | } |
apetushkov@9858 | 407 | return min; |
apetushkov@9858 | 408 | } |
apetushkov@9858 | 409 | |
apetushkov@9858 | 410 | class RootResolutionSet : public ResourceObj, public RootCallback { |
apetushkov@9858 | 411 | private: |
apetushkov@9858 | 412 | GrowableArray<const ObjectSampleRootDescriptionInfo*>* _unresolved_roots; |
apetushkov@9858 | 413 | |
apetushkov@9858 | 414 | const uintptr_t high() const { |
apetushkov@9858 | 415 | return (uintptr_t)_unresolved_roots->top()->_data._root_edge->reference(); |
apetushkov@9858 | 416 | } |
apetushkov@9858 | 417 | |
apetushkov@9858 | 418 | const uintptr_t low() const { |
apetushkov@9858 | 419 | return (uintptr_t)_unresolved_roots->first()->_data._root_edge->reference(); |
apetushkov@9858 | 420 | } |
apetushkov@9858 | 421 | |
apetushkov@9858 | 422 | bool in_set_address_range(const RootCallbackInfo& callback_info) const { |
apetushkov@9858 | 423 | assert(callback_info._low == NULL, "invariant"); |
apetushkov@9858 | 424 | const uintptr_t addr = (uintptr_t)callback_info._high; |
apetushkov@9858 | 425 | return low() <= addr && high() >= addr; |
apetushkov@9858 | 426 | } |
apetushkov@9858 | 427 | |
apetushkov@9858 | 428 | int compare_to_range(const RootCallbackInfo& callback_info) const { |
apetushkov@9858 | 429 | assert(callback_info._high != NULL, "invariant"); |
apetushkov@9858 | 430 | assert(callback_info._low != NULL, "invariant"); |
apetushkov@9858 | 431 | |
apetushkov@9858 | 432 | for (int i = 0; i < _unresolved_roots->length(); ++i) { |
apetushkov@9858 | 433 | const uintptr_t ref_addr = (uintptr_t)_unresolved_roots->at(i)->_data._root_edge->reference(); |
apetushkov@9858 | 434 | if ((uintptr_t)callback_info._low <= ref_addr && (uintptr_t)callback_info._high >= ref_addr) { |
apetushkov@9858 | 435 | return i; |
apetushkov@9858 | 436 | } |
apetushkov@9858 | 437 | } |
apetushkov@9858 | 438 | return -1; |
apetushkov@9858 | 439 | } |
apetushkov@9858 | 440 | |
apetushkov@9858 | 441 | int exact(const RootCallbackInfo& callback_info) const { |
apetushkov@9858 | 442 | assert(callback_info._high != NULL, "invariant"); |
apetushkov@9858 | 443 | assert(in_set_address_range(callback_info), "invariant"); |
apetushkov@9858 | 444 | |
apetushkov@9858 | 445 | bool found; |
apetushkov@9858 | 446 | const int idx = find_sorted(callback_info, _unresolved_roots, _unresolved_roots->length(), found); |
apetushkov@9858 | 447 | return found ? idx : -1; |
apetushkov@9858 | 448 | } |
apetushkov@9858 | 449 | |
apetushkov@9858 | 450 | bool resolve_root(const RootCallbackInfo& callback_info, int idx) const { |
apetushkov@9858 | 451 | assert(idx >= 0, "invariant"); |
apetushkov@9858 | 452 | assert(idx < _unresolved_roots->length(), "invariant"); |
apetushkov@9858 | 453 | |
apetushkov@9858 | 454 | ObjectSampleRootDescriptionInfo* const desc = |
apetushkov@9858 | 455 | const_cast<ObjectSampleRootDescriptionInfo*>(_unresolved_roots->at(idx)); |
apetushkov@9858 | 456 | assert(desc != NULL, "invariant"); |
apetushkov@9858 | 457 | assert((uintptr_t)callback_info._high == (uintptr_t)desc->_data._root_edge->reference(), "invariant"); |
apetushkov@9858 | 458 | |
apetushkov@9858 | 459 | desc->_data._system = callback_info._system; |
apetushkov@9858 | 460 | desc->_data._type = callback_info._type; |
apetushkov@9858 | 461 | |
apetushkov@9858 | 462 | if (callback_info._system == OldObjectRoot::_threads) { |
apetushkov@9858 | 463 | const JavaThread* jt = (const JavaThread*)callback_info._context; |
apetushkov@9858 | 464 | assert(jt != NULL, "invariant"); |
apetushkov@9858 | 465 | desc->_data._description = jt->name(); |
apetushkov@9858 | 466 | } |
apetushkov@9858 | 467 | |
apetushkov@9858 | 468 | _unresolved_roots->remove_at(idx); |
apetushkov@9858 | 469 | return _unresolved_roots->is_empty(); |
apetushkov@9858 | 470 | } |
apetushkov@9858 | 471 | |
apetushkov@9858 | 472 | public: |
apetushkov@9858 | 473 | RootResolutionSet(RootDescriptionInfo* info) : _unresolved_roots(NULL) { |
apetushkov@9858 | 474 | assert(info != NULL, "invariant"); |
apetushkov@9858 | 475 | // construct a sorted copy |
apetushkov@9858 | 476 | const GrowableArray<const ObjectSampleRootDescriptionInfo*>& info_storage = info->storage(); |
apetushkov@9858 | 477 | const int length = info_storage.length(); |
apetushkov@9858 | 478 | _unresolved_roots = new GrowableArray<const ObjectSampleRootDescriptionInfo*>(length); |
apetushkov@9858 | 479 | assert(_unresolved_roots != NULL, "invariant"); |
apetushkov@9858 | 480 | |
apetushkov@9858 | 481 | for (int i = 0; i < length; ++i) { |
apetushkov@9858 | 482 | _unresolved_roots->insert_sorted<_root_desc_compare_>(info_storage.at(i)); |
apetushkov@9858 | 483 | } |
apetushkov@9858 | 484 | } |
apetushkov@9858 | 485 | |
apetushkov@9858 | 486 | bool process(const RootCallbackInfo& callback_info) { |
apetushkov@9858 | 487 | if (NULL == callback_info._low) { |
apetushkov@9858 | 488 | if (in_set_address_range(callback_info)) { |
apetushkov@9858 | 489 | const int idx = exact(callback_info); |
apetushkov@9858 | 490 | return idx == -1 ? false : resolve_root(callback_info, idx); |
apetushkov@9858 | 491 | } |
apetushkov@9858 | 492 | return false; |
apetushkov@9858 | 493 | } |
apetushkov@9858 | 494 | assert(callback_info._low != NULL, "invariant"); |
apetushkov@9858 | 495 | const int idx = compare_to_range(callback_info); |
apetushkov@9858 | 496 | return idx == -1 ? false : resolve_root(callback_info, idx); |
apetushkov@9858 | 497 | } |
apetushkov@9858 | 498 | |
apetushkov@9858 | 499 | int entries() const { |
apetushkov@9858 | 500 | return _unresolved_roots->length(); |
apetushkov@9858 | 501 | } |
apetushkov@9858 | 502 | |
apetushkov@9858 | 503 | const void* at(int idx) const { |
apetushkov@9858 | 504 | assert(idx >= 0, "invariant"); |
apetushkov@9858 | 505 | assert(idx < _unresolved_roots->length(), "invariant"); |
apetushkov@9858 | 506 | return _unresolved_roots->at(idx)->_data._root_edge->reference(); |
apetushkov@9858 | 507 | } |
apetushkov@9858 | 508 | }; |
apetushkov@9858 | 509 | |
apetushkov@9858 | 510 | static void write_root_descriptors(JfrCheckpointWriter& writer) { |
apetushkov@9858 | 511 | if (root_infos != NULL) { |
apetushkov@9858 | 512 | // resolve roots |
apetushkov@9858 | 513 | RootResolutionSet rrs(root_infos); |
apetushkov@9858 | 514 | RootResolver::resolve(rrs); |
apetushkov@9858 | 515 | // write roots |
apetushkov@9858 | 516 | RootDescriptionWriter rw(&writer, NULL, false); |
apetushkov@9858 | 517 | root_infos->iterate(rw); |
apetushkov@9858 | 518 | } |
apetushkov@9858 | 519 | } |
apetushkov@9858 | 520 | |
apetushkov@9858 | 521 | static void add_old_object_sample_info(const Edge* current, traceid id) { |
apetushkov@9858 | 522 | assert(current != NULL, "invariant"); |
apetushkov@9858 | 523 | if (sample_infos == NULL) { |
apetushkov@9858 | 524 | sample_infos = new SampleInfo(); |
apetushkov@9858 | 525 | } |
apetushkov@9858 | 526 | assert(sample_infos != NULL, "invariant"); |
apetushkov@9858 | 527 | OldObjectSampleInfo* const oosi = new OldObjectSampleInfo(); |
apetushkov@9858 | 528 | assert(oosi != NULL, "invariant"); |
apetushkov@9858 | 529 | oosi->_id = id; |
apetushkov@9858 | 530 | oosi->_data._object = current->pointee(); |
apetushkov@9858 | 531 | oosi->_data._reference_id = current->is_root() ? (traceid)0 : id; |
apetushkov@9858 | 532 | sample_infos->store(oosi); |
apetushkov@9858 | 533 | } |
apetushkov@9858 | 534 | |
apetushkov@9858 | 535 | static void add_reference_info(const RoutableEdge* current, traceid id, traceid parent_id) { |
apetushkov@9858 | 536 | assert(current != NULL, "invariant"); |
apetushkov@9858 | 537 | if (ref_infos == NULL) { |
apetushkov@9858 | 538 | ref_infos = new RefInfo(); |
apetushkov@9858 | 539 | } |
apetushkov@9858 | 540 | |
apetushkov@9858 | 541 | assert(ref_infos != NULL, "invariant"); |
apetushkov@9858 | 542 | ReferenceInfo* const ri = new ReferenceInfo(); |
apetushkov@9858 | 543 | assert(ri != NULL, "invariant"); |
apetushkov@9858 | 544 | |
apetushkov@9858 | 545 | ri->_id = id; |
apetushkov@9858 | 546 | ri->_data._array_info_id = !current->is_skip_edge() ? get_array_info_id(*current, id) : 0; |
apetushkov@9858 | 547 | ri->_data._field_info_id = ri->_data._array_info_id == 0 && !current->is_skip_edge() ? |
apetushkov@9858 | 548 | get_field_info_id(*current) : (traceid)0; |
apetushkov@9858 | 549 | ri->_data._old_object_sample_id = parent_id; |
apetushkov@9858 | 550 | ri->_data._skip = current->skip_length(); |
apetushkov@9858 | 551 | ref_infos->store(ri); |
apetushkov@9858 | 552 | } |
apetushkov@9858 | 553 | |
apetushkov@9858 | 554 | static traceid add_root_info(const Edge* root, traceid id) { |
apetushkov@9858 | 555 | assert(root != NULL, "invariant"); |
apetushkov@9858 | 556 | assert(root->is_root(), "invariant"); |
apetushkov@9858 | 557 | return get_root_description_info_id(*root, id); |
apetushkov@9858 | 558 | } |
apetushkov@9858 | 559 | |
apetushkov@9858 | 560 | void ObjectSampleWriter::write(const RoutableEdge* edge) { |
apetushkov@9858 | 561 | assert(edge != NULL, "invariant"); |
apetushkov@9858 | 562 | const traceid id = _store->get_id(edge); |
apetushkov@9858 | 563 | add_old_object_sample_info(edge, id); |
apetushkov@9858 | 564 | const RoutableEdge* parent = edge->logical_parent(); |
apetushkov@9858 | 565 | if (parent != NULL) { |
apetushkov@9858 | 566 | add_reference_info(edge, id, _store->get_id(parent)); |
apetushkov@9858 | 567 | } else { |
apetushkov@9858 | 568 | assert(edge->is_root(), "invariant"); |
apetushkov@9858 | 569 | add_root_info(edge, id); |
apetushkov@9858 | 570 | } |
apetushkov@9858 | 571 | } |
apetushkov@9858 | 572 | |
apetushkov@9858 | 573 | ObjectSampleWriter::ObjectSampleWriter(JfrCheckpointWriter& writer, const EdgeStore* store) : |
apetushkov@9858 | 574 | _writer(writer), |
apetushkov@9858 | 575 | _store(store) { |
apetushkov@9858 | 576 | assert(store != NULL, "invariant"); |
apetushkov@9858 | 577 | assert(store->number_of_entries() > 0, "invariant"); |
apetushkov@9858 | 578 | sample_infos = NULL; |
apetushkov@9858 | 579 | ref_infos = NULL; |
apetushkov@9858 | 580 | array_infos = NULL; |
apetushkov@9858 | 581 | field_infos = NULL; |
apetushkov@9858 | 582 | root_infos = NULL; |
apetushkov@9858 | 583 | } |
apetushkov@9858 | 584 | |
apetushkov@9858 | 585 | ObjectSampleWriter::~ObjectSampleWriter() { |
apetushkov@9858 | 586 | write_sample_infos(_writer); |
apetushkov@9858 | 587 | write_reference_infos(_writer); |
apetushkov@9858 | 588 | write_array_infos(_writer); |
apetushkov@9858 | 589 | write_field_infos(_writer); |
apetushkov@9858 | 590 | write_root_descriptors(_writer); |
apetushkov@9858 | 591 | } |
apetushkov@9858 | 592 | |
apetushkov@9858 | 593 | void ObjectSampleWriter::write_chain(const RoutableEdge& edge) { |
apetushkov@9858 | 594 | assert(EdgeUtils::is_leak_edge(edge), "invariant"); |
apetushkov@9858 | 595 | if (edge.processed()) { |
apetushkov@9858 | 596 | return; |
apetushkov@9858 | 597 | } |
apetushkov@9858 | 598 | EdgeUtils::collapse_chain(edge); |
apetushkov@9858 | 599 | const RoutableEdge* current = &edge; |
apetushkov@9858 | 600 | while (current != NULL) { |
apetushkov@9858 | 601 | if (current->processed()) { |
apetushkov@9858 | 602 | return; |
apetushkov@9858 | 603 | } |
apetushkov@9858 | 604 | write(current); |
apetushkov@9858 | 605 | current->set_processed(); |
apetushkov@9858 | 606 | current = current->logical_parent(); |
apetushkov@9858 | 607 | } |
apetushkov@9858 | 608 | } |
apetushkov@9858 | 609 | |
apetushkov@9858 | 610 | bool ObjectSampleWriter::operator()(const RoutableEdge& edge) { |
apetushkov@9858 | 611 | if (EdgeUtils::is_leak_edge(edge)) { |
apetushkov@9858 | 612 | write_chain(edge); |
apetushkov@9858 | 613 | } |
apetushkov@9858 | 614 | return true; |
apetushkov@9858 | 615 | } |