• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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