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