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