xref: /aosp_15_r20/external/perfetto/src/profiling/common/callstack_trie.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker  * Copyright (C) 2020 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker  *
4*6dbdd20aSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker  *
8*6dbdd20aSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker  *
10*6dbdd20aSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker  * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker  */
16*6dbdd20aSAndroid Build Coastguard Worker 
17*6dbdd20aSAndroid Build Coastguard Worker #ifndef SRC_PROFILING_COMMON_CALLSTACK_TRIE_H_
18*6dbdd20aSAndroid Build Coastguard Worker #define SRC_PROFILING_COMMON_CALLSTACK_TRIE_H_
19*6dbdd20aSAndroid Build Coastguard Worker 
20*6dbdd20aSAndroid Build Coastguard Worker #include <set>
21*6dbdd20aSAndroid Build Coastguard Worker #include <string>
22*6dbdd20aSAndroid Build Coastguard Worker #include <typeindex>
23*6dbdd20aSAndroid Build Coastguard Worker #include <vector>
24*6dbdd20aSAndroid Build Coastguard Worker 
25*6dbdd20aSAndroid Build Coastguard Worker #include <unwindstack/Unwinder.h>
26*6dbdd20aSAndroid Build Coastguard Worker 
27*6dbdd20aSAndroid Build Coastguard Worker #include "src/profiling/common/interner.h"
28*6dbdd20aSAndroid Build Coastguard Worker #include "src/profiling/common/unwind_support.h"
29*6dbdd20aSAndroid Build Coastguard Worker 
30*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
31*6dbdd20aSAndroid Build Coastguard Worker namespace profiling {
32*6dbdd20aSAndroid Build Coastguard Worker 
33*6dbdd20aSAndroid Build Coastguard Worker struct Mapping {
MappingMapping34*6dbdd20aSAndroid Build Coastguard Worker   explicit Mapping(Interned<std::string> b) : build_id(std::move(b)) {}
35*6dbdd20aSAndroid Build Coastguard Worker 
36*6dbdd20aSAndroid Build Coastguard Worker   Interned<std::string> build_id;
37*6dbdd20aSAndroid Build Coastguard Worker   uint64_t exact_offset = 0;
38*6dbdd20aSAndroid Build Coastguard Worker   uint64_t start_offset = 0;
39*6dbdd20aSAndroid Build Coastguard Worker   uint64_t start = 0;
40*6dbdd20aSAndroid Build Coastguard Worker   uint64_t end = 0;
41*6dbdd20aSAndroid Build Coastguard Worker   uint64_t load_bias = 0;
42*6dbdd20aSAndroid Build Coastguard Worker   std::vector<Interned<std::string>> path_components{};
43*6dbdd20aSAndroid Build Coastguard Worker 
44*6dbdd20aSAndroid Build Coastguard Worker   bool operator<(const Mapping& other) const {
45*6dbdd20aSAndroid Build Coastguard Worker     return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
46*6dbdd20aSAndroid Build Coastguard Worker                     path_components) <
47*6dbdd20aSAndroid Build Coastguard Worker            std::tie(other.build_id, other.exact_offset, other.start_offset,
48*6dbdd20aSAndroid Build Coastguard Worker                     other.start, other.end, other.load_bias,
49*6dbdd20aSAndroid Build Coastguard Worker                     other.path_components);
50*6dbdd20aSAndroid Build Coastguard Worker   }
51*6dbdd20aSAndroid Build Coastguard Worker   bool operator==(const Mapping& other) const {
52*6dbdd20aSAndroid Build Coastguard Worker     return std::tie(build_id, exact_offset, start_offset, start, end, load_bias,
53*6dbdd20aSAndroid Build Coastguard Worker                     path_components) ==
54*6dbdd20aSAndroid Build Coastguard Worker            std::tie(other.build_id, other.exact_offset, other.start_offset,
55*6dbdd20aSAndroid Build Coastguard Worker                     other.start, other.end, other.load_bias,
56*6dbdd20aSAndroid Build Coastguard Worker                     other.path_components);
57*6dbdd20aSAndroid Build Coastguard Worker   }
58*6dbdd20aSAndroid Build Coastguard Worker };
59*6dbdd20aSAndroid Build Coastguard Worker 
60*6dbdd20aSAndroid Build Coastguard Worker struct Frame {
FrameFrame61*6dbdd20aSAndroid Build Coastguard Worker   Frame(Interned<Mapping> m, Interned<std::string> fn_name, uint64_t pc)
62*6dbdd20aSAndroid Build Coastguard Worker       : mapping(m), function_name(fn_name), rel_pc(pc) {}
63*6dbdd20aSAndroid Build Coastguard Worker   Interned<Mapping> mapping;
64*6dbdd20aSAndroid Build Coastguard Worker   Interned<std::string> function_name;
65*6dbdd20aSAndroid Build Coastguard Worker   uint64_t rel_pc;
66*6dbdd20aSAndroid Build Coastguard Worker 
67*6dbdd20aSAndroid Build Coastguard Worker   bool operator<(const Frame& other) const {
68*6dbdd20aSAndroid Build Coastguard Worker     return std::tie(mapping, function_name, rel_pc) <
69*6dbdd20aSAndroid Build Coastguard Worker            std::tie(other.mapping, other.function_name, other.rel_pc);
70*6dbdd20aSAndroid Build Coastguard Worker   }
71*6dbdd20aSAndroid Build Coastguard Worker 
72*6dbdd20aSAndroid Build Coastguard Worker   bool operator==(const Frame& other) const {
73*6dbdd20aSAndroid Build Coastguard Worker     return std::tie(mapping, function_name, rel_pc) ==
74*6dbdd20aSAndroid Build Coastguard Worker            std::tie(other.mapping, other.function_name, other.rel_pc);
75*6dbdd20aSAndroid Build Coastguard Worker   }
76*6dbdd20aSAndroid Build Coastguard Worker };
77*6dbdd20aSAndroid Build Coastguard Worker 
78*6dbdd20aSAndroid Build Coastguard Worker // Graph of function callsites. A single instance can be used for callsites from
79*6dbdd20aSAndroid Build Coastguard Worker // different processes. Each call site is represented by a
80*6dbdd20aSAndroid Build Coastguard Worker // GlobalCallstackTrie::Node that is owned by the parent callsite. Each node has
81*6dbdd20aSAndroid Build Coastguard Worker // a pointer to its parent, which means the function call-graph can be
82*6dbdd20aSAndroid Build Coastguard Worker // reconstructed from a GlobalCallstackTrie::Node by walking down the parent
83*6dbdd20aSAndroid Build Coastguard Worker // chain.
84*6dbdd20aSAndroid Build Coastguard Worker //
85*6dbdd20aSAndroid Build Coastguard Worker // For the following two callstacks:
86*6dbdd20aSAndroid Build Coastguard Worker //  * libc_init -> main -> foo -> alloc_buf
87*6dbdd20aSAndroid Build Coastguard Worker //  * libc_init -> main -> bar -> alloc_buf
88*6dbdd20aSAndroid Build Coastguard Worker // The tree looks as following:
89*6dbdd20aSAndroid Build Coastguard Worker //             alloc_buf  alloc_buf
90*6dbdd20aSAndroid Build Coastguard Worker //                   |      |
91*6dbdd20aSAndroid Build Coastguard Worker //                  foo    bar
92*6dbdd20aSAndroid Build Coastguard Worker //                    \    /
93*6dbdd20aSAndroid Build Coastguard Worker //                      main
94*6dbdd20aSAndroid Build Coastguard Worker //                       |
95*6dbdd20aSAndroid Build Coastguard Worker //                   libc_init
96*6dbdd20aSAndroid Build Coastguard Worker //                       |
97*6dbdd20aSAndroid Build Coastguard Worker //                    [root_]
98*6dbdd20aSAndroid Build Coastguard Worker class GlobalCallstackTrie {
99*6dbdd20aSAndroid Build Coastguard Worker  public:
100*6dbdd20aSAndroid Build Coastguard Worker   // Optionally, Nodes can be externally refcounted via |IncrementNode| and
101*6dbdd20aSAndroid Build Coastguard Worker   // |DecrementNode|. In which case, the orphaned nodes are deleted when the
102*6dbdd20aSAndroid Build Coastguard Worker   // last reference is decremented.
103*6dbdd20aSAndroid Build Coastguard Worker   class Node {
104*6dbdd20aSAndroid Build Coastguard Worker    public:
105*6dbdd20aSAndroid Build Coastguard Worker     // This is opaque except to GlobalCallstackTrie.
106*6dbdd20aSAndroid Build Coastguard Worker     friend class GlobalCallstackTrie;
107*6dbdd20aSAndroid Build Coastguard Worker 
108*6dbdd20aSAndroid Build Coastguard Worker     // Allow building a node out of a frame for GetChild.
Node(Interned<Frame> frame)109*6dbdd20aSAndroid Build Coastguard Worker     explicit Node(Interned<Frame> frame) : Node(frame, 0, nullptr) {}
110*6dbdd20aSAndroid Build Coastguard Worker     Node(const Node&) = default;
111*6dbdd20aSAndroid Build Coastguard Worker     Node(Node&&) = default;
112*6dbdd20aSAndroid Build Coastguard Worker 
Node(Interned<Frame> frame,uint64_t id)113*6dbdd20aSAndroid Build Coastguard Worker     Node(Interned<Frame> frame, uint64_t id)
114*6dbdd20aSAndroid Build Coastguard Worker         : Node(std::move(frame), id, nullptr) {}
Node(Interned<Frame> frame,uint64_t id,Node * parent)115*6dbdd20aSAndroid Build Coastguard Worker     Node(Interned<Frame> frame, uint64_t id, Node* parent)
116*6dbdd20aSAndroid Build Coastguard Worker         : id_(id), parent_(parent), location_(frame) {}
117*6dbdd20aSAndroid Build Coastguard Worker 
~Node()118*6dbdd20aSAndroid Build Coastguard Worker     ~Node() { PERFETTO_DCHECK(!ref_count_); }
119*6dbdd20aSAndroid Build Coastguard Worker 
id()120*6dbdd20aSAndroid Build Coastguard Worker     uint64_t id() const { return id_; }
121*6dbdd20aSAndroid Build Coastguard Worker 
122*6dbdd20aSAndroid Build Coastguard Worker    private:
123*6dbdd20aSAndroid Build Coastguard Worker     Node* GetOrCreateChild(const Interned<Frame>& loc);
124*6dbdd20aSAndroid Build Coastguard Worker     // Deletes all descendant nodes, regardless of |ref_count_|.
DeleteChildren()125*6dbdd20aSAndroid Build Coastguard Worker     void DeleteChildren() { children_.clear(); }
126*6dbdd20aSAndroid Build Coastguard Worker 
127*6dbdd20aSAndroid Build Coastguard Worker     uint64_t ref_count_ = 0;
128*6dbdd20aSAndroid Build Coastguard Worker     uint64_t id_;
129*6dbdd20aSAndroid Build Coastguard Worker     Node* const parent_;
130*6dbdd20aSAndroid Build Coastguard Worker     const Interned<Frame> location_;
131*6dbdd20aSAndroid Build Coastguard Worker 
132*6dbdd20aSAndroid Build Coastguard Worker     class NodeComparator {
133*6dbdd20aSAndroid Build Coastguard Worker      public:
operator()134*6dbdd20aSAndroid Build Coastguard Worker       bool operator()(const Node& one, const Node& other) const {
135*6dbdd20aSAndroid Build Coastguard Worker         return one.location_ < other.location_;
136*6dbdd20aSAndroid Build Coastguard Worker       }
137*6dbdd20aSAndroid Build Coastguard Worker     };
138*6dbdd20aSAndroid Build Coastguard Worker     Node* AddChild(const Interned<Frame>& loc,
139*6dbdd20aSAndroid Build Coastguard Worker                    uint64_t next_callstack_id_,
140*6dbdd20aSAndroid Build Coastguard Worker                    Node* parent);
141*6dbdd20aSAndroid Build Coastguard Worker     void RemoveChild(Node* node);
142*6dbdd20aSAndroid Build Coastguard Worker     Node* GetChild(const Interned<Frame>& loc);
143*6dbdd20aSAndroid Build Coastguard Worker 
144*6dbdd20aSAndroid Build Coastguard Worker     std::set<Node, NodeComparator> children_;
145*6dbdd20aSAndroid Build Coastguard Worker   };
146*6dbdd20aSAndroid Build Coastguard Worker 
147*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie() = default;
148*6dbdd20aSAndroid Build Coastguard Worker   ~GlobalCallstackTrie() = default;
149*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie(const GlobalCallstackTrie&) = delete;
150*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie& operator=(const GlobalCallstackTrie&) = delete;
151*6dbdd20aSAndroid Build Coastguard Worker 
152*6dbdd20aSAndroid Build Coastguard Worker   // Moving this would invalidate the back pointers to the root_ node.
153*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie(GlobalCallstackTrie&&) = delete;
154*6dbdd20aSAndroid Build Coastguard Worker   GlobalCallstackTrie& operator=(GlobalCallstackTrie&&) = delete;
155*6dbdd20aSAndroid Build Coastguard Worker 
156*6dbdd20aSAndroid Build Coastguard Worker   Interned<Frame> InternCodeLocation(const unwindstack::FrameData& loc,
157*6dbdd20aSAndroid Build Coastguard Worker                                      const std::string& build_id);
158*6dbdd20aSAndroid Build Coastguard Worker 
159*6dbdd20aSAndroid Build Coastguard Worker   Node* CreateCallsite(const std::vector<unwindstack::FrameData>& callstack,
160*6dbdd20aSAndroid Build Coastguard Worker                        const std::vector<std::string>& build_ids);
161*6dbdd20aSAndroid Build Coastguard Worker   Node* CreateCallsite(const std::vector<Interned<Frame>>& callstack);
162*6dbdd20aSAndroid Build Coastguard Worker 
163*6dbdd20aSAndroid Build Coastguard Worker   static void IncrementNode(Node* node);
164*6dbdd20aSAndroid Build Coastguard Worker   static void DecrementNode(Node* node);
165*6dbdd20aSAndroid Build Coastguard Worker 
166*6dbdd20aSAndroid Build Coastguard Worker   std::vector<Interned<Frame>> BuildInverseCallstack(const Node* node) const;
167*6dbdd20aSAndroid Build Coastguard Worker 
168*6dbdd20aSAndroid Build Coastguard Worker   // Purges all interned callstacks (and the associated internings), without
169*6dbdd20aSAndroid Build Coastguard Worker   // restarting any interning sequences. Incompatible with external refcounting
170*6dbdd20aSAndroid Build Coastguard Worker   // of nodes (Node.ref_count_).
ClearTrie()171*6dbdd20aSAndroid Build Coastguard Worker   void ClearTrie() {
172*6dbdd20aSAndroid Build Coastguard Worker     PERFETTO_DLOG("Clearing trie");
173*6dbdd20aSAndroid Build Coastguard Worker     root_.DeleteChildren();
174*6dbdd20aSAndroid Build Coastguard Worker   }
175*6dbdd20aSAndroid Build Coastguard Worker 
176*6dbdd20aSAndroid Build Coastguard Worker  private:
177*6dbdd20aSAndroid Build Coastguard Worker   Node* GetOrCreateChild(Node* self, const Interned<Frame>& loc);
178*6dbdd20aSAndroid Build Coastguard Worker 
179*6dbdd20aSAndroid Build Coastguard Worker   Interned<Frame> MakeRootFrame();
180*6dbdd20aSAndroid Build Coastguard Worker 
181*6dbdd20aSAndroid Build Coastguard Worker   Interner<std::string> string_interner_;
182*6dbdd20aSAndroid Build Coastguard Worker   Interner<Mapping> mapping_interner_;
183*6dbdd20aSAndroid Build Coastguard Worker   Interner<Frame> frame_interner_;
184*6dbdd20aSAndroid Build Coastguard Worker 
185*6dbdd20aSAndroid Build Coastguard Worker   uint64_t next_callstack_id_ = 0;
186*6dbdd20aSAndroid Build Coastguard Worker 
187*6dbdd20aSAndroid Build Coastguard Worker   // Note: profile_module in trace processor relies on the value of this root
188*6dbdd20aSAndroid Build Coastguard Worker   // callsite being exactly "1". See the perf_sample parsing code.
189*6dbdd20aSAndroid Build Coastguard Worker   Node root_{MakeRootFrame(), ++next_callstack_id_};
190*6dbdd20aSAndroid Build Coastguard Worker };
191*6dbdd20aSAndroid Build Coastguard Worker 
192*6dbdd20aSAndroid Build Coastguard Worker }  // namespace profiling
193*6dbdd20aSAndroid Build Coastguard Worker }  // namespace perfetto
194*6dbdd20aSAndroid Build Coastguard Worker 
195*6dbdd20aSAndroid Build Coastguard Worker template <>
196*6dbdd20aSAndroid Build Coastguard Worker struct std::hash<::perfetto::profiling::Mapping> {
197*6dbdd20aSAndroid Build Coastguard Worker   using argument_type = ::perfetto::profiling::Mapping;
198*6dbdd20aSAndroid Build Coastguard Worker   using result_type = size_t;
199*6dbdd20aSAndroid Build Coastguard Worker   result_type operator()(const argument_type& mapping) {
200*6dbdd20aSAndroid Build Coastguard Worker     size_t h =
201*6dbdd20aSAndroid Build Coastguard Worker         std::hash<::perfetto::profiling::InternID>{}(mapping.build_id.id());
202*6dbdd20aSAndroid Build Coastguard Worker     h ^= std::hash<uint64_t>{}(mapping.exact_offset);
203*6dbdd20aSAndroid Build Coastguard Worker     h ^= std::hash<uint64_t>{}(mapping.start_offset);
204*6dbdd20aSAndroid Build Coastguard Worker     h ^= std::hash<uint64_t>{}(mapping.start);
205*6dbdd20aSAndroid Build Coastguard Worker     h ^= std::hash<uint64_t>{}(mapping.end);
206*6dbdd20aSAndroid Build Coastguard Worker     h ^= std::hash<uint64_t>{}(mapping.load_bias);
207*6dbdd20aSAndroid Build Coastguard Worker     for (const auto& path : mapping.path_components)
208*6dbdd20aSAndroid Build Coastguard Worker       h ^= std::hash<uint64_t>{}(path.id());
209*6dbdd20aSAndroid Build Coastguard Worker     return h;
210*6dbdd20aSAndroid Build Coastguard Worker   }
211*6dbdd20aSAndroid Build Coastguard Worker };
212*6dbdd20aSAndroid Build Coastguard Worker 
213*6dbdd20aSAndroid Build Coastguard Worker template <>
214*6dbdd20aSAndroid Build Coastguard Worker struct std::hash<::perfetto::profiling::Frame> {
215*6dbdd20aSAndroid Build Coastguard Worker   using argument_type = ::perfetto::profiling::Frame;
216*6dbdd20aSAndroid Build Coastguard Worker   using result_type = size_t;
217*6dbdd20aSAndroid Build Coastguard Worker   result_type operator()(const argument_type& frame) {
218*6dbdd20aSAndroid Build Coastguard Worker     size_t h = std::hash<::perfetto::profiling::InternID>{}(frame.mapping.id());
219*6dbdd20aSAndroid Build Coastguard Worker     h ^= std::hash<::perfetto::profiling::InternID>{}(frame.function_name.id());
220*6dbdd20aSAndroid Build Coastguard Worker     h ^= std::hash<uint64_t>{}(frame.rel_pc);
221*6dbdd20aSAndroid Build Coastguard Worker     return h;
222*6dbdd20aSAndroid Build Coastguard Worker   }
223*6dbdd20aSAndroid Build Coastguard Worker };
224*6dbdd20aSAndroid Build Coastguard Worker 
225*6dbdd20aSAndroid Build Coastguard Worker #endif  // SRC_PROFILING_COMMON_CALLSTACK_TRIE_H_
226