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