• 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 THOUSANDS_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 && 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     std::string err = "";
103     if (!IsExistDebugByApp(appPackage_, err)) {
104         return false;
105     }
106     if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
107         return false;
108     }
109     if (!Option::GetOptionValue(args, "-p", selectPids_)) {
110         HLOGD("get option -p failed");
111         return false;
112     }
113     inputPidTidArgs_ = selectPids_;
114     if (!Option::GetOptionValue(args, "-t", selectTids_)) {
115         HLOGD("get option -t failed");
116         return false;
117     }
118     inputPidTidArgs_.insert(inputPidTidArgs_.end(), selectTids_.begin(), selectTids_.end());
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 (const auto &it : countEvents) {
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 == THOUSANDS_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 == THOUSANDS_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, 1, "countEvent is nullptr");
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         }
425         return "hw-cpu-cycles";
426     }
427     if (configName == GetCommentConfigName(countEvent, "hw-instructions") && summary.eventCount != 0) {
428         double otherScale = 1.0;
429         __u64 cpuCyclesCount = 0;
430         bool other = FindPerCoreEventCount(summary, cpuCyclesCount, otherScale);
431         if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
432             comment += static_cast<double>(cpuCyclesCount) / summary.eventCount;
433             return "hw-instructions";
434         }
435     }
436     if (configName == GetCommentConfigName(countEvent, "hw-branch-misses")) {
437         double otherScale = 1.0;
438         __u64 branchInstructionsCount = 0;
439         bool other = FindPerCoreEventCount(summary, branchInstructionsCount, otherScale);
440         if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
441             branchInstructionsCount != 0) {
442             comment += (static_cast<double>(summary.eventCount) / branchInstructionsCount) * ONE_HUNDRED;
443             return "hw-branch-misses";
444         }
445     }
446     return HandleOtherConfig(comment, summary, running_time_in_sec, scale, findRunningTime);
447 }
448 
HandleOtherConfig(double & comment,PerfEvents::Summary & summary,double running_time_in_sec,double scale,bool findRunningTime)449 std::string SubCommandStat::HandleOtherConfig(double &comment, PerfEvents::Summary &summary, double running_time_in_sec,
450                                               double scale, bool findRunningTime)
451 {
452     comment = 0;
453     if (findRunningTime) {
454         double rate = 0;
455         if (scale != 0) {
456             rate = summary.eventCount / (running_time_in_sec / scale);
457         }
458         comment += rate;
459     }
460     return "";
461 }
462 
IsMonitoredAtAllTime(const double & scale)463 bool SubCommandStat::IsMonitoredAtAllTime(const double &scale)
464 {
465     constexpr double SCALE_ERROR_LIMIT = 1e-5;
466     return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
467 }
468 
GetComments(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,std::map<std::string,std::string> & comments)469 void SubCommandStat::GetComments(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
470     std::map<std::string, std::string> &comments)
471 {
472     double running_time_in_sec = 0;
473     __u64 group_id = 0;
474     double main_scale = 1.0;
475     bool findRunningTime = FindRunningTime(countEvents, running_time_in_sec, group_id, main_scale);
476     for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
477         std::string configName = it->first;
478         std::string commentConfigName = GetCommentConfigName(it->second, "sw-cpu-clock");
479         if (configName == commentConfigName) {
480             comments[configName] = "";
481             continue;
482         }
483         double scale = 1.0;
484         if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) {
485             scale = static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
486         }
487         commentConfigName = GetCommentConfigName(it->second, "sw-task-clock");
488         if (configName == commentConfigName) {
489             double usedCpus = it->second->usedCpus * scale;
490             comments[configName] = StringPrintf("%lf cpus used", usedCpus);
491             continue;
492         }
493         commentConfigName = GetCommentConfigName(it->second, "hw-cpu-cycles");
494         if (configName == commentConfigName) {
495             if (findRunningTime &&
496                 ((group_id == it->second->id) ||
497                  (IsMonitoredAtAllTime(main_scale) && IsMonitoredAtAllTime(scale)))) {
498                 double hz = 0;
499                 if (abs(running_time_in_sec) > ALMOST_ZERO) {
500                     hz = it->second->eventCount / (running_time_in_sec / scale);
501                 }
502                 comments[configName] = StringPrintf("%lf GHz", hz / 1e9);
503             } else {
504                 comments[configName] = "";
505             }
506             continue;
507         }
508         commentConfigName = GetCommentConfigName(it->second, "hw-instructions");
509         if (configName == commentConfigName && it->second->eventCount != 0) {
510             std::string cpuSyclesName = GetCommentConfigName(it->second, "hw-cpu-cycles");
511             double otherScale = 1.0;
512             __u64 cpuCyclesCount = 0;
513             bool other = FindEventCount(countEvents, cpuSyclesName, it->second->id, cpuCyclesCount,
514                                         otherScale);
515             if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
516                 double cpi = static_cast<double>(cpuCyclesCount) / it->second->eventCount;
517                 comments[configName] = StringPrintf("%lf cycles per instruction", cpi);
518                 continue;
519             }
520         }
521         commentConfigName = GetCommentConfigName(it->second, "hw-branch-misses");
522         if (configName == commentConfigName) {
523             std::string branchInsName = GetCommentConfigName(it->second, "hw-branch-instructions");
524             double otherScale = 1.0;
525             __u64 branchInstructionsCount = 0;
526             bool other = FindEventCount(countEvents, branchInsName, it->second->id,
527                                         branchInstructionsCount, otherScale);
528             if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
529                 branchInstructionsCount != 0) {
530                 double miss_rate =
531                     static_cast<double>(it->second->eventCount) / branchInstructionsCount;
532                 comments[configName] = StringPrintf("%lf miss rate", miss_rate * ONE_HUNDRED);
533                 continue;
534             }
535         }
536         if (findRunningTime && ((group_id == it->second->id) || (IsMonitoredAtAllTime(main_scale) &&
537                                                                  IsMonitoredAtAllTime(scale)))) {
538             double rate = it->second->eventCount / (running_time_in_sec / scale);
539             if (rate > 1e9) {
540                 comments[configName] = StringPrintf("%.3lf G/sec", rate / 1e9);
541                 continue;
542             }
543             if (rate > 1e6) {
544                 comments[configName] = StringPrintf("%.3lf M/sec", rate / 1e6);
545                 continue;
546             }
547             if (rate > 1e3) {
548                 comments[configName] = StringPrintf("%.3lf K/sec", rate / 1e3);
549                 continue;
550             }
551             comments[configName] = StringPrintf("%.3lf /sec", rate);
552         } else {
553             comments[configName] = "";
554         }
555     }
556 }
557 
FindRunningTime(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,double & running_time_in_sec,__u64 & group_id,double & main_scale)558 bool SubCommandStat::FindRunningTime(
559     const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
560     double &running_time_in_sec, __u64 &group_id, double &main_scale)
561 {
562     for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
563         if ((it->first == "sw-task-clock" || it->first == "sw-task-clock:u" ||
564              it->first == "sw-task-clock:k" || it->first == "sw-cpu-clock" ||
565              it->first == "sw-cpu-clock:u" || it->first == "sw-cpu-clock:k") &&
566             it->second->eventCount != 0u) {
567             group_id = it->second->id;
568             running_time_in_sec = it->second->eventCount / 1e9;
569             if (it->second->timeRunning < it->second->timeEnabled &&
570                 it->second->timeRunning != 0) {
571                 main_scale =
572                     static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
573             }
574             return true;
575         }
576     }
577     return false;
578 }
579 
FindPercoreRunningTime(PerfEvents::Summary & summary,double & running_time_int_sec,double & main_scale)580 bool SubCommandStat::FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec,
581                                             double &main_scale)
582 {
583     CHECK_TRUE(summary.eventCount == 0, false, 0, "");
584     running_time_int_sec = summary.eventCount / 1e9;
585     if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
586         main_scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
587     }
588     return true;
589 }
590 
CheckOptionPidAndApp(std::vector<pid_t> pids)591 bool SubCommandStat::CheckOptionPidAndApp(std::vector<pid_t> pids)
592 {
593     if (!CheckOptionPid(pids)) {
594         printf("Problems finding threads of monitor\n\n");
595         printf("Usage: perf stat [<options>] [<command>]\n\n");
596         printf("-p <pid>        stat events on existing process id\n");
597         printf("-t <tid>        stat events on existing thread id\n");
598         return false;
599     }
600     return true;
601 }
602 
CheckOptionPid(std::vector<pid_t> pids)603 bool SubCommandStat::CheckOptionPid(std::vector<pid_t> pids)
604 {
605     if (pids.empty()) {
606         return true;
607     }
608 
609     for (auto pid : pids) {
610         std::ostringstream oss;
611         oss << "/proc/" << pid;
612         if (!IsDir(oss.str())) {
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 HiperfError SubCommandStat::OnSubCommand(std::vector<std::string>& args)
636 {
637     CHECK_TRUE(HelpOption(), HiperfError::NO_ERR, 0, "");
638     if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
639         return HiperfError::CHECK_RESTART_OPTION_FAIL;
640     }
641     // check option
642     if (!CheckSelectCpuPidOption()) {
643         return HiperfError::CHECK_SELECT_CPU_PID_FAIL;
644     }
645     if (!CheckOptions(selectPids_)) {
646         HLOGV("CheckOptions() failed");
647         return HiperfError::CHECK_STAT_OPTION_FAIL;
648     }
649     if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) {
650         HLOGV("CheckAppIsRunning() failed");
651         return HiperfError::CHECK_APP_RUNNING_FAIL;
652     }
653     if (!CheckOptionPid(selectPids_)) {
654         HLOGV("CheckOptionPid() failed");
655         return HiperfError::CHECK_OPTION_PID_FAIL;
656     }
657 
658     perfEvents_.SetCpu(selectCpus_);
659     std::vector<pid_t> pids;
660     for (auto selectPid : selectPids_) {
661         HLOGD("[OnSubCommand] selectPid %d\n", selectPid);
662         std::vector<pid_t> subTids = GetSubthreadIDs(selectPid, thread_map_);
663         if (!subTids.empty()) {
664             pids.insert(pids.end(), subTids.begin(), subTids.end());
665         } else {
666             HLOGD("[OnSubCommand] subTids empty for %d\n", selectPid);
667         }
668     }
669     pids.insert(pids.end(), selectTids_.begin(), selectTids_.end());
670     perfEvents_.SetPid(pids);
671     if (!CheckOptionPidAndApp(pids)) {
672         HLOGV("CheckOptionPidAndApp() failed");
673         return HiperfError::CHECK_OPTION_PID_APP_FAIL;
674     }
675     std::string err = "";
676     if (!IsExistDebugByPid(inputPidTidArgs_, err)) {
677         return HiperfError::CHECK_OPTION_PID_APP_FAIL;
678     }
679     SetPerfEvent();
680     if (!PrepairEvents()) {
681         HLOGV("PrepairEvents() failed");
682         return HiperfError::PREPAIR_EVENTS_FAIL;
683     }
684 
685     // preapare fd
686     perfEvents_.PrepareTracking();
687 
688     // start tracking
689     perfEvents_.StartTracking();
690 
691     return HiperfError::NO_ERR;
692 }
693 
RegisterSubCommandStat()694 bool RegisterSubCommandStat()
695 {
696     return SubCommand::RegisterSubCommand("stat", SubCommandStat::GetInstance);
697 }
698 
PrepairEvents()699 bool SubCommandStat::PrepairEvents()
700 {
701     if (selectEvents_.empty() && selectGroups_.empty()) {
702         perfEvents_.AddDefaultEvent(PERF_TYPE_HARDWARE);
703         perfEvents_.AddDefaultEvent(PERF_TYPE_SOFTWARE);
704     } else {
705         for (auto events : selectEvents_) {
706             if (!perfEvents_.AddEvents(events)) {
707                 HLOGV("add events failed");
708                 return false;
709             }
710         }
711         for (auto events : selectGroups_) {
712             if (!perfEvents_.AddEvents(events, true)) {
713                 HLOGV("add groups failed");
714                 return false;
715             }
716         }
717     }
718     return true;
719 }
720 
CheckSelectCpuPidOption()721 bool SubCommandStat::CheckSelectCpuPidOption()
722 {
723     if (!selectCpus_.empty()) {
724         // the only value is not -1
725         if (!(selectCpus_.size() == 1 && selectCpus_.front() == -1)) {
726             int maxCpuid = sysconf(_SC_NPROCESSORS_CONF) - 1;
727             for (auto cpu : selectCpus_) {
728                 if (cpu < 0 || cpu > maxCpuid) {
729                     printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid);
730                     return false;
731                 }
732             }
733         }
734     } else {
735         // the cpu default -1
736         if (!targetSystemWide_) {
737             selectCpus_.push_back(-1);
738         }
739     }
740 
741     if (!selectPids_.empty()) {
742         for (auto pid : selectPids_) {
743             if (pid <= 0) {
744                 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid);
745                 return false;
746             }
747         }
748     }
749     if (!selectTids_.empty()) {
750         for (auto tid : selectTids_) {
751             if (tid <= 0) {
752                 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid);
753                 return false;
754             }
755         }
756     }
757     return true;
758 }
759 
CheckOptions(const std::vector<pid_t> & pids)760 bool SubCommandStat::CheckOptions(const std::vector<pid_t> &pids)
761 {
762     if (targetSystemWide_) {
763         if (!pids.empty() || !selectTids_.empty()) {
764             printf("You cannot specify -a and -t/-p at the same time\n");
765             return false;
766         }
767         if (!appPackage_.empty()) {
768             printf("You cannot specify -a and --app at the same time\n");
769             return false;
770         }
771     }
772     if (!appPackage_.empty() && (!pids.empty() || !selectTids_.empty())) {
773         printf("You cannot specify --app and -t/-p at the same time\n");
774         return false;
775     }
776     if (!targetSystemWide_ && trackedCommand_.empty() && pids.empty() && appPackage_.empty()
777         && selectTids_.empty()) {
778         printf("You need to set the -p option or --app option.\n");
779         return false;
780     }
781     if (targetSystemWide_ && !trackedCommand_.empty()) {
782         printf("You cannot specify -a and a cmd at the same time\n");
783         return false;
784     }
785     if (!trackedCommand_.empty()) {
786         if (!pids.empty() || !selectTids_.empty()) {
787             printf("You cannot specify a cmd and -t/-p at the same time\n");
788             return false;
789         }
790         if (!appPackage_.empty()) {
791             printf("You cannot specify a cmd and --app at the same time\n");
792             return false;
793         }
794         if (!IsRoot()) {
795             printf("%s options needs root privilege, please check usage\n",
796                    VectorToString(trackedCommand_).c_str());
797             return false;
798         }
799     }
800     if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) {
801         printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
802                MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
803         return false;
804     }
805     if (timeStopSec_ < 0) {
806         printf("monitoring duration should be positive but %f is given\n", timeStopSec_);
807         return false;
808     }
809     if (timeReportMs_ < 0) {
810         printf("print interval should be non-negative but %d is given\n", timeReportMs_);
811         return false;
812     }
813     return true;
814 }
815 
AddReportArgs(CommandReporter & reporter)816 void SubCommandStat::AddReportArgs(CommandReporter& reporter)
817 {
818     if (targetSystemWide_) {
819         reporter.targetProcess_ = "ALL";
820     } else if (!appPackage_.empty()) {
821         reporter.targetProcess_ = appPackage_;
822     } else {
823         std::unordered_set<std::string> processNames = {};
824         for_each(selectPids_.begin(), selectPids_.end(), [&processNames] (const pid_t& pid) {
825             processNames.insert(GetProcessName(pid));
826         });
827         reporter.targetProcess_ = SetToString<std::string>(processNames);
828     }
829 }
830 
GetInstance()831 SubCommand& SubCommandStat::GetInstance()
832 {
833     static SubCommandStat subCommand;
834     return subCommand;
835 }
836 } // namespace HiPerf
837 } // namespace Developtools
838 } // namespace OHOS
839