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 "report.h"
1848f512ceSopenharmony_ci
1948f512ceSopenharmony_ci#include <memory>
2048f512ceSopenharmony_ci#include <set>
2148f512ceSopenharmony_ci#include <sstream>
2248f512ceSopenharmony_ci
2348f512ceSopenharmony_ci#if defined(is_mingw) && is_mingw
2448f512ceSopenharmony_ci#include <windows.h>
2548f512ceSopenharmony_ci#else
2648f512ceSopenharmony_ci#include <sys/ioctl.h>
2748f512ceSopenharmony_ci#endif
2848f512ceSopenharmony_ci
2948f512ceSopenharmony_ci#include "hiperf_hilog.h"
3048f512ceSopenharmony_ci
3148f512ceSopenharmony_ciusing namespace std::placeholders;
3248f512ceSopenharmony_cinamespace OHOS {
3348f512ceSopenharmony_cinamespace Developtools {
3448f512ceSopenharmony_cinamespace HiPerf {
3548f512ceSopenharmony_ciunsigned long long ReportItem::allIndex_ = 0;
3648f512ceSopenharmony_civoid Report::AddReportItem(const PerfRecordSample &sample, bool includeCallStack)
3748f512ceSopenharmony_ci{
3848f512ceSopenharmony_ci    size_t configIndex = GetConfigIndex(sample.data_.id);
3948f512ceSopenharmony_ci    HLOG_ASSERT_MESSAGE(configs_.size() > configIndex,
4048f512ceSopenharmony_ci                        "in %zu configs found index %zu, from ids %llu", configs_.size(),
4148f512ceSopenharmony_ci                        configIndex, sample.data_.id);
4248f512ceSopenharmony_ci    VirtualThread &thread = virtualRuntime_.GetThread(sample.data_.pid, sample.data_.tid);
4348f512ceSopenharmony_ci    HLOG_ASSERT(sample.callFrames_.size() > 0);
4448f512ceSopenharmony_ci    if (sample.callFrames_.size() > 0) {
4548f512ceSopenharmony_ci        // if we need callstack ?
4648f512ceSopenharmony_ci        if (includeCallStack) {
4748f512ceSopenharmony_ci            // we will use caller mode , from last to first
4848f512ceSopenharmony_ci            auto frameIt = sample.callFrames_.rbegin();
4948f512ceSopenharmony_ci            ReportItem &item = configs_[configIndex].reportItems_.emplace_back(
5048f512ceSopenharmony_ci                sample.data_.pid, sample.data_.tid, thread.name_, frameIt->mapName,
5148f512ceSopenharmony_ci                frameIt->funcName, frameIt->funcOffset, sample.data_.period);
5248f512ceSopenharmony_ci            HLOGD("ReportItem: %s", item.ToDebugString().c_str());
5348f512ceSopenharmony_ci            HLOG_ASSERT(!item.func_.empty());
5448f512ceSopenharmony_ci
5548f512ceSopenharmony_ci            std::vector<ReportItemCallFrame> *currentCallFrames = &item.callStacks_;
5648f512ceSopenharmony_ci            for (frameIt = sample.callFrames_.rbegin(); frameIt != sample.callFrames_.rend();
5748f512ceSopenharmony_ci                 frameIt++) {
5848f512ceSopenharmony_ci                HLOG_ASSERT(frameIt->pc < PERF_CONTEXT_MAX);
5948f512ceSopenharmony_ci                // in add items case , right one should only have 1 callstack
6048f512ceSopenharmony_ci                // so just new callfames and move to next level
6148f512ceSopenharmony_ci                ReportItemCallFrame &nextCallFrame = currentCallFrames->emplace_back(
6248f512ceSopenharmony_ci                    frameIt->funcName, frameIt->funcOffset, frameIt->mapName,
6348f512ceSopenharmony_ci                    sample.data_.period,
6448f512ceSopenharmony_ci                    (std::next(frameIt) == sample.callFrames_.rend()) ? sample.data_.period : 0);
6548f512ceSopenharmony_ci                HLOGV("add callframe %s", nextCallFrame.ToDebugString().c_str());
6648f512ceSopenharmony_ci                currentCallFrames = &nextCallFrame.childs;
6748f512ceSopenharmony_ci            }
6848f512ceSopenharmony_ci            HLOGV("callstack %zu", item.callStacks_.size());
6948f512ceSopenharmony_ci            if (item.callStacks_.size() > 0) {
7048f512ceSopenharmony_ci                HLOGV("callstack 2nd level %zu", item.callStacks_[0].childs.size());
7148f512ceSopenharmony_ci            }
7248f512ceSopenharmony_ci        } else {
7348f512ceSopenharmony_ci            auto frameIt = sample.callFrames_.begin();
7448f512ceSopenharmony_ci            if (frameIt != sample.callFrames_.end()) {
7548f512ceSopenharmony_ci                HLOG_ASSERT(frameIt->pc < PERF_CONTEXT_MAX);
7648f512ceSopenharmony_ci                // for arkjs frame, skip the stub.an frame
7748f512ceSopenharmony_ci                if (StringEndsWith(frameIt->mapName, "stub.an") && sample.callFrames_.size() > 1) {
7848f512ceSopenharmony_ci                    HLOGV("stub.an frame, go to next, mapname %s", frameIt->mapName.c_str());
7948f512ceSopenharmony_ci                    frameIt++;
8048f512ceSopenharmony_ci                }
8148f512ceSopenharmony_ci                ReportItem &item = configs_[configIndex].reportItems_.emplace_back(
8248f512ceSopenharmony_ci                    sample.data_.pid, sample.data_.tid, thread.name_, frameIt->mapName,
8348f512ceSopenharmony_ci                    frameIt->funcName, frameIt->funcOffset, sample.data_.period);
8448f512ceSopenharmony_ci                HLOGV("%s", item.ToDebugString().c_str());
8548f512ceSopenharmony_ci                HLOG_ASSERT(!item.func_.empty());
8648f512ceSopenharmony_ci            }
8748f512ceSopenharmony_ci        }
8848f512ceSopenharmony_ci    }
8948f512ceSopenharmony_ci    configs_[configIndex].sampleCount_++;
9048f512ceSopenharmony_ci    configs_[configIndex].eventCount_ += sample.data_.period;
9148f512ceSopenharmony_ci}
9248f512ceSopenharmony_ci
9348f512ceSopenharmony_civoid Report::AddReportItemBranch(const PerfRecordSample &sample)
9448f512ceSopenharmony_ci{
9548f512ceSopenharmony_ci    size_t configIndex = GetConfigIndex(sample.data_.id);
9648f512ceSopenharmony_ci    HLOG_ASSERT(configs_.size() > configIndex);
9748f512ceSopenharmony_ci    VirtualThread &thread = virtualRuntime_.GetThread(sample.data_.pid, sample.data_.tid);
9848f512ceSopenharmony_ci    for (u64 i = 0; i < sample.data_.bnr; i++) {
9948f512ceSopenharmony_ci        DfxSymbol symbolTo =
10048f512ceSopenharmony_ci            virtualRuntime_.GetSymbol(sample.data_.lbr[i].to, sample.data_.pid, sample.data_.tid);
10148f512ceSopenharmony_ci        DfxSymbol symbolFrom =
10248f512ceSopenharmony_ci            virtualRuntime_.GetSymbol(sample.data_.lbr[i].from, sample.data_.pid, sample.data_.tid);
10348f512ceSopenharmony_ci
10448f512ceSopenharmony_ci        // branch only have 1 time only for period
10548f512ceSopenharmony_ci        ReportItem &item = configs_[configIndex].reportItems_.emplace_back(
10648f512ceSopenharmony_ci            sample.data_.pid, sample.data_.tid, thread.name_, symbolTo.module_, symbolTo.GetName(),
10748f512ceSopenharmony_ci            symbolTo.funcVaddr_, 1u);
10848f512ceSopenharmony_ci
10948f512ceSopenharmony_ci        item.fromDso_ = symbolFrom.module_;
11048f512ceSopenharmony_ci        item.fromFunc_ = symbolFrom.GetName();
11148f512ceSopenharmony_ci
11248f512ceSopenharmony_ci        HLOGV("%s 0x%" PRIx64 "", item.ToDebugString().c_str(), symbolTo.taskVaddr_);
11348f512ceSopenharmony_ci    }
11448f512ceSopenharmony_ci    configs_[configIndex].sampleCount_++;
11548f512ceSopenharmony_ci    configs_[configIndex].eventCount_ += sample.data_.bnr;
11648f512ceSopenharmony_ci}
11748f512ceSopenharmony_ci
11848f512ceSopenharmony_civoid Report::StatisticsRecords()
11948f512ceSopenharmony_ci{
12048f512ceSopenharmony_ci    for (auto &config : configs_) {
12148f512ceSopenharmony_ci        size_t duplicates = 0;
12248f512ceSopenharmony_ci        size_t totalReportCount = config.reportItems_.size();
12348f512ceSopenharmony_ci        // merge duplicate
12448f512ceSopenharmony_ci        HLOGD("uniquing %zu", totalReportCount);
12548f512ceSopenharmony_ci        auto last = std::unique(config.reportItems_.begin(), config.reportItems_.end(),
12648f512ceSopenharmony_ci            [this] (ReportItem &l, ReportItem &r) -> bool {
12748f512ceSopenharmony_ci                return this->MultiLevelSameAndUpdateCount(l, r);
12848f512ceSopenharmony_ci            });
12948f512ceSopenharmony_ci
13048f512ceSopenharmony_ci        config.reportItems_.erase(last, config.reportItems_.end());
13148f512ceSopenharmony_ci
13248f512ceSopenharmony_ci        duplicates = totalReportCount - config.reportItems_.size();
13348f512ceSopenharmony_ci        HLOGD("duplicates %zu, %zu -> %zu", duplicates, totalReportCount,
13448f512ceSopenharmony_ci              config.reportItems_.size());
13548f512ceSopenharmony_ci    }
13648f512ceSopenharmony_ci}
13748f512ceSopenharmony_ci
13848f512ceSopenharmony_civoid Report::FilterDisplayRecords()
13948f512ceSopenharmony_ci{
14048f512ceSopenharmony_ci    // remove the item with not in fliter
14148f512ceSopenharmony_ci    for (auto &config : configs_) {
14248f512ceSopenharmony_ci        size_t filterOuts = 0;
14348f512ceSopenharmony_ci        size_t totalReportCount = config.reportItems_.size();
14448f512ceSopenharmony_ci        for (const auto &reportKeyPair : reportKeyMap_) {
14548f512ceSopenharmony_ci            auto reportKey = reportKeyPair.second;
14648f512ceSopenharmony_ci            if (reportKey.displayFilter_.size() != 0) {
14748f512ceSopenharmony_ci                auto itemIt = config.reportItems_.begin();
14848f512ceSopenharmony_ci                while (itemIt != config.reportItems_.end()) {
14948f512ceSopenharmony_ci                    if (!reportKey.ShouldDisplay(*itemIt)) {
15048f512ceSopenharmony_ci                        HLOGM("filter out %s", itemIt->ToDebugString().c_str());
15148f512ceSopenharmony_ci
15248f512ceSopenharmony_ci                        // we need recalc the heating ,so also remove in total count
15348f512ceSopenharmony_ci                        config.eventCount_ -= itemIt->eventCount_;
15448f512ceSopenharmony_ci
15548f512ceSopenharmony_ci                        // after update total eventCount remove this
15648f512ceSopenharmony_ci                        itemIt = config.reportItems_.erase(itemIt);
15748f512ceSopenharmony_ci                        filterOuts++;
15848f512ceSopenharmony_ci                    } else {
15948f512ceSopenharmony_ci                        itemIt++;
16048f512ceSopenharmony_ci                    }
16148f512ceSopenharmony_ci                }
16248f512ceSopenharmony_ci            }
16348f512ceSopenharmony_ci        }
16448f512ceSopenharmony_ci        HLOGD("filter out %zu, %zu -> %zu", filterOuts, totalReportCount,
16548f512ceSopenharmony_ci              config.reportItems_.size());
16648f512ceSopenharmony_ci    }
16748f512ceSopenharmony_ci}
16848f512ceSopenharmony_ci
16948f512ceSopenharmony_civoid Report::UpdateReportItemsAfterAdjust()
17048f512ceSopenharmony_ci{
17148f512ceSopenharmony_ci    for (auto &config : configs_) {
17248f512ceSopenharmony_ci        HLOGV("percentage %zu items", config.reportItems_.size());
17348f512ceSopenharmony_ci        uint64_t totalEventCount = 0; // just for debug check
17448f512ceSopenharmony_ci        for (auto &item : config.reportItems_) {
17548f512ceSopenharmony_ci            item.heat = Percentage(item.eventCount_, config.eventCount_);
17648f512ceSopenharmony_ci            totalEventCount += item.eventCount_;
17748f512ceSopenharmony_ci            HLOGM("%s percentage from %5.2f%% %" PRIu64 "/ %" PRIu64 "",
17848f512ceSopenharmony_ci                  item.ToDebugString().c_str(), item.heat, item.eventCount_, config.eventCount_);
17948f512ceSopenharmony_ci            for (auto keyPair : reportKeyMap_) {
18048f512ceSopenharmony_ci                reportKeyMap_.at(keyPair.first).UpdateValueMaxLen(keyPair.second.GetValue(item));
18148f512ceSopenharmony_ci            }
18248f512ceSopenharmony_ci        }
18348f512ceSopenharmony_ci        // check again
18448f512ceSopenharmony_ci        HLOGV("recalc totalEventCount is %" PRIu64 " old totalEventCount is %" PRIu64 "",
18548f512ceSopenharmony_ci              totalEventCount, config.eventCount_);
18648f512ceSopenharmony_ci        HLOG_ASSERT(totalEventCount == config.eventCount_);
18748f512ceSopenharmony_ci    }
18848f512ceSopenharmony_ci}
18948f512ceSopenharmony_ci
19048f512ceSopenharmony_civoid Report::AdjustReportItems()
19148f512ceSopenharmony_ci{
19248f512ceSopenharmony_ci    HLOGD("Adjust Record Order ....");
19348f512ceSopenharmony_ci    for (auto &config : configs_) {
19448f512ceSopenharmony_ci        uint64_t totalReportCount = config.reportItems_.size();
19548f512ceSopenharmony_ci        if (option_.debug_) {
19648f512ceSopenharmony_ci            for (auto &reportItem : config.reportItems_) {
19748f512ceSopenharmony_ci                HLOGV("reportItem %s", reportItem.ToDebugString().c_str());
19848f512ceSopenharmony_ci            }
19948f512ceSopenharmony_ci        }
20048f512ceSopenharmony_ci        // sort first.
20148f512ceSopenharmony_ci        HLOGD("MultiLevelSorting %" PRIu64 "", totalReportCount);
20248f512ceSopenharmony_ci        std::sort(config.reportItems_.begin(), config.reportItems_.end(),
20348f512ceSopenharmony_ci            [this] (const ReportItem &a, const ReportItem &b) -> bool {
20448f512ceSopenharmony_ci                return this->MultiLevelSorting(a, b);
20548f512ceSopenharmony_ci            });
20648f512ceSopenharmony_ci        HLOGD("MultiLevelSorting %" PRIu64 " done", totalReportCount);
20748f512ceSopenharmony_ci        // reorder the callstack
20848f512ceSopenharmony_ci        if (option_.debug_) {
20948f512ceSopenharmony_ci            for (auto &reportItem : config.reportItems_) {
21048f512ceSopenharmony_ci                HLOGV("reportItem %s", reportItem.ToDebugString().c_str());
21148f512ceSopenharmony_ci            }
21248f512ceSopenharmony_ci        }
21348f512ceSopenharmony_ci        StatisticsRecords();
21448f512ceSopenharmony_ci        FilterDisplayRecords();
21548f512ceSopenharmony_ci
21648f512ceSopenharmony_ci        // reorder by count
21748f512ceSopenharmony_ci        std::sort(config.reportItems_.begin(), config.reportItems_.end(),
21848f512ceSopenharmony_ci                  &ReportItem::CompareSortingEventCount);
21948f512ceSopenharmony_ci
22048f512ceSopenharmony_ci        // reorder the callstack
22148f512ceSopenharmony_ci        for (auto &reportItem : config.reportItems_) {
22248f512ceSopenharmony_ci            ReportItemCallFrame::OrderCallFrames(reportItem.callStacks_);
22348f512ceSopenharmony_ci        }
22448f512ceSopenharmony_ci        HLOGD("afater sorting and unique, we have %zu report items,", config.reportItems_.size());
22548f512ceSopenharmony_ci    }
22648f512ceSopenharmony_ci    // udpate percentage
22748f512ceSopenharmony_ci    UpdateReportItemsAfterAdjust();
22848f512ceSopenharmony_ci}
22948f512ceSopenharmony_ci
23048f512ceSopenharmony_ciint Report::MultiLevelCompare(const ReportItem &a, const ReportItem &b)
23148f512ceSopenharmony_ci{
23248f512ceSopenharmony_ci    HLOGM("MultiLevelCompare %s vs %s sort order %s", a.ToDebugString().c_str(),
23348f512ceSopenharmony_ci          b.ToDebugString().c_str(), VectorToString(option_.sortKeys_).c_str());
23448f512ceSopenharmony_ci
23548f512ceSopenharmony_ci    // check each key user care
23648f512ceSopenharmony_ci    for (auto it = option_.sortKeys_.begin(); it != option_.sortKeys_.end(); ++it) {
23748f512ceSopenharmony_ci        int result = reportKeyMap_.at(*it).compareFunction_(a, b);
23848f512ceSopenharmony_ci        if (result == 0) {
23948f512ceSopenharmony_ci            // this key is same , check  the next one
24048f512ceSopenharmony_ci            continue;
24148f512ceSopenharmony_ci        } else {
24248f512ceSopenharmony_ci            // if onekey is not same ,  returl as not same
24348f512ceSopenharmony_ci            HLOGM("not same because %s %d : %s vs %s", it->c_str(), result,
24448f512ceSopenharmony_ci                  reportKeyMap_.at(*it).GetValue(a).c_str(),
24548f512ceSopenharmony_ci                  reportKeyMap_.at(*it).GetValue(b).c_str());
24648f512ceSopenharmony_ci            return result;
24748f512ceSopenharmony_ci        }
24848f512ceSopenharmony_ci    }
24948f512ceSopenharmony_ci    // all the key is same
25048f512ceSopenharmony_ci    return 0;
25148f512ceSopenharmony_ci}
25248f512ceSopenharmony_ci
25348f512ceSopenharmony_cibool Report::MultiLevelSame(const ReportItem &a, const ReportItem &b)
25448f512ceSopenharmony_ci{
25548f512ceSopenharmony_ci    return MultiLevelCompare(a, b) == 0;
25648f512ceSopenharmony_ci}
25748f512ceSopenharmony_ci
25848f512ceSopenharmony_civoid Report::MergeCallFrameCount(ReportItem &leftItem, ReportItem &rightItem)
25948f512ceSopenharmony_ci{
26048f512ceSopenharmony_ci    // add to left (right to left)
26148f512ceSopenharmony_ci    std::vector<ReportItemCallFrame> *leftCallFrames = &leftItem.callStacks_;
26248f512ceSopenharmony_ci    const std::vector<ReportItemCallFrame> *rightCallFrames = &rightItem.callStacks_;
26348f512ceSopenharmony_ci    uint64_t maxEventCount = leftItem.eventCount_;
26448f512ceSopenharmony_ci    // right should only have one call stack
26548f512ceSopenharmony_ci    int level = 0;
26648f512ceSopenharmony_ci    while (rightCallFrames->size() != 0) {
26748f512ceSopenharmony_ci        HLOG_ASSERT(rightCallFrames->size() == 1u);
26848f512ceSopenharmony_ci        const ReportItemCallFrame &rightFrame = rightCallFrames->at(0);
26948f512ceSopenharmony_ci        auto leftFrameIt = std::find(leftCallFrames->begin(), leftCallFrames->end(), rightFrame);
27048f512ceSopenharmony_ci        if (leftFrameIt == leftCallFrames->end()) {
27148f512ceSopenharmony_ci            // new callfames
27248f512ceSopenharmony_ci            auto &leftCallFrame = leftCallFrames->emplace_back(rightFrame);
27348f512ceSopenharmony_ci            HLOGV("%*s create frame %s in %s", level, "", leftCallFrame.ToDebugString().c_str(),
27448f512ceSopenharmony_ci                  leftItem.ToDebugString().c_str());
27548f512ceSopenharmony_ci            HLOG_ASSERT(leftCallFrame.eventCount_ <= maxEventCount);
27648f512ceSopenharmony_ci            // this is a new call stack ,
27748f512ceSopenharmony_ci            // all the child in rightFrame has been copy to left.
27848f512ceSopenharmony_ci            break;
27948f512ceSopenharmony_ci        } else {
28048f512ceSopenharmony_ci            // already have , add count
28148f512ceSopenharmony_ci            leftFrameIt->eventCount_ += rightFrame.eventCount_;
28248f512ceSopenharmony_ci            leftFrameIt->selfEventCount_ += rightFrame.selfEventCount_;
28348f512ceSopenharmony_ci            // left move to next
28448f512ceSopenharmony_ci            leftCallFrames = &(leftFrameIt->childs);
28548f512ceSopenharmony_ci            HLOGM("%*s udpate frame +%" PRIu64 " %s in %s", level, "", rightFrame.eventCount_,
28648f512ceSopenharmony_ci                  leftFrameIt->ToDebugString().c_str(), leftItem.ToDebugString().c_str());
28748f512ceSopenharmony_ci            HLOG_ASSERT_MESSAGE(leftFrameIt->eventCount_ <= maxEventCount,
28848f512ceSopenharmony_ci                                " maxEventCount is %" PRIu64 "", maxEventCount);
28948f512ceSopenharmony_ci            maxEventCount = leftFrameIt->eventCount_;
29048f512ceSopenharmony_ci        }
29148f512ceSopenharmony_ci        // move to next level
29248f512ceSopenharmony_ci        rightCallFrames = &(rightFrame.childs);
29348f512ceSopenharmony_ci        level++;
29448f512ceSopenharmony_ci    }
29548f512ceSopenharmony_ci}
29648f512ceSopenharmony_ci
29748f512ceSopenharmony_cibool Report::MultiLevelSameAndUpdateCount(ReportItem &l, ReportItem &r)
29848f512ceSopenharmony_ci{
29948f512ceSopenharmony_ci    if (MultiLevelCompare(l, r) == 0) {
30048f512ceSopenharmony_ci        l.eventCount_ += r.eventCount_;
30148f512ceSopenharmony_ci        HLOGM("l %" PRIu64 " %s c:%zu vs r %" PRIu64 " %s c:%zu", l.eventCount_, l.func_.data(),
30248f512ceSopenharmony_ci              l.callStacks_.size(), r.eventCount_, r.func_.data(), r.callStacks_.size());
30348f512ceSopenharmony_ci        // if it have call stack?
30448f512ceSopenharmony_ci        if (r.callStacks_.size() != 0) {
30548f512ceSopenharmony_ci            // add to left (right to left)
30648f512ceSopenharmony_ci            MergeCallFrameCount(l, r);
30748f512ceSopenharmony_ci        }
30848f512ceSopenharmony_ci        return true;
30948f512ceSopenharmony_ci    } else {
31048f512ceSopenharmony_ci        return false;
31148f512ceSopenharmony_ci    }
31248f512ceSopenharmony_ci}
31348f512ceSopenharmony_ci
31448f512ceSopenharmony_cibool Report::MultiLevelSorting(const ReportItem &a, const ReportItem &b)
31548f512ceSopenharmony_ci{
31648f512ceSopenharmony_ci    /*
31748f512ceSopenharmony_ci    The value returned indicates whether the element passed as first argument is
31848f512ceSopenharmony_ci    considered to go before the second in the specific strict weak ordering it defines.
31948f512ceSopenharmony_ci    */
32048f512ceSopenharmony_ci    bool result = MultiLevelCompare(a, b) > 0;
32148f512ceSopenharmony_ci#ifdef HIPERF_DEBUG
32248f512ceSopenharmony_ci    if (DebugLogger::GetInstance()->GetLogLevel() <= LEVEL_VERBOSE) {
32348f512ceSopenharmony_ci        bool result2 = MultiLevelCompare(b, a) > 0;
32448f512ceSopenharmony_ci        if (result and result == result2) {
32548f512ceSopenharmony_ci            HLOGE("MultiLevelSorting a->b %d vs b->a %d", result, result2);
32648f512ceSopenharmony_ci            HLOGE("left %s", a.ToDebugString().c_str());
32748f512ceSopenharmony_ci            HLOGE("right %s", b.ToDebugString().c_str());
32848f512ceSopenharmony_ci            HLOG_ASSERT(false);
32948f512ceSopenharmony_ci        }
33048f512ceSopenharmony_ci    }
33148f512ceSopenharmony_ci#endif
33248f512ceSopenharmony_ci    return result;
33348f512ceSopenharmony_ci}
33448f512ceSopenharmony_ci
33548f512ceSopenharmony_civoid Report::OutputStdStatistics(ReportEventConfigItem &config)
33648f512ceSopenharmony_ci{
33748f512ceSopenharmony_ci    if (fprintf(output_, "\n") < 0) {
33848f512ceSopenharmony_ci        return;
33948f512ceSopenharmony_ci    } // make a blank line for new event
34048f512ceSopenharmony_ci    if (fprintf(output_, "Event: %s (type %" PRIu32 " id %" PRIu64 ")\n", config.eventName_.c_str(),
34148f512ceSopenharmony_ci            config.type_, config.config_) < 0) {
34248f512ceSopenharmony_ci        return;
34348f512ceSopenharmony_ci    }
34448f512ceSopenharmony_ci    if (fprintf(output_, "Samples Count: %" PRIu64 "\n", config.sampleCount_) < 0) {
34548f512ceSopenharmony_ci        return;
34648f512ceSopenharmony_ci    }
34748f512ceSopenharmony_ci    if (!config.coutMode_) {
34848f512ceSopenharmony_ci        fprintf(output_, "Time in ns: ");
34948f512ceSopenharmony_ci    } else {
35048f512ceSopenharmony_ci        fprintf(output_, "Event Count: ");
35148f512ceSopenharmony_ci    }
35248f512ceSopenharmony_ci    fprintf(output_, "%" PRIu64 "\n", config.eventCount_);
35348f512ceSopenharmony_ci}
35448f512ceSopenharmony_ci
35548f512ceSopenharmony_civoid Report::OutputStdHead(ReportEventConfigItem &config, bool diffMode)
35648f512ceSopenharmony_ci{
35748f512ceSopenharmony_ci    // head print
35848f512ceSopenharmony_ci    const std::string head = "Heating";
35948f512ceSopenharmony_ci    if (fprintf(output_, "%-*s ", FULL_PERCENTAGE_LEN, head.c_str()) < 0) {
36048f512ceSopenharmony_ci        return;
36148f512ceSopenharmony_ci    }
36248f512ceSopenharmony_ci
36348f512ceSopenharmony_ci    if (diffMode) {
36448f512ceSopenharmony_ci        const std::string diff = "Diff";
36548f512ceSopenharmony_ci        fprintf(output_, "%-*s ", FULL_PERCENTAGE_DIFF_LEN, diff.c_str());
36648f512ceSopenharmony_ci    }
36748f512ceSopenharmony_ci
36848f512ceSopenharmony_ci    // merge sort key and no-sort key (like count)
36948f512ceSopenharmony_ci
37048f512ceSopenharmony_ci    displayKeyNames_ = option_.sortKeys_;
37148f512ceSopenharmony_ci    if (!option_.hideCount_) {
37248f512ceSopenharmony_ci        displayKeyNames_.insert(displayKeyNames_.begin(), "count");
37348f512ceSopenharmony_ci    }
37448f512ceSopenharmony_ci
37548f512ceSopenharmony_ci    unsigned int remainingWidth = consoleWidth_;
37648f512ceSopenharmony_ci    // sort key head
37748f512ceSopenharmony_ci    for (auto &keyName : displayKeyNames_) {
37848f512ceSopenharmony_ci        auto &key = reportKeyMap_.at(keyName);
37948f512ceSopenharmony_ci        remainingWidth -= key.maxLen_;
38048f512ceSopenharmony_ci        if (remainingWidth == 0) {
38148f512ceSopenharmony_ci            key.maxLen_ = 0;
38248f512ceSopenharmony_ci        }
38348f512ceSopenharmony_ci        if (fprintf(output_, "%-*s ", (remainingWidth > 0) ? static_cast<unsigned int>(key.maxLen_) : 0,
38448f512ceSopenharmony_ci            key.keyName_.c_str()) < 0) {
38548f512ceSopenharmony_ci            return;
38648f512ceSopenharmony_ci        }
38748f512ceSopenharmony_ci        HLOGD("'%s' max len %zu(from '%s') console width %d", key.keyName_.c_str(), key.maxLen_,
38848f512ceSopenharmony_ci              key.maxValue_.c_str(), remainingWidth);
38948f512ceSopenharmony_ci    }
39048f512ceSopenharmony_ci    if (fprintf(output_, "\n") < 0) {
39148f512ceSopenharmony_ci        return;
39248f512ceSopenharmony_ci    }
39348f512ceSopenharmony_ci}
39448f512ceSopenharmony_ci
39548f512ceSopenharmony_cibool Report::OutputStdCallFrame(int indent, const std::string_view &funcName, uint64_t eventCount,
39648f512ceSopenharmony_ci                                uint64_t totalEventCount)
39748f512ceSopenharmony_ci{
39848f512ceSopenharmony_ci    float heat = Percentage(eventCount, totalEventCount);
39948f512ceSopenharmony_ci    float num = 100.0;
40048f512ceSopenharmony_ci    HLOGV("frame %f indent %d at %s", heat, indent, funcName.data());
40148f512ceSopenharmony_ci
40248f512ceSopenharmony_ci    CHECK_TRUE(heat < option_.callStackHeatLimit_, false, 0, ""); // don't print this three anymore
40348f512ceSopenharmony_ci
40448f512ceSopenharmony_ci    if (abs(heat - num) < ALMOST_ZERO) {
40548f512ceSopenharmony_ci        fprintf(output_, "%*s", indent, "   ");
40648f512ceSopenharmony_ci        fprintf(output_, "%*s  ", FULL_PERCENTAGE_NUM_LEN, " ");
40748f512ceSopenharmony_ci    } else {
40848f512ceSopenharmony_ci        fprintf(output_, "%*s", indent, "|- ");
40948f512ceSopenharmony_ci        fprintf(output_, "%*.2f%% ", FULL_PERCENTAGE_NUM_LEN, heat);
41048f512ceSopenharmony_ci    }
41148f512ceSopenharmony_ci    if (option_.debug_) {
41248f512ceSopenharmony_ci        fprintf(output_, "%" PRIu64 "/%" PRIu64 " %s\n", eventCount, totalEventCount,
41348f512ceSopenharmony_ci                funcName.data());
41448f512ceSopenharmony_ci    } else {
41548f512ceSopenharmony_ci        fprintf(output_, "%s\n", funcName.data());
41648f512ceSopenharmony_ci    }
41748f512ceSopenharmony_ci    return true;
41848f512ceSopenharmony_ci}
41948f512ceSopenharmony_ci
42048f512ceSopenharmony_civoid Report::PrepareConsole()
42148f512ceSopenharmony_ci{
42248f512ceSopenharmony_ci#if defined(is_mingw) && is_mingw
42348f512ceSopenharmony_ci    CONSOLE_SCREEN_BUFFER_INFO csbi;
42448f512ceSopenharmony_ci    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
42548f512ceSopenharmony_ci    consoleWidth_ = static_cast<unsigned int>(csbi.srWindow.Right - csbi.srWindow.Left + 1);
42648f512ceSopenharmony_ci    const auto handle = GetStdHandle(STD_OUTPUT_HANDLE);
42748f512ceSopenharmony_ci    DWORD mode;
42848f512ceSopenharmony_ci    GetConsoleMode(handle, &mode);
42948f512ceSopenharmony_ci    mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
43048f512ceSopenharmony_ci    SetConsoleMode(handle, mode);
43148f512ceSopenharmony_ci#else
43248f512ceSopenharmony_ci    struct winsize w = {0, 0, 0, 0};
43348f512ceSopenharmony_ci    ioctl(fileno(stdout), TIOCGWINSZ, &w);
43448f512ceSopenharmony_ci    consoleWidth_ = static_cast<unsigned int>(w.ws_col);
43548f512ceSopenharmony_ci#endif
43648f512ceSopenharmony_ci    if (consoleWidth_ == 0) {
43748f512ceSopenharmony_ci        consoleWidth_ = ConsoleDefaultWidth;
43848f512ceSopenharmony_ci    }
43948f512ceSopenharmony_ci    HLOGD("consoleWidth_:%d", consoleWidth_);
44048f512ceSopenharmony_ci}
44148f512ceSopenharmony_ci
44248f512ceSopenharmony_civoid Report::OutputStdCallFrames(int indent, const ReportItemCallFrame &callFrame, uint64_t totalEventCount)
44348f512ceSopenharmony_ci{
44448f512ceSopenharmony_ci    /*
44548f512ceSopenharmony_ci    90% a
44648f512ceSopenharmony_ci        |- 80% b
44748f512ceSopenharmony_ci            c
44848f512ceSopenharmony_ci            d
44948f512ceSopenharmony_ci            |- 50% e
45048f512ceSopenharmony_ci            |- 50% f
45148f512ceSopenharmony_ci                   g
45248f512ceSopenharmony_ci    */
45348f512ceSopenharmony_ci    // this is the first call frame
45448f512ceSopenharmony_ci    // this tree will skipped.
45548f512ceSopenharmony_ci    CHECK_TRUE(!OutputStdCallFrame(indent, callFrame.func_, callFrame.eventCount_, totalEventCount),
45648f512ceSopenharmony_ci               NO_RETVAL, 0, "");
45748f512ceSopenharmony_ci
45848f512ceSopenharmony_ci    // print it self
45948f512ceSopenharmony_ci    if (callFrame.selfEventCount_ != 0 and callFrame.selfEventCount_ != callFrame.eventCount_) {
46048f512ceSopenharmony_ci        OutputStdCallFrame(indent + CALLSTACK_INDENT, "[run in self function]",
46148f512ceSopenharmony_ci                           callFrame.selfEventCount_, callFrame.eventCount_);
46248f512ceSopenharmony_ci    }
46348f512ceSopenharmony_ci
46448f512ceSopenharmony_ci    // printf children
46548f512ceSopenharmony_ci    // if only one children
46648f512ceSopenharmony_ci    if (callFrame.childs.size() == 1u and
46748f512ceSopenharmony_ci        callFrame.childs[0].eventCount_ == callFrame.eventCount_) {
46848f512ceSopenharmony_ci        HLOGV("childCallFream %*c %s", indent, ' ', callFrame.childs[0].func_.data());
46948f512ceSopenharmony_ci        // don't indent if same count (only one 100% children)
47048f512ceSopenharmony_ci        OutputStdCallFrames(indent, callFrame.childs[0], callFrame.eventCount_);
47148f512ceSopenharmony_ci    } else {
47248f512ceSopenharmony_ci        // else a lot children
47348f512ceSopenharmony_ci        for (const ReportItemCallFrame &childCallFrame : callFrame.childs) {
47448f512ceSopenharmony_ci            HLOGV("childCallFream %*c %s", indent, ' ', childCallFrame.func_.data());
47548f512ceSopenharmony_ci            OutputStdCallFrames(indent + CALLSTACK_INDENT, childCallFrame, callFrame.eventCount_);
47648f512ceSopenharmony_ci        }
47748f512ceSopenharmony_ci    }
47848f512ceSopenharmony_ci}
47948f512ceSopenharmony_ci
48048f512ceSopenharmony_civoid Report::OutputStdContent(ReportEventConfigItem &config)
48148f512ceSopenharmony_ci{
48248f512ceSopenharmony_ci    // content print
48348f512ceSopenharmony_ci    auto it = config.reportItems_.begin();
48448f512ceSopenharmony_ci    while (it != config.reportItems_.end()) {
48548f512ceSopenharmony_ci        const ReportItem &reportItem = it.operator*();
48648f512ceSopenharmony_ci        // if we need skip it ?
48748f512ceSopenharmony_ci        if (reportItem.heat < option_.heatLimit_) {
48848f512ceSopenharmony_ci            it++;
48948f512ceSopenharmony_ci            continue; // below limit
49048f512ceSopenharmony_ci        } else {
49148f512ceSopenharmony_ci            fprintf(output_, "%*.2f%%  ", FULL_PERCENTAGE_NUM_LEN, reportItem.heat);
49248f512ceSopenharmony_ci        }
49348f512ceSopenharmony_ci        OutputStdContentItem(reportItem);
49448f512ceSopenharmony_ci        if (reportItem.callStacks_.size() != 0) {
49548f512ceSopenharmony_ci            HLOGV("reportItem.callStacks_ %zu %s", reportItem.callStacks_.size(),
49648f512ceSopenharmony_ci                  reportItem.ToDebugString().c_str());
49748f512ceSopenharmony_ci            HLOG_ASSERT(reportItem.callStacks_.size() == 1u);
49848f512ceSopenharmony_ci            for (auto &callFrame : reportItem.callStacks_) {
49948f512ceSopenharmony_ci                OutputStdCallFrames(CALLSTACK_INDENT, callFrame, reportItem.eventCount_);
50048f512ceSopenharmony_ci            }
50148f512ceSopenharmony_ci        }
50248f512ceSopenharmony_ci        it++;
50348f512ceSopenharmony_ci    }
50448f512ceSopenharmony_ci}
50548f512ceSopenharmony_ci
50648f512ceSopenharmony_civoid Report::OutputStdContentItem(const ReportItem &reportItem)
50748f512ceSopenharmony_ci{
50848f512ceSopenharmony_ci    // output by sort keys
50948f512ceSopenharmony_ci    for (auto sortKey : displayKeyNames_) {
51048f512ceSopenharmony_ci        ReportKey &reportKey = Report::reportKeyMap_.at(sortKey);
51148f512ceSopenharmony_ci        if (fprintf(output_, "%s ", reportKey.GetValue(reportItem).c_str()) < 0) {
51248f512ceSopenharmony_ci            return;
51348f512ceSopenharmony_ci        }
51448f512ceSopenharmony_ci    }
51548f512ceSopenharmony_ci    if (fprintf(output_, "\n") < 0) {
51648f512ceSopenharmony_ci        return;
51748f512ceSopenharmony_ci    }
51848f512ceSopenharmony_ci}
51948f512ceSopenharmony_ci
52048f512ceSopenharmony_civoid Report::OutputStdItemHeating(float heat, float heat2)
52148f512ceSopenharmony_ci{
52248f512ceSopenharmony_ci    if (heat == heat2 and heat == 0.0f) {
52348f512ceSopenharmony_ci        fprintf(output_, "something error , all it is end.\n");
52448f512ceSopenharmony_ci    } else if (heat2 == 0) {
52548f512ceSopenharmony_ci        // only have first
52648f512ceSopenharmony_ci        fprintf(output_, "%*.2f%%  ", FULL_PERCENTAGE_NUM_LEN, heat);
52748f512ceSopenharmony_ci        fprintf(output_, "%*s ", FULL_PERCENTAGE_DIFF_LEN, "");
52848f512ceSopenharmony_ci    } else if (heat == 0) {
52948f512ceSopenharmony_ci        // only have second
53048f512ceSopenharmony_ci        fprintf(output_, "%*s  ", FULL_PERCENTAGE_LEN, "");
53148f512ceSopenharmony_ci        fprintf(output_, "%+*.2f%% ", FULL_PERCENTAGE_DIFF_NUM_LEN, heat2);
53248f512ceSopenharmony_ci    } else if (heat2 > heat) {
53348f512ceSopenharmony_ci        fprintf(output_, "%s%*.2f%%%s  ", TEXT_RED.c_str(), FULL_PERCENTAGE_NUM_LEN, heat,
53448f512ceSopenharmony_ci                TEXT_RESET.c_str());
53548f512ceSopenharmony_ci        fprintf(output_, "%s%+*.2f%%%s ", TEXT_GREEN.c_str(), FULL_PERCENTAGE_DIFF_NUM_LEN,
53648f512ceSopenharmony_ci                heat2 - heat, TEXT_RESET.c_str());
53748f512ceSopenharmony_ci    } else if (heat2 < heat) {
53848f512ceSopenharmony_ci        fprintf(output_, "%s%*.2f%%%s  ", TEXT_GREEN.c_str(), FULL_PERCENTAGE_NUM_LEN, heat,
53948f512ceSopenharmony_ci                TEXT_RESET.c_str());
54048f512ceSopenharmony_ci        fprintf(output_, "%s%+*.2f%%%s ", TEXT_RED.c_str(), FULL_PERCENTAGE_DIFF_NUM_LEN,
54148f512ceSopenharmony_ci                heat2 - heat, TEXT_RESET.c_str());
54248f512ceSopenharmony_ci    } else {
54348f512ceSopenharmony_ci        // same heating
54448f512ceSopenharmony_ci        fprintf(output_, "%*.2f%% ", FULL_PERCENTAGE_NUM_LEN, heat);
54548f512ceSopenharmony_ci        fprintf(output_, "%+*.2f%% ", FULL_PERCENTAGE_DIFF_NUM_LEN, heat2 - heat);
54648f512ceSopenharmony_ci    }
54748f512ceSopenharmony_ci}
54848f512ceSopenharmony_ci
54948f512ceSopenharmony_civoid Report::OutputStdContentDiff(ReportEventConfigItem &left, ReportEventConfigItem &right)
55048f512ceSopenharmony_ci{
55148f512ceSopenharmony_ci    // first we need found the match config
55248f512ceSopenharmony_ci    HLOGD("first count %zu second count %zu", left.reportItems_.size(), right.reportItems_.size());
55348f512ceSopenharmony_ci    ReportItemsConstIt it = left.reportItems_.begin();
55448f512ceSopenharmony_ci    ReportItemsConstIt it2 = right.reportItems_.begin();
55548f512ceSopenharmony_ci    while (it != left.reportItems_.end()) {
55648f512ceSopenharmony_ci        // still have it2 ?
55748f512ceSopenharmony_ci        if (it2 != right.reportItems_.end()) {
55848f512ceSopenharmony_ci            // find the same item in it2 by same sort key
55948f512ceSopenharmony_ci            while (it2 != right.reportItems_.end()) {
56048f512ceSopenharmony_ci                if (MultiLevelSame(*it, *it2)) {
56148f512ceSopenharmony_ci                    // we found the same item
56248f512ceSopenharmony_ci                    // output the diff heating
56348f512ceSopenharmony_ci                    if (it->heat > option_.heatLimit_ and it2->heat > option_.heatLimit_) {
56448f512ceSopenharmony_ci                        OutputStdItemHeating(it->heat, it2->heat);
56548f512ceSopenharmony_ci                        OutputStdContentItem(*it);
56648f512ceSopenharmony_ci                    }
56748f512ceSopenharmony_ci                    it++;
56848f512ceSopenharmony_ci                    it2++;
56948f512ceSopenharmony_ci                    break; // next it
57048f512ceSopenharmony_ci                } else {
57148f512ceSopenharmony_ci                    // only print it2 item
57248f512ceSopenharmony_ci                    if (it2->heat > option_.heatLimit_) {
57348f512ceSopenharmony_ci                        OutputStdItemHeating(0.0f, it2->heat);
57448f512ceSopenharmony_ci                        OutputStdContentItem(*it2);
57548f512ceSopenharmony_ci                    }
57648f512ceSopenharmony_ci                    it2++;
57748f512ceSopenharmony_ci                    continue; // next it2
57848f512ceSopenharmony_ci                }
57948f512ceSopenharmony_ci            }
58048f512ceSopenharmony_ci        } else {
58148f512ceSopenharmony_ci            // no more it2, go on print all the it
58248f512ceSopenharmony_ci            if (it->heat > option_.heatLimit_) {
58348f512ceSopenharmony_ci                OutputStdItemHeating(it->heat, 0.0f);
58448f512ceSopenharmony_ci                OutputStdContentItem(*it);
58548f512ceSopenharmony_ci            }
58648f512ceSopenharmony_ci            it++;
58748f512ceSopenharmony_ci            continue; // next it
58848f512ceSopenharmony_ci        }
58948f512ceSopenharmony_ci    }
59048f512ceSopenharmony_ci    while (it2 != right.reportItems_.end()) {
59148f512ceSopenharmony_ci        // if diff still have some item in it2 ,print it
59248f512ceSopenharmony_ci        OutputStdItemHeating(0, it2->heat);
59348f512ceSopenharmony_ci        OutputStdContentItem(*it2);
59448f512ceSopenharmony_ci        it2++;
59548f512ceSopenharmony_ci    }
59648f512ceSopenharmony_ci}
59748f512ceSopenharmony_ci
59848f512ceSopenharmony_civoid Report::OutputStd(FILE *output)
59948f512ceSopenharmony_ci{
60048f512ceSopenharmony_ci    output_ = output;
60148f512ceSopenharmony_ci    PrepareConsole();
60248f512ceSopenharmony_ci
60348f512ceSopenharmony_ci    for (auto &config : configs_) {
60448f512ceSopenharmony_ci        OutputStdStatistics(config);
60548f512ceSopenharmony_ci        OutputStdHead(config);
60648f512ceSopenharmony_ci        OutputStdContent(config);
60748f512ceSopenharmony_ci    }
60848f512ceSopenharmony_ci}
60948f512ceSopenharmony_ci
61048f512ceSopenharmony_civoid Report::OutputStdDiff(FILE *output, Report &other)
61148f512ceSopenharmony_ci{
61248f512ceSopenharmony_ci    output_ = output;
61348f512ceSopenharmony_ci    PrepareConsole();
61448f512ceSopenharmony_ci
61548f512ceSopenharmony_ci    auto left = configs_.begin();
61648f512ceSopenharmony_ci    while (left != configs_.end()) {
61748f512ceSopenharmony_ci        auto right = other.configs_.begin();
61848f512ceSopenharmony_ci        while (right != other.configs_.end()) {
61948f512ceSopenharmony_ci            if (*left == *right) {
62048f512ceSopenharmony_ci                OutputStdStatistics(*left);
62148f512ceSopenharmony_ci                OutputStdHead(*left, true);
62248f512ceSopenharmony_ci                OutputStdContentDiff(*left, *right);
62348f512ceSopenharmony_ci                break; // check next left
62448f512ceSopenharmony_ci            }
62548f512ceSopenharmony_ci            right++;
62648f512ceSopenharmony_ci        }
62748f512ceSopenharmony_ci        left++; // go on left
62848f512ceSopenharmony_ci    }
62948f512ceSopenharmony_ci}
63048f512ceSopenharmony_ci} // namespace HiPerf
63148f512ceSopenharmony_ci} // namespace Developtools
63248f512ceSopenharmony_ci} // namespace OHOS
633