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