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