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