xref: /aosp_15_r20/system/extras/simpleperf/CallChainJoiner.cpp (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 #include "CallChainJoiner.h"
18*288bf522SAndroid Build Coastguard Worker 
19*288bf522SAndroid Build Coastguard Worker #include <android-base/logging.h>
20*288bf522SAndroid Build Coastguard Worker 
21*288bf522SAndroid Build Coastguard Worker #include "environment.h"
22*288bf522SAndroid Build Coastguard Worker #include "utils.h"
23*288bf522SAndroid Build Coastguard Worker 
24*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
25*288bf522SAndroid Build Coastguard Worker namespace call_chain_joiner_impl {
26*288bf522SAndroid Build Coastguard Worker 
LRUCache(size_t cache_size,size_t matched_node_count_to_extend_callchain)27*288bf522SAndroid Build Coastguard Worker LRUCache::LRUCache(size_t cache_size, size_t matched_node_count_to_extend_callchain)
28*288bf522SAndroid Build Coastguard Worker     : node_set_(1024 * 16, CacheNodeHash, CacheNodeEqual) {
29*288bf522SAndroid Build Coastguard Worker   cache_stat_.cache_size = cache_size;
30*288bf522SAndroid Build Coastguard Worker   cache_stat_.max_node_count = cache_size / sizeof(CacheNode);
31*288bf522SAndroid Build Coastguard Worker   CHECK_GE(cache_stat_.max_node_count, 2u);
32*288bf522SAndroid Build Coastguard Worker   CHECK_GE(matched_node_count_to_extend_callchain, 1u);
33*288bf522SAndroid Build Coastguard Worker   cache_stat_.matched_node_count_to_extend_callchain = matched_node_count_to_extend_callchain;
34*288bf522SAndroid Build Coastguard Worker   nodes_ = new CacheNode[cache_stat_.max_node_count + 1];  // with 1 sentinel node
35*288bf522SAndroid Build Coastguard Worker   // nodes_[0] is the sentinel node of the LRU linked list.
36*288bf522SAndroid Build Coastguard Worker   nodes_[0].is_leaf = 1;
37*288bf522SAndroid Build Coastguard Worker   nodes_[0].parent_index = 0;
38*288bf522SAndroid Build Coastguard Worker   nodes_[0].leaf_link_prev = nodes_[0].leaf_link_next = 0;
39*288bf522SAndroid Build Coastguard Worker }
40*288bf522SAndroid Build Coastguard Worker 
~LRUCache()41*288bf522SAndroid Build Coastguard Worker LRUCache::~LRUCache() {
42*288bf522SAndroid Build Coastguard Worker   delete[] nodes_;
43*288bf522SAndroid Build Coastguard Worker }
44*288bf522SAndroid Build Coastguard Worker 
AddCallChain(pid_t tid,std::vector<uint64_t> & ips,std::vector<uint64_t> & sps)45*288bf522SAndroid Build Coastguard Worker void LRUCache::AddCallChain(pid_t tid, std::vector<uint64_t>& ips, std::vector<uint64_t>& sps) {
46*288bf522SAndroid Build Coastguard Worker   // 1. Build current call chain.
47*288bf522SAndroid Build Coastguard Worker   std::vector<CacheNode*> chain;
48*288bf522SAndroid Build Coastguard Worker   for (size_t i = 0; i < ips.size(); ++i) {
49*288bf522SAndroid Build Coastguard Worker     CacheNode* node = GetNode(tid, ips[i], sps[i]);
50*288bf522SAndroid Build Coastguard Worker     chain.push_back(node);
51*288bf522SAndroid Build Coastguard Worker   }
52*288bf522SAndroid Build Coastguard Worker 
53*288bf522SAndroid Build Coastguard Worker   // 2. Check if the (tid, ip, sp) tuples of the top [matched_node_count_to_extend_callchain]
54*288bf522SAndroid Build Coastguard Worker   // nodes of current call chain exist in the cache and have the same relation as in current
55*288bf522SAndroid Build Coastguard Worker   // call chain. If true, we can extend current call chain.
56*288bf522SAndroid Build Coastguard Worker   bool can_extend = true;
57*288bf522SAndroid Build Coastguard Worker   if (chain.size() < cache_stat_.matched_node_count_to_extend_callchain) {
58*288bf522SAndroid Build Coastguard Worker     can_extend = false;
59*288bf522SAndroid Build Coastguard Worker   } else {
60*288bf522SAndroid Build Coastguard Worker     size_t chain_pos = chain.size() - 2;
61*288bf522SAndroid Build Coastguard Worker     for (size_t i = 1; i < cache_stat_.matched_node_count_to_extend_callchain; ++i) {
62*288bf522SAndroid Build Coastguard Worker       if (GetParent(chain[chain_pos]) != chain[chain_pos + 1]) {
63*288bf522SAndroid Build Coastguard Worker         can_extend = false;
64*288bf522SAndroid Build Coastguard Worker         break;
65*288bf522SAndroid Build Coastguard Worker       }
66*288bf522SAndroid Build Coastguard Worker     }
67*288bf522SAndroid Build Coastguard Worker   }
68*288bf522SAndroid Build Coastguard Worker   std::vector<uint64_t> origin_ip = ips;
69*288bf522SAndroid Build Coastguard Worker 
70*288bf522SAndroid Build Coastguard Worker   // 3. Add current call chain to the cache.
71*288bf522SAndroid Build Coastguard Worker   for (size_t i = 0; i + 1 < chain.size(); ++i) {
72*288bf522SAndroid Build Coastguard Worker     LinkParent(chain[i], chain[i + 1]);
73*288bf522SAndroid Build Coastguard Worker   }
74*288bf522SAndroid Build Coastguard Worker   // 4. Optionally extend current call chain.
75*288bf522SAndroid Build Coastguard Worker   if (can_extend) {
76*288bf522SAndroid Build Coastguard Worker     CacheNode* top = chain.back();
77*288bf522SAndroid Build Coastguard Worker     while ((top = GetParent(top)) != nullptr) {
78*288bf522SAndroid Build Coastguard Worker       if (top->sp == chain.back()->sp) {
79*288bf522SAndroid Build Coastguard Worker         // This is to prevent a loop case as below, which is unlikely to happen.
80*288bf522SAndroid Build Coastguard Worker         // The cache has node A.parent = B, while current call chain has node B.parent = A.
81*288bf522SAndroid Build Coastguard Worker         // This makes a loop of A -> B -> A -> B...
82*288bf522SAndroid Build Coastguard Worker         bool has_loop = false;
83*288bf522SAndroid Build Coastguard Worker         for (auto it = chain.rbegin(); it != chain.rend() && (*it)->sp == top->sp; ++it) {
84*288bf522SAndroid Build Coastguard Worker           if ((*it)->ip == top->ip) {
85*288bf522SAndroid Build Coastguard Worker             has_loop = true;
86*288bf522SAndroid Build Coastguard Worker             break;
87*288bf522SAndroid Build Coastguard Worker           }
88*288bf522SAndroid Build Coastguard Worker         }
89*288bf522SAndroid Build Coastguard Worker         if (has_loop) {
90*288bf522SAndroid Build Coastguard Worker           UnlinkParent(chain.back());
91*288bf522SAndroid Build Coastguard Worker           ips.resize(chain.size());
92*288bf522SAndroid Build Coastguard Worker           sps.resize(chain.size());
93*288bf522SAndroid Build Coastguard Worker           break;
94*288bf522SAndroid Build Coastguard Worker         }
95*288bf522SAndroid Build Coastguard Worker       }
96*288bf522SAndroid Build Coastguard Worker       ips.push_back(top->ip);
97*288bf522SAndroid Build Coastguard Worker       sps.push_back(top->sp);
98*288bf522SAndroid Build Coastguard Worker     }
99*288bf522SAndroid Build Coastguard Worker   } else {
100*288bf522SAndroid Build Coastguard Worker     UnlinkParent(chain.back());
101*288bf522SAndroid Build Coastguard Worker   }
102*288bf522SAndroid Build Coastguard Worker }
103*288bf522SAndroid Build Coastguard Worker 
CacheNodeEqual(const CacheNode * n1,const CacheNode * n2)104*288bf522SAndroid Build Coastguard Worker bool LRUCache::CacheNodeEqual(const CacheNode* n1, const CacheNode* n2) {
105*288bf522SAndroid Build Coastguard Worker   return n1->tid == n2->tid && n1->ip == n2->ip && n1->sp == n2->sp;
106*288bf522SAndroid Build Coastguard Worker }
107*288bf522SAndroid Build Coastguard Worker 
CacheNodeHash(const CacheNode * n)108*288bf522SAndroid Build Coastguard Worker size_t LRUCache::CacheNodeHash(const CacheNode* n) {
109*288bf522SAndroid Build Coastguard Worker   return static_cast<size_t>(n->tid ^ n->ip ^ n->sp);
110*288bf522SAndroid Build Coastguard Worker }
111*288bf522SAndroid Build Coastguard Worker 
GetNode(uint32_t tid,uint64_t ip,uint64_t sp)112*288bf522SAndroid Build Coastguard Worker CacheNode* LRUCache::GetNode(uint32_t tid, uint64_t ip, uint64_t sp) {
113*288bf522SAndroid Build Coastguard Worker   CacheNode* node = FindNode(tid, ip, sp);
114*288bf522SAndroid Build Coastguard Worker   if (node != nullptr) {
115*288bf522SAndroid Build Coastguard Worker     if (node->is_leaf) {
116*288bf522SAndroid Build Coastguard Worker       // Update the node's position in the LRU linked list.
117*288bf522SAndroid Build Coastguard Worker       RemoveNodeFromLRUList(node);
118*288bf522SAndroid Build Coastguard Worker       AppendNodeToLRUList(node);
119*288bf522SAndroid Build Coastguard Worker     }
120*288bf522SAndroid Build Coastguard Worker     return node;
121*288bf522SAndroid Build Coastguard Worker   }
122*288bf522SAndroid Build Coastguard Worker   node = AllocNode();
123*288bf522SAndroid Build Coastguard Worker   node->tid = tid;
124*288bf522SAndroid Build Coastguard Worker   node->ip = ip;
125*288bf522SAndroid Build Coastguard Worker   node->sp = sp;
126*288bf522SAndroid Build Coastguard Worker   node->is_leaf = 1;
127*288bf522SAndroid Build Coastguard Worker   node->parent_index = 0;
128*288bf522SAndroid Build Coastguard Worker   node->leaf_link_prev = node->leaf_link_next = GetNodeIndex(node);
129*288bf522SAndroid Build Coastguard Worker   node_set_.insert(node);
130*288bf522SAndroid Build Coastguard Worker   AppendNodeToLRUList(node);
131*288bf522SAndroid Build Coastguard Worker   return node;
132*288bf522SAndroid Build Coastguard Worker }
133*288bf522SAndroid Build Coastguard Worker 
AllocNode()134*288bf522SAndroid Build Coastguard Worker CacheNode* LRUCache::AllocNode() {
135*288bf522SAndroid Build Coastguard Worker   if (cache_stat_.used_node_count < cache_stat_.max_node_count) {
136*288bf522SAndroid Build Coastguard Worker     return &nodes_[++cache_stat_.used_node_count];
137*288bf522SAndroid Build Coastguard Worker   }
138*288bf522SAndroid Build Coastguard Worker   // Recycle the node at the front of the LRU linked list.
139*288bf522SAndroid Build Coastguard Worker   CacheNode* node = &nodes_[nodes_->leaf_link_next];
140*288bf522SAndroid Build Coastguard Worker   RemoveNodeFromLRUList(node);
141*288bf522SAndroid Build Coastguard Worker   node_set_.erase(node);
142*288bf522SAndroid Build Coastguard Worker   CacheNode* parent = GetParent(node);
143*288bf522SAndroid Build Coastguard Worker   if (parent != nullptr) {
144*288bf522SAndroid Build Coastguard Worker     DecreaseChildCountOfNode(parent);
145*288bf522SAndroid Build Coastguard Worker   }
146*288bf522SAndroid Build Coastguard Worker   cache_stat_.recycled_node_count++;
147*288bf522SAndroid Build Coastguard Worker   return node;
148*288bf522SAndroid Build Coastguard Worker }
149*288bf522SAndroid Build Coastguard Worker 
LinkParent(CacheNode * child,CacheNode * new_parent)150*288bf522SAndroid Build Coastguard Worker void LRUCache::LinkParent(CacheNode* child, CacheNode* new_parent) {
151*288bf522SAndroid Build Coastguard Worker   CacheNode* old_parent = GetParent(child);
152*288bf522SAndroid Build Coastguard Worker   if (old_parent != nullptr) {
153*288bf522SAndroid Build Coastguard Worker     DecreaseChildCountOfNode(old_parent);
154*288bf522SAndroid Build Coastguard Worker   }
155*288bf522SAndroid Build Coastguard Worker   child->parent_index = GetNodeIndex(new_parent);
156*288bf522SAndroid Build Coastguard Worker   if (new_parent->is_leaf) {
157*288bf522SAndroid Build Coastguard Worker     RemoveNodeFromLRUList(new_parent);
158*288bf522SAndroid Build Coastguard Worker     new_parent->is_leaf = 0;
159*288bf522SAndroid Build Coastguard Worker     new_parent->children_count = 1;
160*288bf522SAndroid Build Coastguard Worker   } else {
161*288bf522SAndroid Build Coastguard Worker     new_parent->children_count++;
162*288bf522SAndroid Build Coastguard Worker   }
163*288bf522SAndroid Build Coastguard Worker }
164*288bf522SAndroid Build Coastguard Worker 
UnlinkParent(CacheNode * child)165*288bf522SAndroid Build Coastguard Worker void LRUCache::UnlinkParent(CacheNode* child) {
166*288bf522SAndroid Build Coastguard Worker   CacheNode* old_parent = GetParent(child);
167*288bf522SAndroid Build Coastguard Worker   if (old_parent != nullptr) {
168*288bf522SAndroid Build Coastguard Worker     DecreaseChildCountOfNode(old_parent);
169*288bf522SAndroid Build Coastguard Worker   }
170*288bf522SAndroid Build Coastguard Worker   child->parent_index = 0;
171*288bf522SAndroid Build Coastguard Worker }
172*288bf522SAndroid Build Coastguard Worker 
173*288bf522SAndroid Build Coastguard Worker }  // namespace call_chain_joiner_impl
174*288bf522SAndroid Build Coastguard Worker 
175*288bf522SAndroid Build Coastguard Worker using namespace call_chain_joiner_impl;
176*288bf522SAndroid Build Coastguard Worker 
WriteCallChain(FILE * fp,pid_t pid,pid_t tid,CallChainJoiner::ChainType type,const std::vector<uint64_t> & ips,const std::vector<uint64_t> & sps,size_t ip_count)177*288bf522SAndroid Build Coastguard Worker static bool WriteCallChain(FILE* fp, pid_t pid, pid_t tid, CallChainJoiner::ChainType type,
178*288bf522SAndroid Build Coastguard Worker                            const std::vector<uint64_t>& ips, const std::vector<uint64_t>& sps,
179*288bf522SAndroid Build Coastguard Worker                            size_t ip_count) {
180*288bf522SAndroid Build Coastguard Worker   // Below is the content of a call chain stored in file.
181*288bf522SAndroid Build Coastguard Worker   //   uint32_t pid;
182*288bf522SAndroid Build Coastguard Worker   //   uint32_t tid;
183*288bf522SAndroid Build Coastguard Worker   //   uint32_t chain_type;
184*288bf522SAndroid Build Coastguard Worker   //   uint32_t ip_count;
185*288bf522SAndroid Build Coastguard Worker   //   uint64_t ips[];
186*288bf522SAndroid Build Coastguard Worker   //   uint64_t sps[];
187*288bf522SAndroid Build Coastguard Worker   //   uint32_t size;
188*288bf522SAndroid Build Coastguard Worker   uint32_t size = 4 * sizeof(uint32_t) + sizeof(uint64_t) * ip_count * 2 + sizeof(uint32_t);
189*288bf522SAndroid Build Coastguard Worker   std::vector<char> data(size);
190*288bf522SAndroid Build Coastguard Worker   char* p = data.data();
191*288bf522SAndroid Build Coastguard Worker   MoveToBinaryFormat(pid, p);
192*288bf522SAndroid Build Coastguard Worker   MoveToBinaryFormat(tid, p);
193*288bf522SAndroid Build Coastguard Worker   MoveToBinaryFormat(type, p);
194*288bf522SAndroid Build Coastguard Worker   MoveToBinaryFormat(static_cast<uint32_t>(ip_count), p);
195*288bf522SAndroid Build Coastguard Worker   MoveToBinaryFormat(ips.data(), ip_count, p);
196*288bf522SAndroid Build Coastguard Worker   MoveToBinaryFormat(sps.data(), ip_count, p);
197*288bf522SAndroid Build Coastguard Worker   MoveToBinaryFormat(size, p);
198*288bf522SAndroid Build Coastguard Worker   if (fwrite(data.data(), size, 1, fp) != 1) {
199*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fwrite";
200*288bf522SAndroid Build Coastguard Worker     return false;
201*288bf522SAndroid Build Coastguard Worker   }
202*288bf522SAndroid Build Coastguard Worker   return true;
203*288bf522SAndroid Build Coastguard Worker }
204*288bf522SAndroid Build Coastguard Worker 
ReadCallChain(FILE * fp,pid_t & pid,pid_t & tid,CallChainJoiner::ChainType & type,std::vector<uint64_t> & ips,std::vector<uint64_t> & sps)205*288bf522SAndroid Build Coastguard Worker static bool ReadCallChain(FILE* fp, pid_t& pid, pid_t& tid, CallChainJoiner::ChainType& type,
206*288bf522SAndroid Build Coastguard Worker                           std::vector<uint64_t>& ips, std::vector<uint64_t>& sps) {
207*288bf522SAndroid Build Coastguard Worker   std::vector<char> data(4 * sizeof(uint32_t));
208*288bf522SAndroid Build Coastguard Worker   if (fread(data.data(), data.size(), 1, fp) != 1) {
209*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fread";
210*288bf522SAndroid Build Coastguard Worker     return false;
211*288bf522SAndroid Build Coastguard Worker   }
212*288bf522SAndroid Build Coastguard Worker   const char* p = data.data();
213*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(pid, p);
214*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(tid, p);
215*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(type, p);
216*288bf522SAndroid Build Coastguard Worker   uint32_t ip_count;
217*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(ip_count, p);
218*288bf522SAndroid Build Coastguard Worker   data.resize(sizeof(uint64_t) * ip_count * 2 + sizeof(uint32_t));
219*288bf522SAndroid Build Coastguard Worker   if (fread(data.data(), data.size(), 1, fp) != 1) {
220*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fread";
221*288bf522SAndroid Build Coastguard Worker     return false;
222*288bf522SAndroid Build Coastguard Worker   }
223*288bf522SAndroid Build Coastguard Worker   p = data.data();
224*288bf522SAndroid Build Coastguard Worker   ips.resize(ip_count);
225*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(ips.data(), ip_count, p);
226*288bf522SAndroid Build Coastguard Worker   sps.resize(ip_count);
227*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(sps.data(), ip_count, p);
228*288bf522SAndroid Build Coastguard Worker   return true;
229*288bf522SAndroid Build Coastguard Worker }
230*288bf522SAndroid Build Coastguard Worker 
ReadCallChainInReverseOrder(FILE * fp,pid_t & pid,pid_t & tid,CallChainJoiner::ChainType & type,std::vector<uint64_t> & ips,std::vector<uint64_t> & sps)231*288bf522SAndroid Build Coastguard Worker static bool ReadCallChainInReverseOrder(FILE* fp, pid_t& pid, pid_t& tid,
232*288bf522SAndroid Build Coastguard Worker                                         CallChainJoiner::ChainType& type,
233*288bf522SAndroid Build Coastguard Worker                                         std::vector<uint64_t>& ips, std::vector<uint64_t>& sps) {
234*288bf522SAndroid Build Coastguard Worker   uint32_t size;
235*288bf522SAndroid Build Coastguard Worker   if (fseek(fp, -4, SEEK_CUR) != 0 || fread(&size, sizeof(size), 1, fp) != 1) {
236*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fread";
237*288bf522SAndroid Build Coastguard Worker     return false;
238*288bf522SAndroid Build Coastguard Worker   }
239*288bf522SAndroid Build Coastguard Worker   std::vector<char> data(size - 4);
240*288bf522SAndroid Build Coastguard Worker   if (fseek(fp, -static_cast<int>(size), SEEK_CUR) != 0 ||
241*288bf522SAndroid Build Coastguard Worker       fread(data.data(), data.size(), 1, fp) != 1 ||
242*288bf522SAndroid Build Coastguard Worker       fseek(fp, -static_cast<int>(data.size()), SEEK_CUR) != 0) {
243*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fread";
244*288bf522SAndroid Build Coastguard Worker     return false;
245*288bf522SAndroid Build Coastguard Worker   }
246*288bf522SAndroid Build Coastguard Worker   const char* p = data.data();
247*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(pid, p);
248*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(tid, p);
249*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(type, p);
250*288bf522SAndroid Build Coastguard Worker   uint32_t ip_count;
251*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(ip_count, p);
252*288bf522SAndroid Build Coastguard Worker   ips.resize(ip_count);
253*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(ips.data(), ip_count, p);
254*288bf522SAndroid Build Coastguard Worker   sps.resize(ip_count);
255*288bf522SAndroid Build Coastguard Worker   MoveFromBinaryFormat(sps.data(), ip_count, p);
256*288bf522SAndroid Build Coastguard Worker   return true;
257*288bf522SAndroid Build Coastguard Worker }
258*288bf522SAndroid Build Coastguard Worker 
CreateTempFp()259*288bf522SAndroid Build Coastguard Worker static FILE* CreateTempFp() {
260*288bf522SAndroid Build Coastguard Worker   std::unique_ptr<TemporaryFile> tmpfile = ScopedTempFiles::CreateTempFile();
261*288bf522SAndroid Build Coastguard Worker   FILE* fp = fdopen(tmpfile->release(), "web+");
262*288bf522SAndroid Build Coastguard Worker   if (fp == nullptr) {
263*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fdopen";
264*288bf522SAndroid Build Coastguard Worker     return nullptr;
265*288bf522SAndroid Build Coastguard Worker   }
266*288bf522SAndroid Build Coastguard Worker   return fp;
267*288bf522SAndroid Build Coastguard Worker }
268*288bf522SAndroid Build Coastguard Worker 
CallChainJoiner(size_t cache_size,size_t matched_node_count_to_extend_callchain,bool keep_original_callchains)269*288bf522SAndroid Build Coastguard Worker CallChainJoiner::CallChainJoiner(size_t cache_size, size_t matched_node_count_to_extend_callchain,
270*288bf522SAndroid Build Coastguard Worker                                  bool keep_original_callchains)
271*288bf522SAndroid Build Coastguard Worker     : keep_original_callchains_(keep_original_callchains),
272*288bf522SAndroid Build Coastguard Worker       original_chains_fp_(nullptr),
273*288bf522SAndroid Build Coastguard Worker       joined_chains_fp_(nullptr),
274*288bf522SAndroid Build Coastguard Worker       next_chain_index_(0u) {
275*288bf522SAndroid Build Coastguard Worker   cache_stat_.cache_size = cache_size;
276*288bf522SAndroid Build Coastguard Worker   cache_stat_.matched_node_count_to_extend_callchain = matched_node_count_to_extend_callchain;
277*288bf522SAndroid Build Coastguard Worker }
278*288bf522SAndroid Build Coastguard Worker 
~CallChainJoiner()279*288bf522SAndroid Build Coastguard Worker CallChainJoiner::~CallChainJoiner() {
280*288bf522SAndroid Build Coastguard Worker   if (original_chains_fp_ != nullptr) {
281*288bf522SAndroid Build Coastguard Worker     fclose(original_chains_fp_);
282*288bf522SAndroid Build Coastguard Worker   }
283*288bf522SAndroid Build Coastguard Worker   if (joined_chains_fp_ != nullptr) {
284*288bf522SAndroid Build Coastguard Worker     fclose(joined_chains_fp_);
285*288bf522SAndroid Build Coastguard Worker   }
286*288bf522SAndroid Build Coastguard Worker }
287*288bf522SAndroid Build Coastguard Worker 
AddCallChain(pid_t pid,pid_t tid,ChainType type,const std::vector<uint64_t> & ips,const std::vector<uint64_t> & sps)288*288bf522SAndroid Build Coastguard Worker bool CallChainJoiner::AddCallChain(pid_t pid, pid_t tid, ChainType type,
289*288bf522SAndroid Build Coastguard Worker                                    const std::vector<uint64_t>& ips,
290*288bf522SAndroid Build Coastguard Worker                                    const std::vector<uint64_t>& sps) {
291*288bf522SAndroid Build Coastguard Worker   CHECK_EQ(ips.size(), sps.size());
292*288bf522SAndroid Build Coastguard Worker   CHECK_GT(ips.size(), 0u);
293*288bf522SAndroid Build Coastguard Worker   CHECK(type == ORIGINAL_OFFLINE || type == ORIGINAL_REMOTE);
294*288bf522SAndroid Build Coastguard Worker   // Make sure sps are in non-decreasing order, and there is no duplicated items.
295*288bf522SAndroid Build Coastguard Worker   size_t ip_count = ips.size();
296*288bf522SAndroid Build Coastguard Worker   for (size_t i = 1; i < ips.size(); ++i) {
297*288bf522SAndroid Build Coastguard Worker     if (sps[i] < sps[i - 1]) {
298*288bf522SAndroid Build Coastguard Worker       ip_count = i;
299*288bf522SAndroid Build Coastguard Worker       break;
300*288bf522SAndroid Build Coastguard Worker     } else if (sps[i] == sps[i - 1]) {
301*288bf522SAndroid Build Coastguard Worker       bool has_duplicated_items = false;
302*288bf522SAndroid Build Coastguard Worker       for (size_t j = i; j > 0 && sps[j - 1] == sps[i]; --j) {
303*288bf522SAndroid Build Coastguard Worker         if (ips[j - 1] == ips[i]) {
304*288bf522SAndroid Build Coastguard Worker           has_duplicated_items = true;
305*288bf522SAndroid Build Coastguard Worker           break;
306*288bf522SAndroid Build Coastguard Worker         }
307*288bf522SAndroid Build Coastguard Worker       }
308*288bf522SAndroid Build Coastguard Worker       if (has_duplicated_items) {
309*288bf522SAndroid Build Coastguard Worker         ip_count = i;
310*288bf522SAndroid Build Coastguard Worker         break;
311*288bf522SAndroid Build Coastguard Worker       }
312*288bf522SAndroid Build Coastguard Worker     }
313*288bf522SAndroid Build Coastguard Worker   }
314*288bf522SAndroid Build Coastguard Worker 
315*288bf522SAndroid Build Coastguard Worker   if (original_chains_fp_ == nullptr) {
316*288bf522SAndroid Build Coastguard Worker     original_chains_fp_ = CreateTempFp();
317*288bf522SAndroid Build Coastguard Worker     if (original_chains_fp_ == nullptr) {
318*288bf522SAndroid Build Coastguard Worker       return false;
319*288bf522SAndroid Build Coastguard Worker     }
320*288bf522SAndroid Build Coastguard Worker   }
321*288bf522SAndroid Build Coastguard Worker   stat_.chain_count++;
322*288bf522SAndroid Build Coastguard Worker   return WriteCallChain(original_chains_fp_, pid, tid, type, ips, sps, ip_count);
323*288bf522SAndroid Build Coastguard Worker }
324*288bf522SAndroid Build Coastguard Worker 
JoinCallChains()325*288bf522SAndroid Build Coastguard Worker bool CallChainJoiner::JoinCallChains() {
326*288bf522SAndroid Build Coastguard Worker   if (stat_.chain_count == 0u) {
327*288bf522SAndroid Build Coastguard Worker     return true;
328*288bf522SAndroid Build Coastguard Worker   }
329*288bf522SAndroid Build Coastguard Worker   LRUCache cache(cache_stat_.cache_size, cache_stat_.matched_node_count_to_extend_callchain);
330*288bf522SAndroid Build Coastguard Worker   std::unique_ptr<FILE, decltype(&fclose)> tmp_fp(CreateTempFp(), fclose);
331*288bf522SAndroid Build Coastguard Worker   if (!tmp_fp) {
332*288bf522SAndroid Build Coastguard Worker     return false;
333*288bf522SAndroid Build Coastguard Worker   }
334*288bf522SAndroid Build Coastguard Worker   joined_chains_fp_ = CreateTempFp();
335*288bf522SAndroid Build Coastguard Worker   if (joined_chains_fp_ == nullptr) {
336*288bf522SAndroid Build Coastguard Worker     return false;
337*288bf522SAndroid Build Coastguard Worker   }
338*288bf522SAndroid Build Coastguard Worker   pid_t pid;
339*288bf522SAndroid Build Coastguard Worker   pid_t tid;
340*288bf522SAndroid Build Coastguard Worker   ChainType type;
341*288bf522SAndroid Build Coastguard Worker   std::vector<uint64_t> ips;
342*288bf522SAndroid Build Coastguard Worker   std::vector<uint64_t> sps;
343*288bf522SAndroid Build Coastguard Worker   if (fseek(original_chains_fp_, 0, SEEK_END) != 0) {
344*288bf522SAndroid Build Coastguard Worker     PLOG(ERROR) << "fseek";
345*288bf522SAndroid Build Coastguard Worker     return false;
346*288bf522SAndroid Build Coastguard Worker   }
347*288bf522SAndroid Build Coastguard Worker   std::vector<std::pair<FILE*, FILE*>> file_pairs = {
348*288bf522SAndroid Build Coastguard Worker       std::make_pair(original_chains_fp_, tmp_fp.get()),
349*288bf522SAndroid Build Coastguard Worker       std::make_pair(tmp_fp.get(), joined_chains_fp_)};
350*288bf522SAndroid Build Coastguard Worker   for (size_t pass = 0; pass < 2u; ++pass) {
351*288bf522SAndroid Build Coastguard Worker     auto& pair = file_pairs[pass];
352*288bf522SAndroid Build Coastguard Worker     for (size_t i = 0; i < stat_.chain_count; ++i) {
353*288bf522SAndroid Build Coastguard Worker       if (!ReadCallChainInReverseOrder(pair.first, pid, tid, type, ips, sps)) {
354*288bf522SAndroid Build Coastguard Worker         return false;
355*288bf522SAndroid Build Coastguard Worker       }
356*288bf522SAndroid Build Coastguard Worker       if (pass == 0u) {
357*288bf522SAndroid Build Coastguard Worker         if (type == ORIGINAL_OFFLINE) {
358*288bf522SAndroid Build Coastguard Worker           type = JOINED_OFFLINE;
359*288bf522SAndroid Build Coastguard Worker         } else if (type == ORIGINAL_REMOTE) {
360*288bf522SAndroid Build Coastguard Worker           type = JOINED_REMOTE;
361*288bf522SAndroid Build Coastguard Worker         }
362*288bf522SAndroid Build Coastguard Worker         stat_.before_join_node_count += ips.size();
363*288bf522SAndroid Build Coastguard Worker       }
364*288bf522SAndroid Build Coastguard Worker 
365*288bf522SAndroid Build Coastguard Worker       cache.AddCallChain(tid, ips, sps);
366*288bf522SAndroid Build Coastguard Worker 
367*288bf522SAndroid Build Coastguard Worker       if (pass == 1u) {
368*288bf522SAndroid Build Coastguard Worker         stat_.after_join_node_count += ips.size();
369*288bf522SAndroid Build Coastguard Worker         stat_.after_join_max_chain_length = std::max(stat_.after_join_max_chain_length, ips.size());
370*288bf522SAndroid Build Coastguard Worker       }
371*288bf522SAndroid Build Coastguard Worker 
372*288bf522SAndroid Build Coastguard Worker       if (!WriteCallChain(pair.second, pid, tid, type, ips, sps, ips.size())) {
373*288bf522SAndroid Build Coastguard Worker         return false;
374*288bf522SAndroid Build Coastguard Worker       }
375*288bf522SAndroid Build Coastguard Worker     }
376*288bf522SAndroid Build Coastguard Worker   }
377*288bf522SAndroid Build Coastguard Worker   cache_stat_ = cache.Stat();
378*288bf522SAndroid Build Coastguard Worker   return true;
379*288bf522SAndroid Build Coastguard Worker }
380*288bf522SAndroid Build Coastguard Worker 
GetNextCallChain(pid_t & pid,pid_t & tid,ChainType & type,std::vector<uint64_t> & ips,std::vector<uint64_t> & sps)381*288bf522SAndroid Build Coastguard Worker bool CallChainJoiner::GetNextCallChain(pid_t& pid, pid_t& tid, ChainType& type,
382*288bf522SAndroid Build Coastguard Worker                                        std::vector<uint64_t>& ips, std::vector<uint64_t>& sps) {
383*288bf522SAndroid Build Coastguard Worker   if (next_chain_index_ == stat_.chain_count * 2) {
384*288bf522SAndroid Build Coastguard Worker     // No more chains.
385*288bf522SAndroid Build Coastguard Worker     return false;
386*288bf522SAndroid Build Coastguard Worker   }
387*288bf522SAndroid Build Coastguard Worker   if (next_chain_index_ == 0u) {
388*288bf522SAndroid Build Coastguard Worker     if (fseek(original_chains_fp_, 0, SEEK_SET) != 0 ||
389*288bf522SAndroid Build Coastguard Worker         fseek(joined_chains_fp_, 0, SEEK_SET) != 0) {
390*288bf522SAndroid Build Coastguard Worker       PLOG(ERROR) << "fseek";
391*288bf522SAndroid Build Coastguard Worker       return false;
392*288bf522SAndroid Build Coastguard Worker     }
393*288bf522SAndroid Build Coastguard Worker   }
394*288bf522SAndroid Build Coastguard Worker   FILE* fp;
395*288bf522SAndroid Build Coastguard Worker   if (keep_original_callchains_) {
396*288bf522SAndroid Build Coastguard Worker     fp = (next_chain_index_ & 1) ? joined_chains_fp_ : original_chains_fp_;
397*288bf522SAndroid Build Coastguard Worker     next_chain_index_++;
398*288bf522SAndroid Build Coastguard Worker   } else {
399*288bf522SAndroid Build Coastguard Worker     fp = joined_chains_fp_;
400*288bf522SAndroid Build Coastguard Worker     next_chain_index_ += 2;
401*288bf522SAndroid Build Coastguard Worker   }
402*288bf522SAndroid Build Coastguard Worker   return ReadCallChain(fp, pid, tid, type, ips, sps);
403*288bf522SAndroid Build Coastguard Worker }
404*288bf522SAndroid Build Coastguard Worker 
DumpStat()405*288bf522SAndroid Build Coastguard Worker void CallChainJoiner::DumpStat() {
406*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "call chain joiner stat:";
407*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  cache_size: " << cache_stat_.cache_size;
408*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  matched_node_count_to_extend_callchain: "
409*288bf522SAndroid Build Coastguard Worker              << cache_stat_.matched_node_count_to_extend_callchain;
410*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  max_node_count in cache: " << cache_stat_.max_node_count;
411*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  used_node_count in cache: " << cache_stat_.used_node_count;
412*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  recycled_node_count in cache: " << cache_stat_.recycled_node_count;
413*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  call_chain_count: " << stat_.chain_count;
414*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  before_join_node_count: " << stat_.before_join_node_count;
415*288bf522SAndroid Build Coastguard Worker   if (stat_.chain_count > 0u) {
416*288bf522SAndroid Build Coastguard Worker     LOG(DEBUG) << "  before_join_average_chain_length: "
417*288bf522SAndroid Build Coastguard Worker                << (stat_.before_join_node_count * 1.0 / stat_.chain_count);
418*288bf522SAndroid Build Coastguard Worker   }
419*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  after_join_node_count: " << stat_.after_join_node_count;
420*288bf522SAndroid Build Coastguard Worker   if (stat_.chain_count > 0u) {
421*288bf522SAndroid Build Coastguard Worker     LOG(DEBUG) << "  after_join_average_chain_length: "
422*288bf522SAndroid Build Coastguard Worker                << (stat_.after_join_node_count * 1.0 / stat_.chain_count);
423*288bf522SAndroid Build Coastguard Worker   }
424*288bf522SAndroid Build Coastguard Worker   LOG(DEBUG) << "  after_join_max_chain_length: " << stat_.after_join_max_chain_length;
425*288bf522SAndroid Build Coastguard Worker }
426*288bf522SAndroid Build Coastguard Worker 
427*288bf522SAndroid Build Coastguard Worker }  // namespace simpleperf
428