summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2018-03-29 17:26:18 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-03-29 17:26:18 +0000
commitcd3c54452bd0fab8214f8915313c83ac46d9c697 (patch)
tree47fed1aa602718821322b48fbd8ba5c7b9d5ce0e
parent16ce379efe7087357e05082beebded72f02c4f09 (diff)
parent516a87cd05e6f7dcf2c45fd8ba6d1d0e1e1e7bdd (diff)
downloadextras-cd3c54452bd0fab8214f8915313c83ac46d9c697.tar.gz
Merge "simpleperf: support showing symbols for interpreted java code."
-rw-r--r--simpleperf/Android.mk10
-rw-r--r--simpleperf/JITDebugReader.cpp10
-rw-r--r--simpleperf/JITDebugReader.h4
-rw-r--r--simpleperf/cmd_debug_unwind.cpp1
-rw-r--r--simpleperf/cmd_dumprecord.cpp9
-rw-r--r--simpleperf/cmd_record.cpp63
-rw-r--r--simpleperf/cmd_record_test.cpp4
-rw-r--r--simpleperf/dso.cpp480
-rw-r--r--simpleperf/dso.h45
-rw-r--r--simpleperf/environment.cpp2
-rw-r--r--simpleperf/environment.h2
-rw-r--r--simpleperf/event_selection_set.cpp12
-rw-r--r--simpleperf/event_selection_set.h2
-rw-r--r--simpleperf/nonlinux_support/nonlinux_support.cpp6
-rw-r--r--simpleperf/read_dex_file.cpp108
-rw-r--r--simpleperf/read_dex_file.h35
-rw-r--r--simpleperf/read_dex_file_test.cpp40
-rw-r--r--simpleperf/record_file.h11
-rw-r--r--simpleperf/record_file_format.h7
-rw-r--r--simpleperf/record_file_reader.cpp15
-rw-r--r--simpleperf/record_file_writer.cpp20
-rw-r--r--simpleperf/thread_tree.cpp28
-rw-r--r--simpleperf/thread_tree.h7
-rw-r--r--simpleperf/tracing.cpp2
24 files changed, 602 insertions, 321 deletions
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index 1a996376..1f89cdb2 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -120,6 +120,7 @@ libsimpleperf_src_files_linux := \
JITDebugReader.cpp \
OfflineUnwinder.cpp \
perf_clock.cpp \
+ read_dex_file.cpp \
record_file_writer.cpp \
UnixSocket.cpp \
workload.cpp \
@@ -144,6 +145,8 @@ LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_target)
LOCAL_MULTILIB := both
LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
include $(LLVM_DEVICE_BUILD_MK)
+# Remove -std=c++11 flag to compile read_dex_file.cpp.
+LOCAL_CPPFLAGS := $(filter-out $(LOCAL_CPPFLAGS),-std=c++11)
include $(BUILD_STATIC_LIBRARY)
# libsimpleperf host
@@ -165,6 +168,8 @@ LOCAL_MULTILIB := both
LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
LOCAL_CXX_STL := libc++_static
include $(LLVM_HOST_BUILD_MK)
+# Remove -std=c++11 flag to compile read_dex_file.cpp.
+LOCAL_CPPFLAGS := $(filter-out $(LOCAL_CPPFLAGS),-std=c++11)
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -374,6 +379,7 @@ simpleperf_unit_test_src_files_linux := \
cmd_trace_sched_test.cpp \
environment_test.cpp \
IOEventLoop_test.cpp \
+ read_dex_file_test.cpp \
record_file_test.cpp \
UnixSocket_test.cpp \
workload_test.cpp \
@@ -461,6 +467,8 @@ LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_target)
LOCAL_MULTILIB := both
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(LLVM_DEVICE_BUILD_MK)
+# Remove -std=c++11 flag to compile read_dex_file.cpp.
+LOCAL_CPPFLAGS := $(filter-out $(LOCAL_CPPFLAGS),-std=c++11)
include $(BUILD_STATIC_TEST_LIBRARY)
# libsimpleperf_cts_test linux host
@@ -475,6 +483,8 @@ LOCAL_STATIC_LIBRARIES_linux := $(simpleperf_static_libraries_host_linux)
LOCAL_LDLIBS_linux := $(simpleperf_ldlibs_host_linux)
LOCAL_MULTILIB := both
include $(LLVM_HOST_BUILD_MK)
+# Remove -std=c++11 flag to compile read_dex_file.cpp.
+LOCAL_CPPFLAGS := $(filter-out $(LOCAL_CPPFLAGS),-std=c++11)
include $(BUILD_HOST_STATIC_TEST_LIBRARY)
# simpleperf_record_test
diff --git a/simpleperf/JITDebugReader.cpp b/simpleperf/JITDebugReader.cpp
index ae680e73..cb27996a 100644
--- a/simpleperf/JITDebugReader.cpp
+++ b/simpleperf/JITDebugReader.cpp
@@ -431,14 +431,12 @@ void JITDebugReader::ReadDexSymFiles(const std::vector<CodeEntry>& dex_entries,
// Offset of dex file in .vdex file or .apk file.
uint64_t dex_file_offset = dex_entry.symfile_addr - it->start_addr + it->pgoff;
DexSymFile symfile;
- symfile.addr = dex_entry.symfile_addr;
- symfile.len = dex_entry.symfile_size;
- symfile.pgoff = dex_file_offset;
+ symfile.dex_file_offset = dex_file_offset;
symfile.file_path = it->name;
dex_symfiles->push_back(symfile);
- LOG(VERBOSE) << "DexFile " << symfile.file_path << "+" << std::hex << dex_file_offset
- << " at [" << std::hex << symfile.addr << " - " << (symfile.addr + symfile.len)
- << "] with size " << symfile.len;
+ LOG(VERBOSE) << "DexFile " << symfile.file_path << "+" << std::hex << symfile.dex_file_offset
+ << " in map [" << it->start_addr << " - " << (it->start_addr + it->len)
+ << "] with size " << dex_entry.symfile_size;
}
}
diff --git a/simpleperf/JITDebugReader.h b/simpleperf/JITDebugReader.h
index 22149190..191b351c 100644
--- a/simpleperf/JITDebugReader.h
+++ b/simpleperf/JITDebugReader.h
@@ -37,9 +37,7 @@ struct JITSymFile {
};
struct DexSymFile {
- uint64_t addr; // The start addr of the dex file in memory.
- uint64_t len; // The size of the dex file in memory.
- uint64_t pgoff; // The file offset of the dex file in the file containing it
+ 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
};
diff --git a/simpleperf/cmd_debug_unwind.cpp b/simpleperf/cmd_debug_unwind.cpp
index ed962f9e..d0276409 100644
--- a/simpleperf/cmd_debug_unwind.cpp
+++ b/simpleperf/cmd_debug_unwind.cpp
@@ -190,6 +190,7 @@ bool DebugUnwindCommand::UnwindRecordFile() {
if (!reader_) {
return false;
}
+ reader_->LoadBuildIdAndFileFeatures(thread_tree_);
std::string record_cmd = android::base::Join(reader_->ReadCmdlineFeature(), " ");
if (record_cmd.find("--no-unwind") == std::string::npos ||
(record_cmd.find("-g") == std::string::npos &&
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 97bd7132..8fb3d672 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -235,11 +235,12 @@ bool DumpRecordCommand::DumpFeatureSection() {
uint32_t file_type;
uint64_t min_vaddr;
std::vector<Symbol> symbols;
+ std::vector<uint64_t> dex_file_offsets;
size_t read_pos = 0;
PrintIndented(1, "file:\n");
while (record_file_reader_->ReadFileFeature(read_pos, &file_path,
&file_type, &min_vaddr,
- &symbols)) {
+ &symbols, &dex_file_offsets)) {
PrintIndented(2, "file_path %s\n", file_path.c_str());
PrintIndented(2, "file_type %s\n", DsoTypeToString(static_cast<DsoType>(file_type)));
PrintIndented(2, "min_vaddr 0x%" PRIx64 "\n", min_vaddr);
@@ -248,6 +249,12 @@ bool DumpRecordCommand::DumpFeatureSection() {
PrintIndented(3, "%s [0x%" PRIx64 "-0x%" PRIx64 "]\n", symbol.DemangledName(),
symbol.addr, symbol.addr + symbol.len);
}
+ if (file_type == static_cast<uint32_t>(DSO_DEX_FILE)) {
+ PrintIndented(2, "dex_file_offsets:\n");
+ for (uint64_t offset : dex_file_offsets) {
+ PrintIndented(3, "0x%" PRIx64 "\n", offset);
+ }
+ }
}
} else if (feature == FEAT_META_INFO) {
std::unordered_map<std::string, std::string> info_map;
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 6b7ce94d..5ea42573 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -252,6 +252,7 @@ class RecordCommand : public Command {
bool DumpKernelAndModuleMmaps(const perf_event_attr& attr, uint64_t event_id);
bool DumpThreadCommAndMmaps(const perf_event_attr& attr, uint64_t event_id);
bool ProcessRecord(Record* record);
+ bool ShouldOmitRecord(Record* record);
bool SaveRecordForPostUnwinding(Record* record);
bool SaveRecordAfterUnwinding(Record* record);
bool SaveRecordWithoutUnwinding(Record* record);
@@ -415,6 +416,16 @@ bool RecordCommand::PrepareRecording(Workload* workload) {
} else {
need_to_check_targets = true;
}
+ // Profiling JITed/interpreted Java code is supported starting from Android P.
+ 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.
+ bool keep_symfiles = dwarf_callchain_sampling_ && !unwind_dwarf_callchain_;
+ jit_debug_reader_.reset(new JITDebugReader(app_pid, keep_symfiles));
+ // To profile java code, need to dump maps containing vdex files, which are not executable.
+ event_selection_set_.SetRecordNotExecutableMaps(true);
+ }
// 5. Open perf event files and create mapped buffers.
if (!event_selection_set_.OpenEventFiles(cpus_)) {
@@ -458,14 +469,7 @@ bool RecordCommand::PrepareRecording(Workload* workload) {
return false;
}
}
- // Profiling JITed/interpreted code is supported starting from Android P.
- 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.
- bool keep_symfiles = dwarf_callchain_sampling_ && !unwind_dwarf_callchain_;
- jit_debug_reader_.reset(new JITDebugReader(app_pid, keep_symfiles));
+ if (jit_debug_reader_) {
// Update JIT info at the beginning of recording.
if (!UpdateJITDebugInfo()) {
return false;
@@ -1006,8 +1010,8 @@ bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr& attr,
continue;
}
for (const auto& map : thread_mmaps) {
- if (map.executable == 0) {
- continue; // No need to dump non-executable mmap info.
+ if (map.executable == 0 && !event_selection_set_.RecordNotExecutableMaps()) {
+ continue;
}
MmapRecord record(attr, false, pid, pid, map.start_addr, map.len,
map.pgoff, map.name, event_id);
@@ -1047,6 +1051,9 @@ bool RecordCommand::DumpThreadCommAndMmaps(const perf_event_attr& attr,
}
bool RecordCommand::ProcessRecord(Record* record) {
+ if (ShouldOmitRecord(record)) {
+ return true;
+ }
last_record_timestamp_ = record->Timestamp();
if (unwind_dwarf_callchain_) {
if (post_unwind_) {
@@ -1057,7 +1064,32 @@ bool RecordCommand::ProcessRecord(Record* record) {
return SaveRecordWithoutUnwinding(record);
}
+template <typename MmapRecordType>
+bool IsMappingOnlyExistInMemory(MmapRecordType* record) {
+ return !record->InKernel() && !IsRegularFile(record->filename) && record->filename != "[vdso]";
+}
+
+bool RecordCommand::ShouldOmitRecord(Record* record) {
+ if (jit_debug_reader_) {
+ // To profile jitted Java code, we need PROT_JIT_SYMFILE_MAP maps not overlapped by maps for
+ // /dev/ashmem/dalvik-jit-code-cache. To profile interpreted Java code, we record maps that
+ // are not executable. Some non-exec maps (like those for stack, heap) provide misleading map
+ // entries for unwinding, as in http://b/77236599. So it is better to remove
+ // dalvik-jit-code-cache and other maps that only exist in memory.
+ switch (record->type()) {
+ case PERF_RECORD_MMAP:
+ return IsMappingOnlyExistInMemory(static_cast<MmapRecord*>(record));
+ case PERF_RECORD_MMAP2:
+ return IsMappingOnlyExistInMemory(static_cast<Mmap2Record*>(record));
+ }
+ }
+ return false;
+}
+
bool RecordCommand::SaveRecordForPostUnwinding(Record* record) {
+ if (ShouldOmitRecord(record)) {
+ return true;
+ }
if (record->type() == PERF_RECORD_SAMPLE) {
static_cast<SampleRecord*>(record)->RemoveInvalidStackData();
}
@@ -1131,7 +1163,9 @@ bool RecordCommand::UpdateJITDebugInfo() {
return false;
}
}
- // TODO: Handle dex symfiles.
+ for (auto& symfile : dex_symfiles) {
+ thread_tree_.AddDexFileOffset(symfile.file_path, symfile.dex_file_offset);
+ }
return true;
}
@@ -1285,20 +1319,17 @@ bool RecordCommand::DumpAdditionalFeatures(
return false;
}
- size_t feature_count = 5;
+ size_t feature_count = 6;
if (branch_sampling_) {
feature_count++;
}
- if (dump_symbols_) {
- feature_count++;
- }
if (!record_file_writer_->BeginWriteFeatures(feature_count)) {
return false;
}
if (!DumpBuildIdFeature()) {
return false;
}
- if (dump_symbols_ && !DumpFileFeature()) {
+ if (!DumpFileFeature()) {
return false;
}
utsname uname_buf;
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 7f208862..e6c11aeb 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -389,9 +389,11 @@ TEST(record_cmd, dump_kernel_symbols) {
uint32_t file_type;
uint64_t min_vaddr;
std::vector<Symbol> symbols;
+ std::vector<uint64_t> dex_file_offsets;
size_t read_pos = 0;
bool has_kernel_symbols = false;
- while (reader->ReadFileFeature(read_pos, &file_path, &file_type, &min_vaddr, &symbols)) {
+ while (reader->ReadFileFeature(read_pos, &file_path, &file_type, &min_vaddr, &symbols,
+ &dex_file_offsets)) {
if (file_type == DSO_KERNEL && !symbols.empty()) {
has_kernel_symbols = true;
}
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index 4825ac72..f3e9b2ce 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -28,6 +28,7 @@
#include "environment.h"
#include "read_apk.h"
+#include "read_dex_file.h"
#include "read_elf.h"
#include "utils.h"
@@ -38,7 +39,8 @@ Symbol::Symbol(const std::string& name, uint64_t addr, uint64_t len)
len(len),
name_(symbol_name_allocator.AllocateString(name)),
demangled_name_(nullptr),
- dump_id_(UINT_MAX) {}
+ dump_id_(UINT_MAX) {
+}
const char* Symbol::DemangledName() const {
if (demangled_name_ == nullptr) {
@@ -141,41 +143,14 @@ BuildId Dso::GetExpectedBuildId() {
return FindExpectedBuildIdForPath(path_);
}
-std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path,
- bool force_64bit) {
- return std::unique_ptr<Dso>(new Dso(dso_type, dso_path, force_64bit));
-}
-
-Dso::Dso(DsoType type, const std::string& path, bool force_64bit)
+Dso::Dso(DsoType type, const std::string& path, const std::string& debug_file_path)
: type_(type),
path_(path),
- debug_file_path_(path),
- min_vaddr_(std::numeric_limits<uint64_t>::max()),
+ debug_file_path_(debug_file_path),
is_loaded_(false),
dump_id_(UINT_MAX),
symbol_dump_id_(0),
symbol_warning_loglevel_(android::base::WARNING) {
- if (type_ == DSO_KERNEL) {
- min_vaddr_ = 0;
- }
- // Check if file matching path_ exists in symfs directory before using it as
- // debug_file_path_.
- if (!symfs_dir_.empty()) {
- std::string path_in_symfs = symfs_dir_ + path_;
- std::tuple<bool, std::string, std::string> tuple =
- SplitUrlInApk(path_in_symfs);
- std::string file_path =
- std::get<0>(tuple) ? std::get<1>(tuple) : path_in_symfs;
- if (IsRegularFile(file_path)) {
- debug_file_path_ = path_in_symfs;
- }
- } else if (path == "[vdso]") {
- if (force_64bit && !vdso_64bit_.empty()) {
- debug_file_path_ = vdso_64bit_;
- } else if (!force_64bit && !vdso_32bit_.empty()) {
- debug_file_path_ = vdso_32bit_;
- }
- }
size_t pos = path.find_last_of("/\\");
if (pos != std::string::npos) {
file_name_ = path.substr(pos + 1);
@@ -214,15 +189,13 @@ const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
if (!is_loaded_) {
Load();
}
- if (!symbols_.empty()) {
- auto it = std::upper_bound(symbols_.begin(), symbols_.end(),
- Symbol("", vaddr_in_dso, 0),
- Symbol::CompareValueByAddr);
- if (it != symbols_.begin()) {
- --it;
- if (it->addr <= vaddr_in_dso && (it->addr + it->len > vaddr_in_dso)) {
- return &*it;
- }
+ auto it = std::upper_bound(symbols_.begin(), symbols_.end(),
+ Symbol("", vaddr_in_dso, 0),
+ Symbol::CompareValueByAddr);
+ if (it != symbols_.begin()) {
+ --it;
+ if (it->addr <= vaddr_in_dso && (it->addr + it->len > vaddr_in_dso)) {
+ return &*it;
}
}
if (!unknown_symbols_.empty()) {
@@ -234,13 +207,6 @@ const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
return nullptr;
}
-const std::vector<Symbol>& Dso::GetSymbols() {
- if (!is_loaded_) {
- Load();
- }
- return symbols_;
-}
-
void Dso::SetSymbols(std::vector<Symbol>* symbols) {
symbols_ = std::move(*symbols);
symbols->clear();
@@ -250,252 +216,248 @@ void Dso::AddUnknownSymbol(uint64_t vaddr_in_dso, const std::string& name) {
unknown_symbols_.insert(std::make_pair(vaddr_in_dso, Symbol(name, vaddr_in_dso, 1)));
}
-uint64_t Dso::MinVirtualAddress() {
- if (min_vaddr_ == std::numeric_limits<uint64_t>::max()) {
- min_vaddr_ = 0;
- if (type_ == DSO_ELF_FILE) {
- BuildId build_id = GetExpectedBuildId();
-
- uint64_t addr;
- ElfStatus result = ReadMinExecutableVirtualAddressFromElfFile(
- GetDebugFilePath(), build_id, &addr);
- if (result != ElfStatus::NO_ERROR) {
- LOG(WARNING) << "failed to read min virtual address of "
- << GetDebugFilePath() << ": " << result;
- } else {
- min_vaddr_ = addr;
- }
- }
- }
- return min_vaddr_;
-}
-
-static std::vector<Symbol> MergeSortedSymbols(const std::vector<Symbol>& s1,
- const std::vector<Symbol>& s2) {
- std::vector<Symbol> result;
- std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result),
- Symbol::CompareValueByAddr);
- return result;
-}
-
void Dso::Load() {
is_loaded_ = true;
- std::vector<Symbol> dumped_symbols;
- if (!symbols_.empty()) {
- // If symbols has been read from file feature section of perf.data, move it to
- // dumped_symbols, so later we can merge them with symbols read from file system.
- dumped_symbols = std::move(symbols_);
- symbols_.clear();
- // Don't warn missing symbol table if we have dumped symbols in perf.data.
- symbol_warning_loglevel_ = android::base::DEBUG;
- }
- bool result = false;
- switch (type_) {
- case DSO_KERNEL:
- result = LoadKernel();
- break;
- case DSO_KERNEL_MODULE:
- result = LoadKernelModule();
- break;
- case DSO_ELF_FILE: {
- if (std::get<0>(SplitUrlInApk(path_))) {
- result = LoadEmbeddedElfFile();
- } else {
- result = LoadElfFile();
- }
- break;
- }
- }
- if (result) {
- std::sort(symbols_.begin(), symbols_.end(), Symbol::CompareValueByAddr);
- FixupSymbolLength();
- } else {
- symbols_.clear();
- }
-
- if (symbols_.empty()) {
- symbols_ = std::move(dumped_symbols);
- } else if (!dumped_symbols.empty()) {
- symbols_ = MergeSortedSymbols(symbols_, dumped_symbols);
- }
-
+ std::vector<Symbol> symbols = LoadSymbols();
if (symbols_.empty()) {
- LOG(DEBUG) << "failed to load dso: " << path_;
- }
-}
-
-static bool IsKernelFunctionSymbol(const KernelSymbol& symbol) {
- return (symbol.type == 'T' || symbol.type == 't' || symbol.type == 'W' ||
- symbol.type == 'w');
-}
-
-static bool KernelSymbolCallback(const KernelSymbol& kernel_symbol,
- std::vector<Symbol>* symbols) {
- if (IsKernelFunctionSymbol(kernel_symbol)) {
- symbols->emplace_back(Symbol(kernel_symbol.name, kernel_symbol.addr, 0));
- }
- return false;
-}
-
-static void VmlinuxSymbolCallback(const ElfFileSymbol& elf_symbol,
- std::vector<Symbol>* symbols) {
- if (elf_symbol.is_func) {
- symbols->emplace_back(
- Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len));
+ symbols_ = std::move(symbols);
+ } else {
+ std::vector<Symbol> merged_symbols;
+ std::set_union(symbols_.begin(), symbols_.end(), symbols.begin(), symbols.end(),
+ std::back_inserter(merged_symbols), Symbol::CompareValueByAddr);
+ symbols_ = std::move(merged_symbols);
}
}
-bool Dso::CheckReadSymbolResult(ElfStatus result, const std::string& filename) {
+static void ReportReadElfSymbolResult(ElfStatus result, const std::string& path,
+ const std::string& debug_file_path,
+ android::base::LogSeverity warning_loglevel = android::base::WARNING) {
if (result == ElfStatus::NO_ERROR) {
- LOG(VERBOSE) << "Read symbols from " << filename << " successfully";
- return true;
+ LOG(VERBOSE) << "Read symbols from " << debug_file_path << " successfully";
} else if (result == ElfStatus::NO_SYMBOL_TABLE) {
- if (path_ == "[vdso]") {
+ if (path == "[vdso]") {
// Vdso only contains dynamic symbol table, and we can't change that.
- return true;
+ return;
}
// Lacking symbol table isn't considered as an error but worth reporting.
- LOG(symbol_warning_loglevel_) << filename << " doesn't contain symbol table";
- return true;
+ LOG(warning_loglevel) << debug_file_path << " doesn't contain symbol table";
} else {
- LOG(symbol_warning_loglevel_) << "failed to read symbols from " << filename << ": " << result;
- return false;
+ LOG(warning_loglevel) << "failed to read symbols from " << debug_file_path << ": " << result;
+ }
+}
+
+static void SortAndFixSymbols(std::vector<Symbol>& symbols) {
+ std::sort(symbols.begin(), symbols.end(), Symbol::CompareValueByAddr);
+ Symbol* prev_symbol = nullptr;
+ for (auto& symbol : symbols) {
+ if (prev_symbol != nullptr && prev_symbol->len == 0) {
+ prev_symbol->len = symbol.addr - prev_symbol->addr;
+ }
+ prev_symbol = &symbol;
}
}
-bool Dso::LoadKernel() {
- BuildId build_id = GetExpectedBuildId();
- if (!vmlinux_.empty()) {
- ElfStatus result = ParseSymbolsFromElfFile(vmlinux_, build_id,
- std::bind(VmlinuxSymbolCallback, std::placeholders::_1, &symbols_));
- return CheckReadSymbolResult(result, vmlinux_);
- } else if (!kallsyms_.empty()) {
- ProcessKernelSymbols(kallsyms_, std::bind(&KernelSymbolCallback,
- std::placeholders::_1, &symbols_));
- bool all_zero = true;
- for (const auto& symbol : symbols_) {
- if (symbol.addr != 0) {
- all_zero = false;
- break;
+class ElfDso : public Dso {
+ public:
+ ElfDso(const std::string& path, const std::string& debug_file_path)
+ : Dso(DSO_ELF_FILE, path, debug_file_path),
+ min_vaddr_(std::numeric_limits<uint64_t>::max()) {}
+
+ uint64_t MinVirtualAddress() override {
+ if (min_vaddr_ == std::numeric_limits<uint64_t>::max()) {
+ min_vaddr_ = 0;
+ if (type_ == DSO_ELF_FILE) {
+ BuildId build_id = GetExpectedBuildId();
+
+ uint64_t addr;
+ ElfStatus result = ReadMinExecutableVirtualAddressFromElfFile(
+ GetDebugFilePath(), build_id, &addr);
+ if (result != ElfStatus::NO_ERROR) {
+ LOG(WARNING) << "failed to read min virtual address of "
+ << GetDebugFilePath() << ": " << result;
+ } else {
+ min_vaddr_ = addr;
+ }
}
}
- if (all_zero) {
- LOG(symbol_warning_loglevel_)
- << "Symbol addresses in /proc/kallsyms on device are all zero. "
- "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
- symbols_.clear();
- return false;
+ return min_vaddr_;
+ }
+
+ void SetMinVirtualAddress(uint64_t min_vaddr) override {
+ min_vaddr_ = min_vaddr;
+ }
+
+ protected:
+ std::vector<Symbol> LoadSymbols() override {
+ std::vector<Symbol> symbols;
+ BuildId build_id = GetExpectedBuildId();
+ auto symbol_callback = [&](const ElfFileSymbol& symbol) {
+ if (symbol.is_func || (symbol.is_label && symbol.is_in_text_section)) {
+ symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
+ }
+ };
+ ElfStatus status;
+ std::tuple<bool, std::string, std::string> tuple = SplitUrlInApk(debug_file_path_);
+ if (std::get<0>(tuple)) {
+ status = ParseSymbolsFromApkFile(std::get<1>(tuple), std::get<2>(tuple), build_id,
+ symbol_callback);
+ } else {
+ status = ParseSymbolsFromElfFile(debug_file_path_, build_id, symbol_callback);
}
- } else if (read_kernel_symbols_from_proc_ || !build_id.IsEmpty()) {
- // Try /proc/kallsyms only when asked to do so, or when build id matches.
- // Otherwise, it is likely to use /proc/kallsyms on host for perf.data recorded on device.
- if (!build_id.IsEmpty()) {
- BuildId real_build_id;
- if (!GetKernelBuildId(&real_build_id)) {
- return false;
+ ReportReadElfSymbolResult(status, path_, debug_file_path_,
+ symbols_.empty() ? android::base::WARNING : android::base::DEBUG);
+ SortAndFixSymbols(symbols);
+ return symbols;
+ }
+
+ private:
+ uint64_t min_vaddr_;
+};
+
+class KernelDso : public Dso {
+ public:
+ KernelDso(const std::string& path, const std::string& debug_file_path)
+ : Dso(DSO_KERNEL, path, debug_file_path) {}
+
+ protected:
+ std::vector<Symbol> LoadSymbols() override {
+ std::vector<Symbol> symbols;
+ BuildId build_id = GetExpectedBuildId();
+ if (!vmlinux_.empty()) {
+ auto symbol_callback = [&](const ElfFileSymbol& symbol) {
+ if (symbol.is_func) {
+ symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
+ }
+ };
+ ElfStatus status = ParseSymbolsFromElfFile(vmlinux_, build_id, symbol_callback);
+ ReportReadElfSymbolResult(status, path_, vmlinux_);
+ } else if (!kallsyms_.empty()) {
+ symbols = ReadSymbolsFromKallsyms(kallsyms_);
+ } else if (read_kernel_symbols_from_proc_ || !build_id.IsEmpty()) {
+ // Try /proc/kallsyms only when asked to do so, or when build id matches.
+ // Otherwise, it is likely to use /proc/kallsyms on host for perf.data recorded on device.
+ bool can_read_kallsyms = true;
+ if (!build_id.IsEmpty()) {
+ BuildId real_build_id;
+ if (!GetKernelBuildId(&real_build_id) || build_id != real_build_id) {
+ LOG(DEBUG) << "failed to read symbols from /proc/kallsyms: Build id mismatch";
+ can_read_kallsyms = false;
+ }
}
- bool match = (build_id == real_build_id);
- if (!match) {
- LOG(symbol_warning_loglevel_) << "failed to read symbols from /proc/kallsyms: Build id "
- << "mismatch";
- return false;
+ if (can_read_kallsyms) {
+ std::string kallsyms;
+ if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) {
+ LOG(DEBUG) << "failed to read /proc/kallsyms";
+ } else {
+ symbols = ReadSymbolsFromKallsyms(kallsyms);
+ }
}
}
-
- std::string kallsyms;
- if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) {
- LOG(DEBUG) << "failed to read /proc/kallsyms";
- return false;
+ SortAndFixSymbols(symbols);
+ if (!symbols.empty()) {
+ symbols.back().len = std::numeric_limits<uint64_t>::max() - symbols.back().addr;
}
- ProcessKernelSymbols(kallsyms, std::bind(&KernelSymbolCallback,
- std::placeholders::_1, &symbols_));
- bool all_zero = true;
- for (const auto& symbol : symbols_) {
- if (symbol.addr != 0) {
- all_zero = false;
- break;
+ return symbols;
+ }
+
+ private:
+ std::vector<Symbol> ReadSymbolsFromKallsyms(std::string& kallsyms) {
+ std::vector<Symbol> symbols;
+ auto symbol_callback = [&](const KernelSymbol& symbol) {
+ if (strchr("TtWw", symbol.type) && symbol.addr != 0u) {
+ symbols.emplace_back(symbol.name, symbol.addr, 0);
}
- }
- if (all_zero) {
- LOG(symbol_warning_loglevel_) << "Symbol addresses in /proc/kallsyms are all zero. "
- "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
- symbols_.clear();
return false;
+ };
+ ProcessKernelSymbols(kallsyms, symbol_callback);
+ if (symbols.empty()) {
+ LOG(WARNING) << "Symbol addresses in /proc/kallsyms on device are all zero. "
+ "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
}
+ return symbols;
}
- return true;
-}
-
-static void ElfFileSymbolCallback(const ElfFileSymbol& elf_symbol,
- bool (*filter)(const ElfFileSymbol&),
- std::vector<Symbol>* symbols) {
- if (filter(elf_symbol)) {
- symbols->emplace_back(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len);
+};
+
+class KernelModuleDso : public Dso {
+ public:
+ KernelModuleDso(const std::string& path, const std::string& debug_file_path)
+ : Dso(DSO_KERNEL_MODULE, path, debug_file_path) {}
+
+ protected:
+ std::vector<Symbol> LoadSymbols() override {
+ std::vector<Symbol> symbols;
+ BuildId build_id = GetExpectedBuildId();
+ auto symbol_callback = [&](const ElfFileSymbol& symbol) {
+ if (symbol.is_func || symbol.is_in_text_section) {
+ symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
+ }
+ };
+ ElfStatus status = ParseSymbolsFromElfFile(debug_file_path_, build_id, symbol_callback);
+ ReportReadElfSymbolResult(status, path_, debug_file_path_,
+ symbols_.empty() ? android::base::WARNING : android::base::DEBUG);
+ SortAndFixSymbols(symbols);
+ return symbols;
}
-}
-
-static bool SymbolFilterForKernelModule(const ElfFileSymbol& elf_symbol) {
- // TODO: Parse symbol outside of .text section.
- return (elf_symbol.is_func && elf_symbol.is_in_text_section);
-}
-
-bool Dso::LoadKernelModule() {
- BuildId build_id = GetExpectedBuildId();
- ElfStatus result = ParseSymbolsFromElfFile(GetDebugFilePath(), build_id,
- std::bind(ElfFileSymbolCallback, std::placeholders::_1,
- SymbolFilterForKernelModule, &symbols_));
- return CheckReadSymbolResult(result, GetDebugFilePath());
-}
+};
-static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) {
- return elf_symbol.is_func ||
- (elf_symbol.is_label && elf_symbol.is_in_text_section);
-}
-
-bool Dso::LoadElfFile() {
- BuildId build_id = GetExpectedBuildId();
-
- if (symfs_dir_.empty()) {
- // Linux host can store debug shared libraries in /usr/lib/debug.
- ElfStatus result = ParseSymbolsFromElfFile(
- "/usr/lib/debug" + path_, build_id,
- std::bind(ElfFileSymbolCallback, std::placeholders::_1,
- SymbolFilterForDso, &symbols_));
- if (result == ElfStatus::NO_ERROR) {
- return CheckReadSymbolResult(result, "/usr/lib/debug" + path_);
+std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path,
+ bool force_64bit) {
+ auto find_debug_file = [&]() {
+ // Check if file matching path_ exists in symfs directory before using it as
+ // debug_file_path_.
+ if (!symfs_dir_.empty()) {
+ std::string path_in_symfs = symfs_dir_ + dso_path;
+ std::tuple<bool, std::string, std::string> tuple = SplitUrlInApk(path_in_symfs);
+ std::string file_path = std::get<0>(tuple) ? std::get<1>(tuple) : path_in_symfs;
+ if (IsRegularFile(file_path)) {
+ return path_in_symfs;
+ }
+ } else if (dso_path == "[vdso]") {
+ if (force_64bit && !vdso_64bit_.empty()) {
+ return vdso_64bit_;
+ } else if (!force_64bit && !vdso_32bit_.empty()) {
+ return vdso_32bit_;
+ }
+ } else if (dso_type == DSO_ELF_FILE) {
+ // Linux host can store debug shared libraries in /usr/lib/debug.
+ std::string path = "/usr/lib/debug" + dso_path;
+ if (IsRegularFile(path)) {
+ return path;
+ }
}
- }
- // TODO: load std::vector<Symbol> directly from ParseSymbolsFromElfFile
- // instead of needing to call a callback function for each symbol.
- ElfStatus result = ParseSymbolsFromElfFile(
- GetDebugFilePath(), build_id,
- std::bind(ElfFileSymbolCallback, std::placeholders::_1,
- SymbolFilterForDso, &symbols_));
- return CheckReadSymbolResult(result, GetDebugFilePath());
-}
+ return dso_path;
+ };
-bool Dso::LoadEmbeddedElfFile() {
- BuildId build_id = GetExpectedBuildId();
- auto tuple = SplitUrlInApk(GetDebugFilePath());
- CHECK(std::get<0>(tuple));
- ElfStatus result = ParseSymbolsFromApkFile(
- std::get<1>(tuple), std::get<2>(tuple), build_id,
- std::bind(ElfFileSymbolCallback, std::placeholders::_1,
- SymbolFilterForDso, &symbols_));
- return CheckReadSymbolResult(result, GetDebugFilePath());
+ switch (dso_type) {
+ case DSO_ELF_FILE:
+ return std::unique_ptr<Dso>(new ElfDso(dso_path, find_debug_file()));
+ case DSO_KERNEL:
+ return std::unique_ptr<Dso>(new KernelDso(dso_path, dso_path));
+ case DSO_KERNEL_MODULE:
+ return std::unique_ptr<Dso>(new KernelModuleDso(dso_path, find_debug_file()));
+ case DSO_DEX_FILE:
+ return std::unique_ptr<Dso>(new DexFileDso(dso_path, find_debug_file()));
+ default:
+ LOG(FATAL) << "Unexpected dso_type " << static_cast<int>(dso_type);
+ }
+ return nullptr;
}
-void Dso::FixupSymbolLength() {
- Symbol* prev_symbol = nullptr;
- for (auto& symbol : symbols_) {
- if (prev_symbol != nullptr && prev_symbol->len == 0) {
- prev_symbol->len = symbol.addr - prev_symbol->addr;
- }
- prev_symbol = &symbol;
+std::vector<Symbol> DexFileDso::LoadSymbols() {
+ std::vector<Symbol> symbols;
+ std::vector<DexFileSymbol> dex_file_symbols;
+ if (!ReadSymbolsFromDexFile(debug_file_path_, dex_file_offsets_, &dex_file_symbols)) {
+ android::base::LogSeverity level = symbols_.empty() ? android::base::WARNING
+ : android::base::DEBUG;
+ LOG(level) << "Failed to read symbols from " << debug_file_path_;
+ return symbols;
}
- if (prev_symbol != nullptr && prev_symbol->len == 0) {
- prev_symbol->len = std::numeric_limits<uint64_t>::max() - prev_symbol->addr;
+ LOG(VERBOSE) << "Read symbols from " << debug_file_path_ << " successfully";
+ for (auto& symbol : dex_file_symbols) {
+ symbols.emplace_back(symbol.name, symbol.offset, symbol.len);
}
+ SortAndFixSymbols(symbols);
+ return symbols;
}
const char* DsoTypeToString(DsoType dso_type) {
@@ -506,6 +468,8 @@ const char* DsoTypeToString(DsoType dso_type) {
return "dso_kernel_module";
case DSO_ELF_FILE:
return "dso_elf_file";
+ case DSO_DEX_FILE:
+ return "dso_dex_file";
default:
return "unknown";
}
diff --git a/simpleperf/dso.h b/simpleperf/dso.h
index 4f3df0bd..877e27ff 100644
--- a/simpleperf/dso.h
+++ b/simpleperf/dso.h
@@ -78,6 +78,7 @@ enum DsoType {
DSO_KERNEL,
DSO_KERNEL_MODULE,
DSO_ELF_FILE,
+ DSO_DEX_FILE, // For files containing dex files, like .vdex files.
};
struct KernelSymbol;
@@ -105,7 +106,7 @@ class Dso {
static std::unique_ptr<Dso> CreateDso(DsoType dso_type, const std::string& dso_path,
bool force_64bit = false);
- ~Dso();
+ virtual ~Dso();
DsoType type() const { return type_; }
@@ -132,19 +133,19 @@ class Dso {
uint32_t CreateSymbolDumpId(const Symbol* symbol);
// Return the minimum virtual address in program header.
- uint64_t MinVirtualAddress();
- void SetMinVirtualAddress(uint64_t min_vaddr) { min_vaddr_ = min_vaddr; }
+ virtual uint64_t MinVirtualAddress() { return 0; }
+ virtual void SetMinVirtualAddress(uint64_t) {}
const Symbol* FindSymbol(uint64_t vaddr_in_dso);
- const std::vector<Symbol>& GetSymbols();
+ const std::vector<Symbol>& GetSymbols() { return symbols_; }
void SetSymbols(std::vector<Symbol>* symbols);
// Create a symbol for a virtual address which can't find a corresponding
// symbol in symbol table.
void AddUnknownSymbol(uint64_t vaddr_in_dso, const std::string& name);
- private:
+ protected:
static bool demangle_;
static std::string symfs_dir_;
static std::string vmlinux_;
@@ -156,15 +157,11 @@ class Dso {
static std::string vdso_64bit_;
static std::string vdso_32bit_;
- Dso(DsoType type, const std::string& path, bool force_64bit);
- void Load();
- bool LoadKernel();
- bool LoadKernelModule();
- bool LoadElfFile();
- bool LoadEmbeddedElfFile();
- void FixupSymbolLength();
+ Dso(DsoType type, const std::string& path, const std::string& debug_file_path);
BuildId GetExpectedBuildId();
- bool CheckReadSymbolResult(ElfStatus result, const std::string& filename);
+
+ void Load();
+ virtual std::vector<Symbol> LoadSymbols() = 0;
const DsoType type_;
// path of the shared library used by the profiled program
@@ -174,7 +171,6 @@ class Dso {
std::string debug_file_path_;
// File name of the shared library, got by removing directories in path_.
std::string file_name_;
- uint64_t min_vaddr_;
std::vector<Symbol> symbols_;
// unknown symbols are like [libc.so+0x1234].
std::unordered_map<uint64_t, Symbol> unknown_symbols_;
@@ -186,6 +182,27 @@ class Dso {
android::base::LogSeverity symbol_warning_loglevel_;
};
+class DexFileDso : public Dso {
+ public:
+ void AddDexFileOffset(uint64_t dex_file_offset) {
+ dex_file_offsets_.push_back(dex_file_offset);
+ }
+
+ const std::vector<uint64_t>& DexFileOffsets() {
+ return dex_file_offsets_;
+ }
+
+ protected:
+ DexFileDso(const std::string& path, const std::string& debug_file_path)
+ : Dso(DSO_DEX_FILE, path, debug_file_path) {}
+
+ std::vector<Symbol> LoadSymbols() override;
+
+ private:
+ std::vector<uint64_t> dex_file_offsets_;
+ friend std::unique_ptr<Dso> Dso::CreateDso(DsoType, const std::string&, bool);
+};
+
const char* DsoTypeToString(DsoType dso_type);
#endif // SIMPLE_PERF_DSO_H_
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index bd93d8d3..cf9167c2 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -737,7 +737,7 @@ int GetAndroidVersion() {
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;
+ return s[0] - 'P' + kAndroidVersionP;
}
if (isdigit(s[0])) {
int result;
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 76bdbce7..9f8dc468 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -120,4 +120,6 @@ bool SignalIsIgnored(int signo);
// Return 0 if no android version.
int GetAndroidVersion();
+constexpr int kAndroidVersionP = 9;
+
#endif // SIMPLE_PERF_ENVIRONMENT_H_
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 646de404..29882425 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -410,6 +410,18 @@ bool EventSelectionSet::NeedKernelSymbol() const {
return false;
}
+void EventSelectionSet::SetRecordNotExecutableMaps(bool record) {
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ selection.event_attr.mmap_data = record ? 1 : 0;
+ }
+ }
+}
+
+bool EventSelectionSet::RecordNotExecutableMaps() const {
+ return groups_[0][0].event_attr.mmap_data == 1;
+}
+
static bool CheckIfCpusOnline(const std::vector<int>& cpus) {
std::vector<int> online_cpus = GetOnlineCpus();
for (const auto& cpu : cpus) {
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index 9e2a064c..289208a5 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -103,6 +103,8 @@ class EventSelectionSet {
void SetInherit(bool enable);
void SetClockId(int clock_id);
bool NeedKernelSymbol() const;
+ void SetRecordNotExecutableMaps(bool record);
+ bool RecordNotExecutableMaps() const;
void AddMonitoredProcesses(const std::set<pid_t>& processes) {
processes_.insert(processes.begin(), processes.end());
diff --git a/simpleperf/nonlinux_support/nonlinux_support.cpp b/simpleperf/nonlinux_support/nonlinux_support.cpp
index db5bed20..c51f2a7f 100644
--- a/simpleperf/nonlinux_support/nonlinux_support.cpp
+++ b/simpleperf/nonlinux_support/nonlinux_support.cpp
@@ -17,6 +17,7 @@
// Add fake functions to build successfully on darwin.
#include <android-base/logging.h>
+#include "read_dex_file.h"
#include "environment.h"
#include "OfflineUnwinder.h"
@@ -37,3 +38,8 @@ bool GetKernelBuildId(BuildId*) {
bool CanRecordRawData() {
return false;
}
+
+bool ReadSymbolsFromDexFile(const std::string&, const std::vector<uint64_t>&,
+ std::vector<DexFileSymbol>*) {
+ return true;
+}
diff --git a/simpleperf/read_dex_file.cpp b/simpleperf/read_dex_file.cpp
new file mode 100644
index 00000000..1aa401f2
--- /dev/null
+++ b/simpleperf/read_dex_file.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "read_dex_file.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <functional>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <dex/code_item_accessors-inl.h>
+#include <dex/dex_file_loader.h>
+#include <dex/dex_file.h>
+
+static bool OpenDexFiles(const std::string& file_path, std::vector<uint64_t> dex_file_offsets,
+ const std::function<void (const art::DexFile&, uint64_t)>& callback) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file_path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ return false;
+ }
+ struct stat buf;
+ if (fstat(fd, &buf) == -1 || buf.st_size < 0) {
+ return false;
+ }
+ uint64_t file_size = buf.st_size;
+ void* addr = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (addr == MAP_FAILED) {
+ return false;
+ }
+
+ bool result = true;
+ for (uint64_t offset : dex_file_offsets) {
+ if (offset >= file_size || file_size - offset < sizeof(art::DexFile::Header)) {
+ result = false;
+ break;
+ }
+ auto header = reinterpret_cast<art::DexFile::Header*>(static_cast<char*>(addr) + offset);
+ if (file_size - offset < header->file_size_) {
+ result = false;
+ break;
+ }
+ art::DexFileLoader loader;
+ std::string error;
+ std::unique_ptr<const art::DexFile> dex_file = loader.Open(reinterpret_cast<uint8_t*>(header),
+ header->file_size_, "", 0, nullptr,
+ false, false, &error);
+ if (!dex_file) {
+ result = false;
+ break;
+ }
+ callback(*dex_file, offset);
+ }
+ munmap(addr, file_size);
+ return result;
+}
+
+bool ReadSymbolsFromDexFile(const std::string& file_path,
+ const std::vector<uint64_t>& dex_file_offsets,
+ std::vector<DexFileSymbol>* symbols) {
+ auto dexfile_callback = [&](const art::DexFile& dex_file, uint64_t dex_file_offset) {
+ for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) {
+ const art::DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+ const uint8_t* class_data = dex_file.GetClassData(class_def);
+ if (class_data == nullptr) {
+ continue;
+ }
+ for (art::ClassDataItemIterator it(dex_file, class_data); it.HasNext(); it.Next()) {
+ if (!it.IsAtMethod()) {
+ continue;
+ }
+ const art::DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ if (code_item == nullptr) {
+ continue;
+ }
+ art::CodeItemInstructionAccessor code(dex_file, code_item);
+ if (!code.HasCodeItem()) {
+ continue;
+ }
+ symbols->resize(symbols->size() + 1);
+ DexFileSymbol& symbol = symbols->back();
+ symbol.offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file.Begin() +
+ dex_file_offset;
+ symbol.len = code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
+ symbol.name = dex_file.PrettyMethod(it.GetMemberIndex(), false);
+ }
+ }
+ };
+ return OpenDexFiles(file_path, dex_file_offsets, dexfile_callback);
+}
diff --git a/simpleperf/read_dex_file.h b/simpleperf/read_dex_file.h
new file mode 100644
index 00000000..cd01f586
--- /dev/null
+++ b/simpleperf/read_dex_file.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_PERF_READ_DEX_FILE_H_
+#define SIMPLE_PERF_READ_DEX_FILE_H_
+
+#include <inttypes.h>
+
+#include <string>
+#include <vector>
+
+struct DexFileSymbol {
+ uint64_t offset;
+ uint64_t len;
+ std::string name;
+};
+
+bool ReadSymbolsFromDexFile(const std::string& file_path,
+ const std::vector<uint64_t>& dex_file_offsets,
+ std::vector<DexFileSymbol>* symbols);
+
+#endif // SIMPLE_PERF_READ_DEX_FILE_H_
diff --git a/simpleperf/read_dex_file_test.cpp b/simpleperf/read_dex_file_test.cpp
new file mode 100644
index 00000000..6b90b8b0
--- /dev/null
+++ b/simpleperf/read_dex_file_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "read_dex_file.h"
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+
+#include "get_test_data.h"
+#include "test_util.h"
+#include "utils.h"
+
+bool operator==(const DexFileSymbol& s1, const DexFileSymbol& s2) {
+ return s1.offset == s2.offset && s1.len == s2.len && s1.name == s2.name;
+}
+
+TEST(read_dex_file, smoke) {
+ std::vector<DexFileSymbol> symbols;
+ ASSERT_TRUE(ReadSymbolsFromDexFile(GetTestData("base.vdex"), {0x28}, &symbols));
+ ASSERT_EQ(12435u, symbols.size());
+ DexFileSymbol target;
+ target.offset = 0x6c77e;
+ target.len = 0x16;
+ target.name = "com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run";
+ ASSERT_NE(std::find(symbols.begin(), symbols.end(), target), symbols.end());
+}
diff --git a/simpleperf/record_file.h b/simpleperf/record_file.h
index 00b8a344..c90b2692 100644
--- a/simpleperf/record_file.h
+++ b/simpleperf/record_file.h
@@ -55,10 +55,6 @@ class RecordFileWriter {
bool WriteCmdlineFeature(const std::vector<std::string>& cmdline);
bool WriteBranchStackFeature();
bool WriteFileFeatures(const std::vector<Dso*>& files);
- bool WriteFileFeature(const std::string& file_path,
- uint32_t file_type,
- uint64_t min_vaddr,
- const std::vector<const Symbol*>& symbols);
bool WriteMetaInfoFeature(const std::unordered_map<std::string, std::string>& info_map);
bool WriteFeature(int feature, const std::vector<char>& data);
bool EndWriteFeatures();
@@ -76,6 +72,11 @@ class RecordFileWriter {
bool Read(void* buf, size_t len);
bool GetFilePos(uint64_t* file_pos);
bool WriteStringWithLength(const std::string& s);
+ bool WriteFileFeature(const std::string& file_path,
+ uint32_t file_type,
+ uint64_t min_vaddr,
+ const std::vector<const Symbol*>& symbols,
+ const std::vector<uint64_t>* dex_file_offsets);
bool WriteFeatureBegin(int feature);
bool WriteFeatureEnd(int feature);
@@ -149,7 +150,7 @@ class RecordFileReader {
// information.
bool ReadFileFeature(size_t& read_pos, std::string* file_path,
uint32_t* file_type, uint64_t* min_vaddr,
- std::vector<Symbol>* symbols);
+ std::vector<Symbol>* symbols, std::vector<uint64_t>* dex_file_offsets);
bool ReadMetaInfoFeature(std::unordered_map<std::string, std::string>* info_map);
void LoadBuildIdAndFileFeatures(ThreadTree& thread_tree);
diff --git a/simpleperf/record_file_format.h b/simpleperf/record_file_format.h
index 1ddaf002..cd4e9743 100644
--- a/simpleperf/record_file_format.h
+++ b/simpleperf/record_file_format.h
@@ -47,8 +47,11 @@ file feature section:
struct {
uint64_t start_vaddr;
uint32_t len;
- char symbol_name[];
- } symbol_table;
+ char symbol_name[len+1];
+ } symbol_table[symbol_count];
+
+ uint32_t dex_file_offset_count; // Only when file_type = DSO_DEX_FILE
+ uint64_t dex_file_offsets[dex_file_offset_count]; // Only when file_type = DSO_DEX_FILE
};
meta_info feature section:
diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp
index 38a4b2d6..9b68c654 100644
--- a/simpleperf/record_file_reader.cpp
+++ b/simpleperf/record_file_reader.cpp
@@ -443,7 +443,8 @@ bool RecordFileReader::ReadFileFeature(size_t& read_pos,
std::string* file_path,
uint32_t* file_type,
uint64_t* min_vaddr,
- std::vector<Symbol>* symbols) {
+ std::vector<Symbol>* symbols,
+ std::vector<uint64_t>* dex_file_offsets) {
auto it = feature_section_descriptors_.find(FEAT_FILE);
if (it == feature_section_descriptors_.end()) {
return false;
@@ -484,6 +485,13 @@ bool RecordFileReader::ReadFileFeature(size_t& read_pos,
p += name.size() + 1;
symbols->emplace_back(name, start_vaddr, len);
}
+ dex_file_offsets->clear();
+ if (*file_type == static_cast<uint32_t>(DSO_DEX_FILE)) {
+ uint32_t offset_count;
+ MoveFromBinaryFormat(offset_count, p);
+ dex_file_offsets->resize(offset_count);
+ MoveFromBinaryFormat(dex_file_offsets->data(), offset_count, p);
+ }
CHECK_EQ(size, static_cast<size_t>(p - buf.data()));
return true;
}
@@ -518,10 +526,11 @@ void RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) {
uint32_t file_type;
uint64_t min_vaddr;
std::vector<Symbol> symbols;
+ std::vector<uint64_t> dex_file_offsets;
size_t read_pos = 0;
while (ReadFileFeature(
- read_pos, &file_path, &file_type, &min_vaddr, &symbols)) {
- thread_tree.AddDsoInfo(file_path, file_type, min_vaddr, &symbols);
+ read_pos, &file_path, &file_type, &min_vaddr, &symbols, &dex_file_offsets)) {
+ thread_tree.AddDsoInfo(file_path, file_type, min_vaddr, &symbols, dex_file_offsets);
}
}
}
diff --git a/simpleperf/record_file_writer.cpp b/simpleperf/record_file_writer.cpp
index fd738d10..c91bd3e7 100644
--- a/simpleperf/record_file_writer.cpp
+++ b/simpleperf/record_file_writer.cpp
@@ -299,7 +299,8 @@ bool RecordFileWriter::WriteBranchStackFeature() {
bool RecordFileWriter::WriteFileFeatures(const std::vector<Dso*>& files) {
for (Dso* dso : files) {
- if (!dso->HasDumpId()) {
+ // Always want to dump dex file offsets for DSO_DEX_FILE type.
+ if (!dso->HasDumpId() && dso->type() != DSO_DEX_FILE) {
continue;
}
uint32_t dso_type = dso->type();
@@ -316,7 +317,11 @@ bool RecordFileWriter::WriteFileFeatures(const std::vector<Dso*>& files) {
}
std::sort(dump_symbols.begin(), dump_symbols.end(), Symbol::CompareByAddr);
- if (!WriteFileFeature(dso->Path(), dso_type, min_vaddr, dump_symbols)) {
+ const std::vector<uint64_t>* dex_file_offsets = nullptr;
+ if (dso->type() == DSO_DEX_FILE) {
+ dex_file_offsets = &static_cast<DexFileDso*>(dso)->DexFileOffsets();
+ }
+ if (!WriteFileFeature(dso->Path(), dso_type, min_vaddr, dump_symbols, dex_file_offsets)) {
return false;
}
}
@@ -326,12 +331,16 @@ bool RecordFileWriter::WriteFileFeatures(const std::vector<Dso*>& files) {
bool RecordFileWriter::WriteFileFeature(const std::string& file_path,
uint32_t file_type,
uint64_t min_vaddr,
- const std::vector<const Symbol*>& symbols) {
+ const std::vector<const Symbol*>& symbols,
+ const std::vector<uint64_t>* dex_file_offsets) {
uint32_t size = file_path.size() + 1 + sizeof(uint32_t) * 2 +
sizeof(uint64_t) + symbols.size() * (sizeof(uint64_t) + sizeof(uint32_t));
for (const auto& symbol : symbols) {
size += strlen(symbol->Name()) + 1;
}
+ if (dex_file_offsets != nullptr) {
+ size += sizeof(uint32_t) + sizeof(uint64_t) * dex_file_offsets->size();
+ }
std::vector<char> buf(sizeof(uint32_t) + size);
char* p = buf.data();
MoveToBinaryFormat(size, p);
@@ -346,6 +355,11 @@ bool RecordFileWriter::WriteFileFeature(const std::string& file_path,
MoveToBinaryFormat(len, p);
MoveToBinaryFormat(symbol->Name(), strlen(symbol->Name()) + 1, p);
}
+ if (dex_file_offsets != nullptr) {
+ uint32_t offset_count = dex_file_offsets->size();
+ MoveToBinaryFormat(offset_count, p);
+ MoveToBinaryFormat(dex_file_offsets->data(), offset_count, p);
+ }
CHECK_EQ(buf.size(), static_cast<size_t>(p - buf.data()));
return WriteFeature(FEAT_FILE, buf);
diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp
index 01ba8db1..580338c5 100644
--- a/simpleperf/thread_tree.cpp
+++ b/simpleperf/thread_tree.cpp
@@ -139,12 +139,15 @@ void ThreadTree::AddThreadMap(int pid, int tid, uint64_t start_addr,
thread->maps->version++;
}
-Dso* ThreadTree::FindUserDsoOrNew(const std::string& filename, uint64_t start_addr) {
+Dso* ThreadTree::FindUserDsoOrNew(const std::string& filename, uint64_t start_addr,
+ DsoType dso_type) {
auto it = user_dso_tree_.find(filename);
if (it == user_dso_tree_.end()) {
bool force_64bit = start_addr > UINT_MAX;
- user_dso_tree_[filename] = Dso::CreateDso(DSO_ELF_FILE, filename, force_64bit);
- it = user_dso_tree_.find(filename);
+ std::unique_ptr<Dso> dso = Dso::CreateDso(dso_type, filename, force_64bit);
+ auto pair = user_dso_tree_.insert(std::make_pair(filename, std::move(dso)));
+ CHECK(pair.second);
+ it = pair.first;
}
return it->second.get();
}
@@ -229,6 +232,8 @@ const Symbol* ThreadTree::FindSymbol(const MapEntry* map, uint64_t ip,
// Find symbol in user space shared libraries.
if (map->flags & map_flags::PROT_JIT_SYMFILE_MAP) {
vaddr_in_file = ip;
+ } else if (dso->type() == DSO_DEX_FILE) {
+ vaddr_in_file = ip - map->start_addr + map->pgoff;
} else {
vaddr_in_file = ip - map->start_addr + map->dso->MinVirtualAddress();
}
@@ -283,16 +288,29 @@ void ThreadTree::ClearThreadAndMap() {
}
void ThreadTree::AddDsoInfo(const std::string& file_path, uint32_t file_type,
- uint64_t min_vaddr, std::vector<Symbol>* symbols) {
+ uint64_t min_vaddr, std::vector<Symbol>* symbols,
+ const std::vector<uint64_t>& dex_file_offsets) {
DsoType dso_type = static_cast<DsoType>(file_type);
Dso* dso = nullptr;
if (dso_type == DSO_KERNEL || dso_type == DSO_KERNEL_MODULE) {
dso = FindKernelDsoOrNew(file_path);
} else {
- dso = FindUserDsoOrNew(file_path);
+ dso = FindUserDsoOrNew(file_path, 0, dso_type);
}
dso->SetMinVirtualAddress(min_vaddr);
dso->SetSymbols(symbols);
+ if (!dex_file_offsets.empty()) {
+ CHECK_EQ(static_cast<int>(dso_type), static_cast<int>(DSO_DEX_FILE));
+ for (uint64_t offset : dex_file_offsets) {
+ static_cast<DexFileDso*>(dso)->AddDexFileOffset(offset);
+ }
+ }
+}
+
+void ThreadTree::AddDexFileOffset(const std::string& file_path, uint64_t dex_file_offset) {
+ Dso* dso = FindUserDsoOrNew(file_path, 0, DSO_DEX_FILE);
+ CHECK_EQ(static_cast<int>(dso->type()), static_cast<int>(DSO_DEX_FILE));
+ static_cast<DexFileDso*>(dso)->AddDexFileOffset(dex_file_offset);
}
void ThreadTree::Update(const Record& record) {
diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h
index 11177027..dc936845 100644
--- a/simpleperf/thread_tree.h
+++ b/simpleperf/thread_tree.h
@@ -124,7 +124,9 @@ class ThreadTree {
void ClearThreadAndMap();
void AddDsoInfo(const std::string& file_path, uint32_t file_type,
- uint64_t min_vaddr, std::vector<Symbol>* symbols);
+ uint64_t min_vaddr, std::vector<Symbol>* symbols,
+ const std::vector<uint64_t>& dex_file_offsets);
+ void AddDexFileOffset(const std::string& file_path, uint64_t dex_file_offset);
// Update thread tree with information provided by record.
void Update(const Record& record);
@@ -135,7 +137,8 @@ class ThreadTree {
private:
ThreadEntry* CreateThread(int pid, int tid);
Dso* FindKernelDsoOrNew(const std::string& filename);
- Dso* FindUserDsoOrNew(const std::string& filename, uint64_t start_addr = 0);
+ Dso* FindUserDsoOrNew(const std::string& filename, uint64_t start_addr = 0,
+ DsoType dso_type = DSO_ELF_FILE);
MapEntry* AllocateMap(const MapEntry& value);
void FixOverlappedMap(MapSet* maps, const MapEntry* map);
diff --git a/simpleperf/tracing.cpp b/simpleperf/tracing.cpp
index 908402b4..9b609616 100644
--- a/simpleperf/tracing.cpp
+++ b/simpleperf/tracing.cpp
@@ -411,7 +411,7 @@ bool GetTracingData(const std::vector<const EventType*>& event_types,
data->clear();
std::vector<TraceType> trace_types;
for (const auto& type : event_types) {
- CHECK_EQ(PERF_TYPE_TRACEPOINT, type->type);
+ CHECK_EQ(static_cast<uint32_t>(PERF_TYPE_TRACEPOINT), type->type);
size_t pos = type->name.find(':');
TraceType trace_type;
trace_type.system = type->name.substr(0, pos);