summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2018-08-30 00:20:46 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-08-30 00:20:46 +0000
commit20cf8d3ae40d6d35f6fd7ebfb739f09a28430323 (patch)
tree98efa168c3935508b5b708ec00f9d1720bf1bfd6
parent410c1f1e753d7cae8eb40818d4955f8137bc8bc5 (diff)
parent5d5c01a3e66b4516defe3741dcfa43590aa544dc (diff)
downloadextras-20cf8d3ae40d6d35f6fd7ebfb739f09a28430323.tar.gz
Merge "simpleperf: sync JIT debug info with records."
-rw-r--r--simpleperf/JITDebugReader.cpp84
-rw-r--r--simpleperf/JITDebugReader.h91
-rw-r--r--simpleperf/OfflineUnwinder.cpp15
-rw-r--r--simpleperf/OfflineUnwinder.h5
-rw-r--r--simpleperf/cmd_record.cpp55
-rw-r--r--simpleperf/event_selection_set.cpp24
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() {