1 /*
2  * Copyright (C) 2020 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 #pragma once
18 
19 #include <math.h>
20 #include <sys/types.h>
21 
22 #include <algorithm>
23 #include <optional>
24 #include <string>
25 #include <unordered_map>
26 #include <vector>
27 
28 #include <android-base/stringprintf.h>
29 
30 #include "command.h"
31 #include "event_selection_set.h"
32 #include "SampleComparator.h"
33 
34 namespace simpleperf {
35 
36 struct CounterSum {
37   uint64_t value = 0;
38   uint64_t time_enabled = 0;
39   uint64_t time_running = 0;
40 
FromCounterCounterSum41   void FromCounter(const PerfCounter& counter) {
42     value = counter.value;
43     time_enabled = counter.time_enabled;
44     time_running = counter.time_running;
45   }
46 
ToCounterCounterSum47   void ToCounter(PerfCounter& counter) const {
48     counter.value = value;
49     counter.time_enabled = time_enabled;
50     counter.time_running = time_running;
51   }
52 
53   CounterSum operator+(const CounterSum& other) const {
54     CounterSum res;
55     res.value = value + other.value;
56     res.time_enabled = time_enabled + other.time_enabled;
57     res.time_running = time_running + other.time_running;
58     return res;
59   }
60 
61   CounterSum operator-(const CounterSum& other) const {
62     CounterSum res;
63     res.value = value - other.value;
64     res.time_enabled = time_enabled - other.time_enabled;
65     res.time_running = time_running - other.time_running;
66     return res;
67   }
68 };
69 
70 struct ThreadInfo {
71   pid_t tid;
72   pid_t pid;
73   std::string name;
74 };
75 
76 struct CounterSummary {
77   std::string type_name;
78   std::string modifier;
79   uint32_t group_id;
80   const ThreadInfo* thread;
81   int cpu;  // -1 represents all cpus
82   uint64_t count;
83   uint64_t runtime_in_ns;
84   double scale;
85   std::string readable_count;
86   std::string comment;
87   bool auto_generated;
88 
89   // used to sort summaries by count_per_thread
90   uint64_t count_per_thread = 0;
91 
CounterSummaryCounterSummary92   CounterSummary(const std::string& type_name, const std::string& modifier, uint32_t group_id,
93                  const ThreadInfo* thread, int cpu, uint64_t count, uint64_t runtime_in_ns,
94                  double scale, bool auto_generated, bool csv)
95       : type_name(type_name),
96         modifier(modifier),
97         group_id(group_id),
98         thread(thread),
99         cpu(cpu),
100         count(count),
101         runtime_in_ns(runtime_in_ns),
102         scale(scale),
103         auto_generated(auto_generated) {
104     readable_count = ReadableCountValue(csv);
105   }
106 
IsMonitoredAtTheSameTimeCounterSummary107   bool IsMonitoredAtTheSameTime(const CounterSummary& other) const {
108     // Two summaries are monitored at the same time if they are in the same
109     // group or are monitored all the time.
110     if (group_id == other.group_id) {
111       return true;
112     }
113     return IsMonitoredAllTheTime() && other.IsMonitoredAllTheTime();
114   }
115 
NameCounterSummary116   std::string Name() const {
117     if (modifier.empty()) {
118       return type_name;
119     }
120     return type_name + ":" + modifier;
121   }
122 
IsMonitoredAllTheTimeCounterSummary123   bool IsMonitoredAllTheTime() const {
124     // If an event runs all the time it is enabled (by not sharing hardware
125     // counters with other events), the scale of its summary is usually within
126     // [1, 1 + 1e-5]. By setting SCALE_ERROR_LIMIT to 1e-5, We can identify
127     // events monitored all the time in most cases while keeping the report
128     // error rate <= 1e-5.
129     constexpr double SCALE_ERROR_LIMIT = 1e-5;
130     return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
131   }
132 
133  private:
ReadableCountValueCounterSummary134   std::string ReadableCountValue(bool csv) {
135     if (type_name == "cpu-clock" || type_name == "task-clock") {
136       // Convert nanoseconds to milliseconds.
137       double value = count / 1e6;
138       return android::base::StringPrintf("%lf(ms)", value);
139     } else {
140       // Convert big numbers to human friendly mode. For example,
141       // 1000000 will be converted to 1,000,000.
142       std::string s = android::base::StringPrintf("%" PRIu64, count);
143       if (csv) {
144         return s;
145       } else {
146         for (size_t i = s.size() - 1, j = 1; i > 0; --i, ++j) {
147           if (j == 3) {
148             s.insert(s.begin() + i, ',');
149             j = 0;
150           }
151         }
152         return s;
153       }
154     }
155   }
156 };
157 
158 BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareSummaryCount, count);
159 BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareSummaryCountPerThread, count_per_thread);
160 BUILD_COMPARE_VALUE_FUNCTION(CompareSummaryCpu, cpu);
161 BUILD_COMPARE_VALUE_FUNCTION(CompareSummaryPid, thread->pid);
162 BUILD_COMPARE_VALUE_FUNCTION(CompareSummaryTid, thread->tid);
163 BUILD_COMPARE_VALUE_FUNCTION(CompareSummaryComm, thread->name);
164 
165 using SummaryComparator = SampleComparator<CounterSummary>;
166 
BuildSummaryComparator(const std::vector<std::string> & keys,bool report_per_thread,bool report_per_core)167 inline std::optional<SummaryComparator> BuildSummaryComparator(const std::vector<std::string>& keys,
168                                                                bool report_per_thread,
169                                                                bool report_per_core) {
170   SummaryComparator comparator;
171   for (auto& key : keys) {
172     if (key == "count") {
173       comparator.AddCompareFunction(CompareSummaryCount);
174     } else if (key == "count_per_thread") {
175       if (report_per_thread) {
176         comparator.AddCompareFunction(CompareSummaryCountPerThread);
177       }
178     } else if (key == "cpu") {
179       if (report_per_core) {
180         comparator.AddCompareFunction(CompareSummaryCpu);
181       }
182     } else if (key == "pid") {
183       if (report_per_thread) {
184         comparator.AddCompareFunction(CompareSummaryPid);
185       }
186     } else if (key == "tid") {
187       if (report_per_thread) {
188         comparator.AddCompareFunction(CompareSummaryTid);
189       }
190     } else if (key == "comm") {
191       if (report_per_thread) {
192         comparator.AddCompareFunction(CompareSummaryComm);
193       }
194     } else {
195       LOG(ERROR) << "Unknown sort key: " << key;
196       return {};
197     }
198   }
199   return comparator;
200 }
201 
202 // Build a vector of CounterSummary.
203 class CounterSummaryBuilder {
204  public:
CounterSummaryBuilder(bool report_per_thread,bool report_per_core,bool csv,const std::unordered_map<pid_t,ThreadInfo> & thread_map,const std::optional<SummaryComparator> & comparator)205   CounterSummaryBuilder(bool report_per_thread, bool report_per_core, bool csv,
206                         const std::unordered_map<pid_t, ThreadInfo>& thread_map,
207                         const std::optional<SummaryComparator>& comparator)
208       : report_per_thread_(report_per_thread),
209         report_per_core_(report_per_core),
210         csv_(csv),
211         thread_map_(thread_map),
212         summary_comparator_(comparator) {}
213 
AddCountersForOneEventType(const CountersInfo & info)214   void AddCountersForOneEventType(const CountersInfo& info) {
215     std::unordered_map<uint64_t, CounterSum> sum_map;
216     for (const auto& counter : info.counters) {
217       uint64_t key = 0;
218       if (report_per_thread_) {
219         key |= counter.tid;
220       }
221       if (report_per_core_) {
222         key |= static_cast<uint64_t>(counter.cpu) << 32;
223       }
224       CounterSum& sum = sum_map[key];
225       CounterSum add;
226       add.FromCounter(counter.counter);
227       sum = sum + add;
228     }
229     size_t pre_sum_count = summaries_.size();
230     for (const auto& pair : sum_map) {
231       pid_t tid = report_per_thread_ ? static_cast<pid_t>(pair.first & UINT32_MAX) : 0;
232       int cpu = report_per_core_ ? static_cast<int>(pair.first >> 32) : -1;
233       const CounterSum& sum = pair.second;
234       AddSummary(info, tid, cpu, sum);
235     }
236     if (report_per_thread_ || report_per_core_) {
237       SortSummaries(summaries_.begin() + pre_sum_count, summaries_.end());
238     }
239   }
240 
Build()241   std::vector<CounterSummary> Build() {
242     std::vector<CounterSummary> res = std::move(summaries_);
243     summaries_.clear();
244     return res;
245   }
246 
247  private:
AddSummary(const CountersInfo & info,pid_t tid,int cpu,const CounterSum & sum)248   void AddSummary(const CountersInfo& info, pid_t tid, int cpu, const CounterSum& sum) {
249     double scale = 1.0;
250     if (sum.time_running < sum.time_enabled && sum.time_running != 0) {
251       scale = static_cast<double>(sum.time_enabled) / sum.time_running;
252     }
253     if ((report_per_thread_ || report_per_core_) && sum.time_running == 0) {
254       // No need to report threads or cpus not running.
255       return;
256     }
257     const ThreadInfo* thread = nullptr;
258     if (report_per_thread_) {
259       auto it = thread_map_.find(tid);
260       CHECK(it != thread_map_.end());
261       thread = &it->second;
262     }
263     summaries_.emplace_back(info.event_name, info.event_modifier, info.group_id, thread, cpu,
264                             sum.value, sum.time_running, scale, false, csv_);
265   }
266 
SortSummaries(std::vector<CounterSummary>::iterator begin,std::vector<CounterSummary>::iterator end)267   void SortSummaries(std::vector<CounterSummary>::iterator begin,
268                      std::vector<CounterSummary>::iterator end) {
269     // Generate count_per_thread value for sorting.
270     if (report_per_thread_) {
271       if (report_per_core_) {
272         std::unordered_map<pid_t, uint64_t> count_per_thread;
273         for (auto it = begin; it != end; ++it) {
274           count_per_thread[it->thread->tid] += it->count;
275         }
276         for (auto it = begin; it != end; ++it) {
277           it->count_per_thread = count_per_thread[it->thread->tid];
278         }
279       } else {
280         for (auto it = begin; it != end; ++it) {
281           it->count_per_thread = it->count;
282         }
283       }
284     }
285 
286     std::sort(begin, end, summary_comparator_.value());
287   };
288 
289   const bool report_per_thread_;
290   const bool report_per_core_;
291   const bool csv_;
292   const std::unordered_map<pid_t, ThreadInfo>& thread_map_;
293   const std::optional<SummaryComparator>& summary_comparator_;
294   std::vector<CounterSummary> summaries_;
295 };
296 
297 class CounterSummaries {
298  public:
CounterSummaries(std::vector<CounterSummary> && summaries,bool csv)299   explicit CounterSummaries(std::vector<CounterSummary>&& summaries, bool csv)
300       : summaries_(std::move(summaries)), csv_(csv) {}
Summaries()301   const std::vector<CounterSummary>& Summaries() { return summaries_; }
302 
303   const CounterSummary* FindSummary(const std::string& type_name, const std::string& modifier,
304                                     const ThreadInfo* thread, int cpu);
305 
306   // If we have two summaries monitoring the same event type at the same time,
307   // that one is for user space only, and the other is for kernel space only;
308   // then we can automatically generate a summary combining the two results.
309   // For example, a summary of branch-misses:u and a summary for branch-misses:k
310   // can generate a summary of branch-misses.
311   void AutoGenerateSummaries();
312   void GenerateComments(double duration_in_sec);
313   void Show(FILE* fp);
314 
315  private:
316   std::string GetCommentForSummary(const CounterSummary& s, double duration_in_sec);
317   std::string GetRateComment(const CounterSummary& s, char sep);
318   bool FindRunningTimeForSummary(const CounterSummary& summary, double* running_time_in_sec);
319   void ShowCSV(FILE* fp, bool show_thread, bool show_core);
320   void ShowText(FILE* fp, bool show_thread, bool show_core);
321 
322  private:
323   std::vector<CounterSummary> summaries_;
324   bool csv_;
325 };
326 
GetStatCmdOptionFormats()327 inline const OptionFormatMap& GetStatCmdOptionFormats() {
328   static const OptionFormatMap option_formats = {
329       {"-a", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
330       {"--app", {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
331       {"--cpu", {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::ALLOWED}},
332       {"--csv", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
333       {"--duration", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
334       {"--interval", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
335       {"--interval-only-values",
336        {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
337       {"-e", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
338       {"--group", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
339       {"--in-app", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
340       {"--no-inherit", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
341       {"-o", {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
342       {"--out-fd", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::CHECK_FD}},
343       {"-p", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
344       {"--per-core", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
345       {"--per-thread", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
346       {"--sort", {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::ALLOWED}},
347       {"--stop-signal-fd", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::CHECK_FD}},
348       {"-t", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
349       {"--tracepoint-events",
350        {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::CHECK_PATH}},
351       {"--use-devfreq-counters",
352        {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
353       {"--verbose", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
354   };
355   return option_formats;
356 }
357 
358 }  // namespace simpleperf