123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- /*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #ifndef SIMPLE_PERF_CALLCHAIN_JOINER_H_
- #define SIMPLE_PERF_CALLCHAIN_JOINER_H_
- #include <inttypes.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <unordered_set>
- #include <vector>
- namespace simpleperf {
- namespace call_chain_joiner_impl {
- // Each node represents a function in a call chain, having a tuple info (tid, ip, sp).
- // A node remembers its parent in the call chain.
- struct CacheNode {
- uint64_t ip;
- uint64_t sp;
- uint32_t tid;
- // Whether the node is at the bottom of a call chain.
- uint32_t is_leaf : 1;
- // The index of the parent node in a call chain.
- uint32_t parent_index : 31;
- // When is_leaf = false, children_count remembers how many nodes have current node as parent.
- // When is_leaf = true, leaf_link_prev and leaf_link_next keeps current node in a LRU linked list.
- union {
- uint32_t children_count;
- struct {
- uint32_t leaf_link_prev;
- uint32_t leaf_link_next;
- };
- };
- };
- static_assert(sizeof(CacheNode) == 32, "");
- struct LRUCacheStat {
- size_t cache_size = 0u;
- size_t matched_node_count_to_extend_callchain = 0u;
- size_t max_node_count = 0u;
- size_t used_node_count = 0u;
- size_t recycled_node_count = 0u;
- };
- // LRUCache caches call chains in memory, and supports extending a call chain if the (tid, ip, sp)
- // tuples of its top [matched_node_count_to_extend_callchain] appear in the cache.
- class LRUCache {
- public:
- // cache_size is the bytes of memory we want to use in this cache.
- // matched_node_count_to_extend_callchain decides how many nodes we need to match to extend a
- // call chain. Higher value means more strict.
- LRUCache(size_t cache_size = 8 * 1024 * 1024, size_t matched_node_count_to_extend_callchain = 1);
- ~LRUCache();
- // Add a call chain in the cache, and extend it if possible.
- void AddCallChain(pid_t tid, std::vector<uint64_t>& ips, std::vector<uint64_t>& sps);
- const LRUCacheStat& Stat() {
- return cache_stat_;
- }
- CacheNode* FindNode(uint32_t tid, uint64_t ip, uint64_t sp) {
- CacheNode key;
- key.tid = tid;
- key.ip = ip;
- key.sp = sp;
- auto it = node_set_.find(&key);
- return it != node_set_.end() ? *it : nullptr;
- }
- private:
- static bool CacheNodeEqual(const CacheNode* n1, const CacheNode* n2);
- static size_t CacheNodeHash(const CacheNode* n);
- typedef std::unordered_set<CacheNode*, decltype(&CacheNodeHash), decltype(&CacheNodeEqual)>
- set_type;
- CacheNode* GetParent(CacheNode* node) {
- return node->parent_index == 0u ? nullptr : nodes_ + node->parent_index;
- }
- int GetNodeIndex(CacheNode* node) {
- return node - nodes_;
- }
- void RemoveNodeFromLRUList(CacheNode* node) {
- CacheNode* prev = &nodes_[node->leaf_link_prev];
- CacheNode* next = &nodes_[node->leaf_link_next];
- prev->leaf_link_next = node->leaf_link_next;
- next->leaf_link_prev = node->leaf_link_prev;
- }
- void AppendNodeToLRUList(CacheNode* node) {
- CacheNode* next = &nodes_[0];
- CacheNode* prev = &nodes_[next->leaf_link_prev];
- node->leaf_link_next = 0;
- node->leaf_link_prev = next->leaf_link_prev;
- next->leaf_link_prev = prev->leaf_link_next = GetNodeIndex(node);
- }
- void DecreaseChildCountOfNode(CacheNode* node) {
- if (--node->children_count == 0u) {
- node->is_leaf = true;
- AppendNodeToLRUList(node);
- }
- }
- CacheNode* GetNode(uint32_t tid, uint64_t ip, uint64_t sp);
- CacheNode* AllocNode();
- void LinkParent(CacheNode* child, CacheNode* new_parent);
- void UnlinkParent(CacheNode* child);
- CacheNode* nodes_;
- set_type node_set_;
- LRUCacheStat cache_stat_;
- };
- } // namespace call_chain_joiner_impl
- // CallChainJoiner is used to join callchains of samples in the same thread, in order to get
- // complete call graph. For example, if we have two samples for a thread:
- // sample 1: (ip A, sp A) -> (ip B, sp B) -> (ip C, sp C) -> ...
- // sample 2: (ip B, sp B) -> (ip C, sp C) -> ...
- // Because we know (ip A, sp A) calls (ip B, sp B) in sample 1, then we can guess the (ip B, sp B)
- // in sample 2 is also called from (ip A, sp A). So we can add (ip A, sp A) in sample 2 as below.
- // sample 2: (ip A, sp A) -> (ip B, sp B) -> (ip C, sp C) -> ...
- class CallChainJoiner {
- public:
- // The parameters are used in LRUCache.
- CallChainJoiner(size_t cache_size, size_t matched_node_count_to_extend_callchain,
- bool keep_original_callchains);
- ~CallChainJoiner();
- enum ChainType {
- ORIGINAL_OFFLINE,
- ORIGINAL_REMOTE,
- JOINED_OFFLINE,
- JOINED_REMOTE,
- };
- bool AddCallChain(pid_t pid, pid_t tid, ChainType type, const std::vector<uint64_t>& ips,
- const std::vector<uint64_t>& sps);
- bool JoinCallChains();
- bool GetNextCallChain(pid_t& pid, pid_t& tid, ChainType& type, std::vector<uint64_t>& ips,
- std::vector<uint64_t>& sps);
- struct Stat {
- size_t chain_count = 0u;
- size_t before_join_node_count = 0u;
- size_t after_join_node_count = 0u;
- size_t after_join_max_chain_length = 0u;
- };
- void DumpStat();
- const Stat& GetStat() {
- return stat_;
- }
- const call_chain_joiner_impl::LRUCacheStat& GetCacheStat() {
- return cache_stat_;
- }
- private:
- bool keep_original_callchains_;
- FILE* original_chains_fp_;
- FILE* joined_chains_fp_;
- size_t next_chain_index_;
- call_chain_joiner_impl::LRUCacheStat cache_stat_;
- Stat stat_;
- };
- } // namespace simpleperf
- #endif // SIMPLE_PERF_CALLCHAIN_JOINER_H_
|