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