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