SampleDisplayer.h revision afe99a53d3030f54fa843af3e1558852a4cb3815
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
30template <typename EntryT, typename InfoT>
31std::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
39template <typename EntryT>
40std::string DisplayAccumulatedPeriod(const EntryT* sample) {
41  return android::base::StringPrintf("%" PRIu64, sample->period + sample->accumulated_period);
42}
43
44template <typename EntryT, typename InfoT>
45std::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
64BUILD_DISPLAY_UINT64_FUNCTION(DisplaySelfPeriod, period);
65BUILD_DISPLAY_UINT64_FUNCTION(DisplaySampleCount, sample_count);
66
67template <typename EntryT>
68std::string DisplayPid(const EntryT* sample) {
69  return android::base::StringPrintf("%d", sample->thread->pid);
70}
71
72template <typename EntryT>
73std::string DisplayTid(const EntryT* sample) {
74  return android::base::StringPrintf("%d", sample->thread->tid);
75}
76
77template <typename EntryT>
78std::string DisplayComm(const EntryT* sample) {
79  return sample->thread_comm;
80}
81
82template <typename EntryT>
83std::string DisplayDso(const EntryT* sample) {
84  return sample->map->dso->Path();
85}
86
87template <typename EntryT>
88std::string DisplaySymbol(const EntryT* sample) {
89  return sample->symbol->DemangledName();
90}
91
92template <typename EntryT>
93std::string DisplayDsoFrom(const EntryT* sample) {
94  return sample->branch_from.map->dso->Path();
95}
96
97template <typename EntryT>
98std::string DisplaySymbolFrom(const EntryT* sample) {
99  return sample->branch_from.symbol->DemangledName();
100}
101
102template <typename SampleT, typename CallChainNodeT>
103class 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)
110      : max_stack_(max_stack), percent_limit_(percent_limit) {}
111
112  virtual ~CallgraphDisplayer() {}
113
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
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:
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
177template <typename EntryT, typename InfoT>
178class 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:
195  void SetInfo(const InfoT* info) { info_ = info; }
196
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
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
216  void AddExclusiveDisplayFunction(exclusive_display_sample_func_t func) {
217    exclusive_display_v_.push_back(func);
218  }
219
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
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
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