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