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