/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef REPORT_JSON_FILE_H #define REPORT_JSON_FILE_H #include #include #include #include #include #include "debug_logger.h" #include "perf_file_reader.h" #include "utilities.h" #include "virtual_runtime.h" namespace OHOS { namespace Developtools { namespace HiPerf { using jsonStringMap = std::map; using jsonStringVector = std::vector; using jsonIntVector = std::vector; template void OutputJsonKey(FILE *output, const T &value) { if constexpr (std::is_same::value) { if (value.empty()) { // for key vector [] mode, not key is needed return; } fprintf(output, "\"%s\":", value.c_str()); } else if constexpr (std::is_same::value) { if (value.empty()) { // for key vector [] mode, not key is needed return; } fprintf(output, "\"%s\":", value.data()); } else if constexpr (std::is_same::type, char *>::value) { if (value[0] == '\0') { // same as value.empty() return; } fprintf(output, "\"%s\":", value); } else { fprintf(output, "\"%s\":", std::to_string(value).c_str()); } } template void OutputJsonValue(FILE *output, const T &value, bool first = true) { if (!first) { fprintf(output, ","); } if constexpr (std::is_same::value) { fprintf(output, "\"%s\"", value.c_str()); } else if constexpr (std::is_same::value) { fprintf(output, "\"%s\"", value.data()); } else if constexpr (std::is_same::value) { fprintf(output, "%s", std::to_string(value).c_str()); } else if constexpr (std::is_same::value) { fprintf(output, "%s", std::to_string(value).c_str()); } else if constexpr (std::is_same::value) { fprintf(output, "%s", std::to_string(value).c_str()); } else if constexpr (std::is_same::value) { fprintf(output, "%s", std::to_string(value).c_str()); } else if constexpr (std::is_same::type, char *>::value) { fprintf(output, "\"%s\"", value); } else { value.OutputJson(output); } } /* k:"v" k:1 */ template void OutputJsonPair(FILE *output, const K &key, const T &value, bool first = false) { if (!first) { if (fprintf(output, ",") < 0) { return; } } OutputJsonKey(output, key); OutputJsonValue(output, value); } /* k:[v1,v2,v3] */ template void OutputJsonVectorList(FILE *output, const std::string &key, const std::vector &value, bool first = false) { if (!first) { if (fprintf(output, ",") < 0) { return; } } if (fprintf(output, "\"%s\":[", key.c_str()) != -1) { auto it = value.begin(); while (it != value.end()) { OutputJsonValue(output, *it, it == value.begin()); it++; } if (fprintf(output, "]") < 0) { return; } } } /* k:[v1,v2,v3] */ template void OutputJsonMapList(FILE *output, const std::string &key, const std::map &value, bool first = false) { if (!first) { if (fprintf(output, ",") < 0) { return; } } if (fprintf(output, "\"%s\":[", key.c_str()) != -1) { auto it = value.begin(); while (it != value.end()) { OutputJsonValue(output, it->second, it == value.begin()); it++; } if (fprintf(output, "]") < 0) { return; } } } /* k:{k1:v1,k2:v2,k3:v3} */ template void OutputJsonMap(FILE *output, const std::string &key, const std::map &value, bool first = false) { if (!first) { if (fprintf(output, ",") < 0) { return; } } if (fprintf(output, "\"%s\":{", key.c_str()) != -1) { auto it = value.begin(); while (it != value.end()) { OutputJsonPair(output, it->first, it->second, it == value.begin()); it++; } if (fprintf(output, "}") < 0) { return; } } } template V &GetOrCreateMapItem(std::map &map, const K &key) { if (map.count(key) == 0) { map.emplace(key, (key)); return map.at(key); } else { return map.at(key); } } struct ReportFuncMapItem { int libId_ = -1; std::string_view funcName_; void OutputJson(FILE *output) const { if (fprintf(output, "{") < 0) { return; } OutputJsonPair(output, "file", libId_, true); OutputJsonPair(output, "symbol", funcName_); if (fprintf(output, "}") < 0) { return; } } ReportFuncMapItem(int libId, std::string_view funcName) : libId_(libId), funcName_(funcName) {} }; struct ReportFuncItem { int functionId_ = -1; int functionInLibId_ = -1; uint64_t sampleCount_ = 0; uint64_t eventCount_ = 0; uint64_t subTreeEventCount_ = 0; explicit ReportFuncItem(int functionId) : functionId_(functionId) {} void OutputJson(FILE *output) const { if (fprintf(output, "{") < 0) { return; } OutputJsonPair(output, "symbol", functionId_, true); OutputJsonVectorList(output, "counts", std::vector {sampleCount_, eventCount_, subTreeEventCount_}); if (fprintf(output, "}") < 0) { return; } } }; struct ReportCallNodeItem { uint64_t selfEventCount_ = 0; uint64_t subTreeEventCount_ = 0; int functionId_ = -1; int nodeIndex_ = -1; bool reverseCaller_ = false; std::string_view funcName_ = ""; std::string debug_ = ""; std::map childrenMap; void OutputJson(FILE *output) const { if (fprintf(output, "{") < 0) { return; } OutputJsonPair(output, "selfEvents", selfEventCount_, true); OutputJsonPair(output, "subEvents", subTreeEventCount_); OutputJsonPair(output, "symbol", functionId_); if (!funcName_.empty()) { // for debug OutputJsonPair(output, "funcName", funcName_); OutputJsonPair(output, "nodeIndex", nodeIndex_); OutputJsonPair(output, "reversed", reverseCaller_); } OutputJsonMapList(output, "callStack", childrenMap); if (fprintf(output, "}") < 0) { return; } } uint64_t UpdateChildrenEventCount() { subTreeEventCount_ = selfEventCount_; for (auto &pair : childrenMap) { subTreeEventCount_ += pair.second.UpdateChildrenEventCount(); if (!funcName_.empty()) { } } return subTreeEventCount_; } static bool FindByFunctionId(ReportCallNodeItem &a, int functionId) { return (a.functionId_ == functionId); } explicit ReportCallNodeItem(int functionId) : functionId_(functionId) {} }; struct ReportLibItem { int libId_ = 0; uint64_t eventCount_ = 0; std::map funcs_; void OutputJson(FILE *output) const { if (fprintf(output, "{") < 0) { return; } OutputJsonPair(output, "fileId", libId_, true); OutputJsonPair(output, "eventCount", eventCount_); OutputJsonMapList(output, "functions", funcs_); if (fprintf(output, "}") < 0) { return; } } }; struct ReportThreadItem { pid_t tid_ = 0; uint64_t eventCount_ = 0; uint64_t sampleCount_ = 0; std::map libs_; ReportCallNodeItem callNode; ReportCallNodeItem callNodeReverse; void OutputJson(FILE *output) const { if (fprintf(output, "{") < 0) { return; } OutputJsonPair(output, "tid", tid_, true); OutputJsonPair(output, "eventCount", eventCount_); OutputJsonPair(output, "sampleCount", sampleCount_); OutputJsonMapList(output, "libs", libs_); OutputJsonPair(output, "CallOrder", callNode); OutputJsonPair(output, "CalledOrder", callNodeReverse); if (fprintf(output, "}") < 0) { return; } } ReportThreadItem(pid_t id) : tid_(id), callNode(-1), callNodeReverse(-1) {} }; struct ReportProcessItem { pid_t pid_ = 0; uint64_t eventCount_ = 0; std::map threads_; void OutputJson(FILE *output) const { if (fprintf(output, "{") < 0) { return; } OutputJsonPair(output, "pid", pid_, true); OutputJsonPair(output, "eventCount", eventCount_); OutputJsonMapList(output, "threads", threads_); if (fprintf(output, "}") < 0) { return; } } explicit ReportProcessItem(pid_t pid) : pid_(pid) {} }; struct ReportConfigItem { int index_; std::string eventName_; uint64_t eventCount_ = 0; std::map processes_; void OutputJson(FILE *output) const { if (fprintf(output, "{") < 0) { return; } OutputJsonPair(output, "eventConfigName", eventName_, true); OutputJsonPair(output, "eventCount", eventCount_); OutputJsonMapList(output, "processes", processes_); if (fprintf(output, "}") < 0) { return; } } ReportConfigItem(int index, std::string eventName) : index_(index), eventName_(eventName) {} }; using functionKey = std::tuple; static constexpr const int keyLibId = 0; static constexpr const int keyfuncName = 1; class ReportJsonFile { public: int nodeIndex_ = 0; // debug only static bool debug_; FILE *output_ = nullptr; ReportJsonFile(const std::unique_ptr &recordFileReader, const VirtualRuntime &virtualRuntime) : recordFileReader_(recordFileReader), virtualRuntime_(virtualRuntime) { } void UpdateReportSample(uint64_t configid, pid_t pid, pid_t tid, uint64_t eventCount); void UpdateReportCallStack(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount, std::vector &frames); void UpdateCallNodeEventCount(); void ProcessSymbolsFiles(const std::vector> &symbolsFiles); // json bool OutputJson(FILE *output = nullptr); std::map, ReportConfigItem> reportConfigItems_; private: const std::unique_ptr &recordFileReader_; const VirtualRuntime &virtualRuntime_; std::vector libList_; std::vector functionList_; std::map functionMap_; void addNewFunction(int libId, std::string_view name); ReportConfigItem &GetConfig(uint64_t id); std::string GetConfigName(uint64_t id); uint32_t GetConfigIndex(uint64_t id); int GetFuncionID(int libId, std::string_view function); int GetLibID(std::string_view filepath); void OutputJsonFeatureString(); void OutputJsonRuntimeInfo(); void AddReportCallStack(uint64_t eventCount, ReportCallNodeItem &callNode, const std::vector &frames); void AddReportCallStackReverse(uint64_t eventCount, ReportCallNodeItem &callNode, const std::vector &frames); uint64_t sampleCount_ = 0; FRIEND_TEST(ReportJsonFileTest, UpdateReportSample); FRIEND_TEST(ReportJsonFileTest, UpdateReportCallStack); FRIEND_TEST(ReportJsonFileTest, UpdateCallNodeEventCount); FRIEND_TEST(ReportJsonFileTest, ProcessSymbolsFiles); FRIEND_TEST(ReportJsonFileTest, GetFuncionID); FRIEND_TEST(ReportJsonFileTest, GetLibID); FRIEND_TEST(ReportJsonFileTest, GetConfigIndex); FRIEND_TEST(ReportJsonFileTest, GetConfigName); FRIEND_TEST(ReportJsonFileTest, GetConfig); friend class ReportJsonFileTest; }; } // namespace HiPerf } // namespace Developtools } // namespace OHOS #endif // REPORT_JSON_FILE_H