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