1 /* 2 * Copyright (C) 2016 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 <inttypes.h> 18 19 #include <memory> 20 21 #include "system/extras/simpleperf/report_sample.pb.h" 22 23 #include <google/protobuf/io/coded_stream.h> 24 #include <google/protobuf/io/zero_copy_stream_impl_lite.h> 25 26 #include "command.h" 27 #include "event_attr.h" 28 #include "event_type.h" 29 #include "record_file.h" 30 #include "thread_tree.h" 31 #include "utils.h" 32 33 namespace proto = simpleperf_report_proto; 34 35 namespace { 36 37 class ProtobufFileWriter : public google::protobuf::io::CopyingOutputStream { 38 public: 39 explicit ProtobufFileWriter(FILE* out_fp) : out_fp_(out_fp) {} 40 41 bool Write(const void* buffer, int size) override { 42 return fwrite(buffer, size, 1, out_fp_) == 1; 43 } 44 45 private: 46 FILE* out_fp_; 47 }; 48 49 class ProtobufFileReader : public google::protobuf::io::CopyingInputStream { 50 public: 51 explicit ProtobufFileReader(FILE* in_fp) : in_fp_(in_fp) {} 52 53 int Read(void* buffer, int size) override { 54 return fread(buffer, 1, size, in_fp_); 55 } 56 57 private: 58 FILE* in_fp_; 59 }; 60 61 class ReportSampleCommand : public Command { 62 public: 63 ReportSampleCommand() 64 : Command( 65 "report-sample", "report raw sample information in perf.data", 66 // clang-format off 67 "Usage: simpleperf report-sample [options]\n" 68 "--dump-protobuf-report <file>\n" 69 " Dump report file generated by\n" 70 " `simpleperf report-sample --protobuf -o <file>`.\n" 71 "-i <file> Specify path of record file, default is perf.data.\n" 72 "-o report_file_name Set report file name, default is stdout.\n" 73 "--protobuf Use protobuf format in report_sample.proto to output samples.\n" 74 " Need to set a report_file_name when using this option.\n" 75 "--show-callchain Print callchain samples.\n" 76 // clang-format on 77 ), 78 record_filename_("perf.data"), 79 show_callchain_(false), 80 use_protobuf_(false), 81 report_fp_(nullptr), 82 coded_os_(nullptr), 83 sample_count_(0), 84 lost_count_(0), 85 trace_offcpu_(false) {} 86 87 bool Run(const std::vector<std::string>& args) override; 88 89 private: 90 bool ParseOptions(const std::vector<std::string>& args); 91 bool DumpProtobufReport(const std::string& filename); 92 bool OpenRecordFile(); 93 bool PrintMetaInfo(); 94 bool ProcessRecord(std::unique_ptr<Record> record); 95 bool PrintSampleRecordInProtobuf(const SampleRecord& record); 96 bool GetCallEntry(const ThreadEntry* thread, bool in_kernel, uint64_t ip, bool omit_unknown_dso, 97 uint64_t* pvaddr_in_file, Dso** pdso, const Symbol** psymbol); 98 bool WriteRecordInProtobuf(proto::Record& proto_record); 99 bool PrintLostSituationInProtobuf(); 100 bool PrintFileInfoInProtobuf(); 101 bool PrintThreadInfoInProtobuf(); 102 bool PrintSampleRecord(const SampleRecord& record); 103 void PrintLostSituation(); 104 105 std::string record_filename_; 106 std::unique_ptr<RecordFileReader> record_file_reader_; 107 std::string dump_protobuf_report_file_; 108 bool show_callchain_; 109 bool use_protobuf_; 110 ThreadTree thread_tree_; 111 std::string report_filename_; 112 FILE* report_fp_; 113 google::protobuf::io::CodedOutputStream* coded_os_; 114 size_t sample_count_; 115 size_t lost_count_; 116 bool trace_offcpu_; 117 std::unique_ptr<ScopedEventTypes> scoped_event_types_; 118 std::vector<std::string> event_types_; 119 }; 120 121 bool ReportSampleCommand::Run(const std::vector<std::string>& args) { 122 // 1. Parse options. 123 if (!ParseOptions(args)) { 124 return false; 125 } 126 // 2. Prepare report fp. 127 report_fp_ = stdout; 128 std::unique_ptr<FILE, decltype(&fclose)> fp(nullptr, fclose); 129 if (!report_filename_.empty()) { 130 const char* open_mode = "w"; 131 if (!dump_protobuf_report_file_.empty() && use_protobuf_) { 132 open_mode = "wb"; 133 } 134 fp.reset(fopen(report_filename_.c_str(), open_mode)); 135 if (fp == nullptr) { 136 PLOG(ERROR) << "failed to open " << report_filename_; 137 return false; 138 } 139 report_fp_ = fp.get(); 140 } 141 142 // 3. Dump protobuf report. 143 if (!dump_protobuf_report_file_.empty()) { 144 return DumpProtobufReport(dump_protobuf_report_file_); 145 } 146 147 // 4. Open record file. 148 if (!OpenRecordFile()) { 149 return false; 150 } 151 if (use_protobuf_) { 152 GOOGLE_PROTOBUF_VERIFY_VERSION; 153 } else { 154 thread_tree_.ShowMarkForUnknownSymbol(); 155 thread_tree_.ShowIpForUnknownSymbol(); 156 } 157 158 // 5. Prepare protobuf output stream. 159 std::unique_ptr<ProtobufFileWriter> protobuf_writer; 160 std::unique_ptr<google::protobuf::io::CopyingOutputStreamAdaptor> protobuf_os; 161 std::unique_ptr<google::protobuf::io::CodedOutputStream> protobuf_coded_os; 162 if (use_protobuf_) { 163 protobuf_writer.reset(new ProtobufFileWriter(report_fp_)); 164 protobuf_os.reset(new google::protobuf::io::CopyingOutputStreamAdaptor( 165 protobuf_writer.get())); 166 protobuf_coded_os.reset( 167 new google::protobuf::io::CodedOutputStream(protobuf_os.get())); 168 coded_os_ = protobuf_coded_os.get(); 169 } 170 171 // 6. Read record file, and print samples online. 172 if (!PrintMetaInfo()) { 173 return false; 174 } 175 if (!record_file_reader_->ReadDataSection( 176 [this](std::unique_ptr<Record> record) { 177 return ProcessRecord(std::move(record)); 178 })) { 179 return false; 180 } 181 182 if (use_protobuf_) { 183 if (!PrintLostSituationInProtobuf()) { 184 return false; 185 } 186 if (!PrintFileInfoInProtobuf()) { 187 return false; 188 } 189 if (!PrintThreadInfoInProtobuf()) { 190 return false; 191 } 192 coded_os_->WriteLittleEndian32(0); 193 if (coded_os_->HadError()) { 194 LOG(ERROR) << "print protobuf report failed"; 195 return false; 196 } 197 protobuf_coded_os.reset(nullptr); 198 } else { 199 PrintLostSituation(); 200 fflush(report_fp_); 201 } 202 if (ferror(report_fp_) != 0) { 203 PLOG(ERROR) << "print report failed"; 204 return false; 205 } 206 return true; 207 } 208 209 bool ReportSampleCommand::ParseOptions(const std::vector<std::string>& args) { 210 for (size_t i = 0; i < args.size(); ++i) { 211 if (args[i] == "--dump-protobuf-report") { 212 if (!NextArgumentOrError(args, &i)) { 213 return false; 214 } 215 dump_protobuf_report_file_ = args[i]; 216 } else if (args[i] == "-i") { 217 if (!NextArgumentOrError(args, &i)) { 218 return false; 219 } 220 record_filename_ = args[i]; 221 } else if (args[i] == "-o") { 222 if (!NextArgumentOrError(args, &i)) { 223 return false; 224 } 225 report_filename_ = args[i]; 226 } else if (args[i] == "--protobuf") { 227 use_protobuf_ = true; 228 } else if (args[i] == "--show-callchain") { 229 show_callchain_ = true; 230 } else { 231 ReportUnknownOption(args, i); 232 return false; 233 } 234 } 235 236 if (use_protobuf_ && report_filename_.empty()) { 237 LOG(ERROR) << "please specify a report filename to write protobuf data"; 238 return false; 239 } 240 return true; 241 } 242 243 bool ReportSampleCommand::DumpProtobufReport(const std::string& filename) { 244 GOOGLE_PROTOBUF_VERIFY_VERSION; 245 std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(filename.c_str(), "rb"), 246 fclose); 247 if (fp == nullptr) { 248 PLOG(ERROR) << "failed to open " << filename; 249 return false; 250 } 251 ProtobufFileReader protobuf_reader(fp.get()); 252 google::protobuf::io::CopyingInputStreamAdaptor adaptor(&protobuf_reader); 253 google::protobuf::io::CodedInputStream coded_is(&adaptor); 254 // map from file_id to max_symbol_id requested on the file. 255 std::unordered_map<uint32_t, int32_t> max_symbol_id_map; 256 // files[file_id] is the number of symbols in the file. 257 std::vector<uint32_t> files; 258 uint32_t max_message_size = 64 * (1 << 20); 259 uint32_t warning_message_size = 512 * (1 << 20); 260 coded_is.SetTotalBytesLimit(max_message_size, warning_message_size); 261 while (true) { 262 uint32_t size; 263 if (!coded_is.ReadLittleEndian32(&size)) { 264 PLOG(ERROR) << "failed to read " << filename; 265 return false; 266 } 267 if (size == 0) { 268 break; 269 } 270 // Handle files having large symbol table. 271 if (size > max_message_size) { 272 max_message_size = size; 273 coded_is.SetTotalBytesLimit(max_message_size, warning_message_size); 274 } 275 auto limit = coded_is.PushLimit(size); 276 proto::Record proto_record; 277 if (!proto_record.ParseFromCodedStream(&coded_is)) { 278 PLOG(ERROR) << "failed to read " << filename; 279 return false; 280 } 281 coded_is.PopLimit(limit); 282 if (proto_record.has_sample()) { 283 auto& sample = proto_record.sample(); 284 static size_t sample_count = 0; 285 FprintIndented(report_fp_, 0, "sample %zu:\n", ++sample_count); 286 FprintIndented(report_fp_, 1, "event_type_id: %zu\n", sample.event_type_id()); 287 FprintIndented(report_fp_, 1, "time: %" PRIu64 "\n", sample.time()); 288 FprintIndented(report_fp_, 1, "event_count: %" PRIu64 "\n", sample.event_count()); 289 FprintIndented(report_fp_, 1, "thread_id: %d\n", sample.thread_id()); 290 FprintIndented(report_fp_, 1, "callchain:\n"); 291 for (int i = 0; i < sample.callchain_size(); ++i) { 292 const proto::Sample_CallChainEntry& callchain = sample.callchain(i); 293 FprintIndented(report_fp_, 2, "vaddr_in_file: %" PRIx64 "\n", 294 callchain.vaddr_in_file()); 295 FprintIndented(report_fp_, 2, "file_id: %u\n", callchain.file_id()); 296 int32_t symbol_id = callchain.symbol_id(); 297 FprintIndented(report_fp_, 2, "symbol_id: %d\n", symbol_id); 298 if (symbol_id < -1) { 299 LOG(ERROR) << "unexpected symbol_id " << symbol_id; 300 return false; 301 } 302 if (symbol_id != -1) { 303 max_symbol_id_map[callchain.file_id()] = 304 std::max(max_symbol_id_map[callchain.file_id()], symbol_id); 305 } 306 } 307 } else if (proto_record.has_lost()) { 308 auto& lost = proto_record.lost(); 309 FprintIndented(report_fp_, 0, "lost_situation:\n"); 310 FprintIndented(report_fp_, 1, "sample_count: %" PRIu64 "\n", 311 lost.sample_count()); 312 FprintIndented(report_fp_, 1, "lost_count: %" PRIu64 "\n", 313 lost.lost_count()); 314 } else if (proto_record.has_file()) { 315 auto& file = proto_record.file(); 316 FprintIndented(report_fp_, 0, "file:\n"); 317 FprintIndented(report_fp_, 1, "id: %u\n", file.id()); 318 FprintIndented(report_fp_, 1, "path: %s\n", file.path().c_str()); 319 for (int i = 0; i < file.symbol_size(); ++i) { 320 FprintIndented(report_fp_, 1, "symbol: %s\n", file.symbol(i).c_str()); 321 } 322 if (file.id() != files.size()) { 323 LOG(ERROR) << "file id doesn't increase orderly, expected " 324 << files.size() << ", really " << file.id(); 325 return false; 326 } 327 files.push_back(file.symbol_size()); 328 } else if (proto_record.has_thread()) { 329 auto& thread = proto_record.thread(); 330 FprintIndented(report_fp_, 0, "thread:\n"); 331 FprintIndented(report_fp_, 1, "thread_id: %u\n", thread.thread_id()); 332 FprintIndented(report_fp_, 1, "process_id: %u\n", thread.process_id()); 333 FprintIndented(report_fp_, 1, "thread_name: %s\n", thread.thread_name().c_str()); 334 } else if (proto_record.has_meta_info()) { 335 auto& meta_info = proto_record.meta_info(); 336 FprintIndented(report_fp_, 0, "meta_info:\n"); 337 for (int i = 0; i < meta_info.event_type_size(); ++i) { 338 FprintIndented(report_fp_, 1, "event_type: %s\n", meta_info.event_type(i).c_str()); 339 } 340 } else { 341 LOG(ERROR) << "unexpected record type "; 342 return false; 343 } 344 } 345 for (auto pair : max_symbol_id_map) { 346 if (pair.first >= files.size()) { 347 LOG(ERROR) << "file_id(" << pair.first << ") >= file count (" 348 << files.size() << ")"; 349 return false; 350 } 351 if (static_cast<uint32_t>(pair.second) >= files[pair.first]) { 352 LOG(ERROR) << "symbol_id(" << pair.second << ") >= symbol count (" 353 << files[pair.first] << ") in file_id( " << pair.first << ")"; 354 return false; 355 } 356 } 357 return true; 358 } 359 360 bool ReportSampleCommand::OpenRecordFile() { 361 record_file_reader_ = RecordFileReader::CreateInstance(record_filename_); 362 if (record_file_reader_ == nullptr) { 363 return false; 364 } 365 record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_); 366 if (record_file_reader_->HasFeature(PerfFileFormat::FEAT_META_INFO)) { 367 std::unordered_map<std::string, std::string> meta_info; 368 if (!record_file_reader_->ReadMetaInfoFeature(&meta_info)) { 369 return false; 370 } 371 auto it = meta_info.find("event_type_info"); 372 if (it != meta_info.end()) { 373 scoped_event_types_.reset(new ScopedEventTypes(it->second)); 374 } 375 it = meta_info.find("trace_offcpu"); 376 if (it != meta_info.end()) { 377 trace_offcpu_ = it->second == "true"; 378 } 379 } 380 for (EventAttrWithId& attr : record_file_reader_->AttrSection()) { 381 event_types_.push_back(GetEventNameByAttr(*attr.attr)); 382 } 383 return true; 384 } 385 386 bool ReportSampleCommand::PrintMetaInfo() { 387 if (use_protobuf_) { 388 proto::Record proto_record; 389 proto::MetaInfo* meta_info = proto_record.mutable_meta_info(); 390 for (auto& event_type : event_types_) { 391 *(meta_info->add_event_type()) = event_type; 392 } 393 return WriteRecordInProtobuf(proto_record); 394 } 395 FprintIndented(report_fp_, 0, "meta_info:\n"); 396 FprintIndented(report_fp_, 1, "trace_offcpu: %s\n", trace_offcpu_ ? "true" : "false"); 397 for (auto& event_type : event_types_) { 398 FprintIndented(report_fp_, 1, "event_type: %s\n", event_type.c_str()); 399 } 400 return true; 401 } 402 403 bool ReportSampleCommand::ProcessRecord(std::unique_ptr<Record> record) { 404 thread_tree_.Update(*record); 405 if (record->type() == PERF_RECORD_SAMPLE) { 406 sample_count_++; 407 auto& r = *static_cast<const SampleRecord*>(record.get()); 408 if (use_protobuf_) { 409 return PrintSampleRecordInProtobuf(r); 410 } else { 411 return PrintSampleRecord(r); 412 } 413 } else if (record->type() == PERF_RECORD_LOST) { 414 lost_count_ += static_cast<const LostRecord*>(record.get())->lost; 415 } 416 return true; 417 } 418 419 420 bool ReportSampleCommand::PrintSampleRecordInProtobuf(const SampleRecord& r) { 421 struct Node { 422 Dso* dso; 423 const Symbol* symbol; 424 uint64_t vaddr_in_file; 425 }; 426 std::vector<Node> nodes; 427 Node node; 428 proto::Record proto_record; 429 proto::Sample* sample = proto_record.mutable_sample(); 430 sample->set_time(r.time_data.time); 431 sample->set_event_count(r.period_data.period); 432 sample->set_thread_id(r.tid_data.tid); 433 sample->set_event_type_id(record_file_reader_->GetAttrIndexOfRecord(&r)); 434 435 bool in_kernel = r.InKernel(); 436 const ThreadEntry* thread = 437 thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid); 438 bool ret = GetCallEntry(thread, in_kernel, r.ip_data.ip, false, &node.vaddr_in_file, 439 &node.dso, &node.symbol); 440 CHECK(ret); 441 nodes.push_back(node); 442 if (show_callchain_ && (r.sample_type & PERF_SAMPLE_CALLCHAIN)) { 443 bool first_ip = true; 444 for (uint64_t i = 0; i < r.callchain_data.ip_nr; ++i) { 445 uint64_t ip = r.callchain_data.ips[i]; 446 if (ip >= PERF_CONTEXT_MAX) { 447 switch (ip) { 448 case PERF_CONTEXT_KERNEL: 449 in_kernel = true; 450 break; 451 case PERF_CONTEXT_USER: 452 in_kernel = false; 453 break; 454 default: 455 LOG(DEBUG) << "Unexpected perf_context in callchain: " << std::hex 456 << ip << std::dec; 457 } 458 } else { 459 if (first_ip) { 460 first_ip = false; 461 // Remove duplication with sample ip. 462 if (ip == r.ip_data.ip) { 463 continue; 464 } 465 } 466 if (!GetCallEntry(thread, in_kernel, ip, true, &node.vaddr_in_file, &node.dso, 467 &node.symbol)) { 468 break; 469 } 470 nodes.push_back(node); 471 } 472 } 473 } 474 475 for (const Node& node : nodes) { 476 proto::Sample_CallChainEntry* callchain = sample->add_callchain(); 477 uint32_t file_id; 478 if (!node.dso->GetDumpId(&file_id)) { 479 file_id = node.dso->CreateDumpId(); 480 } 481 int32_t symbol_id = -1; 482 if (node.symbol != thread_tree_.UnknownSymbol()) { 483 if (!node.symbol->GetDumpId(reinterpret_cast<uint32_t*>(&symbol_id))) { 484 symbol_id = node.dso->CreateSymbolDumpId(node.symbol); 485 } 486 } 487 callchain->set_vaddr_in_file(node.vaddr_in_file); 488 callchain->set_file_id(file_id); 489 callchain->set_symbol_id(symbol_id); 490 491 // Android studio wants a clear call chain end to notify whether a call chain is complete. 492 // For the main thread, the call chain ends at __libc_init in libc.so. For other threads, 493 // the call chain ends at __start_thread in libc.so. 494 // The call chain of the main thread can go beyond __libc_init, to _start (<= android O) or 495 // _start_main (> android O). 496 if (node.dso->FileName() == "libc.so" && 497 (strcmp(node.symbol->Name(), "__libc_init") == 0 || 498 strcmp(node.symbol->Name(), "__start_thread") == 0)) { 499 break; 500 } 501 } 502 return WriteRecordInProtobuf(proto_record); 503 } 504 505 bool ReportSampleCommand::WriteRecordInProtobuf(proto::Record& proto_record) { 506 coded_os_->WriteLittleEndian32(proto_record.ByteSize()); 507 if (!proto_record.SerializeToCodedStream(coded_os_)) { 508 LOG(ERROR) << "failed to write record to protobuf"; 509 return false; 510 } 511 return true; 512 } 513 514 bool ReportSampleCommand::GetCallEntry(const ThreadEntry* thread, 515 bool in_kernel, uint64_t ip, 516 bool omit_unknown_dso, 517 uint64_t* pvaddr_in_file, Dso** pdso, 518 const Symbol** psymbol) { 519 const MapEntry* map = thread_tree_.FindMap(thread, ip, in_kernel); 520 if (omit_unknown_dso && thread_tree_.IsUnknownDso(map->dso)) { 521 return false; 522 } 523 *psymbol = thread_tree_.FindSymbol(map, ip, pvaddr_in_file, pdso); 524 // If we can't find symbol, use the dso shown in the map. 525 if (*psymbol == thread_tree_.UnknownSymbol()) { 526 *pdso = map->dso; 527 } 528 return true; 529 } 530 531 bool ReportSampleCommand::PrintLostSituationInProtobuf() { 532 proto::Record proto_record; 533 proto::LostSituation* lost = proto_record.mutable_lost(); 534 lost->set_sample_count(sample_count_); 535 lost->set_lost_count(lost_count_); 536 return WriteRecordInProtobuf(proto_record); 537 } 538 539 static bool CompareDsoByDumpId(Dso* d1, Dso* d2) { 540 uint32_t id1 = UINT_MAX; 541 d1->GetDumpId(&id1); 542 uint32_t id2 = UINT_MAX; 543 d2->GetDumpId(&id2); 544 return id1 < id2; 545 } 546 547 bool ReportSampleCommand::PrintFileInfoInProtobuf() { 548 std::vector<Dso*> dsos = thread_tree_.GetAllDsos(); 549 std::sort(dsos.begin(), dsos.end(), CompareDsoByDumpId); 550 for (Dso* dso : dsos) { 551 uint32_t file_id; 552 if (!dso->GetDumpId(&file_id)) { 553 continue; 554 } 555 proto::Record proto_record; 556 proto::File* file = proto_record.mutable_file(); 557 file->set_id(file_id); 558 file->set_path(dso->Path()); 559 const std::vector<Symbol>& symbols = dso->GetSymbols(); 560 std::vector<const Symbol*> dump_symbols; 561 for (const auto& sym : symbols) { 562 if (sym.HasDumpId()) { 563 dump_symbols.push_back(&sym); 564 } 565 } 566 std::sort(dump_symbols.begin(), dump_symbols.end(), 567 Symbol::CompareByDumpId); 568 569 for (const auto& sym : dump_symbols) { 570 std::string* symbol = file->add_symbol(); 571 *symbol = sym->DemangledName(); 572 } 573 if (!WriteRecordInProtobuf(proto_record)) { 574 return false; 575 } 576 } 577 return true; 578 } 579 580 bool ReportSampleCommand::PrintThreadInfoInProtobuf() { 581 std::vector<const ThreadEntry*> threads = thread_tree_.GetAllThreads(); 582 auto compare_thread_id = [](const ThreadEntry* t1, const ThreadEntry* t2) { 583 return t1->tid < t2->tid; 584 }; 585 std::sort(threads.begin(), threads.end(), compare_thread_id); 586 for (auto& thread : threads) { 587 proto::Record proto_record; 588 proto::Thread* proto_thread = proto_record.mutable_thread(); 589 proto_thread->set_thread_id(thread->tid); 590 proto_thread->set_process_id(thread->pid); 591 proto_thread->set_thread_name(thread->comm); 592 if (!WriteRecordInProtobuf(proto_record)) { 593 return false; 594 } 595 } 596 return true; 597 } 598 599 bool ReportSampleCommand::PrintSampleRecord(const SampleRecord& r) { 600 uint64_t vaddr_in_file; 601 Dso* dso; 602 const Symbol* symbol; 603 604 FprintIndented(report_fp_, 0, "sample:\n"); 605 FprintIndented(report_fp_, 1, "event_type: %s\n", 606 event_types_[record_file_reader_->GetAttrIndexOfRecord(&r)].c_str()); 607 FprintIndented(report_fp_, 1, "time: %" PRIu64 "\n", r.time_data.time); 608 FprintIndented(report_fp_, 1, "event_count: %" PRIu64 "\n", r.period_data.period); 609 FprintIndented(report_fp_, 1, "thread_id: %d\n", r.tid_data.tid); 610 const char* thread_name = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid)->comm; 611 FprintIndented(report_fp_, 1, "thread_name: %s\n", thread_name); 612 bool in_kernel = r.InKernel(); 613 const ThreadEntry* thread = 614 thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid); 615 bool ret = GetCallEntry(thread, in_kernel, r.ip_data.ip, false, &vaddr_in_file, &dso, &symbol); 616 CHECK(ret); 617 FprintIndented(report_fp_, 1, "vaddr_in_file: %" PRIx64 "\n", vaddr_in_file); 618 FprintIndented(report_fp_, 1, "file: %s\n", dso->Path().c_str()); 619 FprintIndented(report_fp_, 1, "symbol: %s\n", symbol->DemangledName()); 620 621 if (show_callchain_ && (r.sample_type & PERF_SAMPLE_CALLCHAIN)) { 622 FprintIndented(report_fp_, 1, "callchain:\n"); 623 bool first_ip = true; 624 for (uint64_t i = 0; i < r.callchain_data.ip_nr; ++i) { 625 uint64_t ip = r.callchain_data.ips[i]; 626 if (ip >= PERF_CONTEXT_MAX) { 627 switch (ip) { 628 case PERF_CONTEXT_KERNEL: 629 in_kernel = true; 630 break; 631 case PERF_CONTEXT_USER: 632 in_kernel = false; 633 break; 634 default: 635 LOG(DEBUG) << "Unexpected perf_context in callchain: " << std::hex 636 << ip; 637 } 638 } else { 639 if (first_ip) { 640 first_ip = false; 641 // Remove duplication with sample ip. 642 if (ip == r.ip_data.ip) { 643 continue; 644 } 645 } 646 if (!GetCallEntry(thread, in_kernel, ip, true, &vaddr_in_file, &dso, &symbol)) { 647 break; 648 } 649 FprintIndented(report_fp_, 2, "vaddr_in_file: %" PRIx64 "\n", 650 vaddr_in_file); 651 FprintIndented(report_fp_, 2, "file: %s\n", dso->Path().c_str()); 652 FprintIndented(report_fp_, 2, "symbol: %s\n", symbol->DemangledName()); 653 } 654 } 655 } 656 return true; 657 } 658 659 void ReportSampleCommand::PrintLostSituation() { 660 FprintIndented(report_fp_, 0, "lost_situation:\n"); 661 FprintIndented(report_fp_, 1, "sample_count: %" PRIu64 "\n", sample_count_); 662 FprintIndented(report_fp_, 1, "lost_count: %" PRIu64 "\n", lost_count_); 663 } 664 665 } // namespace 666 667 void RegisterReportSampleCommand() { 668 RegisterCommand("report-sample", [] { 669 return std::unique_ptr<Command>(new ReportSampleCommand()); 670 }); 671 } 672