OSDN Git Service

Merge "simpleperf: better support kernel symbols when running as root."
authorYabin Cui <yabinc@google.com>
Mon, 22 May 2017 18:16:21 +0000 (18:16 +0000)
committerGerrit Code Review <noreply-gerritcodereview@google.com>
Mon, 22 May 2017 18:16:23 +0000 (18:16 +0000)
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 486f360..acba6ab 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 ec926f9..d0cb835 100644 (file)
@@ -481,3 +481,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);