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 <android-base/logging.h>
28 #include <android-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 // Used in cpu-hotplug test.
59 bool system_wide_perf_event_open_failed = false;
61 class RecordCommand : public Command {
65 "record", "record sampling info in perf.data",
66 "Usage: simpleperf record [options] [command [command-args]]\n"
67 " Gather sampling information when running [command].\n"
68 " -a System-wide collection.\n"
69 " -b Enable take branch stack sampling. Same as '-j any'\n"
70 " -c count Set event sample period.\n"
71 " --call-graph fp | dwarf[,<dump_stack_size>]\n"
72 " Enable call graph recording. Use frame pointer or dwarf as the\n"
73 " method to parse call graph in stack. Default is dwarf,8192.\n"
74 " --cpu cpu_item1,cpu_item2,...\n"
75 " Collect samples only on the selected cpus. cpu_item can be cpu\n"
76 " number like 1, or cpu range like 0-3.\n"
77 " -e event1[:modifier1],event2[:modifier2],...\n"
78 " Select the event list to sample. Use `simpleperf list` to find\n"
79 " all possible event names. Modifiers can be added to define\n"
80 " how the event should be monitored. Possible modifiers are:\n"
81 " u - monitor user space events only\n"
82 " k - monitor kernel space events only\n"
83 " -f freq Set event sample frequency.\n"
84 " -F freq Same as '-f freq'.\n"
85 " -g Same as '--call-graph dwarf'.\n"
86 " -j branch_filter1,branch_filter2,...\n"
87 " Enable taken branch stack sampling. Each sample\n"
88 " captures a series of consecutive taken branches.\n"
89 " The following filters are defined:\n"
90 " any: any type of branch\n"
91 " any_call: any function call or system call\n"
92 " any_ret: any function return or system call return\n"
93 " ind_call: any indirect branch\n"
94 " u: only when the branch target is at the user level\n"
95 " k: only when the branch target is in the kernel\n"
96 " This option requires at least one branch type among any,\n"
97 " any_call, any_ret, ind_call.\n"
99 " Don't record created child threads/processes.\n"
100 " --no-unwind If `--call-graph dwarf` option is used, then the user's stack will\n"
101 " be unwound by default. Use this option to disable the unwinding of\n"
102 " the user's stack.\n"
103 " -o record_file_name Set record file name, default is perf.data.\n"
104 " -p pid1,pid2,...\n"
105 " Record events on existing processes. Mutually exclusive with -a.\n"
107 " If `--call-graph dwarf` option is used, then the user's stack will\n"
108 " be unwound while recording by default. But it may lose records as\n"
109 " stacking unwinding can be time consuming. Use this option to unwind\n"
110 " the user's stack after recording.\n"
111 " -t tid1,tid2,...\n"
112 " Record events on existing threads. Mutually exclusive with -a.\n"),
113 use_sample_freq_(true),
115 system_wide_collection_(false),
117 fp_callchain_sampling_(false),
118 dwarf_callchain_sampling_(false),
119 dump_stack_size_in_dwarf_sampling_(8192),
120 unwind_dwarf_callchain_(true),
122 child_inherit_(true),
123 perf_mmap_pages_(256),
124 record_filename_("perf.data") {
126 signal_handler_register_.reset(
127 new SignalHandlerRegister({SIGCHLD, SIGINT, SIGTERM}, signal_handler));
130 bool Run(const std::vector<std::string>& args);
132 static bool ReadMmapDataCallback(const char* data, size_t size);
135 bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
136 bool AddMeasuredEventType(const std::string& event_type_name);
137 bool SetEventSelection();
138 bool CreateAndInitRecordFile();
139 std::unique_ptr<RecordFileWriter> CreateRecordFile(const std::string& filename);
140 bool DumpKernelAndModuleMmaps();
141 bool DumpThreadCommAndMmaps(bool all_threads, const std::vector<pid_t>& selected_threads);
142 bool CollectRecordsFromKernel(const char* data, size_t size);
143 bool ProcessRecord(Record* record);
144 void UnwindRecord(Record* record);
145 bool PostUnwind(const std::vector<std::string>& args);
146 bool DumpAdditionalFeatures(const std::vector<std::string>& args);
147 bool DumpBuildIdFeature();
148 void CollectHitFileInfo(const Record* record);
150 bool use_sample_freq_; // Use sample_freq_ when true, otherwise using sample_period_.
151 uint64_t sample_freq_; // Sample 'sample_freq_' times per second.
152 uint64_t sample_period_; // Sample once when 'sample_period_' events occur.
154 bool system_wide_collection_;
155 uint64_t branch_sampling_;
156 bool fp_callchain_sampling_;
157 bool dwarf_callchain_sampling_;
158 uint32_t dump_stack_size_in_dwarf_sampling_;
159 bool unwind_dwarf_callchain_;
162 std::vector<pid_t> monitored_threads_;
163 std::vector<int> cpus_;
164 std::vector<EventTypeAndModifier> measured_event_types_;
165 EventSelectionSet event_selection_set_;
167 // mmap pages used by each perf event file, should be power of 2.
168 const size_t perf_mmap_pages_;
170 std::unique_ptr<RecordCache> record_cache_;
171 ThreadTree thread_tree_;
172 std::string record_filename_;
173 std::unique_ptr<RecordFileWriter> record_file_writer_;
175 std::set<std::string> hit_kernel_modules_;
176 std::set<std::string> hit_user_files_;
178 std::unique_ptr<SignalHandlerRegister> signal_handler_register_;
181 bool RecordCommand::Run(const std::vector<std::string>& args) {
182 // 1. Parse options, and use default measured event type if not given.
183 std::vector<std::string> workload_args;
184 if (!ParseOptions(args, &workload_args)) {
187 if (measured_event_types_.empty()) {
188 if (!AddMeasuredEventType(default_measured_event_type)) {
192 if (!SetEventSelection()) {
196 // 2. Create workload.
197 std::unique_ptr<Workload> workload;
198 if (!workload_args.empty()) {
199 workload = Workload::CreateWorkload(workload_args);
200 if (workload == nullptr) {
204 if (!system_wide_collection_ && monitored_threads_.empty()) {
205 if (workload != nullptr) {
206 monitored_threads_.push_back(workload->GetPid());
207 event_selection_set_.SetEnableOnExec(true);
209 LOG(ERROR) << "No threads to monitor. Try `simpleperf help record` for help\n";
214 // 3. Open perf_event_files, create memory mapped buffers for perf_event_files, add prepare poll
215 // for perf_event_files.
216 if (system_wide_collection_) {
217 if (!event_selection_set_.OpenEventFilesForCpus(cpus_)) {
218 system_wide_perf_event_open_failed = true;
222 if (!event_selection_set_.OpenEventFilesForThreadsOnCpus(monitored_threads_, cpus_)) {
226 if (!event_selection_set_.MmapEventFiles(perf_mmap_pages_)) {
229 std::vector<pollfd> pollfds;
230 event_selection_set_.PreparePollForEventFiles(&pollfds);
232 // 4. Create perf.data.
233 if (!CreateAndInitRecordFile()) {
237 // 5. Write records in mmap buffers of perf_event_files to output file while workload is running.
238 if (workload != nullptr && !workload->Start()) {
242 new RecordCache(*event_selection_set_.FindEventAttrByType(measured_event_types_[0])));
243 auto callback = std::bind(&RecordCommand::CollectRecordsFromKernel, this, std::placeholders::_1,
244 std::placeholders::_2);
246 if (!event_selection_set_.ReadMmapEventData(callback)) {
252 poll(&pollfds[0], pollfds.size(), -1);
254 std::vector<std::unique_ptr<Record>> records = record_cache_->PopAll();
255 for (auto& r : records) {
256 if (!ProcessRecord(r.get())) {
261 // 6. Dump additional features, and close record file.
262 if (!DumpAdditionalFeatures(args)) {
265 if (!record_file_writer_->Close()) {
269 // 7. Unwind dwarf callchain.
271 if (!PostUnwind(args)) {
279 bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
280 std::vector<std::string>* non_option_args) {
281 std::set<pid_t> tid_set;
283 for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
284 if (args[i] == "-a") {
285 system_wide_collection_ = true;
286 } else if (args[i] == "-b") {
287 branch_sampling_ = branch_sampling_type_map["any"];
288 } else if (args[i] == "-c") {
289 if (!NextArgumentOrError(args, &i)) {
293 sample_period_ = strtoull(args[i].c_str(), &endptr, 0);
294 if (*endptr != '\0' || sample_period_ == 0) {
295 LOG(ERROR) << "Invalid sample period: '" << args[i] << "'";
298 use_sample_freq_ = false;
299 } else if (args[i] == "--call-graph") {
300 if (!NextArgumentOrError(args, &i)) {
303 std::vector<std::string> strs = android::base::Split(args[i], ",");
304 if (strs[0] == "fp") {
305 fp_callchain_sampling_ = true;
306 dwarf_callchain_sampling_ = false;
307 } else if (strs[0] == "dwarf") {
308 fp_callchain_sampling_ = false;
309 dwarf_callchain_sampling_ = true;
310 if (strs.size() > 1) {
312 uint64_t size = strtoull(strs[1].c_str(), &endptr, 0);
313 if (*endptr != '\0' || size > UINT_MAX) {
314 LOG(ERROR) << "invalid dump stack size in --call-graph option: " << strs[1];
317 if ((size & 7) != 0) {
318 LOG(ERROR) << "dump stack size " << size << " is not 8-byte aligned.";
321 dump_stack_size_in_dwarf_sampling_ = static_cast<uint32_t>(size);
324 LOG(ERROR) << "unexpected argument for --call-graph option: " << args[i];
327 } else if (args[i] == "--cpu") {
328 if (!NextArgumentOrError(args, &i)) {
331 cpus_ = GetCpusFromString(args[i]);
332 } else if (args[i] == "-e") {
333 if (!NextArgumentOrError(args, &i)) {
336 std::vector<std::string> event_types = android::base::Split(args[i], ",");
337 for (auto& event_type : event_types) {
338 if (!AddMeasuredEventType(event_type)) {
342 } else if (args[i] == "-f" || args[i] == "-F") {
343 if (!NextArgumentOrError(args, &i)) {
347 sample_freq_ = strtoull(args[i].c_str(), &endptr, 0);
348 if (*endptr != '\0' || sample_freq_ == 0) {
349 LOG(ERROR) << "Invalid sample frequency: '" << args[i] << "'";
352 use_sample_freq_ = true;
353 } else if (args[i] == "-g") {
354 fp_callchain_sampling_ = false;
355 dwarf_callchain_sampling_ = true;
356 } else if (args[i] == "-j") {
357 if (!NextArgumentOrError(args, &i)) {
360 std::vector<std::string> branch_sampling_types = android::base::Split(args[i], ",");
361 for (auto& type : branch_sampling_types) {
362 auto it = branch_sampling_type_map.find(type);
363 if (it == branch_sampling_type_map.end()) {
364 LOG(ERROR) << "unrecognized branch sampling filter: " << type;
367 branch_sampling_ |= it->second;
369 } else if (args[i] == "--no-inherit") {
370 child_inherit_ = false;
371 } else if (args[i] == "--no-unwind") {
372 unwind_dwarf_callchain_ = false;
373 } else if (args[i] == "-o") {
374 if (!NextArgumentOrError(args, &i)) {
377 record_filename_ = args[i];
378 } else if (args[i] == "-p") {
379 if (!NextArgumentOrError(args, &i)) {
382 if (!GetValidThreadsFromProcessString(args[i], &tid_set)) {
385 } else if (args[i] == "--post-unwind") {
387 } else if (args[i] == "-t") {
388 if (!NextArgumentOrError(args, &i)) {
391 if (!GetValidThreadsFromThreadString(args[i], &tid_set)) {
395 ReportUnknownOption(args, i);
400 if (!dwarf_callchain_sampling_) {
401 if (!unwind_dwarf_callchain_) {
402 LOG(ERROR) << "--no-unwind is only used with `--call-graph dwarf` option.";
405 unwind_dwarf_callchain_ = false;
408 if (!dwarf_callchain_sampling_) {
409 LOG(ERROR) << "--post-unwind is only used with `--call-graph dwarf` option.";
412 if (!unwind_dwarf_callchain_) {
413 LOG(ERROR) << "--post-unwind can't be used with `--no-unwind` option.";
418 monitored_threads_.insert(monitored_threads_.end(), tid_set.begin(), tid_set.end());
419 if (system_wide_collection_ && !monitored_threads_.empty()) {
421 << "Record system wide and existing processes/threads can't be used at the same time.";
425 if (non_option_args != nullptr) {
426 non_option_args->clear();
427 for (; i < args.size(); ++i) {
428 non_option_args->push_back(args[i]);
434 bool RecordCommand::AddMeasuredEventType(const std::string& event_type_name) {
435 std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_name);
436 if (event_type_modifier == nullptr) {
439 measured_event_types_.push_back(*event_type_modifier);
443 bool RecordCommand::SetEventSelection() {
444 for (auto& event_type : measured_event_types_) {
445 if (!event_selection_set_.AddEventType(event_type)) {
449 if (use_sample_freq_) {
450 event_selection_set_.SetSampleFreq(sample_freq_);
452 event_selection_set_.SetSamplePeriod(sample_period_);
454 event_selection_set_.SampleIdAll();
455 if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
458 if (fp_callchain_sampling_) {
459 event_selection_set_.EnableFpCallChainSampling();
460 } else if (dwarf_callchain_sampling_) {
461 if (!event_selection_set_.EnableDwarfCallChainSampling(dump_stack_size_in_dwarf_sampling_)) {
465 event_selection_set_.SetInherit(child_inherit_);
469 bool RecordCommand::CreateAndInitRecordFile() {
470 record_file_writer_ = CreateRecordFile(record_filename_);
471 if (record_file_writer_ == nullptr) {
474 if (!DumpKernelAndModuleMmaps()) {
477 if (!DumpThreadCommAndMmaps(system_wide_collection_, monitored_threads_)) {
483 std::unique_ptr<RecordFileWriter> RecordCommand::CreateRecordFile(const std::string& filename) {
484 std::unique_ptr<RecordFileWriter> writer = RecordFileWriter::CreateInstance(filename);
485 if (writer == nullptr) {
489 std::vector<AttrWithId> attr_ids;
490 for (auto& event_type : measured_event_types_) {
492 attr_id.attr = event_selection_set_.FindEventAttrByType(event_type);
493 CHECK(attr_id.attr != nullptr);
494 const std::vector<std::unique_ptr<EventFd>>* fds =
495 event_selection_set_.FindEventFdsByType(event_type);
496 CHECK(fds != nullptr);
497 for (auto& fd : *fds) {
498 attr_id.ids.push_back(fd->Id());
500 attr_ids.push_back(attr_id);
502 if (!writer->WriteAttrSection(attr_ids)) {
508 bool RecordCommand::DumpKernelAndModuleMmaps() {
509 KernelMmap kernel_mmap;
510 std::vector<ModuleMmap> module_mmaps;
511 if (!GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps)) {
514 const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
515 CHECK(attr != nullptr);
516 MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
517 kernel_mmap.len, kernel_mmap.pgoff, kernel_mmap.name);
518 if (!ProcessRecord(&mmap_record)) {
521 for (auto& module_mmap : module_mmaps) {
522 std::string filename = module_mmap.filepath;
523 if (filename.empty()) {
524 filename = "[" + module_mmap.name + "]";
526 MmapRecord mmap_record = CreateMmapRecord(*attr, true, UINT_MAX, 0, module_mmap.start_addr,
527 module_mmap.len, 0, filename);
528 if (!ProcessRecord(&mmap_record)) {
535 bool RecordCommand::DumpThreadCommAndMmaps(bool all_threads,
536 const std::vector<pid_t>& selected_threads) {
537 std::vector<ThreadComm> thread_comms;
538 if (!GetThreadComms(&thread_comms)) {
541 // Decide which processes and threads to dump.
542 std::set<pid_t> dump_processes;
543 std::set<pid_t> dump_threads;
544 for (auto& tid : selected_threads) {
545 dump_threads.insert(tid);
547 for (auto& thread : thread_comms) {
548 if (dump_threads.find(thread.tid) != dump_threads.end()) {
549 dump_processes.insert(thread.pid);
553 const perf_event_attr* attr = event_selection_set_.FindEventAttrByType(measured_event_types_[0]);
554 CHECK(attr != nullptr);
557 for (auto& thread : thread_comms) {
558 if (thread.pid != thread.tid) {
561 if (!all_threads && dump_processes.find(thread.pid) == dump_processes.end()) {
564 CommRecord record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm);
565 if (!ProcessRecord(&record)) {
568 std::vector<ThreadMmap> thread_mmaps;
569 if (!GetThreadMmapsInProcess(thread.pid, &thread_mmaps)) {
570 // The thread may exit before we get its info.
573 for (auto& thread_mmap : thread_mmaps) {
574 if (thread_mmap.executable == 0) {
575 continue; // No need to dump non-executable mmap info.
578 CreateMmapRecord(*attr, false, thread.pid, thread.tid, thread_mmap.start_addr,
579 thread_mmap.len, thread_mmap.pgoff, thread_mmap.name);
580 if (!ProcessRecord(&record)) {
587 for (auto& thread : thread_comms) {
588 if (thread.pid == thread.tid) {
591 if (!all_threads && dump_threads.find(thread.tid) == dump_threads.end()) {
594 ForkRecord fork_record = CreateForkRecord(*attr, thread.pid, thread.tid, thread.pid, thread.pid);
595 if (!ProcessRecord(&fork_record)) {
598 CommRecord comm_record = CreateCommRecord(*attr, thread.pid, thread.tid, thread.comm);
599 if (!ProcessRecord(&comm_record)) {
606 bool RecordCommand::CollectRecordsFromKernel(const char* data, size_t size) {
607 record_cache_->Push(data, size);
609 std::unique_ptr<Record> r = record_cache_->Pop();
613 if (!ProcessRecord(r.get())) {
620 bool RecordCommand::ProcessRecord(Record* record) {
621 BuildThreadTree(*record, &thread_tree_);
622 CollectHitFileInfo(record);
623 if (unwind_dwarf_callchain_ && !post_unwind_) {
624 UnwindRecord(record);
626 bool result = record_file_writer_->WriteData(record->BinaryFormat());
630 void RecordCommand::UnwindRecord(Record* record) {
631 if (record->type() == PERF_RECORD_SAMPLE) {
632 SampleRecord& r = *static_cast<SampleRecord*>(record);
633 if ((r.sample_type & PERF_SAMPLE_CALLCHAIN) && (r.sample_type & PERF_SAMPLE_REGS_USER) &&
634 (r.regs_user_data.reg_mask != 0) && (r.sample_type & PERF_SAMPLE_STACK_USER) &&
635 (!r.stack_user_data.data.empty())) {
636 ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
637 RegSet regs = CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
638 std::vector<char>& stack = r.stack_user_data.data;
639 std::vector<uint64_t> unwind_ips = UnwindCallChain(*thread, regs, stack);
640 r.callchain_data.ips.push_back(PERF_CONTEXT_USER);
641 r.callchain_data.ips.insert(r.callchain_data.ips.end(), unwind_ips.begin(), unwind_ips.end());
642 r.regs_user_data.abi = 0;
643 r.regs_user_data.reg_mask = 0;
644 r.regs_user_data.regs.clear();
645 r.stack_user_data.data.clear();
646 r.stack_user_data.dyn_size = 0;
647 r.AdjustSizeBasedOnData();
652 bool RecordCommand::PostUnwind(const std::vector<std::string>& args) {
653 thread_tree_.Clear();
654 std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(record_filename_);
655 if (reader == nullptr) {
658 std::string tmp_filename = record_filename_ + ".tmp";
659 record_file_writer_ = CreateRecordFile(tmp_filename);
660 if (record_file_writer_ == nullptr) {
663 bool result = reader->ReadDataSection(
664 [this](std::unique_ptr<Record> record) {
665 BuildThreadTree(*record, &thread_tree_);
666 UnwindRecord(record.get());
667 return record_file_writer_->WriteData(record->BinaryFormat());
673 if (!DumpAdditionalFeatures(args)) {
676 if (!record_file_writer_->Close()) {
680 if (unlink(record_filename_.c_str()) != 0) {
681 PLOG(ERROR) << "failed to remove " << record_filename_;
684 if (rename(tmp_filename.c_str(), record_filename_.c_str()) != 0) {
685 PLOG(ERROR) << "failed to rename " << tmp_filename << " to " << record_filename_;
691 bool RecordCommand::DumpAdditionalFeatures(const std::vector<std::string>& args) {
692 size_t feature_count = (branch_sampling_ != 0 ? 5 : 4);
693 if (!record_file_writer_->WriteFeatureHeader(feature_count)) {
696 if (!DumpBuildIdFeature()) {
700 if (TEMP_FAILURE_RETRY(uname(&uname_buf)) != 0) {
701 PLOG(ERROR) << "uname() failed";
704 if (!record_file_writer_->WriteFeatureString(PerfFileFormat::FEAT_OSRELEASE, uname_buf.release)) {
707 if (!record_file_writer_->WriteFeatureString(PerfFileFormat::FEAT_ARCH, uname_buf.machine)) {
711 std::string exec_path = "simpleperf";
712 GetExecPath(&exec_path);
713 std::vector<std::string> cmdline;
714 cmdline.push_back(exec_path);
715 cmdline.push_back("record");
716 cmdline.insert(cmdline.end(), args.begin(), args.end());
717 if (!record_file_writer_->WriteCmdlineFeature(cmdline)) {
720 if (branch_sampling_ != 0 && !record_file_writer_->WriteBranchStackFeature()) {
726 bool RecordCommand::DumpBuildIdFeature() {
727 std::vector<BuildIdRecord> build_id_records;
729 // Add build_ids for kernel/modules.
730 for (auto& filename : hit_kernel_modules_) {
731 if (filename == DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID) {
732 if (!GetKernelBuildId(&build_id)) {
733 LOG(DEBUG) << "can't read build_id for kernel";
736 build_id_records.push_back(
737 CreateBuildIdRecord(true, UINT_MAX, build_id, DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID));
739 std::string path = filename;
740 std::string module_name = basename(&path[0]);
741 if (android::base::EndsWith(module_name, ".ko")) {
742 module_name = module_name.substr(0, module_name.size() - 3);
744 if (!GetModuleBuildId(module_name, &build_id)) {
745 LOG(DEBUG) << "can't read build_id for module " << module_name;
748 build_id_records.push_back(CreateBuildIdRecord(true, UINT_MAX, build_id, filename));
751 // Add build_ids for user elf files.
752 for (auto& filename : hit_user_files_) {
753 if (filename == DEFAULT_EXECNAME_FOR_THREAD_MMAP) {
756 if (!GetBuildIdFromElfFile(filename, &build_id)) {
757 LOG(DEBUG) << "can't read build_id from file " << filename;
760 build_id_records.push_back(CreateBuildIdRecord(false, UINT_MAX, build_id, filename));
762 if (!record_file_writer_->WriteBuildIdFeature(build_id_records)) {
768 void RecordCommand::CollectHitFileInfo(const Record* record) {
769 if (record->type() == PERF_RECORD_SAMPLE) {
770 auto r = *static_cast<const SampleRecord*>(record);
771 bool in_kernel = ((r.header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_KERNEL);
772 const ThreadEntry* thread = thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
773 const MapEntry* map = thread_tree_.FindMap(thread, r.ip_data.ip, in_kernel);
775 hit_kernel_modules_.insert(map->dso->Path());
777 hit_user_files_.insert(map->dso->Path());
782 __attribute__((constructor)) static void RegisterRecordCommand() {
783 RegisterCommand("record", [] { return std::unique_ptr<Command>(new RecordCommand()); });