OSDN Git Service

simpleperf: add --include-filter in record cmd.
authorYabin Cui <yabinc@google.com>
Tue, 6 Aug 2019 17:29:45 +0000 (10:29 -0700)
committerYabin Cui <yabinc@google.com>
Thu, 29 Aug 2019 21:53:54 +0000 (14:53 -0700)
It is to only record selected binaries in cs-etm tracing.

Bug: 135204414
Test: run simpleperf_unit_test.

Change-Id: I5dae729aee1642d9384ee9bb88ee8cb25950131a

simpleperf/ETMRecorder.cpp
simpleperf/ETMRecorder.h
simpleperf/cmd_record.cpp
simpleperf/cmd_record_test.cpp
simpleperf/event_fd.cpp
simpleperf/event_fd.h
simpleperf/event_selection_set.cpp
simpleperf/event_selection_set.h

index b44a898..b561c48 100644 (file)
@@ -140,6 +140,7 @@ bool ETMRecorder::ReadEtmInfo() {
           ReadValueInEtmDir(name + "/trcidr/trcidr0", &cpu_info.trcidr0) &&
           ReadValueInEtmDir(name + "/trcidr/trcidr1", &cpu_info.trcidr1) &&
           ReadValueInEtmDir(name + "/trcidr/trcidr2", &cpu_info.trcidr2) &&
+          ReadValueInEtmDir(name + "/trcidr/trcidr4", &cpu_info.trcidr4) &&
           ReadValueInEtmDir(name + "/trcidr/trcidr8", &cpu_info.trcidr8) &&
           ReadValueInEtmDir(name + "/mgmt/trcauthstatus", &cpu_info.trcauthstatus);
       if (!success) {
@@ -209,4 +210,16 @@ AuxTraceInfoRecord ETMRecorder::CreateAuxTraceInfoRecord() {
   return AuxTraceInfoRecord(data, etm4_v);
 }
 
+size_t ETMRecorder::GetAddrFilterPairs() {
+  CHECK(etm_supported_);
+  size_t min_pairs = std::numeric_limits<size_t>::max();
+  for (auto& p : etm_info_) {
+    min_pairs = std::min<size_t>(min_pairs, GetBits(p.second.trcidr4, 0, 3));
+  }
+  if (min_pairs > 0) {
+    --min_pairs;  // One pair is used by the kernel to set default addr filter.
+  }
+  return min_pairs;
+}
+
 }  // namespace simpleperf
\ No newline at end of file
index 978efbc..219741f 100644 (file)
@@ -31,6 +31,7 @@ struct ETMPerCpu {
   uint32_t trcidr0;
   uint32_t trcidr1;
   uint32_t trcidr2;
+  uint32_t trcidr4;
   uint32_t trcidr8;
   uint32_t trcauthstatus;
 
@@ -55,6 +56,7 @@ class ETMRecorder {
   bool CheckEtmSupport();
   void SetEtmPerfEventAttr(perf_event_attr* attr);
   AuxTraceInfoRecord CreateAuxTraceInfoRecord();
+  size_t GetAddrFilterPairs();
 
  private:
   bool ReadEtmInfo();
index 41775cd..8945b54 100644 (file)
@@ -189,6 +189,9 @@ class RecordCommand : public Command {
 "--no-inherit  Don't record created child threads/processes.\n"
 "--cpu-percent <percent>  Set the max percent of cpu time used for recording.\n"
 "                         percent is in range [1-100], default is 25.\n"
+"--include-filter binary1,binary2,...\n"
+"                Trace only selected binaries in cs-etm instruction tracing.\n"
+"                Each entry is a binary path.\n"
 "\n"
 "Dwarf unwinding options:\n"
 "--post-unwind=(yes|no) If `--call-graph dwarf` option is used, then the user's\n"
@@ -825,6 +828,11 @@ bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
       }
     } else if (args[i] == "--in-app") {
       in_app_context_ = true;
+    } else if (args[i] == "--include-filter") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      event_selection_set_.SetIncludeFilters(android::base::Split(args[i], ","));
     } else if (args[i] == "-j") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
index 1821913..eade574 100644 (file)
@@ -23,6 +23,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
 #include <map>
 #include <memory>
@@ -840,4 +841,37 @@ TEST(record_cmd, aux_buffer_size_option) {
   ASSERT_FALSE(RunRecordCmd({"-e", "cs-etm", "--aux-buffer-size", "1024"}));
   // not power of two
   ASSERT_FALSE(RunRecordCmd({"-e", "cs-etm", "--aux-buffer-size", "12k"}));
+}
+
+TEST(record_cmd, include_filter_option) {
+  TEST_REQUIRE_HW_COUNTER();
+  if (!ETMRecorder::GetInstance().CheckEtmSupport()) {
+    GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device";
+    return;
+  }
+  FILE* fp = popen("which sleep", "r");
+  ASSERT_TRUE(fp != nullptr);
+  std::string path;
+  ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &path));
+  pclose(fp);
+  path = android::base::Trim(path);
+  std::string sleep_exec_path;
+  ASSERT_TRUE(android::base::Realpath(path, &sleep_exec_path));
+  // --include-filter doesn't apply to cpu-cycles.
+  ASSERT_FALSE(RunRecordCmd({"--include-filter", sleep_exec_path}));
+  TemporaryFile record_file;
+  ASSERT_TRUE(
+      RunRecordCmd({"-e", "cs-etm", "--include-filter", sleep_exec_path}, record_file.path));
+  TemporaryFile inject_file;
+  ASSERT_TRUE(
+      CreateCommandInstance("inject")->Run({"-i", record_file.path, "-o", inject_file.path}));
+  std::string data;
+  ASSERT_TRUE(android::base::ReadFileToString(inject_file.path, &data));
+  // Only instructions in sleep_exec_path are traced.
+  for (auto& line : android::base::Split(data, "\n")) {
+    if (android::base::StartsWith(line, "dso ")) {
+      std::string dso = line.substr(strlen("dso "), sleep_exec_path.size());
+      ASSERT_EQ(dso, sleep_exec_path);
+    }
+  }
 }
\ No newline at end of file
index 5bda379..e4dfab5 100644 (file)
@@ -124,6 +124,14 @@ bool EventFd::SetEnableEvent(bool enable) {
   return true;
 }
 
+bool EventFd::SetFilter(const std::string& filter) {
+  bool success = ioctl(perf_event_fd_, PERF_EVENT_IOC_SET_FILTER, filter.c_str()) >= 0;
+  if (!success) {
+    PLOG(ERROR) << "failed to set filter";
+  }
+  return success;
+}
+
 bool EventFd::InnerReadCounter(PerfCounter* counter) const {
   CHECK(counter != nullptr);
   if (!android::base::ReadFully(perf_event_fd_, counter, sizeof(*counter))) {
index 0de94a9..0b3b0e6 100644 (file)
@@ -59,6 +59,7 @@ class EventFd {
   // It tells the kernel to start counting and recording events specified by
   // this file.
   bool SetEnableEvent(bool enable);
+  bool SetFilter(const std::string& filter);
 
   bool ReadCounter(PerfCounter* counter);
 
index 1e7f790..e1cba8c 100644 (file)
@@ -565,7 +565,7 @@ bool EventSelectionSet::OpenEventFiles(const std::vector<int>& on_cpus) {
       }
     }
   }
-  return true;
+  return ApplyFilters();
 }
 
 bool EventSelectionSet::IsUserSpaceSamplerGroup(EventSelectionGroup& group) {
@@ -591,6 +591,47 @@ bool EventSelectionSet::OpenUserSpaceSamplersOnGroup(EventSelectionGroup& group,
   return true;
 }
 
+bool EventSelectionSet::ApplyFilters() {
+  if (include_filters_.empty()) {
+    return true;
+  }
+  if (!has_aux_trace_) {
+    LOG(ERROR) << "include filters only take effect in cs-etm instruction tracing";
+    return false;
+  }
+  size_t supported_pairs = ETMRecorder::GetInstance().GetAddrFilterPairs();
+  if (supported_pairs < include_filters_.size()) {
+    LOG(ERROR) << "filter binary count is " << include_filters_.size()
+               << ", bigger than maximum supported filters on device, which is " << supported_pairs;
+    return false;
+  }
+  std::string filter_str;
+  for (auto& binary : include_filters_) {
+    std::string path;
+    if (!android::base::Realpath(binary, &path)) {
+      PLOG(ERROR) << "failed to find include filter binary: " << binary;
+      return false;
+    }
+    uint64_t file_size = GetFileSize(path);
+    if (!filter_str.empty()) {
+      filter_str += ',';
+    }
+    android::base::StringAppendF(&filter_str, "filter 0/%" PRIu64 "@%s", file_size, path.c_str());
+  }
+  for (auto& group : groups_) {
+    for (auto& selection : group) {
+      if (IsEtmEventType(selection.event_type_modifier.event_type.type)) {
+        for (auto& event_fd : selection.event_fds) {
+          if (!event_fd->SetFilter(filter_str)) {
+            return false;
+          }
+        }
+      }
+    }
+  }
+  return true;
+}
+
 static bool ReadCounter(EventFd* event_fd, CounterInfo* counter) {
   if (!event_fd->ReadCounter(&counter->counter)) {
     return false;
index 426af2f..180213f 100644 (file)
@@ -109,6 +109,9 @@ class EventSelectionSet {
   bool NeedKernelSymbol() const;
   void SetRecordNotExecutableMaps(bool record);
   bool RecordNotExecutableMaps() const;
+  void SetIncludeFilters(std::vector<std::string>&& filters) {
+    include_filters_ = std::move(filters);
+  }
 
   void AddMonitoredProcesses(const std::set<pid_t>& processes) {
     processes_.insert(processes.begin(), processes.end());
@@ -172,6 +175,7 @@ class EventSelectionSet {
                                     const std::map<pid_t, std::set<pid_t>>& process_map);
   bool OpenEventFilesOnGroup(EventSelectionGroup& group, pid_t tid, int cpu,
                              std::string* failed_event_type);
+  bool ApplyFilters();
   bool ReadMmapEventData(bool with_time_limit);
 
   bool DetectCpuHotplugEvents();
@@ -196,6 +200,7 @@ class EventSelectionSet {
   std::unique_ptr<simpleperf::RecordReadThread> record_read_thread_;
 
   bool has_aux_trace_ = false;
+  std::vector<std::string> include_filters_;
 
   DISALLOW_COPY_AND_ASSIGN(EventSelectionSet);
 };