1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "record.h" 18 19 #include <inttypes.h> 20 #include <algorithm> 21 #include <unordered_map> 22 23 #include <android-base/logging.h> 24 #include <android-base/stringprintf.h> 25 26 #include "environment.h" 27 #include "perf_regs.h" 28 #include "utils.h" 29 30 static std::string RecordTypeToString(int record_type) { 31 static std::unordered_map<int, std::string> record_type_names = { 32 {PERF_RECORD_MMAP, "mmap"}, {PERF_RECORD_LOST, "lost"}, 33 {PERF_RECORD_COMM, "comm"}, {PERF_RECORD_EXIT, "exit"}, 34 {PERF_RECORD_THROTTLE, "throttle"}, {PERF_RECORD_UNTHROTTLE, "unthrottle"}, 35 {PERF_RECORD_FORK, "fork"}, {PERF_RECORD_READ, "read"}, 36 {PERF_RECORD_SAMPLE, "sample"}, {PERF_RECORD_BUILD_ID, "build_id"}, 37 {PERF_RECORD_MMAP2, "mmap2"}, 38 }; 39 40 auto it = record_type_names.find(record_type); 41 if (it != record_type_names.end()) { 42 return it->second; 43 } 44 return android::base::StringPrintf("unknown(%d)", record_type); 45 } 46 47 template <class T> 48 void MoveFromBinaryFormat(T* data_p, size_t n, const char*& p) { 49 size_t size = n * sizeof(T); 50 memcpy(data_p, p, size); 51 p += size; 52 } 53 54 template <class T> 55 void MoveToBinaryFormat(const T& data, char*& p) { 56 *reinterpret_cast<T*>(p) = data; 57 p += sizeof(T); 58 } 59 60 template <class T> 61 void MoveToBinaryFormat(const T* data_p, size_t n, char*& p) { 62 size_t size = n * sizeof(T); 63 memcpy(p, data_p, size); 64 p += size; 65 } 66 67 SampleId::SampleId() { 68 memset(this, 0, sizeof(SampleId)); 69 } 70 71 // Return sample_id size in binary format. 72 size_t SampleId::CreateContent(const perf_event_attr& attr) { 73 sample_id_all = attr.sample_id_all; 74 sample_type = attr.sample_type; 75 // Other data are not necessary. TODO: Set missing SampleId data. 76 return Size(); 77 } 78 79 void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end) { 80 sample_id_all = attr.sample_id_all; 81 sample_type = attr.sample_type; 82 if (sample_id_all) { 83 if (sample_type & PERF_SAMPLE_TID) { 84 MoveFromBinaryFormat(tid_data, p); 85 } 86 if (sample_type & PERF_SAMPLE_TIME) { 87 MoveFromBinaryFormat(time_data, p); 88 } 89 if (sample_type & PERF_SAMPLE_ID) { 90 MoveFromBinaryFormat(id_data, p); 91 } 92 if (sample_type & PERF_SAMPLE_STREAM_ID) { 93 MoveFromBinaryFormat(stream_id_data, p); 94 } 95 if (sample_type & PERF_SAMPLE_CPU) { 96 MoveFromBinaryFormat(cpu_data, p); 97 } 98 // TODO: Add parsing of PERF_SAMPLE_IDENTIFIER. 99 } 100 CHECK_LE(p, end); 101 if (p < end) { 102 LOG(DEBUG) << "Record SampleId part has " << end - p << " bytes left\n"; 103 } 104 } 105 106 void SampleId::WriteToBinaryFormat(char*& p) const { 107 if (sample_id_all) { 108 if (sample_type & PERF_SAMPLE_TID) { 109 MoveToBinaryFormat(tid_data, p); 110 } 111 if (sample_type & PERF_SAMPLE_TIME) { 112 MoveToBinaryFormat(time_data, p); 113 } 114 if (sample_type & PERF_SAMPLE_ID) { 115 MoveToBinaryFormat(id_data, p); 116 } 117 if (sample_type & PERF_SAMPLE_STREAM_ID) { 118 MoveToBinaryFormat(stream_id_data, p); 119 } 120 if (sample_type & PERF_SAMPLE_CPU) { 121 MoveToBinaryFormat(cpu_data, p); 122 } 123 } 124 } 125 126 void SampleId::Dump(size_t indent) const { 127 if (sample_id_all) { 128 if (sample_type & PERF_SAMPLE_TID) { 129 PrintIndented(indent, "sample_id: pid %u, tid %u\n", tid_data.pid, tid_data.tid); 130 } 131 if (sample_type & PERF_SAMPLE_TIME) { 132 PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time); 133 } 134 if (sample_type & PERF_SAMPLE_ID) { 135 PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", id_data.id); 136 } 137 if (sample_type & PERF_SAMPLE_STREAM_ID) { 138 PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", stream_id_data.stream_id); 139 } 140 if (sample_type & PERF_SAMPLE_CPU) { 141 PrintIndented(indent, "sample_id: cpu %u, res %u\n", cpu_data.cpu, cpu_data.res); 142 } 143 } 144 } 145 146 size_t SampleId::Size() const { 147 size_t size = 0; 148 if (sample_id_all) { 149 if (sample_type & PERF_SAMPLE_TID) { 150 size += sizeof(PerfSampleTidType); 151 } 152 if (sample_type & PERF_SAMPLE_TIME) { 153 size += sizeof(PerfSampleTimeType); 154 } 155 if (sample_type & PERF_SAMPLE_ID) { 156 size += sizeof(PerfSampleIdType); 157 } 158 if (sample_type & PERF_SAMPLE_STREAM_ID) { 159 size += sizeof(PerfSampleStreamIdType); 160 } 161 if (sample_type & PERF_SAMPLE_CPU) { 162 size += sizeof(PerfSampleCpuType); 163 } 164 } 165 return size; 166 } 167 168 Record::Record() { 169 memset(&header, 0, sizeof(header)); 170 } 171 172 Record::Record(const perf_event_header* pheader) { 173 header = *pheader; 174 } 175 176 void Record::Dump(size_t indent) const { 177 PrintIndented(indent, "record %s: type %u, misc %u, size %u\n", 178 RecordTypeToString(header.type).c_str(), header.type, header.misc, header.size); 179 DumpData(indent + 1); 180 sample_id.Dump(indent + 1); 181 } 182 183 uint64_t Record::Timestamp() const { 184 return sample_id.time_data.time; 185 } 186 187 MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader) 188 : Record(pheader) { 189 const char* p = reinterpret_cast<const char*>(pheader + 1); 190 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 191 MoveFromBinaryFormat(data, p); 192 filename = p; 193 p += ALIGN(filename.size() + 1, 8); 194 CHECK_LE(p, end); 195 sample_id.ReadFromBinaryFormat(attr, p, end); 196 } 197 198 std::vector<char> MmapRecord::BinaryFormat() const { 199 std::vector<char> buf(header.size); 200 char* p = buf.data(); 201 MoveToBinaryFormat(header, p); 202 MoveToBinaryFormat(data, p); 203 strcpy(p, filename.c_str()); 204 p += ALIGN(filename.size() + 1, 8); 205 sample_id.WriteToBinaryFormat(p); 206 return buf; 207 } 208 209 void MmapRecord::AdjustSizeBasedOnData() { 210 header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size(); 211 } 212 213 void MmapRecord::DumpData(size_t indent) const { 214 PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid, 215 data.tid, data.addr, data.len); 216 PrintIndented(indent, "pgoff 0x%" PRIx64 ", filename %s\n", data.pgoff, filename.c_str()); 217 } 218 219 Mmap2Record::Mmap2Record(const perf_event_attr& attr, const perf_event_header* pheader) 220 : Record(pheader) { 221 const char* p = reinterpret_cast<const char*>(pheader + 1); 222 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 223 MoveFromBinaryFormat(data, p); 224 filename = p; 225 p += ALIGN(filename.size() + 1, 8); 226 CHECK_LE(p, end); 227 sample_id.ReadFromBinaryFormat(attr, p, end); 228 } 229 230 std::vector<char> Mmap2Record::BinaryFormat() const { 231 std::vector<char> buf(header.size); 232 char* p = buf.data(); 233 MoveToBinaryFormat(header, p); 234 MoveToBinaryFormat(data, p); 235 strcpy(p, filename.c_str()); 236 p += ALIGN(filename.size() + 1, 8); 237 sample_id.WriteToBinaryFormat(p); 238 return buf; 239 } 240 241 void Mmap2Record::AdjustSizeBasedOnData() { 242 header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size(); 243 } 244 245 void Mmap2Record::DumpData(size_t indent) const { 246 PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid, 247 data.tid, data.addr, data.len); 248 PrintIndented(indent, 249 "pgoff 0x" PRIx64 ", maj %u, min %u, ino %" PRId64 ", ino_generation %" PRIu64 "\n", 250 data.pgoff, data.maj, data.min, data.ino, data.ino_generation); 251 PrintIndented(indent, "prot %u, flags %u, filenames %s\n", data.prot, data.flags, 252 filename.c_str()); 253 } 254 255 CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* pheader) 256 : Record(pheader) { 257 const char* p = reinterpret_cast<const char*>(pheader + 1); 258 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 259 MoveFromBinaryFormat(data, p); 260 comm = p; 261 p += ALIGN(strlen(p) + 1, 8); 262 CHECK_LE(p, end); 263 sample_id.ReadFromBinaryFormat(attr, p, end); 264 } 265 266 std::vector<char> CommRecord::BinaryFormat() const { 267 std::vector<char> buf(header.size); 268 char* p = buf.data(); 269 MoveToBinaryFormat(header, p); 270 MoveToBinaryFormat(data, p); 271 strcpy(p, comm.c_str()); 272 p += ALIGN(comm.size() + 1, 8); 273 sample_id.WriteToBinaryFormat(p); 274 return buf; 275 } 276 277 void CommRecord::DumpData(size_t indent) const { 278 PrintIndented(indent, "pid %u, tid %u, comm %s\n", data.pid, data.tid, comm.c_str()); 279 } 280 281 ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr, const perf_event_header* pheader) 282 : Record(pheader) { 283 const char* p = reinterpret_cast<const char*>(pheader + 1); 284 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 285 MoveFromBinaryFormat(data, p); 286 CHECK_LE(p, end); 287 sample_id.ReadFromBinaryFormat(attr, p, end); 288 } 289 290 std::vector<char> ExitOrForkRecord::BinaryFormat() const { 291 std::vector<char> buf(header.size); 292 char* p = buf.data(); 293 MoveToBinaryFormat(header, p); 294 MoveToBinaryFormat(data, p); 295 sample_id.WriteToBinaryFormat(p); 296 return buf; 297 } 298 299 void ExitOrForkRecord::DumpData(size_t indent) const { 300 PrintIndented(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data.pid, data.ppid, data.tid, 301 data.ptid); 302 } 303 304 SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader) 305 : Record(pheader) { 306 const char* p = reinterpret_cast<const char*>(pheader + 1); 307 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 308 sample_type = attr.sample_type; 309 310 if (sample_type & PERF_SAMPLE_IP) { 311 MoveFromBinaryFormat(ip_data, p); 312 } 313 if (sample_type & PERF_SAMPLE_TID) { 314 MoveFromBinaryFormat(tid_data, p); 315 } 316 if (sample_type & PERF_SAMPLE_TIME) { 317 MoveFromBinaryFormat(time_data, p); 318 } 319 if (sample_type & PERF_SAMPLE_ADDR) { 320 MoveFromBinaryFormat(addr_data, p); 321 } 322 if (sample_type & PERF_SAMPLE_ID) { 323 MoveFromBinaryFormat(id_data, p); 324 } 325 if (sample_type & PERF_SAMPLE_STREAM_ID) { 326 MoveFromBinaryFormat(stream_id_data, p); 327 } 328 if (sample_type & PERF_SAMPLE_CPU) { 329 MoveFromBinaryFormat(cpu_data, p); 330 } 331 if (sample_type & PERF_SAMPLE_PERIOD) { 332 MoveFromBinaryFormat(period_data, p); 333 } 334 if (sample_type & PERF_SAMPLE_CALLCHAIN) { 335 uint64_t nr; 336 MoveFromBinaryFormat(nr, p); 337 callchain_data.ips.resize(nr); 338 MoveFromBinaryFormat(callchain_data.ips.data(), nr, p); 339 } 340 if (sample_type & PERF_SAMPLE_RAW) { 341 uint32_t size; 342 MoveFromBinaryFormat(size, p); 343 raw_data.data.resize(size); 344 MoveFromBinaryFormat(raw_data.data.data(), size, p); 345 } 346 if (sample_type & PERF_SAMPLE_BRANCH_STACK) { 347 uint64_t nr; 348 MoveFromBinaryFormat(nr, p); 349 branch_stack_data.stack.resize(nr); 350 MoveFromBinaryFormat(branch_stack_data.stack.data(), nr, p); 351 } 352 if (sample_type & PERF_SAMPLE_REGS_USER) { 353 MoveFromBinaryFormat(regs_user_data.abi, p); 354 if (regs_user_data.abi == 0) { 355 regs_user_data.reg_mask = 0; 356 } else { 357 regs_user_data.reg_mask = attr.sample_regs_user; 358 size_t bit_nr = 0; 359 for (size_t i = 0; i < 64; ++i) { 360 if ((regs_user_data.reg_mask >> i) & 1) { 361 bit_nr++; 362 } 363 } 364 regs_user_data.regs.resize(bit_nr); 365 MoveFromBinaryFormat(regs_user_data.regs.data(), bit_nr, p); 366 } 367 } 368 if (sample_type & PERF_SAMPLE_STACK_USER) { 369 uint64_t size; 370 MoveFromBinaryFormat(size, p); 371 if (size == 0) { 372 stack_user_data.dyn_size = 0; 373 } else { 374 stack_user_data.data.resize(size); 375 MoveFromBinaryFormat(stack_user_data.data.data(), size, p); 376 MoveFromBinaryFormat(stack_user_data.dyn_size, p); 377 } 378 } 379 // TODO: Add parsing of other PERF_SAMPLE_*. 380 CHECK_LE(p, end); 381 if (p < end) { 382 LOG(DEBUG) << "Record has " << end - p << " bytes left\n"; 383 } 384 } 385 386 std::vector<char> SampleRecord::BinaryFormat() const { 387 std::vector<char> buf(header.size); 388 char* p = buf.data(); 389 MoveToBinaryFormat(header, p); 390 if (sample_type & PERF_SAMPLE_IP) { 391 MoveToBinaryFormat(ip_data, p); 392 } 393 if (sample_type & PERF_SAMPLE_TID) { 394 MoveToBinaryFormat(tid_data, p); 395 } 396 if (sample_type & PERF_SAMPLE_TIME) { 397 MoveToBinaryFormat(time_data, p); 398 } 399 if (sample_type & PERF_SAMPLE_ADDR) { 400 MoveToBinaryFormat(addr_data, p); 401 } 402 if (sample_type & PERF_SAMPLE_ID) { 403 MoveToBinaryFormat(id_data, p); 404 } 405 if (sample_type & PERF_SAMPLE_STREAM_ID) { 406 MoveToBinaryFormat(stream_id_data, p); 407 } 408 if (sample_type & PERF_SAMPLE_CPU) { 409 MoveToBinaryFormat(cpu_data, p); 410 } 411 if (sample_type & PERF_SAMPLE_PERIOD) { 412 MoveToBinaryFormat(period_data, p); 413 } 414 if (sample_type & PERF_SAMPLE_CALLCHAIN) { 415 uint64_t nr = callchain_data.ips.size(); 416 MoveToBinaryFormat(nr, p); 417 MoveToBinaryFormat(callchain_data.ips.data(), nr, p); 418 } 419 if (sample_type & PERF_SAMPLE_RAW) { 420 uint32_t size = raw_data.data.size(); 421 MoveToBinaryFormat(size, p); 422 MoveToBinaryFormat(raw_data.data.data(), size, p); 423 } 424 if (sample_type & PERF_SAMPLE_BRANCH_STACK) { 425 uint64_t nr = branch_stack_data.stack.size(); 426 MoveToBinaryFormat(nr, p); 427 MoveToBinaryFormat(branch_stack_data.stack.data(), nr, p); 428 } 429 if (sample_type & PERF_SAMPLE_REGS_USER) { 430 MoveToBinaryFormat(regs_user_data.abi, p); 431 if (regs_user_data.abi != 0) { 432 MoveToBinaryFormat(regs_user_data.regs.data(), regs_user_data.regs.size(), p); 433 } 434 } 435 if (sample_type & PERF_SAMPLE_STACK_USER) { 436 uint64_t size = stack_user_data.data.size(); 437 MoveToBinaryFormat(size, p); 438 if (size != 0) { 439 MoveToBinaryFormat(stack_user_data.data.data(), size, p); 440 MoveToBinaryFormat(stack_user_data.dyn_size, p); 441 } 442 } 443 444 // If record command does stack unwinding, sample records' size may be decreased. 445 // So we can't trust header.size here, and should adjust buffer size based on real need. 446 buf.resize(p - buf.data()); 447 return buf; 448 } 449 450 void SampleRecord::AdjustSizeBasedOnData() { 451 size_t size = BinaryFormat().size(); 452 LOG(DEBUG) << "Record (type " << RecordTypeToString(header.type) << ") size is changed from " 453 << header.size << " to " << size; 454 header.size = size; 455 } 456 457 void SampleRecord::DumpData(size_t indent) const { 458 PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type); 459 if (sample_type & PERF_SAMPLE_IP) { 460 PrintIndented(indent, "ip %p\n", reinterpret_cast<void*>(ip_data.ip)); 461 } 462 if (sample_type & PERF_SAMPLE_TID) { 463 PrintIndented(indent, "pid %u, tid %u\n", tid_data.pid, tid_data.tid); 464 } 465 if (sample_type & PERF_SAMPLE_TIME) { 466 PrintIndented(indent, "time %" PRId64 "\n", time_data.time); 467 } 468 if (sample_type & PERF_SAMPLE_ADDR) { 469 PrintIndented(indent, "addr %p\n", reinterpret_cast<void*>(addr_data.addr)); 470 } 471 if (sample_type & PERF_SAMPLE_ID) { 472 PrintIndented(indent, "id %" PRId64 "\n", id_data.id); 473 } 474 if (sample_type & PERF_SAMPLE_STREAM_ID) { 475 PrintIndented(indent, "stream_id %" PRId64 "\n", stream_id_data.stream_id); 476 } 477 if (sample_type & PERF_SAMPLE_CPU) { 478 PrintIndented(indent, "cpu %u, res %u\n", cpu_data.cpu, cpu_data.res); 479 } 480 if (sample_type & PERF_SAMPLE_PERIOD) { 481 PrintIndented(indent, "period %" PRId64 "\n", period_data.period); 482 } 483 if (sample_type & PERF_SAMPLE_CALLCHAIN) { 484 PrintIndented(indent, "callchain nr=%" PRIu64 "\n", callchain_data.ips.size()); 485 for (auto& ip : callchain_data.ips) { 486 PrintIndented(indent + 1, "0x%" PRIx64 "\n", ip); 487 } 488 } 489 if (sample_type & PERF_SAMPLE_RAW) { 490 PrintIndented(indent, "raw size=%zu\n", raw_data.data.size()); 491 const uint32_t* data = reinterpret_cast<const uint32_t*>(raw_data.data.data()); 492 size_t size = raw_data.data.size() / sizeof(uint32_t); 493 for (size_t i = 0; i < size; ++i) { 494 PrintIndented(indent + 1, "0x%08x (%zu)\n", data[i], data[i]); 495 } 496 } 497 if (sample_type & PERF_SAMPLE_BRANCH_STACK) { 498 PrintIndented(indent, "branch_stack nr=%" PRIu64 "\n", branch_stack_data.stack.size()); 499 for (auto& item : branch_stack_data.stack) { 500 PrintIndented(indent + 1, "from 0x%" PRIx64 ", to 0x%" PRIx64 ", flags 0x%" PRIx64 "\n", 501 item.from, item.to, item.flags); 502 } 503 } 504 if (sample_type & PERF_SAMPLE_REGS_USER) { 505 PrintIndented(indent, "user regs: abi=%" PRId64 "\n", regs_user_data.abi); 506 for (size_t i = 0, pos = 0; i < 64; ++i) { 507 if ((regs_user_data.reg_mask >> i) & 1) { 508 PrintIndented(indent + 1, "reg (%s) 0x%016" PRIx64 "\n", 509 GetRegName(i, ScopedCurrentArch::GetCurrentArch()).c_str(), 510 regs_user_data.regs[pos++]); 511 } 512 } 513 } 514 if (sample_type & PERF_SAMPLE_STACK_USER) { 515 PrintIndented(indent, "user stack: size %zu dyn_size %" PRIu64 "\n", 516 stack_user_data.data.size(), stack_user_data.dyn_size); 517 const uint64_t* p = reinterpret_cast<const uint64_t*>(stack_user_data.data.data()); 518 const uint64_t* end = p + (stack_user_data.data.size() / sizeof(uint64_t)); 519 while (p < end) { 520 PrintIndented(indent + 1, ""); 521 for (size_t i = 0; i < 4 && p < end; ++i, ++p) { 522 printf(" %016" PRIx64, *p); 523 } 524 printf("\n"); 525 } 526 printf("\n"); 527 } 528 } 529 530 uint64_t SampleRecord::Timestamp() const { 531 return time_data.time; 532 } 533 534 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) { 535 const char* p = reinterpret_cast<const char*>(pheader + 1); 536 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 537 MoveFromBinaryFormat(pid, p); 538 build_id = BuildId(p, BUILD_ID_SIZE); 539 p += ALIGN(build_id.Size(), 8); 540 filename = p; 541 p += ALIGN(filename.size() + 1, 64); 542 CHECK_EQ(p, end); 543 } 544 545 std::vector<char> BuildIdRecord::BinaryFormat() const { 546 std::vector<char> buf(header.size); 547 char* p = buf.data(); 548 MoveToBinaryFormat(header, p); 549 MoveToBinaryFormat(pid, p); 550 memcpy(p, build_id.Data(), build_id.Size()); 551 p += ALIGN(build_id.Size(), 8); 552 strcpy(p, filename.c_str()); 553 p += ALIGN(filename.size() + 1, 64); 554 return buf; 555 } 556 557 void BuildIdRecord::DumpData(size_t indent) const { 558 PrintIndented(indent, "pid %u\n", pid); 559 PrintIndented(indent, "build_id %s\n", build_id.ToString().c_str()); 560 PrintIndented(indent, "filename %s\n", filename.c_str()); 561 } 562 563 UnknownRecord::UnknownRecord(const perf_event_header* pheader) : Record(pheader) { 564 const char* p = reinterpret_cast<const char*>(pheader + 1); 565 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size; 566 data.insert(data.end(), p, end); 567 } 568 569 std::vector<char> UnknownRecord::BinaryFormat() const { 570 std::vector<char> buf(header.size); 571 char* p = buf.data(); 572 MoveToBinaryFormat(header, p); 573 MoveToBinaryFormat(data.data(), data.size(), p); 574 return buf; 575 } 576 577 void UnknownRecord::DumpData(size_t) const { 578 } 579 580 static std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr, 581 const perf_event_header* pheader) { 582 switch (pheader->type) { 583 case PERF_RECORD_MMAP: 584 return std::unique_ptr<Record>(new MmapRecord(attr, pheader)); 585 case PERF_RECORD_MMAP2: 586 return std::unique_ptr<Record>(new Mmap2Record(attr, pheader)); 587 case PERF_RECORD_COMM: 588 return std::unique_ptr<Record>(new CommRecord(attr, pheader)); 589 case PERF_RECORD_EXIT: 590 return std::unique_ptr<Record>(new ExitRecord(attr, pheader)); 591 case PERF_RECORD_FORK: 592 return std::unique_ptr<Record>(new ForkRecord(attr, pheader)); 593 case PERF_RECORD_SAMPLE: 594 return std::unique_ptr<Record>(new SampleRecord(attr, pheader)); 595 default: 596 return std::unique_ptr<Record>(new UnknownRecord(pheader)); 597 } 598 } 599 600 std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr& attr, 601 const char* buf, size_t buf_size) { 602 std::vector<std::unique_ptr<Record>> result; 603 const char* p = buf; 604 const char* end = buf + buf_size; 605 while (p < end) { 606 const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p); 607 CHECK_LE(p + header->size, end); 608 CHECK_NE(0u, header->size); 609 result.push_back(ReadRecordFromBuffer(attr, header)); 610 p += header->size; 611 } 612 return result; 613 } 614 615 std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp) { 616 std::vector<char> buf(sizeof(perf_event_header)); 617 perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]); 618 if (fread(header, sizeof(perf_event_header), 1, fp) != 1) { 619 PLOG(ERROR) << "Failed to read record file"; 620 return nullptr; 621 } 622 buf.resize(header->size); 623 header = reinterpret_cast<perf_event_header*>(&buf[0]); 624 if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, fp) != 1) { 625 PLOG(ERROR) << "Failed to read record file"; 626 return nullptr; 627 } 628 return ReadRecordFromBuffer(attr, header); 629 } 630 631 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid, 632 uint64_t addr, uint64_t len, uint64_t pgoff, 633 const std::string& filename) { 634 MmapRecord record; 635 record.header.type = PERF_RECORD_MMAP; 636 record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER); 637 record.data.pid = pid; 638 record.data.tid = tid; 639 record.data.addr = addr; 640 record.data.len = len; 641 record.data.pgoff = pgoff; 642 record.filename = filename; 643 size_t sample_id_size = record.sample_id.CreateContent(attr); 644 record.header.size = sizeof(record.header) + sizeof(record.data) + 645 ALIGN(record.filename.size() + 1, 8) + sample_id_size; 646 return record; 647 } 648 649 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, 650 const std::string& comm) { 651 CommRecord record; 652 record.header.type = PERF_RECORD_COMM; 653 record.header.misc = 0; 654 record.data.pid = pid; 655 record.data.tid = tid; 656 record.comm = comm; 657 size_t sample_id_size = record.sample_id.CreateContent(attr); 658 record.header.size = sizeof(record.header) + sizeof(record.data) + 659 ALIGN(record.comm.size() + 1, 8) + sample_id_size; 660 return record; 661 } 662 663 ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid, 664 uint32_t ptid) { 665 ForkRecord record; 666 record.header.type = PERF_RECORD_FORK; 667 record.header.misc = 0; 668 record.data.pid = pid; 669 record.data.ppid = ppid; 670 record.data.tid = tid; 671 record.data.ptid = ptid; 672 record.data.time = 0; 673 size_t sample_id_size = record.sample_id.CreateContent(attr); 674 record.header.size = sizeof(record.header) + sizeof(record.data) + sample_id_size; 675 return record; 676 } 677 678 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id, 679 const std::string& filename) { 680 BuildIdRecord record; 681 record.header.type = PERF_RECORD_BUILD_ID; 682 record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER); 683 record.pid = pid; 684 record.build_id = build_id; 685 record.filename = filename; 686 record.header.size = sizeof(record.header) + sizeof(record.pid) + 687 ALIGN(record.build_id.Size(), 8) + ALIGN(filename.size() + 1, 64); 688 return record; 689 } 690 691 bool RecordCache::RecordWithSeq::IsHappensBefore(const RecordWithSeq& other) const { 692 bool is_sample = (record->header.type == PERF_RECORD_SAMPLE); 693 bool is_other_sample = (other.record->header.type == PERF_RECORD_SAMPLE); 694 uint64_t time = record->Timestamp(); 695 uint64_t other_time = other.record->Timestamp(); 696 // The record with smaller time happens first. 697 if (time != other_time) { 698 return time < other_time; 699 } 700 // If happening at the same time, make non-sample records before sample records, 701 // because non-sample records may contain useful information to parse sample records. 702 if (is_sample != is_other_sample) { 703 return is_sample ? false : true; 704 } 705 // Otherwise, use the same order as they enter the cache. 706 return seq < other.seq; 707 } 708 709 bool RecordCache::RecordComparator::operator()(const RecordWithSeq& r1, 710 const RecordWithSeq& r2) { 711 return r2.IsHappensBefore(r1); 712 } 713 714 RecordCache::RecordCache(const perf_event_attr& attr, size_t min_cache_size, 715 uint64_t min_time_diff_in_ns) 716 : attr_(attr), 717 has_timestamp_(attr.sample_id_all && (attr.sample_type & PERF_SAMPLE_TIME)), 718 min_cache_size_(min_cache_size), 719 min_time_diff_in_ns_(min_time_diff_in_ns), 720 last_time_(0), 721 cur_seq_(0), 722 queue_(RecordComparator()) { 723 } 724 725 RecordCache::~RecordCache() { 726 PopAll(); 727 } 728 729 void RecordCache::Push(const char* data, size_t size) { 730 std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr_, data, size); 731 if (has_timestamp_) { 732 for (const auto& r : records) { 733 last_time_ = std::max(last_time_, r->Timestamp()); 734 } 735 } 736 for (auto& r : records) { 737 queue_.push(CreateRecordWithSeq(r.release())); 738 } 739 } 740 741 void RecordCache::Push(std::unique_ptr<Record> record) { 742 queue_.push(CreateRecordWithSeq(record.release())); 743 } 744 745 std::unique_ptr<Record> RecordCache::Pop() { 746 if (queue_.size() < min_cache_size_) { 747 return nullptr; 748 } 749 Record* r = queue_.top().record; 750 if (has_timestamp_) { 751 if (r->Timestamp() + min_time_diff_in_ns_ > last_time_) { 752 return nullptr; 753 } 754 } 755 queue_.pop(); 756 return std::unique_ptr<Record>(r); 757 } 758 759 std::vector<std::unique_ptr<Record>> RecordCache::PopAll() { 760 std::vector<std::unique_ptr<Record>> result; 761 while (!queue_.empty()) { 762 result.emplace_back(queue_.top().record); 763 queue_.pop(); 764 } 765 return result; 766 } 767 768 RecordCache::RecordWithSeq RecordCache::CreateRecordWithSeq(Record *r) { 769 RecordWithSeq result; 770 result.seq = cur_seq_++; 771 result.record = r; 772 return result; 773 } 774