• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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