summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2017-05-22 18:14:51 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2017-05-22 18:14:51 +0000
commitb8b93042c948bf8fa329336e43481d3d50227039 (patch)
treec1ce718db9892825761737404d0716280863971b
parent542c1b32fa9b9b878ff353336dc36df4d6f442e7 (diff)
parent63a1c3d83a68e9d94f37b71afe76d0769d744448 (diff)
downloadextras-b8b93042c948bf8fa329336e43481d3d50227039.tar.gz
Merge "simpleperf: support [vdso]."
-rw-r--r--simpleperf/cmd_record.cpp3
-rw-r--r--simpleperf/dso.cpp32
-rw-r--r--simpleperf/dso.h13
-rw-r--r--simpleperf/environment.cpp28
-rw-r--r--simpleperf/environment.h1
-rw-r--r--simpleperf/environment_test.cpp18
-rw-r--r--simpleperf/thread_tree.cpp7
-rw-r--r--simpleperf/thread_tree.h2
8 files changed, 92 insertions, 12 deletions
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 27c089c3..1eff009e 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -29,6 +29,7 @@
#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <android-base/test_utils.h>
#include "command.h"
#include "dwarf_unwind.h"
@@ -227,12 +228,14 @@ class RecordCommand : public Command {
};
bool RecordCommand::Run(const std::vector<std::string>& args) {
+ // 0. Do some environment preparation.
if (!CheckPerfEventLimit()) {
return false;
}
if (!InitPerfClock()) {
return false;
}
+ PrepareVdsoFile();
// 1. Parse options, and use default measured event type if not given.
std::vector<std::string> workload_args;
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index 40603fe8..4f202bcd 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -60,6 +60,8 @@ bool Dso::read_kernel_symbols_from_proc_;
std::unordered_map<std::string, BuildId> Dso::build_id_map_;
size_t Dso::dso_count_;
uint32_t Dso::g_dump_id_;
+std::unique_ptr<TemporaryFile> Dso::vdso_64bit_;
+std::unique_ptr<TemporaryFile> Dso::vdso_32bit_;
void Dso::SetDemangle(bool demangle) { demangle_ = demangle; }
@@ -119,6 +121,14 @@ void Dso::SetBuildIds(
build_id_map_ = std::move(map);
}
+void Dso::SetVdsoFile(std::unique_ptr<TemporaryFile> vdso_file, bool is_64bit) {
+ if (is_64bit) {
+ vdso_64bit_ = std::move(vdso_file);
+ } else {
+ vdso_32bit_ = std::move(vdso_file);
+ }
+}
+
BuildId Dso::FindExpectedBuildIdForPath(const std::string& path) {
auto it = build_id_map_.find(path);
if (it != build_id_map_.end()) {
@@ -131,12 +141,12 @@ BuildId Dso::GetExpectedBuildId() {
return FindExpectedBuildIdForPath(path_);
}
-std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type,
- const std::string& dso_path) {
- return std::unique_ptr<Dso>(new Dso(dso_type, dso_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)
+Dso::Dso(DsoType type, const std::string& path, bool force_64bit)
: type_(type),
path_(path),
debug_file_path_(path),
@@ -158,6 +168,12 @@ Dso::Dso(DsoType type, const std::string& path)
if (IsRegularFile(file_path)) {
debug_file_path_ = path_in_symfs;
}
+ } else if (path == "[vdso]") {
+ if (force_64bit && vdso_64bit_ != nullptr) {
+ debug_file_path_ = vdso_64bit_->path;
+ } else if (!force_64bit && vdso_32bit_ != nullptr) {
+ debug_file_path_ = vdso_32bit_->path;
+ }
}
size_t pos = path.find_last_of("/\\");
if (pos != std::string::npos) {
@@ -179,6 +195,8 @@ Dso::~Dso() {
read_kernel_symbols_from_proc_ = false;
build_id_map_.clear();
g_dump_id_ = 0;
+ vdso_64bit_ = nullptr;
+ vdso_32bit_ = nullptr;
}
}
@@ -326,11 +344,15 @@ static void VmlinuxSymbolCallback(const ElfFileSymbol& elf_symbol,
}
}
-bool CheckReadSymbolResult(ElfStatus result, const std::string& filename) {
+bool Dso::CheckReadSymbolResult(ElfStatus result, const std::string& filename) {
if (result == ElfStatus::NO_ERROR) {
LOG(VERBOSE) << "Read symbols from " << filename << " successfully";
return true;
} else if (result == ElfStatus::NO_SYMBOL_TABLE) {
+ if (path_ == "[vdso]") {
+ // Vdso only contains dynamic symbol table, and we can't change that.
+ return true;
+ }
// Lacking symbol table isn't considered as an error but worth reporting.
LOG(WARNING) << filename << " doesn't contain symbol table";
return true;
diff --git a/simpleperf/dso.h b/simpleperf/dso.h
index b742a9cc..f41b1404 100644
--- a/simpleperf/dso.h
+++ b/simpleperf/dso.h
@@ -22,7 +22,10 @@
#include <unordered_map>
#include <vector>
+#include <android-base/test_utils.h>
+
#include "build_id.h"
+#include "read_elf.h"
struct Symbol {
uint64_t addr;
@@ -96,9 +99,10 @@ class Dso {
static void SetBuildIds(
const std::vector<std::pair<std::string, BuildId>>& build_ids);
static BuildId FindExpectedBuildIdForPath(const std::string& path);
+ static void SetVdsoFile(std::unique_ptr<TemporaryFile> vdso_file, bool is_64bit);
- static std::unique_ptr<Dso> CreateDso(DsoType dso_type,
- const std::string& dso_path);
+ static std::unique_ptr<Dso> CreateDso(DsoType dso_type, const std::string& dso_path,
+ bool force_64bit = false);
~Dso();
@@ -148,8 +152,10 @@ class Dso {
static std::unordered_map<std::string, BuildId> build_id_map_;
static size_t dso_count_;
static uint32_t g_dump_id_;
+ static std::unique_ptr<TemporaryFile> vdso_64bit_;
+ static std::unique_ptr<TemporaryFile> vdso_32bit_;
- Dso(DsoType type, const std::string& path);
+ Dso(DsoType type, const std::string& path, bool force_64bit);
void Load();
bool LoadKernel();
bool LoadKernelModule();
@@ -157,6 +163,7 @@ class Dso {
bool LoadEmbeddedElfFile();
void FixupSymbolLength();
BuildId GetExpectedBuildId();
+ bool CheckReadSymbolResult(ElfStatus result, const std::string& filename);
const DsoType type_;
// path of the shared library used by the profiled program
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index ed161c23..0ad2d6bd 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -475,3 +475,31 @@ ArchType GetMachineArch() {
}
return GetBuildArch();
}
+
+void PrepareVdsoFile() {
+ // vdso is an elf file in memory loaded in each process's user space by the kernel. To read
+ // symbols from it and unwind through it, we need to dump it into a file in storage.
+ // It doesn't affect much when failed to prepare vdso file, so there is no need to return values.
+ std::vector<ThreadMmap> thread_mmaps;
+ if (!GetThreadMmapsInProcess(getpid(), &thread_mmaps)) {
+ return;
+ }
+ const ThreadMmap* vdso_map = nullptr;
+ for (const auto& map : thread_mmaps) {
+ if (map.name == "[vdso]") {
+ vdso_map = &map;
+ break;
+ }
+ }
+ if (vdso_map == nullptr) {
+ return;
+ }
+ std::string s(vdso_map->len, '\0');
+ memcpy(&s[0], reinterpret_cast<void*>(static_cast<uintptr_t>(vdso_map->start_addr)),
+ vdso_map->len);
+ std::unique_ptr<TemporaryFile> tmpfile(new TemporaryFile);
+ if (!android::base::WriteStringToFile(s, tmpfile->path)) {
+ return;
+ }
+ Dso::SetVdsoFile(std::move(tmpfile), sizeof(size_t) == sizeof(uint64_t));
+}
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 11eee2ff..2f4d58b9 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -89,5 +89,6 @@ static inline int gettid() {
#endif
ArchType GetMachineArch();
+void PrepareVdsoFile();
#endif // SIMPLE_PERF_ENVIRONMENT_H_
diff --git a/simpleperf/environment_test.cpp b/simpleperf/environment_test.cpp
index 9b4cbab3..48914674 100644
--- a/simpleperf/environment_test.cpp
+++ b/simpleperf/environment_test.cpp
@@ -16,6 +16,9 @@
#include <gtest/gtest.h>
+#include <android-base/file.h>
+
+#include "dso.h"
#include "environment.h"
TEST(environment, GetCpusFromString) {
@@ -24,3 +27,18 @@ TEST(environment, GetCpusFromString) {
ASSERT_EQ(GetCpusFromString("0,2-3"), std::vector<int>({0, 2, 3}));
ASSERT_EQ(GetCpusFromString("1,0-3,3,4"), std::vector<int>({0, 1, 2, 3, 4}));
}
+
+TEST(environment, PrepareVdsoFile) {
+ std::string content;
+ ASSERT_TRUE(android::base::ReadFileToString("/proc/self/maps", &content));
+ if (content.find("[vdso]") == std::string::npos) {
+ // Vdso isn't used, no need to test.
+ return;
+ }
+ PrepareVdsoFile();
+ std::unique_ptr<Dso> dso = Dso::CreateDso(DSO_ELF_FILE, "[vdso]",
+ sizeof(size_t) == sizeof(uint64_t));
+ ASSERT_TRUE(dso != nullptr);
+ const std::vector<Symbol>& symbols = dso->GetSymbols();
+ ASSERT_FALSE(symbols.empty());
+}
diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp
index 9def6f21..a6f86a8a 100644
--- a/simpleperf/thread_tree.cpp
+++ b/simpleperf/thread_tree.cpp
@@ -130,7 +130,7 @@ void ThreadTree::AddThreadMap(int pid, int tid, uint64_t start_addr,
uint64_t len, uint64_t pgoff, uint64_t time,
const std::string& filename) {
ThreadEntry* thread = FindThreadOrNew(pid, tid);
- Dso* dso = FindUserDsoOrNew(filename);
+ Dso* dso = FindUserDsoOrNew(filename, start_addr);
MapEntry* map =
AllocateMap(MapEntry(start_addr, len, pgoff, time, dso, false));
FixOverlappedMap(thread->maps, map);
@@ -138,10 +138,11 @@ void ThreadTree::AddThreadMap(int pid, int tid, uint64_t start_addr,
CHECK(pair.second);
}
-Dso* ThreadTree::FindUserDsoOrNew(const std::string& filename) {
+Dso* ThreadTree::FindUserDsoOrNew(const std::string& filename, uint64_t start_addr) {
auto it = user_dso_tree_.find(filename);
if (it == user_dso_tree_.end()) {
- user_dso_tree_[filename] = Dso::CreateDso(DSO_ELF_FILE, filename);
+ 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);
}
return it->second.get();
diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h
index 79a4439c..8df6b7ba 100644
--- a/simpleperf/thread_tree.h
+++ b/simpleperf/thread_tree.h
@@ -123,7 +123,7 @@ class ThreadTree {
private:
ThreadEntry* CreateThread(int pid, int tid);
Dso* FindKernelDsoOrNew(const std::string& filename);
- Dso* FindUserDsoOrNew(const std::string& filename);
+ Dso* FindUserDsoOrNew(const std::string& filename, uint64_t start_addr = 0);
MapEntry* AllocateMap(const MapEntry& value);
void FixOverlappedMap(MapSet* maps, const MapEntry* map);