blob: d8390ef84aa32b2ef551e4144a8380defd8b25f7 [file] [log] [blame]
Yabin Cuib64a8632016-05-24 18:23:33 -07001/*
2 * Copyright (C) 2016 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_DISPLAYER_H_
18#define SIMPLE_PERF_SAMPLE_DISPLAYER_H_
19
20#include <inttypes.h>
21
Yabin Cui9970a232016-06-29 12:18:11 -070022#include <functional>
Yabin Cui8036c962021-08-24 10:22:50 -070023#include <optional>
Yabin Cuib64a8632016-05-24 18:23:33 -070024#include <string>
25
26#include <android-base/logging.h>
27#include <android-base/stringprintf.h>
28
Yabin Cuifaa7b922021-01-11 17:35:57 -080029namespace simpleperf {
30
Yabin Cuib64a8632016-05-24 18:23:33 -070031// The display functions below are used to show items in a sample.
32
33template <typename EntryT, typename InfoT>
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +020034std::string DisplayAccumulatedOverhead(const EntryT* sample, const InfoT* info) {
Yabin Cuib64a8632016-05-24 18:23:33 -070035 uint64_t period = sample->period + sample->accumulated_period;
36 uint64_t total_period = info->total_period;
37 double percentage = (total_period != 0) ? 100.0 * period / total_period : 0.0;
38 return android::base::StringPrintf("%.2f%%", percentage);
39}
40
Yabin Cuiafe99a52017-02-23 16:27:09 -080041template <typename EntryT>
42std::string DisplayAccumulatedPeriod(const EntryT* sample) {
43 return android::base::StringPrintf("%" PRIu64, sample->period + sample->accumulated_period);
44}
45
Yabin Cuib64a8632016-05-24 18:23:33 -070046template <typename EntryT, typename InfoT>
47std::string DisplaySelfOverhead(const EntryT* sample, const InfoT* info) {
48 uint64_t period = sample->period;
49 uint64_t total_period = info->total_period;
50 double percentage = (total_period != 0) ? 100.0 * period / total_period : 0.0;
51 return android::base::StringPrintf("%.2f%%", percentage);
52}
53
54#define BUILD_DISPLAY_UINT64_FUNCTION(function_name, display_part) \
55 template <typename EntryT> \
56 std::string function_name(const EntryT* sample) { \
57 return android::base::StringPrintf("%" PRIu64, sample->display_part); \
58 }
59
Yabin Cui9970a232016-06-29 12:18:11 -070060#define BUILD_DISPLAY_HEX64_FUNCTION(function_name, display_part) \
61 template <typename EntryT> \
62 std::string function_name(const EntryT* sample) { \
63 return android::base::StringPrintf("0x%" PRIx64, sample->display_part); \
64 }
Yabin Cui6965d422016-06-15 11:41:42 -070065
Yabin Cuiafe99a52017-02-23 16:27:09 -080066BUILD_DISPLAY_UINT64_FUNCTION(DisplaySelfPeriod, period);
Yabin Cuib64a8632016-05-24 18:23:33 -070067BUILD_DISPLAY_UINT64_FUNCTION(DisplaySampleCount, sample_count);
68
69template <typename EntryT>
70std::string DisplayPid(const EntryT* sample) {
Yabin Cui847f3fd2019-05-02 12:58:05 -070071 return android::base::StringPrintf("%d", static_cast<int>(sample->pid));
Yabin Cuib64a8632016-05-24 18:23:33 -070072}
73
74template <typename EntryT>
75std::string DisplayTid(const EntryT* sample) {
Yabin Cui847f3fd2019-05-02 12:58:05 -070076 return android::base::StringPrintf("%d", static_cast<int>(sample->tid));
Yabin Cuib64a8632016-05-24 18:23:33 -070077}
78
79template <typename EntryT>
80std::string DisplayComm(const EntryT* sample) {
81 return sample->thread_comm;
82}
83
84template <typename EntryT>
85std::string DisplayDso(const EntryT* sample) {
Yabin Cuie32ed2b2020-07-23 15:30:14 -070086 return std::string{sample->map->dso->GetReportPath()};
Yabin Cuib64a8632016-05-24 18:23:33 -070087}
88
89template <typename EntryT>
90std::string DisplaySymbol(const EntryT* sample) {
91 return sample->symbol->DemangledName();
92}
93
94template <typename EntryT>
95std::string DisplayDsoFrom(const EntryT* sample) {
Yabin Cuie32ed2b2020-07-23 15:30:14 -070096 return std::string{sample->branch_from.map->dso->GetReportPath()};
Yabin Cuib64a8632016-05-24 18:23:33 -070097}
98
99template <typename EntryT>
100std::string DisplaySymbolFrom(const EntryT* sample) {
101 return sample->branch_from.symbol->DemangledName();
102}
103
Yabin Cui9970a232016-06-29 12:18:11 -0700104template <typename SampleT, typename CallChainNodeT>
105class CallgraphDisplayer {
Yabin Cui18385c42016-12-09 14:51:04 -0800106 private:
107 static constexpr int SPACES_BETWEEN_CALLGRAPH_ENTRIES = 4;
108
Yabin Cui9970a232016-06-29 12:18:11 -0700109 public:
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200110 CallgraphDisplayer(uint32_t max_stack = UINT32_MAX, double percent_limit = 0.0,
Yabin Cui0c093f32017-04-19 11:48:44 -0700111 bool brief_callgraph = false)
112 : max_stack_(max_stack), percent_limit_(percent_limit), brief_callgraph_(brief_callgraph) {}
Yabin Cuic0565bb2016-09-29 15:59:33 -0700113
Yabin Cui9970a232016-06-29 12:18:11 -0700114 virtual ~CallgraphDisplayer() {}
Yabin Cuib64a8632016-05-24 18:23:33 -0700115
Yabin Cui9970a232016-06-29 12:18:11 -0700116 void operator()(FILE* fp, const SampleT* sample) {
Yabin Cui0c093f32017-04-19 11:48:44 -0700117 if (sample->callchain.children.empty()) {
118 return;
119 }
Yabin Cui9970a232016-06-29 12:18:11 -0700120 std::string prefix = " ";
Yabin Cui0c093f32017-04-19 11:48:44 -0700121 if (brief_callgraph_ && sample->callchain.duplicated) {
122 fprintf(fp, "%s[skipped in brief callgraph mode]\n", prefix.c_str());
123 return;
124 }
Yabin Cui9970a232016-06-29 12:18:11 -0700125 fprintf(fp, "%s|\n", prefix.c_str());
126 fprintf(fp, "%s-- %s\n", prefix.c_str(), PrintSampleName(sample).c_str());
127 prefix.append(3, ' ');
128 for (size_t i = 0; i < sample->callchain.children.size(); ++i) {
129 DisplayCallGraphEntry(fp, 1, prefix, sample->callchain.children[i],
Yabin Cui18385c42016-12-09 14:51:04 -0800130 sample->callchain.children_period + sample->GetPeriod(),
Yabin Cui9970a232016-06-29 12:18:11 -0700131 (i + 1 == sample->callchain.children.size()));
132 }
Yabin Cuib64a8632016-05-24 18:23:33 -0700133 }
Yabin Cui9970a232016-06-29 12:18:11 -0700134
135 void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix,
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200136 const std::unique_ptr<CallChainNodeT>& node, uint64_t parent_period,
137 bool last) {
Yabin Cuic0565bb2016-09-29 15:59:33 -0700138 if (depth > max_stack_) {
Yabin Cui9970a232016-06-29 12:18:11 -0700139 return;
140 }
Yabin Cui9970a232016-06-29 12:18:11 -0700141 std::string percentage_s = "-- ";
142 if (node->period + node->children_period != parent_period) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200143 double percentage = 100.0 * (node->period + node->children_period) / parent_period;
Yabin Cuic0565bb2016-09-29 15:59:33 -0700144 if (percentage < percent_limit_) {
145 return;
146 }
Yabin Cui9970a232016-06-29 12:18:11 -0700147 percentage_s = android::base::StringPrintf("--%.2f%%-- ", percentage);
148 }
Yabin Cuic0565bb2016-09-29 15:59:33 -0700149 prefix += "|";
150 fprintf(fp, "%s\n", prefix.c_str());
151 if (last) {
152 prefix.back() = ' ';
153 }
Yabin Cui9970a232016-06-29 12:18:11 -0700154 fprintf(fp, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(),
155 PrintSampleName(node->chain[0]).c_str());
Yabin Cui9970a232016-06-29 12:18:11 -0700156 for (size_t i = 1; i < node->chain.size(); ++i) {
Yabin Cui18385c42016-12-09 14:51:04 -0800157 fprintf(fp, "%s%*s%s\n", prefix.c_str(), static_cast<int>(percentage_s.size()), "",
Yabin Cui9970a232016-06-29 12:18:11 -0700158 PrintSampleName(node->chain[i]).c_str());
159 }
Yabin Cui18385c42016-12-09 14:51:04 -0800160 prefix.append(SPACES_BETWEEN_CALLGRAPH_ENTRIES, ' ');
161 if (!node->children.empty() && node->period != 0) {
162 fprintf(fp, "%s|--%.2f%%-- [hit in function]\n", prefix.c_str(),
163 100.0 * node->period / (node->period + node->children_period));
164 }
Yabin Cui9970a232016-06-29 12:18:11 -0700165 for (size_t i = 0; i < node->children.size(); ++i) {
166 DisplayCallGraphEntry(fp, depth + 1, prefix, node->children[i],
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200167 node->children_period + node->period, (i + 1 == node->children.size()));
Yabin Cui9970a232016-06-29 12:18:11 -0700168 }
169 }
170
171 protected:
172 virtual std::string PrintSampleName(const SampleT* sample) {
173 return sample->symbol->DemangledName();
174 }
Yabin Cuic0565bb2016-09-29 15:59:33 -0700175
176 private:
177 uint32_t max_stack_;
178 double percent_limit_;
Yabin Cui0c093f32017-04-19 11:48:44 -0700179 bool brief_callgraph_;
Yabin Cui9970a232016-06-29 12:18:11 -0700180};
Yabin Cuib64a8632016-05-24 18:23:33 -0700181
182// SampleDisplayer is a class using a collections of display functions to show a
183// sample.
184
185template <typename EntryT, typename InfoT>
186class SampleDisplayer {
187 public:
Yabin Cui2da26a02021-12-21 15:47:05 -0800188 using display_sample_func_t = std::function<std::string(const EntryT*)>;
189 using display_sample_with_info_func_t = std::function<std::string(const EntryT*, const InfoT*)>;
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200190 using exclusive_display_sample_func_t = std::function<void(FILE*, const EntryT*)>;
Yabin Cuib64a8632016-05-24 18:23:33 -0700191
192 private:
193 struct Item {
194 std::string name;
195 size_t width;
196 display_sample_func_t func;
197 display_sample_with_info_func_t func_with_info;
198 };
199
200 public:
201 void SetInfo(const InfoT* info) { info_ = info; }
Yabin Cui51dca6f2021-12-16 19:00:41 -0800202 void SetReportFormat(bool report_csv, const std::string& csv_separator) {
203 report_csv_ = report_csv;
204 csv_separator_ = csv_separator;
205 }
Yabin Cui8036c962021-08-24 10:22:50 -0700206 void SetFilterFunction(const std::function<bool(const EntryT*, const InfoT*)>& filter) {
207 filter_func_ = filter;
208 }
Yabin Cuib64a8632016-05-24 18:23:33 -0700209
Yabin Cui2da26a02021-12-21 15:47:05 -0800210 void AddDisplayFunction(const std::string& name, const display_sample_func_t& func) {
Yabin Cuib64a8632016-05-24 18:23:33 -0700211 Item item;
212 item.name = name;
213 item.width = name.size();
214 item.func = func;
215 item.func_with_info = nullptr;
216 display_v_.push_back(item);
217 }
218
Yabin Cui2da26a02021-12-21 15:47:05 -0800219 void AddDisplayFunction(const std::string& name,
220 const display_sample_with_info_func_t& func_with_info) {
Yabin Cuib64a8632016-05-24 18:23:33 -0700221 Item item;
222 item.name = name;
223 item.width = name.size();
224 item.func = nullptr;
225 item.func_with_info = func_with_info;
226 display_v_.push_back(item);
227 }
228
Yabin Cui2da26a02021-12-21 15:47:05 -0800229 void AddExclusiveDisplayFunction(const exclusive_display_sample_func_t& func) {
Yabin Cuib64a8632016-05-24 18:23:33 -0700230 exclusive_display_v_.push_back(func);
231 }
232
233 void AdjustWidth(const EntryT* sample) {
Yabin Cui30cc3c52020-05-27 13:37:18 -0700234 if (report_csv_) {
235 return;
236 }
Yabin Cui8036c962021-08-24 10:22:50 -0700237 if (filter_func_ && !filter_func_.value()(sample, info_)) {
238 return;
239 }
Yabin Cuib64a8632016-05-24 18:23:33 -0700240 for (auto& item : display_v_) {
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200241 std::string data =
242 (item.func != nullptr) ? item.func(sample) : item.func_with_info(sample, info_);
Yabin Cuib64a8632016-05-24 18:23:33 -0700243 item.width = std::max(item.width, data.size());
244 }
245 }
246
247 void PrintNames(FILE* fp) {
248 for (size_t i = 0; i < display_v_.size(); ++i) {
249 auto& item = display_v_[i];
Yabin Cui30cc3c52020-05-27 13:37:18 -0700250 if (report_csv_) {
Yabin Cui51dca6f2021-12-16 19:00:41 -0800251 fprintf(fp, "%s%s", item.name.c_str(),
252 (i + 1 == display_v_.size()) ? "\n" : csv_separator_.c_str());
Yabin Cuib64a8632016-05-24 18:23:33 -0700253 } else {
Yabin Cui30cc3c52020-05-27 13:37:18 -0700254 if (i != display_v_.size() - 1) {
255 fprintf(fp, "%-*s ", static_cast<int>(item.width), item.name.c_str());
256 } else {
257 fprintf(fp, "%s\n", item.name.c_str());
258 }
Yabin Cuib64a8632016-05-24 18:23:33 -0700259 }
260 }
261 }
262
263 void PrintSample(FILE* fp, const EntryT* sample) {
Yabin Cui8036c962021-08-24 10:22:50 -0700264 if (filter_func_ && !filter_func_.value()(sample, info_)) {
265 return;
266 }
Yabin Cuib64a8632016-05-24 18:23:33 -0700267 for (size_t i = 0; i < display_v_.size(); ++i) {
268 auto& item = display_v_[i];
Thiébaud Weksteen4848ee02020-10-23 16:06:59 +0200269 std::string data =
270 (item.func != nullptr) ? item.func(sample) : item.func_with_info(sample, info_);
Yabin Cui30cc3c52020-05-27 13:37:18 -0700271 if (report_csv_) {
Yabin Cui51dca6f2021-12-16 19:00:41 -0800272 if (data.find(csv_separator_) == std::string::npos) {
Yabin Cui30cc3c52020-05-27 13:37:18 -0700273 fprintf(fp, "%s", data.c_str());
274 } else {
275 fprintf(fp, "\"%s\"", data.c_str());
276 }
Yabin Cui51dca6f2021-12-16 19:00:41 -0800277 fputs((i + 1 == display_v_.size()) ? "\n" : csv_separator_.c_str(), fp);
Yabin Cuib64a8632016-05-24 18:23:33 -0700278 } else {
Yabin Cui30cc3c52020-05-27 13:37:18 -0700279 if (i != display_v_.size() - 1) {
280 fprintf(fp, "%-*s ", static_cast<int>(item.width), data.c_str());
281 } else {
282 fprintf(fp, "%s\n", data.c_str());
283 }
Yabin Cuib64a8632016-05-24 18:23:33 -0700284 }
285 }
286 for (auto& func : exclusive_display_v_) {
287 func(fp, sample);
288 }
289 }
290
291 private:
292 const InfoT* info_;
293 std::vector<Item> display_v_;
294 std::vector<exclusive_display_sample_func_t> exclusive_display_v_;
Yabin Cui8036c962021-08-24 10:22:50 -0700295 std::optional<std::function<bool(const EntryT*, const InfoT*)>> filter_func_;
Yabin Cui30cc3c52020-05-27 13:37:18 -0700296 bool report_csv_ = false;
Yabin Cui51dca6f2021-12-16 19:00:41 -0800297 std::string csv_separator_;
Yabin Cuib64a8632016-05-24 18:23:33 -0700298};
299
Yabin Cuifaa7b922021-01-11 17:35:57 -0800300} // namespace simpleperf
301
Yabin Cuib64a8632016-05-24 18:23:33 -0700302#endif // SIMPLE_PERF_SAMPLE_DISPLAYER_H_