Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 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 | |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 22 | #include <functional> |
Yabin Cui | 8036c96 | 2021-08-24 10:22:50 -0700 | [diff] [blame] | 23 | #include <optional> |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 24 | #include <string> |
| 25 | |
| 26 | #include <android-base/logging.h> |
| 27 | #include <android-base/stringprintf.h> |
| 28 | |
Yabin Cui | faa7b92 | 2021-01-11 17:35:57 -0800 | [diff] [blame] | 29 | namespace simpleperf { |
| 30 | |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 31 | // The display functions below are used to show items in a sample. |
| 32 | |
| 33 | template <typename EntryT, typename InfoT> |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 34 | std::string DisplayAccumulatedOverhead(const EntryT* sample, const InfoT* info) { |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 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 | |
Yabin Cui | afe99a5 | 2017-02-23 16:27:09 -0800 | [diff] [blame] | 41 | template <typename EntryT> |
| 42 | std::string DisplayAccumulatedPeriod(const EntryT* sample) { |
| 43 | return android::base::StringPrintf("%" PRIu64, sample->period + sample->accumulated_period); |
| 44 | } |
| 45 | |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 46 | template <typename EntryT, typename InfoT> |
| 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 | |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 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 | } |
Yabin Cui | 6965d42 | 2016-06-15 11:41:42 -0700 | [diff] [blame] | 65 | |
Yabin Cui | afe99a5 | 2017-02-23 16:27:09 -0800 | [diff] [blame] | 66 | BUILD_DISPLAY_UINT64_FUNCTION(DisplaySelfPeriod, period); |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 67 | BUILD_DISPLAY_UINT64_FUNCTION(DisplaySampleCount, sample_count); |
| 68 | |
| 69 | template <typename EntryT> |
| 70 | std::string DisplayPid(const EntryT* sample) { |
Yabin Cui | 847f3fd | 2019-05-02 12:58:05 -0700 | [diff] [blame] | 71 | return android::base::StringPrintf("%d", static_cast<int>(sample->pid)); |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 72 | } |
| 73 | |
| 74 | template <typename EntryT> |
| 75 | std::string DisplayTid(const EntryT* sample) { |
Yabin Cui | 847f3fd | 2019-05-02 12:58:05 -0700 | [diff] [blame] | 76 | return android::base::StringPrintf("%d", static_cast<int>(sample->tid)); |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 77 | } |
| 78 | |
| 79 | template <typename EntryT> |
| 80 | std::string DisplayComm(const EntryT* sample) { |
| 81 | return sample->thread_comm; |
| 82 | } |
| 83 | |
| 84 | template <typename EntryT> |
| 85 | std::string DisplayDso(const EntryT* sample) { |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 86 | return std::string{sample->map->dso->GetReportPath()}; |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | template <typename EntryT> |
| 90 | std::string DisplaySymbol(const EntryT* sample) { |
| 91 | return sample->symbol->DemangledName(); |
| 92 | } |
| 93 | |
| 94 | template <typename EntryT> |
| 95 | std::string DisplayDsoFrom(const EntryT* sample) { |
Yabin Cui | e32ed2b | 2020-07-23 15:30:14 -0700 | [diff] [blame] | 96 | return std::string{sample->branch_from.map->dso->GetReportPath()}; |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | template <typename EntryT> |
| 100 | std::string DisplaySymbolFrom(const EntryT* sample) { |
| 101 | return sample->branch_from.symbol->DemangledName(); |
| 102 | } |
| 103 | |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 104 | template <typename SampleT, typename CallChainNodeT> |
| 105 | class CallgraphDisplayer { |
Yabin Cui | 18385c4 | 2016-12-09 14:51:04 -0800 | [diff] [blame] | 106 | private: |
| 107 | static constexpr int SPACES_BETWEEN_CALLGRAPH_ENTRIES = 4; |
| 108 | |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 109 | public: |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 110 | CallgraphDisplayer(uint32_t max_stack = UINT32_MAX, double percent_limit = 0.0, |
Yabin Cui | 0c093f3 | 2017-04-19 11:48:44 -0700 | [diff] [blame] | 111 | bool brief_callgraph = false) |
| 112 | : max_stack_(max_stack), percent_limit_(percent_limit), brief_callgraph_(brief_callgraph) {} |
Yabin Cui | c0565bb | 2016-09-29 15:59:33 -0700 | [diff] [blame] | 113 | |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 114 | virtual ~CallgraphDisplayer() {} |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 115 | |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 116 | void operator()(FILE* fp, const SampleT* sample) { |
Yabin Cui | 0c093f3 | 2017-04-19 11:48:44 -0700 | [diff] [blame] | 117 | if (sample->callchain.children.empty()) { |
| 118 | return; |
| 119 | } |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 120 | std::string prefix = " "; |
Yabin Cui | 0c093f3 | 2017-04-19 11:48:44 -0700 | [diff] [blame] | 121 | if (brief_callgraph_ && sample->callchain.duplicated) { |
| 122 | fprintf(fp, "%s[skipped in brief callgraph mode]\n", prefix.c_str()); |
| 123 | return; |
| 124 | } |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 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], |
Yabin Cui | 18385c4 | 2016-12-09 14:51:04 -0800 | [diff] [blame] | 130 | sample->callchain.children_period + sample->GetPeriod(), |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 131 | (i + 1 == sample->callchain.children.size())); |
| 132 | } |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 133 | } |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 134 | |
| 135 | void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix, |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 136 | const std::unique_ptr<CallChainNodeT>& node, uint64_t parent_period, |
| 137 | bool last) { |
Yabin Cui | c0565bb | 2016-09-29 15:59:33 -0700 | [diff] [blame] | 138 | if (depth > max_stack_) { |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 139 | return; |
| 140 | } |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 141 | std::string percentage_s = "-- "; |
| 142 | if (node->period + node->children_period != parent_period) { |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 143 | double percentage = 100.0 * (node->period + node->children_period) / parent_period; |
Yabin Cui | c0565bb | 2016-09-29 15:59:33 -0700 | [diff] [blame] | 144 | if (percentage < percent_limit_) { |
| 145 | return; |
| 146 | } |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 147 | percentage_s = android::base::StringPrintf("--%.2f%%-- ", percentage); |
| 148 | } |
Yabin Cui | c0565bb | 2016-09-29 15:59:33 -0700 | [diff] [blame] | 149 | prefix += "|"; |
| 150 | fprintf(fp, "%s\n", prefix.c_str()); |
| 151 | if (last) { |
| 152 | prefix.back() = ' '; |
| 153 | } |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 154 | fprintf(fp, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(), |
| 155 | PrintSampleName(node->chain[0]).c_str()); |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 156 | for (size_t i = 1; i < node->chain.size(); ++i) { |
Yabin Cui | 18385c4 | 2016-12-09 14:51:04 -0800 | [diff] [blame] | 157 | fprintf(fp, "%s%*s%s\n", prefix.c_str(), static_cast<int>(percentage_s.size()), "", |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 158 | PrintSampleName(node->chain[i]).c_str()); |
| 159 | } |
Yabin Cui | 18385c4 | 2016-12-09 14:51:04 -0800 | [diff] [blame] | 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 | } |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 165 | for (size_t i = 0; i < node->children.size(); ++i) { |
| 166 | DisplayCallGraphEntry(fp, depth + 1, prefix, node->children[i], |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 167 | node->children_period + node->period, (i + 1 == node->children.size())); |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 168 | } |
| 169 | } |
| 170 | |
| 171 | protected: |
| 172 | virtual std::string PrintSampleName(const SampleT* sample) { |
| 173 | return sample->symbol->DemangledName(); |
| 174 | } |
Yabin Cui | c0565bb | 2016-09-29 15:59:33 -0700 | [diff] [blame] | 175 | |
| 176 | private: |
| 177 | uint32_t max_stack_; |
| 178 | double percent_limit_; |
Yabin Cui | 0c093f3 | 2017-04-19 11:48:44 -0700 | [diff] [blame] | 179 | bool brief_callgraph_; |
Yabin Cui | 9970a23 | 2016-06-29 12:18:11 -0700 | [diff] [blame] | 180 | }; |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 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: |
Yabin Cui | 2da26a0 | 2021-12-21 15:47:05 -0800 | [diff] [blame] | 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*)>; |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 190 | using exclusive_display_sample_func_t = std::function<void(FILE*, const EntryT*)>; |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 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: |
| 201 | void SetInfo(const InfoT* info) { info_ = info; } |
Yabin Cui | 51dca6f | 2021-12-16 19:00:41 -0800 | [diff] [blame] | 202 | void SetReportFormat(bool report_csv, const std::string& csv_separator) { |
| 203 | report_csv_ = report_csv; |
| 204 | csv_separator_ = csv_separator; |
| 205 | } |
Yabin Cui | 8036c96 | 2021-08-24 10:22:50 -0700 | [diff] [blame] | 206 | void SetFilterFunction(const std::function<bool(const EntryT*, const InfoT*)>& filter) { |
| 207 | filter_func_ = filter; |
| 208 | } |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 209 | |
Yabin Cui | 2da26a0 | 2021-12-21 15:47:05 -0800 | [diff] [blame] | 210 | void AddDisplayFunction(const std::string& name, const display_sample_func_t& func) { |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 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 | |
Yabin Cui | 2da26a0 | 2021-12-21 15:47:05 -0800 | [diff] [blame] | 219 | void AddDisplayFunction(const std::string& name, |
| 220 | const display_sample_with_info_func_t& func_with_info) { |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 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 | |
Yabin Cui | 2da26a0 | 2021-12-21 15:47:05 -0800 | [diff] [blame] | 229 | void AddExclusiveDisplayFunction(const exclusive_display_sample_func_t& func) { |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 230 | exclusive_display_v_.push_back(func); |
| 231 | } |
| 232 | |
| 233 | void AdjustWidth(const EntryT* sample) { |
Yabin Cui | 30cc3c5 | 2020-05-27 13:37:18 -0700 | [diff] [blame] | 234 | if (report_csv_) { |
| 235 | return; |
| 236 | } |
Yabin Cui | 8036c96 | 2021-08-24 10:22:50 -0700 | [diff] [blame] | 237 | if (filter_func_ && !filter_func_.value()(sample, info_)) { |
| 238 | return; |
| 239 | } |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 240 | for (auto& item : display_v_) { |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 241 | std::string data = |
| 242 | (item.func != nullptr) ? item.func(sample) : item.func_with_info(sample, info_); |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 243 | 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 Cui | 30cc3c5 | 2020-05-27 13:37:18 -0700 | [diff] [blame] | 250 | if (report_csv_) { |
Yabin Cui | 51dca6f | 2021-12-16 19:00:41 -0800 | [diff] [blame] | 251 | fprintf(fp, "%s%s", item.name.c_str(), |
| 252 | (i + 1 == display_v_.size()) ? "\n" : csv_separator_.c_str()); |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 253 | } else { |
Yabin Cui | 30cc3c5 | 2020-05-27 13:37:18 -0700 | [diff] [blame] | 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 | } |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 259 | } |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | void PrintSample(FILE* fp, const EntryT* sample) { |
Yabin Cui | 8036c96 | 2021-08-24 10:22:50 -0700 | [diff] [blame] | 264 | if (filter_func_ && !filter_func_.value()(sample, info_)) { |
| 265 | return; |
| 266 | } |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 267 | for (size_t i = 0; i < display_v_.size(); ++i) { |
| 268 | auto& item = display_v_[i]; |
Thiébaud Weksteen | 4848ee0 | 2020-10-23 16:06:59 +0200 | [diff] [blame] | 269 | std::string data = |
| 270 | (item.func != nullptr) ? item.func(sample) : item.func_with_info(sample, info_); |
Yabin Cui | 30cc3c5 | 2020-05-27 13:37:18 -0700 | [diff] [blame] | 271 | if (report_csv_) { |
Yabin Cui | 51dca6f | 2021-12-16 19:00:41 -0800 | [diff] [blame] | 272 | if (data.find(csv_separator_) == std::string::npos) { |
Yabin Cui | 30cc3c5 | 2020-05-27 13:37:18 -0700 | [diff] [blame] | 273 | fprintf(fp, "%s", data.c_str()); |
| 274 | } else { |
| 275 | fprintf(fp, "\"%s\"", data.c_str()); |
| 276 | } |
Yabin Cui | 51dca6f | 2021-12-16 19:00:41 -0800 | [diff] [blame] | 277 | fputs((i + 1 == display_v_.size()) ? "\n" : csv_separator_.c_str(), fp); |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 278 | } else { |
Yabin Cui | 30cc3c5 | 2020-05-27 13:37:18 -0700 | [diff] [blame] | 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 | } |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 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_; |
Yabin Cui | 8036c96 | 2021-08-24 10:22:50 -0700 | [diff] [blame] | 295 | std::optional<std::function<bool(const EntryT*, const InfoT*)>> filter_func_; |
Yabin Cui | 30cc3c5 | 2020-05-27 13:37:18 -0700 | [diff] [blame] | 296 | bool report_csv_ = false; |
Yabin Cui | 51dca6f | 2021-12-16 19:00:41 -0800 | [diff] [blame] | 297 | std::string csv_separator_; |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 298 | }; |
| 299 | |
Yabin Cui | faa7b92 | 2021-01-11 17:35:57 -0800 | [diff] [blame] | 300 | } // namespace simpleperf |
| 301 | |
Yabin Cui | b64a863 | 2016-05-24 18:23:33 -0700 | [diff] [blame] | 302 | #endif // SIMPLE_PERF_SAMPLE_DISPLAYER_H_ |