xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/proto/profile_packet_sequence_state.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_SEQUENCE_STATE_H_
18 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_SEQUENCE_STATE_H_
19 
20 #include <cstdint>
21 #include "perfetto/base/flat_set.h"
22 #include "perfetto/ext/base/flat_hash_map.h"
23 
24 #include "perfetto/ext/base/hash.h"
25 #include "perfetto/ext/base/string_view.h"
26 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
27 #include "src/trace_processor/importers/proto/stack_profile_sequence_state.h"
28 #include "src/trace_processor/storage/trace_storage.h"
29 
30 namespace perfetto {
31 namespace trace_processor {
32 
33 class VirtualMemoryMapping;
34 
35 // Keeps sequence specific state for profile packets.
36 class ProfilePacketSequenceState final
37     : public PacketSequenceStateGeneration::CustomState {
38  public:
39   using SourceStringId = uint64_t;
40 
41   struct SourceMapping {
42     SourceStringId build_id = 0;
43     uint64_t exact_offset = 0;
44     uint64_t start_offset = 0;
45     uint64_t start = 0;
46     uint64_t end = 0;
47     uint64_t load_bias = 0;
48     std::vector<SourceStringId> name_ids;
49   };
50   using SourceMappingId = uint64_t;
51 
52   struct SourceFrame {
53     SourceStringId name_id = 0;
54     SourceMappingId mapping_id = 0;
55     uint64_t rel_pc = 0;
56   };
57   using SourceFrameId = uint64_t;
58 
59   using SourceCallstack = std::vector<SourceFrameId>;
60   using SourceCallstackId = uint64_t;
61   struct SourceAllocation {
62     uint64_t pid = 0;
63     // This is int64_t, because we get this from the TraceSorter which also
64     // converts this for us.
65     int64_t timestamp = 0;
66     StringId heap_name;
67     uint64_t callstack_id = 0;
68     uint64_t self_allocated = 0;
69     uint64_t self_freed = 0;
70     uint64_t alloc_count = 0;
71     uint64_t free_count = 0;
72   };
73 
74   explicit ProfilePacketSequenceState(TraceProcessorContext* context);
75   virtual ~ProfilePacketSequenceState() override;
76 
77   // Profile packets keep track of a index to detect packet loss. Call this
78   // method to update this index with the latest seen value.
79   void SetProfilePacketIndex(uint64_t index);
80 
81   // In Android version Q we did not intern Mappings, Frames nor Callstacks,
82   // instead the profile packed "interned these". The following methods are used
83   // to support this old use case. They add the given object to a sequence local
84   // index for them to be retrieved later (see Find* Lookup* methods).
85   void AddString(SourceStringId id, base::StringView str);
86   void AddMapping(SourceMappingId id, const SourceMapping& mapping);
87   void AddFrame(SourceFrameId id, const SourceFrame& frame);
88   void AddCallstack(SourceCallstackId id, const SourceCallstack& callstack);
89 
90   void StoreAllocation(const SourceAllocation& allocation);
91   void FinalizeProfile();
92   void CommitAllocations();
93 
94   FrameId GetDatabaseFrameIdForTesting(SourceFrameId);
95 
96  private:
97   struct SourceAllocationIndex {
98     UniquePid upid;
99     SourceCallstackId src_callstack_id;
100     StringPool::Id heap_name;
101     bool operator==(const SourceAllocationIndex& o) const {
102       return std::tie(upid, src_callstack_id, heap_name) ==
103              std::tie(o.upid, o.src_callstack_id, o.heap_name);
104     }
105     struct Hasher {
operatorSourceAllocationIndex::Hasher106       size_t operator()(const SourceAllocationIndex& o) const {
107         return static_cast<size_t>(base::Hasher::Combine(
108             o.upid, o.src_callstack_id, o.heap_name.raw_id()));
109       }
110     };
111   };
112 
113   void AddAllocation(const SourceAllocation& alloc);
114 
115   // The following methods deal with interned data. In Android version Q we did
116   // not intern Mappings, Frames nor Callstacks, instead the profile packed
117   // "interned these" and this class keeps those ina  sequence local index. In
118   // newer versions, these objects are in InternedData (see
119   // protos/perfetto/trace/interned_data) and are shared across multiple
120   // ProfilePackets. For backwards compatibility, the following methods first
121   // look up interned data in the private sequence local index (for values added
122   // via the Add* methods), and then, if this lookup fails, in the InternedData
123   // instead.
124   std::optional<MappingId> FindOrInsertMapping(uint64_t iid);
125   std::optional<CallsiteId> FindOrInsertCallstack(UniquePid upid, uint64_t iid);
126 
127   TraceProcessorContext* const context_;
128 
129   base::FlatHashMap<SourceStringId, std::string> strings_;
130   base::FlatHashMap<SourceMappingId, VirtualMemoryMapping*> mappings_;
131   base::FlatHashMap<SourceFrameId, FrameId> frames_;
132   base::FlatHashMap<SourceCallstackId, CallsiteId> callstacks_;
133 
134   std::vector<SourceAllocation> pending_allocs_;
135 
136   struct Hasher {
operatorHasher137     size_t operator()(const std::pair<UniquePid, CallsiteId>& p) const {
138       return static_cast<size_t>(
139           base::Hasher::Combine(p.first, p.second.value));
140     }
141   };
142   base::FlatHashMap<std::pair<UniquePid, CallsiteId>,
143                     tables::HeapProfileAllocationTable::Row,
144                     Hasher>
145       prev_alloc_;
146   base::FlatHashMap<std::pair<UniquePid, CallsiteId>,
147                     tables::HeapProfileAllocationTable::Row,
148                     Hasher>
149       prev_free_;
150 
151   // For continuous dumps, we only store the delta in the data-base. To do
152   // this, we subtract the previous dump's value. Sometimes, we should not
153   // do that subtraction, because heapprofd garbage collects stacks that
154   // have no unfreed allocations. If the application then allocations again
155   // at that stack, it gets recreated and initialized to zero.
156   //
157   // To correct for this, we add the previous' stacks value to the current
158   // one, and then handle it as normal. If it is the first time we see a
159   // SourceCallstackId for a CallsiteId, we put the previous value into
160   // the correction maps below.
161   base::FlatHashMap<SourceAllocationIndex,
162                     base::FlatSet<CallsiteId>,
163                     SourceAllocationIndex::Hasher>
164       seen_callstacks_;
165   base::FlatHashMap<SourceCallstackId, tables::HeapProfileAllocationTable::Row>
166       alloc_correction_;
167   base::FlatHashMap<SourceCallstackId, tables::HeapProfileAllocationTable::Row>
168       free_correction_;
169 
170   std::optional<uint64_t> prev_index;
171 };
172 
173 }  // namespace trace_processor
174 }  // namespace perfetto
175 
176 #endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_SEQUENCE_STATE_H_
177