xref: /developtools/hiperf/include/report.h (revision 48f512ce)
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
16#ifndef REPORT_H
17#define REPORT_H
18
19#include <algorithm>
20#include <cstdio>
21#include <cstdlib>
22#include <functional>
23#include <map>
24
25#include "debug_logger.h"
26// remove me latter
27#include "report_json_file.h"
28#include "utilities.h"
29#include "virtual_runtime.h"
30
31namespace OHOS {
32namespace Developtools {
33namespace HiPerf {
34class ReportItemCallFrame {
35public:
36    std::string func_;
37    uint64_t vaddr_;
38    std::string dso_;
39    uint64_t eventCount_ = 0;     // call chain event
40    uint64_t selfEventCount_ = 0; // call chain event end in this function
41    std::vector<ReportItemCallFrame> childs;
42    ReportItemCallFrame(std::string func, uint64_t vaddr, std::string dso,
43                        uint64_t eventCount, uint64_t selfEventCount)
44        : func_(func),
45          vaddr_(vaddr),
46          dso_(dso),
47          eventCount_(eventCount),
48          selfEventCount_(selfEventCount)
49    {
50    }
51
52    bool operator==(const ReportItemCallFrame &b) const
53    {
54        return Same(b);
55    }
56
57    bool operator!=(const ReportItemCallFrame &b) const
58    {
59        return !Same(b);
60    }
61
62    static int CompareSortingEventCount(const ReportItemCallFrame &a, const ReportItemCallFrame &b)
63    {
64        return a.eventCount_ > b.eventCount_;
65    }
66
67    static void OrderCallFrames(std::vector<ReportItemCallFrame> &callframes, int indent = 2)
68    {
69        int i = 2;
70        if (callframes.size() > 0) {
71            std::sort(callframes.begin(), callframes.end(),
72                      &ReportItemCallFrame::CompareSortingEventCount);
73
74            for (auto &callframe : callframes) {
75                HLOGDUMMY("%*s%s", indent, "", callframe.ToDebugString().c_str());
76                if (callframe.childs.size() > 0) {
77                    OrderCallFrames(callframe.childs, indent + i);
78                }
79            }
80        }
81    }
82
83    // just a log
84    static void DumpCallFrames(std::vector<ReportItemCallFrame> &callframes, int indent = 2)
85    {
86        int y = 2;
87        if (callframes.size() > 0) {
88            for (auto &callframe : callframes) {
89                HLOGV("%*s%s", indent, "", callframe.ToDebugString().c_str());
90                if (callframe.childs.size() > 0) {
91                    DumpCallFrames(callframe.childs, indent + y);
92                }
93            }
94        }
95    }
96
97    const std::string ToDebugString() const
98    {
99        return StringPrintf("%" PRIu64 "(%" PRIu64 ")%s(%s+0x%" PRIx64 ") child %zu", eventCount_,
100                            selfEventCount_, func_.data(), dso_.data(), vaddr_, childs.size());
101    }
102
103private:
104    bool Same(const ReportItemCallFrame &b) const
105    {
106        return (func_ == b.func_) and (vaddr_ == b.vaddr_) and (dso_ == b.dso_);
107    }
108};
109
110// one item or one line in report
111class ReportItem {
112public:
113    pid_t pid_ = 0;
114    pid_t tid_ = 0;
115    std::string_view comm_ = "";
116    std::string_view dso_ = "";
117    std::string_view fromDso_ = "";
118    std::string_view fromFunc_ = "";
119    std::string_view func_ = "";
120    uint64_t vaddr_ = 0;
121    uint64_t eventCount_ = 0; // event count
122    std::vector<ReportItemCallFrame> callStacks_;
123    float heat = 0.0f;
124    static unsigned long long allIndex_; // debug only
125    unsigned long long index_;
126
127    // only for ut test
128    ReportItem(pid_t pid, pid_t tid, const char *comm, const char *dso, const char *func,
129               uint64_t vaddr, uint64_t eventCount)
130        : pid_(pid),
131          tid_(tid),
132          comm_(comm),
133          dso_(dso),
134          func_(func),
135          vaddr_(vaddr),
136          eventCount_(eventCount)
137    {
138        HLOG_ASSERT(comm != nullptr);
139        index_ = allIndex_++;
140    }
141
142    ReportItem(pid_t pid, pid_t tid, std::string &comm, const std::string_view &dso,
143               const std::string_view &func, uint64_t vaddr, uint64_t eventCount)
144        : pid_(pid),
145          tid_(tid),
146          comm_(comm),
147          dso_(StringViewHold::Get().Hold(dso)),
148          func_(StringViewHold::Get().Hold(func)),
149          vaddr_(vaddr),
150          eventCount_(eventCount)
151    {
152        HLOG_ASSERT(!comm.empty());
153        index_ = allIndex_++;
154    }
155
156    bool operator==(const ReportItem &b) const
157    {
158        return Same(b);
159    }
160
161    bool operator!=(const ReportItem &b) const
162    {
163        return !Same(b);
164    }
165
166    // debug only
167    const std::string ToDebugString() const
168    {
169        return StringPrintf("%d:%d:%s-%s(%s):%zu i:%llu", pid_, tid_, comm_.data(), func_.data(),
170                            dso_.data(), eventCount_, index_);
171    }
172
173    // Count
174    static int CompareEventCount(const ReportItem &a, const ReportItem &b)
175    {
176        if (a.eventCount_ != b.eventCount_) {
177            return (a.eventCount_ > b.eventCount_) ? 1 : -1;
178        } else {
179            return 0;
180        }
181    }
182
183    static int CompareSortingEventCount(const ReportItem &a, const ReportItem &b)
184    {
185        return a.eventCount_ > b.eventCount_;
186    }
187
188    static const std::string GetEventCount(const ReportItem &a, size_t len,
189                                           const std::string &format)
190    {
191        return StringPrintf(format.c_str(), len, a.eventCount_);
192    }
193
194    // Pid
195    static int ComparePid(const ReportItem &a, const ReportItem &b)
196    {
197        if (a.pid_ != b.pid_) {
198            return (a.pid_ > b.pid_) ? 1 : -1;
199        } else {
200            return 0;
201        }
202    }
203    static const std::string GetPid(const ReportItem &a, size_t len, const std::string &format)
204    {
205        return StringPrintf(format.c_str(), len, a.pid_);
206    }
207
208    // Tid
209    static int CompareTid(const ReportItem &a, const ReportItem &b)
210    {
211        if (a.tid_ != b.tid_) {
212            return (a.tid_ > b.tid_) ? 1 : -1;
213        } else {
214            return 0;
215        }
216    }
217    static const std::string GetTid(const ReportItem &a, size_t len, const std::string &format)
218    {
219        return StringPrintf(format.c_str(), len, a.tid_);
220    }
221
222    // Comm
223    static int CompareComm(const ReportItem &a, const ReportItem &b)
224    {
225        int result = a.comm_.compare(b.comm_);
226        return result;
227    }
228    static const std::string GetComm(const ReportItem &a, size_t len, const std::string &format)
229    {
230        return StringPrintf(format.c_str(), len, a.comm_.data());
231    }
232
233    // Func
234    static int CompareFunc(const ReportItem &a, const ReportItem &b)
235    {
236        return a.func_.compare(b.func_);
237    }
238    static const std::string GetFunc(const ReportItem &a, size_t len, const std::string &format)
239    {
240        return StringPrintf(format.c_str(), len, a.func_.data());
241    }
242
243    // Dso
244    static int CompareDso(const ReportItem &a, const ReportItem &b)
245    {
246        return a.dso_.compare(b.dso_);
247    }
248    static const std::string GetDso(const ReportItem &a, size_t len, const std::string &format)
249    {
250        return StringPrintf(format.c_str(), len, a.dso_.data());
251    }
252
253    // fromDso
254    static int CompareFromDso(const ReportItem &a, const ReportItem &b)
255    {
256        return a.fromDso_.compare(b.fromDso_);
257    }
258    static const std::string GetFromDso(const ReportItem &a, size_t len, const std::string &format)
259    {
260        return StringPrintf(format.c_str(), len, a.fromDso_.data());
261    }
262
263    // fromFunc
264    static int CompareFromFunc(const ReportItem &a, const ReportItem &b)
265    {
266        return a.fromFunc_.compare(b.fromFunc_);
267    }
268    static const std::string GetFromFunc(const ReportItem &a, size_t len, const std::string &format)
269    {
270        return StringPrintf(format.c_str(), len, a.fromFunc_.data());
271    }
272
273private:
274    bool Same(const ReportItem &b) const
275    {
276        return (comm_ == b.comm_) && (pid_ == b.pid_) && (tid_ == b.tid_) && (func_ == b.func_) &&
277               (dso_ == b.dso_) && (vaddr_ == b.vaddr_);
278    }
279};
280
281using ReportKeyCompareFunction = int(const ReportItem &, const ReportItem &);
282using ReportKeyGetFunction = const std::string(const ReportItem &, size_t, const std::string &);
283
284constexpr const int MAX_FILED_LEN = 20;
285constexpr const int CALLSTACK_INDENT = 4;
286struct ReportKey {
287    const std::string keyName_;
288    const std::string valueFormat_;
289    size_t maxLen_ = 0u;
290    std::string maxValue_;
291    ReportKeyCompareFunction &compareFunction_;
292    ReportKeyGetFunction &GetFunction_;
293    const std::vector<std::string> &displayFilter_;
294
295    ReportKey(const std::string keyName, ReportKeyCompareFunction &compareFunction,
296              ReportKeyGetFunction &GetFunction, const std::string valueFormat,
297              const std::vector<std::string> &displayFilter)
298        : keyName_(keyName),
299          valueFormat_(valueFormat),
300          compareFunction_(compareFunction),
301          GetFunction_(GetFunction),
302          displayFilter_(displayFilter)
303    {
304        maxLen_ = keyName.size();
305    }
306
307    void UpdateValueMaxLen(const std::string &value)
308    {
309        size_t newMaxLen = std::max(maxLen_, value.size());
310        if (maxLen_ < newMaxLen) {
311            maxValue_ = value;
312            maxLen_ = newMaxLen;
313        }
314    }
315
316    void UpdateValueMaxLen(size_t value)
317    {
318        size_t newMaxLen = std::max(maxLen_, std::to_string(value).size());
319        if (maxLen_ < newMaxLen) {
320            maxValue_ = std::to_string(value);
321            maxLen_ = newMaxLen;
322        }
323    }
324
325    std::string GetValue(const ReportItem &i)
326    {
327        return GetFunction_(i, maxLen_, valueFormat_);
328    }
329
330    bool ShouldDisplay(const ReportItem &i)
331    {
332        if (displayFilter_.size() == 0) {
333            return true;
334        } else {
335            std::string value = GetFunction_(i, 0, valueFormat_);
336            auto it = find(displayFilter_.begin(), displayFilter_.end(), value);
337            if (it == displayFilter_.end()) {
338                HLOGV("  not found '%s' in %s", value.c_str(),
339                      VectorToString(displayFilter_).c_str());
340            }
341            return (it != displayFilter_.end());
342        }
343    }
344};
345
346using ReportItems = std::vector<ReportItem>;
347using ReportItemsIt = ReportItems::iterator;
348using ReportItemsConstIt = ReportItems::const_iterator;
349
350struct ReportOption {
351    float heatLimit_ = 0.0f;
352    float callStackHeatLimit_ = 0.0f;
353
354    // display filter
355    std::vector<std::string> displayComms_ {};
356    std::vector<std::string> displayPids_ {};
357    std::vector<std::string> displayTids_ {};
358    std::vector<std::string> displayDsos_ {};
359    std::vector<std::string> displayFromDsos_ {};
360    std::vector<std::string> displayFuncs_ {};
361    std::vector<std::string> displayFromFuncs_ {};
362    std::vector<std::string> displayDummy_ {};
363
364    std::vector<std::string> sortKeys_ = {"comm", "pid", "tid", "dso", "func"};
365
366    bool debug_ = false;
367    bool hideCount_ = false;
368};
369
370class Report {
371public:
372    Report() : option_(defaultOption_), virtualRuntime_(false)
373    {
374        // works for ut test
375    }
376    Report(ReportOption &option) : option_(option), virtualRuntime_(false) {}
377    bool MultiLevelSame(const ReportItem &a, const ReportItem &b);
378    void AdjustReportItems();
379    void AddReportItem(const PerfRecordSample &sample, bool includeCallStack);
380    void AddReportItemBranch(const PerfRecordSample &sample);
381    void OutputStd(FILE *output);
382    void OutputStdDiff(FILE *output, Report &other);
383
384    ReportOption &option_;
385
386    VirtualRuntime virtualRuntime_;
387
388    std::map<std::string, ReportKey> reportKeyMap_ = {
389        {
390            "count",
391            {
392                "count",
393                ReportItem::CompareEventCount,
394                ReportItem::GetEventCount,
395                "%*" PRIu64 "",
396                option_.displayDummy_,
397            },
398        },
399        {
400            "comm",
401            {
402                "comm",
403                ReportItem::CompareComm,
404                ReportItem::GetComm,
405                "%-*s",
406                option_.displayComms_,
407            },
408        },
409        {
410            "pid",
411            {
412                "pid",
413                ReportItem::ComparePid,
414                ReportItem::GetPid,
415                "%*d",
416                option_.displayPids_,
417            },
418        },
419        {
420            "tid",
421            {
422                "tid",
423                ReportItem::CompareTid,
424                ReportItem::GetTid,
425                "%*d",
426                option_.displayTids_,
427            },
428        },
429        {
430            "dso",
431            {
432                "dso",
433                ReportItem::CompareDso,
434                ReportItem::GetDso,
435                "%-*s",
436                option_.displayDsos_,
437            },
438        },
439        {
440            "from_dso",
441            {
442                "from_dso",
443                ReportItem::CompareFromDso,
444                ReportItem::GetFromDso,
445                "%-*s",
446                option_.displayFromDsos_,
447            },
448        },
449        {
450            "func",
451            {
452                "func",
453                ReportItem::CompareFunc,
454                ReportItem::GetFunc,
455                "%-*s",
456                option_.displayFuncs_,
457            },
458        },
459        {
460            "from_func",
461            {
462                "from_func",
463                ReportItem::CompareFromFunc,
464                ReportItem::GetFromFunc,
465                "%-*s",
466                option_.displayFromFuncs_,
467            },
468        },
469    };
470    struct ReportEventConfigItem {
471        ReportEventConfigItem(const ReportEventConfigItem &) = delete;
472        ReportEventConfigItem &operator=(const ReportEventConfigItem &) = delete;
473        ReportEventConfigItem(ReportEventConfigItem &&) = default;
474        ReportEventConfigItem &operator=(ReportEventConfigItem &&) = default;
475        std::string eventName_;
476        uint64_t sampleCount_ = 0;
477        uint64_t eventCount_ = 0;
478        std::vector<ReportItem> reportItems_;
479        uint32_t type_;
480        uint64_t config_;
481        std::vector<uint64_t> ids_;
482
483        bool coutMode_ = true; // use cout or time ?
484        bool operator==(const ReportEventConfigItem &o) const
485        {
486            return (type_ == o.type_) && (config_ == o.config_);
487        }
488        bool operator!=(const ReportEventConfigItem &o) const
489        {
490            return !(operator==(o));
491        }
492        std::string toDebugString()
493        {
494            return StringPrintf("%s(%" PRIu32 "-%" PRIu64 "):PRIu64", eventName_.c_str(), type_,
495                                config_, sampleCount_);
496        }
497        ReportEventConfigItem(std::string eventName, uint32_t type, uint64_t config,
498                              bool coutMode = true)
499            : eventName_(eventName), type_(type), config_(config), coutMode_(coutMode)
500        {
501        }
502    };
503    std::vector<ReportEventConfigItem> configs_;
504    virtual ~Report() {}
505
506    std::map<uint64_t, size_t> configIdIndexMaps_; // index of configNames_
507    std::string GetConfigName(uint64_t id)
508    {
509        return configs_[GetConfigIndex(id)].eventName_;
510    }
511    size_t GetConfigIndex(uint64_t id)
512    {
513        HLOG_ASSERT_MESSAGE(configIdIndexMaps_.find(id) != configIdIndexMaps_.end(),
514                            "unable found id %" PRIx64 "", id);
515        return configIdIndexMaps_.at(id);
516    }
517
518private:
519    FILE *output_ = nullptr;
520    const std::string TEXT_RED = "\x1b[31m";
521    const std::string TEXT_GREEN = "\x1b[32m";
522    const std::string TEXT_RESET = "\033[0m";
523    const unsigned int ConsoleDefaultWidth = 80;
524
525    // sometime caller don't give the option
526    ReportOption defaultOption_;
527
528    std::vector<std::string> displayKeyNames_;
529
530    // use virtual only for gmock test
531    bool MultiLevelSorting(const ReportItem &a, const ReportItem &b);
532    bool MultiLevelSameAndUpdateCount(ReportItem &l, ReportItem &r);
533    void MergeCallFrameCount(ReportItem &leftItem, ReportItem &rightItem);
534    virtual int MultiLevelCompare(const ReportItem &a, const ReportItem &b);
535
536    void StatisticsRecords();
537    void FilterDisplayRecords();
538    void UpdateReportItemsAfterAdjust();
539
540    // std out
541    unsigned int consoleWidth_ = 0;
542    void PrepareConsole();
543
544    void OutputStdStatistics(ReportEventConfigItem &config);
545
546    void OutputStdHead(ReportEventConfigItem &config, bool diffMode = false);
547
548    void OutputStdContent(ReportEventConfigItem &config);
549    void OutputStdContentDiff(ReportEventConfigItem &left, ReportEventConfigItem &right);
550
551    void OutputStdContentItem(const ReportItem &reportItem);
552
553    void OutputStdCallFrames(int indent, const ReportItemCallFrame &callFrame, uint64_t totalEventCount);
554    bool OutputStdCallFrame(int indent, const std::string_view &funcName, uint64_t eventCount,
555                            uint64_t totalEventCount);
556    void OutputStdItemHeating(float heat, float heat2);
557
558    FRIEND_TEST(ReportTest, MultiLevelSorting);
559    FRIEND_TEST(ReportTest, MultiLevelSameAndUpdateCount);
560    FRIEND_TEST(ReportTest, MergeCallFrameCount);
561    FRIEND_TEST(ReportTest, MultiLevelCompare);
562    FRIEND_TEST(ReportTest, PrepareConsole);
563};
564} // namespace HiPerf
565} // namespace Developtools
566} // namespace OHOS
567#endif // REPORT_H
568