1 /*
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
22 #include <functional>
23 #include <optional>
24 #include <string>
25
26 #include <android-base/logging.h>
27 #include <android-base/stringprintf.h>
28
29 namespace simpleperf {
30
31 // The display functions below are used to show items in a sample.
32
33 template <typename EntryT, typename InfoT>
DisplayAccumulatedOverhead(const EntryT * sample,const InfoT * info)34 std::string DisplayAccumulatedOverhead(const EntryT* sample, const InfoT* info) {
35 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
41 template <typename EntryT>
DisplayAccumulatedPeriod(const EntryT * sample)42 std::string DisplayAccumulatedPeriod(const EntryT* sample) {
43 return android::base::StringPrintf("%" PRIu64, sample->period + sample->accumulated_period);
44 }
45
46 template <typename EntryT, typename InfoT>
DisplaySelfOverhead(const EntryT * sample,const InfoT * info)47 std::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
60 #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 }
65
66 BUILD_DISPLAY_UINT64_FUNCTION(DisplaySelfPeriod, period);
67 BUILD_DISPLAY_UINT64_FUNCTION(DisplaySampleCount, sample_count);
68
69 template <typename EntryT>
DisplayPid(const EntryT * sample)70 std::string DisplayPid(const EntryT* sample) {
71 return android::base::StringPrintf("%d", static_cast<int>(sample->pid));
72 }
73
74 template <typename EntryT>
DisplayTid(const EntryT * sample)75 std::string DisplayTid(const EntryT* sample) {
76 return android::base::StringPrintf("%d", static_cast<int>(sample->tid));
77 }
78
79 template <typename EntryT>
DisplayComm(const EntryT * sample)80 std::string DisplayComm(const EntryT* sample) {
81 return sample->thread_comm;
82 }
83
84 template <typename EntryT>
DisplayDso(const EntryT * sample)85 std::string DisplayDso(const EntryT* sample) {
86 return std::string{sample->map->dso->GetReportPath()};
87 }
88
89 template <typename EntryT>
DisplaySymbol(const EntryT * sample)90 std::string DisplaySymbol(const EntryT* sample) {
91 return sample->symbol->DemangledName();
92 }
93
94 template <typename EntryT>
DisplayDsoFrom(const EntryT * sample)95 std::string DisplayDsoFrom(const EntryT* sample) {
96 return std::string{sample->branch_from.map->dso->GetReportPath()};
97 }
98
99 template <typename EntryT>
DisplaySymbolFrom(const EntryT * sample)100 std::string DisplaySymbolFrom(const EntryT* sample) {
101 return sample->branch_from.symbol->DemangledName();
102 }
103
104 template <typename SampleT, typename CallChainNodeT>
105 class CallgraphDisplayer {
106 private:
107 static constexpr int SPACES_BETWEEN_CALLGRAPH_ENTRIES = 4;
108
109 public:
110 CallgraphDisplayer(uint32_t max_stack = UINT32_MAX, double percent_limit = 0.0,
111 bool brief_callgraph = false)
max_stack_(max_stack)112 : max_stack_(max_stack), percent_limit_(percent_limit), brief_callgraph_(brief_callgraph) {}
113
~CallgraphDisplayer()114 virtual ~CallgraphDisplayer() {}
115
operator()116 void operator()(FILE* fp, const SampleT* sample) {
117 if (sample->callchain.children.empty()) {
118 return;
119 }
120 std::string prefix = " ";
121 if (brief_callgraph_ && sample->callchain.duplicated) {
122 fprintf(fp, "%s[skipped in brief callgraph mode]\n", prefix.c_str());
123 return;
124 }
125 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],
130 sample->callchain.children_period + sample->GetPeriod(),
131 (i + 1 == sample->callchain.children.size()));
132 }
133 }
134
DisplayCallGraphEntry(FILE * fp,size_t depth,std::string prefix,const std::unique_ptr<CallChainNodeT> & node,uint64_t parent_period,bool last)135 void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix,
136 const std::unique_ptr<CallChainNodeT>& node, uint64_t parent_period,
137 bool last) {
138 if (depth > max_stack_) {
139 return;
140 }
141 std::string percentage_s = "-- ";
142 if (node->period + node->children_period != parent_period) {
143 double percentage = 100.0 * (node->period + node->children_period) / parent_period;
144 if (percentage < percent_limit_) {
145 return;
146 }
147 percentage_s = android::base::StringPrintf("--%.2f%%-- ", percentage);
148 }
149 prefix += "|";
150 fprintf(fp, "%s\n", prefix.c_str());
151 if (last) {
152 prefix.back() = ' ';
153 }
154 fprintf(fp, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(),
155 PrintSampleName(node->chain[0]).c_str());
156 for (size_t i = 1; i < node->chain.size(); ++i) {
157 fprintf(fp, "%s%*s%s\n", prefix.c_str(), static_cast<int>(percentage_s.size()), "",
158 PrintSampleName(node->chain[i]).c_str());
159 }
160 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 }
165 for (size_t i = 0; i < node->children.size(); ++i) {
166 DisplayCallGraphEntry(fp, depth + 1, prefix, node->children[i],
167 node->children_period + node->period, (i + 1 == node->children.size()));
168 }
169 }
170
171 protected:
PrintSampleName(const SampleT * sample)172 virtual std::string PrintSampleName(const SampleT* sample) {
173 return sample->symbol->DemangledName();
174 }
175
176 private:
177 uint32_t max_stack_;
178 double percent_limit_;
179 bool brief_callgraph_;
180 };
181
182 // SampleDisplayer is a class using a collections of display functions to show a
183 // sample.
184
185 template <typename EntryT, typename InfoT>
186 class SampleDisplayer {
187 public:
188 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*)>;
190 using exclusive_display_sample_func_t = std::function<void(FILE*, const EntryT*)>;
191
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:
SetInfo(const InfoT * info)201 void SetInfo(const InfoT* info) { info_ = info; }
SetReportFormat(bool report_csv,const std::string & csv_separator)202 void SetReportFormat(bool report_csv, const std::string& csv_separator) {
203 report_csv_ = report_csv;
204 csv_separator_ = csv_separator;
205 }
SetFilterFunction(const std::function<bool (const EntryT *,const InfoT *)> & filter)206 void SetFilterFunction(const std::function<bool(const EntryT*, const InfoT*)>& filter) {
207 filter_func_ = filter;
208 }
209
AddDisplayFunction(const std::string & name,const display_sample_func_t & func)210 void AddDisplayFunction(const std::string& name, const display_sample_func_t& func) {
211 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
AddDisplayFunction(const std::string & name,const display_sample_with_info_func_t & func_with_info)219 void AddDisplayFunction(const std::string& name,
220 const display_sample_with_info_func_t& func_with_info) {
221 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
AddExclusiveDisplayFunction(const exclusive_display_sample_func_t & func)229 void AddExclusiveDisplayFunction(const exclusive_display_sample_func_t& func) {
230 exclusive_display_v_.push_back(func);
231 }
232
AdjustWidth(const EntryT * sample)233 void AdjustWidth(const EntryT* sample) {
234 if (report_csv_) {
235 return;
236 }
237 if (filter_func_ && !filter_func_.value()(sample, info_)) {
238 return;
239 }
240 for (auto& item : display_v_) {
241 std::string data =
242 (item.func != nullptr) ? item.func(sample) : item.func_with_info(sample, info_);
243 item.width = std::max(item.width, data.size());
244 }
245 }
246
PrintNames(FILE * fp)247 void PrintNames(FILE* fp) {
248 for (size_t i = 0; i < display_v_.size(); ++i) {
249 auto& item = display_v_[i];
250 if (report_csv_) {
251 fprintf(fp, "%s%s", item.name.c_str(),
252 (i + 1 == display_v_.size()) ? "\n" : csv_separator_.c_str());
253 } else {
254 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 }
259 }
260 }
261 }
262
PrintSample(FILE * fp,const EntryT * sample)263 void PrintSample(FILE* fp, const EntryT* sample) {
264 if (filter_func_ && !filter_func_.value()(sample, info_)) {
265 return;
266 }
267 for (size_t i = 0; i < display_v_.size(); ++i) {
268 auto& item = display_v_[i];
269 std::string data =
270 (item.func != nullptr) ? item.func(sample) : item.func_with_info(sample, info_);
271 if (report_csv_) {
272 if (data.find(csv_separator_) == std::string::npos) {
273 fprintf(fp, "%s", data.c_str());
274 } else {
275 fprintf(fp, "\"%s\"", data.c_str());
276 }
277 fputs((i + 1 == display_v_.size()) ? "\n" : csv_separator_.c_str(), fp);
278 } else {
279 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 }
284 }
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_;
295 std::optional<std::function<bool(const EntryT*, const InfoT*)>> filter_func_;
296 bool report_csv_ = false;
297 std::string csv_separator_;
298 };
299
300 } // namespace simpleperf
301
302 #endif // SIMPLE_PERF_SAMPLE_DISPLAYER_H_
303