xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/common/virtual_memory_mapping.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 
2 /*
3  * Copyright (C) 2024 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_VIRTUAL_MEMORY_MAPPING_H_
19 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_VIRTUAL_MEMORY_MAPPING_H_
20 
21 #include <cstddef>
22 #include <cstdint>
23 #include <optional>
24 #include <string>
25 #include <vector>
26 
27 #include "perfetto/ext/base/flat_hash_map.h"
28 #include "perfetto/ext/base/hash.h"
29 #include "perfetto/ext/base/string_view.h"
30 #include "src/trace_processor/importers/common/address_range.h"
31 #include "src/trace_processor/importers/common/create_mapping_params.h"
32 #include "src/trace_processor/storage/trace_storage.h"
33 #include "src/trace_processor/types/trace_processor_context.h"
34 #include "src/trace_processor/util/build_id.h"
35 
36 namespace perfetto {
37 namespace trace_processor {
38 
39 // TODO(carlscab): Reconsider whether jit is the best abstraction here. All we
40 // really care is about mapping a `rel_pc` to a symbol (aka symbolization) and
41 // whether is this is constant.
42 class JitCache;
43 
44 // Represents a mapping in virtual memory.
45 class VirtualMemoryMapping {
46  public:
47   virtual ~VirtualMemoryMapping();
48   // Range of virtual memory this mapping covers.
memory_range()49   AddressRange memory_range() const { return memory_range_; }
mapping_id()50   MappingId mapping_id() const { return mapping_id_; }
51   // This name could be the path of the underlying file mapped into memory.
name()52   const std::string& name() const { return name_; }
53   // For file mappings, this is the offset into the file for the first byte in
54   // the mapping
offset()55   uint64_t offset() const { return offset_; }
56   // If the mapped file is an executable or shared library this will return the
57   // load bias, if known. Returns 0 otherwise.
load_bias()58   uint64_t load_bias() const { return load_bias_; }
59   // If the mapped file is an executable or shared library this will return its
60   // build id, if known.
build_id()61   const std::optional<BuildId>& build_id() const { return build_id_; }
62 
63   // Whether this maps to a region that holds jitted code.
is_jitted()64   bool is_jitted() const { return jit_cache_ != nullptr; }
65 
66   // Converts an absolute address into a relative one.
ToRelativePc(uint64_t address)67   uint64_t ToRelativePc(uint64_t address) const {
68     return address - memory_range_.start() + offset_ + load_bias_;
69   }
70 
71   // Converts a relative address to an absolute one.
ToAddress(uint64_t rel_pc)72   uint64_t ToAddress(uint64_t rel_pc) const {
73     return rel_pc + (memory_range_.start() - offset_ - load_bias_);
74   }
75 
76   // Creates a frame for the given `rel_pc`. Note that if the mapping
77   // `is_jitted()` same `rel_pc` values can return different mappings (as jitted
78   // functions can be created and deleted over time.) So for such mappings the
79   // returned `FrameId` should not be cached.
80   FrameId InternFrame(uint64_t rel_pc, base::StringView function_name);
81 
82   // Returns all frames ever created in this mapping for the given `rel_pc`.
83   std::vector<FrameId> FindFrameIds(uint64_t rel_pc) const;
84 
85  protected:
86   VirtualMemoryMapping(TraceProcessorContext* context,
87                        CreateMappingParams params);
88 
context()89   TraceProcessorContext* context() const { return context_; }
90 
91  private:
92   friend class MappingTracker;
93 
94   std::pair<FrameId, bool> InternFrameImpl(uint64_t rel_pc,
95                                            base::StringView function_name);
96 
SetJitCache(JitCache * jit_cache)97   void SetJitCache(JitCache* jit_cache) { jit_cache_ = jit_cache; }
98 
99   TraceProcessorContext* const context_;
100   const MappingId mapping_id_;
101   const AddressRange memory_range_;
102   const uint64_t offset_;
103   const uint64_t load_bias_;
104   const std::string name_;
105   std::optional<BuildId> const build_id_;
106   JitCache* jit_cache_ = nullptr;
107 
108   struct FrameKey {
109     uint64_t rel_pc;
110     // It doesn't seem to make too much sense to key on name, as for the same
111     // mapping and same rel_pc the name should always be the same. But who knows
112     // how producers behave.
113     StringId name_id;
114 
115     bool operator==(const FrameKey& o) const {
116       return rel_pc == o.rel_pc && name_id == o.name_id;
117     }
118 
119     struct Hasher {
operatorFrameKey::Hasher120       size_t operator()(const FrameKey& k) const {
121         return static_cast<size_t>(
122             base::Hasher::Combine(k.rel_pc, k.name_id.raw_id()));
123       }
124     };
125   };
126   base::FlatHashMap<FrameKey, FrameId, FrameKey::Hasher> interned_frames_;
127   base::FlatHashMap<uint64_t, std::vector<FrameId>> frames_by_rel_pc_;
128 };
129 
130 class KernelMemoryMapping : public VirtualMemoryMapping {
131  public:
132   ~KernelMemoryMapping() override;
133 
134  private:
135   friend class MappingTracker;
136   KernelMemoryMapping(TraceProcessorContext* context,
137                       CreateMappingParams params);
138 };
139 
140 class UserMemoryMapping : public VirtualMemoryMapping {
141  public:
142   ~UserMemoryMapping() override;
upid()143   UniquePid upid() const { return upid_; }
144 
145  private:
146   friend class MappingTracker;
147   UserMemoryMapping(TraceProcessorContext* context,
148                     UniquePid upid,
149                     CreateMappingParams params);
150 
151   const UniquePid upid_;
152 };
153 
154 // Dummy mapping to be able to create frames when we have no real pc addresses
155 // or real mappings.
156 class DummyMemoryMapping : public VirtualMemoryMapping {
157  public:
158   ~DummyMemoryMapping() override;
159 
160   // Interns a frame based solely on function name and source file. This is
161   // useful for profilers that do not emit an address nor a mapping.
162   FrameId InternDummyFrame(base::StringView function_name,
163                            base::StringView source_file);
164 
165  private:
166   friend class MappingTracker;
167   DummyMemoryMapping(TraceProcessorContext* context,
168                      CreateMappingParams params);
169 
170   struct DummyFrameKey {
171     StringId function_name_id;
172     StringId source_file_id;
173 
174     bool operator==(const DummyFrameKey& o) const {
175       return function_name_id == o.function_name_id &&
176              source_file_id == o.source_file_id;
177     }
178 
179     struct Hasher {
operatorDummyFrameKey::Hasher180       size_t operator()(const DummyFrameKey& k) const {
181         return static_cast<size_t>(base::Hasher::Combine(
182             k.function_name_id.raw_id(), k.source_file_id.raw_id()));
183       }
184     };
185   };
186   base::FlatHashMap<DummyFrameKey, FrameId, DummyFrameKey::Hasher>
187       interned_dummy_frames_;
188 };
189 
190 }  // namespace trace_processor
191 }  // namespace perfetto
192 
193 #endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_VIRTUAL_MEMORY_MAPPING_H_
194