diff options
-rw-r--r-- | simpleperf/JITDebugReader.cpp | 2 | ||||
-rw-r--r-- | simpleperf/OfflineUnwinder.cpp | 7 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 34 | ||||
-rw-r--r-- | simpleperf/environment.cpp | 3 | ||||
-rw-r--r-- | simpleperf/event_selection_set.h | 2 | ||||
-rw-r--r-- | simpleperf/record.cpp | 18 | ||||
-rw-r--r-- | simpleperf/record.h | 3 | ||||
-rw-r--r-- | simpleperf/thread_tree.cpp | 12 | ||||
-rw-r--r-- | simpleperf/thread_tree.h | 14 |
9 files changed, 80 insertions, 15 deletions
diff --git a/simpleperf/JITDebugReader.cpp b/simpleperf/JITDebugReader.cpp index 11852292..ae680e73 100644 --- a/simpleperf/JITDebugReader.cpp +++ b/simpleperf/JITDebugReader.cpp @@ -76,7 +76,7 @@ struct JITCodeEntry { } }; -// Match the format of JITCodeEntry in art/runtime/jit/debugger_itnerface.cc. +// Match the format of JITCodeEntry in art/runtime/jit/debugger_interface.cc. template <typename ADDRT> struct __attribute__((packed)) PackedJITCodeEntry { ADDRT next_addr; diff --git a/simpleperf/OfflineUnwinder.cpp b/simpleperf/OfflineUnwinder.cpp index 9d561447..ebb0be9f 100644 --- a/simpleperf/OfflineUnwinder.cpp +++ b/simpleperf/OfflineUnwinder.cpp @@ -23,6 +23,7 @@ #include <unwindstack/MachineArm64.h> #include <unwindstack/MachineX86.h> #include <unwindstack/MachineX86_64.h> +#include <unwindstack/Maps.h> #include <unwindstack/Regs.h> #include <unwindstack/RegsArm.h> #include <unwindstack/RegsArm64.h> @@ -38,6 +39,10 @@ #include "read_apk.h" #include "thread_tree.h" +static_assert(simpleperf::map_flags::PROT_JIT_SYMFILE_MAP == PROT_JIT_SYMFILE_MAP, ""); +static_assert(simpleperf::map_flags::PROT_JIT_SYMFILE_MAP == + unwindstack::MAPS_FLAGS_JIT_SYMFILE_MAP, ""); + namespace simpleperf { static unwindstack::Regs* GetBacktraceRegs(const RegSet& regs) { @@ -154,7 +159,7 @@ bool OfflineUnwinder::UnwindCallChain(const ThreadEntry& thread, const RegSet& r } } } - bt_map.flags = PROT_READ | PROT_EXEC; + bt_map.flags = PROT_READ | PROT_EXEC | map->flags; } cached_map.map.reset(BacktraceMap::CreateOffline(thread.pid, bt_maps)); if (!cached_map.map) { diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 35d0f939..6b7ce94d 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -224,7 +224,8 @@ class RecordCommand : public Command { trace_offcpu_(false), exclude_kernel_callchain_(false), allow_callchain_joiner_(true), - callchain_joiner_min_matching_nodes_(1u) { + callchain_joiner_min_matching_nodes_(1u), + last_record_timestamp_(0u) { // If we run `adb shell simpleperf record xxx` and stop profiling by ctrl-c, adb closes // sockets connecting simpleperf. After that, simpleperf will receive SIGPIPE when writing // to stdout/stderr, which is a problem when we use '--app' option. So ignore SIGPIPE to @@ -305,6 +306,7 @@ class RecordCommand : public Command { std::unique_ptr<CallChainJoiner> callchain_joiner_; std::unique_ptr<JITDebugReader> jit_debug_reader_; + uint64_t last_record_timestamp_; // used to insert Mmap2Records for JIT debug info }; bool RecordCommand::Run(const std::vector<std::string>& args) { @@ -457,7 +459,8 @@ bool RecordCommand::PrepareRecording(Workload* workload) { } } // Profiling JITed/interpreted code is supported starting from Android P. - if (app_pid != 0 && GetAndroidVersion() >= 9) { + const int kAndroidVersionP = 9; + if (app_pid != 0 && GetAndroidVersion() >= kAndroidVersionP) { // JIT symfiles are stored in temporary files, and are deleted after recording. But if // `-g --no-unwind` option is used, we want to keep symfiles to support unwinding in // the debug-unwind cmd. @@ -467,7 +470,13 @@ bool RecordCommand::PrepareRecording(Workload* workload) { if (!UpdateJITDebugInfo()) { return false; } - if (!loop->AddPeriodicEvent(SecondToTimeval(0.1), [&]() { return UpdateJITDebugInfo(); })) { + // It takes about 30us-130us on Pixel (depending on the cpu frequency) to check update when + // no update happens (most time spent in process_vm_preadv). We want to know the JIT debug + // info change as soon as possible, while not wasting too much time checking updates. So use + // a period of 100 ms. + const double kUpdateJITDebugInfoPeriodInSecond = 0.1; + if (!loop->AddPeriodicEvent(SecondToTimeval(kUpdateJITDebugInfoPeriodInSecond), + [&]() { return UpdateJITDebugInfo(); })) { return false; } } @@ -1038,6 +1047,7 @@ bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr& attr, } bool RecordCommand::ProcessRecord(Record* record) { + last_record_timestamp_ = record->Timestamp(); if (unwind_dwarf_callchain_) { if (post_unwind_) { return SaveRecordForPostUnwinding(record); @@ -1105,7 +1115,23 @@ bool RecordCommand::UpdateJITDebugInfo() { std::vector<JITSymFile> jit_symfiles; std::vector<DexSymFile> dex_symfiles; jit_debug_reader_->ReadUpdate(&jit_symfiles, &dex_symfiles); - // TODO: Handle jit/dex symfiles. + if (jit_symfiles.empty() && dex_symfiles.empty()) { + return true; + } + // Process records before dumping symfiles, so new symfiles won't affect old samples. + if (!event_selection_set_.ReadMmapEventData()) { + return false; + } + EventAttrWithId attr_id = event_selection_set_.GetEventAttrWithId()[0]; + for (auto& symfile : jit_symfiles) { + Mmap2Record record(*attr_id.attr, false, jit_debug_reader_->Pid(), jit_debug_reader_->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; + } + } + // TODO: Handle dex symfiles. return true; } diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index 3267870e..bd93d8d3 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -732,7 +732,10 @@ bool SignalIsIgnored(int signo) { int GetAndroidVersion() { #if defined(__ANDROID__) std::string s = android::base::GetProperty("ro.build.version.release", ""); + // The release string can be a list of numbers (like 8.1.0), a character (like Q) + // or many characters (like OMR1). if (!s.empty()) { + // Each Android version has a version number: L is 5, M is 6, N is 7, O is 8, etc. if (s[0] >= 'A' && s[0] <= 'Z') { return s[0] - 'O' + 8; } diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h index 7397c6fb..9e2a064c 100644 --- a/simpleperf/event_selection_set.h +++ b/simpleperf/event_selection_set.h @@ -128,6 +128,7 @@ class EventSelectionSet { bool ReadCounters(std::vector<CountersInfo>* counters); bool MmapEventFiles(size_t min_mmap_pages, size_t max_mmap_pages); bool PrepareToReadMmapEventData(const std::function<bool(Record*)>& callback); + bool ReadMmapEventData(); bool FinishReadMmapEventData(); // If monitored_cpus is empty, monitor all cpus. @@ -160,7 +161,6 @@ class EventSelectionSet { std::string* failed_event_type); bool MmapEventFiles(size_t mmap_pages, bool report_error); - bool ReadMmapEventData(); bool DetectCpuHotplugEvents(); bool HandleCpuOnlineEvent(int cpu); diff --git a/simpleperf/record.cpp b/simpleperf/record.cpp index a03f3771..5ae2dd6a 100644 --- a/simpleperf/record.cpp +++ b/simpleperf/record.cpp @@ -265,6 +265,22 @@ Mmap2Record::Mmap2Record(const perf_event_attr& attr, char* p) : Record(p) { sample_id.ReadFromBinaryFormat(attr, p, end); } +Mmap2Record::Mmap2Record(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid, + uint64_t addr, uint64_t len, uint64_t pgoff, uint32_t prot, + const std::string& filename, uint64_t event_id, uint64_t time) { + SetTypeAndMisc(PERF_RECORD_MMAP2, in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER); + sample_id.CreateContent(attr, event_id); + sample_id.time_data.time = time; + Mmap2RecordDataType data; + data.pid = pid; + data.tid = tid; + data.addr = addr; + data.len = len; + data.pgoff = pgoff; + data.prot = prot; + SetDataAndFilename(data, filename); +} + void Mmap2Record::SetDataAndFilename(const Mmap2RecordDataType& data, const std::string& filename) { SetSize(header_size() + sizeof(data) + Align(filename.size() + 1, 8) + @@ -285,7 +301,7 @@ void Mmap2Record::DumpData(size_t indent) const { PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data->pid, data->tid, data->addr, data->len); - PrintIndented(indent, "pgoff 0x" PRIx64 ", maj %u, min %u, ino %" PRId64 + PrintIndented(indent, "pgoff 0x%" PRIx64 ", maj %u, min %u, ino %" PRId64 ", ino_generation %" PRIu64 "\n", data->pgoff, data->maj, data->min, data->ino, data->ino_generation); diff --git a/simpleperf/record.h b/simpleperf/record.h index b862e401..6e5806ce 100644 --- a/simpleperf/record.h +++ b/simpleperf/record.h @@ -303,6 +303,9 @@ struct Mmap2Record : public Record { const char* filename; Mmap2Record(const perf_event_attr& attr, char* p); + Mmap2Record(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid, + uint64_t addr, uint64_t len, uint64_t pgoff, uint32_t prot, + const std::string& filename, uint64_t event_id, uint64_t time = 0); void SetDataAndFilename(const Mmap2RecordDataType& data, const std::string& filename); diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp index 283c697d..01ba8db1 100644 --- a/simpleperf/thread_tree.cpp +++ b/simpleperf/thread_tree.cpp @@ -128,11 +128,11 @@ Dso* ThreadTree::FindKernelDsoOrNew(const std::string& filename) { void ThreadTree::AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time, - const std::string& filename) { + const std::string& filename, uint32_t flags) { ThreadEntry* thread = FindThreadOrNew(pid, tid); Dso* dso = FindUserDsoOrNew(filename, start_addr); MapEntry* map = - AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, false)); + AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, false, flags)); FixOverlappedMap(thread->maps, map); auto pair = thread->maps->maps.insert(map); CHECK(pair.second); @@ -227,7 +227,11 @@ const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip, Dso* dso = map->dso; if (!map->in_kernel) { // Find symbol in user space shared libraries. - vaddr_in_file = ip - map->start_addr + map->dso->MinVirtualAddress(); + if (map->flags & map_flags::PROT_JIT_SYMFILE_MAP) { + vaddr_in_file = ip; + } else { + vaddr_in_file = ip - map->start_addr + map->dso->MinVirtualAddress(); + } symbol = dso->FindSymbol(vaddr_in_file); } else { if (dso != kernel_dso_.get()) { @@ -311,7 +315,7 @@ void ThreadTree::Update(const Record& record) { ? "[unknown]" : r.filename; AddThreadMap(r.data->pid, r.data->tid, r.data->addr, r.data->len, - r.data->pgoff, r.sample_id.time_data.time, filename); + r.data->pgoff, r.sample_id.time_data.time, filename, r.data->prot); } } else if (record.type() == PERF_RECORD_COMM) { const CommRecord& r = *static_cast<const CommRecord*>(&record); diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h index a3653f5f..11177027 100644 --- a/simpleperf/thread_tree.h +++ b/simpleperf/thread_tree.h @@ -34,6 +34,10 @@ constexpr char DEFAULT_EXECNAME_FOR_THREAD_MMAP[] = "//anon"; namespace simpleperf { +namespace map_flags { +constexpr uint32_t PROT_JIT_SYMFILE_MAP = 0x4000; +} // namespace map_flags + struct MapEntry { uint64_t start_addr; uint64_t len; @@ -41,15 +45,18 @@ struct MapEntry { uint64_t time; // Map creation time. Dso* dso; bool in_kernel; + uint32_t flags; + MapEntry(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time, - Dso* dso, bool in_kernel) + Dso* dso, bool in_kernel, uint32_t flags = 0) : start_addr(start_addr), len(len), pgoff(pgoff), time(time), dso(dso), - in_kernel(in_kernel) {} + in_kernel(in_kernel), + flags(flags) {} MapEntry() {} uint64_t get_end_addr() const { return start_addr + len; } @@ -95,7 +102,8 @@ class ThreadTree { void AddKernelMap(uint64_t start_addr, uint64_t len, uint64_t pgoff, uint64_t time, const std::string& filename); void AddThreadMap(int pid, int tid, uint64_t start_addr, uint64_t len, - uint64_t pgoff, uint64_t time, const std::string& filename); + uint64_t pgoff, uint64_t time, const std::string& filename, + uint32_t flags = 0); const MapEntry* FindMap(const ThreadEntry* thread, uint64_t ip, bool in_kernel); // Find map for an ip address when we don't know whether it is in kernel. |