2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <sys/utsname.h>
24 #include <unordered_map>
27 #include <base/logging.h>
28 #include <base/strings.h>
31 #include "dwarf_unwind.h"
32 #include "environment.h"
33 #include "event_selection_set.h"
34 #include "event_type.h"
37 #include "record_file.h"
38 #include "thread_tree.h"
42 static std::string default_measured_event_type = "cpu-cycles";
44 static std::unordered_map<std::string, uint64_t> branch_sampling_type_map = {
45 {"u", PERF_SAMPLE_BRANCH_USER},
46 {"k", PERF_SAMPLE_BRANCH_KERNEL},
47 {"any", PERF_SAMPLE_BRANCH_ANY},
48 {"any_call", PERF_SAMPLE_BRANCH_ANY_CALL},
49 {"any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN},
50 {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL},
53 static volatile bool signaled;
54 static void signal_handler(int) {
58 class RecordCommand : public Command {
62 "record", "record sampling info in perf.data",
63 "Usage: simpleperf record [options] [command [command-args]]\n"
64 " Gather sampling information when running [command].\n"
65 " -a System-wide collection.\n"
66 " -b Enable take branch stack sampling. Same as '-j any'\n"
67 " -c count Set event sample period.\n"
68 " --call-graph fp | dwarf[,<dump_stack_size>]\n"
69 " Enable call graph recording. Use frame pointer or dwarf as the\n"
70 " method to parse call graph in stack. Default is dwarf,8192.\n"
71 " --cpu cpu_item1,cpu_item2,...\n"
72 " Collect samples only on the selected cpus. cpu_item can be cpu\n"
73 " number like 1, or cpu range like 0-3.\n"
74 " -e event1[:modifier1],event2[:modifier2],...\n"
75 " Select the event list to sample. Use `simpleperf list` to find\n"
76 " all possible event names. Modifiers can be added to define\n"
77 " how the event should be monitored. Possible modifiers are:\n"
78 " u - monitor user space events only\n"
79 " k - monitor kernel space events only\n"
80 " -f freq Set event sample frequency.\n"
81 " -F freq Same as '-f freq'.\n"
82 " -g Same as '--call-graph dwarf'.\n"
83 " -j branch_filter1,branch_filter2,...\n"
84 " Enable taken branch stack sampling. Each sample\n"
85 " captures a series of consecutive taken branches.\n"
86 " The following filters are defined:\n"
87 " any: any type of branch\n"
88 " any_call: any function call or system call\n"
89 " any_ret: any function return or system call return\n"
90 " ind_call: any indirect branch\n"
91 " u: only when the branch target is at the user level\n"
92 " k: only when the branch target is in the kernel\n"
93 " This option requires at least one branch type among any,\n"
94 " any_call, any_ret, ind_call.\n"
96 " Don't record created child threads/processes.\n"
97 " --no-unwind If `--call-graph dwarf` option is used, then the user's stack will\n"
98 " be unwound by default. Use this option to disable the unwinding of\n"
99 " the user's stack.\n"
100 " -o record_file_name Set record file name, default is perf.data.\n"
101 " -p pid1,pid2,...\n"
102 " Record events on existing processes. Mutually exclusive with -a.\n"
104 " If `--call-graph dwarf` option is used, then the user's stack will\n"
105 " be unwound while recording by default. But it may lose records as\n"
106 " stacking unwinding can be time consuming. Use this option to unwind\n"
107 " the user's stack after recording.\n"
108 " -t tid1,tid2,...\n"
109 " Record events on existing threads. Mutually exclusive with -a.\n"),
110 use_sample_freq_(true),
112 system_wide_collection_(false),
114 fp_callchain_sampling_(false),
115 dwarf_callchain_sampling_(false),
116 dump_stack_size_in_dwarf_sampling_(8192),
117 unwind_dwarf_callchain_(true),
119 child_inherit_(true),
120 perf_mmap_pages_(256),
121 record_filename_("perf.data") {
123 signal_handler_register_.reset(
124 new SignalHandlerRegister({SIGCHLD, SIGINT, SIGTERM}, signal_handler));
127 bool Run(const std::vector<std::string>& args);
129 static bool ReadMmapDataCallback(const char* data, size_t size);
132 bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
133 bool AddMeasuredEventType(const std::string& event_type_name);
134 bool SetEventSelection();
135 bool CreateAndInitRecordFile();
136 std::unique_ptr<RecordFileWriter> CreateRecordFile(const std::string& filename);
137 bool DumpKernelAndModuleMmaps();
138 bool DumpThreadCommAndMmaps(bool all_threads, const std::vector<pid_t>& selected_threads);
139 bool CollectRecordsFromKernel(const char* data, size_t size);
140 bool ProcessRecord(Record* record);
141 void UnwindRecord(Record* record);
142 bool PostUnwind(const std::vector<std::string>& args);
143 bool DumpAdditionalFeatures(const std::vector<std::string>& args);
144 bool DumpBuildIdFeature();
145 void CollectHitFileInfo(const Record* record);
147 bool use_sample_freq_; // Use sample_freq_ when true, otherwise using sample_period_.
148 uint64_t sample_freq_; // Sample 'sample_freq_' times per second.
149 uint64_t sample_period_; // Sample once when 'sample_period_' events occur.
151 bool system_wide_collection_;
152 uint64_t branch_sampling_;
153 bool fp_callchain_sampling_;
154 bool dwarf_callchain_sampling_;
155 uint32_t dump_stack_size_in_dwarf_sampling_;
156 bool unwind_dwarf_callchain_;
159 std::vector<pid_t> monitored_threads_;
160 std::vector<int> cpus_;
161 std::vector<EventTypeAndModifier> measured_event_types_;
162 EventSelectionSet event_selection_set_;
164 // mmap pages used by each perf event file, should be power of 2.
165 const size_t perf_mmap_pages_;
167 std::unique_ptr<RecordCache> record_cache_;
168 ThreadTree thread_tree_;
169 std::string record_filename_;
170 std::unique_ptr<RecordFileWriter> record_file_writer_;
172 std::set<std::string> hit_kernel_modules_;
173 std::set<std::string> hit_user_files_;
175 std::unique_ptr<SignalHandlerRegister> signal_handler_register_;
178 bool RecordCommand::Run(const std::vector<std::string>& args) {
179 // 1. Parse options, and use default measured event type if not given.
180 std::vector<std::string> workload_args;
181 if (!ParseOptions(args, &workload_args)) {
184 if (measured_event_types_.empty()) {
185 if (!AddMeasuredEventType(default_measured_event_type)) {
189 if (!SetEventSelection()) {
193 // 2. Create workload.
194 std::unique_ptr<Workload> workload;
195 if (!workload_args.empty()) {
196 workload = Workload::CreateWorkload(workload_args);
197 if (workload == nullptr) {
201 if (!system_wide_collection_ && monitored_threads_.empty()) {
202 if (workload != nullptr) {
203 monitored_threads_.push_back(workload->GetPid());
204 event_selection_set_.SetEnableOnExec(true);
206 LOG(ERROR) << "No threads to monitor. Try `simpleperf help record` for help\n";
211 // 3. Open perf_event_files, create memory mapped buffers for perf_event_files, add prepare poll
212 // for perf_event_files.
213 if (system_wide_collection_) {
214 if (!event_selection_set_.OpenEventFilesForCpus(cpus_)) {
218 if (!event_selection_set_.OpenEventFilesForThreadsOnCpus(monitored_threads_, cpus_)) {
222 if (!event_selection_set_.MmapEventFiles(perf_mmap_pages_)) {
225 std::vector<pollfd> pollfds;
226 event_selection_set_.PreparePollForEventFiles(&pollfds);
228 // 4. Create perf.data.
229 if (!CreateAndInitRecordFile()) {
233 // 5. Write records in mmap buffers of perf_event_files to output file while workload is running.
234 if (!event_selection_set_.GetEnableOnExec()) {
235 if (!event_selection_set_.EnableEvents()) {
239 if (workload != nullptr && !workload->Start()) {
243 new RecordCache(*event_selection_set_.FindEventAttrByType(measured_event_types_[0])));
244 auto callback = std::bind(&RecordCommand::CollectRecordsFromKernel, this, std::placeholders::_1,
245 std::placeholders::_2);
247 if (!event_selection_set_.ReadMmapEventData(callback)) {
253 poll(&pollfds[0], pollfds.size(), -1);
255 std::vector<std::unique_ptr<Record>> records = record_cache_->PopAll();
256 for (auto& r : records) {
257 if (!ProcessRecord(r.get())) {
262 // 6. Dump additional features, and close record file.
263 if (!DumpAdditionalFeatures(args)) {
266 if (!record_file_writer_->Close()) {
270 // 7. Unwind dwarf callchain.
272 if (!PostUnwind(args)) {
280 bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
281 std::vector<std::string>* non_option_args) {
282 std::set<pid_t> tid_set;
284 for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
285 if (args[i] == "-a") {
286 system_wide_collection_ = true;
287 } else if (args[i] == "-b") {
288 branch_sampling_ = branch_sampling_type_map["any"];
289 } else if (args[i] == "-c") {
290 if (!NextArgumentOrError(args, &i)) {
294 sample_period_ = strtoull(args[i].c_str(), &endptr, 0);
295 if (*endptr != '\0' || sample_period_ == 0) {
296 LOG(ERROR) << "Invalid sample period: '" << args[i] << "'";
299 use_sample_freq_ = false;
300 } else if (args[i] == "--call-graph") {
301 if (!NextArgumentOrError(args, &i)) {
304 std::vector<std::string> strs = android::base::Split(args[i], ",");
305 if (strs[0] == "fp") {
306 fp_callchain_sampling_ = true;
307 dwarf_callchain_sampling_ = false;
308 } else if (strs[0] == "dwarf") {
309 fp_callchain_sampling_ = false;
310 dwarf_callchain_sampling_ = true;
311 if (strs.size() > 1) {
313 uint64_t size = strtoull(strs[1].c_str(), &endptr, 0);
314 if (*endptr != '\0' || size > UINT_MAX) {
315 LOG(ERROR) << "invalid dump stack size in --call-graph option: " << strs[1];
318 if ((size & 7) != 0) {
319 LOG(ERROR) << "dump stack size " << size << " is not 8-byte aligned.";
322 dump_stack_size_in_dwarf_sampling_ = static_cast<uint32_t>(size);
325 LOG(ERROR) << "unexpected argument for --call-graph option: " << args[i];
328 } else if (args[i] == "--cpu") {
329 if (!NextArgumentOrError(args, &i)) {
332 cpus_ = GetCpusFromString(args[i]);
333 } else if (args[i] == "-e") {
334 if (!NextArgumentOrError(args, &i)) {
337 std::vector<std::string> event_types = android::base::Split(args[i], ",");
338 for (auto& event_type : event_types) {
339 if (!AddMeasuredEventType(event_type)) {
343 } else if (args[i] == "-f" || args[i] == "-F") {
344 if (!NextArgumentOrError(args, &i)) {
348 sample_freq_ = strtoull(args[i].c_str(), &endptr, 0);
349 if (*endptr != '\0' || sample_freq_ == 0) {
350 LOG(ERROR) << "Invalid sample frequency: '" << args[i] << "'";
353 use_sample_freq_ = true;
354 } else if (args[i] == "-g") {
355 fp_callchain_sampling_ = false;
356 dwarf_callchain_sampling_ = true;
357 } else if (args[i] == "-j") {
358 if (!NextArgumentOrError(args, &i)) {
361 std::vector<std::string> branch_sampling_types = android::base::Split(args[i], ",");
362 for (auto& type : branch_sampling_types) {
363 auto it = branch_sampling_type_map.find(type);
364 if (it == branch_sampling_type_map.end()) {
365 LOG(ERROR) << "unrecognized branch sampling filter: " << type;
368 branch_sampling_ |= it->second;
370 } else if (args[i] == "--no-inherit") {
371 child_inherit_ = false;
372 } else if (args[i] == "--no-unwind") {
373 unwind_dwarf_callchain_ = false;
374 } else if (args[i] == "-o") {
375 if (!NextArgumentOrError(args, &i)) {
378 record_filename_ = args[i];
379 } else if (args[i] == "-p") {
380 if (!NextArgumentOrError(args, &i)) {
383 if (!GetValidThreadsFromProcessString(args[i], &tid_set)) {
386 } else if (args[i] == "--post-unwind") {
388 } else if (args[i] == "-t") {
389 if (!NextArgumentOrError(args, &i)) {
392 if (!GetValidThreadsFromThreadString(args[i], &tid_set)) {
396 ReportUnknownOption(args, i);
401 if (!dwarf_callchain_sampling_) {
402 if (!unwind_dwarf_callchain_) {
403 LOG(ERROR) << "--no-unwind is only used with `--call-graph dwarf` option.";
406 unwind_dwarf_callchain_ = false;
409 if (!dwarf_callchain_sampling_) {
410 LOG(ERROR) << "--post-unwind is only used with `--call-graph dwarf` option.";
413 if (!unwind_dwarf_callchain_) {
414 LOG(ERROR) << "--post-unwind can't be used with `--no-unwind` option.";
419 monitored_threads_.insert(monitored_threads_.end(), tid_set.begin(), tid_set.end());
420 if (system_wide_collection_ && !monitored_threads_.empty()) {
422 << "Record system wide and existing processes/threads can't be used at the same time.";
426 if (non_option_args != nullptr) {
427 non_option_args->clear();
428 for (; i < args.size(); ++i) {
429 non_option_args->push_back(args[i]);
435 bool RecordCommand::AddMeasuredEventType(const std::string& event_type_name) {
436 std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_name);
437 if (event_type_modifier == nullptr) {
440 measured_event_types_.push_back(*event_type_modifier);
444 bool RecordCommand::SetEventSelection() {
445 for (auto& event_type : measured_event_types_) {
446 if (!event_selection_set_.AddEventType(event_type)) {
450 if (use_sample_freq_) {
451 event_selection_set_.SetSampleFreq(sample_freq_);
453 event_selection_set_.SetSamplePeriod(sample_period_);
455 event_selection_set_.SampleIdAll();
456 if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
459 if (fp_callchain_sampling_) {
460 event_selection_set_.EnableFpCallChainSampling();
461 } else if (dwarf_callchain_sampling_) {
462 if (!event_selection_set_.EnableDwarfCallChainSampling(dump_stack_size_in_dwarf_sampling_)) {
466 event_selection_set_.SetInherit(child_inherit_);
470 bool RecordCommand::CreateAndInitRecordFile() {
471 record_file_writer_ = CreateRecordFile(record_filename_);
472 if (record_file_writer_ == nullptr) {
475 if (!DumpKernelAndModuleMmaps()) {
478 if (!DumpThreadCommAndMmaps(system_wide_collection_, monitored_threads_)) {
484 std::unique_ptr<RecordFileWriter> RecordCommand::CreateRecordFile(const std::string& filename) {
485 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename);
486 if (writer == nullptr) {
490 std::vector<AttrWithId> attr_ids;
491 for (auto& event_type : measured_event_types_) {
493 attr_id.attr = event_selection_set_.FindEventAttrByType(event_type);
494 CHECK(attr_id.attr != nullptr);
495 const std::vector<std::unique_ptr<EventFd>>* fds =
496 event_selection_set_.FindEventFdsByType(event_type);
497 CHECK(fds != nullptr);
498 for (auto& fd : *fds) {
499 attr_id.ids.push_back(fd->Id());
501 attr_ids.push_back(attr_id);
503 if (!writer->WriteAttrSection(attr_ids)) {
509 bool RecordCommand::DumpKernelAndModuleMmaps() {
510 KernelMmap kernel_mmap;
511 std::vector<ModuleMmap> module_mmaps;
512 if (!GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps)) {
515 const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
516 CHECK(attr != nullptr);
517 MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
518 kernel_mmap.len, kernel_mmap.pgoff, kernel_mmap.name);
519 if (!ProcessRecord(&mmap_record)) {
522 for (auto& module_mmap : module_mmaps) {
523 std::string filename = module_mmap.filepath;
524 if (filename.empty()) {
525 filename = "[" + module_mmap.name + "]";
527 MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, module_mmap.start_addr,
528 module_mmap.len, 0, filename);
529 if (!ProcessRecord(&mmap_record)) {
536 bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads,
537 const std::vector<pid_t>& selected_threads) {
538 std::vector<ThreadComm> thread_comms;
539 if (!GetThreadComms(&thread_comms)) {
542 // Decide which processes and threads to dump.
543 std::set<pid_t> dump_processes;
544 std::set<pid_t> dump_threads;
545 for (auto& tid : selected_threads) {
546 dump_threads.insert(tid);
548 for (auto& thread : thread_comms) {
549 if (dump_threads.find(thread.tid) != dump_threads.end()) {
550 dump_processes.insert(thread.pid);
554 const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
555 CHECK(attr != nullptr);
558 for (auto& thread : thread_comms) {
559 if (thread.pid != thread.tid) {
562 if (!all_threads && dump_processes.find(thread.pid) == dump_processes.end()) {
565 CommRecord record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm);
566 if (!ProcessRecord(&record)) {
569 std::vector<ThreadMmap> thread_mmaps;
570 if (!GetThreadMmapsInProcess(thread.pid, &thread_mmaps)) {
571 // The thread may exit before we get its info.
574 for (auto& thread_mmap : thread_mmaps) {
575 if (thread_mmap.executable == 0) {
576 continue; // No need to dump non-executable mmap info.
579 CreateMmapRecord(*attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
580 thread_mmap.len, thread_mmap.pgoff, thread_mmap.name);
581 if (!ProcessRecord(&record)) {
588 for (auto& thread : thread_comms) {
589 if (thread.pid == thread.tid) {
592 if (!all_threads && dump_threads.find(thread.tid) == dump_threads.end()) {
595 ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid, thread.pid, thread.pid);
596 if (!ProcessRecord(&fork_record)) {
599 CommRecord comm_record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm);
600 if (!ProcessRecord(&comm_record)) {
607 bool RecordCommand::CollectRecordsFromKernel(const char* data, size_t size) {
608 record_cache_->Push(data, size);
610 std::unique_ptr<Record> r = record_cache_->Pop();
614 if (!ProcessRecord(r.get())) {
621 bool RecordCommand::ProcessRecord(Record* record) {
622 BuildThreadTree(*record, &thread_tree_);
623 CollectHitFileInfo(record);
624 if (unwind_dwarf_callchain_ && !post_unwind_) {
625 UnwindRecord(record);
627 bool result = record_file_writer_->WriteData(record->BinaryFormat());
631 void RecordCommand::UnwindRecord(Record* record) {
632 if (record->type() == PERF_RECORD_SAMPLE) {
633 SampleRecord& r = *static_cast<SampleRecord*>(record);
634 if ((r.sample_type & PERF_SAMPLE_CALLCHAIN) && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
635 (r.regs_user_data.reg_mask != 0) && (r.sample_type & PERF_SAMPLE_STACK_USER) &&
636 (!r.stack_user_data.data.empty())) {
637 ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
638 RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
639 std::vector<char>& stack = r.stack_user_data.data;
640 std::vector<uint64_t> unwind_ips = UnwindCallChain(*thread, regs, stack);
641 r.callchain_data.ips.push_back(PERF_CONTEXT_USER);
642 r.callchain_data.ips.insert(r.callchain_data.ips.end(), unwind_ips.begin(), unwind_ips.end());
643 r.regs_user_data.abi = 0;
644 r.regs_user_data.reg_mask = 0;
645 r.regs_user_data.regs.clear();
646 r.stack_user_data.data.clear();
647 r.stack_user_data.dyn_size = 0;
648 r.AdjustSizeBasedOnData();
653 bool RecordCommand::PostUnwind(const std::vector<std::string>& args) {
654 thread_tree_.Clear();
655 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(record_filename_);
656 if (reader == nullptr) {
659 std::string tmp_filename = record_filename_ + ".tmp";
660 record_file_writer_ = CreateRecordFile(tmp_filename);
661 if (record_file_writer_ == nullptr) {
664 bool result = reader->ReadDataSection(
665 [this](std::unique_ptr<Record> record) {
666 BuildThreadTree(*record, &thread_tree_);
667 UnwindRecord(record.get());
668 return record_file_writer_->WriteData(record->BinaryFormat());
674 if (!DumpAdditionalFeatures(args)) {
677 if (!record_file_writer_->Close()) {
681 if (unlink(record_filename_.c_str()) != 0) {
682 PLOG(ERROR) << "failed to remove " << record_filename_;
685 if (rename(tmp_filename.c_str(), record_filename_.c_str()) != 0) {
686 PLOG(ERROR) << "failed to rename " << tmp_filename << " to " << record_filename_;
692 bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args) {
693 size_t feature_count = (branch_sampling_ != 0 ? 5 : 4);
694 if (!record_file_writer_->WriteFeatureHeader(feature_count)) {
697 if (!DumpBuildIdFeature()) {
701 if (TEMP_FAILURE_RETRY(uname(&uname_buf)) != 0) {
702 PLOG(ERROR) << "uname() failed";
705 if (!record_file_writer_->WriteFeatureString(PerfFileFormat::FEAT_OSRELEASE, uname_buf.release)) {
708 if (!record_file_writer_->WriteFeatureString(PerfFileFormat::FEAT_ARCH, uname_buf.machine)) {
712 std::string exec_path = "simpleperf";
713 GetExecPath(&exec_path);
714 std::vector<std::string> cmdline;
715 cmdline.push_back(exec_path);
716 cmdline.push_back("record");
717 cmdline.insert(cmdline.end(), args.begin(), args.end());
718 if (!record_file_writer_->WriteCmdlineFeature(cmdline)) {
721 if (branch_sampling_ != 0 && !record_file_writer_->WriteBranchStackFeature()) {
727 bool RecordCommand::DumpBuildIdFeature() {
728 std::vector<BuildIdRecord> build_id_records;
730 // Add build_ids for kernel/modules.
731 for (auto& filename : hit_kernel_modules_) {
732 if (filename == DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID) {
733 if (!GetKernelBuildId(&build_id)) {
734 LOG(DEBUG) << "can't read build_id for kernel";
737 build_id_records.push_back(
738 CreateBuildIdRecord(true, UINT_MAX, build_id, DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID));
740 std::string path = filename;
741 std::string module_name = basename(&path[0]);
742 if (android::base::EndsWith(module_name, ".ko")) {
743 module_name = module_name.substr(0, module_name.size() - 3);
745 if (!GetModuleBuildId(module_name, &build_id)) {
746 LOG(DEBUG) << "can't read build_id for module " << module_name;
749 build_id_records.push_back(CreateBuildIdRecord(true, UINT_MAX, build_id, filename));
752 // Add build_ids for user elf files.
753 for (auto& filename : hit_user_files_) {
754 if (filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) {
757 if (!GetBuildIdFromElfFile(filename, &build_id)) {
758 LOG(DEBUG) << "can't read build_id from file " << filename;
761 build_id_records.push_back(CreateBuildIdRecord(false, UINT_MAX, build_id, filename));
763 if (!record_file_writer_->WriteBuildIdFeature(build_id_records)) {
769 void RecordCommand::CollectHitFileInfo(const Record* record) {
770 if (record->type() == PERF_RECORD_SAMPLE) {
771 auto r = *static_cast<const SampleRecord*>(record);
772 bool in_kernel = ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL);
773 const ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
774 const MapEntry* map = thread_tree_.FindMap(thread, r.ip_data.ip, in_kernel);
776 hit_kernel_modules_.insert(map->dso->Path());
778 hit_user_files_.insert(map->dso->Path());
783 __attribute__((constructor)) static void RegisterRecordCommand() {
784 RegisterCommand("record", [] { return std::unique_ptr<Command>(new RecordCommand()); });