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
28const uint16_t ONE_HUNDRED = 100;
29const uint16_t THOUSNADS_SEPARATOR = 3;
30namespace OHOS {
31namespace Developtools {
32namespace HiPerf {
33static std::map<pid_t, ThreadInfos> thread_map_;
34static bool g_reportCpuFlag = false;
35static bool g_reportThreadFlag = false;
36static VirtualRuntime g_runtimeInstance;
37void 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
57bool SubCommandStat::ParseOption(std::vector<std::string> &args)
58{
59    if (args.size() == 1 and 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    if (!IsExistDebugByApp(appPackage_)) {
103        return false;
104    }
105    if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
106        return false;
107    }
108    if (!Option::GetOptionValue(args, "-p", selectPids_)) {
109        HLOGD("get option -p failed");
110        return false;
111    }
112    if (!IsExistDebugByPid(selectPids_)) {
113        return false;
114    }
115    if (!Option::GetOptionValue(args, "-t", selectTids_)) {
116        HLOGD("get option -t failed");
117        return false;
118    }
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
138bool 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
151void SubCommandStat::PrintUsage()
152{
153    printf("%s\n", Help().c_str());
154}
155
156void SubCommandStat::SetReportFlags(bool cpuFlag, bool threadFlag)
157{
158    g_reportCpuFlag = cpuFlag;
159    g_reportThreadFlag = threadFlag;
160}
161
162void SubCommandStat::Report(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents)
163{
164    bool isNeedPerCpuTid = false;
165    for (auto it = countEvents.begin(); it != countEvents.end(); ++it) {
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
179void 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
196void 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 == THOUSNADS_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
229void 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
246void 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
259void 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
294void 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 == THOUSNADS_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
323bool 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
339bool 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
349std::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, 0, "");
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
364void 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
399std::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        } else {
425            comment += 0;
426        }
427        return "hw-cpu-cycles";
428    }
429    if (configName == GetCommentConfigName(countEvent, "hw-instructions") && summary.eventCount != 0) {
430        double otherScale = 1.0;
431        __u64 cpuCyclesCount = 0;
432        bool other = FindPerCoreEventCount(summary, cpuCyclesCount, otherScale);
433        if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
434            comment += static_cast<double>(cpuCyclesCount) / summary.eventCount;
435            return "hw-instructions";
436        }
437    }
438    if (configName == GetCommentConfigName(countEvent, "hw-branch-misses")) {
439        double otherScale = 1.0;
440        __u64 branchInstructionsCount = 0;
441        bool other = FindPerCoreEventCount(summary, branchInstructionsCount, otherScale);
442        if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
443            branchInstructionsCount != 0) {
444            comment += (static_cast<double>(summary.eventCount) / branchInstructionsCount) * ONE_HUNDRED;
445            return "hw-branch-misses";
446        }
447    }
448    return HandleOtherConfig(comment, summary, running_time_in_sec, scale, findRunningTime);
449}
450
451std::string SubCommandStat::HandleOtherConfig(double &comment, PerfEvents::Summary &summary, double running_time_in_sec,
452                                              double scale, bool findRunningTime)
453{
454    comment = 0;
455    if (findRunningTime) {
456        double rate = 0;
457        if (scale != 0) {
458            rate = summary.eventCount / (running_time_in_sec / scale);
459        }
460        comment += rate;
461    }
462    return "";
463}
464
465bool SubCommandStat::IsMonitoredAtAllTime(const double &scale)
466{
467    constexpr double SCALE_ERROR_LIMIT = 1e-5;
468    return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
469}
470
471void SubCommandStat::GetComments(const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
472    std::map<std::string, std::string> &comments)
473{
474    double running_time_in_sec = 0;
475    __u64 group_id = 0;
476    double main_scale = 1.0;
477    bool findRunningTime = FindRunningTime(countEvents, running_time_in_sec, group_id, main_scale);
478    for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
479        std::string configName = it->first;
480        std::string commentConfigName = GetCommentConfigName(it->second, "sw-cpu-clock");
481        if (configName == commentConfigName) {
482            comments[configName] = "";
483            continue;
484        }
485        double scale = 1.0;
486        if (it->second->timeRunning < it->second->timeEnabled && it->second->timeRunning != 0) {
487            scale = static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
488        }
489        commentConfigName = GetCommentConfigName(it->second, "sw-task-clock");
490        if (configName == commentConfigName) {
491            double usedCpus = it->second->usedCpus * scale;
492            comments[configName] = StringPrintf("%lf cpus used", usedCpus);
493            continue;
494        }
495        commentConfigName = GetCommentConfigName(it->second, "hw-cpu-cycles");
496        if (configName == commentConfigName) {
497            if (findRunningTime &&
498                ((group_id == it->second->id) ||
499                 (IsMonitoredAtAllTime(main_scale) && IsMonitoredAtAllTime(scale)))) {
500                double hz = 0;
501                if (abs(running_time_in_sec) > ALMOST_ZERO) {
502                    hz = it->second->eventCount / (running_time_in_sec / scale);
503                }
504                comments[configName] = StringPrintf("%lf GHz", hz / 1e9);
505            } else {
506                comments[configName] = "";
507            }
508            continue;
509        }
510        commentConfigName = GetCommentConfigName(it->second, "hw-instructions");
511        if (configName == commentConfigName && it->second->eventCount != 0) {
512            std::string cpuSyclesName = GetCommentConfigName(it->second, "hw-cpu-cycles");
513            double otherScale = 1.0;
514            __u64 cpuCyclesCount = 0;
515            bool other = FindEventCount(countEvents, cpuSyclesName, it->second->id, cpuCyclesCount,
516                                        otherScale);
517            if (other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) {
518                double cpi = static_cast<double>(cpuCyclesCount) / it->second->eventCount;
519                comments[configName] = StringPrintf("%lf cycles per instruction", cpi);
520                continue;
521            }
522        }
523        commentConfigName = GetCommentConfigName(it->second, "hw-branch-misses");
524        if (configName == commentConfigName) {
525            std::string branchInsName = GetCommentConfigName(it->second, "hw-branch-instructions");
526            double otherScale = 1.0;
527            __u64 branchInstructionsCount = 0;
528            bool other = FindEventCount(countEvents, branchInsName, it->second->id,
529                                        branchInstructionsCount, otherScale);
530            if ((other || (IsMonitoredAtAllTime(otherScale) && IsMonitoredAtAllTime(scale))) &&
531                branchInstructionsCount != 0) {
532                double miss_rate =
533                    static_cast<double>(it->second->eventCount) / branchInstructionsCount;
534                comments[configName] = StringPrintf("%lf miss rate", miss_rate * ONE_HUNDRED);
535                continue;
536            }
537        }
538        if (findRunningTime && ((group_id == it->second->id) || (IsMonitoredAtAllTime(main_scale) &&
539                                                                 IsMonitoredAtAllTime(scale)))) {
540            double rate = it->second->eventCount / (running_time_in_sec / scale);
541            if (rate > 1e9) {
542                comments[configName] = StringPrintf("%.3lf G/sec", rate / 1e9);
543                continue;
544            }
545            if (rate > 1e6) {
546                comments[configName] = StringPrintf("%.3lf M/sec", rate / 1e6);
547                continue;
548            }
549            if (rate > 1e3) {
550                comments[configName] = StringPrintf("%.3lf K/sec", rate / 1e3);
551                continue;
552            }
553            comments[configName] = StringPrintf("%.3lf /sec", rate);
554        } else {
555            comments[configName] = "";
556        }
557    }
558}
559
560bool SubCommandStat::FindRunningTime(
561    const std::map<std::string, std::unique_ptr<PerfEvents::CountEvent>> &countEvents,
562    double &running_time_in_sec, __u64 &group_id, double &main_scale)
563{
564    for (auto it = countEvents.begin(); it != countEvents.end(); it++) {
565        if ((it->first == "sw-task-clock" || it->first == "sw-task-clock:u" ||
566             it->first == "sw-task-clock:k" || it->first == "sw-cpu-clock" ||
567             it->first == "sw-cpu-clock:u" || it->first == "sw-cpu-clock:k") &&
568            it->second->eventCount != 0u) {
569            group_id = it->second->id;
570            running_time_in_sec = it->second->eventCount / 1e9;
571            if (it->second->timeRunning < it->second->timeEnabled &&
572                it->second->timeRunning != 0) {
573                main_scale =
574                    static_cast<double>(it->second->timeEnabled) / it->second->timeRunning;
575            }
576            return true;
577        }
578    }
579    return false;
580}
581
582bool SubCommandStat::FindPercoreRunningTime(PerfEvents::Summary &summary, double &running_time_int_sec,
583                                            double &main_scale)
584{
585    CHECK_TRUE(summary.eventCount == 0, false, 0, "");
586    running_time_int_sec = summary.eventCount / 1e9;
587    if (summary.timeRunning < summary.timeEnabled && summary.timeRunning != 0) {
588        main_scale = static_cast<double>(summary.timeEnabled) / summary.timeRunning;
589    }
590    return true;
591}
592
593bool SubCommandStat::CheckOptionPidAndApp(std::vector<pid_t> pids)
594{
595    if (!CheckOptionPid(pids)) {
596        printf("Problems finding threads of monitor\n\n");
597        printf("Usage: perf stat [<options>] [<command>]\n\n");
598        printf("-p <pid>        stat events on existing process id\n");
599        printf("-t <tid>        stat events on existing thread id\n");
600        return false;
601    }
602    return true;
603}
604
605bool SubCommandStat::CheckOptionPid(std::vector<pid_t> pids)
606{
607    if (pids.empty()) {
608        return true;
609    }
610
611    for (auto pid : pids) {
612        if (!IsDir("/proc/" + std::to_string(pid))) {
613            printf("not exit pid %d\n", pid);
614            return false;
615        }
616    }
617    return true;
618}
619
620void 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
635bool SubCommandStat::OnSubCommand(std::vector<std::string> &args)
636{
637    CHECK_TRUE(HelpOption(), true, 0, "");
638    if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
639        return false;
640    }
641    // check option
642    if (!CheckSelectCpuPidOption()) {
643        return false;
644    }
645    if (!CheckOptions(selectPids_)) {
646        HLOGV("CheckOptions() failed");
647        return false;
648    }
649    if (!CheckAppIsRunning(selectPids_, appPackage_, checkAppMs_)) {
650        HLOGV("CheckAppIsRunning() failed");
651        return false;
652    }
653
654    perfEvents_.SetCpu(selectCpus_);
655    std::vector<pid_t> pids;
656    for (auto selectPid : selectPids_) {
657        HLOGD("[OnSubCommand] selectPid %d\n", 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
686bool RegisterSubCommandStat()
687{
688    return SubCommand::RegisterSubCommand("stat", std::make_unique<SubCommandStat>());
689}
690
691bool 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
713bool 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
752bool 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        if (!IsRoot()) {
787            printf("%s options needs root privilege, please check usage\n",
788                   VectorToString(trackedCommand_).c_str());
789            return false;
790        }
791    }
792    if (checkAppMs_ < MIN_CHECK_APP_MS || checkAppMs_ > MAX_CHECK_APP_MS) {
793        printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
794               MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
795        return false;
796    }
797    if (timeStopSec_ < 0) {
798        printf("monitoring duration should be positive but %f is given\n", timeStopSec_);
799        return false;
800    }
801    if (timeReportMs_ < 0) {
802        printf("print interval should be non-negative but %d is given\n", timeReportMs_);
803        return false;
804    }
805    return true;
806}
807} // namespace HiPerf
808} // namespace Developtools
809} // namespace OHOS
810