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 #define HILOG_TAG "Report"
16 
17 #include <set>
18 #include "report_json_file.h"
19 
20 #if defined(is_mingw) && is_mingw
21 #include <windows.h>
22 #else
23 #include <sys/ioctl.h>
24 #endif
25 
26 #include "hiperf_hilog.h"
27 
28 namespace OHOS {
29 namespace Developtools {
30 namespace HiPerf {
31 bool ReportJsonFile::debug_ = false;
32 
AddNewFunction(int libId, std::string name)33 void ReportJsonFile::AddNewFunction(int libId, std::string name)
34 {
35     auto it = functionMap_.find(libId);
36     if (it == functionMap_.end()) {
37         it = functionMap_.try_emplace(libId).first;
38     }
39     it->second.insert_or_assign(name, ReportFuncMapItem(libId, name, functionId_++));
40 }
41 
OutputJsonFunctionMap(FILE *output)42 void ReportJsonFile::OutputJsonFunctionMap(FILE *output)
43 {
44     std::string key = "SymbolMap";
45     if (fprintf(output, "\"%s\":{", key.c_str()) != -1) {
46         bool first = true;
47 
48         for (const auto& [libId, funcMap] : functionMap_) {
49             for (const auto& [_, reportFuncMapItem] : funcMap) {
50                 OutputJsonPair(output, reportFuncMapItem.reportFuncId_, reportFuncMapItem, first);
51                 first = false;
52             }
53         }
54 
55         fprintf(output, "}");
56     }
57 }
58 
ProcessSymbolsFiles( const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)59 void ReportJsonFile::ProcessSymbolsFiles(
60     const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
61 {
62     auto symbolsFileIt = symbolsFiles.begin();
63     while (symbolsFileIt != symbolsFiles.end()) {
64         size_t libId = libList_.size();
65         libList_.emplace_back(symbolsFileIt->get()->filePath_);
66         const auto &symbols = symbolsFileIt->get()->GetSymbols();
67         auto symbolIt = symbols.begin();
68         while (symbolIt != symbols.end()) {
69             AddNewFunction(libId, std::string(symbolIt->GetName()));
70             symbolIt++;
71         }
72         symbolsFileIt++;
73     }
74 }
75 
UpdateCallNodeEventCount()76 void ReportJsonFile::UpdateCallNodeEventCount()
77 {
78     for (auto &config : reportConfigItems_) {
79         HLOGV("Config %s", config.second.eventName_.c_str());
80         for (auto &process : config.second.processes_) {
81             for (auto &thread : process.second.threads_) {
82                 thread.second.callNode.UpdateChildrenEventCount();
83                 thread.second.callNodeReverse.UpdateChildrenEventCount();
84             }
85         }
86     }
87 }
88 
GetConfig(uint64_t id)89 ReportConfigItem &ReportJsonFile::GetConfig(uint64_t id)
90 {
91     for (auto &configpair : reportConfigItems_) {
92         if (find(configpair.first.begin(), configpair.first.end(), id) != configpair.first.end()) {
93             return configpair.second;
94         }
95     }
96     HLOGE("unable found config item for config id %" PRIu64 "", id);
97     // return default one
98     return reportConfigItems_.begin()->second;
99 }
100 
GetFunctionID(int libId, const std::string &function)101 int ReportJsonFile::GetFunctionID(int libId, const std::string &function)
102 {
103     auto functionMapIt = functionMap_.find(libId);
104     if (functionMapIt == functionMap_.end()) {
105         functionMapIt = functionMap_.try_emplace(libId).first;
106     }
107     auto funcMapIt = functionMapIt->second.find(function);
108     if (funcMapIt == functionMapIt->second.end()) {
109         HLOGW("'%s' not found in function list in lib %d", function.data(), libId);
110         // make a new function for unknown name
111         AddNewFunction(libId, function);
112         // return the last index
113         return functionId_ - 1;
114     }
115     return funcMapIt->second.reportFuncId_;
116 }
117 
UpdateReportSample(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount)118 void ReportJsonFile::UpdateReportSample(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount)
119 {
120     auto &config = GetConfig(id);
121 
122     config.eventCount_ += eventCount;
123     auto &process = GetOrCreateMapItem(config.processes_, pid);
124     process.eventCount_ += eventCount;
125     auto &thread = GetOrCreateMapItem(process.threads_, tid);
126     thread.eventCount_ += eventCount;
127     thread.sampleCount_++;
128     sampleCount_++;
129 }
130 
AddReportCallStack(uint64_t eventCount, ReportCallNodeItem &callNode, const std::vector<DfxFrame> &frames)131 void ReportJsonFile::AddReportCallStack(uint64_t eventCount, ReportCallNodeItem &callNode,
132                                         const std::vector<DfxFrame> &frames)
133 {
134     std::map<int, ReportCallNodeItem> *child = &callNode.childrenMap;
135     auto it = frames.begin();
136     while (it != frames.end()) {
137         int libId = GetLibID(it->mapName);
138         if (libId >= 0) {
139             int funcId = GetFunctionID(libId, it->funcName);
140             // new children funid
141             ReportCallNodeItem &grandchildren = GetOrCreateMapItem(*child, funcId);
142             if (debug_) {
143                 grandchildren.nodeIndex_ = nodeIndex_++;
144                 grandchildren.funcName_ = it->funcName;
145                 grandchildren.reverseCaller_ = true;
146             }
147             // only the last one need count
148             if (it + 1 == frames.end()) {
149                 grandchildren.selfEventCount_ += eventCount;
150             }
151             // new children's children
152             child = &grandchildren.childrenMap;
153 
154             HLOGV("add child %*s %d-%d %s @%d %s", static_cast<int>(it - frames.begin()), "", libId,
155                   funcId, it->funcName.data(), grandchildren.nodeIndex_, it->mapName.data());
156         } else {
157             HLOGV("add child failed at %s", it->ToSymbolString().c_str());
158         }
159         it++;
160     }
161 }
162 
AddReportCallStackReverse(uint64_t eventCount, ReportCallNodeItem &callNode, const std::vector<DfxFrame> &frames)163 void ReportJsonFile::AddReportCallStackReverse(uint64_t eventCount, ReportCallNodeItem &callNode,
164                                                const std::vector<DfxFrame> &frames)
165 {
166     std::map<int, ReportCallNodeItem> *child = &callNode.childrenMap;
167     auto it = frames.rbegin();
168     while (it != frames.rend()) {
169         int libId = GetLibID(it->mapName);
170         if (libId >= 0) {
171             int funcId = GetFunctionID(libId, it->funcName);
172             // new children funid
173             ReportCallNodeItem &grandchildren = GetOrCreateMapItem(*child, funcId);
174             if (debug_) {
175                 grandchildren.nodeIndex_ = nodeIndex_++;
176                 grandchildren.funcName_ = it->funcName;
177             }
178             // only the last one need count
179             if (it + 1 == frames.rend()) {
180                 grandchildren.selfEventCount_ += eventCount;
181             }
182             // new children's children
183             child = &grandchildren.childrenMap;
184 
185             HLOGV("add child %*s %d-%d %s @%d %s", static_cast<int>(it - frames.rbegin()), "",
186                   libId, funcId, it->funcName.data(), grandchildren.nodeIndex_,
187                   it->mapName.data());
188         } else {
189             HLOGV("add child failed at %s", it->ToSymbolString().c_str());
190         }
191         it++;
192     }
193 }
194 
GetConfigIndex(uint64_t id)195 uint32_t ReportJsonFile::GetConfigIndex(uint64_t id)
196 {
197     return GetConfig(id).index_;
198 }
199 
GetConfigName(uint64_t id)200 std::string ReportJsonFile::GetConfigName(uint64_t id)
201 {
202     auto &config = GetConfig(id);
203     return config.eventName_;
204 }
205 
GetLibID(std::string_view filepath)206 int ReportJsonFile::GetLibID(std::string_view filepath)
207 {
208     auto it = find(libList_.begin(), libList_.end(), filepath);
209     if (it != libList_.end()) {
210         return it - libList_.begin();
211     } else {
212         HLOGE("'%s' not found in lib list", filepath.data());
213         return -1;
214     }
215 }
216 
UpdateReportCallStack(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount, std::vector<DfxFrame> &frames)217 void ReportJsonFile::UpdateReportCallStack(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount,
218                                            std::vector<DfxFrame> &frames)
219 {
220     auto &config = GetConfig(id);
221     std::set<int> RepeatFunctionId;
222     CHECK_TRUE(frames.size() == 0, NO_RETVAL, 0, ""); // do nothing with no frame
223     auto &process = GetOrCreateMapItem(config.processes_, pid);
224     auto &thread = GetOrCreateMapItem(process.threads_, tid);
225     auto it = frames.begin();
226     bool jsFrame = StringEndsWith(it->mapName, "stub.an");
227     size_t skipFrame = 0;
228     while (it != frames.end()) {
229         int libId = GetLibID(it->mapName);
230         if (libId < 0) {
231             HLOGW("not found lib path %s", it->mapName.data());
232             it++;
233             continue;
234         }
235         ReportLibItem &lib = thread.libs_[libId];
236         lib.libId_ = libId;
237         int funcId = GetFunctionID(libId, it->funcName);
238         // we will always have a funId, it will create a new one if not found
239         // so that we can see abc+0x123 in the html
240         HLOG_ASSERT(funcId >= 0);
241         if (RepeatFunctionId.count(funcId) != 0) {
242             it++;
243             continue;
244         } else {
245             RepeatFunctionId.emplace(funcId);
246         }
247 
248         ReportFuncItem &func = GetOrCreateMapItem(lib.funcs_, funcId);
249 
250         // always count subtree
251         func.subTreeEventCount_ += eventCount;
252 
253         // only calc the first frame event count
254         if (jsFrame && frames.size() > 1) {
255             skipFrame = 1; // 1 : for arkjs frame,skip the stub.an frame
256         }
257         if (it == frames.begin() + skipFrame) {
258             func.eventCount_ += eventCount;
259             func.sampleCount_ += 1;
260             lib.eventCount_ += eventCount;
261         }
262         // go on next frame
263         it++;
264     }
265     /*
266         frames are the other way around
267         0 is the last called.
268         So the order of json callstack should be 0 at the end
269         callNode is Reverse Order of frames
270         callNodeReverse is Normal Order frames
271     */
272     AddReportCallStackReverse(eventCount, thread.callNode, frames);
273     AddReportCallStack(eventCount, thread.callNodeReverse, frames);
274 }
275 
OutputJsonFeatureString()276 void ReportJsonFile::OutputJsonFeatureString()
277 {
278     OutputJsonPair(output_, "deviceTime",
279                    recordFileReader_->GetFeatureString(FEATURE::HIPERF_RECORD_TIME), true);
280     std::string device = recordFileReader_->GetFeatureString(FEATURE::HOSTNAME);
281     device.append(" " + recordFileReader_->GetFeatureString(FEATURE::OSRELEASE));
282     device.append(" " + recordFileReader_->GetFeatureString(FEATURE::ARCH));
283 
284     OutputJsonPair(output_, "deviceType", device);
285 
286     OutputJsonPair(output_, "osVersion", recordFileReader_->GetFeatureString(FEATURE::OSRELEASE));
287 
288     OutputJsonPair(output_, "deviceCommandLine",
289                    recordFileReader_->GetFeatureString(FEATURE::CMDLINE));
290 
291     OutputJsonPair(output_, "totalRecordSamples", sampleCount_);
292 }
293 
OutputJsonRuntimeInfo()294 void ReportJsonFile::OutputJsonRuntimeInfo()
295 {
296     const auto &threadMaps = virtualRuntime_.GetThreads();
297     std::map<std::string, std::string> jsonProcesses;
298     std::map<std::string, std::string> jsonThreads;
299     for (const auto &pair : threadMaps) {
300         const VirtualThread &thread = pair.second;
301         if (thread.pid_ == thread.tid_) {
302             jsonProcesses.emplace(std::to_string(thread.pid_), thread.name_);
303         }
304         // process also is a thread.
305         jsonThreads.emplace(std::to_string(thread.tid_), thread.name_);
306     }
307 
308     OutputJsonMap(output_, "processNameMap", jsonProcesses);
309 
310     OutputJsonMap(output_, "threadNameMap", jsonThreads);
311 
312     const auto &symbolsFiles = virtualRuntime_.GetSymbolsFiles();
313     jsonStringVector jsonFilePaths;
314     for (const auto &symbolsFile : symbolsFiles) {
315         jsonFilePaths.emplace_back(symbolsFile->filePath_);
316     }
317 
318     OutputJsonVectorList(output_, "symbolsFileList", jsonFilePaths);
319     if (fprintf(output_, ",") < 0) {
320         return;
321     }
322 
323     OutputJsonFunctionMap(output_);
324     if (fprintf(output_, ",") < 0) {
325         return;
326     }
327 
328     OutputJsonMapList(output_, "recordSampleInfo", reportConfigItems_, true);
329 }
330 
OutputJson(FILE *output)331 bool ReportJsonFile::OutputJson(FILE *output)
332 {
333     CHECK_TRUE(output == nullptr, false, 0, "");
334     output_ = output;
335     if (fprintf(output, "{") < 0) {
336         return false;
337     }
338     OutputJsonFeatureString();
339     OutputJsonRuntimeInfo();
340 
341     if (fprintf(output, "}") < 0) {
342         return false;
343     }
344     return true;
345 }
346 } // namespace HiPerf
347 } // namespace Developtools
348 } // namespace OHOS
349