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