OSDN Git Service

simpleperf: add test for dumping regs for tracepoint events.
authorYabin Cui <yabinc@google.com>
Mon, 17 Jul 2017 21:36:37 +0000 (14:36 -0700)
committerYabin Cui <yabinc@google.com>
Tue, 18 Jul 2017 01:10:16 +0000 (18:10 -0700)
If the test fails, probably a kernel patch is missing:
5b09a094f2 arm64: perf: Fix callchain parse error with kernel tracepoint events

To support the test, also enable recording tracepoint events in
app's context.

Bug: http://b/29520177
Test: run CtsSimpleperfTestCases64 on devices.

Change-Id: I085114113732366305e92f6a1e6c3b6efc6ff5ff

simpleperf/cmd_record.cpp
simpleperf/cmd_record_test.cpp
simpleperf/cmd_stat.cpp
simpleperf/environment.cpp
simpleperf/environment.h
simpleperf/event_attr.cpp
simpleperf/event_type.cpp
simpleperf/event_type.h
simpleperf/nonlinux_support/nonlinux_support.cpp
simpleperf/workload.cpp

index d37c2a8..8bd7962 100644 (file)
@@ -153,6 +153,7 @@ class RecordCommand : public Command {
 #if 0
 // Below options are only used internally and shouldn't be visible to the public.
 "--in-app         We are already running in the app's context.\n"
+"--tracepoint-events file_name   Read tracepoint events from [file_name] instead of tracefs.\n"
 #endif
             // clang-format on
             ),
@@ -241,14 +242,9 @@ 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;
@@ -261,7 +257,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     // root.
     if (!IsRoot()) {
       return RunInAppContext(app_package_name_, "record", args, workload_args.size(),
-                             record_filename_);
+                             record_filename_, !event_selection_set_.GetTracepointEvents().empty());
     }
   }
   if (event_selection_set_.empty()) {
@@ -272,9 +268,15 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
   if (!SetEventSelectionFlags()) {
     return false;
   }
+
+  // 2. Do some environment preparation.
   ScopedCurrentArch scoped_arch(GetMachineArch());
+  if (!InitPerfClock()) {
+    return false;
+  }
+  PrepareVdsoFile();
 
-  // 2. Create workload.
+  // 3. Create workload.
   std::unique_ptr<Workload> workload;
   if (!workload_args.empty()) {
     workload = Workload::CreateWorkload(workload_args);
@@ -310,7 +312,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     need_to_check_targets = true;
   }
 
-  // 3. Open perf_event_files, create mapped buffers for perf_event_files.
+  // 4. Open perf_event_files, create mapped buffers for perf_event_files.
   if (!event_selection_set_.OpenEventFiles(cpus_)) {
     return false;
   }
@@ -319,12 +321,12 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     return false;
   }
 
-  // 4. Create perf.data.
+  // 5. Create perf.data.
   if (!CreateAndInitRecordFile()) {
     return false;
   }
 
-  // 5. Add read/signal/periodic Events.
+  // 6. Add read/signal/periodic Events.
   auto callback =
       std::bind(&RecordCommand::ProcessRecord, this, std::placeholders::_1);
   if (!event_selection_set_.PrepareToReadMmapEventData(callback)) {
@@ -348,7 +350,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     }
   }
 
-  // 6. Write records in mapped buffers of perf_event_files to output file while
+  // 7. Write records in mapped buffers of perf_event_files to output file while
   //    workload is running.
   start_sampling_time_in_ns_ = GetPerfClock();
   LOG(VERBOSE) << "start_sampling_time is " << start_sampling_time_in_ns_
@@ -369,7 +371,7 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     return false;
   }
 
-  // 7. Dump additional features, and close record file.
+  // 8. Dump additional features, and close record file.
   if (!DumpAdditionalFeatures(args)) {
     return false;
   }
@@ -377,14 +379,14 @@ bool RecordCommand::Run(const std::vector<std::string>& args) {
     return false;
   }
 
-  // 8. Unwind dwarf callchain.
+  // 9. Unwind dwarf callchain.
   if (post_unwind_) {
     if (!PostUnwind(args)) {
       return false;
     }
   }
 
-  // 9. Show brief record result.
+  // 10. Show brief record result.
   LOG(INFO) << "Samples recorded: " << sample_record_count_
             << ". Samples lost: " << lost_record_count_ << ".";
   if (sample_record_count_ + lost_record_count_ != 0) {
@@ -584,6 +586,13 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
         return false;
       }
       event_selection_set_.AddMonitoredThreads(tids);
+    } else if (args[i] == "--tracepoint-events") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      if (!SetTracepointEventsFilePath(args[i])) {
+        return false;
+      }
     } else {
       ReportUnknownOption(args, i);
       return false;
@@ -726,8 +735,8 @@ bool RecordCommand::DumpKernelSymbol() {
 bool RecordCommand::DumpTracingData() {
   std::vector<const EventType*> tracepoint_event_types =
       event_selection_set_.GetTracepointEvents();
-  if (tracepoint_event_types.empty()) {
-    return true;  // No need to dump tracing data.
+  if (tracepoint_event_types.empty() || !CanRecordRawData()) {
+    return true;  // No need to dump tracing data, or can't do it.
   }
   std::vector<char> tracing_data;
   if (!GetTracingData(tracepoint_event_types, &tracing_data)) {
index fcedcee..8b70fcd 100644 (file)
@@ -429,3 +429,34 @@ TEST(record_cmd, cpu_clock_for_a_long_time) {
   ASSERT_TRUE(RecordCmd()->Run(
       {"-e", "cpu-clock", "-o", tmpfile.path, "-p", pid, "--duration", "3"}));
 }
+
+TEST(record_cmd, dump_regs_for_tracepoint_events) {
+  // Check if the kernel can dump registers for tracepoint events.
+  // If not, probably a kernel patch below is missing:
+  // "5b09a094f2 arm64: perf: Fix callchain parse error with kernel tracepoint events"
+  std::vector<std::unique_ptr<Workload>> workloads;
+  CreateProcesses(1, &workloads);
+  std::string pid = std::to_string(workloads[0]->GetPid());
+  TemporaryFile tmpfile;
+  ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-p", pid, "-e", "sched:sched_switch",
+                                "-g", "--no-unwind", "--duration", "1"}));
+
+  // If the kernel patch is missing, all regs dumped in sample records are zero.
+  std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
+  CHECK(reader != nullptr);
+  std::unique_ptr<Record> r;
+  bool regs_all_zero = true;
+  while (reader->ReadRecord(r) && r && regs_all_zero) {
+    if (r->type() != PERF_RECORD_SAMPLE) {
+      continue;
+    }
+    SampleRecord* s = static_cast<SampleRecord*>(r.get());
+    for (size_t i = 0; i < s->regs_user_data.reg_nr; ++i) {
+      if (s->regs_user_data.regs[i] != 0u) {
+        regs_all_zero = false;
+        break;
+      }
+    }
+  }
+  ASSERT_FALSE(regs_all_zero);
+}
index 4c79801..a9d5036 100644 (file)
@@ -307,6 +307,7 @@ class StatCommand : public Command {
 #if 0
 // Below options are only used internally and shouldn't be visible to the public.
 "--in-app         We are already running in the app's context.\n"
+"--tracepoint-events file_name   Read tracepoint events from [file_name] instead of tracefs.\n"
 #endif
                 // clang-format on
                 ),
@@ -359,7 +360,7 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
   if (!app_package_name_.empty() && !in_app_context_) {
     if (!IsRoot()) {
       return RunInAppContext(app_package_name_, "stat", args, workload_args.size(),
-                             output_filename_);
+                             output_filename_, !event_selection_set_.GetTracepointEvents().empty());
     }
   }
   if (event_selection_set_.empty()) {
@@ -554,6 +555,13 @@ bool StatCommand::ParseOptions(const std::vector<std::string>& args,
         return false;
       }
       event_selection_set_.AddMonitoredThreads(tids);
+    } else if (args[i] == "--tracepoint-events") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      if (!SetTracepointEventsFilePath(args[i])) {
+        return false;
+      }
     } else if (args[i] == "--verbose") {
       verbose_mode_ = true;
     } else {
index e0027fc..bf5e663 100644 (file)
@@ -38,6 +38,7 @@
 #include <sys/system_properties.h>
 #endif
 
+#include "event_type.h"
 #include "IOEventLoop.h"
 #include "read_elf.h"
 #include "thread_tree.h"
@@ -352,6 +353,11 @@ static bool ReadPerfEventParanoid(int* value) {
   return true;
 }
 
+bool CanRecordRawData() {
+  int value;
+  return IsRoot() || (ReadPerfEventParanoid(&value) && value == -1);
+}
+
 static const char* GetLimitLevelDescription(int limit_level) {
   switch (limit_level) {
     case -1: return "unlimited";
@@ -541,16 +547,34 @@ int WaitForAppProcess(const std::string& package_name) {
   }
 }
 
+class ScopedFile {
+ public:
+  ScopedFile(const std::string& filepath, std::string app_package_name = "")
+      : filepath_(filepath), app_package_name_(app_package_name) {}
+
+  ~ScopedFile() {
+    if (app_package_name_.empty()) {
+      unlink(filepath_.c_str());
+    } else {
+      Workload::RunCmd({"run-as", app_package_name_, "rm", "-rf", filepath_});
+    }
+  }
+
+ private:
+  std::string filepath_;
+  std::string app_package_name_;
+};
+
 bool RunInAppContext(const std::string& app_package_name, const std::string& cmd,
                      const std::vector<std::string>& args, size_t workload_args_size,
-                     const std::string& output_filepath) {
+                     const std::string& output_filepath, bool need_tracepoint_events) {
   // 1. Test if the package exists.
   if (!Workload::RunCmd({"run-as", app_package_name, "echo", ">/dev/null"}, false)) {
     LOG(ERROR) << "Package " << app_package_name << "doesn't exist or isn't debuggable.";
     return false;
   }
 
-  // 2. Copy simpleperf binary to the package.
+  // 2. Copy simpleperf binary to the package. Create tracepoint_file if needed.
   std::string simpleperf_path;
   if (!android::base::Readlink("/proc/self/exe", &simpleperf_path)) {
     PLOG(ERROR) << "ReadLink failed";
@@ -559,12 +583,29 @@ bool RunInAppContext(const std::string& app_package_name, const std::string& cmd
   if (!Workload::RunCmd({"run-as", app_package_name, "cp", simpleperf_path, "simpleperf"})) {
     return false;
   }
+  ScopedFile scoped_simpleperf("simpleperf", app_package_name);
+  std::unique_ptr<ScopedFile> scoped_tracepoint_file;
+  const std::string tracepoint_file = "/data/local/tmp/tracepoint_events";
+  if (need_tracepoint_events) {
+    // Since we can't read tracepoint events from tracefs in app's context, we need to prepare
+    // them in tracepoint_file in shell's context, and pass the path of tracepoint_file to the
+    // child process using --tracepoint-events option.
+    if (!android::base::WriteStringToFile(GetTracepointEvents(), tracepoint_file)) {
+      PLOG(ERROR) << "Failed to store tracepoint events";
+      return false;
+    }
+    scoped_tracepoint_file.reset(new ScopedFile(tracepoint_file));
+  }
 
   // 3. Prepare to start child process to profile.
   std::string output_basename = output_filepath.empty() ? "" :
                                     android::base::Basename(output_filepath);
   std::vector<std::string> new_args =
       {"run-as", app_package_name, "./simpleperf", cmd, "--in-app"};
+  if (need_tracepoint_events) {
+    new_args.push_back("--tracepoint-events");
+    new_args.push_back(tracepoint_file);
+  }
   for (size_t i = 0; i < args.size(); ++i) {
     if (i >= args.size() - workload_args_size || args[i] != "-o") {
       new_args.push_back(args[i]);
index 603c9e6..b1c52ea 100644 (file)
@@ -72,6 +72,7 @@ bool CheckPerfEventLimit();
 bool GetMaxSampleFrequency(uint64_t* max_sample_freq);
 bool CheckSampleFrequency(uint64_t sample_freq);
 bool CheckKernelSymbolAddresses();
+bool CanRecordRawData();
 
 #if defined(__linux__)
 static inline uint64_t GetSystemClock() {
@@ -94,7 +95,7 @@ void PrepareVdsoFile();
 int WaitForAppProcess(const std::string& package_name);
 bool RunInAppContext(const std::string& app_package_name, const std::string& cmd,
                      const std::vector<std::string>& args, size_t workload_args_size,
-                     const std::string& output_filepath);
+                     const std::string& output_filepath, bool need_tracepoint_events);
 
 // Below two functions are only used in cts tests, to force stat/record cmd to run in app's context.
 void SetDefaultAppPackageName(const std::string& package_name);
index 1936448..7c58081 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <android-base/logging.h>
 
+#include "environment.h"
 #include "event_type.h"
 #include "utils.h"
 
@@ -92,7 +93,9 @@ perf_event_attr CreateDefaultPerfEventAttr(const EventType& event_type) {
 
   if (attr.type == PERF_TYPE_TRACEPOINT) {
     // Tracepoint information are stored in raw data in sample records.
-    attr.sample_type |= PERF_SAMPLE_RAW;
+    if (CanRecordRawData()) {
+      attr.sample_type |= PERF_SAMPLE_RAW;
+    }
   }
   return attr;
 }
index a69067d..bc639d1 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "event_type.h"
 
+#include <inttypes.h>
 #include <unistd.h>
 #include <algorithm>
 #include <string>
@@ -23,6 +24,8 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
 #include "event_attr.h"
@@ -35,12 +38,42 @@ static const std::vector<EventType> static_event_type_array = {
 #include "event_type_table.h"
 };
 
-static const std::vector<EventType> GetTracepointEventTypes() {
+static std::string tracepoint_events;
+
+bool SetTracepointEventsFilePath(const std::string& filepath) {
+  if (!android::base::ReadFileToString(filepath, &tracepoint_events)) {
+    PLOG(ERROR) << "Failed to read " << filepath;
+    return false;
+  }
+  return true;
+}
+
+std::string GetTracepointEvents() {
+  std::string result;
+  for (const EventType& event : GetAllEventTypes()) {
+    if (!result.empty()) {
+      result.push_back('\n');
+    }
+    result += android::base::StringPrintf("%s %" PRIu64, event.name.c_str(), event.config);
+  }
+  return result;
+}
+
+static std::vector<EventType> GetTracepointEventTypesFromString(const std::string& s) {
   std::vector<EventType> result;
-  if (!IsRoot()) {
-    // Not having permission to profile tracing events.
-    return result;
+  for (auto& line : android::base::Split(s, "\n")) {
+    std::vector<std::string> items = android::base::Split(line, " ");
+    CHECK_EQ(items.size(), 2u);
+    std::string event_name = items[0];
+    uint64_t id;
+    CHECK(android::base::ParseUint(items[1].c_str(), &id));
+    result.push_back(EventType(event_name, PERF_TYPE_TRACEPOINT, id, "", ""));
   }
+  return result;
+}
+
+static std::vector<EventType> GetTracepointEventTypesFromTraceFs() {
+  std::vector<EventType> result;
   const std::string tracepoint_dirname = "/sys/kernel/debug/tracing/events";
   for (const auto& system_name : GetSubDirs(tracepoint_dirname)) {
     std::string system_path = tracepoint_dirname + "/" + system_name;
@@ -59,6 +92,16 @@ static const std::vector<EventType> GetTracepointEventTypes() {
       result.push_back(EventType(system_name + ":" + event_name, PERF_TYPE_TRACEPOINT, id, "", ""));
     }
   }
+  return result;
+}
+
+static std::vector<EventType> GetTracepointEventTypes() {
+  std::vector<EventType> result;
+  if (!tracepoint_events.empty()) {
+    result = GetTracepointEventTypesFromString(tracepoint_events);
+  } else {
+    result = GetTracepointEventTypesFromTraceFs();
+  }
   std::sort(result.begin(), result.end(),
             [](const EventType& type1, const EventType& type2) { return type1.name < type2.name; });
   return result;
@@ -69,7 +112,7 @@ const std::vector<EventType>& GetAllEventTypes() {
   if (event_type_array.empty()) {
     event_type_array.insert(event_type_array.end(), static_event_type_array.begin(),
                             static_event_type_array.end());
-    const std::vector<EventType> tracepoint_array = GetTracepointEventTypes();
+    std::vector<EventType> tracepoint_array = GetTracepointEventTypes();
     event_type_array.insert(event_type_array.end(), tracepoint_array.begin(),
                             tracepoint_array.end());
   }
index a804b08..6ec544d 100644 (file)
@@ -52,6 +52,8 @@ struct EventType {
   std::string limited_arch;
 };
 
+bool SetTracepointEventsFilePath(const std::string& filepath);
+std::string GetTracepointEvents();
 const std::vector<EventType>& GetAllEventTypes();
 const EventType* FindEventTypeByName(const std::string& name);
 
index f132dd9..8c245f1 100644 (file)
@@ -28,3 +28,7 @@ std::vector<uint64_t> UnwindCallChain(int, const ThreadEntry&, const RegSet&,
 bool GetKernelBuildId(BuildId*) {
   return false;
 }
+
+bool CanRecordRawData() {
+  return false;
+}
index 4aaeb2a..60c9ed1 100644 (file)
@@ -45,7 +45,7 @@ bool Workload::RunCmd(const std::vector<std::string>& args, bool report_error) {
   std::string arg_str = android::base::Join(args, ' ');
   int ret = system(arg_str.c_str());
   if (ret != 0 && report_error) {
-    PLOG(ERROR) << "Failed to run cmd " << arg_str;
+    LOG(ERROR) << "Failed to run cmd " << arg_str << ", exitcode " << ret;
     return false;
   }
   return ret == 0;