| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_GRAPH_TRACKER_H_ |
| #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_GRAPH_TRACKER_H_ |
| |
| #include <map> |
| #include <optional> |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include "perfetto/ext/base/string_view.h" |
| |
| #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h" |
| #include "src/trace_processor/storage/trace_storage.h" |
| #include "src/trace_processor/types/trace_processor_context.h" |
| |
| namespace perfetto { |
| namespace trace_processor { |
| |
| class TraceProcessorContext; |
| |
| struct NormalizedType { |
| base::StringView name; |
| bool is_static_class; |
| size_t number_of_arrays; |
| }; |
| |
| struct PathFromRoot { |
| static constexpr size_t kRoot = 0; |
| struct Node { |
| uint32_t depth = 0; |
| // Invariant: parent_id < id of this node. |
| size_t parent_id = 0; |
| int64_t size = 0; |
| int64_t count = 0; |
| StringId class_name_id = {}; |
| std::map<StringId, size_t> children; |
| }; |
| std::vector<Node> nodes{Node{}}; |
| std::set<tables::HeapGraphObjectTable::Id> visited; |
| }; |
| |
| void MarkRoot(TraceStorage*, |
| tables::HeapGraphObjectTable::RowReference, |
| StringId type); |
| void UpdateShortestPaths(TraceStorage* s, |
| tables::HeapGraphObjectTable::RowReference row_ref); |
| void FindPathFromRoot(TraceStorage* storage, |
| tables::HeapGraphObjectTable::RowReference, |
| PathFromRoot* path); |
| |
| std::optional<base::StringView> GetStaticClassTypeName(base::StringView type); |
| size_t NumberOfArrays(base::StringView type); |
| NormalizedType GetNormalizedType(base::StringView type); |
| base::StringView NormalizeTypeName(base::StringView type); |
| std::string DenormalizeTypeName(NormalizedType normalized, |
| base::StringView deobfuscated_type_name); |
| |
| class HeapGraphTracker : public Destructible { |
| public: |
| struct SourceObject { |
| // All ids in this are in the trace iid space, not in the trace processor |
| // id space. |
| uint64_t object_id = 0; |
| uint64_t self_size = 0; |
| uint64_t type_id = 0; |
| |
| std::vector<uint64_t> field_name_ids; |
| std::vector<uint64_t> referred_objects; |
| |
| // If this object is an instance of `libcore.util.NativeAllocationRegistry`, |
| // this is the value of its `size` field. |
| std::optional<int64_t> native_allocation_registry_size; |
| }; |
| |
| struct SourceRoot { |
| StringId root_type; |
| std::vector<uint64_t> object_ids; |
| }; |
| |
| explicit HeapGraphTracker(TraceStorage* storage); |
| |
| static HeapGraphTracker* GetOrCreate(TraceProcessorContext* context) { |
| if (!context->heap_graph_tracker) { |
| context->heap_graph_tracker.reset( |
| new HeapGraphTracker(context->storage.get())); |
| } |
| return static_cast<HeapGraphTracker*>(context->heap_graph_tracker.get()); |
| } |
| |
| void AddRoot(uint32_t seq_id, UniquePid upid, int64_t ts, SourceRoot root); |
| void AddObject(uint32_t seq_id, UniquePid upid, int64_t ts, SourceObject obj); |
| void AddInternedType(uint32_t seq_id, |
| uint64_t intern_id, |
| StringId strid, |
| std::optional<uint64_t> location_id, |
| uint64_t object_size, |
| std::vector<uint64_t> field_name_ids, |
| uint64_t superclass_id, |
| uint64_t classloader_id, |
| bool no_fields, |
| StringId kind); |
| void AddInternedFieldName(uint32_t seq_id, |
| uint64_t intern_id, |
| base::StringView str); |
| void AddInternedLocationName(uint32_t seq_id, |
| uint64_t intern_id, |
| StringId str); |
| void FinalizeProfile(uint32_t seq); |
| void FinalizeAllProfiles(); |
| void SetPacketIndex(uint32_t seq_id, uint64_t index); |
| |
| ~HeapGraphTracker() override; |
| |
| const std::vector<tables::HeapGraphClassTable::RowNumber>* RowsForType( |
| std::optional<StringId> package_name, |
| StringId type_name) const { |
| auto it = class_to_rows_.find(std::make_pair(package_name, type_name)); |
| if (it == class_to_rows_.end()) |
| return nullptr; |
| return &it->second; |
| } |
| |
| const std::vector<tables::HeapGraphReferenceTable::RowNumber>* RowsForField( |
| StringId field_name) const { |
| return field_to_rows_.Find(field_name); |
| } |
| |
| std::unique_ptr<tables::ExperimentalFlamegraphNodesTable> BuildFlamegraph( |
| const int64_t current_ts, |
| const UniquePid current_upid); |
| |
| uint64_t GetLastObjectId(uint32_t seq_id) { |
| return GetOrCreateSequence(seq_id).last_object_id; |
| } |
| |
| private: |
| struct InternedField { |
| StringId name; |
| StringId type_name; |
| }; |
| struct InternedType { |
| StringId name; |
| std::optional<uint64_t> location_id; |
| uint64_t object_size; |
| std::vector<uint64_t> field_name_ids; |
| uint64_t superclass_id; |
| bool no_fields; |
| uint64_t classloader_id; |
| StringId kind; |
| }; |
| struct SequenceState { |
| UniquePid current_upid = 0; |
| int64_t current_ts = 0; |
| uint64_t last_object_id = 0; |
| std::vector<SourceRoot> current_roots; |
| |
| // Note: the below maps are a mix of std::map and base::FlatHashMap because |
| // of the incremental evolution of this code (i.e. when the code was written |
| // FlatHashMap did not exist and pieces were migrated as they were found to |
| // be performance problems). |
| // |
| // In the future, likely all of these should be base::FlatHashMap. This |
| // was not done when the first use of base::FlatHashMap happened because |
| // there are some subtle cases where base::FlatHashMap *regresses* perf and |
| // there was not time for investigation. |
| |
| std::map<uint64_t, InternedType> interned_types; |
| std::map<uint64_t, StringId> interned_location_names; |
| base::FlatHashMap<uint64_t, tables::HeapGraphObjectTable::RowNumber> |
| object_id_to_db_row; |
| base::FlatHashMap<uint64_t, tables::HeapGraphClassTable::RowNumber> |
| type_id_to_db_row; |
| std::map<uint64_t, std::vector<tables::HeapGraphReferenceTable::RowNumber>> |
| references_for_field_name_id; |
| base::FlatHashMap<uint64_t, InternedField> interned_fields; |
| std::map<tables::HeapGraphClassTable::Id, |
| std::vector<tables::HeapGraphObjectTable::RowNumber>> |
| deferred_reference_objects_for_type_; |
| std::optional<uint64_t> prev_index; |
| // For most objects, we need not store the size in the object's message |
| // itself, because all instances of the type have the same type. In this |
| // case, we defer setting self_size in the table until we process the class |
| // message in FinalizeProfile. |
| std::map<tables::HeapGraphClassTable::Id, |
| std::vector<tables::HeapGraphObjectTable::RowNumber>> |
| deferred_size_objects_for_type_; |
| // Contains the value of the "size" field for each |
| // "libcore.util.NativeAllocationRegistry" object. |
| std::map<tables::HeapGraphObjectTable::Id, int64_t> nar_size_by_obj_id; |
| bool truncated = false; |
| }; |
| |
| SequenceState& GetOrCreateSequence(uint32_t seq_id); |
| tables::HeapGraphObjectTable::RowReference GetOrInsertObject( |
| SequenceState* sequence_state, |
| uint64_t object_id); |
| tables::HeapGraphClassTable::RowReference GetOrInsertType( |
| SequenceState* sequence_state, |
| uint64_t type_id); |
| bool SetPidAndTimestamp(SequenceState* seq, UniquePid upid, int64_t ts); |
| void PopulateSuperClasses(const SequenceState& seq); |
| InternedType* GetSuperClass(SequenceState* sequence_state, |
| const InternedType* current_type); |
| bool IsTruncated(UniquePid upid, int64_t ts); |
| |
| // Returns the object pointed to by `field` in `obj`. |
| std::optional<tables::HeapGraphObjectTable::Id> GetReferenceByFieldName( |
| tables::HeapGraphObjectTable::Id obj, |
| StringId field); |
| |
| // Populates HeapGraphObject::native_size by walking the graph for |
| // `seq`. |
| // |
| // This should be called only once (it is not idempotent) per seq, after the |
| // all the other tables have been fully populated. |
| void PopulateNativeSize(const SequenceState& seq); |
| |
| TraceStorage* const storage_; |
| std::map<uint32_t, SequenceState> sequence_state_; |
| |
| std::map<std::pair<std::optional<StringId>, StringId>, |
| std::vector<tables::HeapGraphClassTable::RowNumber>> |
| class_to_rows_; |
| base::FlatHashMap<StringId, |
| std::vector<tables::HeapGraphReferenceTable::RowNumber>> |
| field_to_rows_; |
| |
| std::map<std::pair<std::optional<StringId>, StringId>, StringId> |
| deobfuscation_mapping_; |
| std::map<std::pair<UniquePid, int64_t>, |
| std::set<tables::HeapGraphObjectTable::RowNumber>> |
| roots_; |
| std::set<std::pair<UniquePid, int64_t>> truncated_graphs_; |
| |
| StringId cleaner_thunk_str_id_; |
| StringId referent_str_id_; |
| StringId cleaner_thunk_this0_str_id_; |
| StringId native_size_str_id_; |
| StringId cleaner_next_str_id_; |
| }; |
| |
| } // namespace trace_processor |
| } // namespace perfetto |
| |
| #endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_GRAPH_TRACKER_H_ |