• 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 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