| /* |
| * 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_STACK_PROFILE_TRACKER_H_ |
| #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_ |
| |
| #include <deque> |
| #include <optional> |
| #include <unordered_map> |
| |
| #include "src/trace_processor/storage/trace_storage.h" |
| #include "src/trace_processor/tables/profiler_tables_py.h" |
| |
| #include "protos/perfetto/trace/profiling/profile_common.pbzero.h" |
| #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h" |
| |
| template <> |
| struct std::hash<std::pair<uint32_t, int64_t>> { |
| using argument_type = std::pair<uint32_t, int64_t>; |
| using result_type = size_t; |
| |
| result_type operator()(const argument_type& p) const { |
| return std::hash<uint32_t>{}(p.first) ^ std::hash<int64_t>{}(p.second); |
| } |
| }; |
| |
| template <> |
| struct std::hash<std::pair<uint32_t, perfetto::trace_processor::CallsiteId>> { |
| using argument_type = |
| std::pair<uint32_t, perfetto::trace_processor::CallsiteId>; |
| using result_type = size_t; |
| |
| result_type operator()(const argument_type& p) const { |
| return std::hash<uint32_t>{}(p.first) ^ |
| std::hash<uint32_t>{}(p.second.value); |
| } |
| }; |
| |
| template <> |
| struct std::hash<std::pair<uint32_t, perfetto::trace_processor::MappingId>> { |
| using argument_type = |
| std::pair<uint32_t, perfetto::trace_processor::MappingId>; |
| using result_type = size_t; |
| |
| result_type operator()(const argument_type& p) const { |
| return std::hash<uint32_t>{}(p.first) ^ |
| std::hash<uint32_t>{}(p.second.value); |
| } |
| }; |
| |
| template <> |
| struct std::hash<std::pair<uint32_t, perfetto::trace_processor::FrameId>> { |
| using argument_type = std::pair<uint32_t, perfetto::trace_processor::FrameId>; |
| using result_type = size_t; |
| |
| result_type operator()(const argument_type& p) const { |
| return std::hash<uint32_t>{}(p.first) ^ |
| std::hash<uint32_t>{}(p.second.value); |
| } |
| }; |
| |
| template <> |
| struct std::hash<std::vector<uint64_t>> { |
| using argument_type = std::vector<uint64_t>; |
| using result_type = size_t; |
| |
| result_type operator()(const argument_type& p) const { |
| size_t h = 0u; |
| for (auto v : p) |
| h = h ^ std::hash<uint64_t>{}(v); |
| return h; |
| } |
| }; |
| |
| namespace perfetto { |
| namespace trace_processor { |
| |
| struct NameInPackage { |
| StringId name; |
| StringId package; |
| |
| bool operator<(const NameInPackage& b) const { |
| return std::tie(name, package) < std::tie(b.name, b.package); |
| } |
| }; |
| |
| class TraceProcessorContext; |
| |
| class GlobalStackProfileTracker { |
| public: |
| std::vector<MappingId> FindMappingRow(StringId name, |
| StringId build_id) const { |
| auto it = stack_profile_mapping_index_.find(std::make_pair(name, build_id)); |
| if (it == stack_profile_mapping_index_.end()) |
| return {}; |
| return it->second; |
| } |
| |
| void InsertMappingId(StringId name, StringId build_id, MappingId row) { |
| auto pair = std::make_pair(name, build_id); |
| stack_profile_mapping_index_[pair].emplace_back(row); |
| } |
| |
| std::vector<FrameId> FindFrameIds(MappingId mapping_row, |
| uint64_t rel_pc) const { |
| auto it = |
| stack_profile_frame_index_.find(std::make_pair(mapping_row, rel_pc)); |
| if (it == stack_profile_frame_index_.end()) |
| return {}; |
| return it->second; |
| } |
| |
| void InsertFrameRow(MappingId mapping_row, uint64_t rel_pc, FrameId row) { |
| auto pair = std::make_pair(mapping_row, rel_pc); |
| stack_profile_frame_index_[pair].emplace_back(row); |
| } |
| |
| const std::vector<tables::StackProfileFrameTable::Id>* JavaFramesForName( |
| NameInPackage name) { |
| auto it = java_frames_for_name_.find(name); |
| if (it == java_frames_for_name_.end()) |
| return nullptr; |
| return &it->second; |
| } |
| |
| void InsertJavaFrameForName(NameInPackage name, |
| tables::StackProfileFrameTable::Id id) { |
| java_frames_for_name_[name].push_back(id); |
| } |
| |
| private: |
| using MappingKey = std::pair<StringId /* name */, StringId /* build id */>; |
| std::map<MappingKey, std::vector<MappingId>> stack_profile_mapping_index_; |
| |
| using FrameKey = std::pair<MappingId, uint64_t /* rel_pc */>; |
| std::map<FrameKey, std::vector<FrameId>> stack_profile_frame_index_; |
| |
| std::map<NameInPackage, std::vector<tables::StackProfileFrameTable::Id>> |
| java_frames_for_name_; |
| }; |
| |
| // TODO(lalitm): Overhaul this class to make row vs id consistent and use |
| // base::Optional instead of int64_t. |
| class SequenceStackProfileTracker { |
| public: |
| using SourceStringId = uint64_t; |
| |
| enum class InternedStringType { |
| kMappingPath, |
| kBuildId, |
| kFunctionName, |
| }; |
| |
| struct SourceMapping { |
| SourceStringId build_id = 0; |
| uint64_t exact_offset = 0; |
| uint64_t start_offset = 0; |
| uint64_t start = 0; |
| uint64_t end = 0; |
| uint64_t load_bias = 0; |
| std::vector<SourceStringId> name_ids; |
| }; |
| using SourceMappingId = uint64_t; |
| |
| struct SourceFrame { |
| SourceStringId name_id = 0; |
| SourceMappingId mapping_id = 0; |
| uint64_t rel_pc = 0; |
| }; |
| using SourceFrameId = uint64_t; |
| |
| using SourceCallstack = std::vector<SourceFrameId>; |
| using SourceCallstackId = uint64_t; |
| |
| struct SourceAllocation { |
| uint64_t pid = 0; |
| // This is int64_t, because we get this from the TraceSorter which also |
| // converts this for us. |
| int64_t timestamp = 0; |
| SourceCallstackId callstack_id = 0; |
| uint64_t self_allocated = 0; |
| uint64_t self_freed = 0; |
| uint64_t alloc_count = 0; |
| uint64_t free_count = 0; |
| }; |
| |
| class InternLookup { |
| public: |
| virtual ~InternLookup(); |
| |
| virtual std::optional<base::StringView> GetString( |
| SourceStringId, |
| InternedStringType) const = 0; |
| virtual std::optional<SourceMapping> GetMapping(SourceMappingId) const = 0; |
| virtual std::optional<SourceFrame> GetFrame(SourceFrameId) const = 0; |
| virtual std::optional<SourceCallstack> GetCallstack( |
| SourceCallstackId) const = 0; |
| }; |
| |
| explicit SequenceStackProfileTracker(TraceProcessorContext* context); |
| ~SequenceStackProfileTracker(); |
| |
| void AddString(SourceStringId, base::StringView); |
| std::optional<MappingId> AddMapping( |
| SourceMappingId, |
| const SourceMapping&, |
| const InternLookup* intern_lookup = nullptr); |
| std::optional<FrameId> AddFrame(SourceFrameId, |
| const SourceFrame&, |
| const InternLookup* intern_lookup = nullptr); |
| std::optional<CallsiteId> AddCallstack( |
| SourceCallstackId, |
| const SourceCallstack&, |
| const InternLookup* intern_lookup = nullptr); |
| |
| FrameId GetDatabaseFrameIdForTesting(SourceFrameId); |
| |
| // Gets the row number of string / mapping / frame / callstack previously |
| // added through AddString / AddMapping/ AddFrame / AddCallstack. |
| // |
| // If it is not found, look up the string / mapping / frame / callstack in |
| // the global InternedData state, and if found, add to the database, if not |
| // already added before. |
| // |
| // This is to support both ProfilePackets that contain the interned data |
| // (for Android Q) and where the interned data is kept globally in |
| // InternedData (for versions newer than Q). |
| std::optional<StringId> FindAndInternString(SourceStringId, |
| const InternLookup* intern_lookup, |
| InternedStringType type); |
| std::optional<std::string> FindOrInsertString( |
| SourceStringId, |
| const InternLookup* intern_lookup, |
| InternedStringType type); |
| std::optional<MappingId> FindOrInsertMapping( |
| SourceMappingId, |
| const InternLookup* intern_lookup); |
| std::optional<FrameId> FindOrInsertFrame(SourceFrameId, |
| const InternLookup* intern_lookup); |
| |
| std::optional<CallsiteId> FindOrInsertCallstack( |
| SourceCallstackId, |
| const InternLookup* intern_lookup); |
| |
| // Clear indices when they're no longer needed. |
| void ClearIndices(); |
| |
| private: |
| StringId GetEmptyStringId(); |
| |
| std::unordered_map<SourceStringId, std::string> string_map_; |
| |
| // Mapping from ID of mapping / frame / callstack in original trace and the |
| // index in the respective table it was inserted into. |
| std::unordered_map<SourceMappingId, MappingId> mapping_ids_; |
| std::unordered_map<SourceFrameId, FrameId> frame_ids_; |
| std::unordered_map<SourceCallstackId, CallsiteId> callstack_ids_; |
| |
| // TODO(oysteine): Share these indices between the StackProfileTrackers, |
| // since they're not sequence-specific. |
| // |
| // Mapping from content of database row to the index of the raw. |
| std::unordered_map<tables::StackProfileMappingTable::Row, MappingId> |
| mapping_idx_; |
| std::unordered_map<tables::StackProfileFrameTable::Row, FrameId> frame_idx_; |
| std::unordered_map<tables::StackProfileCallsiteTable::Row, CallsiteId> |
| callsite_idx_; |
| |
| TraceProcessorContext* const context_; |
| StringId empty_; |
| }; |
| |
| } // namespace trace_processor |
| } // namespace perfetto |
| |
| #endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_ |