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