blob: 43e3ba026b478faeac0acaeef9264e9f9467b582 [file] [log] [blame]
Yabin Cui2672dea2015-05-21 12:17:23 -07001/*
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
Yabin Cui0c093f32017-04-19 11:48:44 -070020#include <unordered_map>
21
Yabin Cui03381452017-12-13 11:31:53 -080022#include "OfflineUnwinder.h"
Yabin Cuib64a8632016-05-24 18:23:33 -070023#include "SampleComparator.h"
24#include "SampleDisplayer.h"
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020025#include "callchain.h"
26#include "perf_regs.h"
27#include "record.h"
Yabin Cui60a0ea92015-07-22 20:30:43 -070028#include "thread_tree.h"
Yabin Cui41d4ba92015-06-22 12:27:58 -070029
Yabin Cuifaa7b922021-01-11 17:35:57 -080030namespace simpleperf {
Yabin Cui03381452017-12-13 11:31:53 -080031
Yabin Cuib64a8632016-05-24 18:23:33 -070032// 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.
Yabin Cuiecb9a302015-07-01 10:00:52 -070054
Yabin Cuib64a8632016-05-24 18:23:33 -070055template <typename EntryT, typename AccumulateInfoT>
56class SampleTreeBuilder {
Yabin Cui2672dea2015-05-21 12:17:23 -070057 public:
Yabin Cui68b83832017-07-19 17:54:57 -070058 explicit SampleTreeBuilder(const SampleComparator<EntryT>& comparator)
Yabin Cuib64a8632016-05-24 18:23:33 -070059 : sample_set_(comparator),
Yabin Cui6965d422016-06-15 11:41:42 -070060 accumulate_callchain_(false),
Yabin Cui9970a232016-06-29 12:18:11 -070061 sample_comparator_(comparator),
Yabin Cui4e391de2021-11-02 13:28:24 -070062 filtered_sample_set_(comparator),
Yabin Cuib64a8632016-05-24 18:23:33 -070063 use_branch_address_(false),
Yabin Cuib64a8632016-05-24 18:23:33 -070064 build_callchain_(false),
Yabin Cui03381452017-12-13 11:31:53 -080065 use_caller_as_callchain_root_(false) {}
Yabin Cuib64a8632016-05-24 18:23:33 -070066
67 virtual ~SampleTreeBuilder() {}
68
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020069 void SetBranchSampleOption(bool use_branch_address) { use_branch_address_ = use_branch_address; }
Yabin Cuib47de4a2015-06-08 12:27:19 -070070
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020071 void SetCallChainSampleOptions(bool accumulate_callchain, bool build_callchain,
Yabin Cui6e75e1b2018-02-06 13:42:16 -080072 bool use_caller_as_callchain_root) {
Yabin Cuib64a8632016-05-24 18:23:33 -070073 accumulate_callchain_ = accumulate_callchain;
74 build_callchain_ = build_callchain;
75 use_caller_as_callchain_root_ = use_caller_as_callchain_root;
Yabin Cui03381452017-12-13 11:31:53 -080076 if (accumulate_callchain_) {
Yabin Cui5ac7a252019-07-18 10:43:32 -070077 offline_unwinder_ = OfflineUnwinder::Create(false);
Yabin Cui03381452017-12-13 11:31:53 -080078 }
Yabin Cui2672dea2015-05-21 12:17:23 -070079 }
80
Yabin Cui91784fd2020-01-29 17:08:49 -080081 OfflineUnwinder* GetUnwinder() { return offline_unwinder_.get(); }
82
Yabin Cuib64a8632016-05-24 18:23:33 -070083 void ProcessSampleRecord(const SampleRecord& r) {
84 if (use_branch_address_ && (r.sample_type & PERF_SAMPLE_BRANCH_STACK)) {
Yabin Cui190a8482016-08-04 10:22:17 -070085 for (uint64_t i = 0; i < r.branch_stack_data.stack_nr; ++i) {
86 auto& item = r.branch_stack_data.stack[i];
Yabin Cuib64a8632016-05-24 18:23:33 -070087 if (item.from != 0 && item.to != 0) {
88 CreateBranchSample(r, item);
89 }
90 }
91 return;
92 }
93 bool in_kernel = r.InKernel();
94 AccumulateInfoT acc_info;
95 EntryT* sample = CreateSample(r, in_kernel, &acc_info);
96 if (sample == nullptr) {
97 return;
98 }
99 if (accumulate_callchain_) {
100 std::vector<uint64_t> ips;
101 if (r.sample_type & PERF_SAMPLE_CALLCHAIN) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200102 ips.insert(ips.end(), r.callchain_data.ips, r.callchain_data.ips + r.callchain_data.ip_nr);
Yabin Cuib64a8632016-05-24 18:23:33 -0700103 }
104 const ThreadEntry* thread = GetThreadOfSample(sample);
105 // Use stack_user_data.data.size() instead of stack_user_data.dyn_size, to
106 // make up for the missing kernel patch in N9. See b/22612370.
107 if (thread != nullptr && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200108 (r.regs_user_data.reg_mask != 0) && (r.sample_type & PERF_SAMPLE_STACK_USER) &&
Yabin Cui190a8482016-08-04 10:22:17 -0700109 (r.GetValidStackSize() > 0)) {
Yabin Cui6e75e1b2018-02-06 13:42:16 -0800110 RegSet regs(r.regs_user_data.abi, r.regs_user_data.reg_mask, r.regs_user_data.regs);
Yabin Cui81a9d332017-12-10 13:09:07 -0800111 std::vector<uint64_t> user_ips;
112 std::vector<uint64_t> sps;
Yabin Cui6e75e1b2018-02-06 13:42:16 -0800113 if (offline_unwinder_->UnwindCallChain(*thread, regs, r.stack_user_data.data,
114 r.GetValidStackSize(), &user_ips, &sps)) {
Yabin Cuib64a8632016-05-24 18:23:33 -0700115 ips.push_back(PERF_CONTEXT_USER);
Yabin Cui81a9d332017-12-10 13:09:07 -0800116 ips.insert(ips.end(), user_ips.begin(), user_ips.end());
Yabin Cuib64a8632016-05-24 18:23:33 -0700117 }
118 }
119
120 std::vector<EntryT*> callchain;
121 callchain.push_back(sample);
122
123 bool first_ip = true;
124 for (auto& ip : ips) {
125 if (ip >= PERF_CONTEXT_MAX) {
126 switch (ip) {
127 case PERF_CONTEXT_KERNEL:
128 in_kernel = true;
129 break;
130 case PERF_CONTEXT_USER:
131 in_kernel = false;
132 break;
133 default:
134 LOG(DEBUG) << "Unexpected perf_context in callchain: " << ip;
135 }
136 } else {
137 if (first_ip) {
138 first_ip = false;
139 // Remove duplication with sampled ip.
140 if (ip == r.ip_data.ip) {
141 continue;
142 }
143 }
144 EntryT* callchain_sample =
Yabin Cui847f3fd2019-05-02 12:58:05 -0700145 CreateCallChainSample(thread, sample, ip, in_kernel, callchain, acc_info);
Yabin Cuib64a8632016-05-24 18:23:33 -0700146 if (callchain_sample == nullptr) {
147 break;
148 }
149 callchain.push_back(callchain_sample);
150 }
151 }
152
153 if (build_callchain_) {
154 std::set<EntryT*> added_set;
155 if (use_caller_as_callchain_root_) {
156 std::reverse(callchain.begin(), callchain.end());
157 }
Yabin Cui0c093f32017-04-19 11:48:44 -0700158 EntryT* parent = nullptr;
Yabin Cuib64a8632016-05-24 18:23:33 -0700159 while (callchain.size() >= 2) {
160 EntryT* sample = callchain[0];
161 callchain.erase(callchain.begin());
162 // Add only once for recursive calls on callchain.
163 if (added_set.find(sample) != added_set.end()) {
164 continue;
165 }
166 added_set.insert(sample);
167 InsertCallChainForSample(sample, callchain, acc_info);
Yabin Cui0c093f32017-04-19 11:48:44 -0700168 UpdateCallChainParentInfo(sample, parent);
169 parent = sample;
Yabin Cuib64a8632016-05-24 18:23:33 -0700170 }
171 }
172 }
173 }
174
175 std::vector<EntryT*> GetSamples() const {
176 std::vector<EntryT*> result;
177 for (auto& entry : sample_set_) {
178 result.push_back(entry);
179 }
180 return result;
181 }
182
183 protected:
184 virtual EntryT* CreateSample(const SampleRecord& r, bool in_kernel,
185 AccumulateInfoT* acc_info) = 0;
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200186 virtual EntryT* CreateBranchSample(const SampleRecord& r, const BranchStackItemType& item) = 0;
Yabin Cui847f3fd2019-05-02 12:58:05 -0700187 virtual EntryT* CreateCallChainSample(const ThreadEntry* thread, const EntryT* sample,
188 uint64_t ip, bool in_kernel,
Yabin Cuib64a8632016-05-24 18:23:33 -0700189 const std::vector<EntryT*>& callchain,
190 const AccumulateInfoT& acc_info) = 0;
191 virtual const ThreadEntry* GetThreadOfSample(EntryT*) = 0;
Yabin Cui9970a232016-06-29 12:18:11 -0700192 virtual uint64_t GetPeriodForCallChain(const AccumulateInfoT& acc_info) = 0;
Yabin Cuib64a8632016-05-24 18:23:33 -0700193 virtual bool FilterSample(const EntryT*) { return true; }
194
195 virtual void UpdateSummary(const EntryT*) {}
196
197 virtual void MergeSample(EntryT* sample1, EntryT* sample2) = 0;
198
199 EntryT* InsertSample(std::unique_ptr<EntryT> sample) {
Yabin Cui4e391de2021-11-02 13:28:24 -0700200 if (sample == nullptr) {
Yabin Cuib64a8632016-05-24 18:23:33 -0700201 return nullptr;
202 }
Yabin Cui4e391de2021-11-02 13:28:24 -0700203 if (!FilterSample(sample.get())) {
204 // Store in filtered_sample_set_ for use in other EntryT's callchain.
205 auto it = filtered_sample_set_.find(sample.get());
206 if (it != filtered_sample_set_.end()) {
207 return *it;
208 }
209 EntryT* result = sample.get();
210 filtered_sample_set_.insert(sample.get());
211 sample_storage_.push_back(std::move(sample));
212 return result;
213 }
Yabin Cuib64a8632016-05-24 18:23:33 -0700214 UpdateSummary(sample.get());
215 EntryT* result;
216 auto it = sample_set_.find(sample.get());
217 if (it == sample_set_.end()) {
218 result = sample.get();
219 sample_set_.insert(sample.get());
220 sample_storage_.push_back(std::move(sample));
221 } else {
222 result = *it;
223 MergeSample(*it, sample.get());
224 }
225 return result;
226 }
227
228 EntryT* InsertCallChainSample(std::unique_ptr<EntryT> sample,
229 const std::vector<EntryT*>& callchain) {
230 if (sample == nullptr) {
231 return nullptr;
232 }
Yabin Cuib64a8632016-05-24 18:23:33 -0700233 auto it = sample_set_.find(sample.get());
234 if (it != sample_set_.end()) {
235 EntryT* sample = *it;
236 // Process only once for recursive function call.
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200237 if (std::find(callchain.begin(), callchain.end(), sample) != callchain.end()) {
Yabin Cuib64a8632016-05-24 18:23:33 -0700238 return sample;
239 }
240 }
241 return InsertSample(std::move(sample));
242 }
243
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200244 void InsertCallChainForSample(EntryT* sample, const std::vector<EntryT*>& callchain,
Yabin Cui9970a232016-06-29 12:18:11 -0700245 const AccumulateInfoT& acc_info) {
246 uint64_t period = GetPeriodForCallChain(acc_info);
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200247 sample->callchain.AddCallChain(callchain, period, [&](const EntryT* s1, const EntryT* s2) {
248 return sample_comparator_.IsSameSample(s1, s2);
249 });
Yabin Cui9970a232016-06-29 12:18:11 -0700250 }
251
Yabin Cui0c093f32017-04-19 11:48:44 -0700252 void AddCallChainDuplicateInfo() {
253 if (build_callchain_) {
254 for (EntryT* sample : sample_set_) {
255 auto it = callchain_parent_map_.find(sample);
256 if (it != callchain_parent_map_.end() && !it->second.has_multiple_parents) {
257 sample->callchain.duplicated = true;
258 }
259 }
260 }
261 }
262
Yabin Cuib64a8632016-05-24 18:23:33 -0700263 std::set<EntryT*, SampleComparator<EntryT>> sample_set_;
Yabin Cui6965d422016-06-15 11:41:42 -0700264 bool accumulate_callchain_;
Yabin Cuib64a8632016-05-24 18:23:33 -0700265
266 private:
Yabin Cui0c093f32017-04-19 11:48:44 -0700267 void UpdateCallChainParentInfo(EntryT* sample, EntryT* parent) {
268 if (parent == nullptr) {
269 return;
270 }
271 auto it = callchain_parent_map_.find(sample);
272 if (it == callchain_parent_map_.end()) {
273 CallChainParentInfo info;
274 info.parent = parent;
275 info.has_multiple_parents = false;
276 callchain_parent_map_[sample] = info;
277 } else if (it->second.parent != parent) {
278 it->second.has_multiple_parents = true;
279 }
280 }
281
Yabin Cui9970a232016-06-29 12:18:11 -0700282 const SampleComparator<EntryT> sample_comparator_;
Yabin Cui4e391de2021-11-02 13:28:24 -0700283 // If a Sample/CallChainSample is filtered out, it is stored in filtered_sample_set_,
Yabin Cuib64a8632016-05-24 18:23:33 -0700284 // and only used in other EntryT's callchain.
Yabin Cui4e391de2021-11-02 13:28:24 -0700285 std::set<EntryT*, SampleComparator<EntryT>> filtered_sample_set_;
Yabin Cuib64a8632016-05-24 18:23:33 -0700286 std::vector<std::unique_ptr<EntryT>> sample_storage_;
287
Yabin Cui0c093f32017-04-19 11:48:44 -0700288 struct CallChainParentInfo {
289 EntryT* parent;
290 bool has_multiple_parents;
291 };
292 std::unordered_map<EntryT*, CallChainParentInfo> callchain_parent_map_;
293
Yabin Cuib64a8632016-05-24 18:23:33 -0700294 bool use_branch_address_;
Yabin Cuib64a8632016-05-24 18:23:33 -0700295 bool build_callchain_;
296 bool use_caller_as_callchain_root_;
Yabin Cui03381452017-12-13 11:31:53 -0800297 std::unique_ptr<OfflineUnwinder> offline_unwinder_;
Yabin Cuib64a8632016-05-24 18:23:33 -0700298};
299
300template <typename EntryT>
301class SampleTreeSorter {
302 public:
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200303 explicit SampleTreeSorter(SampleComparator<EntryT> comparator) : comparator_(comparator) {}
Yabin Cuib64a8632016-05-24 18:23:33 -0700304
305 virtual ~SampleTreeSorter() {}
306
307 void Sort(std::vector<EntryT*>& v, bool sort_callchain) {
308 if (sort_callchain) {
309 for (auto& sample : v) {
310 SortCallChain(sample);
311 }
312 }
313 if (!comparator_.empty()) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200314 std::sort(v.begin(), v.end(),
315 [this](const EntryT* s1, const EntryT* s2) { return comparator_(s1, s2); });
Yabin Cuib64a8632016-05-24 18:23:33 -0700316 }
317 }
318
319 protected:
320 void SortCallChain(EntryT* sample) { sample->callchain.SortByPeriod(); }
321
322 private:
323 SampleComparator<EntryT> comparator_;
324};
325
326template <typename EntryT, typename InfoT>
327class SampleTreeDisplayer {
328 public:
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200329 explicit SampleTreeDisplayer(SampleDisplayer<EntryT, InfoT> displayer) : displayer_(displayer) {}
Yabin Cuib64a8632016-05-24 18:23:33 -0700330
331 virtual ~SampleTreeDisplayer() {}
332
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200333 void DisplaySamples(FILE* fp, const std::vector<EntryT*>& samples, const InfoT* info) {
Yabin Cuib64a8632016-05-24 18:23:33 -0700334 displayer_.SetInfo(info);
335 for (const auto& sample : samples) {
336 displayer_.AdjustWidth(sample);
337 }
338 displayer_.PrintNames(fp);
339 for (const auto& sample : samples) {
340 displayer_.PrintSample(fp, sample);
341 }
Yabin Cui2672dea2015-05-21 12:17:23 -0700342 }
343
344 private:
Yabin Cuib64a8632016-05-24 18:23:33 -0700345 SampleDisplayer<EntryT, InfoT> displayer_;
Yabin Cui2672dea2015-05-21 12:17:23 -0700346};
347
Yabin Cuifaa7b922021-01-11 17:35:57 -0800348} // namespace simpleperf
349
Yabin Cui2672dea2015-05-21 12:17:23 -0700350#endif // SIMPLE_PERF_SAMPLE_TREE_H_