* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define HILOG_TAG "Report"
#include <set>
#include "report_json_file.h"
#if defined(is_mingw) && is_mingw
#include <windows.h>
#else
#include <sys/ioctl.h>
#endif
#include "hiperf_hilog.h"
namespace OHOS {
namespace Developtools {
namespace HiPerf {
bool ReportJsonFile::debug_ = false;
void ReportJsonFile::AddNewFunction(const int libId, std::string name)
{
auto it = functionMap_.find(libId);
if (it == functionMap_.end()) {
it = functionMap_.try_emplace(libId).first;
}
name = StringReplace(name, "\\", "\\\\");
name = StringReplace(name, "\"", "");
it->second.insert_or_assign(name, ReportFuncMapItem(libId, name, functionId_++));
}
void ReportJsonFile::OutputJsonFunctionMap(FILE *output)
{
std::string key = "SymbolMap";
if (fprintf(output, "\"%s\":{", key.c_str()) != -1) {
bool first = true;
for (const auto& [libId, funcMap] : functionMap_) {
for (const auto& [_, reportFuncMapItem] : funcMap) {
if (!reportFuncMapItem.hiddenFlag) {
OutputJsonPair(output, reportFuncMapItem.reportFuncId_, reportFuncMapItem, first);
first = false;
}
}
}
fprintf(output, "}");
}
}
void ReportJsonFile::ProcessSymbolsFiles(
const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
{
auto symbolsFileIt = symbolsFiles.begin();
while (symbolsFileIt != symbolsFiles.end()) {
size_t libId = libList_.size();
libList_.emplace_back(symbolsFileIt->get()->filePath_);
const auto &symbols = symbolsFileIt->get()->GetSymbols();
auto symbolIt = symbols.begin();
while (symbolIt != symbols.end()) {
AddNewFunction(libId, std::string(symbolIt->GetName()));
symbolIt++;
}
symbolsFileIt++;
}
}
void ReportJsonFile::SupplementSymbolsFiles(
const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
{
HLOGV("libsize old %zu", libList_.size());
for (size_t i = libList_.size(); i < symbolsFiles.size(); ++i) {
size_t libId = libList_.size();
libList_.emplace_back(symbolsFiles[i]->filePath_);
HLOGV("add new id success %zu, path:%s", libId, libList_[libId].data());
}
HLOGV("libsize new %zu", libList_.size());
}
void ReportJsonFile::UpdateCallNodeEventCount()
{
for (auto &config : reportConfigItems_) {
HLOGV("Config %s", config.second.eventName_.c_str());
for (auto &process : config.second.processes_) {
for (auto &thread : process.second.threads_) {
thread.second.callNode.UpdateChildrenEventCount();
thread.second.callNodeReverse.UpdateChildrenEventCount();
}
}
}
}
ReportConfigItem &ReportJsonFile::GetConfig(const uint64_t id)
{
for (auto &configpair : reportConfigItems_) {
if (find(configpair.first.begin(), configpair.first.end(), id) != configpair.first.end()) {
return configpair.second;
}
}
HLOGE("unable found config item for config id %" PRIu64 "", id);
return reportConfigItems_.begin()->second;
}
int ReportJsonFile::GetFunctionID(const int libId, const std::string &function)
{
auto functionMapIt = functionMap_.find(libId);
if (functionMapIt == functionMap_.end()) {
functionMapIt = functionMap_.try_emplace(libId).first;
}
auto funcMapIt = functionMapIt->second.find(function);
if (funcMapIt == functionMapIt->second.end()) {
HLOGW("'%s' not found in function list in lib %d", function.data(), libId);
AddNewFunction(libId, function);
return functionId_ - 1;
}
return funcMapIt->second.reportFuncId_;
}
void ReportJsonFile::HiddenFunctionInLib(const int libId, const std::string &function)
{
auto functionMapIt = functionMap_.find(libId);
if (functionMapIt == functionMap_.end()) {
return;
}
auto funcMapIt = functionMapIt->second.find(function);
if (funcMapIt == functionMapIt->second.end()) {
return;
}
funcMapIt->second.hiddenFlag = true;
HLOGV("hidden func'%s' in lib %d", function.c_str(), libId);
}
void ReportJsonFile::UpdateReportSample(const uint64_t id, const pid_t pid,
const pid_t tid, const uint64_t eventCount)
{
auto &config = GetConfig(id);
config.eventCount_ += eventCount;
auto &process = GetOrCreateMapItem(config.processes_, pid);
process.eventCount_ += eventCount;
auto &thread = GetOrCreateMapItem(process.threads_, tid);
thread.eventCount_ += eventCount;
thread.sampleCount_++;
sampleCount_++;
}
void ReportJsonFile::AddReportCallStack(const uint64_t eventCount, ReportCallNodeItem &callNode,
const std::vector<DfxFrame> &frames)
{
std::map<int, ReportCallNodeItem> *child = &callNode.childrenMap;
auto it = frames.begin();
while (it != frames.end()) {
int libId = GetLibID(it->mapName, it->originSoName, it->funcName);
if (libId >= 0) {
int funcId = GetFunctionID(libId, it->funcName);
ReportCallNodeItem &grandchildren = GetOrCreateMapItem(*child, funcId);
if (debug_) {
grandchildren.nodeIndex_ = nodeIndex_++;
grandchildren.funcName_ = it->funcName;
grandchildren.reverseCaller_ = true;
}
if (it + 1 == frames.end()) {
grandchildren.selfEventCount_ += eventCount;
}
child = &grandchildren.childrenMap;
HLOGV("add child %*s %d-%d %s @%d %s", static_cast<int>(it - frames.begin()), "", libId,
funcId, it->funcName.data(), grandchildren.nodeIndex_, it->mapName.data());
} else {
HLOGV("add child failed at %s", it->ToSymbolString().c_str());
}
it++;
}
}
void ReportJsonFile::AddReportCallStackReverse(const uint64_t eventCount, ReportCallNodeItem &callNode,
const std::vector<DfxFrame> &frames)
{
std::map<int, ReportCallNodeItem> *child = &callNode.childrenMap;
auto it = frames.rbegin();
while (it != frames.rend()) {
int libId = GetLibID(it->mapName, it->originSoName, it->funcName);
if (libId >= 0) {
int funcId = GetFunctionID(libId, it->funcName);
ReportCallNodeItem &grandchildren = GetOrCreateMapItem(*child, funcId);
if (debug_) {
grandchildren.nodeIndex_ = nodeIndex_++;
grandchildren.funcName_ = it->funcName;
}
if (it + 1 == frames.rend()) {
grandchildren.selfEventCount_ += eventCount;
}
child = &grandchildren.childrenMap;
HLOGV("add child %*s %d-%d %s @%d %s", static_cast<int>(it - frames.rbegin()), "",
libId, funcId, it->funcName.data(), grandchildren.nodeIndex_,
it->mapName.data());
} else {
HLOGV("add child failed at %s", it->ToSymbolString().c_str());
}
it++;
}
}
uint32_t ReportJsonFile::GetConfigIndex(const uint64_t id)
{
return GetConfig(id).index_;
}
std::string ReportJsonFile::GetConfigName(const uint64_t id)
{
auto &config = GetConfig(id);
return config.eventName_;
}
int ReportJsonFile::GetLibID(const std::string filepath, const std::string originSoName, const std::string funcName)
{
std::string newFilePath = filepath;
if (filepath.find("libadlt") != std::string::npos && EndsWith(filepath.data(), ".so")) {
HLOGW("extendName: %s", originSoName.c_str());
if (!originSoName.empty()) {
newFilePath = filepath + ":" + originSoName;
auto oldIt = std::find(libList_.begin(), libList_.end(), std::string_view(filepath));
if (oldIt != libList_.end()) {
int oldLibId = oldIt - libList_.begin();
HiddenFunctionInLib(oldLibId, funcName);
}
}
}
auto it = std::find(libList_.begin(), libList_.end(), std::string_view(newFilePath));
if (it != libList_.end()) {
return it - libList_.begin();
} else {
HLOGE("'%s' not found in lib list, liblist size: %zu", filepath.data(), libList_.size());
return -1;
}
}
void ReportJsonFile::UpdateReportCallStack(const uint64_t id, const pid_t pid, const pid_t tid,
uint64_t const eventCount, const std::vector<DfxFrame> &frames)
{
auto &config = GetConfig(id);
std::set<int> RepeatFunctionId;
CHECK_TRUE(frames.size() != 0, NO_RETVAL, 0, "");
auto &process = GetOrCreateMapItem(config.processes_, pid);
auto &thread = GetOrCreateMapItem(process.threads_, tid);
auto it = frames.begin();
bool jsFrame = StringEndsWith(it->mapName, "stub.an");
size_t skipFrame = 0;
while (it != frames.end()) {
int libId = GetLibID(it->mapName, it->originSoName, it->funcName);
if (libId < 0) {
HLOGW("not found lib path %s", it->mapName.data());
it++;
continue;
}
ReportLibItem &lib = thread.libs_[libId];
lib.libId_ = libId;
int funcId = GetFunctionID(libId, it->funcName);
HLOG_ASSERT(funcId >= 0);
if (RepeatFunctionId.count(funcId) != 0) {
it++;
continue;
} else {
RepeatFunctionId.emplace(funcId);
}
ReportFuncItem &func = GetOrCreateMapItem(lib.funcs_, funcId);
func.subTreeEventCount_ += eventCount;
if (jsFrame && frames.size() > 1) {
skipFrame = 1;
}
if (it == frames.begin() + skipFrame) {
func.eventCount_ += eventCount;
func.sampleCount_ += 1;
lib.eventCount_ += eventCount;
}
it++;
}
frames are the other way around
0 is the last called.
So the order of json callstack should be 0 at the end
callNode is Reverse Order of frames
callNodeReverse is Normal Order frames
*/
AddReportCallStackReverse(eventCount, thread.callNode, frames);
AddReportCallStack(eventCount, thread.callNodeReverse, frames);
}
void ReportJsonFile::OutputJsonFeatureString()
{
OutputJsonPair(output_, "deviceTime",
recordFileReader_->GetFeatureString(FEATURE::HIPERF_RECORD_TIME), true);
std::string device = recordFileReader_->GetFeatureString(FEATURE::HOSTNAME);
device.append(" " + recordFileReader_->GetFeatureString(FEATURE::OSRELEASE));
device.append(" " + recordFileReader_->GetFeatureString(FEATURE::ARCH));
OutputJsonPair(output_, "deviceType", device);
OutputJsonPair(output_, "osVersion", recordFileReader_->GetFeatureString(FEATURE::OSRELEASE));
OutputJsonPair(output_, "deviceCommandLine",
recordFileReader_->GetFeatureString(FEATURE::CMDLINE));
OutputJsonPair(output_, "totalRecordSamples", sampleCount_);
}
void ReportJsonFile::OutputJsonRuntimeInfo()
{
const auto &threadMaps = virtualRuntime_.GetThreads();
std::map<std::string, std::string> jsonProcesses;
std::map<std::string, std::string> jsonThreads;
for (const auto &pair : threadMaps) {
const VirtualThread &thread = pair.second;
if (thread.pid_ == thread.tid_) {
jsonProcesses.emplace(std::to_string(thread.pid_), thread.name_);
}
jsonThreads.emplace(std::to_string(thread.tid_), thread.name_);
}
OutputJsonMap(output_, "processNameMap", jsonProcesses);
OutputJsonMap(output_, "threadNameMap", jsonThreads);
const auto &symbolsFiles = virtualRuntime_.GetSymbolsFiles();
jsonStringVector jsonFilePaths;
for (const auto &symbolsFile : symbolsFiles) {
std::string filePath = StringReplace(symbolsFile->filePath_, "\"", "");
jsonFilePaths.emplace_back(filePath);
}
OutputJsonVectorList(output_, "symbolsFileList", jsonFilePaths);
if (fprintf(output_, ",") < 0) {
return;
}
OutputJsonFunctionMap(output_);
if (fprintf(output_, ",") < 0) {
return;
}
OutputJsonMapList(output_, "recordSampleInfo", reportConfigItems_, true);
}
bool ReportJsonFile::OutputJson(FILE *output)
{
CHECK_TRUE(output != nullptr, false, 0, "");
output_ = output;
if (fprintf(output, "{") < 0) {
return false;
}
OutputJsonFeatureString();
OutputJsonRuntimeInfo();
if (fprintf(output, "}") < 0) {
return false;
}
return true;
}
}
}
}