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(const int indent = 0, std::string outputFilename = "", FILE *outputDump = nullptr) const; 126 virtual void DumpData(const int indent) const = 0; 127 virtual void DumpLog(const std::string& prefix) const; 128 129 protected: 130 void Init(const perf_event_type type, const bool inKernel); 131 void Init(const 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(const u64 size, const u64 offset, const u64 reference, const u32 idx, 184 const u32 tid, const u32 cpu, const u32 pid); 185 void Init(uint8_t* data, const perf_event_attr& attr = {}) override; 186 187 bool GetBinary1(std::vector<uint8_t> &buf) const; 188 bool GetBinary(std::vector<uint8_t> &buf) const override; 189 void DumpData(const int indent) const override; 190 void DumpLog(const std::string &prefix) const override; 191 192 size_t GetSize() const override; 193 }; 194 195 class PerfRecordAuxTraceInfo : public PerfEventRecordTemplate<PerfRecordAuxtraceInfoData, 196 PERF_RECORD_TYPE_AUXTRACEINFO> { 197 public: 198 PerfRecordAuxTraceInfo() = default; 199 bool GetBinary(std::vector<uint8_t> &buf) const override; 200 void DumpData(const int indent) const override; 201 }; 202 203 class PerfRecordTimeConv : public PerfEventRecordTemplate<PerfRecordTtimeConvData, PERF_RECORD_TYPE_TIMECONV> { 204 public: 205 PerfRecordTimeConv() = default; 206 bool GetBinary(std::vector<uint8_t> &buf) const override; 207 void DumpData(const int indent) const override; 208 }; 209 210 class PerfRecordCpuMap : public PerfEventRecordTemplate<PerfRecordCpuMapData, PERF_RECORD_TYPE_CPUMAP> { 211 public: 212 PerfRecordCpuMap() = default; 213 bool GetBinary(std::vector<uint8_t> &buf) const override; 214 void DumpData(const int indent) const override; 215 }; 216 217 class PerfRecordMmap : public PerfEventRecordTemplate<PerfRecordMmapData, PERF_RECORD_TYPE_MMAP> { 218 public: 219 PerfRecordMmap() = default; 220 void Init(uint8_t* data, const perf_event_attr& attr = {}) override; 221 PerfRecordMmap(bool inKernel, u32 pid, u32 tid, u64 addr, u64 len, u64 pgoff, 222 const std::string &filename); 223 224 bool GetBinary(std::vector<uint8_t> &buf) const override; 225 void DumpData(const int indent) const override; 226 void DumpLog(const std::string &prefix) const override; 227 }; 228 229 class PerfRecordMmap2 : public PerfEventRecordTemplate<PerfRecordMmap2Data, PERF_RECORD_TYPE_MMAP2> { 230 public: 231 232 PerfRecordMmap2() = default; 233 void Init(uint8_t* data, const perf_event_attr& attr = {}) override; 234 PerfRecordMmap2(const bool inKernel, const u32 pid, const u32 tid, const u64 addr, const u64 len, const u64 pgoff, 235 const u32 maj, const u32 min, u64 ino, u32 prot, u32 flags, const std::string &filename); 236 237 PerfRecordMmap2(const bool inKernel, const u32 pid, const u32 tid, std::shared_ptr<DfxMap> item); 238 239 bool GetBinary(std::vector<uint8_t> &buf) const override; 240 void DumpData(const int indent) const override; 241 void DumpLog(const std::string &prefix) const override; 242 bool discard_ = false; 243 }; 244 245 class PerfRecordLost : public PerfEventRecordTemplate<PerfRecordLostData, PERF_RECORD_TYPE_LOST> { 246 public: 247 248 PerfRecordLost() = default; 249 250 bool GetBinary(std::vector<uint8_t> &buf) const override; 251 void DumpData(const int indent) const override; 252 253 // only for UT PerfRecordLost(bool inKernel,u64 id,u64 lost)254 PerfRecordLost(bool inKernel, u64 id, u64 lost) 255 { 256 PerfEventRecord::Init(PERF_RECORD_LOST, inKernel); 257 data_.id = id; 258 data_.lost = lost; 259 header_.size = sizeof(header_) + sizeof(data_); 260 } 261 }; 262 263 class PerfRecordComm : public PerfEventRecordTemplate<PerfRecordCommData, PERF_RECORD_TYPE_COMM> { 264 public: 265 266 PerfRecordComm() = default; 267 void Init(uint8_t* data, const perf_event_attr& attr = {}) override; 268 PerfRecordComm(const bool inKernel, const u32 pid, const u32 tid, const std::string &comm); 269 270 bool GetBinary(std::vector<uint8_t> &buf) const override; 271 void DumpData(const int indent) const override; 272 void DumpLog(const std::string &prefix) const override; 273 }; 274 275 class PerfRecordSample : public PerfEventRecordTemplate<PerfRecordSampleData, PERF_RECORD_TYPE_SAMPLE> { 276 public: 277 uint64_t sampleType_ = SAMPLE_TYPE; 278 uint64_t skipKernel_ = 0; 279 uint64_t skipPid_ = 0; 280 // extend 281 // hold the new ips memory (after unwind) 282 // used for data_.ips replace (ReplaceWithCallStack) 283 static std::vector<u64> ips_; 284 static std::vector<DfxFrame> callFrames_; 285 static std::vector<pid_t> serverPidMap_; 286 287 PerfRecordSample() = default; 288 PerfRecordSample(const PerfRecordSample& sample); 289 // referenced input(p) in PerfRecordSample, require caller keep input(p) together 290 void Init(uint8_t* data, const perf_event_attr& attr) override; 291 292 StackId stackId_ {0}; 293 bool removeStack_ {false}; 294 static void SetDumpRemoveStack(bool dumpRemoveStack); 295 static bool IsDumpRemoveStack(); 296 bool GetBinary(std::vector<uint8_t> &buf) const override; 297 void DumpData(const int indent = 0) const override; 298 void DumpLog(const std::string &prefix) const override; 299 300 void RecoverCallStack(); 301 // originalSize is use for expand callstack 302 void ReplaceWithCallStack(const size_t originalSize = 0); 303 pid_t GetPid() const override; 304 uint64_t GetTime() const; 305 void Clean(); 306 307 // only for UT 308 PerfRecordSample(const bool inKernel, const u32 pid, const u32 tid, const u64 period = 0, 309 const u64 time = 0, const u64 id = 0) 310 { 311 PerfEventRecord::Init(PERF_RECORD_SAMPLE, inKernel); 312 Clean(); 313 data_.pid = pid; 314 data_.tid = tid; 315 data_.period = period; 316 data_.time = time; 317 data_.id = 0; 318 header_.size = sizeof(header_) + sizeof(data_); 319 } 320 321 pid_t GetUstackServerPid(); 322 pid_t GetServerPidof(unsigned int ipNr); 323 private: 324 static bool dumpRemoveStack_; 325 }; 326 327 class PerfRecordExit : public PerfEventRecordTemplate<PerfRecordExitData, PERF_RECORD_TYPE_EXIT> { 328 public: 329 PerfRecordExit() = default; 330 331 bool GetBinary(std::vector<uint8_t> &buf) const override; 332 void DumpData(const int indent) const override; 333 }; 334 335 class PerfRecordThrottle : public PerfEventRecordTemplate<PerfRecordThrottleData, PERF_RECORD_TYPE_THROTTLE> { 336 public: 337 PerfRecordThrottle() = default; 338 339 bool GetBinary(std::vector<uint8_t> &buf) const override; 340 void DumpData(const int indent) const override; 341 }; 342 343 class PerfRecordUnthrottle : public PerfEventRecordTemplate<PerfRecordThrottleData, PERF_RECORD_TYPE_UNTHROTTLE> { 344 public: 345 PerfRecordUnthrottle() = default; 346 347 bool GetBinary(std::vector<uint8_t> &buf) const override; 348 void DumpData(const int indent) const override; 349 }; 350 351 class PerfRecordFork : public PerfEventRecordTemplate<PerfRecordForkData, PERF_RECORD_TYPE_FORK> { 352 public: 353 PerfRecordFork() = default; 354 355 bool GetBinary(std::vector<uint8_t> &buf) const override; 356 void DumpData(const int indent) const override; 357 }; 358 359 /* 360 This record indicates a read event. 361 */ 362 class PerfRecordRead : public PerfEventRecordTemplate<PerfRecordReadData, PERF_RECORD_TYPE_READ> { 363 public: 364 PerfRecordRead() = default; 365 366 bool GetBinary(std::vector<uint8_t> &buf) const override; 367 void DumpData(const int indent) const override; 368 }; 369 370 /* 371 This record reports that new data is available in the 372 separate AUX buffer region. 373 374 aux_offset 375 offset in the AUX mmap region where the new 376 data begins. 377 aux_size 378 size of the data made available. 379 flags describes the AUX update. 380 PERF_AUX_FLAG_TRUNCATED 381 if set, then the data returned was 382 truncated to fit the available buffer 383 size. 384 385 PERF_AUX_FLAG_OVERWRITE 386 if set, then the data returned has 387 overwritten previous data. 388 */ 389 class PerfRecordAux : public PerfEventRecordTemplate<PerfRecordAuxData, PERF_RECORD_TYPE_AUX> { 390 public: 391 uint64_t sampleType_ = SAMPLE_ID; 392 PerfRecordAux() = default; 393 394 bool GetBinary(std::vector<uint8_t> &buf) const override; 395 void DumpData(const int indent) const override; 396 }; 397 398 /* 399 This record indicates which process has initiated an 400 instruction trace event, allowing tools to properly 401 correlate the instruction addresses in the AUX buffer 402 with the proper executable. 403 404 pid process ID of the thread starting an 405 instruction trace. 406 tid thread ID of the thread starting an instruction 407 trace. 408 */ 409 class PerfRecordItraceStart : public PerfEventRecordTemplate<PerfRecordItraceStartData, PERF_RECORD_TYPE_ITRACESTART> { 410 public: 411 PerfRecordItraceStart() = default; 412 413 bool GetBinary(std::vector<uint8_t> &buf) const override; 414 void DumpData(const int indent) const override; 415 }; 416 417 /* 418 When using hardware sampling (such as Intel PEBS) this 419 record indicates some number of samples that may have 420 been lost. 421 */ 422 class PerfRecordLostSamples : public PerfEventRecordTemplate<PerfRecordLostSamplesData, PERF_RECORD_TYPE_LOSTSAMPLE> { 423 public: 424 PerfRecordLostSamples() = default; 425 426 bool GetBinary(std::vector<uint8_t> &buf) const override; 427 void DumpData(const int indent) const override; 428 }; 429 430 /* 431 This record indicates a context switch has happened. 432 The PERF_RECORD_MISC_SWITCH_OUT bit in the misc field 433 indicates whether it was a context switch into or away 434 from the current process. 435 */ 436 class PerfRecordSwitch : public PerfEventRecordTemplate<PerfRecordSwitchData, PERF_RECORD_TYPE_SWITCH> { 437 public: 438 PerfRecordSwitch() = default; 439 440 bool GetBinary(std::vector<uint8_t> &buf) const override; DumpData(const int)441 void DumpData(const int) const override {}; 442 }; 443 444 /* 445 As with PERF_RECORD_SWITCH this record indicates a 446 context switch has happened, but it only occurs when 447 sampling in CPU-wide mode and provides additional 448 information on the process being switched to/from. 449 The PERF_RECORD_MISC_SWITCH_OUT bit in the misc field 450 indicates whether it was a context switch into or away 451 from the current process. 452 453 next_prev_pid 454 The process ID of the previous (if switching 455 in) or next (if switching out) process on the 456 CPU. 457 458 next_prev_tid 459 The thread ID of the previous (if switching in) 460 or next (if switching out) thread on the CPU. 461 */ 462 class PerfRecordSwitchCpuWide 463 : public PerfEventRecordTemplate<PerfRecordSwitchCpuWideData, PERF_RECORD_TYPE_SWITCHCPUWIDE> { 464 public: 465 PerfRecordSwitchCpuWide() = default; 466 467 bool GetBinary(std::vector<uint8_t> &buf) const override; 468 void DumpData(const int indent) const override; 469 }; 470 471 class PerfRecordNull : public PerfEventRecordTemplate<PerfRecordSwitchCpuWideData, PERF_RECORD_TYPE_NULL> { 472 public: 473 PerfRecordNull() = default; 474 GetBinary(std::vector<uint8_t> &)475 bool GetBinary(std::vector<uint8_t>&) const override { return false; }; DumpData(const int indent)476 void DumpData(const int indent) const override {}; 477 }; 478 479 class PerfEventRecordFactory { 480 public: 481 static PerfEventRecord& GetPerfEventRecord(PerfRecordType type, uint8_t* data, 482 const perf_event_attr& attr); 483 private: 484 static thread_local std::unordered_map<PerfRecordType, PerfEventRecord*> recordMap_; 485 }; 486 487 template<typename T> 488 void PushToBinary(const bool condition, uint8_t *&p, const T &v); 489 490 template<typename T1, typename T2> 491 void PushToBinary2(const bool condition, uint8_t *&p, const T1 &v1, const T2 &v2); 492 493 template<typename T> 494 void PopFromBinary(const bool condition, uint8_t *&p, T &v); 495 496 template<typename T1, typename T2> 497 void PopFromBinary2(const bool condition, uint8_t *&p, T1 &v1, T2 &v2); 498 } // namespace HiPerf 499 } // namespace Developtools 500 } // namespace OHOS 501 #endif // HIPERF_PERF_EVENT_RECORD_H 502