xref: /aosp_15_r20/system/extras/simpleperf/CallChainJoiner.h (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker  * Copyright (C) 2017 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker  *
4*288bf522SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker  *
8*288bf522SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker  *
10*288bf522SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker  * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker  */
16*288bf522SAndroid Build Coastguard Worker 
17*288bf522SAndroid Build Coastguard Worker #ifndef SIMPLE_PERF_CALLCHAIN_JOINER_H_
18*288bf522SAndroid Build Coastguard Worker #define SIMPLE_PERF_CALLCHAIN_JOINER_H_
19*288bf522SAndroid Build Coastguard Worker 
20*288bf522SAndroid Build Coastguard Worker #include <inttypes.h>
21*288bf522SAndroid Build Coastguard Worker #include <stdio.h>
22*288bf522SAndroid Build Coastguard Worker #include <unistd.h>
23*288bf522SAndroid Build Coastguard Worker 
24*288bf522SAndroid Build Coastguard Worker #include <unordered_set>
25*288bf522SAndroid Build Coastguard Worker #include <vector>
26*288bf522SAndroid Build Coastguard Worker 
27*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
28*288bf522SAndroid Build Coastguard Worker 
29*288bf522SAndroid Build Coastguard Worker namespace call_chain_joiner_impl {
30*288bf522SAndroid Build Coastguard Worker 
31*288bf522SAndroid Build Coastguard Worker // Each node represents a function in a call chain, having a tuple info (tid, ip, sp).
32*288bf522SAndroid Build Coastguard Worker // A node remembers its parent in the call chain.
33*288bf522SAndroid Build Coastguard Worker struct CacheNode {
34*288bf522SAndroid Build Coastguard Worker   uint64_t ip;
35*288bf522SAndroid Build Coastguard Worker   uint64_t sp;
36*288bf522SAndroid Build Coastguard Worker   uint32_t tid;
37*288bf522SAndroid Build Coastguard Worker   // Whether the node is at the bottom of a call chain.
38*288bf522SAndroid Build Coastguard Worker   uint32_t is_leaf : 1;
39*288bf522SAndroid Build Coastguard Worker   // The index of the parent node in a call chain.
40*288bf522SAndroid Build Coastguard Worker   uint32_t parent_index : 31;
41*288bf522SAndroid Build Coastguard Worker   // When is_leaf = false, children_count remembers how many nodes have current node as parent.
42*288bf522SAndroid Build Coastguard Worker   // When is_leaf = true, leaf_link_prev and leaf_link_next keeps current node in a LRU linked list.
43*288bf522SAndroid Build Coastguard Worker   union {
44*288bf522SAndroid Build Coastguard Worker     uint32_t children_count;
45*288bf522SAndroid Build Coastguard Worker     struct {
46*288bf522SAndroid Build Coastguard Worker       uint32_t leaf_link_prev;
47*288bf522SAndroid Build Coastguard Worker       uint32_t leaf_link_next;
48*288bf522SAndroid Build Coastguard Worker     };
49*288bf522SAndroid Build Coastguard Worker   };
50*288bf522SAndroid Build Coastguard Worker };
51*288bf522SAndroid Build Coastguard Worker 
52*288bf522SAndroid Build Coastguard Worker static_assert(sizeof(CacheNode) == 32, "");
53*288bf522SAndroid Build Coastguard Worker 
54*288bf522SAndroid Build Coastguard Worker struct LRUCacheStat {
55*288bf522SAndroid Build Coastguard Worker   size_t cache_size = 0u;
56*288bf522SAndroid Build Coastguard Worker   size_t matched_node_count_to_extend_callchain = 0u;
57*288bf522SAndroid Build Coastguard Worker   size_t max_node_count = 0u;
58*288bf522SAndroid Build Coastguard Worker   size_t used_node_count = 0u;
59*288bf522SAndroid Build Coastguard Worker   size_t recycled_node_count = 0u;
60*288bf522SAndroid Build Coastguard Worker };
61*288bf522SAndroid Build Coastguard Worker 
62*288bf522SAndroid Build Coastguard Worker // LRUCache caches call chains in memory, and supports extending a call chain if the (tid, ip, sp)
63*288bf522SAndroid Build Coastguard Worker // tuples of its top [matched_node_count_to_extend_callchain] appear in the cache.
64*288bf522SAndroid Build Coastguard Worker class LRUCache {
65*288bf522SAndroid Build Coastguard Worker  public:
66*288bf522SAndroid Build Coastguard Worker   // cache_size is the bytes of memory we want to use in this cache.
67*288bf522SAndroid Build Coastguard Worker   // matched_node_count_to_extend_callchain decides how many nodes we need to match to extend a
68*288bf522SAndroid Build Coastguard Worker   // call chain. Higher value means more strict.
69*288bf522SAndroid Build Coastguard Worker   LRUCache(size_t cache_size = 8 * 1024 * 1024, size_t matched_node_count_to_extend_callchain = 1);
70*288bf522SAndroid Build Coastguard Worker   ~LRUCache();
71*288bf522SAndroid Build Coastguard Worker 
72*288bf522SAndroid Build Coastguard Worker   // Add a call chain in the cache, and extend it if possible.
73*288bf522SAndroid Build Coastguard Worker   void AddCallChain(pid_t tid, std::vector<uint64_t>& ips, std::vector<uint64_t>& sps);
74*288bf522SAndroid Build Coastguard Worker 
Stat()75*288bf522SAndroid Build Coastguard Worker   const LRUCacheStat& Stat() { return cache_stat_; }
76*288bf522SAndroid Build Coastguard Worker 
FindNode(uint32_t tid,uint64_t ip,uint64_t sp)77*288bf522SAndroid Build Coastguard Worker   CacheNode* FindNode(uint32_t tid, uint64_t ip, uint64_t sp) {
78*288bf522SAndroid Build Coastguard Worker     CacheNode key;
79*288bf522SAndroid Build Coastguard Worker     key.tid = tid;
80*288bf522SAndroid Build Coastguard Worker     key.ip = ip;
81*288bf522SAndroid Build Coastguard Worker     key.sp = sp;
82*288bf522SAndroid Build Coastguard Worker     auto it = node_set_.find(&key);
83*288bf522SAndroid Build Coastguard Worker     return it != node_set_.end() ? *it : nullptr;
84*288bf522SAndroid Build Coastguard Worker   }
85*288bf522SAndroid Build Coastguard Worker 
86*288bf522SAndroid Build Coastguard Worker  private:
87*288bf522SAndroid Build Coastguard Worker   static bool CacheNodeEqual(const CacheNode* n1, const CacheNode* n2);
88*288bf522SAndroid Build Coastguard Worker   static size_t CacheNodeHash(const CacheNode* n);
89*288bf522SAndroid Build Coastguard Worker 
90*288bf522SAndroid Build Coastguard Worker   typedef std::unordered_set<CacheNode*, decltype(&CacheNodeHash), decltype(&CacheNodeEqual)>
91*288bf522SAndroid Build Coastguard Worker       set_type;
92*288bf522SAndroid Build Coastguard Worker 
GetParent(CacheNode * node)93*288bf522SAndroid Build Coastguard Worker   CacheNode* GetParent(CacheNode* node) {
94*288bf522SAndroid Build Coastguard Worker     return node->parent_index == 0u ? nullptr : nodes_ + node->parent_index;
95*288bf522SAndroid Build Coastguard Worker   }
96*288bf522SAndroid Build Coastguard Worker 
GetNodeIndex(CacheNode * node)97*288bf522SAndroid Build Coastguard Worker   int GetNodeIndex(CacheNode* node) { return node - nodes_; }
98*288bf522SAndroid Build Coastguard Worker 
RemoveNodeFromLRUList(CacheNode * node)99*288bf522SAndroid Build Coastguard Worker   void RemoveNodeFromLRUList(CacheNode* node) {
100*288bf522SAndroid Build Coastguard Worker     CacheNode* prev = &nodes_[node->leaf_link_prev];
101*288bf522SAndroid Build Coastguard Worker     CacheNode* next = &nodes_[node->leaf_link_next];
102*288bf522SAndroid Build Coastguard Worker     prev->leaf_link_next = node->leaf_link_next;
103*288bf522SAndroid Build Coastguard Worker     next->leaf_link_prev = node->leaf_link_prev;
104*288bf522SAndroid Build Coastguard Worker   }
105*288bf522SAndroid Build Coastguard Worker 
AppendNodeToLRUList(CacheNode * node)106*288bf522SAndroid Build Coastguard Worker   void AppendNodeToLRUList(CacheNode* node) {
107*288bf522SAndroid Build Coastguard Worker     CacheNode* next = &nodes_[0];
108*288bf522SAndroid Build Coastguard Worker     CacheNode* prev = &nodes_[next->leaf_link_prev];
109*288bf522SAndroid Build Coastguard Worker     node->leaf_link_next = 0;
110*288bf522SAndroid Build Coastguard Worker     node->leaf_link_prev = next->leaf_link_prev;
111*288bf522SAndroid Build Coastguard Worker     next->leaf_link_prev = prev->leaf_link_next = GetNodeIndex(node);
112*288bf522SAndroid Build Coastguard Worker   }
113*288bf522SAndroid Build Coastguard Worker 
DecreaseChildCountOfNode(CacheNode * node)114*288bf522SAndroid Build Coastguard Worker   void DecreaseChildCountOfNode(CacheNode* node) {
115*288bf522SAndroid Build Coastguard Worker     if (--node->children_count == 0u) {
116*288bf522SAndroid Build Coastguard Worker       node->is_leaf = true;
117*288bf522SAndroid Build Coastguard Worker       AppendNodeToLRUList(node);
118*288bf522SAndroid Build Coastguard Worker     }
119*288bf522SAndroid Build Coastguard Worker   }
120*288bf522SAndroid Build Coastguard Worker 
121*288bf522SAndroid Build Coastguard Worker   CacheNode* GetNode(uint32_t tid, uint64_t ip, uint64_t sp);
122*288bf522SAndroid Build Coastguard Worker   CacheNode* AllocNode();
123*288bf522SAndroid Build Coastguard Worker   void LinkParent(CacheNode* child, CacheNode* new_parent);
124*288bf522SAndroid Build Coastguard Worker   void UnlinkParent(CacheNode* child);
125*288bf522SAndroid Build Coastguard Worker 
126*288bf522SAndroid Build Coastguard Worker   CacheNode* nodes_;
127*288bf522SAndroid Build Coastguard Worker   set_type node_set_;
128*288bf522SAndroid Build Coastguard Worker   LRUCacheStat cache_stat_;
129*288bf522SAndroid Build Coastguard Worker };
130*288bf522SAndroid Build Coastguard Worker 
131*288bf522SAndroid Build Coastguard Worker }  // namespace call_chain_joiner_impl
132*288bf522SAndroid Build Coastguard Worker 
133*288bf522SAndroid Build Coastguard Worker // CallChainJoiner is used to join callchains of samples in the same thread, in order to get
134*288bf522SAndroid Build Coastguard Worker // complete call graph. For example, if we have two samples for a thread:
135*288bf522SAndroid Build Coastguard Worker //   sample 1: (ip A, sp A) -> (ip B, sp B) -> (ip C, sp C) -> ...
136*288bf522SAndroid Build Coastguard Worker //   sample 2:                 (ip B, sp B) -> (ip C, sp C) -> ...
137*288bf522SAndroid Build Coastguard Worker // Because we know (ip A, sp A) calls (ip B, sp B) in sample 1, then we can guess the (ip B, sp B)
138*288bf522SAndroid Build Coastguard Worker // in sample 2 is also called from (ip A, sp A). So we can add (ip A, sp A) in sample 2 as below.
139*288bf522SAndroid Build Coastguard Worker //   sample 2: (ip A, sp A) -> (ip B, sp B) -> (ip C, sp C) -> ...
140*288bf522SAndroid Build Coastguard Worker class CallChainJoiner {
141*288bf522SAndroid Build Coastguard Worker  public:
142*288bf522SAndroid Build Coastguard Worker   // The parameters are used in LRUCache.
143*288bf522SAndroid Build Coastguard Worker   CallChainJoiner(size_t cache_size, size_t matched_node_count_to_extend_callchain,
144*288bf522SAndroid Build Coastguard Worker                   bool keep_original_callchains);
145*288bf522SAndroid Build Coastguard Worker   ~CallChainJoiner();
146*288bf522SAndroid Build Coastguard Worker 
147*288bf522SAndroid Build Coastguard Worker   enum ChainType {
148*288bf522SAndroid Build Coastguard Worker     ORIGINAL_OFFLINE,
149*288bf522SAndroid Build Coastguard Worker     ORIGINAL_REMOTE,
150*288bf522SAndroid Build Coastguard Worker     JOINED_OFFLINE,
151*288bf522SAndroid Build Coastguard Worker     JOINED_REMOTE,
152*288bf522SAndroid Build Coastguard Worker   };
153*288bf522SAndroid Build Coastguard Worker 
154*288bf522SAndroid Build Coastguard Worker   bool AddCallChain(pid_t pid, pid_t tid, ChainType type, const std::vector<uint64_t>& ips,
155*288bf522SAndroid Build Coastguard Worker                     const std::vector<uint64_t>& sps);
156*288bf522SAndroid Build Coastguard Worker   bool JoinCallChains();
157*288bf522SAndroid Build Coastguard Worker   bool GetNextCallChain(pid_t& pid, pid_t& tid, ChainType& type, std::vector<uint64_t>& ips,
158*288bf522SAndroid Build Coastguard Worker                         std::vector<uint64_t>& sps);
159*288bf522SAndroid Build Coastguard Worker 
160*288bf522SAndroid Build Coastguard Worker   struct Stat {
161*288bf522SAndroid Build Coastguard Worker     size_t chain_count = 0u;
162*288bf522SAndroid Build Coastguard Worker     size_t before_join_node_count = 0u;
163*288bf522SAndroid Build Coastguard Worker     size_t after_join_node_count = 0u;
164*288bf522SAndroid Build Coastguard Worker     size_t after_join_max_chain_length = 0u;
165*288bf522SAndroid Build Coastguard Worker   };
166*288bf522SAndroid Build Coastguard Worker   void DumpStat();
GetStat()167*288bf522SAndroid Build Coastguard Worker   const Stat& GetStat() { return stat_; }
GetCacheStat()168*288bf522SAndroid Build Coastguard Worker   const call_chain_joiner_impl::LRUCacheStat& GetCacheStat() { return cache_stat_; }
169*288bf522SAndroid Build Coastguard Worker 
170*288bf522SAndroid Build Coastguard Worker  private:
171*288bf522SAndroid Build Coastguard Worker   bool keep_original_callchains_;
172*288bf522SAndroid Build Coastguard Worker   FILE* original_chains_fp_;
173*288bf522SAndroid Build Coastguard Worker   FILE* joined_chains_fp_;
174*288bf522SAndroid Build Coastguard Worker   size_t next_chain_index_;
175*288bf522SAndroid Build Coastguard Worker   call_chain_joiner_impl::LRUCacheStat cache_stat_;
176*288bf522SAndroid Build Coastguard Worker   Stat stat_;
177*288bf522SAndroid Build Coastguard Worker };
178*288bf522SAndroid Build Coastguard Worker 
179*288bf522SAndroid Build Coastguard Worker }  // namespace simpleperf
180*288bf522SAndroid Build Coastguard Worker 
181*288bf522SAndroid Build Coastguard Worker #endif  // SIMPLE_PERF_CALLCHAIN_JOINER_H_
182