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 
38 namespace OHOS {
39 namespace Developtools {
40 namespace HiPerf {
41 using namespace OHOS::HiviewDFX;
42 
43 static constexpr uint32_t RECORD_SIZE_LIMIT = 65535;
44 static constexpr uint32_t RECORD_SIZE_LIMIT_SPE = 524288; // auxMmapPages_ * pageSize_
45 
46 enum perf_event_hiperf_ext_type {
47     PERF_RECORD_AUXTRACE = 71,
48     PERF_RECORD_HIPERF_CALLSTACK = UINT32_MAX / 2,
49 };
50 
51 struct 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 
CallFrameOHOS::Developtools::HiPerf::CallFrame61     CallFrame(uint64_t ip, uint64_t sp = 0) : ip_(ip), sp_(sp) {}
62 
63     // this is for ut test
CallFrameOHOS::Developtools::HiPerf::CallFrame64     CallFrame(uint64_t ip, uint64_t vaddrInFile, const char *name, const char *filePath)
65         : ip_(ip), vaddrInFile_(vaddrInFile), symbolName_(name), filePath_(filePath)
66     {
67     }
operator ==OHOS::Developtools::HiPerf::CallFrame68     bool operator==(const CallFrame &b) const
69     {
70         return (ip_ == b.ip_) && (sp_ == b.sp_);
71     }
operator !=OHOS::Developtools::HiPerf::CallFrame72     bool operator!=(const CallFrame &b) const
73     {
74         return (ip_ != b.ip_) || (sp_ != b.sp_);
75     }
ToStringOHOS::Developtools::HiPerf::CallFrame76     std::string ToString() const
77     {
78         return StringPrintf("ip: 0x%016llx sp: 0x%016llx", ip_, sp_);
79     }
ToSymbolStringOHOS::Developtools::HiPerf::CallFrame80     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 
99 struct AttrWithId {
100     perf_event_attr attr;
101     std::vector<uint64_t> ids;
102     std::string name; // will be empty in GetAttrSection
103 };
104 
105 class PerfEventRecord {
106 public:
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 
~PerfEventRecord()118     virtual ~PerfEventRecord() {}
119 
GetSize() const120     virtual size_t GetSize() const
121     {
122         return header.size;
123     };
GetHeaderSize() const124     size_t GetHeaderSize() const
125     {
126         return sizeof(header);
127     };
128     void GetHeaderBinary(std::vector<uint8_t> &buf) const;
129 
GetType() const130     uint32_t GetType() const
131     {
132         return header.type;
133     };
GetMisc() const134     uint16_t GetMisc() const
135     {
136         return header.misc;
137     };
inKernel()138     bool inKernel()
139     {
140         return header.misc & PERF_RECORD_MISC_KERNEL;
141     }
inUser()142     bool inUser()
143     {
144         return header.misc & PERF_RECORD_MISC_USER;
145     }
GetName() const146     const std::string &GetName() const
147     {
148         return name_;
149     };
150 
151     // to support --exclude-hiperf, return sample_id.pid to filter record,
GetPid() const152     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 
166 constexpr __u64 SAMPLE_ID = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID |
167                             PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_IDENTIFIER;
168 
169 constexpr __u64 SAMPLE_TYPE = PERF_SAMPLE_IP | SAMPLE_ID | PERF_SAMPLE_PERIOD;
170 
171 constexpr __u32 MIN_SAMPLE_STACK_SIZE = 8;
172 constexpr __u32 MAX_SAMPLE_STACK_SIZE = 65528;
173 
174 class PerfRecordAuxtrace : public PerfEventRecord {
175 public:
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 
189 class PerfRecordMmap : public PerfEventRecord {
190 public:
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 
203 class PerfRecordMmap2 : public PerfEventRecord {
204 public:
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 
220 class PerfRecordLost : public PerfEventRecord {
221 public:
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
PerfRecordLost(bool inKernel, u64 id, u64 lost)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 
239 class PerfRecordComm : public PerfEventRecord {
240 public:
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 
252 class PerfRecordSample : public PerfEventRecord {
253 public:
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
PerfRecordSample(bool inKernel, u32 pid, u32 tid, u64 period = 0, u64 time = 0, u64 id = 0)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 
297 class PerfRecordExit : public PerfEventRecord {
298 public:
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 
307 class PerfRecordThrottle : public PerfEventRecord {
308 public:
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 
317 class PerfRecordUnthrottle : public PerfEventRecord {
318 public:
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 
327 class PerfRecordFork : public PerfEventRecord {
328 public:
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 */
340 class PerfRecordRead : public PerfEventRecord {
341 public:
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 */
368 class PerfRecordAux : public PerfEventRecord {
369 public:
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 */
389 class PerfRecordItraceStart : public PerfEventRecord {
390 public:
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 */
403 class PerfRecordLostSamples : public PerfEventRecord {
404 public:
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 */
418 class PerfRecordSwitch : public PerfEventRecord {
419 public:
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 */
444 class PerfRecordSwitchCpuWide : public PerfEventRecord {
445 public:
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 
452 std::unique_ptr<PerfEventRecord> GetPerfEventRecord(const int type, uint8_t *p,
453                                                     const perf_event_attr &attr);
454 std::unique_ptr<PerfEventRecord> GetPerfSampleFromCache(const int type, uint8_t *p,
455                                                         const perf_event_attr &attr);
456 std::unique_ptr<PerfEventRecord> GetPerfSampleFromCacheMain(const int type, uint8_t *p,
457                                                             const perf_event_attr &attr);
458 
459 template<typename T>
460 void PushToBinary(bool condition, uint8_t *&p, const T &v);
461 
462 template<typename T1, typename T2>
463 void PushToBinary2(bool condition, uint8_t *&p, const T1 &v1, const T2 &v2);
464 
465 template<typename T>
466 void PopFromBinary(bool condition, uint8_t *&p, T &v);
467 
468 template<typename T1, typename T2>
469 void 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