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
30namespace OHOS {
31namespace Developtools {
32namespace HiPerf {
33using jsonStringMap = std::map<std::string, std::string>;
34using jsonStringVector = std::vector<std::string>;
35using jsonIntVector = std::vector<int>;
36
37template<class T>
38void 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}
62template<class T>
63void 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*/
91template<class K, class T>
92void 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*/
108template<class T>
109void 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*/
132template<class K, class V>
133void 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*/
156template<class K, class V>
157void 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
177template<class K, class V>
178V &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
186struct ReportFuncMapItem {
187    int libId_ = -1;
188    std::string funcName_;
189    int reportFuncId_ = -1;
190    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    }
201    ReportFuncMapItem(int libId, std::string &funcName, int reportFuncId)
202        : libId_(libId), funcName_(funcName), reportFuncId_(reportFuncId) {}
203};
204
205struct ReportFuncItem {
206    int functionId_ = -1;
207    int functionInLibId_ = -1;
208    uint64_t sampleCount_ = 0;
209    uint64_t eventCount_ = 0;
210    uint64_t subTreeEventCount_ = 0;
211    explicit ReportFuncItem(int functionId) : functionId_(functionId) {}
212    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
226struct 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
236    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
255    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
266    static bool FindByFunctionId(ReportCallNodeItem &a, int functionId)
267    {
268        return (a.functionId_ == functionId);
269    }
270
271    explicit ReportCallNodeItem(int functionId) : functionId_(functionId) {}
272};
273
274struct ReportLibItem {
275    int libId_ = 0;
276    uint64_t eventCount_ = 0;
277    std::map<int, ReportFuncItem> funcs_;
278    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
292struct 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;
299    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    }
314    ReportThreadItem(pid_t id) : tid_(id), callNode(-1), callNodeReverse(-1) {}
315};
316
317struct ReportProcessItem {
318    pid_t pid_ = 0;
319    uint64_t eventCount_ = 0;
320    std::map<pid_t, ReportThreadItem> threads_;
321    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    }
333    explicit ReportProcessItem(pid_t pid) : pid_(pid) {}
334};
335
336struct ReportConfigItem {
337    int index_;
338    std::string eventName_;
339    uint64_t eventCount_ = 0;
340    std::map<pid_t, ReportProcessItem> processes_;
341    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    }
353    ReportConfigItem(int index, std::string eventName) : index_(index), eventName_(eventName) {}
354};
355
356static constexpr const int keyLibId = 0;
357static constexpr const int keyfuncName = 1;
358
359class ReportJsonFile {
360public:
361    int nodeIndex_ = 0; // debug only
362    static bool debug_;
363    FILE *output_ = nullptr;
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
381private:
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