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