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