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 <string>
24 
25 #include <android-base/logging.h>
26 #include <android-base/stringprintf.h>
27 
28 namespace simpleperf {
29 
30 // The display functions below are used to show items in a sample.
31 
32 template <typename EntryT, typename InfoT>
DisplayAccumulatedOverhead(const EntryT * sample,const InfoT * info)33 std::string DisplayAccumulatedOverhead(const EntryT* sample, const InfoT* info) {
34   uint64_t period = sample->period + sample->accumulated_period;
35   uint64_t total_period = info->total_period;
36   double percentage = (total_period != 0) ? 100.0 * period / total_period : 0.0;
37   return android::base::StringPrintf("%.2f%%", percentage);
38 }
39 
40 template <typename EntryT>
DisplayAccumulatedPeriod(const EntryT * sample)41 std::string DisplayAccumulatedPeriod(const EntryT* sample) {
42   return android::base::StringPrintf("%" PRIu64, sample->period + sample->accumulated_period);
43 }
44 
45 template <typename EntryT, typename InfoT>
DisplaySelfOverhead(const EntryT * sample,const InfoT * info)46 std::string DisplaySelfOverhead(const EntryT* sample, const InfoT* info) {
47   uint64_t period = sample->period;
48   uint64_t total_period = info->total_period;
49   double percentage = (total_period != 0) ? 100.0 * period / total_period : 0.0;
50   return android::base::StringPrintf("%.2f%%", percentage);
51 }
52 
53 #define BUILD_DISPLAY_UINT64_FUNCTION(function_name, display_part)        \
54   template <typename EntryT>                                              \
55   std::string function_name(const EntryT* sample) {                       \
56     return android::base::StringPrintf("%" PRIu64, sample->display_part); \
57   }
58 
59 #define BUILD_DISPLAY_HEX64_FUNCTION(function_name, display_part)           \
60   template <typename EntryT>                                                \
61   std::string function_name(const EntryT* sample) {                         \
62     return android::base::StringPrintf("0x%" PRIx64, sample->display_part); \
63   }
64 
65 BUILD_DISPLAY_UINT64_FUNCTION(DisplaySelfPeriod, period);
66 BUILD_DISPLAY_UINT64_FUNCTION(DisplaySampleCount, sample_count);
67 
68 template <typename EntryT>
DisplayPid(const EntryT * sample)69 std::string DisplayPid(const EntryT* sample) {
70   return android::base::StringPrintf("%d", static_cast<int>(sample->pid));
71 }
72 
73 template <typename EntryT>
DisplayTid(const EntryT * sample)74 std::string DisplayTid(const EntryT* sample) {
75   return android::base::StringPrintf("%d", static_cast<int>(sample->tid));
76 }
77 
78 template <typename EntryT>
DisplayComm(const EntryT * sample)79 std::string DisplayComm(const EntryT* sample) {
80   return sample->thread_comm;
81 }
82 
83 template <typename EntryT>
DisplayDso(const EntryT * sample)84 std::string DisplayDso(const EntryT* sample) {
85   return std::string{sample->map->dso->GetReportPath()};
86 }
87 
88 template <typename EntryT>
DisplaySymbol(const EntryT * sample)89 std::string DisplaySymbol(const EntryT* sample) {
90   return sample->symbol->DemangledName();
91 }
92 
93 template <typename EntryT>
DisplayDsoFrom(const EntryT * sample)94 std::string DisplayDsoFrom(const EntryT* sample) {
95   return std::string{sample->branch_from.map->dso->GetReportPath()};
96 }
97 
98 template <typename EntryT>
DisplaySymbolFrom(const EntryT * sample)99 std::string DisplaySymbolFrom(const EntryT* sample) {
100   return sample->branch_from.symbol->DemangledName();
101 }
102 
103 template <typename SampleT, typename CallChainNodeT>
104 class CallgraphDisplayer {
105  private:
106   static constexpr int SPACES_BETWEEN_CALLGRAPH_ENTRIES = 4;
107 
108  public:
109   CallgraphDisplayer(uint32_t max_stack = UINT32_MAX, double percent_limit = 0.0,
110                      bool brief_callgraph = false)
111       : max_stack_(max_stack), percent_limit_(percent_limit), brief_callgraph_(brief_callgraph) {}
112 
~CallgraphDisplayer()113   virtual ~CallgraphDisplayer() {}
114 
operator()115   void operator()(FILE* fp, const SampleT* sample) {
116     if (sample->callchain.children.empty()) {
117       return;
118     }
119     std::string prefix = "       ";
120     if (brief_callgraph_ && sample->callchain.duplicated) {
121       fprintf(fp, "%s[skipped in brief callgraph mode]\n", prefix.c_str());
122       return;
123     }
124     fprintf(fp, "%s|\n", prefix.c_str());
125     fprintf(fp, "%s-- %s\n", prefix.c_str(), PrintSampleName(sample).c_str());
126     prefix.append(3, ' ');
127     for (size_t i = 0; i < sample->callchain.children.size(); ++i) {
128       DisplayCallGraphEntry(fp, 1, prefix, sample->callchain.children[i],
129                             sample->callchain.children_period + sample->GetPeriod(),
130                             (i + 1 == sample->callchain.children.size()));
131     }
132   }
133 
DisplayCallGraphEntry(FILE * fp,size_t depth,std::string prefix,const std::unique_ptr<CallChainNodeT> & node,uint64_t parent_period,bool last)134   void DisplayCallGraphEntry(FILE* fp, size_t depth, std::string prefix,
135                              const std::unique_ptr<CallChainNodeT>& node, uint64_t parent_period,
136                              bool last) {
137     if (depth > max_stack_) {
138       return;
139     }
140     std::string percentage_s = "-- ";
141     if (node->period + node->children_period != parent_period) {
142       double percentage = 100.0 * (node->period + node->children_period) / parent_period;
143       if (percentage < percent_limit_) {
144         return;
145       }
146       percentage_s = android::base::StringPrintf("--%.2f%%-- ", percentage);
147     }
148     prefix += "|";
149     fprintf(fp, "%s\n", prefix.c_str());
150     if (last) {
151       prefix.back() = ' ';
152     }
153     fprintf(fp, "%s%s%s\n", prefix.c_str(), percentage_s.c_str(),
154             PrintSampleName(node->chain[0]).c_str());
155     for (size_t i = 1; i < node->chain.size(); ++i) {
156       fprintf(fp, "%s%*s%s\n", prefix.c_str(), static_cast<int>(percentage_s.size()), "",
157               PrintSampleName(node->chain[i]).c_str());
158     }
159     prefix.append(SPACES_BETWEEN_CALLGRAPH_ENTRIES, ' ');
160     if (!node->children.empty() && node->period != 0) {
161       fprintf(fp, "%s|--%.2f%%-- [hit in function]\n", prefix.c_str(),
162               100.0 * node->period / (node->period + node->children_period));
163     }
164     for (size_t i = 0; i < node->children.size(); ++i) {
165       DisplayCallGraphEntry(fp, depth + 1, prefix, node->children[i],
166                             node->children_period + node->period, (i + 1 == node->children.size()));
167     }
168   }
169 
170  protected:
PrintSampleName(const SampleT * sample)171   virtual std::string PrintSampleName(const SampleT* sample) {
172     return sample->symbol->DemangledName();
173   }
174 
175  private:
176   uint32_t max_stack_;
177   double percent_limit_;
178   bool brief_callgraph_;
179 };
180 
181 // SampleDisplayer is a class using a collections of display functions to show a
182 // sample.
183 
184 template <typename EntryT, typename InfoT>
185 class SampleDisplayer {
186  public:
187   typedef std::string (*display_sample_func_t)(const EntryT*);
188   typedef std::string (*display_sample_with_info_func_t)(const EntryT*, const InfoT*);
189   using exclusive_display_sample_func_t = std::function<void(FILE*, const EntryT*)>;
190 
191  private:
192   struct Item {
193     std::string name;
194     size_t width;
195     display_sample_func_t func;
196     display_sample_with_info_func_t func_with_info;
197   };
198 
199  public:
SetInfo(const InfoT * info)200   void SetInfo(const InfoT* info) { info_ = info; }
SetReportFormat(bool report_csv)201   void SetReportFormat(bool report_csv) { report_csv_ = report_csv; }
202 
AddDisplayFunction(const std::string & name,display_sample_func_t func)203   void AddDisplayFunction(const std::string& name, display_sample_func_t func) {
204     Item item;
205     item.name = name;
206     item.width = name.size();
207     item.func = func;
208     item.func_with_info = nullptr;
209     display_v_.push_back(item);
210   }
211 
AddDisplayFunction(const std::string & name,display_sample_with_info_func_t func_with_info)212   void AddDisplayFunction(const std::string& name, display_sample_with_info_func_t func_with_info) {
213     Item item;
214     item.name = name;
215     item.width = name.size();
216     item.func = nullptr;
217     item.func_with_info = func_with_info;
218     display_v_.push_back(item);
219   }
220 
AddExclusiveDisplayFunction(exclusive_display_sample_func_t func)221   void AddExclusiveDisplayFunction(exclusive_display_sample_func_t func) {
222     exclusive_display_v_.push_back(func);
223   }
224 
AdjustWidth(const EntryT * sample)225   void AdjustWidth(const EntryT* sample) {
226     if (report_csv_) {
227       return;
228     }
229     for (auto& item : display_v_) {
230       std::string data =
231           (item.func != nullptr) ? item.func(sample) : item.func_with_info(sample, info_);
232       item.width = std::max(item.width, data.size());
233     }
234   }
235 
PrintNames(FILE * fp)236   void PrintNames(FILE* fp) {
237     for (size_t i = 0; i < display_v_.size(); ++i) {
238       auto& item = display_v_[i];
239       if (report_csv_) {
240         fprintf(fp, "%s%c", item.name.c_str(), (i + 1 == display_v_.size()) ? '\n' : ',');
241       } else {
242         if (i != display_v_.size() - 1) {
243           fprintf(fp, "%-*s  ", static_cast<int>(item.width), item.name.c_str());
244         } else {
245           fprintf(fp, "%s\n", item.name.c_str());
246         }
247       }
248     }
249   }
250 
PrintSample(FILE * fp,const EntryT * sample)251   void PrintSample(FILE* fp, const EntryT* sample) {
252     for (size_t i = 0; i < display_v_.size(); ++i) {
253       auto& item = display_v_[i];
254       std::string data =
255           (item.func != nullptr) ? item.func(sample) : item.func_with_info(sample, info_);
256       if (report_csv_) {
257         if (data.find(',') == std::string::npos) {
258           fprintf(fp, "%s", data.c_str());
259         } else {
260           fprintf(fp, "\"%s\"", data.c_str());
261         }
262         fputc((i + 1 == display_v_.size()) ? '\n' : ',', fp);
263       } else {
264         if (i != display_v_.size() - 1) {
265           fprintf(fp, "%-*s  ", static_cast<int>(item.width), data.c_str());
266         } else {
267           fprintf(fp, "%s\n", data.c_str());
268         }
269       }
270     }
271     for (auto& func : exclusive_display_v_) {
272       func(fp, sample);
273     }
274   }
275 
276  private:
277   const InfoT* info_;
278   std::vector<Item> display_v_;
279   std::vector<exclusive_display_sample_func_t> exclusive_display_v_;
280   bool report_csv_ = false;
281 };
282 
283 }  // namespace simpleperf
284 
285 #endif  // SIMPLE_PERF_SAMPLE_DISPLAYER_H_
286