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