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