OSDN Git Service

simpleperf: support [vdso].
authorYabin Cui <yabinc@google.com>
Fri, 19 May 2017 19:57:44 +0000 (12:57 -0700)
committerYabin Cui <yabinc@google.com>
Fri, 19 May 2017 22:12:04 +0000 (15:12 -0700)
Before this CL, there is no way to parse symbols from [vdso] or unwind
through it. In this CL, simpleperf dumps [vdso] segment in its own memory space
to local file system, so it can be used for getting symbols or unwinding.
It takes care that vdso files for 32bit version and 64bit version are not
misused.

Bug: None.
Test: run simpleperf_unit_test.
Test: run simpleperf on processes using vdso.

Change-Id: I9233daf1d07df262a4a0fcdeadd3e544f3ccc906

simpleperf/cmd_record.cpp
simpleperf/dso.cpp
simpleperf/dso.h
simpleperf/environment.cpp
simpleperf/environment.h
simpleperf/environment_test.cpp
simpleperf/thread_tree.cpp
simpleperf/thread_tree.h

index 27c089c..1eff009 100644 (file)
@@ -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;
index 40603fe..4f202bc 100644 (file)
@@ -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;
index b742a9c..f41b140 100644 (file)
 #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
index ed161c2..0ad2d6b 100644 (file)
@@ -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));
+}
index 11eee2f..2f4d58b 100644 (file)
@@ -89,5 +89,6 @@ static inline int gettid() {
 #endif
 
 ArchType GetMachineArch();
+void PrepareVdsoFile();
 
 #endif  // SIMPLE_PERF_ENVIRONMENT_H_
index 9b4cbab..4891467 100644 (file)
@@ -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());
+}
index 9def6f2..a6f86a8 100644 (file)
@@ -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();
index 79a4439..8df6b7b 100644 (file)
@@ -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);