1 /*
2  * Copyright (c) 2021 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 REPORT_JSON_FILE_H
17 #define REPORT_JSON_FILE_H
18 
19 #include <algorithm>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <functional>
23 #include <map>
24 
25 #include "debug_logger.h"
26 #include "perf_file_reader.h"
27 #include "utilities.h"
28 #include "virtual_runtime.h"
29 
30 namespace OHOS {
31 namespace Developtools {
32 namespace HiPerf {
33 using jsonStringMap = std::map<std::string, std::string>;
34 using jsonStringVector = std::vector<std::string>;
35 using jsonIntVector = std::vector<int>;
36 
37 template<class T>
OutputJsonKey(FILE *output, const T &value)38 void OutputJsonKey(FILE *output, const T &value)
39 {
40     if constexpr (std::is_same<T, std::string>::value) {
41         if (value.empty()) {
42             // for key vector [] mode, not key is needed
43             return;
44         }
45         fprintf(output, "\"%s\":", value.c_str());
46     } else if constexpr (std::is_same<T, std::string_view>::value) {
47         if (value.empty()) {
48             // for key vector [] mode, not key is needed
49             return;
50         }
51         fprintf(output, "\"%s\":", value.data());
52     } else if constexpr (std::is_same<typename std::decay<T>::type, char *>::value) {
53         if (value[0] == '\0') {
54             // same as value.empty()
55             return;
56         }
57         fprintf(output, "\"%s\":", value);
58     } else {
59         fprintf(output, "\"%s\":", std::to_string(value).c_str());
60     }
61 }
62 template<class T>
OutputJsonValue(FILE *output, const T &value, bool first = true)63 void OutputJsonValue(FILE *output, const T &value, bool first = true)
64 {
65     if (!first) {
66         fprintf(output, ",");
67     }
68     if constexpr (std::is_same<T, std::string>::value) {
69         fprintf(output, "\"%s\"", value.c_str());
70     } else if constexpr (std::is_same<T, std::string_view>::value) {
71         fprintf(output, "\"%s\"", value.data());
72     } else if constexpr (std::is_same<T, int>::value) {
73         fprintf(output, "%s", std::to_string(value).c_str());
74     } else if constexpr (std::is_same<T, uint64_t>::value) {
75         fprintf(output, "%s", std::to_string(value).c_str());
76     } else if constexpr (std::is_same<T, bool>::value) {
77         fprintf(output, "%s", std::to_string(value).c_str());
78     } else if constexpr (std::is_same<T, size_t>::value) {
79         fprintf(output, "%s", std::to_string(value).c_str());
80     } else if constexpr (std::is_same<typename std::decay<T>::type, char *>::value) {
81         fprintf(output, "\"%s\"", value);
82     } else {
83         value.OutputJson(output);
84     }
85 }
86 
87 /*
88     k:"v"
89     k:1
90 */
91 template<class K, class T>
OutputJsonPair(FILE *output, const K &key, const T &value, bool first = false)92 void OutputJsonPair(FILE *output, const K &key, const T &value, bool first = false)
93 {
94     if (!first) {
95         if (fprintf(output, ",") < 0) {
96             return;
97         }
98     }
99     // for id, symbol
100     OutputJsonKey(output, key);
101     // ReportFuncMapItem funcName.
102     OutputJsonValue(output, value);
103 }
104 
105 /*
106     k:[v1,v2,v3]
107 */
108 template<class T>
OutputJsonVectorList(FILE *output, const std::string &key, const std::vector<T> &value, bool first = false)109 void OutputJsonVectorList(FILE *output, const std::string &key, const std::vector<T> &value,
110                           bool first = false)
111 {
112     if (!first) {
113         if (fprintf(output, ",") < 0) {
114             return;
115         }
116     }
117     if (fprintf(output, "\"%s\":[", key.c_str()) != -1) {
118         auto it = value.begin();
119         while (it != value.end()) {
120             OutputJsonValue(output, *it, it == value.begin());
121             it++;
122         }
123         if (fprintf(output, "]") < 0) {
124             return;
125         }
126     }
127 }
128 
129 /*
130     k:[v1,v2,v3]
131 */
132 template<class K, class V>
OutputJsonMapList(FILE *output, const std::string &key, const std::map<K, V> &value, bool first = false)133 void OutputJsonMapList(FILE *output, const std::string &key, const std::map<K, V> &value,
134                        bool first = false)
135 {
136     if (!first) {
137         if (fprintf(output, ",") < 0) {
138             return;
139         }
140     }
141     if (fprintf(output, "\"%s\":[", key.c_str()) != -1) {
142         auto it = value.begin();
143         while (it != value.end()) {
144             OutputJsonValue(output, it->second, it == value.begin());
145             it++;
146         }
147         if (fprintf(output, "]") < 0) {
148             return;
149         }
150     }
151 }
152 
153 /*
154     k:{k1:v1,k2:v2,k3:v3}
155 */
156 template<class K, class V>
OutputJsonMap(FILE *output, const std::string &key, const std::map<K, V> &value, bool first = false)157 void OutputJsonMap(FILE *output, const std::string &key, const std::map<K, V> &value,
158                    bool first = false)
159 {
160     if (!first) {
161         if (fprintf(output, ",") < 0) {
162             return;
163         }
164     }
165     if (fprintf(output, "\"%s\":{", key.c_str()) != -1) {
166         auto it = value.begin();
167         while (it != value.end()) {
168             OutputJsonPair(output, it->first, it->second, it == value.begin());
169             it++;
170         }
171         if (fprintf(output, "}") < 0) {
172             return;
173         }
174     }
175 }
176 
177 template<class K, class V>
GetOrCreateMapItem(std::map<K, V> &map, const K &key)178 V &GetOrCreateMapItem(std::map<K, V> &map, const K &key)
179 {
180     if (map.count(key) == 0) {
181         map.emplace(key, (key));
182     }
183     return map.at(key);
184 }
185 
186 struct ReportFuncMapItem {
187     int libId_ = -1;
188     std::string funcName_;
189     int reportFuncId_ = -1;
OutputJsonOHOS::Developtools::HiPerf::ReportFuncMapItem190     void OutputJson(FILE *output) const
191     {
192         if (fprintf(output, "{") < 0) {
193             return;
194         }
195         OutputJsonPair(output, "file", libId_, true);
196         OutputJsonPair(output, "symbol", funcName_);
197         if (fprintf(output, "}") < 0) {
198             return;
199         }
200     }
ReportFuncMapItemOHOS::Developtools::HiPerf::ReportFuncMapItem201     ReportFuncMapItem(int libId, std::string &funcName, int reportFuncId)
202         : libId_(libId), funcName_(funcName), reportFuncId_(reportFuncId) {}
203 };
204 
205 struct ReportFuncItem {
206     int functionId_ = -1;
207     int functionInLibId_ = -1;
208     uint64_t sampleCount_ = 0;
209     uint64_t eventCount_ = 0;
210     uint64_t subTreeEventCount_ = 0;
ReportFuncItemOHOS::Developtools::HiPerf::ReportFuncItem211     explicit ReportFuncItem(int functionId) : functionId_(functionId) {}
OutputJsonOHOS::Developtools::HiPerf::ReportFuncItem212     void OutputJson(FILE *output) const
213     {
214         if (fprintf(output, "{") < 0) {
215             return;
216         }
217         OutputJsonPair(output, "symbol", functionId_, true);
218         OutputJsonVectorList(output, "counts",
219                              std::vector<uint64_t> {sampleCount_, eventCount_, subTreeEventCount_});
220         if (fprintf(output, "}") < 0) {
221             return;
222         }
223     }
224 };
225 
226 struct ReportCallNodeItem {
227     uint64_t selfEventCount_ = 0;
228     uint64_t subTreeEventCount_ = 0;
229     int functionId_ = -1;
230     int nodeIndex_ = -1;
231     bool reverseCaller_ = false;
232     std::string_view funcName_ = "";
233     std::string debug_ = "";
234     std::map<int, ReportCallNodeItem> childrenMap;
235 
OutputJsonOHOS::Developtools::HiPerf::ReportCallNodeItem236     void OutputJson(FILE *output) const
237     {
238         if (fprintf(output, "{") < 0) {
239             return;
240         }
241         OutputJsonPair(output, "selfEvents", selfEventCount_, true);
242         OutputJsonPair(output, "subEvents", subTreeEventCount_);
243         OutputJsonPair(output, "symbol", functionId_);
244         if (!funcName_.empty()) { // for debug
245             OutputJsonPair(output, "funcName", funcName_);
246             OutputJsonPair(output, "nodeIndex", nodeIndex_);
247             OutputJsonPair(output, "reversed", reverseCaller_);
248         }
249         OutputJsonMapList(output, "callStack", childrenMap);
250         if (fprintf(output, "}") < 0) {
251             return;
252         }
253     }
254 
UpdateChildrenEventCountOHOS::Developtools::HiPerf::ReportCallNodeItem255     uint64_t UpdateChildrenEventCount()
256     {
257         subTreeEventCount_ = selfEventCount_;
258         for (auto &pair : childrenMap) {
259             subTreeEventCount_ += pair.second.UpdateChildrenEventCount();
260             if (!funcName_.empty()) {
261             }
262         }
263         return subTreeEventCount_;
264     }
265 
FindByFunctionIdOHOS::Developtools::HiPerf::ReportCallNodeItem266     static bool FindByFunctionId(ReportCallNodeItem &a, int functionId)
267     {
268         return (a.functionId_ == functionId);
269     }
270 
ReportCallNodeItemOHOS::Developtools::HiPerf::ReportCallNodeItem271     explicit ReportCallNodeItem(int functionId) : functionId_(functionId) {}
272 };
273 
274 struct ReportLibItem {
275     int libId_ = 0;
276     uint64_t eventCount_ = 0;
277     std::map<int, ReportFuncItem> funcs_;
OutputJsonOHOS::Developtools::HiPerf::ReportLibItem278     void OutputJson(FILE *output) const
279     {
280         if (fprintf(output, "{") < 0) {
281             return;
282         }
283         OutputJsonPair(output, "fileId", libId_, true);
284         OutputJsonPair(output, "eventCount", eventCount_);
285         OutputJsonMapList(output, "functions", funcs_);
286         if (fprintf(output, "}") < 0) {
287             return;
288         }
289     }
290 };
291 
292 struct ReportThreadItem {
293     pid_t tid_ = 0;
294     uint64_t eventCount_ = 0;
295     uint64_t sampleCount_ = 0;
296     std::map<int, ReportLibItem> libs_;
297     ReportCallNodeItem callNode;
298     ReportCallNodeItem callNodeReverse;
OutputJsonOHOS::Developtools::HiPerf::ReportThreadItem299     void OutputJson(FILE *output) const
300     {
301         if (fprintf(output, "{") < 0) {
302             return;
303         }
304         OutputJsonPair(output, "tid", tid_, true);
305         OutputJsonPair(output, "eventCount", eventCount_);
306         OutputJsonPair(output, "sampleCount", sampleCount_);
307         OutputJsonMapList(output, "libs", libs_);
308         OutputJsonPair(output, "CallOrder", callNode);
309         OutputJsonPair(output, "CalledOrder", callNodeReverse);
310         if (fprintf(output, "}") < 0) {
311             return;
312         }
313     }
ReportThreadItemOHOS::Developtools::HiPerf::ReportThreadItem314     ReportThreadItem(pid_t id) : tid_(id), callNode(-1), callNodeReverse(-1) {}
315 };
316 
317 struct ReportProcessItem {
318     pid_t pid_ = 0;
319     uint64_t eventCount_ = 0;
320     std::map<pid_t, ReportThreadItem> threads_;
OutputJsonOHOS::Developtools::HiPerf::ReportProcessItem321     void OutputJson(FILE *output) const
322     {
323         if (fprintf(output, "{") < 0) {
324             return;
325         }
326         OutputJsonPair(output, "pid", pid_, true);
327         OutputJsonPair(output, "eventCount", eventCount_);
328         OutputJsonMapList(output, "threads", threads_);
329         if (fprintf(output, "}") < 0) {
330             return;
331         }
332     }
ReportProcessItemOHOS::Developtools::HiPerf::ReportProcessItem333     explicit ReportProcessItem(pid_t pid) : pid_(pid) {}
334 };
335 
336 struct ReportConfigItem {
337     int index_;
338     std::string eventName_;
339     uint64_t eventCount_ = 0;
340     std::map<pid_t, ReportProcessItem> processes_;
OutputJsonOHOS::Developtools::HiPerf::ReportConfigItem341     void OutputJson(FILE *output) const
342     {
343         if (fprintf(output, "{") < 0) {
344             return;
345         }
346         OutputJsonPair(output, "eventConfigName", eventName_, true);
347         OutputJsonPair(output, "eventCount", eventCount_);
348         OutputJsonMapList(output, "processes", processes_);
349         if (fprintf(output, "}") < 0) {
350             return;
351         }
352     }
ReportConfigItemOHOS::Developtools::HiPerf::ReportConfigItem353     ReportConfigItem(int index, std::string eventName) : index_(index), eventName_(eventName) {}
354 };
355 
356 static constexpr const int keyLibId = 0;
357 static constexpr const int keyfuncName = 1;
358 
359 class ReportJsonFile {
360 public:
361     int nodeIndex_ = 0; // debug only
362     static bool debug_;
363     FILE *output_ = nullptr;
ReportJsonFile(const std::unique_ptr<PerfFileReader> &recordFileReader, const VirtualRuntime &virtualRuntime)364     ReportJsonFile(const std::unique_ptr<PerfFileReader> &recordFileReader,
365                    const VirtualRuntime &virtualRuntime)
366         : recordFileReader_(recordFileReader), virtualRuntime_(virtualRuntime)
367     {
368     }
369 
370     void UpdateReportSample(uint64_t configid, pid_t pid, pid_t tid, uint64_t eventCount);
371     void UpdateReportCallStack(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount,
372                                std::vector<DfxFrame> &frames);
373     void UpdateCallNodeEventCount();
374     void ProcessSymbolsFiles(const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles);
375 
376     // json
377     bool OutputJson(FILE *output = nullptr);
378 
379     std::map<std::vector<uint64_t>, ReportConfigItem> reportConfigItems_;
380 
381 private:
382     const std::unique_ptr<PerfFileReader> &recordFileReader_;
383     const VirtualRuntime &virtualRuntime_;
384     std::vector<std::string_view> libList_;
385     int functionId_ = 0;
386     std::map<int, std::map<std::string, ReportFuncMapItem>> functionMap_;
387     void AddNewFunction(int libId, std::string name);
388     void OutputJsonFunctionMap(FILE *output);
389 
390     ReportConfigItem &GetConfig(uint64_t id);
391     std::string GetConfigName(uint64_t id);
392     uint32_t GetConfigIndex(uint64_t id);
393 
394     int GetFunctionID(int libId, const std::string &function);
395     int GetLibID(std::string_view filepath);
396 
397     void OutputJsonFeatureString();
398     void OutputJsonRuntimeInfo();
399 
400     void AddReportCallStack(uint64_t eventCount, ReportCallNodeItem &callNode,
401                             const std::vector<DfxFrame> &frames);
402     void AddReportCallStackReverse(uint64_t eventCount, ReportCallNodeItem &callNode,
403                                    const std::vector<DfxFrame> &frames);
404     uint64_t sampleCount_ = 0;
405 
406     FRIEND_TEST(ReportJsonFileTest, UpdateReportSample);
407     FRIEND_TEST(ReportJsonFileTest, UpdateReportCallStack);
408     FRIEND_TEST(ReportJsonFileTest, UpdateCallNodeEventCount);
409     FRIEND_TEST(ReportJsonFileTest, ProcessSymbolsFiles);
410     FRIEND_TEST(ReportJsonFileTest, GetFunctionID);
411     FRIEND_TEST(ReportJsonFileTest, GetLibID);
412     FRIEND_TEST(ReportJsonFileTest, GetConfigIndex);
413     FRIEND_TEST(ReportJsonFileTest, GetConfigName);
414     FRIEND_TEST(ReportJsonFileTest, GetConfig);
415     friend class ReportJsonFileTest;
416 };
417 } // namespace HiPerf
418 } // namespace Developtools
419 } // namespace OHOS
420 #endif // REPORT_JSON_FILE_H
421