1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define HILOG_TAG "Stat"
17
18 #include "subcommand_stat.h"
19
20 #include <csignal>
21 #include <iostream>
22 #include <memory>
23
24 #include "debug_logger.h"
25 #include "hiperf_hilog.h"
26 #include "utilities.h"
27
28 const uint16_t ONE_HUNDRED = 100;
29 const uint16_t THOUSNADS_SEPARATOR = 3;
30 namespace OHOS {
31 namespace Developtools {
32 namespace HiPerf {
33 static std::map<pid_t, ThreadInfos> thread_map_;
34 static bool g_reportCpuFlag = false;
35 static bool g_reportThreadFlag = false;
36 static VirtualRuntime g_runtimeInstance;
DumpOptions() const37 void SubCommandStat::DumpOptions() const
38 {
39 printf("DumpOptions:\n");
40 printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false");
41 printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str());
42 printf(" timeStopSec:\t%f sec\n", timeStopSec_);
43 printf(" timeReportMs:\t%d ms\n", timeReportMs_);
44 printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str());
45 printf(" selectGroups:\t%s\n", VectorToString(selectGroups_).c_str());
46 printf(" noCreateNew:\t%s\n", noCreateNew_ ? "true" : "false");
47 printf(" appPackage:\t%s\n", appPackage_.c_str());
48 printf(" checkAppMs_:\t%d\n", checkAppMs_);
49 printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str());
50 printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str());
51 printf(" restart:\t%s\n", restart_ ? "true" : "false");
52 printf(" perCore:\t%s\n", perCpus_ ? "true" : "false");
53 printf(" perTread:\t%s\n", perThreads_ ? "true" : "false");
54 printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false");
55 }
56
ParseOption(std::vector<std::string> &args)57 bool SubCommandStat::ParseOption(std::vector<std::string> &args)
58 {
59 if (args.size() == 1 and args[0] == "-h") {
60 args.clear();
61 helpOption_ = true;
62 PrintUsage();
63 return true;
64 }
65 if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) {
66 HLOGD("get option -a failed");
67 return false;
68 }
69 if (targetSystemWide_ && !IsSupportNonDebuggableApp()) {
70 HLOGD("-a option needs root privilege for system wide profiling.");
71 printf("-a option needs root privilege for system wide profiling.\n");
72 return false;
73 }
74 if (!Option::GetOptionValue(args, "-c", selectCpus_)) {
75 HLOGD("get option -c failed");
76 return false;
77 }
78 if (!Option::GetOptionValue(args, "-d", timeStopSec_)) {
79 HLOGD("get option -d failed");
80 return false;
81 }
82 if (!Option::GetOptionValue(args, "-i", timeReportMs_)) {
83 HLOGD("get option -i failed");
84 return false;
85 }
86 if (!Option::GetOptionValue(args, "-e", selectEvents_)) {
87 HLOGD("get option -e failed");
88 return false;
89 }
90 if (!Option::GetOptionValue(args, "-g", selectGroups_)) {
91 HLOGD("get option -g failed");
92 return false;
93 }
94 if (!Option::GetOptionValue(args, "--no-inherit", noCreateNew_)) {
95 HLOGD("get option --no-inherit failed");
96 return false;
97 }
98 if (!Option::GetOptionValue(args, "--app", appPackage_)) {
99 HLOGD("get option --app failed");
100 return false;
101 }
102 if (!IsExistDebugByApp(appPackage_)) {
103 return false;
104 }
105 if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
106 return false;
107 }
108 if (!Option::GetOptionValue(args, "-p", selectPids_)) {
109 HLOGD("get option -p failed");
110 return false;
111 }
112 if (!IsExistDebugByPid(selectPids_)) {
113 return false;
114 }
115 if (!Option::GetOptionValue(args, "-t", selectTids_)) {
116 HLOGD("get option -t failed");
117 return false;
118 }
119 if (!Option::GetOptionValue(args, "--restart", restart_)) {
120 HLOGD("get option --restart failed");
121 return false;
122 }
123 if (!Option::GetOptionValue(args, "--per-core", perCpus_)) {
124 HLOGD("get option --per-core failed");
125 return false;
126 }
127 if (!Option::GetOptionValue(args, "--per-thread", perThreads_)) {
128 HLOGD("get option --per-thread failed");
129 return false;
130 }
131 if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) {
132 HLOGD("get option --verbose failed");
133 return false;
134 }
135 return ParseSpecialOption(args);
136 }
137
ParseSpecialOption(std::vector<std::string> &args)138 bool SubCommandStat::ParseSpecialOption(std::vector<std::string> &args)
139 {
140 if (!Option::GetOptionTrackedCommand(args, trackedCommand_)) {
141 HLOGD("get cmd failed");
142 return false;
143 }
144 if (!args.empty()) {
145 HLOGD("redundant option(s)");
146 return false;
147 }
148 return true;
149 }
150
PrintUsage()151 void SubCommandStat::PrintUsage()
152 {
153 printf("%s\n", Help().c_str());
154 }
155
SetReportFlags(bool cpuFlag, bool threadFlag)156 void SubCommandStat::SetReportFlags(bool cpuFlag, bool threadFlag)
157 {
158 g_reportCpuFlag = cpuFlag;
159 g_reportThreadFlag = threadFlag;
160 }
161
Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)162 void SubCommandStat::Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
163 {
164 bool isNeedPerCpuTid = false;
165 for (auto it = countEvents.begin(); it != countEvents.end(); ++it) {
166 if (!(it->second->summaries.empty())) {
167 isNeedPerCpuTid = true;
168 break;
169 }
170 }
171 if (isNeedPerCpuTid) {
172 PrintPerHead();
173 ReportDetailInfos(countEvents);
174 } else {
175 ReportNormal(countEvents);
176 }
177 }
178
PrintPerHead()179 void SubCommandStat::PrintPerHead()
180 {
181 // print head
182 if (g_reportCpuFlag && g_reportThreadFlag) {
183 printf(" %24s %-30s | %-30s %10s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name",
184 "pid", "tid", "coreid", "comment", "coverage");
185 return;
186 }
187 if (g_reportCpuFlag) {
188 printf(" %24s %-30s | %10s | %-32s | %s\n", "count", "event_name", "coreid", "comment", "coverage");
189 return;
190 }
191 printf(" %24s %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", "pid", "tid",
192 "comment", "coverage");
193 return;
194 }
195
PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, const float &ratio, std::string &configName)196 void SubCommandStat::PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, const float &ratio,
197 std::string &configName)
198 {
199 if (reportSum == nullptr) {
200 return;
201 }
202 // print value
203 std::string strEventCount = std::to_string(reportSum->eventCountSum);
204 for (size_t i = strEventCount.size() >= 1 ? strEventCount.size() - 1 : 0, j = 1; i > 0; --i, ++j) {
205 if (j == THOUSNADS_SEPARATOR) {
206 j = 0;
207 strEventCount.insert(strEventCount.begin() + i, ',');
208 }
209 }
210
211 std::string commentStr;
212 MakeComments(reportSum, commentStr);
213
214 if (g_reportCpuFlag && g_reportThreadFlag) {
215 printf(" %24s %-30s | %-30s %10d %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
216 reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, reportSum->cpu, commentStr.c_str(),
217 reportSum->scaleSum * ratio);
218 } else if (g_reportCpuFlag) {
219 printf(" %24s %-30s | %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
220 reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio);
221 } else {
222 printf(" %24s %-30s | %-30s %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
223 reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, commentStr.c_str(),
224 reportSum->scaleSum * ratio);
225 }
226 fflush(stdout);
227 }
228
InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> &newPerMap, const PerfEvents::Summary &summary, VirtualRuntime& virtualInstance)229 void SubCommandStat::InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> &newPerMap,
230 const PerfEvents::Summary &summary, VirtualRuntime& virtualInstance)
231 {
232 CHECK_TRUE(newPerMap == nullptr, NO_RETVAL, 0, "");
233 newPerMap->cpu = summary.cpu;
234 if (g_reportCpuFlag && !g_reportThreadFlag) {
235 return;
236 }
237 newPerMap->tid = summary.tid;
238 newPerMap->pid = thread_map_.find(summary.tid)->second.pid;
239 bool isTid = true;
240 if (newPerMap->pid == newPerMap->tid) {
241 isTid = false;
242 }
243 newPerMap->threadName = virtualInstance.ReadThreadName(summary.tid, isTid);
244 }
245
GetPerKey(std::string &perKey, const PerfEvents::Summary &summary)246 void SubCommandStat::GetPerKey(std::string &perKey, const PerfEvents::Summary &summary)
247 {
248 perKey = "";
249 if (g_reportCpuFlag) {
250 perKey += std::to_string(summary.cpu);
251 perKey += "|";
252 }
253 if (g_reportThreadFlag) {
254 perKey += std::to_string(summary.tid);
255 }
256 return;
257 }
258
ReportDetailInfos( const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)259 void SubCommandStat::ReportDetailInfos(
260 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
261 {
262 std::string perKey = "";
263 std::map<std::string, std::unique_ptr<PerfEvents::ReportSum>> perMaps;
264 for (auto event = countEvents.begin(); event != countEvents.end(); ++event) {
265 if (event->second == nullptr || event->second->eventCount == 0) {
266 continue;
267 }
268 constexpr float ratio {100.0};
269 std::string configName = event->first;
270 perMaps.clear();
271 for (auto &it : event->second->summaries) {
272 GetPerKey(perKey, it);
273 if (perMaps.count(perKey) == 0) {
274 auto perMap = std::make_unique<PerfEvents::ReportSum>(PerfEvents::ReportSum {});
275 InitPerMap(perMap, it, g_runtimeInstance);
276 perMaps[perKey] = std::move(perMap);
277 }
278 if (perMaps[perKey] == nullptr) {
279 continue;
280 }
281 perMaps[perKey]->configName = GetDetailComments(event->second, perMaps[perKey]->commentSum,
282 it, configName);
283 perMaps[perKey]->eventCountSum += it.eventCount;
284 if (it.timeRunning < it.timeEnabled && it.timeRunning != 0) {
285 perMaps[perKey]->scaleSum = 1 / (static_cast<double>(it.timeEnabled) / it.timeRunning);
286 }
287 }
288 for (auto iper = perMaps.begin(); iper != perMaps.end(); iper++) {
289 PrintPerValue(iper->second, ratio, configName);
290 }
291 }
292 }
293
ReportNormal( const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)294 void SubCommandStat::ReportNormal(
295 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
296 {
297 // print head
298 printf(" %24s %-30s | %-32s | %s\n", "count", "name", "comment", "coverage");
299 std::map<std::string, std::string> comments;
300 GetComments(countEvents, comments);
301 for (auto it = countEvents.begin(); it != countEvents.end(); ++it) {
302 double scale = 1.0;
303 constexpr float ratio {100.0};
304 std::string configName = it->first;
305 std::string comment = comments[configName];
306 std::string strEventCount = std::to_string(it->second->eventCount);
307 for (size_t i = strEventCount.size() >= 1 ? strEventCount.size() - 1 : 0, j = 1; i > 0; --i, ++j) {
308 if (j == THOUSNADS_SEPARATOR) {
309 strEventCount.insert(strEventCount.begin() + i, ',');
310 j = 0;
311 }
312 }
313 if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) {
314 scale = 1 / (static_cast<double>(it->second->timeEnabled) / it->second->timeRunning);
315 }
316 printf(" %24s %-30s | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
317 comment.c_str(), scale * ratio);
318
319 fflush(stdout);
320 }
321 }
322
FindEventCount(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents, const std::string &configName, const __u64 group_id, __u64 &eventCount, double &scale)323 bool SubCommandStat::FindEventCount(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
324 const std::string &configName, const __u64 group_id, __u64 &eventCount, double &scale)
325 {
326 auto itr = countEvents.find(configName);
327 if (itr != countEvents.end()) {
328 eventCount = itr->second->eventCount;
329 if (itr->second->id == group_id
330 && itr->second->timeRunning < itr->second->timeEnabled
331 && itr->second->timeRunning != 0) {
332 scale = static_cast<double>(itr->second->timeEnabled) / itr->second->timeRunning;
333 return true;
334 }
335 }
336 return false;
337 }
338
FindPerCoreEventCount(PerfEvents::Summary &summary, __u64 &eventCount, double &scale)339 bool SubCommandStat::FindPerCoreEventCount(PerfEvents::Summary &summary, __u64 &eventCount, double &scale)
340 {
341 eventCount = summary.eventCount;
342 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
343 scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
344 return true;
345 }
346 return false;
347 }
348
GetCommentConfigName( const std::unique_ptr<PerfEvents::CountEvent> &countEvent, std::string eventName)349 std::string SubCommandStat::GetCommentConfigName(
350 const std::unique_ptr<PerfEvents::CountEvent> &countEvent, std::string eventName)
351 {
352 std::string commentConfigName = "";
353 CHECK_TRUE(countEvent == nullptr || eventName.length() == 0, commentConfigName, 0, "");
354 if (countEvent->userOnly) {
355 commentConfigName = eventName + ":u";
356 } else if (countEvent->kernelOnly) {
357 commentConfigName = eventName + ":k";
358 } else {
359 commentConfigName = eventName;
360 }
361 return commentConfigName;
362 }
363
MakeComments(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, std::string &commentStr)364 void SubCommandStat::MakeComments(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, std::string &commentStr)
365 {
366 CHECK_TRUE(reportSum == nullptr || reportSum->commentSum == 0, NO_RETVAL, 0, "");
367 if (reportSum->configName == "sw-task-clock") {
368 commentStr = StringPrintf("%lf cpus used", reportSum->commentSum);
369 return;
370 }
371 if (reportSum->configName == "hw-cpu-cycles") {
372 commentStr = StringPrintf("%lf GHz", reportSum->commentSum);
373 return;
374 }
375 if (reportSum->configName == "hw-instructions") {
376 commentStr = StringPrintf("%lf cycles per instruction", reportSum->commentSum);
377 return;
378 }
379 if (reportSum->configName == "hw-branch-misses") {
380 commentStr = StringPrintf("%lf miss rate", reportSum->commentSum);
381 return;
382 }
383
384 if (reportSum->commentSum > 1e9) {
385 commentStr = StringPrintf("%.3lf G/sec", reportSum->commentSum / 1e9);
386 return;
387 }
388 if (reportSum->commentSum > 1e6) {
389 commentStr = StringPrintf("%.3lf M/sec", reportSum->commentSum / 1e6);
390 return;
391 }
392 if (reportSum->commentSum > 1e3) {
393 commentStr = StringPrintf("%.3lf K/sec", reportSum->commentSum / 1e3);
394 return;
395 }
396 commentStr = StringPrintf("%.3lf /sec", reportSum->commentSum);
397 }
398
GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> &countEvent, double &comment, PerfEvents::Summary &summary, std::string &configName)399 std::string SubCommandStat::GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> &countEvent,
400 double &comment, PerfEvents::Summary &summary, std::string &configName)
401 {
402 double running_time_in_sec = 0;
403 double main_scale = 1.0;
404 bool findRunningTime = FindPercoreRunningTime(summary, running_time_in_sec, main_scale);
405 if (configName == GetCommentConfigName(countEvent, "sw-cpu-clock")) {
406 comment = 0;
407 return "sw-cpu-clock";
408 }
409 double scale = 1.0;
410 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
411 scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
412 }
413 if (configName == GetCommentConfigName(countEvent, "sw-task-clock")) {
414 comment += countEvent->usedCpus * scale;
415 return "sw-task-clock";
416 }
417 if (configName == GetCommentConfigName(countEvent, "hw-cpu-cycles")) {
418 if (findRunningTime) {
419 double hz = 0;
420 if (abs(running_time_in_sec) > ALMOST_ZERO) {
421 hz = summary.eventCount / (running_time_in_sec / scale);
422 }
423 comment += hz / 1e9;
424 } else {
425 comment += 0;
426 }
427 return "hw-cpu-cycles";
428 }
429 if (configName == GetCommentConfigName(countEvent, "hw-instructions") && summary.eventCount != 0) {
430 double otherScale = 1.0;
431 __u64 cpuCyclesCount = 0;
432 bool other = FindPerCoreEventCount(summary, cpuCyclesCount, otherScale);
433 if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
434 comment += static_cast<double>(cpuCyclesCount) / summary.eventCount;
435 return "hw-instructions";
436 }
437 }
438 if (configName == GetCommentConfigName(countEvent, "hw-branch-misses")) {
439 double otherScale = 1.0;
440 __u64 branchInstructionsCount = 0;
441 bool other = FindPerCoreEventCount(summary, branchInstructionsCount, otherScale);
442 if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
443 branchInstructionsCount != 0) {
444 comment += (static_cast<double>(summary.eventCount) / branchInstructionsCount) * ONE_HUNDRED;
445 return "hw-branch-misses";
446 }
447 }
448 return HandleOtherConfig(comment, summary, running_time_in_sec, scale, findRunningTime);
449 }
450
HandleOtherConfig(double &comment, PerfEvents::Summary &summary, double running_time_in_sec, double scale, bool findRunningTime)451 std::string SubCommandStat::HandleOtherConfig(double &comment, PerfEvents::Summary &summary, double running_time_in_sec,
452 double scale, bool findRunningTime)
453 {
454 comment = 0;
455 if (findRunningTime) {
456 double rate = 0;
457 if (scale != 0) {
458 rate = summary.eventCount / (running_time_in_sec / scale);
459 }
460 comment += rate;
461 }
462 return "";
463 }
464
IsMonitoredAtAllTime(const double &scale)465 bool SubCommandStat::IsMonitoredAtAllTime(const double &scale)
466 {
467 constexpr double SCALE_ERROR_LIMIT = 1e-5;
468 return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
469 }
470
GetComments(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents, std::map<std::string, std::string> &comments)471 void SubCommandStat::GetComments(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
472 std::map<std::string, std::string> &comments)
473 {
474 double running_time_in_sec = 0;
475 __u64 group_id = 0;
476 double main_scale = 1.0;
477 bool findRunningTime = FindRunningTime(countEvents, running_time_in_sec, group_id, main_scale);
478 for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
479 std::string configName = it->first;
480 std::string commentConfigName = GetCommentConfigName(it->second, "sw-cpu-clock");
481 if (configName == commentConfigName) {
482 comments[configName] = "";
483 continue;
484 }
485 double scale = 1.0;
486 if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) {
487 scale = static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
488 }
489 commentConfigName = GetCommentConfigName(it->second, "sw-task-clock");
490 if (configName == commentConfigName) {
491 double usedCpus = it->second->usedCpus * scale;
492 comments[configName] = StringPrintf("%lf cpus used", usedCpus);
493 continue;
494 }
495 commentConfigName = GetCommentConfigName(it->second, "hw-cpu-cycles");
496 if (configName == commentConfigName) {
497 if (findRunningTime &&
498 ((group_id == it->second->id) ||
499 (IsMonitoredAtAllTime(main_scale) && IsMonitoredAtAllTime(scale)))) {
500 double hz = 0;
501 if (abs(running_time_in_sec) > ALMOST_ZERO) {
502 hz = it->second->eventCount / (running_time_in_sec / scale);
503 }
504 comments[configName] = StringPrintf("%lf GHz", hz / 1e9);
505 } else {
506 comments[configName] = "";
507 }
508 continue;
509 }
510 commentConfigName = GetCommentConfigName(it->second, "hw-instructions");
511 if (configName == commentConfigName && it->second->eventCount != 0) {
512 std::string cpuSyclesName = GetCommentConfigName(it->second, "hw-cpu-cycles");
513 double otherScale = 1.0;
514 __u64 cpuCyclesCount = 0;
515 bool other = FindEventCount(countEvents, cpuSyclesName, it->second->id, cpuCyclesCount,
516 otherScale);
517 if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
518 double cpi = static_cast<double>(cpuCyclesCount) / it->second->eventCount;
519 comments[configName] = StringPrintf("%lf cycles per instruction", cpi);
520 continue;
521 }
522 }
523 commentConfigName = GetCommentConfigName(it->second, "hw-branch-misses");
524 if (configName == commentConfigName) {
525 std::string branchInsName = GetCommentConfigName(it->second, "hw-branch-instructions");
526 double otherScale = 1.0;
527 __u64 branchInstructionsCount = 0;
528 bool other = FindEventCount(countEvents, branchInsName, it->second->id,
529 branchInstructionsCount, otherScale);
530 if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
531 branchInstructionsCount != 0) {
532 double miss_rate =
533 static_cast<double>(it->second->eventCount) / branchInstructionsCount;
534 comments[configName] = StringPrintf("%lf miss rate", miss_rate * ONE_HUNDRED);
535 continue;
536 }
537 }
538 if (findRunningTime && ((group_id == it->second->id) || (IsMonitoredAtAllTime(main_scale) &&
539 IsMonitoredAtAllTime(scale)))) {
540 double rate = it->second->eventCount / (running_time_in_sec / scale);
541 if (rate > 1e9) {
542 comments[configName] = StringPrintf("%.3lf G/sec", rate / 1e9);
543 continue;
544 }
545 if (rate > 1e6) {
546 comments[configName] = StringPrintf("%.3lf M/sec", rate / 1e6);
547 continue;
548 }
549 if (rate > 1e3) {
550 comments[configName] = StringPrintf("%.3lf K/sec", rate / 1e3);
551 continue;
552 }
553 comments[configName] = StringPrintf("%.3lf /sec", rate);
554 } else {
555 comments[configName] = "";
556 }
557 }
558 }
559
FindRunningTime( const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents, double &running_time_in_sec, __u64 &group_id, double &main_scale)560 bool SubCommandStat::FindRunningTime(
561 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
562 double &running_time_in_sec, __u64 &group_id, double &main_scale)
563 {
564 for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
565 if ((it->first == "sw-task-clock" || it->first == "sw-task-clock:u" ||
566 it->first == "sw-task-clock:k" || it->first == "sw-cpu-clock" ||
567 it->first == "sw-cpu-clock:u" || it->first == "sw-cpu-clock:k") &&
568 it->second->eventCount != 0u) {
569 group_id = it->second->id;
570 running_time_in_sec = it->second->eventCount / 1e9;
571 if (it->second->timeRunning < it->second->timeEnabled &&
572 it->second->timeRunning != 0) {
573 main_scale =
574 static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
575 }
576 return true;
577 }
578 }
579 return false;
580 }
581
FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec, double &main_scale)582 bool SubCommandStat::FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec,
583 double &main_scale)
584 {
585 CHECK_TRUE(summary.eventCount == 0, false, 0, "");
586 running_time_int_sec = summary.eventCount / 1e9;
587 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
588 main_scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
589 }
590 return true;
591 }
592
CheckOptionPidAndApp(std::vector<pid_t> pids)593 bool SubCommandStat::CheckOptionPidAndApp(std::vector<pid_t> pids)
594 {
595 if (!CheckOptionPid(pids)) {
596 printf("Problems finding threads of monitor\n\n");
597 printf("Usage: perf stat [<options>] [<command>]\n\n");
598 printf("-p <pid> stat events on existing process id\n");
599 printf("-t <tid> stat events on existing thread id\n");
600 return false;
601 }
602 return true;
603 }
604
CheckOptionPid(std::vector<pid_t> pids)605 bool SubCommandStat::CheckOptionPid(std::vector<pid_t> pids)
606 {
607 if (pids.empty()) {
608 return true;
609 }
610
611 for (auto pid : pids) {
612 if (!IsDir("/proc/" + std::to_string(pid))) {
613 printf("not exit pid %d\n", pid);
614 return false;
615 }
616 }
617 return true;
618 }
619
SetPerfEvent()620 void SubCommandStat::SetPerfEvent()
621 {
622 SetReportFlags(perCpus_, perThreads_);
623 perfEvents_.SetSystemTarget(targetSystemWide_);
624 perfEvents_.SetTimeOut(timeStopSec_);
625 perfEvents_.SetTimeReport(timeReportMs_);
626 perfEvents_.SetPerCpu(perCpus_);
627 perfEvents_.SetPerThread(perThreads_);
628 perfEvents_.SetVerboseReport(verboseReport_);
629 perfEvents_.SetInherit(!noCreateNew_);
630 perfEvents_.SetTrackedCommand(trackedCommand_);
631 // set report handle
632 perfEvents_.SetStatCallBack(Report);
633 }
634
OnSubCommand(std::vector<std::string> &args)635 bool SubCommandStat::OnSubCommand(std::vector<std::string> &args)
636 {
637 CHECK_TRUE(HelpOption(), true, 0, "");
638 if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
639 return false;
640 }
641 // check option
642 if (!CheckSelectCpuPidOption()) {
643 return false;
644 }
645 if (!CheckOptions(selectPids_)) {
646 HLOGV("CheckOptions() failed");
647 return false;
648 }
649 if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) {
650 HLOGV("CheckAppIsRunning() failed");
651 return false;
652 }
653
654 perfEvents_.SetCpu(selectCpus_);
655 std::vector<pid_t> pids;
656 for (auto selectPid : selectPids_) {
657 HLOGD("[OnSubCommand] selectPid %d\n", selectPid);
658 std::vector<pid_t> subTids = GetSubthreadIDs(selectPid, thread_map_);
659 if (!subTids.empty()) {
660 pids.insert(pids.end(), subTids.begin(), subTids.end());
661 } else {
662 HLOGD("[OnSubCommand] subTids empty for %d\n", selectPid);
663 }
664 }
665 pids.insert(pids.end(), selectTids_.begin(), selectTids_.end());
666 perfEvents_.SetPid(pids);
667 if (!CheckOptionPidAndApp(pids)) {
668 HLOGV("CheckOptionPidAndApp() failed");
669 return false;
670 }
671 SetPerfEvent();
672 if (!PrepairEvents()) {
673 HLOGV("PrepairEvents() failed");
674 return false;
675 }
676
677 // preapare fd
678 perfEvents_.PrepareTracking();
679
680 // start tracking
681 perfEvents_.StartTracking();
682
683 return true;
684 }
685
RegisterSubCommandStat()686 bool RegisterSubCommandStat()
687 {
688 return SubCommand::RegisterSubCommand("stat", std::make_unique<SubCommandStat>());
689 }
690
PrepairEvents()691 bool SubCommandStat::PrepairEvents()
692 {
693 if (selectEvents_.empty() && selectGroups_.empty()) {
694 perfEvents_.AddDefaultEvent(PERF_TYPE_HARDWARE);
695 perfEvents_.AddDefaultEvent(PERF_TYPE_SOFTWARE);
696 } else {
697 for (auto events : selectEvents_) {
698 if (!perfEvents_.AddEvents(events)) {
699 HLOGV("add events failed");
700 return false;
701 }
702 }
703 for (auto events : selectGroups_) {
704 if (!perfEvents_.AddEvents(events, true)) {
705 HLOGV("add groups failed");
706 return false;
707 }
708 }
709 }
710 return true;
711 }
712
CheckSelectCpuPidOption()713 bool SubCommandStat::CheckSelectCpuPidOption()
714 {
715 if (!selectCpus_.empty()) {
716 // the only value is not -1
717 if (!(selectCpus_.size() == 1 && selectCpus_.front() == -1)) {
718 int maxCpuid = sysconf(_SC_NPROCESSORS_CONF) - 1;
719 for (auto cpu : selectCpus_) {
720 if (cpu < 0 || cpu > maxCpuid) {
721 printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid);
722 return false;
723 }
724 }
725 }
726 } else {
727 // the cpu default -1
728 if (!targetSystemWide_) {
729 selectCpus_.push_back(-1);
730 }
731 }
732
733 if (!selectPids_.empty()) {
734 for (auto pid : selectPids_) {
735 if (pid <= 0) {
736 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid);
737 return false;
738 }
739 }
740 }
741 if (!selectTids_.empty()) {
742 for (auto tid : selectTids_) {
743 if (tid <= 0) {
744 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid);
745 return false;
746 }
747 }
748 }
749 return true;
750 }
751
CheckOptions(const std::vector<pid_t> &pids)752 bool SubCommandStat::CheckOptions(const std::vector<pid_t> &pids)
753 {
754 if (targetSystemWide_) {
755 if (!pids.empty() || !selectTids_.empty()) {
756 printf("You cannot specify -a and -t/-p at the same time\n");
757 return false;
758 }
759 if (!appPackage_.empty()) {
760 printf("You cannot specify -a and --app at the same time\n");
761 return false;
762 }
763 }
764 if (!appPackage_.empty() && (!pids.empty() || !selectTids_.empty())) {
765 printf("You cannot specify --app and -t/-p at the same time\n");
766 return false;
767 }
768 if (!targetSystemWide_ && trackedCommand_.empty() && pids.empty() && appPackage_.empty()
769 && selectTids_.empty()) {
770 printf("You need to set the -p option or --app option.\n");
771 return false;
772 }
773 if (targetSystemWide_ && !trackedCommand_.empty()) {
774 printf("You cannot specify -a and a cmd at the same time\n");
775 return false;
776 }
777 if (!trackedCommand_.empty()) {
778 if (!pids.empty() || !selectTids_.empty()) {
779 printf("You cannot specify a cmd and -t/-p at the same time\n");
780 return false;
781 }
782 if (!appPackage_.empty()) {
783 printf("You cannot specify a cmd and --app at the same time\n");
784 return false;
785 }
786 if (!IsRoot()) {
787 printf("%s options needs root privilege, please check usage\n",
788 VectorToString(trackedCommand_).c_str());
789 return false;
790 }
791 }
792 if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) {
793 printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
794 MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
795 return false;
796 }
797 if (timeStopSec_ < 0) {
798 printf("monitoring duration should be positive but %f is given\n", timeStopSec_);
799 return false;
800 }
801 if (timeReportMs_ < 0) {
802 printf("print interval should be non-negative but %d is given\n", timeReportMs_);
803 return false;
804 }
805 return true;
806 }
807 } // namespace HiPerf
808 } // namespace Developtools
809 } // namespace OHOS
810