OSDN Git Service

simpleperf: check perf event limit.
[android-x86/system-extras.git] / simpleperf / environment.cpp
index 8b39881..5a8552a 100644 (file)
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 
+#if defined(__ANDROID__)
+#include <sys/system_properties.h>
+#endif
+
 #include "read_elf.h"
 #include "utils.h"
 
@@ -141,37 +145,8 @@ bool ProcessKernelSymbols(const std::string& symbol_file,
   return false;
 }
 
-static bool FindStartOfKernelSymbolCallback(const KernelSymbol& symbol, uint64_t* start_addr) {
-  if (symbol.module == nullptr) {
-    *start_addr = symbol.addr;
-    return true;
-  }
-  return false;
-}
-
-static bool FindStartOfKernelSymbol(const std::string& symbol_file, uint64_t* start_addr) {
-  return ProcessKernelSymbols(
-      symbol_file, std::bind(&FindStartOfKernelSymbolCallback, std::placeholders::_1, start_addr));
-}
-
-static bool FindKernelFunctionSymbolCallback(const KernelSymbol& symbol, const std::string& name,
-                                             uint64_t* addr) {
-  if ((symbol.type == 'T' || symbol.type == 'W' || symbol.type == 'A') &&
-      symbol.module == nullptr && name == symbol.name) {
-    *addr = symbol.addr;
-    return true;
-  }
-  return false;
-}
-
-static bool FindKernelFunctionSymbol(const std::string& symbol_file, const std::string& name,
-                                     uint64_t* addr) {
-  return ProcessKernelSymbols(
-      symbol_file, std::bind(&FindKernelFunctionSymbolCallback, std::placeholders::_1, name, addr));
-}
-
-std::vector<ModuleMmap> GetLoadedModules() {
-  std::vector<ModuleMmap> result;
+static std::vector<KernelMmap> GetLoadedModules() {
+  std::vector<KernelMmap> result;
   FILE* fp = fopen("/proc/modules", "re");
   if (fp == nullptr) {
     // There is no /proc/modules on Android devices, so we don't print error if failed to open it.
@@ -185,7 +160,7 @@ std::vector<ModuleMmap> GetLoadedModules() {
     char name[reader.MaxLineSize()];
     uint64_t addr;
     if (sscanf(line, "%s%*lu%*u%*s%*s 0x%" PRIx64, name, &addr) == 2) {
-      ModuleMmap map;
+      KernelMmap map;
       map.name = name;
       map.start_addr = addr;
       result.push_back(map);
@@ -223,9 +198,9 @@ static void GetAllModuleFiles(const std::string& path,
   }
 }
 
-static std::vector<ModuleMmap> GetModulesInUse() {
+static std::vector<KernelMmap> GetModulesInUse() {
   // TODO: There is no /proc/modules or /lib/modules on Android, find methods work on it.
-  std::vector<ModuleMmap> module_mmaps = GetLoadedModules();
+  std::vector<KernelMmap> module_mmaps = GetLoadedModules();
   std::string linux_version = GetLinuxVersion();
   std::string module_dirpath = "/lib/modules/" + linux_version + "/kernel";
   std::unordered_map<std::string, std::string> module_file_map;
@@ -239,24 +214,23 @@ static std::vector<ModuleMmap> GetModulesInUse() {
   return module_mmaps;
 }
 
-bool GetKernelAndModuleMmaps(KernelMmap* kernel_mmap, std::vector<ModuleMmap>* module_mmaps) {
-  if (!FindStartOfKernelSymbol("/proc/kallsyms", &kernel_mmap->start_addr)) {
-    LOG(DEBUG) << "call FindStartOfKernelSymbol() failed";
-    return false;
-  }
-  if (!FindKernelFunctionSymbol("/proc/kallsyms", "_text", &kernel_mmap->pgoff)) {
-    LOG(DEBUG) << "call FindKernelFunctionSymbol() failed";
-    return false;
-  }
+void GetKernelAndModuleMmaps(KernelMmap* kernel_mmap, std::vector<KernelMmap>* module_mmaps) {
   kernel_mmap->name = DEFAULT_KERNEL_MMAP_NAME;
+  kernel_mmap->start_addr = 0;
+  kernel_mmap->filepath = kernel_mmap->name;
   *module_mmaps = GetModulesInUse();
+  for (auto& map : *module_mmaps) {
+    if (map.filepath.empty()) {
+      map.filepath = "[" + map.name + "]";
+    }
+  }
+
   if (module_mmaps->size() == 0) {
     kernel_mmap->len = std::numeric_limits<unsigned long long>::max() - kernel_mmap->start_addr;
   } else {
     std::sort(
         module_mmaps->begin(), module_mmaps->end(),
-        [](const ModuleMmap& m1, const ModuleMmap& m2) { return m1.start_addr < m2.start_addr; });
-    CHECK_LE(kernel_mmap->start_addr, (*module_mmaps)[0].start_addr);
+        [](const KernelMmap& m1, const KernelMmap& m2) { return m1.start_addr < m2.start_addr; });
     // When not having enough privilege, all addresses are read as 0.
     if (kernel_mmap->start_addr == (*module_mmaps)[0].start_addr) {
       kernel_mmap->len = 0;
@@ -274,7 +248,6 @@ bool GetKernelAndModuleMmaps(KernelMmap* kernel_mmap, std::vector<ModuleMmap>* m
     module_mmaps->back().len =
         std::numeric_limits<unsigned long long>::max() - module_mmaps->back().start_addr;
   }
-  return true;
 }
 
 static bool ReadThreadNameAndTgid(const std::string& status_file, std::string* comm, pid_t* tgid) {
@@ -441,3 +414,66 @@ bool GetExecPath(std::string* exec_path) {
   *exec_path = path;
   return true;
 }
+
+/*
+ * perf event paranoia level:
+ *  -1 - not paranoid at all
+ *   0 - disallow raw tracepoint access for unpriv
+ *   1 - disallow cpu events for unpriv
+ *   2 - disallow kernel profiling for unpriv
+ *   3 - disallow user profiling for unpriv
+ */
+static bool ReadPerfEventParanoid(int* value) {
+  std::string s;
+  if (!android::base::ReadFileToString("/proc/sys/kernel/perf_event_paranoid", &s)) {
+    PLOG(ERROR) << "failed to read /proc/sys/kernel/perf_event_paranoid";
+    return false;
+  }
+  s = android::base::Trim(s);
+  if (!android::base::ParseInt(s.c_str(), value)) {
+    PLOG(ERROR) << "failed to parse /proc/sys/kernel/perf_event_paranoid: " << s;
+    return false;
+  }
+  return true;
+}
+
+static const char* GetLimitLevelDescription(int limit_level) {
+  switch (limit_level) {
+    case -1: return "unlimited";
+    case 0: return "disallowing raw tracepoint access for unpriv";
+    case 1: return "disallowing cpu events for unpriv";
+    case 2: return "disallowing kernel profiling for unpriv";
+    case 3: return "disallowing user profiling for unpriv";
+    default: return "unknown level";
+  }
+}
+
+bool CheckPerfEventLimit() {
+  // root is not limited by /proc/sys/kernel/perf_event_paranoid.
+  if (IsRoot()) {
+    return true;
+  }
+  int limit_level;
+  if (!ReadPerfEventParanoid(&limit_level)) {
+    return false;
+  }
+  if (limit_level <= 1) {
+    return true;
+  }
+#if defined(__ANDROID__)
+  // Try to enable perf_event_paranoid by setprop security.perf_harden=0.
+  if (__system_property_set("security.perf_harden", "0") == 0) {
+    sleep(1);
+    if (ReadPerfEventParanoid(&limit_level) && limit_level <= 1) {
+      return true;
+    }
+  }
+  LOG(WARNING) << "/proc/sys/kernel/perf_event_paranoid is " << limit_level
+      << ", " << GetLimitLevelDescription(limit_level) << ".";
+  LOG(WARNING) << "Try using `adb shell setprop security.perf_harden 0` to allow profiling.";
+#else
+  LOG(WARNING) << "/proc/sys/kernel/perf_event_paranoid is " << limit_level
+      << ", " << GetLimitLevelDescription(limit_level) << ".";
+#endif
+  return true;
+}