diff options
author | Yabin Cui <yabinc@google.com> | 2018-08-30 00:20:46 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-08-30 00:20:46 +0000 |
commit | 20cf8d3ae40d6d35f6fd7ebfb739f09a28430323 (patch) | |
tree | 98efa168c3935508b5b708ec00f9d1720bf1bfd6 | |
parent | 410c1f1e753d7cae8eb40818d4955f8137bc8bc5 (diff) | |
parent | 5d5c01a3e66b4516defe3741dcfa43590aa544dc (diff) | |
download | extras-20cf8d3ae40d6d35f6fd7ebfb739f09a28430323.tar.gz |
Merge "simpleperf: sync JIT debug info with records."
-rw-r--r-- | simpleperf/JITDebugReader.cpp | 84 | ||||
-rw-r--r-- | simpleperf/JITDebugReader.h | 91 | ||||
-rw-r--r-- | simpleperf/OfflineUnwinder.cpp | 15 | ||||
-rw-r--r-- | simpleperf/OfflineUnwinder.h | 5 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 55 | ||||
-rw-r--r-- | simpleperf/event_selection_set.cpp | 24 |
6 files changed, 184 insertions, 90 deletions
diff --git a/simpleperf/JITDebugReader.cpp b/simpleperf/JITDebugReader.cpp index 274ea013..d6b9685a 100644 --- a/simpleperf/JITDebugReader.cpp +++ b/simpleperf/JITDebugReader.cpp @@ -119,9 +119,9 @@ static_assert(sizeof(JITCodeEntry32) == 32, ""); #endif static_assert(sizeof(JITCodeEntry64) == 40, ""); -bool JITDebugReader::RegisterSymFileCallback(IOEventLoop* loop, - const symfile_callback_t& callback) { - symfile_callback_ = callback; +bool JITDebugReader::RegisterDebugInfoCallback(IOEventLoop* loop, + const debug_info_callback_t& callback) { + debug_info_callback_ = callback; read_event_ = loop->AddPeriodicEvent(SecondToTimeval(kUpdateJITDebugInfoIntervalInMs / 1000.0), [this]() { return ReadAllProcesses(); }); return (read_event_ != nullptr && IOEventLoop::DisableEvent(read_event_)); @@ -173,6 +173,20 @@ bool JITDebugReader::UpdateRecord(const Record* record) { return ReadProcess(r->tid_data.pid); } } + return FlushDebugInfo(record->Timestamp()); +} + +bool JITDebugReader::FlushDebugInfo(uint64_t timestamp) { + if (sync_with_records_) { + if (!debug_info_q_.empty() && debug_info_q_.top().timestamp < timestamp) { + std::vector<JITDebugInfo> debug_info; + while (!debug_info_q_.empty() && debug_info_q_.top().timestamp < timestamp) { + debug_info.emplace_back(debug_info_q_.top()); + debug_info_q_.pop(); + } + return debug_info_callback_(debug_info, false); + } + } return true; } @@ -180,11 +194,10 @@ bool JITDebugReader::ReadAllProcesses() { if (!IOEventLoop::DisableEvent(read_event_)) { return false; } - std::vector<JITSymFile> jit_symfiles; - std::vector<DexSymFile> dex_symfiles; + std::vector<JITDebugInfo> debug_info; for (auto it = processes_.begin(); it != processes_.end();) { Process& process = it->second; - ReadProcess(process, &jit_symfiles, &dex_symfiles); + ReadProcess(process, &debug_info); if (process.died) { LOG(DEBUG) << "Stop monitoring process " << process.pid; it = processes_.erase(it); @@ -192,10 +205,8 @@ bool JITDebugReader::ReadAllProcesses() { ++it; } } - if (!jit_symfiles.empty() || !dex_symfiles.empty()) { - if (!symfile_callback_(jit_symfiles, dex_symfiles, true)) { - return false; - } + if (!AddDebugInfo(debug_info, true)) { + return false; } if (!processes_.empty()) { return IOEventLoop::EnableEvent(read_event_); @@ -206,18 +217,14 @@ bool JITDebugReader::ReadAllProcesses() { bool JITDebugReader::ReadProcess(pid_t pid) { auto it = processes_.find(pid); if (it != processes_.end()) { - std::vector<JITSymFile> jit_symfiles; - std::vector<DexSymFile> dex_symfiles; - ReadProcess(it->second, &jit_symfiles, &dex_symfiles); - if (!jit_symfiles.empty() || !dex_symfiles.empty()) { - return symfile_callback_(jit_symfiles, dex_symfiles, false); - } + std::vector<JITDebugInfo> debug_info; + ReadProcess(it->second, &debug_info); + return AddDebugInfo(debug_info, false); } return true; } -void JITDebugReader::ReadProcess(Process& process, std::vector<JITSymFile>* jit_symfiles, - std::vector<DexSymFile>* dex_symfiles) { +void JITDebugReader::ReadProcess(Process& process, std::vector<JITDebugInfo>* debug_info) { if (process.died || (!process.initialized && !InitializeProcess(process))) { return; } @@ -246,8 +253,7 @@ void JITDebugReader::ReadProcess(Process& process, std::vector<JITSymFile>* jit_ return descriptor.action_seqlock == tmp_dex_descriptor.action_seqlock; }; - auto read_new_symfiles = [&](Descriptor& new_descriptor, Descriptor& old_descriptor, - bool is_jit) { + auto read_debug_info = [&](Descriptor& new_descriptor, Descriptor& old_descriptor, bool is_jit) { bool has_update = new_descriptor.action_seqlock != old_descriptor.action_seqlock && (new_descriptor.action_seqlock & 1) == 0; LOG(DEBUG) << (is_jit ? "JIT" : "Dex") << " symfiles of pid " << process.pid @@ -274,16 +280,16 @@ void JITDebugReader::ReadProcess(Process& process, std::vector<JITSymFile>* jit_ return true; } if (is_jit) { - ReadJITSymFiles(process, new_entries, jit_symfiles); + ReadJITCodeDebugInfo(process, new_entries, debug_info); } else { - ReadDexSymFiles(process, new_entries, dex_symfiles); + ReadDexFileDebugInfo(process, new_entries, debug_info); } return true; }; - if (read_new_symfiles(jit_descriptor, process.last_jit_descriptor, true)) { + if (read_debug_info(jit_descriptor, process.last_jit_descriptor, true)) { process.last_jit_descriptor = jit_descriptor; } - if (read_new_symfiles(dex_descriptor, process.last_dex_descriptor, false)) { + if (read_debug_info(dex_descriptor, process.last_dex_descriptor, false)) { process.last_dex_descriptor = dex_descriptor; } } @@ -465,6 +471,7 @@ bool JITDebugReader::ReadNewCodeEntriesImpl(Process& process, const Descriptor& code_entry.addr = current_entry_addr; code_entry.symfile_addr = entry.symfile_addr; code_entry.symfile_size = entry.symfile_size; + code_entry.timestamp = entry.register_timestamp; new_code_entries->push_back(code_entry); entry_addr_set.insert(current_entry_addr); prev_entry_addr = current_entry_addr; @@ -473,8 +480,9 @@ bool JITDebugReader::ReadNewCodeEntriesImpl(Process& process, const Descriptor& return true; } -void JITDebugReader::ReadJITSymFiles(Process& process, const std::vector<CodeEntry>& jit_entries, - std::vector<JITSymFile>* jit_symfiles) { +void JITDebugReader::ReadJITCodeDebugInfo(Process& process, + const std::vector<CodeEntry>& jit_entries, + std::vector<JITDebugInfo>* debug_info) { std::vector<char> data; for (auto& jit_entry : jit_entries) { if (jit_entry.symfile_size > MAX_JIT_SYMFILE_SIZE) { @@ -509,12 +517,14 @@ void JITDebugReader::ReadJITSymFiles(Process& process, const std::vector<CodeEnt if (keep_symfiles_) { tmp_file->DoNotRemove(); } - jit_symfiles->emplace_back(process.pid, min_addr, max_addr - min_addr, tmp_file->path); + debug_info->emplace_back(process.pid, jit_entry.timestamp, min_addr, max_addr - min_addr, + tmp_file->path); } } -void JITDebugReader::ReadDexSymFiles(Process& process, const std::vector<CodeEntry>& dex_entries, - std::vector<DexSymFile>* dex_symfiles) { +void JITDebugReader::ReadDexFileDebugInfo(Process& process, + const std::vector<CodeEntry>& dex_entries, + std::vector<JITDebugInfo>* debug_info) { std::vector<ThreadMmap> thread_mmaps; if (!GetThreadMmapsInProcess(process.pid, &thread_mmaps)) { process.died = true; @@ -547,11 +557,25 @@ void JITDebugReader::ReadDexSymFiles(Process& process, const std::vector<CodeEnt } // Offset of dex file in .vdex file or .apk file. uint64_t dex_file_offset = dex_entry.symfile_addr - it->start_addr + it->pgoff; - dex_symfiles->emplace_back(dex_file_offset, file_path); + debug_info->emplace_back(process.pid, dex_entry.timestamp, dex_file_offset, file_path); LOG(VERBOSE) << "DexFile " << file_path << "+" << std::hex << dex_file_offset << " in map [" << it->start_addr << " - " << (it->start_addr + it->len) << "] with size " << dex_entry.symfile_size; } } +bool JITDebugReader::AddDebugInfo(const std::vector<JITDebugInfo>& debug_info, + bool sync_kernel_records) { + if (!debug_info.empty()) { + if (sync_with_records_) { + for (auto& info : debug_info) { + debug_info_q_.push(std::move(info)); + } + } else { + return debug_info_callback_(debug_info, sync_kernel_records); + } + } + return true; +} + } // namespace simpleperf diff --git a/simpleperf/JITDebugReader.h b/simpleperf/JITDebugReader.h index 8561461a..73566f6b 100644 --- a/simpleperf/JITDebugReader.h +++ b/simpleperf/JITDebugReader.h @@ -21,6 +21,7 @@ #include <functional> #include <memory> +#include <queue> #include <stack> #include <unordered_map> #include <unordered_set> @@ -34,35 +35,57 @@ namespace simpleperf { -struct JITSymFile { - pid_t pid; // The process having the JITed code - uint64_t addr; // The start addr of the JITed code - uint64_t len; // The length of the JITed code - std::string file_path; // The path of a temporary ELF file storing debug info of the JITed code - - JITSymFile() {} - JITSymFile(pid_t pid, uint64_t addr, uint64_t len, const std::string& file_path) - : pid(pid), addr(addr), len(len), file_path(file_path) {} -}; - -struct DexSymFile { - uint64_t dex_file_offset; // The offset of the dex file in the file containing it - std::string file_path; // The path of file containing the dex file - - DexSymFile() {} - DexSymFile(uint64_t dex_file_offset, const std::string& file_path) - : dex_file_offset(dex_file_offset), file_path(file_path) {} +// JITDebugInfo represents the debug info of a JITed Java method or a dex file. +struct JITDebugInfo { + enum { + JIT_DEBUG_JIT_CODE, + JIT_DEBUG_DEX_FILE, + } type; + pid_t pid; // Process of the debug info + uint64_t timestamp; // Monotonic timestamp for the creation of the debug info + union { + struct { + uint64_t jit_code_addr; // The start addr of the JITed code + uint64_t jit_code_len; // The end addr of the JITed code + }; + uint64_t dex_file_offset; // The offset of the dex file in the file containing it + }; + // For JITed code, it is the path of a temporary ELF file storing its debug info. + // For dex file, it is the path of the file containing the dex file. + std::string file_path; + + JITDebugInfo(pid_t pid, uint64_t timestamp, uint64_t jit_code_addr, uint64_t jit_code_len, + const std::string& file_path) + : type(JIT_DEBUG_JIT_CODE), pid(pid), timestamp(timestamp), jit_code_addr(jit_code_addr), + jit_code_len(jit_code_len), file_path(file_path) {} + + JITDebugInfo(pid_t pid, uint64_t timestamp, uint64_t dex_file_offset, + const std::string& file_path) + : type(JIT_DEBUG_DEX_FILE), pid(pid), timestamp(timestamp), dex_file_offset(dex_file_offset), + file_path(file_path) {} + + bool operator>(const JITDebugInfo& other) const { + return timestamp > other.timestamp; + } }; // JITDebugReader reads debug info of JIT code and dex files of processes using ART. The // corresponding debug interface in ART is at art/runtime/jit/debugger_interface.cc. class JITDebugReader { public: - JITDebugReader(bool keep_symfiles) : keep_symfiles_(keep_symfiles) {} + // keep_symfiles: whether to keep dumped JIT debug info files after recording. Usually they + // are only kept for debug unwinding. + // sync_with_records: If true, sync debug info with records based on monotonic timestamp. + // Otherwise, save debug info whenever they are added. + JITDebugReader(bool keep_symfiles, bool sync_with_records) + : keep_symfiles_(keep_symfiles), sync_with_records_(sync_with_records) {} - typedef std::function<bool(const std::vector<JITSymFile>&, const std::vector<DexSymFile>&, bool)> - symfile_callback_t; - bool RegisterSymFileCallback(IOEventLoop* loop, const symfile_callback_t& callback); + bool SyncWithRecords() const { + return sync_with_records_; + } + + typedef std::function<bool(const std::vector<JITDebugInfo>&, bool)> debug_info_callback_t; + bool RegisterDebugInfoCallback(IOEventLoop* loop, const debug_info_callback_t& callback); // There are two ways to select which processes to monitor. One is using MonitorProcess(), the // other is finding all processes having libart.so using records. @@ -71,6 +94,11 @@ class JITDebugReader { // Read new debug info from all monitored processes. bool ReadAllProcesses(); + // Read new debug info from one process. + bool ReadProcess(pid_t pid); + + // Flush all debug info registered before timestamp. + bool FlushDebugInfo(uint64_t timestamp); private: @@ -116,9 +144,7 @@ class JITDebugReader { uint64_t dex_descriptor_offset = 0; }; - bool ReadProcess(pid_t pid); - void ReadProcess(Process& process, std::vector<JITSymFile>* jit_symfiles, - std::vector<DexSymFile>* dex_symfiles); + void ReadProcess(Process& process, std::vector<JITDebugInfo>* debug_info); bool InitializeProcess(Process& process); const DescriptorsLocation* GetDescriptorsLocation(const std::string& art_lib_path, bool is_64bit); @@ -136,14 +162,16 @@ class JITDebugReader { uint64_t last_action_timestamp, uint32_t read_entry_limit, std::vector<CodeEntry>* new_code_entries); - void ReadJITSymFiles(Process& process, const std::vector<CodeEntry>& jit_entries, - std::vector<JITSymFile>* jit_symfiles); - void ReadDexSymFiles(Process& process, const std::vector<CodeEntry>& dex_entries, - std::vector<DexSymFile>* dex_symfiles); + void ReadJITCodeDebugInfo(Process& process, const std::vector<CodeEntry>& jit_entries, + std::vector<JITDebugInfo>* debug_info); + void ReadDexFileDebugInfo(Process& process, const std::vector<CodeEntry>& dex_entries, + std::vector<JITDebugInfo>* debug_info); + bool AddDebugInfo(const std::vector<JITDebugInfo>& jit_symfiles, bool sync_kernel_records); bool keep_symfiles_ = false; + bool sync_with_records_ = false; IOEventRef read_event_ = nullptr; - symfile_callback_t symfile_callback_; + debug_info_callback_t debug_info_callback_; // Keys are pids of processes having libart.so, values show whether a process has been monitored. std::unordered_map<pid_t, bool> pids_with_art_lib_; @@ -152,6 +180,9 @@ class JITDebugReader { std::unordered_map<pid_t, Process> processes_; std::unordered_map<std::string, DescriptorsLocation> descriptors_location_cache_; std::vector<char> descriptors_buf_; + + std::priority_queue<JITDebugInfo, std::vector<JITDebugInfo>, std::greater<JITDebugInfo>> + debug_info_q_; }; } //namespace simpleperf diff --git a/simpleperf/OfflineUnwinder.cpp b/simpleperf/OfflineUnwinder.cpp index 4e7da138..25a4914b 100644 --- a/simpleperf/OfflineUnwinder.cpp +++ b/simpleperf/OfflineUnwinder.cpp @@ -192,6 +192,9 @@ bool OfflineUnwinder::UnwindCallChain(const ThreadEntry& thread, const RegSet& r if (collect_stat_) { start_time = GetSystemClock(); } + is_callchain_broken_for_incomplete_jit_debug_info_ = false; + ips->clear(); + sps->clear(); std::vector<uint64_t> result; uint64_t sp_reg_value; if (!regs.GetSpRegValue(&sp_reg_value)) { @@ -213,6 +216,7 @@ bool OfflineUnwinder::UnwindCallChain(const ThreadEntry& thread, const RegSet& r stack_memory); unwinder.SetResolveNames(false); unwinder.Unwind(); + size_t last_jit_method_frame = UINT_MAX; for (auto& frame : unwinder.frames()) { // Unwinding in arm architecture can return 0 pc address. @@ -220,14 +224,23 @@ bool OfflineUnwinder::UnwindCallChain(const ThreadEntry& thread, const RegSet& r // 1. In an executable map not backed by a file. Note that RecordCommand::ShouldOmitRecord() // may omit maps only exist memory. // 2. An incorrectly unwound frame. Like caused by invalid stack data, as in - // SampleRecord::GetValidStackSize(). + // SampleRecord::GetValidStackSize(). Or caused by incomplete JIT debug info. // We want to remove this frame and callchains following it in either case. if (frame.pc == 0 || frame.map_start == 0) { + is_callchain_broken_for_incomplete_jit_debug_info_ = true; break; } + if (frame.map_flags & unwindstack::MAPS_FLAGS_JIT_SYMFILE_MAP) { + last_jit_method_frame = ips->size(); + } ips->push_back(frame.pc); sps->push_back(frame.sp); } + // If the unwound frames stop near to a JITed method, it may be caused by incomplete JIT debug + // info. + if (last_jit_method_frame != UINT_MAX && last_jit_method_frame + 3 > ips->size()) { + is_callchain_broken_for_incomplete_jit_debug_info_ = true; + } uint64_t ip_reg_value; if (!regs.GetIpRegValue(&ip_reg_value)) { diff --git a/simpleperf/OfflineUnwinder.h b/simpleperf/OfflineUnwinder.h index 56a39afe..e5091167 100644 --- a/simpleperf/OfflineUnwinder.h +++ b/simpleperf/OfflineUnwinder.h @@ -80,9 +80,14 @@ class OfflineUnwinder { return unwinding_result_; } + bool IsCallChainBrokenForIncompleteJITDebugInfo() { + return is_callchain_broken_for_incomplete_jit_debug_info_; + } + private: bool collect_stat_; UnwindingResult unwinding_result_; + bool is_callchain_broken_for_incomplete_jit_debug_info_; std::unordered_map<pid_t, UnwindMaps> cached_maps_; }; diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 60307b83..ff1977ee 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -155,7 +155,8 @@ class RecordCommand : public Command { "-g Same as '--call-graph dwarf'.\n" "--clockid clock_id Generate timestamps of samples using selected clock.\n" " Possible values are: realtime, monotonic,\n" -" monotonic_raw, boottime, perf. Default is perf.\n" +" monotonic_raw, boottime, perf. If supported, default\n" +" is monotonic, otherwise is perf.\n" "--cpu cpu_item1,cpu_item2,...\n" " Collect samples only on the selected cpus. cpu_item can be cpu\n" " number like 1, or cpu range like 0-3.\n" @@ -232,7 +233,6 @@ class RecordCommand : public Command { duration_in_sec_(0), can_dump_kernel_symbols_(true), dump_symbols_(true), - clockid_("perf"), event_selection_set_(false), mmap_page_range_(std::make_pair(1, DESIRED_PAGES_IN_MAPPED_BUFFER)), record_filename_("perf.data"), @@ -278,8 +278,7 @@ class RecordCommand : public Command { bool SaveRecordForPostUnwinding(Record* record); bool SaveRecordAfterUnwinding(Record* record); bool SaveRecordWithoutUnwinding(Record* record); - bool ProcessJITDebugInfo(const std::vector<JITSymFile>& jit_symfiles, - const std::vector<DexSymFile>& dex_symfiles, bool sync_kernel_records); + bool ProcessJITDebugInfo(const std::vector<JITDebugInfo>& debug_info, bool sync_kernel_records); void UpdateRecord(Record* record); bool UnwindRecord(SampleRecord& r); @@ -447,7 +446,8 @@ bool RecordCommand::PrepareRecording(Workload* workload) { // `-g --no-unwind` option is used, we want to keep symfiles to support unwinding in // the debug-unwind cmd. bool keep_symfiles = dwarf_callchain_sampling_ && !unwind_dwarf_callchain_; - jit_debug_reader_.reset(new JITDebugReader(keep_symfiles)); + bool sync_with_records = clockid_ == "monotonic"; + jit_debug_reader_.reset(new JITDebugReader(keep_symfiles, sync_with_records)); // To profile java code, need to dump maps containing vdex files, which are not executable. event_selection_set_.SetRecordNotExecutableMaps(true); } @@ -497,11 +497,10 @@ bool RecordCommand::PrepareRecording(Workload* workload) { } } if (jit_debug_reader_) { - auto callback = [this](const std::vector<JITSymFile>& jit_symfiles, - const std::vector<DexSymFile>& dex_symfiles, bool sync_kernel_records) { - return ProcessJITDebugInfo(jit_symfiles, dex_symfiles, sync_kernel_records); + auto callback = [this](const std::vector<JITDebugInfo>& debug_info, bool sync_kernel_records) { + return ProcessJITDebugInfo(debug_info, sync_kernel_records); }; - if (!jit_debug_reader_->RegisterSymFileCallback(loop, callback)) { + if (!jit_debug_reader_->RegisterDebugInfoCallback(loop, callback)) { return false; } if (!app_package_name_.empty()) { @@ -882,6 +881,9 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args, // No need to dump kernel symbols as we will dump all required symbols. can_dump_kernel_symbols_ = false; } + if (clockid_.empty()) { + clockid_ = IsSettingClockIdSupported() ? "monotonic" : "perf"; + } non_option_args->clear(); for (; i < args.size(); ++i) { @@ -1218,21 +1220,23 @@ bool RecordCommand::SaveRecordWithoutUnwinding(Record* record) { return record_file_writer_->WriteRecord(*record); } -bool RecordCommand::ProcessJITDebugInfo(const std::vector<JITSymFile>& jit_symfiles, - const std::vector<DexSymFile>& dex_symfiles, +bool RecordCommand::ProcessJITDebugInfo(const std::vector<JITDebugInfo>& debug_info, bool sync_kernel_records) { EventAttrWithId attr_id = event_selection_set_.GetEventAttrWithId()[0]; - for (auto& symfile : jit_symfiles) { - Mmap2Record record(*attr_id.attr, false, symfile.pid, symfile.pid, - symfile.addr, symfile.len, 0, map_flags::PROT_JIT_SYMFILE_MAP, - symfile.file_path, attr_id.ids[0], last_record_timestamp_); - if (!ProcessRecord(&record)) { - return false; + for (auto& info : debug_info) { + if (info.type == JITDebugInfo::JIT_DEBUG_JIT_CODE) { + uint64_t timestamp = jit_debug_reader_->SyncWithRecords() ? info.timestamp + : last_record_timestamp_; + Mmap2Record record(*attr_id.attr, false, info.pid, info.pid, + info.jit_code_addr, info.jit_code_len, 0, map_flags::PROT_JIT_SYMFILE_MAP, + info.file_path, attr_id.ids[0], timestamp); + if (!ProcessRecord(&record)) { + return false; + } + } else { + thread_tree_.AddDexFileOffset(info.file_path, info.dex_file_offset); } } - for (auto& symfile : dex_symfiles) { - thread_tree_.AddDexFileOffset(symfile.file_path, symfile.dex_file_offset); - } // We want to let samples see the most recent JIT maps generated before them, but no JIT maps // generated after them. So process existing samples each time generating new JIT maps. We prefer // to process samples after processing JIT maps. Because some of the samples may hit the new JIT @@ -1320,6 +1324,17 @@ bool RecordCommand::UnwindRecord(SampleRecord& r) { r.GetValidStackSize(), &ips, &sps)) { return false; } + // The unwinding may fail if JIT debug info isn't the latest. In this case, read JIT debug info + // from the process and retry unwinding. + if (jit_debug_reader_ && !post_unwind_ && + offline_unwinder_->IsCallChainBrokenForIncompleteJITDebugInfo()) { + jit_debug_reader_->ReadProcess(r.tid_data.pid); + jit_debug_reader_->FlushDebugInfo(r.Timestamp()); + if (!offline_unwinder_->UnwindCallChain(*thread, regs, r.stack_user_data.data, + r.GetValidStackSize(), &ips, &sps)) { + return false; + } + } r.ReplaceRegAndStackWithCallChain(ips); if (callchain_joiner_) { return callchain_joiner_->AddCallChain(r.tid_data.pid, r.tid_data.tid, diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp index fa7777dd..c91d96a3 100644 --- a/simpleperf/event_selection_set.cpp +++ b/simpleperf/event_selection_set.cpp @@ -101,16 +101,22 @@ bool IsDumpingRegsForTracepointEventsSupported() { } bool IsSettingClockIdSupported() { - const EventType* type = FindEventTypeByName("cpu-cycles"); - if (type == nullptr) { - return false; + // Do the real check only once and keep the result in a static variable. + static int is_supported = -1; + if (is_supported == -1) { + const EventType* type = FindEventTypeByName("cpu-cycles"); + if (type == nullptr) { + is_supported = 0; + } else { + // Check if the kernel supports setting clockid, which was added in kernel 4.0. Just check + // with one clockid is enough. Because all needed clockids were supported before kernel 4.0. + perf_event_attr attr = CreateDefaultPerfEventAttr(*type); + attr.use_clockid = 1; + attr.clockid = CLOCK_MONOTONIC; + is_supported = IsEventAttrSupported(attr) ? 1 : 0; + } } - // Check if the kernel supports setting clockid, which was added in kernel 4.0. Just check with - // one clockid is enough. Because all needed clockids were supported before kernel 4.0. - perf_event_attr attr = CreateDefaultPerfEventAttr(*type); - attr.use_clockid = 1; - attr.clockid = CLOCK_MONOTONIC; - return IsEventAttrSupported(attr); + return is_supported; } bool IsMmap2Supported() { |