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