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