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#ifndef HIPERF_PERF_EVENT_RECORD_H
16#define HIPERF_PERF_EVENT_RECORD_H
17
18#include <atomic>
19#include <chrono>
20#include <map>
21#include <memory>
22#include <stdint.h>
23#include <string>
24#include <sys/types.h>
25#include <unique_fd.h>
26#include <variant>
27#include <vector>
28#include <linux/perf_event.h>
29#include <linux/types.h>
30
31#include "debug_logger.h"
32#include "dfx_frame.h"
33#include "dfx_map.h"
34#include "perf_record_format.h"
35#include "unique_stack_table.h"
36#include "utilities.h"
37
38namespace OHOS {
39namespace Developtools {
40namespace HiPerf {
41using namespace OHOS::HiviewDFX;
42
43static constexpr uint32_t RECORD_SIZE_LIMIT = 65535;
44static constexpr uint32_t RECORD_SIZE_LIMIT_SPE = 524288; // auxMmapPages_ * pageSize_
45
46enum perf_event_hiperf_ext_type {
47    PERF_RECORD_AUXTRACE = 71,
48    PERF_RECORD_HIPERF_CALLSTACK = UINT32_MAX / 2,
49};
50
51struct CallFrame {
52    uint64_t ip_ = 0;               // pc
53    uint64_t sp_ = 0;               // sp
54    int32_t symbolFileIndex_ = -1;  // symbolFileIndex_, symbols file index, used to report protobuf file
55    uint64_t vaddrInFile_ = 0;      // funcOffset, vaddr of symbol in file
56    uint64_t offsetToVaddr_ = 0;    // mapOffset, offset of ip to vaddr
57    int32_t symbolIndex_ = -1;      // index, symbols index , should update after sort
58    std::string_view symbolName_;   // funcName
59    std::string_view filePath_;     // mapName, lib path , elf path
60
61    CallFrame(uint64_t ip, uint64_t sp = 0) : ip_(ip), sp_(sp) {}
62
63    // this is for ut test
64    CallFrame(uint64_t ip, uint64_t vaddrInFile, const char *name, const char *filePath)
65        : ip_(ip), vaddrInFile_(vaddrInFile), symbolName_(name), filePath_(filePath)
66    {
67    }
68    bool operator==(const CallFrame &b) const
69    {
70        return (ip_ == b.ip_) && (sp_ == b.sp_);
71    }
72    bool operator!=(const CallFrame &b) const
73    {
74        return (ip_ != b.ip_) || (sp_ != b.sp_);
75    }
76    std::string ToString() const
77    {
78        return StringPrintf("ip: 0x%016llx sp: 0x%016llx", ip_, sp_);
79    }
80    std::string ToSymbolString() const
81    {
82        std::string output = StringPrintf(" 0x%016llx : ", ip_);
83        output.append(symbolName_);
84        if (vaddrInFile_ != 0) {
85            output += StringPrintf("[0x%016llx:0x%016llx][+0x%llx]", ip_ - offsetToVaddr_,
86                vaddrInFile_, offsetToVaddr_);
87        }
88
89        output.append("@");
90        output.append(filePath_);
91        if (symbolIndex_ != -1) {
92            output.append(":");
93            output.append(std::to_string(symbolIndex_));
94        }
95        return output;
96    }
97};
98
99struct AttrWithId {
100    perf_event_attr attr;
101    std::vector<uint64_t> ids;
102    std::string name; // will be empty in GetAttrSection
103};
104
105class PerfEventRecord {
106public:
107    PerfEventRecord(const PerfEventRecord &) = delete;
108    PerfEventRecord &operator=(const PerfEventRecord &) = delete;
109
110    struct perf_event_header header;
111    const std::string name_ {};
112
113    PerfEventRecord(perf_event_type type, bool inKernel, const std::string &name);
114    PerfEventRecord(perf_event_hiperf_ext_type type, const std::string &name);
115
116    PerfEventRecord(uint8_t *p, const std::string &name);
117
118    virtual ~PerfEventRecord() {}
119
120    virtual size_t GetSize() const
121    {
122        return header.size;
123    };
124    size_t GetHeaderSize() const
125    {
126        return sizeof(header);
127    };
128    void GetHeaderBinary(std::vector<uint8_t> &buf) const;
129
130    uint32_t GetType() const
131    {
132        return header.type;
133    };
134    uint16_t GetMisc() const
135    {
136        return header.misc;
137    };
138    bool inKernel()
139    {
140        return header.misc & PERF_RECORD_MISC_KERNEL;
141    }
142    bool inUser()
143    {
144        return header.misc & PERF_RECORD_MISC_USER;
145    }
146    const std::string &GetName() const
147    {
148        return name_;
149    };
150
151    // to support --exclude-hiperf, return sample_id.pid to filter record,
152    virtual pid_t GetPid() const
153    {
154        return 0;
155    };
156
157    virtual bool GetBinary(std::vector<uint8_t> &buf) const = 0;
158    void Dump(int indent = 0, std::string outputFilename = "", FILE *outputDump = nullptr) const;
159    virtual void DumpData(int indent) const = 0;
160    virtual void DumpLog(const std::string &prefix) const;
161};
162
163// define convert from linux/perf_event.h
164// description from https://man7.org/linux/man-pages/man2/perf_event_open.2.html
165
166constexpr __u64 SAMPLE_ID = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID |
167                            PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_IDENTIFIER;
168
169constexpr __u64 SAMPLE_TYPE = PERF_SAMPLE_IP | SAMPLE_ID | PERF_SAMPLE_PERIOD;
170
171constexpr __u32 MIN_SAMPLE_STACK_SIZE = 8;
172constexpr __u32 MAX_SAMPLE_STACK_SIZE = 65528;
173
174class PerfRecordAuxtrace : public PerfEventRecord {
175public:
176    PerfRecordAuxtraceData data_;
177    u8* rawData_ = nullptr;
178    explicit PerfRecordAuxtrace(uint8_t *p);
179    PerfRecordAuxtrace(u64 size, u64 offset, u64 reference, u32 idx, u32 tid, u32 cpu, u32 pid);
180
181    bool GetBinary1(std::vector<uint8_t> &buf) const;
182    bool GetBinary(std::vector<uint8_t> &buf) const override;
183    void DumpData(int indent) const override;
184    void DumpLog(const std::string &prefix) const override;
185
186    virtual size_t GetSize() const override;
187};
188
189class PerfRecordMmap : public PerfEventRecord {
190public:
191    PerfRecordMmapData data_;
192
193    explicit PerfRecordMmap(uint8_t *p);
194
195    PerfRecordMmap(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff,
196                   const std::string &filename);
197
198    bool GetBinary(std::vector<uint8_t> &buf) const override;
199    void DumpData(int indent) const override;
200    void DumpLog(const std::string &prefix) const override;
201};
202
203class PerfRecordMmap2 : public PerfEventRecord {
204public:
205    PerfRecordMmap2Data data_;
206
207    explicit PerfRecordMmap2(uint8_t *p);
208
209    PerfRecordMmap2(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff, u32 maj, u32 min,
210                    u64 ino, u32 prot, u32 flags, const std::string &filename);
211
212    PerfRecordMmap2(bool inKernel, u32 pid, u32 tid, std::shared_ptr<DfxMap> item);
213
214    bool GetBinary(std::vector<uint8_t> &buf) const override;
215    void DumpData(int indent) const override;
216    void DumpLog(const std::string &prefix) const override;
217    bool discard_ = false;
218};
219
220class PerfRecordLost : public PerfEventRecord {
221public:
222    PerfRecordLostData data_;
223
224    explicit PerfRecordLost(uint8_t *p);
225
226    bool GetBinary(std::vector<uint8_t> &buf) const override;
227    void DumpData(int indent) const override;
228
229    // only for UT
230    PerfRecordLost(bool inKernel, u64 id, u64 lost)
231        : PerfEventRecord(PERF_RECORD_LOST, inKernel, "lost")
232    {
233        data_.id = id;
234        data_.lost = lost;
235        header.size = sizeof(header) + sizeof(data_);
236    }
237};
238
239class PerfRecordComm : public PerfEventRecord {
240public:
241    PerfRecordCommData data_;
242
243    explicit PerfRecordComm(uint8_t *p);
244
245    PerfRecordComm(bool inKernel, u32 pid, u32 tid, const std::string &comm);
246
247    bool GetBinary(std::vector<uint8_t> &buf) const override;
248    void DumpData(int indent) const override;
249    void DumpLog(const std::string &prefix) const override;
250};
251
252class PerfRecordSample : public PerfEventRecord {
253public:
254    PerfRecordSampleData data_ = {};
255    uint64_t sampleType_ = SAMPLE_TYPE;
256    uint64_t skipKernel_ = 0;
257    uint64_t skipPid_ = 0;
258    // extend
259    // hold the new ips memory (after unwind)
260    // used for data_.ips replace (ReplaceWithCallStack)
261    static std::vector<u64> ips_;
262    static std::vector<DfxFrame> callFrames_;
263    static std::vector<pid_t> serverPidMap_;
264
265    StackId stackId_ {0};
266    bool removeStack_ {false};
267    inline static bool dumpRemoveStack_ {false};
268    // referenced input(p) in PerfRecordSample, require caller keep input(p) together
269    PerfRecordSample(uint8_t *p, const perf_event_attr &attr);
270    bool GetBinary(std::vector<uint8_t> &buf) const override;
271    void DumpData(int indent = 0) const override;
272    void DumpLog(const std::string &prefix) const override;
273
274    void RecoverCallStack();
275    // originalSize is use for expand callstack
276    void ReplaceWithCallStack(size_t originalSize = 0);
277    pid_t GetPid() const override;
278    void Clean();
279
280    // only for UT
281    PerfRecordSample(bool inKernel, u32 pid, u32 tid, u64 period = 0, u64 time = 0, u64 id = 0)
282        : PerfEventRecord(PERF_RECORD_SAMPLE, inKernel, "sample")
283    {
284        Clean();
285        data_.pid = pid;
286        data_.tid = tid;
287        data_.period = period;
288        data_.time = time;
289        data_.id = 0;
290        header.size = sizeof(header) + sizeof(data_);
291    };
292
293    pid_t GetUstackServerPid();
294    pid_t GetServerPidof(unsigned int ipNr);
295};
296
297class PerfRecordExit : public PerfEventRecord {
298public:
299    PerfRecordExitData data_;
300
301    explicit PerfRecordExit(uint8_t *p);
302
303    bool GetBinary(std::vector<uint8_t> &buf) const override;
304    void DumpData(int indent) const override;
305};
306
307class PerfRecordThrottle : public PerfEventRecord {
308public:
309    PerfRecordThrottleData data_;
310
311    PerfRecordThrottle(uint8_t *p);
312
313    bool GetBinary(std::vector<uint8_t> &buf) const override;
314    void DumpData(int indent) const override;
315};
316
317class PerfRecordUnthrottle : public PerfEventRecord {
318public:
319    PerfRecordThrottleData data_;
320
321    explicit PerfRecordUnthrottle(uint8_t *p);
322
323    bool GetBinary(std::vector<uint8_t> &buf) const override;
324    void DumpData(int indent) const override;
325};
326
327class PerfRecordFork : public PerfEventRecord {
328public:
329    PerfRecordForkData data_;
330
331    explicit PerfRecordFork(uint8_t *p);
332
333    bool GetBinary(std::vector<uint8_t> &buf) const override;
334    void DumpData(int indent) const override;
335};
336
337/*
338    This record indicates a read event.
339*/
340class PerfRecordRead : public PerfEventRecord {
341public:
342    PerfRecordReadData data_;
343
344    explicit PerfRecordRead(uint8_t *p);
345    bool GetBinary(std::vector<uint8_t> &buf) const override;
346    void DumpData(int indent) const override;
347};
348
349/*
350    This record reports that new data is available in the
351    separate AUX buffer region.
352
353    aux_offset
354            offset in the AUX mmap region where the new
355            data begins.
356    aux_size
357            size of the data made available.
358    flags  describes the AUX update.
359            PERF_AUX_FLAG_TRUNCATED
360                if set, then the data returned was
361                truncated to fit the available buffer
362                size.
363
364            PERF_AUX_FLAG_OVERWRITE
365                if set, then the data returned has
366                overwritten previous data.
367*/
368class PerfRecordAux : public PerfEventRecord {
369public:
370    uint64_t sampleType_ = SAMPLE_ID;
371    PerfRecordAuxData data_;
372
373    explicit PerfRecordAux(uint8_t *p);
374    bool GetBinary(std::vector<uint8_t> &buf) const override;
375    void DumpData(int indent) const override;
376};
377
378/*
379    This record indicates which process has initiated an
380    instruction trace event, allowing tools to properly
381    correlate the instruction addresses in the AUX buffer
382    with the proper executable.
383
384    pid    process ID of the thread starting an
385            instruction trace.
386    tid    thread ID of the thread starting an instruction
387            trace.
388*/
389class PerfRecordItraceStart : public PerfEventRecord {
390public:
391    PerfRecordItraceStartData data_;
392
393    explicit PerfRecordItraceStart(uint8_t *p);
394    bool GetBinary(std::vector<uint8_t> &buf) const override;
395    void DumpData(int indent) const override;
396};
397
398/*
399    When using hardware sampling (such as Intel PEBS) this
400    record indicates some number of samples that may have
401    been lost.
402*/
403class PerfRecordLostSamples : public PerfEventRecord {
404public:
405    PerfRecordLostSamplesData data_;
406
407    explicit PerfRecordLostSamples(uint8_t *p);
408    bool GetBinary(std::vector<uint8_t> &buf) const override;
409    void DumpData(int indent) const override;
410};
411
412/*
413    This record indicates a context switch has happened.
414    The PERF_RECORD_MISC_SWITCH_OUT bit in the misc field
415    indicates whether it was a context switch into or away
416    from the current process.
417*/
418class PerfRecordSwitch : public PerfEventRecord {
419public:
420    PerfRecordSwitchData data_;
421    explicit PerfRecordSwitch(uint8_t *p);
422    bool GetBinary(std::vector<uint8_t> &buf) const override;
423    void DumpData([[maybe_unused]] int indent) const override {};
424};
425
426/*
427    As with PERF_RECORD_SWITCH this record indicates a
428    context switch has happened, but it only occurs when
429    sampling in CPU-wide mode and provides additional
430    information on the process being switched to/from.
431    The PERF_RECORD_MISC_SWITCH_OUT bit in the misc field
432    indicates whether it was a context switch into or away
433    from the current process.
434
435    next_prev_pid
436            The process ID of the previous (if switching
437            in) or next (if switching out) process on the
438            CPU.
439
440    next_prev_tid
441            The thread ID of the previous (if switching in)
442            or next (if switching out) thread on the CPU.
443*/
444class PerfRecordSwitchCpuWide : public PerfEventRecord {
445public:
446    PerfRecordSwitchCpuWideData data_;
447    explicit PerfRecordSwitchCpuWide(uint8_t *p);
448    bool GetBinary(std::vector<uint8_t> &buf) const override;
449    void DumpData(int indent) const override;
450};
451
452std::unique_ptr<PerfEventRecord> GetPerfEventRecord(const int type, uint8_t *p,
453                                                    const perf_event_attr &attr);
454std::unique_ptr<PerfEventRecord> GetPerfSampleFromCache(const int type, uint8_t *p,
455                                                        const perf_event_attr &attr);
456std::unique_ptr<PerfEventRecord> GetPerfSampleFromCacheMain(const int type, uint8_t *p,
457                                                            const perf_event_attr &attr);
458
459template<typename T>
460void PushToBinary(bool condition, uint8_t *&p, const T &v);
461
462template<typename T1, typename T2>
463void PushToBinary2(bool condition, uint8_t *&p, const T1 &v1, const T2 &v2);
464
465template<typename T>
466void PopFromBinary(bool condition, uint8_t *&p, T &v);
467
468template<typename T1, typename T2>
469void PopFromBinary2(bool condition, uint8_t *&p, T1 &v1, T2 &v2);
470} // namespace HiPerf
471} // namespace Developtools
472} // namespace OHOS
473#endif // HIPERF_PERF_EVENT_RECORD_H
474