• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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