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 "SampleComparator.h"
31 #include "command.h"
32 #include "event_selection_set.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:
134 std::string ReadableCountValue(bool csv);
135 };
136
137 BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareSummaryCount, count);
138 BUILD_COMPARE_VALUE_FUNCTION_REVERSE(CompareSummaryCountPerThread, count_per_thread);
139 BUILD_COMPARE_VALUE_FUNCTION(CompareSummaryCpu, cpu);
140 BUILD_COMPARE_VALUE_FUNCTION(CompareSummaryPid, thread->pid);
141 BUILD_COMPARE_VALUE_FUNCTION(CompareSummaryTid, thread->tid);
142 BUILD_COMPARE_VALUE_FUNCTION(CompareSummaryComm, thread->name);
143
144 using SummaryComparator = SampleComparator<CounterSummary>;
145
BuildSummaryComparator(const std::vector<std::string> & keys,bool report_per_thread,bool report_per_core)146 inline std::optional<SummaryComparator> BuildSummaryComparator(const std::vector<std::string>& keys,
147 bool report_per_thread,
148 bool report_per_core) {
149 SummaryComparator comparator;
150 for (auto& key : keys) {
151 if (key == "count") {
152 comparator.AddCompareFunction(CompareSummaryCount);
153 } else if (key == "count_per_thread") {
154 if (report_per_thread) {
155 comparator.AddCompareFunction(CompareSummaryCountPerThread);
156 }
157 } else if (key == "cpu") {
158 if (report_per_core) {
159 comparator.AddCompareFunction(CompareSummaryCpu);
160 }
161 } else if (key == "pid") {
162 if (report_per_thread) {
163 comparator.AddCompareFunction(CompareSummaryPid);
164 }
165 } else if (key == "tid") {
166 if (report_per_thread) {
167 comparator.AddCompareFunction(CompareSummaryTid);
168 }
169 } else if (key == "comm") {
170 if (report_per_thread) {
171 comparator.AddCompareFunction(CompareSummaryComm);
172 }
173 } else {
174 LOG(ERROR) << "Unknown sort key: " << key;
175 return {};
176 }
177 }
178 return comparator;
179 }
180
181 // Build a vector of CounterSummary.
182 class CounterSummaryBuilder {
183 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)184 CounterSummaryBuilder(bool report_per_thread, bool report_per_core, bool csv,
185 const std::unordered_map<pid_t, ThreadInfo>& thread_map,
186 const std::optional<SummaryComparator>& comparator)
187 : report_per_thread_(report_per_thread),
188 report_per_core_(report_per_core),
189 csv_(csv),
190 thread_map_(thread_map),
191 summary_comparator_(comparator) {}
192
AddCountersForOneEventType(const CountersInfo & info)193 void AddCountersForOneEventType(const CountersInfo& info) {
194 std::unordered_map<uint64_t, CounterSum> sum_map;
195 for (const auto& counter : info.counters) {
196 uint64_t key = 0;
197 if (report_per_thread_) {
198 key |= counter.tid;
199 }
200 if (report_per_core_) {
201 key |= static_cast<uint64_t>(counter.cpu) << 32;
202 }
203 CounterSum& sum = sum_map[key];
204 CounterSum add;
205 add.FromCounter(counter.counter);
206 sum = sum + add;
207 }
208 size_t pre_sum_count = summaries_.size();
209 for (const auto& pair : sum_map) {
210 pid_t tid = report_per_thread_ ? static_cast<pid_t>(pair.first & UINT32_MAX) : 0;
211 int cpu = report_per_core_ ? static_cast<int>(pair.first >> 32) : -1;
212 const CounterSum& sum = pair.second;
213 AddSummary(info, tid, cpu, sum);
214 }
215 if (report_per_thread_ || report_per_core_) {
216 SortSummaries(summaries_.begin() + pre_sum_count, summaries_.end());
217 }
218 }
219
Build()220 std::vector<CounterSummary> Build() {
221 std::vector<CounterSummary> res = std::move(summaries_);
222 summaries_.clear();
223 return res;
224 }
225
226 private:
AddSummary(const CountersInfo & info,pid_t tid,int cpu,const CounterSum & sum)227 void AddSummary(const CountersInfo& info, pid_t tid, int cpu, const CounterSum& sum) {
228 double scale = 1.0;
229 if (sum.time_running < sum.time_enabled && sum.time_running != 0) {
230 scale = static_cast<double>(sum.time_enabled) / sum.time_running;
231 }
232 if ((report_per_thread_ || report_per_core_) && sum.time_running == 0) {
233 // No need to report threads or cpus not running.
234 return;
235 }
236 const ThreadInfo* thread = nullptr;
237 if (report_per_thread_) {
238 auto it = thread_map_.find(tid);
239 CHECK(it != thread_map_.end());
240 thread = &it->second;
241 }
242 summaries_.emplace_back(info.event_name, info.event_modifier, info.group_id, thread, cpu,
243 sum.value, sum.time_running, scale, false, csv_);
244 }
245
SortSummaries(std::vector<CounterSummary>::iterator begin,std::vector<CounterSummary>::iterator end)246 void SortSummaries(std::vector<CounterSummary>::iterator begin,
247 std::vector<CounterSummary>::iterator end) {
248 // Generate count_per_thread value for sorting.
249 if (report_per_thread_) {
250 if (report_per_core_) {
251 std::unordered_map<pid_t, uint64_t> count_per_thread;
252 for (auto it = begin; it != end; ++it) {
253 count_per_thread[it->thread->tid] += it->count;
254 }
255 for (auto it = begin; it != end; ++it) {
256 it->count_per_thread = count_per_thread[it->thread->tid];
257 }
258 } else {
259 for (auto it = begin; it != end; ++it) {
260 it->count_per_thread = it->count;
261 }
262 }
263 }
264
265 std::sort(begin, end, summary_comparator_.value());
266 };
267
268 const bool report_per_thread_;
269 const bool report_per_core_;
270 const bool csv_;
271 const std::unordered_map<pid_t, ThreadInfo>& thread_map_;
272 const std::optional<SummaryComparator>& summary_comparator_;
273 std::vector<CounterSummary> summaries_;
274 };
275
276 class CounterSummaries {
277 public:
CounterSummaries(std::vector<CounterSummary> && summaries,bool csv)278 explicit CounterSummaries(std::vector<CounterSummary>&& summaries, bool csv)
279 : summaries_(std::move(summaries)), csv_(csv) {}
Summaries()280 const std::vector<CounterSummary>& Summaries() { return summaries_; }
281
282 const CounterSummary* FindSummary(const std::string& type_name, const std::string& modifier,
283 const ThreadInfo* thread, int cpu);
284
285 // If we have two summaries monitoring the same event type at the same time,
286 // that one is for user space only, and the other is for kernel space only;
287 // then we can automatically generate a summary combining the two results.
288 // For example, a summary of branch-misses:u and a summary for branch-misses:k
289 // can generate a summary of branch-misses.
290 void AutoGenerateSummaries();
291 void GenerateComments(double duration_in_sec);
292 void Show(FILE* fp);
293
294 private:
295 std::string GetCommentForSummary(const CounterSummary& s, double duration_in_sec);
296 std::string GetRateComment(const CounterSummary& s, char sep);
297 bool FindRunningTimeForSummary(const CounterSummary& summary, double* running_time_in_sec);
298 void ShowCSV(FILE* fp, bool show_thread, bool show_core);
299 void ShowText(FILE* fp, bool show_thread, bool show_core);
300
301 private:
302 std::vector<CounterSummary> summaries_;
303 bool csv_;
304 };
305
GetStatCmdOptionFormats()306 inline const OptionFormatMap& GetStatCmdOptionFormats() {
307 static const OptionFormatMap option_formats = {
308 {"-a", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
309 {"--app", {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
310 {"--cpu", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
311 {"--csv", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
312 {"--duration", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
313 {"--interval", {OptionValueType::DOUBLE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
314 {"--interval-only-values",
315 {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
316 {"-e", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
317 {"--group", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
318 {"--in-app", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
319 {"--kprobe", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::NOT_ALLOWED}},
320 {"--no-inherit", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
321 {"-o", {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
322 {"--out-fd", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::CHECK_FD}},
323 {"-p", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
324 {"--per-core", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
325 {"--per-thread", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
326 {"--print-hw-counter", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
327 {"--sort", {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::ALLOWED}},
328 {"--stop-signal-fd", {OptionValueType::UINT, OptionType::SINGLE, AppRunnerType::CHECK_FD}},
329 {"-t", {OptionValueType::STRING, OptionType::MULTIPLE, AppRunnerType::ALLOWED}},
330 {"--tp-filter", {OptionValueType::STRING, OptionType::ORDERED, AppRunnerType::ALLOWED}},
331 {"--tracepoint-events",
332 {OptionValueType::STRING, OptionType::SINGLE, AppRunnerType::CHECK_PATH}},
333 {"--use-devfreq-counters",
334 {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::NOT_ALLOWED}},
335 {"--verbose", {OptionValueType::NONE, OptionType::SINGLE, AppRunnerType::ALLOWED}},
336 };
337 return option_formats;
338 }
339
340 } // namespace simpleperf