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