• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SIMPLE_PERF_SAMPLE_TREE_H_
18 #define SIMPLE_PERF_SAMPLE_TREE_H_
19 
20 #include <unordered_map>
21 
22 #include "callchain.h"
23 #include "OfflineUnwinder.h"
24 #include "perf_regs.h"
25 #include "record.h"
26 #include "SampleComparator.h"
27 #include "SampleDisplayer.h"
28 #include "thread_tree.h"
29 
30 using namespace simpleperf;
31 
32 // A SampleTree is a collection of samples. A profiling report is mainly about
33 // constructing a SampleTree and display it. There are three steps involved:
34 // build the tree, sort the tree, and display it. For example, if we want to
35 // show how many cpu-cycles are spent in different functions, we should do as
36 // follows:
37 // 1. Build a SampleTree from SampleRecords with each sample containing
38 //    (cpu-cycles, function name). When building the tree, we should merge
39 //    samples containing the same function name.
40 // 2. Sort the SampleTree by cpu-cycles in the sample. As we want to display the
41 //    samples in a decreasing order of cpu-cycles, we should sort it like this.
42 // 3. Display the SampleTree, each sample prints its (cpu-cycles, function name)
43 //    pair.
44 //
45 // We represent the three steps with three template classes.
46 // 1. A SampleTree is built by SampleTreeBuilder. The comparator passed in
47 //    SampleTreeBuilder's constructor decides the property of samples should be
48 //    merged together.
49 // 2. After a SampleTree is built and got from SampleTreeBuilder, it should be
50 //    sorted by SampleTreeSorter. The sort result decides the order to show
51 //    samples.
52 // 3. At last, the sorted SampleTree is passed to SampleTreeDisplayer, which
53 //    displays each sample in the SampleTree.
54 
55 template <typename EntryT, typename AccumulateInfoT>
56 class SampleTreeBuilder {
57  public:
SampleTreeBuilder(const SampleComparator<EntryT> & comparator)58   explicit SampleTreeBuilder(const SampleComparator<EntryT>& comparator)
59       : sample_set_(comparator),
60         accumulate_callchain_(false),
61         sample_comparator_(comparator),
62         callchain_sample_set_(comparator),
63         use_branch_address_(false),
64         build_callchain_(false),
65         use_caller_as_callchain_root_(false) {}
66 
~SampleTreeBuilder()67   virtual ~SampleTreeBuilder() {}
68 
SetBranchSampleOption(bool use_branch_address)69   void SetBranchSampleOption(bool use_branch_address) {
70     use_branch_address_ = use_branch_address;
71   }
72 
SetCallChainSampleOptions(bool accumulate_callchain,bool build_callchain,bool use_caller_as_callchain_root)73   void SetCallChainSampleOptions(bool accumulate_callchain,
74                                  bool build_callchain,
75                                  bool use_caller_as_callchain_root) {
76     accumulate_callchain_ = accumulate_callchain;
77     build_callchain_ = build_callchain;
78     use_caller_as_callchain_root_ = use_caller_as_callchain_root;
79     if (accumulate_callchain_) {
80       offline_unwinder_.reset(new OfflineUnwinder(false));
81     }
82   }
83 
ProcessSampleRecord(const SampleRecord & r)84   void ProcessSampleRecord(const SampleRecord& r) {
85     if (use_branch_address_ && (r.sample_type & PERF_SAMPLE_BRANCH_STACK)) {
86       for (uint64_t i = 0; i < r.branch_stack_data.stack_nr; ++i) {
87         auto& item = r.branch_stack_data.stack[i];
88         if (item.from != 0 && item.to != 0) {
89           CreateBranchSample(r, item);
90         }
91       }
92       return;
93     }
94     bool in_kernel = r.InKernel();
95     AccumulateInfoT acc_info;
96     EntryT* sample = CreateSample(r, in_kernel, &acc_info);
97     if (sample == nullptr) {
98       return;
99     }
100     if (accumulate_callchain_) {
101       std::vector<uint64_t> ips;
102       if (r.sample_type & PERF_SAMPLE_CALLCHAIN) {
103         ips.insert(ips.end(), r.callchain_data.ips,
104                    r.callchain_data.ips + r.callchain_data.ip_nr);
105       }
106       const ThreadEntry* thread = GetThreadOfSample(sample);
107       // Use stack_user_data.data.size() instead of stack_user_data.dyn_size, to
108       // make up for the missing kernel patch in N9. See b/22612370.
109       if (thread != nullptr && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
110           (r.regs_user_data.reg_mask != 0) &&
111           (r.sample_type & PERF_SAMPLE_STACK_USER) &&
112           (r.GetValidStackSize() > 0)) {
113         RegSet regs(r.regs_user_data.abi, r.regs_user_data.reg_mask, r.regs_user_data.regs);
114         std::vector<uint64_t> user_ips;
115         std::vector<uint64_t> sps;
116         if (offline_unwinder_->UnwindCallChain(*thread, regs, r.stack_user_data.data,
117                                                r.GetValidStackSize(), &user_ips, &sps)) {
118           ips.push_back(PERF_CONTEXT_USER);
119           ips.insert(ips.end(), user_ips.begin(), user_ips.end());
120         }
121       }
122 
123       std::vector<EntryT*> callchain;
124       callchain.push_back(sample);
125 
126       bool first_ip = true;
127       for (auto& ip : ips) {
128         if (ip >= PERF_CONTEXT_MAX) {
129           switch (ip) {
130             case PERF_CONTEXT_KERNEL:
131               in_kernel = true;
132               break;
133             case PERF_CONTEXT_USER:
134               in_kernel = false;
135               break;
136             default:
137               LOG(DEBUG) << "Unexpected perf_context in callchain: " << ip;
138           }
139         } else {
140           if (first_ip) {
141             first_ip = false;
142             // Remove duplication with sampled ip.
143             if (ip == r.ip_data.ip) {
144               continue;
145             }
146           }
147           EntryT* callchain_sample =
148               CreateCallChainSample(sample, ip, in_kernel, callchain, acc_info);
149           if (callchain_sample == nullptr) {
150             break;
151           }
152           callchain.push_back(callchain_sample);
153         }
154       }
155 
156       if (build_callchain_) {
157         std::set<EntryT*> added_set;
158         if (use_caller_as_callchain_root_) {
159           std::reverse(callchain.begin(), callchain.end());
160         }
161         EntryT* parent = nullptr;
162         while (callchain.size() >= 2) {
163           EntryT* sample = callchain[0];
164           callchain.erase(callchain.begin());
165           // Add only once for recursive calls on callchain.
166           if (added_set.find(sample) != added_set.end()) {
167             continue;
168           }
169           added_set.insert(sample);
170           InsertCallChainForSample(sample, callchain, acc_info);
171           UpdateCallChainParentInfo(sample, parent);
172           parent = sample;
173         }
174       }
175     }
176   }
177 
GetSamples()178   std::vector<EntryT*> GetSamples() const {
179     std::vector<EntryT*> result;
180     for (auto& entry : sample_set_) {
181       result.push_back(entry);
182     }
183     return result;
184   }
185 
186  protected:
187   virtual EntryT* CreateSample(const SampleRecord& r, bool in_kernel,
188                                AccumulateInfoT* acc_info) = 0;
189   virtual EntryT* CreateBranchSample(const SampleRecord& r,
190                                      const BranchStackItemType& item) = 0;
191   virtual EntryT* CreateCallChainSample(const EntryT* sample, uint64_t ip,
192                                         bool in_kernel,
193                                         const std::vector<EntryT*>& callchain,
194                                         const AccumulateInfoT& acc_info) = 0;
195   virtual const ThreadEntry* GetThreadOfSample(EntryT*) = 0;
196   virtual uint64_t GetPeriodForCallChain(const AccumulateInfoT& acc_info) = 0;
FilterSample(const EntryT *)197   virtual bool FilterSample(const EntryT*) { return true; }
198 
UpdateSummary(const EntryT *)199   virtual void UpdateSummary(const EntryT*) {}
200 
201   virtual void MergeSample(EntryT* sample1, EntryT* sample2) = 0;
202 
InsertSample(std::unique_ptr<EntryT> sample)203   EntryT* InsertSample(std::unique_ptr<EntryT> sample) {
204     if (sample == nullptr || !FilterSample(sample.get())) {
205       return nullptr;
206     }
207     UpdateSummary(sample.get());
208     EntryT* result;
209     auto it = sample_set_.find(sample.get());
210     if (it == sample_set_.end()) {
211       result = sample.get();
212       sample_set_.insert(sample.get());
213       sample_storage_.push_back(std::move(sample));
214     } else {
215       result = *it;
216       MergeSample(*it, sample.get());
217     }
218     return result;
219   }
220 
InsertCallChainSample(std::unique_ptr<EntryT> sample,const std::vector<EntryT * > & callchain)221   EntryT* InsertCallChainSample(std::unique_ptr<EntryT> sample,
222                                 const std::vector<EntryT*>& callchain) {
223     if (sample == nullptr) {
224       return nullptr;
225     }
226     if (!FilterSample(sample.get())) {
227       // Store in callchain_sample_set_ for use in other EntryT's callchain.
228       auto it = callchain_sample_set_.find(sample.get());
229       if (it != callchain_sample_set_.end()) {
230         return *it;
231       }
232       EntryT* result = sample.get();
233       callchain_sample_set_.insert(sample.get());
234       sample_storage_.push_back(std::move(sample));
235       return result;
236     }
237 
238     auto it = sample_set_.find(sample.get());
239     if (it != sample_set_.end()) {
240       EntryT* sample = *it;
241       // Process only once for recursive function call.
242       if (std::find(callchain.begin(), callchain.end(), sample) !=
243           callchain.end()) {
244         return sample;
245       }
246     }
247     return InsertSample(std::move(sample));
248   }
249 
InsertCallChainForSample(EntryT * sample,const std::vector<EntryT * > & callchain,const AccumulateInfoT & acc_info)250   void InsertCallChainForSample(EntryT* sample,
251                                 const std::vector<EntryT*>& callchain,
252                                 const AccumulateInfoT& acc_info) {
253     uint64_t period = GetPeriodForCallChain(acc_info);
254     sample->callchain.AddCallChain(
255         callchain, period, [&](const EntryT* s1, const EntryT* s2) {
256           return sample_comparator_.IsSameSample(s1, s2);
257         });
258   }
259 
AddCallChainDuplicateInfo()260   void AddCallChainDuplicateInfo() {
261     if (build_callchain_) {
262       for (EntryT* sample : sample_set_) {
263         auto it = callchain_parent_map_.find(sample);
264         if (it != callchain_parent_map_.end() && !it->second.has_multiple_parents) {
265           sample->callchain.duplicated = true;
266         }
267       }
268     }
269   }
270 
271   std::set<EntryT*, SampleComparator<EntryT>> sample_set_;
272   bool accumulate_callchain_;
273 
274  private:
UpdateCallChainParentInfo(EntryT * sample,EntryT * parent)275   void UpdateCallChainParentInfo(EntryT* sample, EntryT* parent) {
276     if (parent == nullptr) {
277       return;
278     }
279     auto it = callchain_parent_map_.find(sample);
280     if (it == callchain_parent_map_.end()) {
281       CallChainParentInfo info;
282       info.parent = parent;
283       info.has_multiple_parents = false;
284       callchain_parent_map_[sample] = info;
285     } else if (it->second.parent != parent) {
286       it->second.has_multiple_parents = true;
287     }
288   }
289 
290   const SampleComparator<EntryT> sample_comparator_;
291   // If a CallChainSample is filtered out, it is stored in callchain_sample_set_
292   // and only used in other EntryT's callchain.
293   std::set<EntryT*, SampleComparator<EntryT>> callchain_sample_set_;
294   std::vector<std::unique_ptr<EntryT>> sample_storage_;
295 
296   struct CallChainParentInfo {
297     EntryT* parent;
298     bool has_multiple_parents;
299   };
300   std::unordered_map<EntryT*, CallChainParentInfo> callchain_parent_map_;
301 
302   bool use_branch_address_;
303   bool build_callchain_;
304   bool use_caller_as_callchain_root_;
305   std::unique_ptr<OfflineUnwinder> offline_unwinder_;
306 };
307 
308 template <typename EntryT>
309 class SampleTreeSorter {
310  public:
SampleTreeSorter(SampleComparator<EntryT> comparator)311   explicit SampleTreeSorter(SampleComparator<EntryT> comparator)
312       : comparator_(comparator) {}
313 
~SampleTreeSorter()314   virtual ~SampleTreeSorter() {}
315 
Sort(std::vector<EntryT * > & v,bool sort_callchain)316   void Sort(std::vector<EntryT*>& v, bool sort_callchain) {
317     if (sort_callchain) {
318       for (auto& sample : v) {
319         SortCallChain(sample);
320       }
321     }
322     if (!comparator_.empty()) {
323       std::sort(v.begin(), v.end(), [this](const EntryT* s1, const EntryT* s2) {
324         return comparator_(s1, s2);
325       });
326     }
327   }
328 
329  protected:
SortCallChain(EntryT * sample)330   void SortCallChain(EntryT* sample) { sample->callchain.SortByPeriod(); }
331 
332  private:
333   SampleComparator<EntryT> comparator_;
334 };
335 
336 template <typename EntryT, typename InfoT>
337 class SampleTreeDisplayer {
338  public:
SampleTreeDisplayer(SampleDisplayer<EntryT,InfoT> displayer)339   explicit SampleTreeDisplayer(SampleDisplayer<EntryT, InfoT> displayer)
340       : displayer_(displayer) {}
341 
~SampleTreeDisplayer()342   virtual ~SampleTreeDisplayer() {}
343 
DisplaySamples(FILE * fp,const std::vector<EntryT * > & samples,const InfoT * info)344   void DisplaySamples(FILE* fp, const std::vector<EntryT*>& samples,
345                       const InfoT* info) {
346     displayer_.SetInfo(info);
347     for (const auto& sample : samples) {
348       displayer_.AdjustWidth(sample);
349     }
350     displayer_.PrintNames(fp);
351     for (const auto& sample : samples) {
352       displayer_.PrintSample(fp, sample);
353     }
354   }
355 
356  private:
357   SampleDisplayer<EntryT, InfoT> displayer_;
358 };
359 
360 #endif  // SIMPLE_PERF_SAMPLE_TREE_H_
361