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