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 #ifndef SUBCOMMAND_REPORT_H
17 #define SUBCOMMAND_REPORT_H
18 
19 #include <algorithm>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <functional>
23 #include <map>
24 #include <optional>
25 #include <set>
26 #include <tuple>
27 #include <linux/perf_event.h>
28 
29 #include "perf_file_reader.h"
30 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
31 #include "report_protobuf_file.h"
32 #endif
33 #include "debug_logger.h"
34 #include "option.h"
35 #include "perf_event_record.h"
36 #include "report.h"
37 #include "report_json_file.h"
38 #include "subcommand.h"
39 #include "symbols_file.h"
40 #include "utilities.h"
41 #include "virtual_runtime.h"
42 
43 namespace OHOS {
44 namespace Developtools {
45 namespace HiPerf {
46 class SubCommandReport : public SubCommand {
47 public:
SubCommandReport()48     SubCommandReport()
49         // clang-format off
50         : SubCommand("report", "report sampling information from perf.data format file",
51         "Usage: hiperf report [options]\n"
52         "       The default options are same as :\n"
53         "       -i perf.data --sort comm,pid,tid,dso,func\n"
54         "   --symbol-dir <dir>\n"
55         "       use symbols path to find symbols.\n"
56         "       separate the paths with commas.\n"
57         "   --limit-percent <number>\n"
58         "       only show heat percent limit content.\n"
59         "   -s / --call-stack\n"
60         "       show the unwind callstack\n"
61         "   --call-stack-limit-percent <number>\n"
62         "       only show the callstack heat percent limit content.\n"
63         "   --proto\n"
64         "       show protobuf content in the save file name.\n"
65         "       default file name is perf.data.\n"
66         "   --json\n"
67         "       report in json format.\n"
68         "       default file name is perf.data.\n"
69         "   --diff <target file>\n"
70         "       show the diff result from -i to -diff .\n"
71         "       example: \"report -i a.data --diff b.data\"\n"
72         "   --branch\n"
73         "       show the branch from address instead of ip address\n"
74         "   --<keys> <keyname1>[,keyname2][,...]\n"
75         "       select able keys: comms,pids,tids,dsos,funcs,from_dsos,from_funcs\n"
76         "           example: --comms hiperf\n"
77         "   --sort <key1>[,key2][,...]\n"
78         "       Choose some keywords.\n"
79         "       These keywords will be used for sorting.\n"
80         "       Only selected keywords will appear in the report.\n"
81         "\n"
82         "           pid             -- process id\n"
83         "           tid             -- thread id\n"
84         "           comm            -- thread name (can be changed for same thread)\n"
85         "           dso             -- like dso name , or elf path\n"
86         "           func            -- function name in the symbols\n"
87         "\n"
88         "       in branch mode:\n"
89         "           from_dso        -- branched from dso or elf\n"
90         "           from_func       -- branched from function\n"
91         "           dso             -- means branched to dso or elf\n"
92         "           func            -- means branched to function\n"
93         "   -i <filename>\n"
94         "           perf data file to report, default is perf.data\n"
95         "   -o <filename>\n"
96         "           report file name. if empty will use stdout print\n"
97         "   --hide_count\n"
98         "           will not show count in report\n"
99         "   --dumpoptions\n"
100         "           Dump command options.\n"
101         "\n"
102         ), recordFile_ {"perf.data", ""} // default file path is perf.data
103     // clang-format on
104     {
105     }
106     bool OnSubCommand(std::vector<std::string> &args) override;
107     bool ParseOption(std::vector<std::string> &args) override;
108     void DumpOptions(void) const override;
109     static bool RegisterSubCommandReport(void);
110     bool RecordCallBack(std::unique_ptr<PerfEventRecord> record);
111 
112     ~SubCommandReport();
113 
114 private:
115     void ProcessSample(std::unique_ptr<PerfRecordSample> &);
116     void BroadcastSample(std::unique_ptr<PerfRecordSample> &);
117 
118     bool VerifyOption();
119     bool VerifyDisplayOption();
120 
121     // load
122     bool PrepareOutput();
123     bool LoadPerfData();
124     void ProcessFeaturesData();
125     void LoadEventConfigData();
126     void LoadAttrSection();
127     void LoadEventDesc();
128     void ProcessSymbolsData();
129     void LoadPerfDataCompleted();
130     void ProcessUniStackTableData();
131 
132     bool OutputReport();
133     bool OutputStd();
134 
135     // prefdata
136     bool showCallStack_ = false;
137     bool diffMode_ = false;
138 
139     // create record file reader pointer
140     std::unique_ptr<PerfFileReader> recordFileReader_;
141     std::vector<std::string> symbolsPaths_;
142 
143     // report file name , if empty will use stdout
144     std::string reportFile_;
145 
146     // for diff report
147     enum RecordIndex { FIRST = 0, SECOND = 1, MAX = 2, CURRENT = -1 } index_ = FIRST;
148     std::string recordFile_[MAX];
149     ReportOption reportOption_;
150     Report report_[MAX] = {reportOption_, reportOption_};
GetReport(RecordIndex index = CURRENT, int configId = 0)151     inline Report &GetReport(RecordIndex index = CURRENT, int configId = 0)
152     {
153         if (index == CURRENT) {
154             return report_[index_];
155         } else {
156             return report_[index];
157         }
158     }
159 
160     std::vector<std::string> configNames_;
161     std::set<uint64_t> cpuOffids_;
162 
163     const std::string cpuOffEventName = "sched:sched_switch";
164     bool cpuOffMode_ = false;
165     std::map<pid_t, std::unique_ptr<PerfRecordSample>> prevSampleCache_;
166     void FlushCacheRecord();
167 
168     // in debug mode we will output some more debug info
169     bool debug_ = false;
170     bool branch_ = false;
171     bool jsonFormat_ = false;
172 
173     FILE *output_ = nullptr;
174 
175     std::unique_ptr<ReportJsonFile> reportJsonFile_ = nullptr;
176 
177     bool protobufFormat_ = false;
178 #if defined(HAVE_PROTOBUF) && HAVE_PROTOBUF
179     std::unique_ptr<ReportProtobufFileWriter> protobufOutputFileWriter_ = nullptr;
180     void UpdateReportInfo();
181 #endif
182     friend class SubCommandReportTest;
183     FRIEND_TEST(SubCommandReportTest, TestLoadPerfData);
184     FRIEND_TEST(SubCommandReportTest, TestOutputReport);
185     FRIEND_TEST(SubCommandReportTest, TestOutputStd);
186     FRIEND_TEST(SubCommandReportTest, TestVerifyOption);
187     FRIEND_TEST(SubCommandReportTest, TestVerifyDisplayOption);
188     FRIEND_TEST(SubCommandReportTest, TestPrepareConsole);
189     FRIEND_TEST(SubCommandReportTest, TestPrepareConsole);
190 
191     void SetHM();
192 };
193 } // namespace HiPerf
194 } // namespace Developtools
195 } // namespace OHOS
196 #endif // SUBCOMMAND_REPORT_H
197