summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--simpleperf/JITDebugReader.cpp2
-rw-r--r--simpleperf/OfflineUnwinder.cpp7
-rw-r--r--simpleperf/cmd_record.cpp34
-rw-r--r--simpleperf/environment.cpp3
-rw-r--r--simpleperf/event_selection_set.h2
-rw-r--r--simpleperf/record.cpp18
-rw-r--r--simpleperf/record.h3
-rw-r--r--simpleperf/thread_tree.cpp12
-rw-r--r--simpleperf/thread_tree.h14
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.