1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define HILOG_TAG "Stat"
17
18 #include "subcommand_stat.h"
19
20 #include <csignal>
21 #include <iostream>
22 #include <memory>
23
24 #include "debug_logger.h"
25 #include "hiperf_hilog.h"
26 #include "utilities.h"
27
28 const uint16_t ONE_HUNDRED = 100;
29 const uint16_t THOUSANDS_SEPARATOR = 3;
30 namespace OHOS {
31 namespace Developtools {
32 namespace HiPerf {
33 static std::map<pid_t, ThreadInfos> thread_map_;
34 static bool g_reportCpuFlag = false;
35 static bool g_reportThreadFlag = false;
36 static VirtualRuntime g_runtimeInstance;
DumpOptions() const37 void SubCommandStat::DumpOptions() const
38 {
39 printf("DumpOptions:\n");
40 printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false");
41 printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str());
42 printf(" timeStopSec:\t%f sec\n", timeStopSec_);
43 printf(" timeReportMs:\t%d ms\n", timeReportMs_);
44 printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str());
45 printf(" selectGroups:\t%s\n", VectorToString(selectGroups_).c_str());
46 printf(" noCreateNew:\t%s\n", noCreateNew_ ? "true" : "false");
47 printf(" appPackage:\t%s\n", appPackage_.c_str());
48 printf(" checkAppMs_:\t%d\n", checkAppMs_);
49 printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str());
50 printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str());
51 printf(" restart:\t%s\n", restart_ ? "true" : "false");
52 printf(" perCore:\t%s\n", perCpus_ ? "true" : "false");
53 printf(" perTread:\t%s\n", perThreads_ ? "true" : "false");
54 printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false");
55 }
56
ParseOption(std::vector<std::string> & args)57 bool SubCommandStat::ParseOption(std::vector<std::string> &args)
58 {
59 if (args.size() == 1 && args[0] == "-h") {
60 args.clear();
61 helpOption_ = true;
62 PrintUsage();
63 return true;
64 }
65 if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) {
66 HLOGD("get option -a failed");
67 return false;
68 }
69 if (targetSystemWide_ && !IsSupportNonDebuggableApp()) {
70 HLOGD("-a option needs root privilege for system wide profiling.");
71 printf("-a option needs root privilege for system wide profiling.\n");
72 return false;
73 }
74 if (!Option::GetOptionValue(args, "-c", selectCpus_)) {
75 HLOGD("get option -c failed");
76 return false;
77 }
78 if (!Option::GetOptionValue(args, "-d", timeStopSec_)) {
79 HLOGD("get option -d failed");
80 return false;
81 }
82 if (!Option::GetOptionValue(args, "-i", timeReportMs_)) {
83 HLOGD("get option -i failed");
84 return false;
85 }
86 if (!Option::GetOptionValue(args, "-e", selectEvents_)) {
87 HLOGD("get option -e failed");
88 return false;
89 }
90 if (!Option::GetOptionValue(args, "-g", selectGroups_)) {
91 HLOGD("get option -g failed");
92 return false;
93 }
94 if (!Option::GetOptionValue(args, "--no-inherit", noCreateNew_)) {
95 HLOGD("get option --no-inherit failed");
96 return false;
97 }
98 if (!Option::GetOptionValue(args, "--app", appPackage_)) {
99 HLOGD("get option --app failed");
100 return false;
101 }
102 std::string err = "";
103 if (!IsExistDebugByApp(appPackage_, err)) {
104 return false;
105 }
106 if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
107 return false;
108 }
109 if (!Option::GetOptionValue(args, "-p", selectPids_)) {
110 HLOGD("get option -p failed");
111 return false;
112 }
113 inputPidTidArgs_ = selectPids_;
114 if (!Option::GetOptionValue(args, "-t", selectTids_)) {
115 HLOGD("get option -t failed");
116 return false;
117 }
118 inputPidTidArgs_.insert(inputPidTidArgs_.end(), selectTids_.begin(), selectTids_.end());
119 if (!Option::GetOptionValue(args, "--restart", restart_)) {
120 HLOGD("get option --restart failed");
121 return false;
122 }
123 if (!Option::GetOptionValue(args, "--per-core", perCpus_)) {
124 HLOGD("get option --per-core failed");
125 return false;
126 }
127 if (!Option::GetOptionValue(args, "--per-thread", perThreads_)) {
128 HLOGD("get option --per-thread failed");
129 return false;
130 }
131 if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) {
132 HLOGD("get option --verbose failed");
133 return false;
134 }
135 return ParseSpecialOption(args);
136 }
137
ParseSpecialOption(std::vector<std::string> & args)138 bool SubCommandStat::ParseSpecialOption(std::vector<std::string> &args)
139 {
140 if (!Option::GetOptionTrackedCommand(args, trackedCommand_)) {
141 HLOGD("get cmd failed");
142 return false;
143 }
144 if (!args.empty()) {
145 HLOGD("redundant option(s)");
146 return false;
147 }
148 return true;
149 }
150
PrintUsage()151 void SubCommandStat::PrintUsage()
152 {
153 printf("%s\n", Help().c_str());
154 }
155
SetReportFlags(bool cpuFlag,bool threadFlag)156 void SubCommandStat::SetReportFlags(bool cpuFlag, bool threadFlag)
157 {
158 g_reportCpuFlag = cpuFlag;
159 g_reportThreadFlag = threadFlag;
160 }
161
Report(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents)162 void SubCommandStat::Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
163 {
164 bool isNeedPerCpuTid = false;
165 for (const auto &it : countEvents) {
166 if (!(it.second->summaries.empty())) {
167 isNeedPerCpuTid = true;
168 break;
169 }
170 }
171 if (isNeedPerCpuTid) {
172 PrintPerHead();
173 ReportDetailInfos(countEvents);
174 } else {
175 ReportNormal(countEvents);
176 }
177 }
178
PrintPerHead()179 void SubCommandStat::PrintPerHead()
180 {
181 // print head
182 if (g_reportCpuFlag && g_reportThreadFlag) {
183 printf(" %24s %-30s | %-30s %10s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name",
184 "pid", "tid", "coreid", "comment", "coverage");
185 return;
186 }
187 if (g_reportCpuFlag) {
188 printf(" %24s %-30s | %10s | %-32s | %s\n", "count", "event_name", "coreid", "comment", "coverage");
189 return;
190 }
191 printf(" %24s %-30s | %-30s %10s %10s | %-32s | %s\n", "count", "event_name", "thread_name", "pid", "tid",
192 "comment", "coverage");
193 return;
194 }
195
PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> & reportSum,const float & ratio,std::string & configName)196 void SubCommandStat::PrintPerValue(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, const float &ratio,
197 std::string &configName)
198 {
199 if (reportSum == nullptr) {
200 return;
201 }
202 // print value
203 std::string strEventCount = std::to_string(reportSum->eventCountSum);
204 for (size_t i = strEventCount.size() >= 1 ? strEventCount.size() - 1 : 0, j = 1; i > 0; --i, ++j) {
205 if (j == THOUSANDS_SEPARATOR) {
206 j = 0;
207 strEventCount.insert(strEventCount.begin() + i, ',');
208 }
209 }
210
211 std::string commentStr;
212 MakeComments(reportSum, commentStr);
213
214 if (g_reportCpuFlag && g_reportThreadFlag) {
215 printf(" %24s %-30s | %-30s %10d %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
216 reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, reportSum->cpu, commentStr.c_str(),
217 reportSum->scaleSum * ratio);
218 } else if (g_reportCpuFlag) {
219 printf(" %24s %-30s | %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
220 reportSum->cpu, commentStr.c_str(), reportSum->scaleSum * ratio);
221 } else {
222 printf(" %24s %-30s | %-30s %10d %10d | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
223 reportSum->threadName.c_str(), reportSum->pid, reportSum->tid, commentStr.c_str(),
224 reportSum->scaleSum * ratio);
225 }
226 fflush(stdout);
227 }
228
InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> & newPerMap,const PerfEvents::Summary & summary,VirtualRuntime & virtualInstance)229 void SubCommandStat::InitPerMap(const std::unique_ptr<PerfEvents::ReportSum> &newPerMap,
230 const PerfEvents::Summary &summary, VirtualRuntime& virtualInstance)
231 {
232 CHECK_TRUE(newPerMap == nullptr, NO_RETVAL, 0, "");
233 newPerMap->cpu = summary.cpu;
234 if (g_reportCpuFlag && !g_reportThreadFlag) {
235 return;
236 }
237 newPerMap->tid = summary.tid;
238 newPerMap->pid = thread_map_.find(summary.tid)->second.pid;
239 bool isTid = true;
240 if (newPerMap->pid == newPerMap->tid) {
241 isTid = false;
242 }
243 newPerMap->threadName = virtualInstance.ReadThreadName(summary.tid, isTid);
244 }
245
GetPerKey(std::string & perKey,const PerfEvents::Summary & summary)246 void SubCommandStat::GetPerKey(std::string &perKey, const PerfEvents::Summary &summary)
247 {
248 perKey = "";
249 if (g_reportCpuFlag) {
250 perKey += std::to_string(summary.cpu);
251 perKey += "|";
252 }
253 if (g_reportThreadFlag) {
254 perKey += std::to_string(summary.tid);
255 }
256 return;
257 }
258
ReportDetailInfos(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents)259 void SubCommandStat::ReportDetailInfos(
260 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
261 {
262 std::string perKey = "";
263 std::map<std::string, std::unique_ptr<PerfEvents::ReportSum>> perMaps;
264 for (auto event = countEvents.begin(); event != countEvents.end(); ++event) {
265 if (event->second == nullptr || event->second->eventCount == 0) {
266 continue;
267 }
268 constexpr float ratio {100.0};
269 std::string configName = event->first;
270 perMaps.clear();
271 for (auto &it : event->second->summaries) {
272 GetPerKey(perKey, it);
273 if (perMaps.count(perKey) == 0) {
274 auto perMap = std::make_unique<PerfEvents::ReportSum>(PerfEvents::ReportSum {});
275 InitPerMap(perMap, it, g_runtimeInstance);
276 perMaps[perKey] = std::move(perMap);
277 }
278 if (perMaps[perKey] == nullptr) {
279 continue;
280 }
281 perMaps[perKey]->configName = GetDetailComments(event->second, perMaps[perKey]->commentSum,
282 it, configName);
283 perMaps[perKey]->eventCountSum += it.eventCount;
284 if (it.timeRunning < it.timeEnabled && it.timeRunning != 0) {
285 perMaps[perKey]->scaleSum = 1 / (static_cast<double>(it.timeEnabled) / it.timeRunning);
286 }
287 }
288 for (auto iper = perMaps.begin(); iper != perMaps.end(); iper++) {
289 PrintPerValue(iper->second, ratio, configName);
290 }
291 }
292 }
293
ReportNormal(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents)294 void SubCommandStat::ReportNormal(
295 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
296 {
297 // print head
298 printf(" %24s %-30s | %-32s | %s\n", "count", "name", "comment", "coverage");
299 std::map<std::string, std::string> comments;
300 GetComments(countEvents, comments);
301 for (auto it = countEvents.begin(); it != countEvents.end(); ++it) {
302 double scale = 1.0;
303 constexpr float ratio {100.0};
304 std::string configName = it->first;
305 std::string comment = comments[configName];
306 std::string strEventCount = std::to_string(it->second->eventCount);
307 for (size_t i = strEventCount.size() >= 1 ? strEventCount.size() - 1 : 0, j = 1; i > 0; --i, ++j) {
308 if (j == THOUSANDS_SEPARATOR) {
309 strEventCount.insert(strEventCount.begin() + i, ',');
310 j = 0;
311 }
312 }
313 if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) {
314 scale = 1 / (static_cast<double>(it->second->timeEnabled) / it->second->timeRunning);
315 }
316 printf(" %24s %-30s | %-32s | (%.0lf%%)\n", strEventCount.c_str(), configName.c_str(),
317 comment.c_str(), scale * ratio);
318
319 fflush(stdout);
320 }
321 }
322
FindEventCount(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,const std::string & configName,const __u64 group_id,__u64 & eventCount,double & scale)323 bool SubCommandStat::FindEventCount(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
324 const std::string &configName, const __u64 group_id, __u64 &eventCount, double &scale)
325 {
326 auto itr = countEvents.find(configName);
327 if (itr != countEvents.end()) {
328 eventCount = itr->second->eventCount;
329 if (itr->second->id == group_id
330 && itr->second->timeRunning < itr->second->timeEnabled
331 && itr->second->timeRunning != 0) {
332 scale = static_cast<double>(itr->second->timeEnabled) / itr->second->timeRunning;
333 return true;
334 }
335 }
336 return false;
337 }
338
FindPerCoreEventCount(PerfEvents::Summary & summary,__u64 & eventCount,double & scale)339 bool SubCommandStat::FindPerCoreEventCount(PerfEvents::Summary &summary, __u64 &eventCount, double &scale)
340 {
341 eventCount = summary.eventCount;
342 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
343 scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
344 return true;
345 }
346 return false;
347 }
348
GetCommentConfigName(const std::unique_ptr<PerfEvents::CountEvent> & countEvent,std::string eventName)349 std::string SubCommandStat::GetCommentConfigName(
350 const std::unique_ptr<PerfEvents::CountEvent> &countEvent, std::string eventName)
351 {
352 std::string commentConfigName = "";
353 CHECK_TRUE(countEvent == nullptr || eventName.length() == 0, commentConfigName, 1, "countEvent is nullptr");
354 if (countEvent->userOnly) {
355 commentConfigName = eventName + ":u";
356 } else if (countEvent->kernelOnly) {
357 commentConfigName = eventName + ":k";
358 } else {
359 commentConfigName = eventName;
360 }
361 return commentConfigName;
362 }
363
MakeComments(const std::unique_ptr<PerfEvents::ReportSum> & reportSum,std::string & commentStr)364 void SubCommandStat::MakeComments(const std::unique_ptr<PerfEvents::ReportSum> &reportSum, std::string &commentStr)
365 {
366 CHECK_TRUE(reportSum == nullptr || reportSum->commentSum == 0, NO_RETVAL, 0, "");
367 if (reportSum->configName == "sw-task-clock") {
368 commentStr = StringPrintf("%lf cpus used", reportSum->commentSum);
369 return;
370 }
371 if (reportSum->configName == "hw-cpu-cycles") {
372 commentStr = StringPrintf("%lf GHz", reportSum->commentSum);
373 return;
374 }
375 if (reportSum->configName == "hw-instructions") {
376 commentStr = StringPrintf("%lf cycles per instruction", reportSum->commentSum);
377 return;
378 }
379 if (reportSum->configName == "hw-branch-misses") {
380 commentStr = StringPrintf("%lf miss rate", reportSum->commentSum);
381 return;
382 }
383
384 if (reportSum->commentSum > 1e9) {
385 commentStr = StringPrintf("%.3lf G/sec", reportSum->commentSum / 1e9);
386 return;
387 }
388 if (reportSum->commentSum > 1e6) {
389 commentStr = StringPrintf("%.3lf M/sec", reportSum->commentSum / 1e6);
390 return;
391 }
392 if (reportSum->commentSum > 1e3) {
393 commentStr = StringPrintf("%.3lf K/sec", reportSum->commentSum / 1e3);
394 return;
395 }
396 commentStr = StringPrintf("%.3lf /sec", reportSum->commentSum);
397 }
398
GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> & countEvent,double & comment,PerfEvents::Summary & summary,std::string & configName)399 std::string SubCommandStat::GetDetailComments(const std::unique_ptr<PerfEvents::CountEvent> &countEvent,
400 double &comment, PerfEvents::Summary &summary, std::string &configName)
401 {
402 double running_time_in_sec = 0;
403 double main_scale = 1.0;
404 bool findRunningTime = FindPercoreRunningTime(summary, running_time_in_sec, main_scale);
405 if (configName == GetCommentConfigName(countEvent, "sw-cpu-clock")) {
406 comment = 0;
407 return "sw-cpu-clock";
408 }
409 double scale = 1.0;
410 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
411 scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
412 }
413 if (configName == GetCommentConfigName(countEvent, "sw-task-clock")) {
414 comment += countEvent->usedCpus * scale;
415 return "sw-task-clock";
416 }
417 if (configName == GetCommentConfigName(countEvent, "hw-cpu-cycles")) {
418 if (findRunningTime) {
419 double hz = 0;
420 if (abs(running_time_in_sec) > ALMOST_ZERO) {
421 hz = summary.eventCount / (running_time_in_sec / scale);
422 }
423 comment += hz / 1e9;
424 }
425 return "hw-cpu-cycles";
426 }
427 if (configName == GetCommentConfigName(countEvent, "hw-instructions") && summary.eventCount != 0) {
428 double otherScale = 1.0;
429 __u64 cpuCyclesCount = 0;
430 bool other = FindPerCoreEventCount(summary, cpuCyclesCount, otherScale);
431 if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
432 comment += static_cast<double>(cpuCyclesCount) / summary.eventCount;
433 return "hw-instructions";
434 }
435 }
436 if (configName == GetCommentConfigName(countEvent, "hw-branch-misses")) {
437 double otherScale = 1.0;
438 __u64 branchInstructionsCount = 0;
439 bool other = FindPerCoreEventCount(summary, branchInstructionsCount, otherScale);
440 if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
441 branchInstructionsCount != 0) {
442 comment += (static_cast<double>(summary.eventCount) / branchInstructionsCount) * ONE_HUNDRED;
443 return "hw-branch-misses";
444 }
445 }
446 return HandleOtherConfig(comment, summary, running_time_in_sec, scale, findRunningTime);
447 }
448
HandleOtherConfig(double & comment,PerfEvents::Summary & summary,double running_time_in_sec,double scale,bool findRunningTime)449 std::string SubCommandStat::HandleOtherConfig(double &comment, PerfEvents::Summary &summary, double running_time_in_sec,
450 double scale, bool findRunningTime)
451 {
452 comment = 0;
453 if (findRunningTime) {
454 double rate = 0;
455 if (scale != 0) {
456 rate = summary.eventCount / (running_time_in_sec / scale);
457 }
458 comment += rate;
459 }
460 return "";
461 }
462
IsMonitoredAtAllTime(const double & scale)463 bool SubCommandStat::IsMonitoredAtAllTime(const double &scale)
464 {
465 constexpr double SCALE_ERROR_LIMIT = 1e-5;
466 return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
467 }
468
GetComments(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,std::map<std::string,std::string> & comments)469 void SubCommandStat::GetComments(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
470 std::map<std::string, std::string> &comments)
471 {
472 double running_time_in_sec = 0;
473 __u64 group_id = 0;
474 double main_scale = 1.0;
475 bool findRunningTime = FindRunningTime(countEvents, running_time_in_sec, group_id, main_scale);
476 for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
477 std::string configName = it->first;
478 std::string commentConfigName = GetCommentConfigName(it->second, "sw-cpu-clock");
479 if (configName == commentConfigName) {
480 comments[configName] = "";
481 continue;
482 }
483 double scale = 1.0;
484 if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) {
485 scale = static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
486 }
487 commentConfigName = GetCommentConfigName(it->second, "sw-task-clock");
488 if (configName == commentConfigName) {
489 double usedCpus = it->second->usedCpus * scale;
490 comments[configName] = StringPrintf("%lf cpus used", usedCpus);
491 continue;
492 }
493 commentConfigName = GetCommentConfigName(it->second, "hw-cpu-cycles");
494 if (configName == commentConfigName) {
495 if (findRunningTime &&
496 ((group_id == it->second->id) ||
497 (IsMonitoredAtAllTime(main_scale) && IsMonitoredAtAllTime(scale)))) {
498 double hz = 0;
499 if (abs(running_time_in_sec) > ALMOST_ZERO) {
500 hz = it->second->eventCount / (running_time_in_sec / scale);
501 }
502 comments[configName] = StringPrintf("%lf GHz", hz / 1e9);
503 } else {
504 comments[configName] = "";
505 }
506 continue;
507 }
508 commentConfigName = GetCommentConfigName(it->second, "hw-instructions");
509 if (configName == commentConfigName && it->second->eventCount != 0) {
510 std::string cpuSyclesName = GetCommentConfigName(it->second, "hw-cpu-cycles");
511 double otherScale = 1.0;
512 __u64 cpuCyclesCount = 0;
513 bool other = FindEventCount(countEvents, cpuSyclesName, it->second->id, cpuCyclesCount,
514 otherScale);
515 if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
516 double cpi = static_cast<double>(cpuCyclesCount) / it->second->eventCount;
517 comments[configName] = StringPrintf("%lf cycles per instruction", cpi);
518 continue;
519 }
520 }
521 commentConfigName = GetCommentConfigName(it->second, "hw-branch-misses");
522 if (configName == commentConfigName) {
523 std::string branchInsName = GetCommentConfigName(it->second, "hw-branch-instructions");
524 double otherScale = 1.0;
525 __u64 branchInstructionsCount = 0;
526 bool other = FindEventCount(countEvents, branchInsName, it->second->id,
527 branchInstructionsCount, otherScale);
528 if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
529 branchInstructionsCount != 0) {
530 double miss_rate =
531 static_cast<double>(it->second->eventCount) / branchInstructionsCount;
532 comments[configName] = StringPrintf("%lf miss rate", miss_rate * ONE_HUNDRED);
533 continue;
534 }
535 }
536 if (findRunningTime && ((group_id == it->second->id) || (IsMonitoredAtAllTime(main_scale) &&
537 IsMonitoredAtAllTime(scale)))) {
538 double rate = it->second->eventCount / (running_time_in_sec / scale);
539 if (rate > 1e9) {
540 comments[configName] = StringPrintf("%.3lf G/sec", rate / 1e9);
541 continue;
542 }
543 if (rate > 1e6) {
544 comments[configName] = StringPrintf("%.3lf M/sec", rate / 1e6);
545 continue;
546 }
547 if (rate > 1e3) {
548 comments[configName] = StringPrintf("%.3lf K/sec", rate / 1e3);
549 continue;
550 }
551 comments[configName] = StringPrintf("%.3lf /sec", rate);
552 } else {
553 comments[configName] = "";
554 }
555 }
556 }
557
FindRunningTime(const std::map<std::string,std::unique_ptr<PerfEvents::CountEvent>> & countEvents,double & running_time_in_sec,__u64 & group_id,double & main_scale)558 bool SubCommandStat::FindRunningTime(
559 const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
560 double &running_time_in_sec, __u64 &group_id, double &main_scale)
561 {
562 for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
563 if ((it->first == "sw-task-clock" || it->first == "sw-task-clock:u" ||
564 it->first == "sw-task-clock:k" || it->first == "sw-cpu-clock" ||
565 it->first == "sw-cpu-clock:u" || it->first == "sw-cpu-clock:k") &&
566 it->second->eventCount != 0u) {
567 group_id = it->second->id;
568 running_time_in_sec = it->second->eventCount / 1e9;
569 if (it->second->timeRunning < it->second->timeEnabled &&
570 it->second->timeRunning != 0) {
571 main_scale =
572 static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
573 }
574 return true;
575 }
576 }
577 return false;
578 }
579
FindPercoreRunningTime(PerfEvents::Summary & summary,double & running_time_int_sec,double & main_scale)580 bool SubCommandStat::FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec,
581 double &main_scale)
582 {
583 CHECK_TRUE(summary.eventCount == 0, false, 0, "");
584 running_time_int_sec = summary.eventCount / 1e9;
585 if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
586 main_scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
587 }
588 return true;
589 }
590
CheckOptionPidAndApp(std::vector<pid_t> pids)591 bool SubCommandStat::CheckOptionPidAndApp(std::vector<pid_t> pids)
592 {
593 if (!CheckOptionPid(pids)) {
594 printf("Problems finding threads of monitor\n\n");
595 printf("Usage: perf stat [<options>] [<command>]\n\n");
596 printf("-p <pid> stat events on existing process id\n");
597 printf("-t <tid> stat events on existing thread id\n");
598 return false;
599 }
600 return true;
601 }
602
CheckOptionPid(std::vector<pid_t> pids)603 bool SubCommandStat::CheckOptionPid(std::vector<pid_t> pids)
604 {
605 if (pids.empty()) {
606 return true;
607 }
608
609 for (auto pid : pids) {
610 std::ostringstream oss;
611 oss << "/proc/" << pid;
612 if (!IsDir(oss.str())) {
613 printf("not exit pid %d\n", pid);
614 return false;
615 }
616 }
617 return true;
618 }
619
SetPerfEvent()620 void SubCommandStat::SetPerfEvent()
621 {
622 SetReportFlags(perCpus_, perThreads_);
623 perfEvents_.SetSystemTarget(targetSystemWide_);
624 perfEvents_.SetTimeOut(timeStopSec_);
625 perfEvents_.SetTimeReport(timeReportMs_);
626 perfEvents_.SetPerCpu(perCpus_);
627 perfEvents_.SetPerThread(perThreads_);
628 perfEvents_.SetVerboseReport(verboseReport_);
629 perfEvents_.SetInherit(!noCreateNew_);
630 perfEvents_.SetTrackedCommand(trackedCommand_);
631 // set report handle
632 perfEvents_.SetStatCallBack(Report);
633 }
634
OnSubCommand(std::vector<std::string> & args)635 HiperfError SubCommandStat::OnSubCommand(std::vector<std::string>& args)
636 {
637 CHECK_TRUE(HelpOption(), HiperfError::NO_ERR, 0, "");
638 if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
639 return HiperfError::CHECK_RESTART_OPTION_FAIL;
640 }
641 // check option
642 if (!CheckSelectCpuPidOption()) {
643 return HiperfError::CHECK_SELECT_CPU_PID_FAIL;
644 }
645 if (!CheckOptions(selectPids_)) {
646 HLOGV("CheckOptions() failed");
647 return HiperfError::CHECK_STAT_OPTION_FAIL;
648 }
649 if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) {
650 HLOGV("CheckAppIsRunning() failed");
651 return HiperfError::CHECK_APP_RUNNING_FAIL;
652 }
653 if (!CheckOptionPid(selectPids_)) {
654 HLOGV("CheckOptionPid() failed");
655 return HiperfError::CHECK_OPTION_PID_FAIL;
656 }
657
658 perfEvents_.SetCpu(selectCpus_);
659 std::vector<pid_t> pids;
660 for (auto selectPid : selectPids_) {
661 HLOGD("[OnSubCommand] selectPid %d\n", selectPid);
662 std::vector<pid_t> subTids = GetSubthreadIDs(selectPid, thread_map_);
663 if (!subTids.empty()) {
664 pids.insert(pids.end(), subTids.begin(), subTids.end());
665 } else {
666 HLOGD("[OnSubCommand] subTids empty for %d\n", selectPid);
667 }
668 }
669 pids.insert(pids.end(), selectTids_.begin(), selectTids_.end());
670 perfEvents_.SetPid(pids);
671 if (!CheckOptionPidAndApp(pids)) {
672 HLOGV("CheckOptionPidAndApp() failed");
673 return HiperfError::CHECK_OPTION_PID_APP_FAIL;
674 }
675 std::string err = "";
676 if (!IsExistDebugByPid(inputPidTidArgs_, err)) {
677 return HiperfError::CHECK_OPTION_PID_APP_FAIL;
678 }
679 SetPerfEvent();
680 if (!PrepairEvents()) {
681 HLOGV("PrepairEvents() failed");
682 return HiperfError::PREPAIR_EVENTS_FAIL;
683 }
684
685 // preapare fd
686 perfEvents_.PrepareTracking();
687
688 // start tracking
689 perfEvents_.StartTracking();
690
691 return HiperfError::NO_ERR;
692 }
693
RegisterSubCommandStat()694 bool RegisterSubCommandStat()
695 {
696 return SubCommand::RegisterSubCommand("stat", SubCommandStat::GetInstance);
697 }
698
PrepairEvents()699 bool SubCommandStat::PrepairEvents()
700 {
701 if (selectEvents_.empty() && selectGroups_.empty()) {
702 perfEvents_.AddDefaultEvent(PERF_TYPE_HARDWARE);
703 perfEvents_.AddDefaultEvent(PERF_TYPE_SOFTWARE);
704 } else {
705 for (auto events : selectEvents_) {
706 if (!perfEvents_.AddEvents(events)) {
707 HLOGV("add events failed");
708 return false;
709 }
710 }
711 for (auto events : selectGroups_) {
712 if (!perfEvents_.AddEvents(events, true)) {
713 HLOGV("add groups failed");
714 return false;
715 }
716 }
717 }
718 return true;
719 }
720
CheckSelectCpuPidOption()721 bool SubCommandStat::CheckSelectCpuPidOption()
722 {
723 if (!selectCpus_.empty()) {
724 // the only value is not -1
725 if (!(selectCpus_.size() == 1 && selectCpus_.front() == -1)) {
726 int maxCpuid = sysconf(_SC_NPROCESSORS_CONF) - 1;
727 for (auto cpu : selectCpus_) {
728 if (cpu < 0 || cpu > maxCpuid) {
729 printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid);
730 return false;
731 }
732 }
733 }
734 } else {
735 // the cpu default -1
736 if (!targetSystemWide_) {
737 selectCpus_.push_back(-1);
738 }
739 }
740
741 if (!selectPids_.empty()) {
742 for (auto pid : selectPids_) {
743 if (pid <= 0) {
744 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid);
745 return false;
746 }
747 }
748 }
749 if (!selectTids_.empty()) {
750 for (auto tid : selectTids_) {
751 if (tid <= 0) {
752 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid);
753 return false;
754 }
755 }
756 }
757 return true;
758 }
759
CheckOptions(const std::vector<pid_t> & pids)760 bool SubCommandStat::CheckOptions(const std::vector<pid_t> &pids)
761 {
762 if (targetSystemWide_) {
763 if (!pids.empty() || !selectTids_.empty()) {
764 printf("You cannot specify -a and -t/-p at the same time\n");
765 return false;
766 }
767 if (!appPackage_.empty()) {
768 printf("You cannot specify -a and --app at the same time\n");
769 return false;
770 }
771 }
772 if (!appPackage_.empty() && (!pids.empty() || !selectTids_.empty())) {
773 printf("You cannot specify --app and -t/-p at the same time\n");
774 return false;
775 }
776 if (!targetSystemWide_ && trackedCommand_.empty() && pids.empty() && appPackage_.empty()
777 && selectTids_.empty()) {
778 printf("You need to set the -p option or --app option.\n");
779 return false;
780 }
781 if (targetSystemWide_ && !trackedCommand_.empty()) {
782 printf("You cannot specify -a and a cmd at the same time\n");
783 return false;
784 }
785 if (!trackedCommand_.empty()) {
786 if (!pids.empty() || !selectTids_.empty()) {
787 printf("You cannot specify a cmd and -t/-p at the same time\n");
788 return false;
789 }
790 if (!appPackage_.empty()) {
791 printf("You cannot specify a cmd and --app at the same time\n");
792 return false;
793 }
794 if (!IsRoot()) {
795 printf("%s options needs root privilege, please check usage\n",
796 VectorToString(trackedCommand_).c_str());
797 return false;
798 }
799 }
800 if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) {
801 printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
802 MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
803 return false;
804 }
805 if (timeStopSec_ < 0) {
806 printf("monitoring duration should be positive but %f is given\n", timeStopSec_);
807 return false;
808 }
809 if (timeReportMs_ < 0) {
810 printf("print interval should be non-negative but %d is given\n", timeReportMs_);
811 return false;
812 }
813 return true;
814 }
815
AddReportArgs(CommandReporter & reporter)816 void SubCommandStat::AddReportArgs(CommandReporter& reporter)
817 {
818 if (targetSystemWide_) {
819 reporter.targetProcess_ = "ALL";
820 } else if (!appPackage_.empty()) {
821 reporter.targetProcess_ = appPackage_;
822 } else {
823 std::unordered_set<std::string> processNames = {};
824 for_each(selectPids_.begin(), selectPids_.end(), [&processNames] (const pid_t& pid) {
825 processNames.insert(GetProcessName(pid));
826 });
827 reporter.targetProcess_ = SetToString<std::string>(processNames);
828 }
829 }
830
GetInstance()831 SubCommand& SubCommandStat::GetInstance()
832 {
833 static SubCommandStat subCommand;
834 return subCommand;
835 }
836 } // namespace HiPerf
837 } // namespace Developtools
838 } // namespace OHOS
839