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