148f512ceSopenharmony_ci/*
248f512ceSopenharmony_ci * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
348f512ceSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
448f512ceSopenharmony_ci * you may not use this file except in compliance with the License.
548f512ceSopenharmony_ci * You may obtain a copy of the License at
648f512ceSopenharmony_ci *
748f512ceSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
848f512ceSopenharmony_ci *
948f512ceSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1048f512ceSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1148f512ceSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1248f512ceSopenharmony_ci * See the License for the specific language governing permissions and
1348f512ceSopenharmony_ci * limitations under the License.
1448f512ceSopenharmony_ci */
1548f512ceSopenharmony_ci#define HILOG_TAG "Report"
1648f512ceSopenharmony_ci
1748f512ceSopenharmony_ci#include <set>
1848f512ceSopenharmony_ci#include "report_json_file.h"
1948f512ceSopenharmony_ci
2048f512ceSopenharmony_ci#if defined(is_mingw) && is_mingw
2148f512ceSopenharmony_ci#include <windows.h>
2248f512ceSopenharmony_ci#else
2348f512ceSopenharmony_ci#include <sys/ioctl.h>
2448f512ceSopenharmony_ci#endif
2548f512ceSopenharmony_ci
2648f512ceSopenharmony_ci#include "hiperf_hilog.h"
2748f512ceSopenharmony_ci
2848f512ceSopenharmony_cinamespace OHOS {
2948f512ceSopenharmony_cinamespace Developtools {
3048f512ceSopenharmony_cinamespace HiPerf {
3148f512ceSopenharmony_cibool ReportJsonFile::debug_ = false;
3248f512ceSopenharmony_ci
3348f512ceSopenharmony_civoid ReportJsonFile::AddNewFunction(int libId, std::string name)
3448f512ceSopenharmony_ci{
3548f512ceSopenharmony_ci    auto it = functionMap_.find(libId);
3648f512ceSopenharmony_ci    if (it == functionMap_.end()) {
3748f512ceSopenharmony_ci        it = functionMap_.try_emplace(libId).first;
3848f512ceSopenharmony_ci    }
3948f512ceSopenharmony_ci    it->second.insert_or_assign(name, ReportFuncMapItem(libId, name, functionId_++));
4048f512ceSopenharmony_ci}
4148f512ceSopenharmony_ci
4248f512ceSopenharmony_civoid ReportJsonFile::OutputJsonFunctionMap(FILE *output)
4348f512ceSopenharmony_ci{
4448f512ceSopenharmony_ci    std::string key = "SymbolMap";
4548f512ceSopenharmony_ci    if (fprintf(output, "\"%s\":{", key.c_str()) != -1) {
4648f512ceSopenharmony_ci        bool first = true;
4748f512ceSopenharmony_ci
4848f512ceSopenharmony_ci        for (const auto& [libId, funcMap] : functionMap_) {
4948f512ceSopenharmony_ci            for (const auto& [_, reportFuncMapItem] : funcMap) {
5048f512ceSopenharmony_ci                OutputJsonPair(output, reportFuncMapItem.reportFuncId_, reportFuncMapItem, first);
5148f512ceSopenharmony_ci                first = false;
5248f512ceSopenharmony_ci            }
5348f512ceSopenharmony_ci        }
5448f512ceSopenharmony_ci
5548f512ceSopenharmony_ci        fprintf(output, "}");
5648f512ceSopenharmony_ci    }
5748f512ceSopenharmony_ci}
5848f512ceSopenharmony_ci
5948f512ceSopenharmony_civoid ReportJsonFile::ProcessSymbolsFiles(
6048f512ceSopenharmony_ci    const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
6148f512ceSopenharmony_ci{
6248f512ceSopenharmony_ci    auto symbolsFileIt = symbolsFiles.begin();
6348f512ceSopenharmony_ci    while (symbolsFileIt != symbolsFiles.end()) {
6448f512ceSopenharmony_ci        size_t libId = libList_.size();
6548f512ceSopenharmony_ci        libList_.emplace_back(symbolsFileIt->get()->filePath_);
6648f512ceSopenharmony_ci        const auto &symbols = symbolsFileIt->get()->GetSymbols();
6748f512ceSopenharmony_ci        auto symbolIt = symbols.begin();
6848f512ceSopenharmony_ci        while (symbolIt != symbols.end()) {
6948f512ceSopenharmony_ci            AddNewFunction(libId, std::string(symbolIt->GetName()));
7048f512ceSopenharmony_ci            symbolIt++;
7148f512ceSopenharmony_ci        }
7248f512ceSopenharmony_ci        symbolsFileIt++;
7348f512ceSopenharmony_ci    }
7448f512ceSopenharmony_ci}
7548f512ceSopenharmony_ci
7648f512ceSopenharmony_civoid ReportJsonFile::UpdateCallNodeEventCount()
7748f512ceSopenharmony_ci{
7848f512ceSopenharmony_ci    for (auto &config : reportConfigItems_) {
7948f512ceSopenharmony_ci        HLOGV("Config %s", config.second.eventName_.c_str());
8048f512ceSopenharmony_ci        for (auto &process : config.second.processes_) {
8148f512ceSopenharmony_ci            for (auto &thread : process.second.threads_) {
8248f512ceSopenharmony_ci                thread.second.callNode.UpdateChildrenEventCount();
8348f512ceSopenharmony_ci                thread.second.callNodeReverse.UpdateChildrenEventCount();
8448f512ceSopenharmony_ci            }
8548f512ceSopenharmony_ci        }
8648f512ceSopenharmony_ci    }
8748f512ceSopenharmony_ci}
8848f512ceSopenharmony_ci
8948f512ceSopenharmony_ciReportConfigItem &ReportJsonFile::GetConfig(uint64_t id)
9048f512ceSopenharmony_ci{
9148f512ceSopenharmony_ci    for (auto &configpair : reportConfigItems_) {
9248f512ceSopenharmony_ci        if (find(configpair.first.begin(), configpair.first.end(), id) != configpair.first.end()) {
9348f512ceSopenharmony_ci            return configpair.second;
9448f512ceSopenharmony_ci        }
9548f512ceSopenharmony_ci    }
9648f512ceSopenharmony_ci    HLOGE("unable found config item for config id %" PRIu64 "", id);
9748f512ceSopenharmony_ci    // return default one
9848f512ceSopenharmony_ci    return reportConfigItems_.begin()->second;
9948f512ceSopenharmony_ci}
10048f512ceSopenharmony_ci
10148f512ceSopenharmony_ciint ReportJsonFile::GetFunctionID(int libId, const std::string &function)
10248f512ceSopenharmony_ci{
10348f512ceSopenharmony_ci    auto functionMapIt = functionMap_.find(libId);
10448f512ceSopenharmony_ci    if (functionMapIt == functionMap_.end()) {
10548f512ceSopenharmony_ci        functionMapIt = functionMap_.try_emplace(libId).first;
10648f512ceSopenharmony_ci    }
10748f512ceSopenharmony_ci    auto funcMapIt = functionMapIt->second.find(function);
10848f512ceSopenharmony_ci    if (funcMapIt == functionMapIt->second.end()) {
10948f512ceSopenharmony_ci        HLOGW("'%s' not found in function list in lib %d", function.data(), libId);
11048f512ceSopenharmony_ci        // make a new function for unknown name
11148f512ceSopenharmony_ci        AddNewFunction(libId, function);
11248f512ceSopenharmony_ci        // return the last index
11348f512ceSopenharmony_ci        return functionId_ - 1;
11448f512ceSopenharmony_ci    }
11548f512ceSopenharmony_ci    return funcMapIt->second.reportFuncId_;
11648f512ceSopenharmony_ci}
11748f512ceSopenharmony_ci
11848f512ceSopenharmony_civoid ReportJsonFile::UpdateReportSample(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount)
11948f512ceSopenharmony_ci{
12048f512ceSopenharmony_ci    auto &config = GetConfig(id);
12148f512ceSopenharmony_ci
12248f512ceSopenharmony_ci    config.eventCount_ += eventCount;
12348f512ceSopenharmony_ci    auto &process = GetOrCreateMapItem(config.processes_, pid);
12448f512ceSopenharmony_ci    process.eventCount_ += eventCount;
12548f512ceSopenharmony_ci    auto &thread = GetOrCreateMapItem(process.threads_, tid);
12648f512ceSopenharmony_ci    thread.eventCount_ += eventCount;
12748f512ceSopenharmony_ci    thread.sampleCount_++;
12848f512ceSopenharmony_ci    sampleCount_++;
12948f512ceSopenharmony_ci}
13048f512ceSopenharmony_ci
13148f512ceSopenharmony_civoid ReportJsonFile::AddReportCallStack(uint64_t eventCount, ReportCallNodeItem &callNode,
13248f512ceSopenharmony_ci                                        const std::vector<DfxFrame> &frames)
13348f512ceSopenharmony_ci{
13448f512ceSopenharmony_ci    std::map<int, ReportCallNodeItem> *child = &callNode.childrenMap;
13548f512ceSopenharmony_ci    auto it = frames.begin();
13648f512ceSopenharmony_ci    while (it != frames.end()) {
13748f512ceSopenharmony_ci        int libId = GetLibID(it->mapName);
13848f512ceSopenharmony_ci        if (libId >= 0) {
13948f512ceSopenharmony_ci            int funcId = GetFunctionID(libId, it->funcName);
14048f512ceSopenharmony_ci            // new children funid
14148f512ceSopenharmony_ci            ReportCallNodeItem &grandchildren = GetOrCreateMapItem(*child, funcId);
14248f512ceSopenharmony_ci            if (debug_) {
14348f512ceSopenharmony_ci                grandchildren.nodeIndex_ = nodeIndex_++;
14448f512ceSopenharmony_ci                grandchildren.funcName_ = it->funcName;
14548f512ceSopenharmony_ci                grandchildren.reverseCaller_ = true;
14648f512ceSopenharmony_ci            }
14748f512ceSopenharmony_ci            // only the last one need count
14848f512ceSopenharmony_ci            if (it + 1 == frames.end()) {
14948f512ceSopenharmony_ci                grandchildren.selfEventCount_ += eventCount;
15048f512ceSopenharmony_ci            }
15148f512ceSopenharmony_ci            // new children's children
15248f512ceSopenharmony_ci            child = &grandchildren.childrenMap;
15348f512ceSopenharmony_ci
15448f512ceSopenharmony_ci            HLOGV("add child %*s %d-%d %s @%d %s", static_cast<int>(it - frames.begin()), "", libId,
15548f512ceSopenharmony_ci                  funcId, it->funcName.data(), grandchildren.nodeIndex_, it->mapName.data());
15648f512ceSopenharmony_ci        } else {
15748f512ceSopenharmony_ci            HLOGV("add child failed at %s", it->ToSymbolString().c_str());
15848f512ceSopenharmony_ci        }
15948f512ceSopenharmony_ci        it++;
16048f512ceSopenharmony_ci    }
16148f512ceSopenharmony_ci}
16248f512ceSopenharmony_ci
16348f512ceSopenharmony_civoid ReportJsonFile::AddReportCallStackReverse(uint64_t eventCount, ReportCallNodeItem &callNode,
16448f512ceSopenharmony_ci                                               const std::vector<DfxFrame> &frames)
16548f512ceSopenharmony_ci{
16648f512ceSopenharmony_ci    std::map<int, ReportCallNodeItem> *child = &callNode.childrenMap;
16748f512ceSopenharmony_ci    auto it = frames.rbegin();
16848f512ceSopenharmony_ci    while (it != frames.rend()) {
16948f512ceSopenharmony_ci        int libId = GetLibID(it->mapName);
17048f512ceSopenharmony_ci        if (libId >= 0) {
17148f512ceSopenharmony_ci            int funcId = GetFunctionID(libId, it->funcName);
17248f512ceSopenharmony_ci            // new children funid
17348f512ceSopenharmony_ci            ReportCallNodeItem &grandchildren = GetOrCreateMapItem(*child, funcId);
17448f512ceSopenharmony_ci            if (debug_) {
17548f512ceSopenharmony_ci                grandchildren.nodeIndex_ = nodeIndex_++;
17648f512ceSopenharmony_ci                grandchildren.funcName_ = it->funcName;
17748f512ceSopenharmony_ci            }
17848f512ceSopenharmony_ci            // only the last one need count
17948f512ceSopenharmony_ci            if (it + 1 == frames.rend()) {
18048f512ceSopenharmony_ci                grandchildren.selfEventCount_ += eventCount;
18148f512ceSopenharmony_ci            }
18248f512ceSopenharmony_ci            // new children's children
18348f512ceSopenharmony_ci            child = &grandchildren.childrenMap;
18448f512ceSopenharmony_ci
18548f512ceSopenharmony_ci            HLOGV("add child %*s %d-%d %s @%d %s", static_cast<int>(it - frames.rbegin()), "",
18648f512ceSopenharmony_ci                  libId, funcId, it->funcName.data(), grandchildren.nodeIndex_,
18748f512ceSopenharmony_ci                  it->mapName.data());
18848f512ceSopenharmony_ci        } else {
18948f512ceSopenharmony_ci            HLOGV("add child failed at %s", it->ToSymbolString().c_str());
19048f512ceSopenharmony_ci        }
19148f512ceSopenharmony_ci        it++;
19248f512ceSopenharmony_ci    }
19348f512ceSopenharmony_ci}
19448f512ceSopenharmony_ci
19548f512ceSopenharmony_ciuint32_t ReportJsonFile::GetConfigIndex(uint64_t id)
19648f512ceSopenharmony_ci{
19748f512ceSopenharmony_ci    return GetConfig(id).index_;
19848f512ceSopenharmony_ci}
19948f512ceSopenharmony_ci
20048f512ceSopenharmony_cistd::string ReportJsonFile::GetConfigName(uint64_t id)
20148f512ceSopenharmony_ci{
20248f512ceSopenharmony_ci    auto &config = GetConfig(id);
20348f512ceSopenharmony_ci    return config.eventName_;
20448f512ceSopenharmony_ci}
20548f512ceSopenharmony_ci
20648f512ceSopenharmony_ciint ReportJsonFile::GetLibID(std::string_view filepath)
20748f512ceSopenharmony_ci{
20848f512ceSopenharmony_ci    auto it = find(libList_.begin(), libList_.end(), filepath);
20948f512ceSopenharmony_ci    if (it != libList_.end()) {
21048f512ceSopenharmony_ci        return it - libList_.begin();
21148f512ceSopenharmony_ci    } else {
21248f512ceSopenharmony_ci        HLOGE("'%s' not found in lib list", filepath.data());
21348f512ceSopenharmony_ci        return -1;
21448f512ceSopenharmony_ci    }
21548f512ceSopenharmony_ci}
21648f512ceSopenharmony_ci
21748f512ceSopenharmony_civoid ReportJsonFile::UpdateReportCallStack(uint64_t id, pid_t pid, pid_t tid, uint64_t eventCount,
21848f512ceSopenharmony_ci                                           std::vector<DfxFrame> &frames)
21948f512ceSopenharmony_ci{
22048f512ceSopenharmony_ci    auto &config = GetConfig(id);
22148f512ceSopenharmony_ci    std::set<int> RepeatFunctionId;
22248f512ceSopenharmony_ci    CHECK_TRUE(frames.size() == 0, NO_RETVAL, 0, ""); // do nothing with no frame
22348f512ceSopenharmony_ci    auto &process = GetOrCreateMapItem(config.processes_, pid);
22448f512ceSopenharmony_ci    auto &thread = GetOrCreateMapItem(process.threads_, tid);
22548f512ceSopenharmony_ci    auto it = frames.begin();
22648f512ceSopenharmony_ci    bool jsFrame = StringEndsWith(it->mapName, "stub.an");
22748f512ceSopenharmony_ci    size_t skipFrame = 0;
22848f512ceSopenharmony_ci    while (it != frames.end()) {
22948f512ceSopenharmony_ci        int libId = GetLibID(it->mapName);
23048f512ceSopenharmony_ci        if (libId < 0) {
23148f512ceSopenharmony_ci            HLOGW("not found lib path %s", it->mapName.data());
23248f512ceSopenharmony_ci            it++;
23348f512ceSopenharmony_ci            continue;
23448f512ceSopenharmony_ci        }
23548f512ceSopenharmony_ci        ReportLibItem &lib = thread.libs_[libId];
23648f512ceSopenharmony_ci        lib.libId_ = libId;
23748f512ceSopenharmony_ci        int funcId = GetFunctionID(libId, it->funcName);
23848f512ceSopenharmony_ci        // we will always have a funId, it will create a new one if not found
23948f512ceSopenharmony_ci        // so that we can see abc+0x123 in the html
24048f512ceSopenharmony_ci        HLOG_ASSERT(funcId >= 0);
24148f512ceSopenharmony_ci        if (RepeatFunctionId.count(funcId) != 0) {
24248f512ceSopenharmony_ci            it++;
24348f512ceSopenharmony_ci            continue;
24448f512ceSopenharmony_ci        } else {
24548f512ceSopenharmony_ci            RepeatFunctionId.emplace(funcId);
24648f512ceSopenharmony_ci        }
24748f512ceSopenharmony_ci
24848f512ceSopenharmony_ci        ReportFuncItem &func = GetOrCreateMapItem(lib.funcs_, funcId);
24948f512ceSopenharmony_ci
25048f512ceSopenharmony_ci        // always count subtree
25148f512ceSopenharmony_ci        func.subTreeEventCount_ += eventCount;
25248f512ceSopenharmony_ci
25348f512ceSopenharmony_ci        // only calc the first frame event count
25448f512ceSopenharmony_ci        if (jsFrame && frames.size() > 1) {
25548f512ceSopenharmony_ci            skipFrame = 1; // 1 : for arkjs frame,skip the stub.an frame
25648f512ceSopenharmony_ci        }
25748f512ceSopenharmony_ci        if (it == frames.begin() + skipFrame) {
25848f512ceSopenharmony_ci            func.eventCount_ += eventCount;
25948f512ceSopenharmony_ci            func.sampleCount_ += 1;
26048f512ceSopenharmony_ci            lib.eventCount_ += eventCount;
26148f512ceSopenharmony_ci        }
26248f512ceSopenharmony_ci        // go on next frame
26348f512ceSopenharmony_ci        it++;
26448f512ceSopenharmony_ci    }
26548f512ceSopenharmony_ci    /*
26648f512ceSopenharmony_ci        frames are the other way around
26748f512ceSopenharmony_ci        0 is the last called.
26848f512ceSopenharmony_ci        So the order of json callstack should be 0 at the end
26948f512ceSopenharmony_ci        callNode is Reverse Order of frames
27048f512ceSopenharmony_ci        callNodeReverse is Normal Order frames
27148f512ceSopenharmony_ci    */
27248f512ceSopenharmony_ci    AddReportCallStackReverse(eventCount, thread.callNode, frames);
27348f512ceSopenharmony_ci    AddReportCallStack(eventCount, thread.callNodeReverse, frames);
27448f512ceSopenharmony_ci}
27548f512ceSopenharmony_ci
27648f512ceSopenharmony_civoid ReportJsonFile::OutputJsonFeatureString()
27748f512ceSopenharmony_ci{
27848f512ceSopenharmony_ci    OutputJsonPair(output_, "deviceTime",
27948f512ceSopenharmony_ci                   recordFileReader_->GetFeatureString(FEATURE::HIPERF_RECORD_TIME), true);
28048f512ceSopenharmony_ci    std::string device = recordFileReader_->GetFeatureString(FEATURE::HOSTNAME);
28148f512ceSopenharmony_ci    device.append(" " + recordFileReader_->GetFeatureString(FEATURE::OSRELEASE));
28248f512ceSopenharmony_ci    device.append(" " + recordFileReader_->GetFeatureString(FEATURE::ARCH));
28348f512ceSopenharmony_ci
28448f512ceSopenharmony_ci    OutputJsonPair(output_, "deviceType", device);
28548f512ceSopenharmony_ci
28648f512ceSopenharmony_ci    OutputJsonPair(output_, "osVersion", recordFileReader_->GetFeatureString(FEATURE::OSRELEASE));
28748f512ceSopenharmony_ci
28848f512ceSopenharmony_ci    OutputJsonPair(output_, "deviceCommandLine",
28948f512ceSopenharmony_ci                   recordFileReader_->GetFeatureString(FEATURE::CMDLINE));
29048f512ceSopenharmony_ci
29148f512ceSopenharmony_ci    OutputJsonPair(output_, "totalRecordSamples", sampleCount_);
29248f512ceSopenharmony_ci}
29348f512ceSopenharmony_ci
29448f512ceSopenharmony_civoid ReportJsonFile::OutputJsonRuntimeInfo()
29548f512ceSopenharmony_ci{
29648f512ceSopenharmony_ci    const auto &threadMaps = virtualRuntime_.GetThreads();
29748f512ceSopenharmony_ci    std::map<std::string, std::string> jsonProcesses;
29848f512ceSopenharmony_ci    std::map<std::string, std::string> jsonThreads;
29948f512ceSopenharmony_ci    for (const auto &pair : threadMaps) {
30048f512ceSopenharmony_ci        const VirtualThread &thread = pair.second;
30148f512ceSopenharmony_ci        if (thread.pid_ == thread.tid_) {
30248f512ceSopenharmony_ci            jsonProcesses.emplace(std::to_string(thread.pid_), thread.name_);
30348f512ceSopenharmony_ci        }
30448f512ceSopenharmony_ci        // process also is a thread.
30548f512ceSopenharmony_ci        jsonThreads.emplace(std::to_string(thread.tid_), thread.name_);
30648f512ceSopenharmony_ci    }
30748f512ceSopenharmony_ci
30848f512ceSopenharmony_ci    OutputJsonMap(output_, "processNameMap", jsonProcesses);
30948f512ceSopenharmony_ci
31048f512ceSopenharmony_ci    OutputJsonMap(output_, "threadNameMap", jsonThreads);
31148f512ceSopenharmony_ci
31248f512ceSopenharmony_ci    const auto &symbolsFiles = virtualRuntime_.GetSymbolsFiles();
31348f512ceSopenharmony_ci    jsonStringVector jsonFilePaths;
31448f512ceSopenharmony_ci    for (const auto &symbolsFile : symbolsFiles) {
31548f512ceSopenharmony_ci        jsonFilePaths.emplace_back(symbolsFile->filePath_);
31648f512ceSopenharmony_ci    }
31748f512ceSopenharmony_ci
31848f512ceSopenharmony_ci    OutputJsonVectorList(output_, "symbolsFileList", jsonFilePaths);
31948f512ceSopenharmony_ci    if (fprintf(output_, ",") < 0) {
32048f512ceSopenharmony_ci        return;
32148f512ceSopenharmony_ci    }
32248f512ceSopenharmony_ci
32348f512ceSopenharmony_ci    OutputJsonFunctionMap(output_);
32448f512ceSopenharmony_ci    if (fprintf(output_, ",") < 0) {
32548f512ceSopenharmony_ci        return;
32648f512ceSopenharmony_ci    }
32748f512ceSopenharmony_ci
32848f512ceSopenharmony_ci    OutputJsonMapList(output_, "recordSampleInfo", reportConfigItems_, true);
32948f512ceSopenharmony_ci}
33048f512ceSopenharmony_ci
33148f512ceSopenharmony_cibool ReportJsonFile::OutputJson(FILE *output)
33248f512ceSopenharmony_ci{
33348f512ceSopenharmony_ci    CHECK_TRUE(output == nullptr, false, 0, "");
33448f512ceSopenharmony_ci    output_ = output;
33548f512ceSopenharmony_ci    if (fprintf(output, "{") < 0) {
33648f512ceSopenharmony_ci        return false;
33748f512ceSopenharmony_ci    }
33848f512ceSopenharmony_ci    OutputJsonFeatureString();
33948f512ceSopenharmony_ci    OutputJsonRuntimeInfo();
34048f512ceSopenharmony_ci
34148f512ceSopenharmony_ci    if (fprintf(output, "}") < 0) {
34248f512ceSopenharmony_ci        return false;
34348f512ceSopenharmony_ci    }
34448f512ceSopenharmony_ci    return true;
34548f512ceSopenharmony_ci}
34648f512ceSopenharmony_ci} // namespace HiPerf
34748f512ceSopenharmony_ci} // namespace Developtools
34848f512ceSopenharmony_ci} // namespace OHOS
349