• 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 "utilities.h"
26 
27 const uint16_t ONE_HUNDRED = 100;
28 const uint16_t THOUSNADS_SEPARATOR = 3;
29 namespace OHOS {
30 namespace Developtools {
31 namespace HiPerf {
32 static std::map<pid_t, ThreadInfos> thread_map_;
33 static bool g_reportCpuFlag = false;
34 static bool g_reportThreadFlag = false;
35 static VirtualRuntime runtimeInstance_;
DumpOptions() const36 void SubCommandStat::DumpOptions() const
37 {
38     printf("DumpOptions:\n");
39     printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false");
40     printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str());
41     printf(" timeStopSec:\t%f sec\n", timeStopSec_);
42     printf(" timeReportMs:\t%d ms\n", timeReportMs_);
43     printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str());
44     printf(" selectGroups:\t%s\n", VectorToString(selectGroups_).c_str());
45     printf(" noCreateNew:\t%s\n", noCreateNew_ ? "true" : "false");
46     printf(" appPackage:\t%s\n", appPackage_.c_str());
47     printf(" checkAppMs_:\t%d\n", checkAppMs_);
48     printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str());
49     printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str());
50     printf(" restart:\t%s\n", restart_ ? "true" : "false");
51     printf(" perCore:\t%s\n", perCpus_ ? "true" : "false");
52     printf(" perTread:\t%s\n", perThreads_ ? "true" : "false");
53     printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false");
54 }
55 
ParseOption(std::vector<std::string> & args)56 bool SubCommandStat::ParseOption(std::vector<std::string> &args)
57 {
58     if (args.size() == 1 and args[0] == "-h") {
59         args.clear();
60         helpOption_ = true;
61         PrintUsage();
62         return true;
63     }
64     if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) {
65         HLOGD("get option -a failed");
66         return false;
67     }
68     if (targetSystemWide_ && !IsSupportNonDebuggableApp()) {
69         HLOGD("-a option needs root privilege for system wide profiling.");
70         printf("-a option needs root privilege for system wide profiling.\n");
71         return false;
72     }
73     if (!Option::GetOptionValue(args, "-c", selectCpus_)) {
74         HLOGD("get option -c failed");
75         return false;
76     }
77     if (!Option::GetOptionValue(args, "-d", timeStopSec_)) {
78         HLOGD("get option -d failed");
79         return false;
80     }
81     if (!Option::GetOptionValue(args, "-i", timeReportMs_)) {
82         HLOGD("get option -i failed");
83         return false;
84     }
85     if (!Option::GetOptionValue(args, "-e", selectEvents_)) {
86         HLOGD("get option -e failed");
87         return false;
88     }
89     if (!Option::GetOptionValue(args, "-g", selectGroups_)) {
90         HLOGD("get option -g failed");
91         return false;
92     }
93     if (!Option::GetOptionValue(args, "--no-inherit", noCreateNew_)) {
94         HLOGD("get option --no-inherit failed");
95         return false;
96     }
97     if (!Option::GetOptionValue(args, "--app", appPackage_)) {
98         HLOGD("get option --app failed");
99         return false;
100     }
101     if (!IsExistDebugByApp(appPackage_)) {
102         return false;
103     }
104     if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
105         return false;
106     }
107     if (!Option::GetOptionValue(args, "-p", selectPids_)) {
108         HLOGD("get option -p failed");
109         return false;
110     }
111     if (!IsExistDebugByPid(selectPids_)) {
112         return false;
113     }
114     if (!Option::GetOptionValue(args, "-t", selectTids_)) {
115         HLOGD("get option -t failed");
116         return false;
117     }
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     // print value
199     std::string strEventCount = std::to_string(reportSum->eventCountSum);
200     for (size_t i = strEventCount.size() - 1, j = 1; i > 0; --i, ++j) {
201         if (j == THOUSNADS_SEPARATOR) {
202             strEventCount.insert(strEventCount.begin() + i, ',');
203             j = 0;
204         }
205     }
206     std::string commentStr;
207     FormatComments(reportSum, commentStr);
208     if (g_reportCpuFlag && g_reportThreadFlag) {
209         printf(" %24s  %-30s | %-30s %10d %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
210                reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, reportSum->cpu, commentStr.c_str(),
211                reportSum->scaleSum * ratio);
212     } else if (g_reportCpuFlag) {
213         printf(" %24s  %-30s | %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
214                reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio);
215     } else {
216         printf(" %24s  %-30s | %-30s %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
217                reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, commentStr.c_str(),
218                reportSum->scaleSum * ratio);
219     }
220     fflush(stdout);
221 }
222 
InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> & newPerMap,const PerfEvents::Summary & summary,VirtualRuntime & virtualInstance)223 void SubCommandStat::InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> &newPerMap,
224                                 const PerfEvents::Summary &summary, VirtualRuntime& virtualInstance)
225 {
226     newPerMap->cpu = summary.cpu;
227     if (g_reportCpuFlag && !g_reportThreadFlag) {
228         return;
229     }
230     newPerMap->tid = summary.tid;
231     newPerMap->pid = thread_map_.find(summary.tid)->second.pid;
232     bool isTid = true;
233     if (newPerMap->pid == newPerMap->tid) {
234         isTid = false;
235     }
236     newPerMap->threadName = virtualInstance.ReadThreadName(summary.tid, isTid);
237 }
238 
GetPerKey(std::string & perKey,const PerfEvents::Summary & summary)239 void SubCommandStat::GetPerKey(std::string &perKey, const PerfEvents::Summary &summary)
240 {
241     perKey = "";
242     if (g_reportCpuFlag) {
243         perKey += std::to_string(summary.cpu);
244         perKey += "|";
245     }
246     if (g_reportThreadFlag) {
247         perKey += std::to_string(summary.tid);
248     }
249     return;
250 }
251 
ReportDetailInfos(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents)252 void SubCommandStat::ReportDetailInfos(
253     const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
254 {
255     std::string perKey = "";
256     std::map<std::string, std::unique_ptr<PerfEvents::ReportSum>> perMaps;
257     for (auto event = countEvents.begin(); event != countEvents.end(); ++event) {
258         if (event->second->eventCount == 0) {
259             continue;
260         }
261         double scale = 1.0;
262         constexpr float ratio {100.0};
263         std::string configName = event->first;
264         perMaps.clear();
265         for (auto &it : event->second->summaries) {
266             GetPerKey(perKey, it);
267             if (perMaps.count(perKey) == 0) {
268                 auto perMap = std::make_unique<PerfEvents::ReportSum>(PerfEvents::ReportSum {});
269                 InitPerMap(perMap, it, runtimeInstance_);
270                 perMaps[perKey] = std::move(perMap);
271             }
272             perMaps[perKey]->configName = GetDetailComments(event->second, perMaps[perKey]->commentSum,
273                                                             it, configName);
274             perMaps[perKey]->eventCountSum += it.eventCount;
275             if (it.time_running < it.time_enabled && it.time_running != 0) {
276                 perMaps[perKey]->scaleSum += 1 / (static_cast<double>(it.time_enabled) / it.time_running);
277             }
278         }
279         for (auto iper = perMaps.begin(); iper != perMaps.end(); iper++) {
280             PrintPerValue(iper->second, ratio, configName);
281         }
282     }
283 }
284 
ReportNormal(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents)285 void SubCommandStat::ReportNormal(
286     const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
287 {
288     // print head
289     printf(" %24s  %-30s | %-32s | %s\n", "count", "name", "comment", "coverage");
290     std::map<std::string, std::string> comments;
291     GetComments(countEvents, comments);
292     for (auto it = countEvents.begin(); it != countEvents.end(); ++it) {
293         double scale = 1.0;
294         constexpr float ratio {100.0};
295         std::string configName = it->first;
296         std::string comment = comments[configName];
297         std::string strEventCount = std::to_string(it->second->eventCount);
298         for (size_t i = strEventCount.size() - 1, j = 1; i > 0; --i, ++j) {
299             if (j == THOUSNADS_SEPARATOR) {
300                 strEventCount.insert(strEventCount.begin() + i, ',');
301                 j = 0;
302             }
303         }
304         if (it->second->time_running < it->second->time_enabled && it->second->time_running != 0) {
305             scale = 1 / (static_cast<double>(it->second->time_enabled) / it->second->time_running);
306         }
307         printf(" %24s  %-30s | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
308                comment.c_str(), scale * ratio);
309 
310         fflush(stdout);
311     }
312 }
313 
FindEventCount(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,const std::string & configName,const __u64 group_id,__u64 & eventCount,double & scale)314 bool SubCommandStat::FindEventCount(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
315     const std::string &configName, const __u64 group_id, __u64 &eventCount, double &scale)
316 {
317     auto itr = countEvents.find(configName);
318     if (itr != countEvents.end()) {
319         eventCount = itr->second->eventCount;
320         if (itr->second->id == group_id
321             && itr->second->time_running < itr->second->time_enabled
322             && itr->second->time_running != 0) {
323             scale = static_cast<double>(itr->second->time_enabled) / itr->second->time_running;
324             return true;
325         }
326     }
327     return false;
328 }
329 
FindPerCoreEventCount(PerfEvents::Summary & summary,__u64 & eventCount,double & scale)330 bool SubCommandStat::FindPerCoreEventCount(PerfEvents::Summary &summary, __u64 &eventCount, double &scale)
331 {
332     eventCount = summary.eventCount;
333     if (summary.time_running < summary.time_enabled && summary.time_running != 0) {
334         scale = static_cast<double>(summary.time_enabled) / summary.time_running;
335         return true;
336     }
337     return false;
338 }
339 
GetCommentConfigName(const std::unique_ptr<PerfEvents::CountEvent> & countEvent,std::string eventName)340 std::string SubCommandStat::GetCommentConfigName(
341     const std::unique_ptr<PerfEvents::CountEvent> &countEvent, std::string eventName)
342 {
343     std::string commentConfigName = "";
344     if (countEvent == nullptr || eventName.length() == 0) {
345         return commentConfigName;
346     }
347     if (countEvent->userOnly) {
348         commentConfigName = eventName + ":u";
349     } else if (countEvent->kernelOnly) {
350         commentConfigName = eventName + ":k";
351     } else {
352         commentConfigName = eventName;
353     }
354     return commentConfigName;
355 }
356 
FormatComments(const std::unique_ptr<PerfEvents::ReportSum> & reportSum,std::string & commentStr)357 void SubCommandStat::FormatComments(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, std::string &commentStr)
358 {
359     if (reportSum->commentSum == 0) {
360         return;
361     }
362     if (reportSum->configName == "sw-task-clock") {
363         commentStr = StringPrintf("%lf cpus used", reportSum->commentSum);
364         return;
365     }
366     if (reportSum->configName == "hw-cpu-cycles") {
367         commentStr = StringPrintf("%lf GHz", reportSum->commentSum);
368         return;
369     }
370     if (reportSum->configName == "hw-instructions") {
371         commentStr = StringPrintf("%lf cycles per instruction", reportSum->commentSum);
372         return;
373     }
374     if (reportSum->configName == "hw-branch-misses") {
375         commentStr = StringPrintf("%lf miss rate", reportSum->commentSum);
376         return;
377     }
378 
379     if (reportSum->commentSum > 1e9) {
380         commentStr = StringPrintf("%.3lf G/sec", reportSum->commentSum / 1e9);
381         return;
382     }
383     if (reportSum->commentSum > 1e6) {
384         commentStr = StringPrintf("%.3lf M/sec", reportSum->commentSum / 1e6);
385         return;
386     }
387     if (reportSum->commentSum > 1e3) {
388         commentStr = StringPrintf("%.3lf K/sec", reportSum->commentSum / 1e3);
389         return;
390     }
391     commentStr = StringPrintf("%.3lf /sec", reportSum->commentSum);
392 }
393 
GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> & countEvent,double & comment,PerfEvents::Summary & summary,std::string & configName)394 std::string SubCommandStat::GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> &countEvent,
395     double &comment, PerfEvents::Summary &summary, std::string &configName)
396 {
397     double running_time_in_sec = 0;
398     double main_scale = 1.0;
399     bool findRunningTime = FindPercoreRunningTime(summary, running_time_in_sec, main_scale);
400     if (configName == GetCommentConfigName(countEvent, "sw-cpu-clock")) {
401         comment = 0;
402         return "sw-cpu-clock";
403     }
404     double scale = 1.0;
405     if (summary.time_running < summary.time_enabled && summary.time_running != 0) {
406         scale = static_cast<double>(summary.time_enabled) / summary.time_running;
407     }
408     if (configName == GetCommentConfigName(countEvent, "sw-task-clock")) {
409         comment += countEvent->used_cpus * scale;
410         return "sw-task-clock";
411     }
412     if (configName == GetCommentConfigName(countEvent, "hw-cpu-cycles")) {
413         if (findRunningTime) {
414             double hz = 0;
415             if (running_time_in_sec != 0) {
416                 hz = summary.eventCount / (running_time_in_sec / scale);
417             }
418             comment += hz / 1e9;
419         } else {
420             comment += 0;
421         }
422         return "hw-cpu-cycles";
423     }
424     if (configName == GetCommentConfigName(countEvent, "hw-instructions") && summary.eventCount != 0) {
425         double otherScale = 1.0;
426         __u64 cpuCyclesCount = 0;
427         bool other = FindPerCoreEventCount(summary, cpuCyclesCount, otherScale);
428         if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
429             comment += static_cast<double>(cpuCyclesCount) / summary.eventCount;
430             return "hw-instructions";
431         }
432     }
433     if (configName == GetCommentConfigName(countEvent, "hw-branch-misses")) {
434         double otherScale = 1.0;
435         __u64 branchInstructionsCount = 0;
436         bool other = FindPerCoreEventCount(summary, branchInstructionsCount, otherScale);
437         if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
438             branchInstructionsCount != 0) {
439             comment += (static_cast<double>(summary.eventCount) / branchInstructionsCount) * ONE_HUNDRED;
440             return "hw-branch-misses";
441         }
442     }
443     return HandleOtherConfig(comment, summary, running_time_in_sec, scale, findRunningTime);
444 }
445 
HandleOtherConfig(double & comment,PerfEvents::Summary & summary,double running_time_in_sec,double scale,bool findRunningTime)446 std::string SubCommandStat::HandleOtherConfig(double &comment, PerfEvents::Summary &summary, double running_time_in_sec,
447                                               double scale, bool findRunningTime)
448 {
449     comment = 0;
450     if (findRunningTime) {
451         double rate = 0;
452         if (scale != 0) {
453             rate = summary.eventCount / (running_time_in_sec / scale);
454         }
455         comment += rate;
456     }
457     return "";
458 }
459 
IsMonitoredAtAllTime(const double & scale)460 bool SubCommandStat::IsMonitoredAtAllTime(const double &scale)
461 {
462     constexpr double SCALE_ERROR_LIMIT = 1e-5;
463     return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
464 }
465 
GetComments(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,std::map<std::string,std::string> & comments)466 void SubCommandStat::GetComments(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
467     std::map<std::string, std::string> &comments)
468 {
469     double running_time_in_sec = 0;
470     __u64 group_id = 0;
471     double main_scale = 1.0;
472     bool findRunningTime = FindRunningTime(countEvents, running_time_in_sec, group_id, main_scale);
473     for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
474         std::string configName = it->first;
475         std::string commentConfigName = GetCommentConfigName(it->second, "sw-cpu-clock");
476         if (configName == commentConfigName) {
477             comments[configName] = "";
478             continue;
479         }
480         double scale = 1.0;
481         if (it->second->time_running < it->second->time_enabled && it->second->time_running != 0) {
482             scale = static_cast<double>(it->second->time_enabled) / it->second->time_running;
483         }
484         commentConfigName = GetCommentConfigName(it->second, "sw-task-clock");
485         if (configName == commentConfigName) {
486             double used_cpus = it->second->used_cpus * scale;
487             comments[configName] = StringPrintf("%lf cpus used", used_cpus);
488             continue;
489         }
490         commentConfigName = GetCommentConfigName(it->second, "hw-cpu-cycles");
491         if (configName == commentConfigName) {
492             if (findRunningTime &&
493                 ((group_id == it->second->id) ||
494                  (IsMonitoredAtAllTime(main_scale) && IsMonitoredAtAllTime(scale)))) {
495                 double hz = 0;
496                 if (running_time_in_sec != 0) {
497                     hz = it->second->eventCount / (running_time_in_sec / scale);
498                 }
499                 comments[configName] = StringPrintf("%lf GHz", hz / 1e9);
500             } else {
501                 comments[configName] = "";
502             }
503             continue;
504         }
505         commentConfigName = GetCommentConfigName(it->second, "hw-instructions");
506         if (configName == commentConfigName && it->second->eventCount != 0) {
507             std::string cpuSyclesName = GetCommentConfigName(it->second, "hw-cpu-cycles");
508             double otherScale = 1.0;
509             __u64 cpuCyclesCount = 0;
510             bool other = FindEventCount(countEvents, cpuSyclesName, it->second->id, cpuCyclesCount,
511                                         otherScale);
512             if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
513                 double cpi = static_cast<double>(cpuCyclesCount) / it->second->eventCount;
514                 comments[configName] = StringPrintf("%lf cycles per instruction", cpi);
515                 continue;
516             }
517         }
518         commentConfigName = GetCommentConfigName(it->second, "hw-branch-misses");
519         if (configName == commentConfigName) {
520             std::string branchInsName = GetCommentConfigName(it->second, "hw-branch-instructions");
521             double otherScale = 1.0;
522             __u64 branchInstructionsCount = 0;
523             bool other = FindEventCount(countEvents, branchInsName, it->second->id,
524                                         branchInstructionsCount, otherScale);
525             if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
526                 branchInstructionsCount != 0) {
527                 double miss_rate =
528                     static_cast<double>(it->second->eventCount) / branchInstructionsCount;
529                 comments[configName] = StringPrintf("%lf miss rate", miss_rate * ONE_HUNDRED);
530                 continue;
531             }
532         }
533         if (findRunningTime && ((group_id == it->second->id) || (IsMonitoredAtAllTime(main_scale) &&
534                                                                  IsMonitoredAtAllTime(scale)))) {
535             double rate = it->second->eventCount / (running_time_in_sec / scale);
536             if (rate > 1e9) {
537                 comments[configName] = StringPrintf("%.3lf G/sec", rate / 1e9);
538                 continue;
539             }
540             if (rate > 1e6) {
541                 comments[configName] = StringPrintf("%.3lf M/sec", rate / 1e6);
542                 continue;
543             }
544             if (rate > 1e3) {
545                 comments[configName] = StringPrintf("%.3lf K/sec", rate / 1e3);
546                 continue;
547             }
548             comments[configName] = StringPrintf("%.3lf /sec", rate);
549         } else {
550             comments[configName] = "";
551         }
552     }
553 }
554 
FindRunningTime(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,double & running_time_in_sec,__u64 & group_id,double & main_scale)555 bool SubCommandStat::FindRunningTime(
556     const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
557     double &running_time_in_sec, __u64 &group_id, double &main_scale)
558 {
559     for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
560         if ((it->first == "sw-task-clock" || it->first == "sw-task-clock:u" ||
561              it->first == "sw-task-clock:k" || it->first == "sw-cpu-clock" ||
562              it->first == "sw-cpu-clock:u" || it->first == "sw-cpu-clock:k") &&
563             it->second->eventCount != 0u) {
564             group_id = it->second->id;
565             running_time_in_sec = it->second->eventCount / 1e9;
566             if (it->second->time_running < it->second->time_enabled &&
567                 it->second->time_running != 0) {
568                 main_scale =
569                     static_cast<double>(it->second->time_enabled) / it->second->time_running;
570             }
571             return true;
572         }
573     }
574     return false;
575 }
576 
FindPercoreRunningTime(PerfEvents::Summary & summary,double & running_time_int_sec,double & main_scale)577 bool SubCommandStat::FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec,
578                                             double &main_scale)
579 {
580     if (summary.eventCount == 0) {
581         return false;
582     }
583     running_time_int_sec = summary.eventCount / 1e9;
584     if (summary.time_running < summary.time_enabled && summary.time_running != 0) {
585         main_scale = static_cast<double>(summary.time_enabled) /summary.time_running;
586     }
587     return true;
588 }
589 
CheckOptionPidAndApp(std::vector<pid_t> pids)590 bool SubCommandStat::CheckOptionPidAndApp(std::vector<pid_t> pids)
591 {
592     if (!CheckOptionPid(pids)) {
593         printf("Problems finding threads of monitor\n\n");
594         printf("Usage: perf stat [<options>] [<command>]\n\n");
595         printf("-p <pid>        stat events on existing process id\n");
596         printf("-t <tid>        stat events on existing thread id\n");
597         return false;
598     }
599     return true;
600 }
601 
CheckOptionPid(std::vector<pid_t> pids)602 bool SubCommandStat::CheckOptionPid(std::vector<pid_t> pids)
603 {
604     if (pids.empty()) {
605         return true;
606     }
607 
608     for (auto pid : pids) {
609         if (!IsDir("/proc/" + std::to_string(pid))) {
610             printf("not exit pid %d\n", pid);
611             return false;
612         }
613     }
614     return true;
615 }
616 
SetPerfEvent()617 void SubCommandStat::SetPerfEvent()
618 {
619     SetReportFlags(perCpus_, perThreads_);
620     perfEvents_.SetSystemTarget(targetSystemWide_);
621     perfEvents_.SetTimeOut(timeStopSec_);
622     perfEvents_.SetTimeReport(timeReportMs_);
623     perfEvents_.SetPerCpu(perCpus_);
624     perfEvents_.SetPerThread(perThreads_);
625     perfEvents_.SetVerboseReport(verboseReport_);
626     perfEvents_.SetInherit(!noCreateNew_);
627     perfEvents_.SetTrackedCommand(trackedCommand_);
628     // set report handle
629     perfEvents_.SetStatCallBack(Report);
630 }
631 
OnSubCommand(std::vector<std::string> & args)632 bool SubCommandStat::OnSubCommand(std::vector<std::string> &args)
633 {
634     if (HelpOption()) {
635         return true;
636     }
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         pids.push_back(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     }
787     if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) {
788         printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
789                MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
790         return false;
791     }
792     if (timeStopSec_ < 0) {
793         printf("monitoring duration should be positive but %f is given\n", timeStopSec_);
794         return false;
795     }
796     if (timeReportMs_ < 0) {
797         printf("print interval should be non-negative but %d is given\n", timeReportMs_);
798         return false;
799     }
800     return true;
801 }
802 } // namespace HiPerf
803 } // namespace Developtools
804 } // namespace OHOS
805