• 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 <cstdlib>
22 #include <ctime>
23 #include <iostream>
24 #include <memory>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 
28 #include "debug_logger.h"
29 #include "hiperf_client.h"
30 #include "hiperf_hilog.h"
31 #include "ipc_utilities.h"
32 #include "utilities.h"
33 
34 using namespace std::chrono;
35 const uint16_t ONE_HUNDRED = 100;
36 const uint16_t THOUSANDS_SEPARATOR = 3;
37 namespace OHOS {
38 namespace Developtools {
39 namespace HiPerf {
40 const std::string DEFAULT_STAT_FILE = "/data/local/tmp/perf_stat.txt";
41 // when there are many events, start record will take more time.
42 const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT = 2000ms;
43 static std::map<pid_t, ThreadInfos> thread_map_;
44 static bool g_reportCpuFlag = false;
45 static bool g_reportThreadFlag = false;
46 static VirtualRuntime g_runtimeInstance;
47 
~SubCommandStat()48 SubCommandStat::~SubCommandStat()
49 {
50     if (filePtr_ != nullptr) {
51         fclose(filePtr_);
52         filePtr_ = nullptr;
53     }
54     CloseClientThread();
55 }
56 
DumpOptions() const57 void SubCommandStat::DumpOptions() const
58 {
59     printf("DumpOptions:\n");
60     printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false");
61     printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str());
62     printf(" timeStopSec:\t%f sec\n", timeStopSec_);
63     printf(" timeReportMs:\t%d ms\n", timeReportMs_);
64     printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str());
65     printf(" selectGroups:\t%s\n", VectorToString(selectGroups_).c_str());
66     printf(" noCreateNew:\t%s\n", noCreateNew_ ? "true" : "false");
67     printf(" appPackage:\t%s\n", appPackage_.c_str());
68     printf(" checkAppMs_:\t%d\n", checkAppMs_);
69     printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str());
70     printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str());
71     printf(" restart:\t%s\n", restart_ ? "true" : "false");
72     printf(" perCore:\t%s\n", perCpus_ ? "true" : "false");
73     printf(" perTread:\t%s\n", perThreads_ ? "true" : "false");
74     printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false");
75 }
76 
ParseOption(std::vector<std::string> & args)77 bool SubCommandStat::ParseOption(std::vector<std::string> &args)
78 {
79     if (args.size() == 1 && args[0] == "-h") {
80         args.clear();
81         helpOption_ = true;
82         PrintUsage();
83         return true;
84     }
85     if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) {
86         HLOGD("get option -a failed");
87         return false;
88     }
89     if (targetSystemWide_ && !IsSupportNonDebuggableApp()) {
90         HLOGD("-a option needs root privilege for system wide profiling.");
91         printf("-a option needs root privilege for system wide profiling.\n");
92         return false;
93     }
94     if (!Option::GetOptionValue(args, "-c", selectCpus_)) {
95         HLOGD("get option -c failed");
96         return false;
97     }
98     if (!Option::GetOptionValue(args, "-d", timeStopSec_)) {
99         HLOGD("get option -d failed");
100         return false;
101     }
102     if (!Option::GetOptionValue(args, "-i", timeReportMs_)) {
103         HLOGD("get option -i failed");
104         return false;
105     }
106     if (!Option::GetOptionValue(args, "-e", selectEvents_)) {
107         HLOGD("get option -e failed");
108         return false;
109     }
110     if (!Option::GetOptionValue(args, "-g", selectGroups_)) {
111         HLOGD("get option -g failed");
112         return false;
113     }
114     if (!Option::GetOptionValue(args, "--no-inherit", noCreateNew_)) {
115         HLOGD("get option --no-inherit failed");
116         return false;
117     }
118     if (!Option::GetOptionValue(args, "-o", outputFilename_)) {
119         return false;
120     }
121     if (!Option::GetOptionValue(args, "--app", appPackage_)) {
122         HLOGD("get option --app failed");
123         return false;
124     }
125     if (!Option::GetOptionValue(args, "--control", controlCmd_)) {
126         return false;
127     }
128     allowIpc_ = controlCmd_ != CONTROL_CMD_PREPARE;
129     std::string err = "";
130     if (allowIpc_ && !IsExistDebugByApp(appPackage_, err)) {
131         return false;
132     }
133     if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
134         return false;
135     }
136     if (!Option::GetOptionValue(args, "-p", selectPids_)) {
137         HLOGD("get option -p failed");
138         return false;
139     }
140     inputPidTidArgs_ = selectPids_;
141     if (!Option::GetOptionValue(args, "-t", selectTids_)) {
142         HLOGD("get option -t failed");
143         return false;
144     }
145     inputPidTidArgs_.insert(inputPidTidArgs_.end(), selectTids_.begin(), selectTids_.end());
146     if (!Option::GetOptionValue(args, "--restart", restart_)) {
147         HLOGD("get option --restart failed");
148         return false;
149     }
150     if (!Option::GetOptionValue(args, "--per-core", perCpus_)) {
151         HLOGD("get option --per-core failed");
152         return false;
153     }
154     if (!Option::GetOptionValue(args, "--per-thread", perThreads_)) {
155         HLOGD("get option --per-thread failed");
156         return false;
157     }
158     if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) {
159         HLOGD("get option --verbose failed");
160         return false;
161     }
162     return ParseSpecialOption(args);
163 }
164 
ParseSpecialOption(std::vector<std::string> & args)165 bool SubCommandStat::ParseSpecialOption(std::vector<std::string> &args)
166 {
167     if (!Option::GetOptionTrackedCommand(args, trackedCommand_)) {
168         HLOGD("get cmd failed");
169         return false;
170     }
171     if (!args.empty()) {
172         HLOGD("redundant option(s)");
173         return false;
174     }
175     return true;
176 }
177 
PrintUsage()178 void SubCommandStat::PrintUsage()
179 {
180     printf("%s\n", Help().c_str());
181 }
182 
SetReportFlags(const bool cpuFlag,const bool threadFlag)183 void SubCommandStat::SetReportFlags(const bool cpuFlag, const bool threadFlag)
184 {
185     g_reportCpuFlag = cpuFlag;
186     g_reportThreadFlag = threadFlag;
187 }
188 
Report(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,FILE * filePtr)189 void SubCommandStat::Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
190                             FILE* filePtr)
191 {
192     bool isNeedPerCpuTid = false;
193     for (const auto &it : countEvents) {
194         if (!(it.second->summaries.empty())) {
195             isNeedPerCpuTid = true;
196             break;
197         }
198     }
199     if (isNeedPerCpuTid) {
200         PrintPerHead(filePtr);
201         ReportDetailInfos(countEvents, filePtr);
202     } else {
203         ReportNormal(countEvents, filePtr);
204     }
205 }
206 
PrintPerHead(FILE * filePtr)207 void SubCommandStat::PrintPerHead(FILE* filePtr)
208 {
209     // print head
210     if (g_reportCpuFlag && g_reportThreadFlag) {
211         if (filePtr == nullptr) {
212             printf(" %24s  %-30s | %-30s %10s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name",
213                    "pid", "tid", "coreid", "comment", "coverage");
214         } else {
215             fprintf(filePtr, " %24s  %-30s | %-30s %10s %10s %10s | %-32s | %s\n",
216                 "count", "event_name", "thread_name", "pid", "tid", "coreid", "comment", "coverage");
217         }
218         return;
219     }
220     if (g_reportCpuFlag) {
221         if (filePtr == nullptr) {
222             printf(" %24s  %-30s | %10s | %-32s | %s\n", "count", "event_name", "coreid", "comment", "coverage");
223         } else {
224             fprintf(filePtr, " %24s  %-30s | %10s | %-32s | %s\n",
225                 "count", "event_name", "coreid", "comment", "coverage");
226         }
227         return;
228     }
229     if (filePtr == nullptr) {
230         printf(" %24s  %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", "pid", "tid",
231                "comment", "coverage");
232     } else {
233         fprintf(filePtr, " %24s  %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name",
234             "pid", "tid", "comment", "coverage");
235     }
236     return;
237 }
238 
PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> & reportSum,const float & ratio,const std::string & configName,FILE * filePtr)239 void SubCommandStat::PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, const float &ratio,
240                                    const std::string &configName, FILE* filePtr)
241 {
242     if (reportSum == nullptr) {
243         return;
244     }
245     // print value
246     std::string strEventCount = std::to_string(reportSum->eventCountSum);
247     for (size_t i = strEventCount.size() >= 1 ? strEventCount.size() - 1 : 0, j = 1; i > 0; --i, ++j) {
248         if (j == THOUSANDS_SEPARATOR) {
249             j = 0;
250             strEventCount.insert(strEventCount.begin() + i, ',');
251         }
252     }
253 
254     std::string commentStr;
255     MakeComments(reportSum, commentStr);
256 
257     if (g_reportCpuFlag && g_reportThreadFlag) {
258         if (filePtr == nullptr) {
259             printf(" %24s  %-30s | %-30s %10d %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(),
260                    configName.c_str(), reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, reportSum->cpu,
261                    commentStr.c_str(), reportSum->scaleSum * ratio);
262         } else {
263             fprintf(filePtr, " %24s  %-30s | %-30s %10d %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(),
264                 configName.c_str(), reportSum->threadName.c_str(), reportSum->pid, reportSum->tid,
265                 reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio);
266         }
267     } else if (g_reportCpuFlag) {
268         if (filePtr == nullptr) {
269             printf(" %24s  %-30s | %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
270                    reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio);
271         } else {
272             fprintf(filePtr, " %24s  %-30s | %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
273                 reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio);
274         }
275     } else {
276         if (filePtr == nullptr) {
277             printf(" %24s  %-30s | %-30s %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
278                    reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, commentStr.c_str(),
279                    reportSum->scaleSum * ratio);
280         } else {
281             fprintf(filePtr, " %24s  %-30s | %-30s %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(),
282                 configName.c_str(), reportSum->threadName.c_str(), reportSum->pid, reportSum->tid,
283                 commentStr.c_str(), reportSum->scaleSum * ratio);
284         }
285     }
286     fflush(stdout);
287 }
288 
InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> & newPerMap,const PerfEvents::Summary & summary,VirtualRuntime & virtualInstance)289 void SubCommandStat::InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> &newPerMap,
290                                 const PerfEvents::Summary &summary, VirtualRuntime& virtualInstance)
291 {
292     CHECK_TRUE(newPerMap != nullptr, NO_RETVAL, 0, "");
293     newPerMap->cpu = summary.cpu;
294     if (g_reportCpuFlag && !g_reportThreadFlag) {
295         return;
296     }
297     newPerMap->tid = summary.tid;
298     newPerMap->pid = thread_map_.find(summary.tid)->second.pid;
299     bool isTid = true;
300     if (newPerMap->pid == newPerMap->tid) {
301         isTid = false;
302     }
303     newPerMap->threadName = virtualInstance.ReadThreadName(summary.tid, isTid);
304 }
305 
GetPerKey(std::string & perKey,const PerfEvents::Summary & summary)306 void SubCommandStat::GetPerKey(std::string &perKey, const PerfEvents::Summary &summary)
307 {
308     perKey = "";
309     if (g_reportCpuFlag) {
310         perKey += std::to_string(summary.cpu);
311         perKey += "|";
312     }
313     if (g_reportThreadFlag) {
314         perKey += std::to_string(summary.tid);
315     }
316     return;
317 }
318 
ReportDetailInfos(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,FILE * filePtr)319 void SubCommandStat::ReportDetailInfos(
320     const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents, FILE* filePtr)
321 {
322     std::string perKey = "";
323     std::map<std::string, std::unique_ptr<PerfEvents::ReportSum>> perMaps;
324     for (auto event = countEvents.begin(); event != countEvents.end(); ++event) {
325         if (event->second == nullptr || event->second->eventCount == 0) {
326             continue;
327         }
328         constexpr float ratio {100.0};
329         std::string configName = event->first;
330         perMaps.clear();
331         for (auto &it : event->second->summaries) {
332             GetPerKey(perKey, it);
333             if (perMaps.count(perKey) == 0) {
334                 auto perMap = std::make_unique<PerfEvents::ReportSum>(PerfEvents::ReportSum {});
335                 InitPerMap(perMap, it, g_runtimeInstance);
336                 perMaps[perKey] = std::move(perMap);
337             }
338             if (perMaps[perKey] == nullptr) {
339                 continue;
340             }
341             perMaps[perKey]->configName = GetDetailComments(event->second, perMaps[perKey]->commentSum,
342                                                             it, configName);
343             perMaps[perKey]->eventCountSum += it.eventCount;
344             if (it.timeRunning < it.timeEnabled && it.timeRunning != 0) {
345                 perMaps[perKey]->scaleSum = 1 / (static_cast<double>(it.timeEnabled) / it.timeRunning);
346             }
347         }
348         for (auto iper = perMaps.begin(); iper != perMaps.end(); iper++) {
349             PrintPerValue(iper->second, ratio, configName, filePtr);
350         }
351     }
352 }
353 
ReportNormal(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,FILE * filePtr)354 void SubCommandStat::ReportNormal(
355     const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents, FILE* filePtr)
356 {
357     // print head
358     if (filePtr == nullptr) {
359         printf(" %24s  %-30s | %-32s | %s\n", "count", "name", "comment", "coverage");
360     } else {
361         fprintf(filePtr, " %24s  %-30s | %-32s | %s\n", "count", "name", "comment", "coverage");
362     }
363     std::map<std::string, std::string> comments;
364     GetComments(countEvents, comments);
365     for (auto it = countEvents.begin(); it != countEvents.end(); ++it) {
366         double scale = 1.0;
367         constexpr float ratio {100.0};
368         std::string configName = it->first;
369         std::string comment = comments[configName];
370         std::string strEventCount = std::to_string(it->second->eventCount);
371         for (size_t i = strEventCount.size() >= 1 ? strEventCount.size() - 1 : 0, j = 1; i > 0; --i, ++j) {
372             if (j == THOUSANDS_SEPARATOR) {
373                 strEventCount.insert(strEventCount.begin() + i, ',');
374                 j = 0;
375             }
376         }
377         if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) {
378             scale = 1 / (static_cast<double>(it->second->timeEnabled) / it->second->timeRunning);
379         }
380         if (filePtr == nullptr) {
381             printf(" %24s  %-30s | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
382                    comment.c_str(), scale * ratio);
383         } else {
384             fprintf(filePtr, " %24s  %-30s | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
385                 comment.c_str(), scale * ratio);
386         }
387         fflush(stdout);
388     }
389 }
390 
FindEventCount(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,const std::string & configName,const __u64 group_id,__u64 & eventCount,double & scale)391 bool SubCommandStat::FindEventCount(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
392     const std::string &configName, const __u64 group_id, __u64 &eventCount, double &scale)
393 {
394     auto itr = countEvents.find(configName);
395     if (itr != countEvents.end()) {
396         eventCount = itr->second->eventCount;
397         if (itr->second->id == group_id
398             && itr->second->timeRunning < itr->second->timeEnabled
399             && itr->second->timeRunning != 0) {
400             scale = static_cast<double>(itr->second->timeEnabled) / itr->second->timeRunning;
401             return true;
402         }
403     }
404     return false;
405 }
406 
FindPerCoreEventCount(PerfEvents::Summary & summary,__u64 & eventCount,double & scale)407 bool SubCommandStat::FindPerCoreEventCount(PerfEvents::Summary &summary, __u64 &eventCount, double &scale)
408 {
409     eventCount = summary.eventCount;
410     if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
411         scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
412         return true;
413     }
414     return false;
415 }
416 
GetCommentConfigName(const std::unique_ptr<PerfEvents::CountEvent> & countEvent,const std::string & eventName)417 std::string SubCommandStat::GetCommentConfigName(
418     const std::unique_ptr<PerfEvents::CountEvent> &countEvent, const std::string &eventName)
419 {
420     std::string commentConfigName = "";
421     CHECK_TRUE(countEvent != nullptr && eventName.length() != 0, commentConfigName, 1, "countEvent is nullptr");
422     if (countEvent->userOnly) {
423         commentConfigName = eventName + ":u";
424     } else if (countEvent->kernelOnly) {
425         commentConfigName = eventName + ":k";
426     } else {
427         commentConfigName = eventName;
428     }
429     return commentConfigName;
430 }
431 
MakeComments(const std::unique_ptr<PerfEvents::ReportSum> & reportSum,std::string & commentStr)432 void SubCommandStat::MakeComments(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, std::string &commentStr)
433 {
434     CHECK_TRUE(reportSum != nullptr && reportSum->commentSum != 0, NO_RETVAL, 0, "");
435     if (reportSum->configName == "sw-task-clock") {
436         commentStr = StringPrintf("%lf cpus used", reportSum->commentSum);
437         return;
438     }
439     if (reportSum->configName == "hw-cpu-cycles") {
440         commentStr = StringPrintf("%lf GHz", reportSum->commentSum);
441         return;
442     }
443     if (reportSum->configName == "hw-instructions") {
444         commentStr = StringPrintf("%lf cycles per instruction", reportSum->commentSum);
445         return;
446     }
447     if (reportSum->configName == "hw-branch-misses") {
448         commentStr = StringPrintf("%lf miss rate", reportSum->commentSum);
449         return;
450     }
451 
452     if (reportSum->commentSum > 1e9) {
453         commentStr = StringPrintf("%.3lf G/sec", reportSum->commentSum / 1e9);
454         return;
455     }
456     if (reportSum->commentSum > 1e6) {
457         commentStr = StringPrintf("%.3lf M/sec", reportSum->commentSum / 1e6);
458         return;
459     }
460     if (reportSum->commentSum > 1e3) {
461         commentStr = StringPrintf("%.3lf K/sec", reportSum->commentSum / 1e3);
462         return;
463     }
464     commentStr = StringPrintf("%.3lf /sec", reportSum->commentSum);
465 }
466 
GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> & countEvent,double & comment,PerfEvents::Summary & summary,std::string & configName)467 std::string SubCommandStat::GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> &countEvent,
468     double &comment, PerfEvents::Summary &summary, std::string &configName)
469 {
470     double running_time_in_sec = 0;
471     double main_scale = 1.0;
472     bool findRunningTime = FindPercoreRunningTime(summary, running_time_in_sec, main_scale);
473     if (configName == GetCommentConfigName(countEvent, "sw-cpu-clock")) {
474         comment = 0;
475         return "sw-cpu-clock";
476     }
477     double scale = 1.0;
478     if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
479         scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
480     }
481     if (configName == GetCommentConfigName(countEvent, "sw-task-clock")) {
482         comment += countEvent->usedCpus * scale;
483         return "sw-task-clock";
484     }
485     if (configName == GetCommentConfigName(countEvent, "hw-cpu-cycles")) {
486         if (findRunningTime) {
487             double hz = 0;
488             if (abs(running_time_in_sec) > ALMOST_ZERO) {
489                 hz = summary.eventCount / (running_time_in_sec / scale);
490             }
491             comment += hz / 1e9;
492         }
493         return "hw-cpu-cycles";
494     }
495     if (configName == GetCommentConfigName(countEvent, "hw-instructions") && summary.eventCount != 0) {
496         double otherScale = 1.0;
497         __u64 cpuCyclesCount = 0;
498         bool other = FindPerCoreEventCount(summary, cpuCyclesCount, otherScale);
499         if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
500             comment += static_cast<double>(cpuCyclesCount) / summary.eventCount;
501             return "hw-instructions";
502         }
503     }
504     if (configName == GetCommentConfigName(countEvent, "hw-branch-misses")) {
505         double otherScale = 1.0;
506         __u64 branchInstructionsCount = 0;
507         bool other = FindPerCoreEventCount(summary, branchInstructionsCount, otherScale);
508         if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
509             branchInstructionsCount != 0) {
510             comment += (static_cast<double>(summary.eventCount) / branchInstructionsCount) * ONE_HUNDRED;
511             return "hw-branch-misses";
512         }
513     }
514     return HandleOtherConfig(comment, summary, running_time_in_sec, scale, findRunningTime);
515 }
516 
HandleOtherConfig(double & comment,PerfEvents::Summary & summary,const double running_time_in_sec,const double scale,const bool findRunningTime)517 std::string SubCommandStat::HandleOtherConfig(double &comment, PerfEvents::Summary &summary,
518                                               const double running_time_in_sec, const double scale,
519                                               const bool findRunningTime)
520 {
521     comment = 0;
522     if (findRunningTime) {
523         double rate = 0;
524         if (scale != 0) {
525             rate = summary.eventCount / (running_time_in_sec / scale);
526         }
527         comment += rate;
528     }
529     return "";
530 }
531 
IsMonitoredAtAllTime(const double & scale)532 bool SubCommandStat::IsMonitoredAtAllTime(const double &scale)
533 {
534     constexpr double SCALE_ERROR_LIMIT = 1e-5;
535     return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
536 }
537 
GetComments(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,std::map<std::string,std::string> & comments)538 void SubCommandStat::GetComments(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
539     std::map<std::string, std::string> &comments)
540 {
541     double running_time_in_sec = 0;
542     __u64 group_id = 0;
543     double main_scale = 1.0;
544     bool findRunningTime = FindRunningTime(countEvents, running_time_in_sec, group_id, main_scale);
545     for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
546         std::string configName = it->first;
547         std::string commentConfigName = GetCommentConfigName(it->second, "sw-cpu-clock");
548         if (configName == commentConfigName) {
549             comments[configName] = "";
550             continue;
551         }
552         double scale = 1.0;
553         if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) {
554             scale = static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
555         }
556         commentConfigName = GetCommentConfigName(it->second, "sw-task-clock");
557         if (configName == commentConfigName) {
558             double usedCpus = it->second->usedCpus * scale;
559             comments[configName] = StringPrintf("%lf cpus used", usedCpus);
560             continue;
561         }
562         commentConfigName = GetCommentConfigName(it->second, "hw-cpu-cycles");
563         if (configName == commentConfigName) {
564             if (findRunningTime &&
565                 ((group_id == it->second->id) ||
566                  (IsMonitoredAtAllTime(main_scale) && IsMonitoredAtAllTime(scale)))) {
567                 double hz = 0;
568                 if (abs(running_time_in_sec) > ALMOST_ZERO) {
569                     hz = it->second->eventCount / (running_time_in_sec / scale);
570                 }
571                 comments[configName] = StringPrintf("%lf GHz", hz / 1e9);
572             } else {
573                 comments[configName] = "";
574             }
575             continue;
576         }
577         commentConfigName = GetCommentConfigName(it->second, "hw-instructions");
578         if (configName == commentConfigName && it->second->eventCount != 0) {
579             std::string cpuSyclesName = GetCommentConfigName(it->second, "hw-cpu-cycles");
580             double otherScale = 1.0;
581             __u64 cpuCyclesCount = 0;
582             bool other = FindEventCount(countEvents, cpuSyclesName, it->second->id, cpuCyclesCount,
583                                         otherScale);
584             if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
585                 double cpi = static_cast<double>(cpuCyclesCount) / it->second->eventCount;
586                 comments[configName] = StringPrintf("%lf cycles per instruction", cpi);
587                 continue;
588             }
589         }
590         commentConfigName = GetCommentConfigName(it->second, "hw-branch-misses");
591         if (configName == commentConfigName) {
592             std::string branchInsName = GetCommentConfigName(it->second, "hw-branch-instructions");
593             double otherScale = 1.0;
594             __u64 branchInstructionsCount = 0;
595             bool other = FindEventCount(countEvents, branchInsName, it->second->id,
596                                         branchInstructionsCount, otherScale);
597             if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
598                 branchInstructionsCount != 0) {
599                 double miss_rate =
600                     static_cast<double>(it->second->eventCount) / branchInstructionsCount;
601                 comments[configName] = StringPrintf("%lf miss rate", miss_rate * ONE_HUNDRED);
602                 continue;
603             }
604         }
605         if (findRunningTime && ((group_id == it->second->id) || (IsMonitoredAtAllTime(main_scale) &&
606                                                                  IsMonitoredAtAllTime(scale)))) {
607             double rate = it->second->eventCount / (running_time_in_sec / scale);
608             if (rate > 1e9) {
609                 comments[configName] = StringPrintf("%.3lf G/sec", rate / 1e9);
610                 continue;
611             }
612             if (rate > 1e6) {
613                 comments[configName] = StringPrintf("%.3lf M/sec", rate / 1e6);
614                 continue;
615             }
616             if (rate > 1e3) {
617                 comments[configName] = StringPrintf("%.3lf K/sec", rate / 1e3);
618                 continue;
619             }
620             comments[configName] = StringPrintf("%.3lf /sec", rate);
621         } else {
622             comments[configName] = "";
623         }
624     }
625 }
626 
FindRunningTime(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,double & running_time_in_sec,__u64 & group_id,double & main_scale)627 bool SubCommandStat::FindRunningTime(
628     const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
629     double &running_time_in_sec, __u64 &group_id, double &main_scale)
630 {
631     for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
632         if ((it->first == "sw-task-clock" || it->first == "sw-task-clock:u" ||
633              it->first == "sw-task-clock:k" || it->first == "sw-cpu-clock" ||
634              it->first == "sw-cpu-clock:u" || it->first == "sw-cpu-clock:k") &&
635             it->second->eventCount != 0u) {
636             group_id = it->second->id;
637             running_time_in_sec = it->second->eventCount / 1e9;
638             if (it->second->timeRunning < it->second->timeEnabled &&
639                 it->second->timeRunning != 0) {
640                 main_scale =
641                     static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
642             }
643             return true;
644         }
645     }
646     return false;
647 }
648 
FindPercoreRunningTime(PerfEvents::Summary & summary,double & running_time_int_sec,double & main_scale)649 bool SubCommandStat::FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec,
650                                             double &main_scale)
651 {
652     CHECK_TRUE(summary.eventCount != 0, false, 0, "");
653     running_time_int_sec = summary.eventCount / 1e9;
654     if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
655         main_scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
656     }
657     return true;
658 }
659 
CheckOptionPidAndApp(const std::vector<pid_t> & pids)660 bool SubCommandStat::CheckOptionPidAndApp(const std::vector<pid_t>& pids)
661 {
662     if (!CheckOptionPid(pids)) {
663         printf("Problems finding threads of monitor\n\n");
664         printf("Usage: perf stat [<options>] [<command>]\n\n");
665         printf("-p <pid>        stat events on existing process id\n");
666         printf("-t <tid>        stat events on existing thread id\n");
667         return false;
668     }
669     return true;
670 }
671 
CheckOptionPid(const std::vector<pid_t> & pids)672 bool SubCommandStat::CheckOptionPid(const std::vector<pid_t>& pids)
673 {
674     if (pids.empty()) {
675         return true;
676     }
677 
678     for (auto pid : pids) {
679         std::ostringstream oss;
680         oss << "/proc/" << pid;
681         if (!IsDir(oss.str())) {
682             printf("not exit pid %d\n", pid);
683             return false;
684         }
685     }
686     return true;
687 }
688 
SetPerfEvent()689 void SubCommandStat::SetPerfEvent()
690 {
691     SetReportFlags(perCpus_, perThreads_);
692     perfEvents_.SetSystemTarget(targetSystemWide_);
693     perfEvents_.SetTimeOut(timeStopSec_);
694     perfEvents_.SetTimeReport(timeReportMs_);
695     perfEvents_.SetPerCpu(perCpus_);
696     perfEvents_.SetPerThread(perThreads_);
697     perfEvents_.SetVerboseReport(verboseReport_);
698     perfEvents_.SetInherit(!noCreateNew_);
699     perfEvents_.SetTrackedCommand(trackedCommand_);
700     // set report handle
701     perfEvents_.SetStatCallBack(Report);
702     perfEvents_.SetStatReportFd(filePtr_);
703 }
704 
CreateFifoServer()705 bool SubCommandStat::CreateFifoServer()
706 {
707     char errInfo[ERRINFOLEN] = { 0 };
708     if (!perfPipe_.CreateFifoFile()) {
709         return false;
710     }
711     CheckIpcBeforeFork();
712     pid_t pid = fork();
713     allowIpc_ = true;
714 
715     if (pid == -1) {
716         strerror_r(errno, errInfo, ERRINFOLEN);
717         HLOGE("fork failed. %d:%s", errno, errInfo);
718         return false;
719     } else if (pid == 0) { // child process
720         close(STDIN_FILENO);
721         close(STDERR_FILENO);
722         isFifoServer_ = true;
723         clientPipeOutput_ = open(fifoFileS2C_.c_str(), O_WRONLY);
724         if (clientPipeOutput_ == -1) {
725             strerror_r(errno, errInfo, ERRINFOLEN);
726             HLOGE("open fifo file(%s) failed. %d:%s", fifoFileS2C_.c_str(), errno, errInfo);
727             HIPERF_HILOGE(MODULE_DEFAULT, "open fifo file(%{public}s) failed. %d:%s",
728                 fifoFileS2C_.c_str(), errno, errInfo);
729             return false;
730         }
731         nullFd_ = open("/dev/null", O_WRONLY);
732         (void)dup2(nullFd_, STDOUT_FILENO); // redirect stdout to /dev/null
733          std::string err = OHOS::Developtools::HiPerf::HandleAppInfo(appPackage_, inputPidTidArgs_);
734         if (!err.empty()) {
735             ClientCommandResponse(err);
736             return false;
737         }
738     } else {            // parent process
739         isFifoClient_ = true;
740         int fd = open(fifoFileS2C_.c_str(), O_RDONLY | O_NONBLOCK);
741         std::string reply = "";
742         if (fd != -1) {
743             perfPipe_.WaitFifoReply(fd, CONTROL_WAITREPY_TIMEOUT, reply);
744         }
745         if (fd == -1 || reply != HiperfClient::ReplyOK) {
746             if (reply != HiperfClient::ReplyOK) {
747                 printf("%s", reply.c_str());
748                 HLOGE("reply is %s", reply.c_str());
749                 HIPERF_HILOGE(MODULE_DEFAULT, "reply is %{public}s", reply.c_str());
750             }
751             HLOGI("fd is %d", fd);
752             HIPERF_HILOGI(MODULE_DEFAULT, "fd is %{public}d", fd);
753             close(fd);
754             if (kill(pid, SIGTERM) != 0) {
755                 HLOGE("Failed to send SIGTERM: %d", pid);
756                 HIPERF_HILOGE(MODULE_DEFAULT, "Failed to send SIGTERM to pid: %{public}d", pid);
757             }
758             // wait for process exit
759             if (waitpid(pid, nullptr, 0) == -1) {
760                 HLOGE("Failed to wait for pid: %d", pid);
761                 HIPERF_HILOGE(MODULE_DEFAULT, "Failed to wait for pid: %{public}d", pid);
762             }
763             remove(fifoFileC2S_.c_str());
764             remove(fifoFileS2C_.c_str());
765             strerror_r(errno, errInfo, ERRINFOLEN);
766             printf("create control hiperf counting failed.\n");
767             HLOGI("errno is %d:%s", errno, errInfo);
768             HIPERF_HILOGI(MODULE_DEFAULT, "errno is %{public}d:%{public}s", errno, errInfo);
769             return false;
770         }
771         close(fd);
772         printf("%s control hiperf counting success.\n", restart_ ? "start" : "create");
773         printf("stat result will saved in %s.\n", outputFilename_ .c_str());
774     }
775     return true;
776 }
777 
ClientCommandResponse(const bool response)778 bool SubCommandStat::ClientCommandResponse(const bool response)
779 {
780     return ClientCommandResponse(response ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL);
781 }
782 
ClientCommandResponse(const std::string & str)783 bool SubCommandStat::ClientCommandResponse(const std::string& str)
784 {
785     ssize_t size = write(clientPipeOutput_, str.c_str(), str.size());
786     if (size != static_cast<ssize_t>(str.size())) {
787         char errInfo[ERRINFOLEN] = { 0 };
788         strerror_r(errno, errInfo, ERRINFOLEN);
789         HLOGD("Server:%s -> %d : %zd %d:%s", str.c_str(), clientPipeOutput_, size, errno, errInfo);
790         return false;
791     }
792     return true;
793 }
794 
IsSamplingRunning()795 bool SubCommandStat::IsSamplingRunning()
796 {
797     constexpr int maxWaitTrackingCount = 3000 / 100; // wait 3 second
798     int waitTrackingCount = maxWaitTrackingCount;
799     while (!perfEvents_.IsTrackRunning()) {
800         waitTrackingCount--;
801         if (waitTrackingCount <= 0) {
802             return false;
803         }
804         constexpr uint64_t waitTrackingSleepMs = 100;
805         std::this_thread::sleep_for(milliseconds(waitTrackingSleepMs));
806     }
807     return true;
808 }
809 
InitControlCommandHandlerMap()810 void SubCommandStat::InitControlCommandHandlerMap()
811 {
812     controlCommandHandlerMap_.clear();
813     controlCommandHandlerMap_.emplace(HiperfClient::ReplyStart, ControlCommandHandler{
814         std::bind(&PerfEvents::EnableTracking, &perfEvents_)
815     });
816 
817     controlCommandHandlerMap_.emplace(HiperfClient::ReplyCheck, ControlCommandHandler{
818         std::bind(&SubCommandStat::clientRunning_, this)
819     });
820 
821     controlCommandHandlerMap_.emplace(HiperfClient::ReplyStop, ControlCommandHandler{
822         std::bind(&PerfEvents::StopTracking, &perfEvents_)
823     });
824 }
825 
CreateClientThread()826 inline void SubCommandStat::CreateClientThread()
827 {
828     // make a thread wait the other command
829     if (clientPipeOutput_ != -1) {
830         clientCommandHandle_ = std::thread(&SubCommandStat::ClientCommandHandle, this);
831     }
832 }
833 
ClientCommandHandle()834 void SubCommandStat::ClientCommandHandle()
835 {
836     using namespace HiperfClient;
837     CHECK_TRUE(IsSamplingRunning(), NO_RETVAL, 0, "");
838     // tell the caller if Exist
839     ClientCommandResponse(true);
840     InitControlCommandHandlerMap();
841 
842     bool hasRead = true;
843     while (clientRunning_.load()) {
844         if (isFifoServer_ && hasRead) {
845             if (clientPipeInput_ != -1) {
846                 // after read(), block is disabled, the poll will be waked neven if no data
847                 close(clientPipeInput_);
848             }
849             clientPipeInput_ = open(fifoFileC2S_.c_str(), O_RDONLY | O_NONBLOCK);
850         }
851         struct pollfd pollFd {
852             clientPipeInput_, POLLIN, 0
853         };
854         int polled = poll(&pollFd, 1, CONTROL_WAITREPY_TIMEOUT.count());
855         if (polled <= 0) {
856             hasRead = false;
857             continue;
858         }
859         hasRead = true;
860         std::string command;
861         bool exitLoop = false;
862         while (!exitLoop) {
863             char c;
864             ssize_t result = TEMP_FAILURE_RETRY(read(clientPipeInput_, &c, 1));
865             if (result <= 0) {
866                 HLOGD("server :read from pipe file failed");
867                 HIPERF_HILOGI(MODULE_DEFAULT, "server :read from pipe file failed");
868                 exitLoop = true;
869             }
870             command.push_back(c);
871             if (c == '\n') {
872                 exitLoop = true;
873             }
874         }
875         HLOGD("server:new command %s", command.c_str());
876         HIPERF_HILOGI(MODULE_DEFAULT, "server:new command : %{public}s", command.c_str());
877         DispatchControlCommand(command);
878     }
879 }
880 
DispatchControlCommand(const std::string & command)881 void SubCommandStat::DispatchControlCommand(const std::string& command)
882 {
883     auto it = controlCommandHandlerMap_.find(command);
884     if (it == controlCommandHandlerMap_.end()) {
885         return;
886     }
887 
888     ControlCommandHandler& handler = it->second;
889     bool ret = handler.preProcess();
890     ClientCommandResponse(ret);
891     handler.postProcess(ret);
892 }
893 
ProcessControl()894 bool SubCommandStat::ProcessControl()
895 {
896     if (controlCmd_.empty()) {
897         return true;
898     }
899     HIPERF_HILOGI(MODULE_DEFAULT, "control cmd : %{public}s", controlCmd_.c_str());
900     perfPipe_.SetFifoFileName(CommandType::STAT, controlCmd_, fifoFileC2S_, fifoFileS2C_);
901     if (controlCmd_ == CONTROL_CMD_PREPARE) {
902         CHECK_TRUE(CreateFifoServer(), false, 0, "");
903         return true;
904     }
905 
906     isFifoClient_ = true;
907     return perfPipe_.ProcessControlCmd();
908 }
909 
CheckStatOption()910 HiperfError SubCommandStat::CheckStatOption()
911 {
912     if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
913         return HiperfError::CHECK_RESTART_OPTION_FAIL;
914     }
915 
916     // check option
917     if (!CheckSelectCpuPidOption()) {
918         return HiperfError::CHECK_SELECT_CPU_PID_FAIL;
919     }
920     if (!CheckOptions(selectPids_)) {
921         HLOGV("CheckOptions() failed");
922         return HiperfError::CHECK_STAT_OPTION_FAIL;
923     }
924     if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) {
925         HLOGV("CheckAppIsRunning() failed");
926         return HiperfError::CHECK_APP_RUNNING_FAIL;
927     }
928     if (!CheckOptionPid(selectPids_)) {
929         HLOGV("CheckOptionPid() failed");
930         return HiperfError::CHECK_OPTION_PID_FAIL;
931     }
932 
933     perfEvents_.SetCpu(selectCpus_);
934     std::vector<pid_t> pids;
935     for (auto selectPid : selectPids_) {
936         HLOGD("[OnSubCommand] selectPid %d\n", selectPid);
937         std::vector<pid_t> subTids = GetSubthreadIDs(selectPid, thread_map_);
938         if (!subTids.empty()) {
939             pids.insert(pids.end(), subTids.begin(), subTids.end());
940         } else {
941             HLOGD("[OnSubCommand] subTids empty for %d\n", selectPid);
942         }
943     }
944     pids.insert(pids.end(), selectTids_.begin(), selectTids_.end());
945     perfEvents_.SetPid(pids);
946     if (!CheckOptionPidAndApp(pids)) {
947         HLOGV("CheckOptionPidAndApp() failed");
948         return HiperfError::CHECK_OPTION_PID_APP_FAIL;
949     }
950     std::string err = "";
951     if (allowIpc_ && !IsExistDebugByPid(inputPidTidArgs_, err)) {
952         return HiperfError::CHECK_OPTION_PID_APP_FAIL;
953     }
954     return HiperfError::NO_ERR;
955 }
956 
OnSubCommand(std::vector<std::string> & args)957 HiperfError SubCommandStat::OnSubCommand(std::vector<std::string>& args)
958 {
959     CHECK_TRUE(!HelpOption(), HiperfError::NO_ERR, 0, "");
960     if (!ParseControlCmd(controlCmd_)) {
961         return HiperfError::WRONG_CONTROL_CMD;
962     }
963     if (controlCmd_.empty() || controlCmd_ == CONTROL_CMD_PREPARE) {
964         HiperfError errorCode = CheckStatOption();
965         if (errorCode != HiperfError::NO_ERR) {
966             return errorCode;
967         }
968     }
969     if (!CheckOutPutFile()) {
970         return HiperfError::CHECK_OUT_PUT_ERROR;
971     }
972     if (!ProcessControl()) {
973         return HiperfError::PROCESS_CONTROL_FAIL;
974     } else if (isFifoClient_) {
975         return HiperfError::NO_ERR;
976     }
977     SetPerfEvent();
978     if (!PrepairEvents()) {
979         HLOGV("PrepairEvents() failed");
980         return HiperfError::PREPAIR_EVENTS_FAIL;
981     }
982 
983     // preapare fd
984     perfEvents_.PrepareTracking();
985     CreateClientThread();
986     // start tracking
987     if (restart_ && controlCmd_ == CONTROL_CMD_PREPARE) {
988         RETURN_IF(!perfEvents_.StartTracking(isFifoServer_), HiperfError::PREPARE_START_TRACKING_FAIL);
989     } else {
990         RETURN_IF(!perfEvents_.StartTracking((!isFifoServer_) && (clientPipeInput_ == -1)),
991                   HiperfError::START_TRACKING_FAIL);
992     }
993     CloseClientThread();
994     return HiperfError::NO_ERR;
995 }
996 
CloseClientThread()997 void SubCommandStat::CloseClientThread()
998 {
999     if (clientCommandHandle_.joinable()) {
1000         clientRunning_.store(false);
1001         HLOGI("CloseClientThread");
1002         if (nullFd_ != -1) {
1003             close(nullFd_);
1004         }
1005         clientCommandHandle_.join();
1006         close(clientPipeInput_);
1007         close(clientPipeOutput_);
1008         if (isFifoServer_) {
1009             remove(fifoFileC2S_.c_str());
1010             remove(fifoFileS2C_.c_str());
1011         }
1012     }
1013 }
1014 
ParseControlCmd(const std::string & cmd)1015 bool SubCommandStat::ParseControlCmd(const std::string& cmd)
1016 {
1017     if (cmd.empty() || cmd == CONTROL_CMD_PREPARE || cmd == CONTROL_CMD_START || cmd == CONTROL_CMD_STOP) {
1018         return true;
1019     }
1020 
1021     printf("Invalid --control %s option, command should be: prepare, start, stop.\n", cmd.c_str());
1022     return false;
1023 }
1024 
RegisterSubCommandStat()1025 bool RegisterSubCommandStat()
1026 {
1027     return SubCommand::RegisterSubCommand("stat", SubCommandStat::GetInstance);
1028 }
1029 
PrepairEvents()1030 bool SubCommandStat::PrepairEvents()
1031 {
1032     if (selectEvents_.empty() && selectGroups_.empty()) {
1033         perfEvents_.AddDefaultEvent(PERF_TYPE_HARDWARE);
1034         perfEvents_.AddDefaultEvent(PERF_TYPE_SOFTWARE);
1035     } else {
1036         for (auto events : selectEvents_) {
1037             if (!perfEvents_.AddEvents(events)) {
1038                 HLOGV("add events failed");
1039                 return false;
1040             }
1041         }
1042         for (auto events : selectGroups_) {
1043             if (!perfEvents_.AddEvents(events, true)) {
1044                 HLOGV("add groups failed");
1045                 return false;
1046             }
1047         }
1048     }
1049     return true;
1050 }
1051 
CheckSelectCpuPidOption()1052 bool SubCommandStat::CheckSelectCpuPidOption()
1053 {
1054     if (!selectCpus_.empty()) {
1055         // the only value is not -1
1056         if (!(selectCpus_.size() == 1 && selectCpus_.front() == -1)) {
1057             int maxCpuid = sysconf(_SC_NPROCESSORS_CONF) - 1;
1058             for (auto cpu : selectCpus_) {
1059                 if (cpu < 0 || cpu > maxCpuid) {
1060                     printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid);
1061                     return false;
1062                 }
1063             }
1064         }
1065     } else {
1066         // the cpu default -1
1067         if (!targetSystemWide_) {
1068             selectCpus_.push_back(-1);
1069         }
1070     }
1071 
1072     if (!selectPids_.empty()) {
1073         for (auto pid : selectPids_) {
1074             if (pid <= 0) {
1075                 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid);
1076                 return false;
1077             }
1078         }
1079     }
1080     if (!selectTids_.empty()) {
1081         for (auto tid : selectTids_) {
1082             if (tid <= 0) {
1083                 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid);
1084                 return false;
1085             }
1086         }
1087     }
1088     return true;
1089 }
1090 
CheckOptions(const std::vector<pid_t> & pids)1091 bool SubCommandStat::CheckOptions(const std::vector<pid_t> &pids)
1092 {
1093     if (targetSystemWide_) {
1094         if (!pids.empty() || !selectTids_.empty()) {
1095             printf("You cannot specify -a and -t/-p at the same time\n");
1096             return false;
1097         }
1098         if (!appPackage_.empty()) {
1099             printf("You cannot specify -a and --app at the same time\n");
1100             return false;
1101         }
1102     }
1103     if (!appPackage_.empty() && (!pids.empty() || !selectTids_.empty())) {
1104         printf("You cannot specify --app and -t/-p at the same time\n");
1105         return false;
1106     }
1107     if (!targetSystemWide_ && trackedCommand_.empty() && pids.empty() && appPackage_.empty()
1108         && selectTids_.empty()) {
1109         printf("You need to set the -p option or --app option.\n");
1110         return false;
1111     }
1112     if (targetSystemWide_ && !trackedCommand_.empty()) {
1113         printf("You cannot specify -a and a cmd at the same time\n");
1114         return false;
1115     }
1116     if (!trackedCommand_.empty()) {
1117         if (!pids.empty() || !selectTids_.empty()) {
1118             printf("You cannot specify a cmd and -t/-p at the same time\n");
1119             return false;
1120         }
1121         if (!appPackage_.empty()) {
1122             printf("You cannot specify a cmd and --app at the same time\n");
1123             return false;
1124         }
1125         if (!IsRoot()) {
1126             printf("%s options needs root privilege, please check usage\n",
1127                    VectorToString(trackedCommand_).c_str());
1128             return false;
1129         }
1130     }
1131     if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) {
1132         printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
1133                MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
1134         return false;
1135     }
1136     if (timeStopSec_ < 0) {
1137         printf("monitoring duration should be positive but %f is given\n", timeStopSec_);
1138         return false;
1139     }
1140     if (timeReportMs_ < 0) {
1141         printf("print interval should be non-negative but %d is given\n", timeReportMs_);
1142         return false;
1143     }
1144     return true;
1145 }
1146 
CheckOutPutFile()1147 bool SubCommandStat::CheckOutPutFile()
1148 {
1149     if (controlCmd_ != CONTROL_CMD_PREPARE) {
1150         if (!outputFilename_.empty()) {
1151             printf("-o option must use with --control prepare option\n");
1152             return false;
1153         } else {
1154             return true;
1155         }
1156     }
1157     if (outputFilename_.empty()) {
1158         outputFilename_ = DEFAULT_STAT_FILE;
1159     }
1160     if (!IsValidOutPath(outputFilename_)) {
1161         printf("Invalid output file path, permission denied\n");
1162         return false;
1163     }
1164     std::string resolvedPath = CanonicalizeSpecPath(outputFilename_.c_str());
1165     filePtr_ = fopen(resolvedPath.c_str(), "w");
1166     if (filePtr_ == nullptr) {
1167         printf("unable open file to '%s' because '%d'\n", outputFilename_.c_str(), errno);
1168         return false;
1169     }
1170     return true;
1171 }
1172 
AddReportArgs(CommandReporter & reporter)1173 void SubCommandStat::AddReportArgs(CommandReporter& reporter)
1174 {
1175     if (targetSystemWide_) {
1176         reporter.targetProcess_ = "ALL";
1177     } else if (!appPackage_.empty()) {
1178         reporter.targetProcess_ = appPackage_;
1179     } else {
1180         std::unordered_set<std::string> processNames = {};
1181         for_each(selectPids_.begin(), selectPids_.end(), [&processNames] (const pid_t& pid) {
1182             processNames.insert(GetProcessName(pid));
1183         });
1184         reporter.targetProcess_ = SetToString<std::string>(processNames);
1185     }
1186 }
1187 
GetInstance()1188 SubCommand& SubCommandStat::GetInstance()
1189 {
1190     static SubCommandStat subCommand;
1191     return subCommand;
1192 }
1193 } // namespace HiPerf
1194 } // namespace Developtools
1195 } // namespace OHOS
1196