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