OSDN Git Service

Simpleperf: support branch stack sampling in `simpleperf record`.
authorYabin Cui <yabinc@google.com>
Wed, 3 Jun 2015 00:54:52 +0000 (17:54 -0700)
committerYabin Cui <yabinc@google.com>
Thu, 4 Jun 2015 00:00:29 +0000 (17:00 -0700)
This only adds support in simpleperf. Branch stack sampling still
lacks kernel support on arm devices.
Use perf_event.h of bionic in host build, because the perf_event.h
of platform glibc is too old.

Bug: 19483574
Change-Id: I9c7332c054e93e7433717dd293d3f366b1802e2d

simpleperf/Android.mk
simpleperf/cmd_record.cpp
simpleperf/cmd_record_test.cpp
simpleperf/event_selection_set.cpp
simpleperf/event_selection_set.h
simpleperf/perf_event.h
simpleperf/record.cpp
simpleperf/record.h

index fc1e0ff..b7213d7 100644 (file)
@@ -17,6 +17,8 @@
 LOCAL_PATH := $(call my-dir)
 
 simpleperf_common_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
+simpleperf_use_bionic_perf_event_h_flag := -DUSE_BIONIC_PERF_EVENT_H -I bionic
+simpleperf_host_common_cppflags := $(simpleperf_common_cppflags) $(simpleperf_use_bionic_perf_event_h_flag)
 
 simpleperf_common_shared_libraries := \
   libbase \
@@ -58,7 +60,7 @@ include $(BUILD_STATIC_LIBRARY)
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
 LOCAL_SRC_FILES := $(libsimpleperf_src_files)
 LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
 LOCAL_LDLIBS := -lrt
@@ -85,7 +87,7 @@ include $(BUILD_EXECUTABLE)
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
 LOCAL_SRC_FILES := main.cpp
 LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
 LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
@@ -123,7 +125,7 @@ include $(BUILD_NATIVE_TEST)
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
 LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
 LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
 LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
index 98a0cd5..94cf052 100644 (file)
@@ -18,6 +18,7 @@
 #include <poll.h>
 #include <signal.h>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include <base/logging.h>
 
 static std::string default_measured_event_type = "cpu-cycles";
 
+static std::unordered_map<std::string, uint64_t> branch_sampling_type_map = {
+    {"u", PERF_SAMPLE_BRANCH_USER},
+    {"k", PERF_SAMPLE_BRANCH_KERNEL},
+    {"any", PERF_SAMPLE_BRANCH_ANY},
+    {"any_call", PERF_SAMPLE_BRANCH_ANY_CALL},
+    {"any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN},
+    {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL},
+};
+
 class RecordCommandImpl {
  public:
   RecordCommandImpl()
       : use_sample_freq_(true),
         sample_freq_(1000),
         system_wide_collection_(false),
+        branch_sampling_(0),
         measured_event_type_(nullptr),
         perf_mmap_pages_(256),
         record_filename_("perf.data") {
@@ -59,7 +70,7 @@ class RecordCommandImpl {
  private:
   bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
   bool SetMeasuredEventType(const std::string& event_type_name);
-  void SetEventSelection();
+  bool SetEventSelection();
   bool WriteData(const char* data, size_t size);
   bool DumpKernelAndModuleMmaps();
   bool DumpThreadCommAndMmaps();
@@ -71,6 +82,7 @@ class RecordCommandImpl {
   uint64_t sample_period_;  // Sample once when 'sample_period_' events occur.
 
   bool system_wide_collection_;
+  uint64_t branch_sampling_;
   const EventType* measured_event_type_;
   EventSelectionSet event_selection_set_;
 
@@ -94,7 +106,9 @@ bool RecordCommandImpl::Run(const std::vector<std::string>& args) {
       return false;
     }
   }
-  SetEventSelection();
+  if (!SetEventSelection()) {
+    return false;
+  }
 
   // 2. Create workload.
   if (workload_args.empty()) {
@@ -178,6 +192,8 @@ bool RecordCommandImpl::ParseOptions(const std::vector<std::string>& args,
   for (i = 1; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
     if (args[i] == "-a") {
       system_wide_collection_ = true;
+    } else if (args[i] == "-b") {
+      branch_sampling_ = branch_sampling_type_map["any"];
     } else if (args[i] == "-c") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -207,6 +223,19 @@ bool RecordCommandImpl::ParseOptions(const std::vector<std::string>& args,
         return false;
       }
       use_sample_freq_ = true;
+    } else if (args[i] == "-j") {
+      if (!NextArgumentOrError(args, &i)) {
+        return false;
+      }
+      std::vector<std::string> branch_sampling_types = android::base::Split(args[i], ",");
+      for (auto& type : branch_sampling_types) {
+        auto it = branch_sampling_type_map.find(type);
+        if (it == branch_sampling_type_map.end()) {
+          LOG(ERROR) << "unrecognized branch sampling filter: " << type;
+          return false;
+        }
+        branch_sampling_ |= it->second;
+      }
     } else if (args[i] == "-o") {
       if (!NextArgumentOrError(args, &i)) {
         return false;
@@ -237,7 +266,7 @@ bool RecordCommandImpl::SetMeasuredEventType(const std::string& event_type_name)
   return true;
 }
 
-void RecordCommandImpl::SetEventSelection() {
+bool RecordCommandImpl::SetEventSelection() {
   event_selection_set_.AddEventType(*measured_event_type_);
   if (use_sample_freq_) {
     event_selection_set_.SetSampleFreq(sample_freq_);
@@ -245,6 +274,10 @@ void RecordCommandImpl::SetEventSelection() {
     event_selection_set_.SetSamplePeriod(sample_period_);
   }
   event_selection_set_.SampleIdAll();
+  if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
+    return false;
+  }
+  return true;
 }
 
 bool RecordCommandImpl::WriteData(const char* data, size_t size) {
@@ -371,11 +404,24 @@ class RecordCommand : public Command {
                 "    Gather sampling information when running [command]. If [command]\n"
                 "    is not specified, sleep 1 is used instead.\n"
                 "    -a           System-wide collection.\n"
+                "    -b           Enable take branch stack sampling. Same as '-j any'\n"
                 "    -c count     Set event sample period.\n"
                 "    -e event     Select the event to sample (Use `simpleperf list`)\n"
                 "                 to find all possible event names.\n"
                 "    -f freq      Set event sample frequency.\n"
                 "    -F freq      Same as '-f freq'.\n"
+                "    -j branch_filter1,branch_filter2,...\n"
+                "                 Enable taken branch stack sampling. Each sample\n"
+                "                 captures a series of consecutive taken branches.\n"
+                "                 The following filters are defined:\n"
+                "                   any: any type of branch\n"
+                "                   any_call: any function call or system call\n"
+                "                   any_ret: any function return or system call return\n"
+                "                   ind_call: any indirect branch\n"
+                "                   u: only when the branch target is at the user level\n"
+                "                   k: only when the branch target is in the kernel\n"
+                "                 This option requires at least one branch type among any,\n"
+                "                 any_call, any_ret, ind_call.\n"
                 "    -o record_file_name    Set record file name, default is perf.data.\n") {
   }
 
index 9e332bb..f92f027 100644 (file)
@@ -90,3 +90,11 @@ TEST_F(RecordCommandTest, dump_build_id_feature) {
 TEST_F(RecordCommandTest, tracepoint_event) {
   ASSERT_TRUE(record_cmd->Run({"record", "-a", "-e", "sched:sched_switch", "sleep", "1"}));
 }
+
+TEST_F(RecordCommandTest, branch_sampling) {
+  ASSERT_TRUE(record_cmd->Run({"record", "-a", "-b", "sleep", "1"}));
+  ASSERT_TRUE(record_cmd->Run({"record", "-j", "any,any_call,any_ret,ind_call", "sleep", "1"}));
+  ASSERT_TRUE(record_cmd->Run({"record", "-j", "any,k", "sleep", "1"}));
+  ASSERT_TRUE(record_cmd->Run({"record", "-j", "any,u", "sleep", "1"}));
+  ASSERT_FALSE(record_cmd->Run({"record", "-j", "u", "sleep", "1"}));
+}
index 858afa6..6f14b80 100644 (file)
@@ -57,6 +57,25 @@ void EventSelectionSet::SetSamplePeriod(uint64_t sample_period) {
   }
 }
 
+bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) {
+  if (branch_sample_type != 0 &&
+      (branch_sample_type & (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
+                             PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) {
+    LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex << branch_sample_type;
+    return false;
+  }
+  for (auto& selection : selections_) {
+    perf_event_attr& attr = selection.event_attr;
+    if (branch_sample_type != 0) {
+      attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
+    } else {
+      attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK;
+    }
+    attr.branch_sample_type = branch_sample_type;
+  }
+  return true;
+}
+
 bool EventSelectionSet::OpenEventFilesForAllCpus() {
   std::vector<int> cpus = GetOnlineCpus();
   if (cpus.empty()) {
@@ -72,8 +91,8 @@ bool EventSelectionSet::OpenEventFilesForAllCpus() {
     // As the online cpus can be enabled or disabled at runtime, we may not open event file for
     // all cpus successfully. But we should open at least one cpu successfully.
     if (selection.event_fds.empty()) {
-      LOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type->name
-                 << " on all cpus";
+      PLOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type->name
+                  << " on all cpus";
       return false;
     }
   }
index 78be069..e764e0b 100644 (file)
@@ -53,6 +53,7 @@ class EventSelectionSet {
   void SampleIdAll();
   void SetSampleFreq(uint64_t sample_freq);
   void SetSamplePeriod(uint64_t sample_period);
+  bool SetBranchSampling(uint64_t branch_sample_type);
 
   bool OpenEventFilesForAllCpus();
   bool OpenEventFilesForProcess(pid_t pid);
index a91eb6b..1688dc9 100644 (file)
 #ifndef SIMPLE_PERF_PERF_EVENT_H_
 #define SIMPLE_PERF_PERF_EVENT_H_
 
+#if defined(USE_BIONIC_PERF_EVENT_H)
+
+#include <libc/kernel/uapi/linux/perf_event.h>
+
+#else
+
 #include <linux/perf_event.h>
 
+#endif
+
 #endif  // SIMPLE_PERF_PERF_EVENT_H_
index 71cb493..4a1edb4 100644 (file)
@@ -48,6 +48,13 @@ void MoveFromBinaryFormat(T& data, const char*& p) {
 }
 
 template <class T>
+void MoveFromBinaryFormat(T* data_p, size_t n, const char*& p) {
+  size_t size = n * sizeof(T);
+  memcpy(data_p, p, size);
+  p += size;
+}
+
+template <class T>
 void MoveToBinaryFormat(const T& data, char*& p) {
   *reinterpret_cast<T*>(p) = data;
   p += sizeof(T);
@@ -263,6 +270,12 @@ SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header*
   if (sample_type & PERF_SAMPLE_PERIOD) {
     MoveFromBinaryFormat(period_data, p);
   }
+  if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
+    uint64_t nr;
+    MoveFromBinaryFormat(nr, p);
+    branch_stack_data.stack.resize(nr);
+    MoveFromBinaryFormat(branch_stack_data.stack.data(), nr, p);
+  }
   // TODO: Add parsing of other PERF_SAMPLE_*.
   CHECK_LE(p, end);
   if (p < end) {
@@ -296,6 +309,13 @@ void SampleRecord::DumpData(size_t indent) const {
   if (sample_type & PERF_SAMPLE_PERIOD) {
     PrintIndented(indent, "period %" PRId64 "\n", period_data.period);
   }
+  if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
+    PrintIndented(indent, "branch_stack nr=%" PRIu64 "\n", branch_stack_data.stack.size());
+    for (auto& item : branch_stack_data.stack) {
+      PrintIndented(indent + 1, "from 0x%" PRIx64 ", to 0x%" PRIx64 ", flags 0x%" PRIx64 "\n",
+                    item.from, item.to, item.flags);
+    }
+  }
 }
 
 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
index 83f60db..bbcd7d0 100644 (file)
@@ -68,6 +68,15 @@ struct PerfSamplePeriodType {
   uint64_t period;
 };
 
+struct PerfSampleBranchStackType {
+  struct BranchStackItemType {
+    uint64_t from;
+    uint64_t to;
+    uint64_t flags;
+  };
+  std::vector<BranchStackItemType> stack;
+};
+
 // SampleId is optional at the end of a record in binary format. Its content is determined by
 // sample_id_all and sample_type in perf_event_attr. To avoid the complexity of referring to
 // perf_event_attr each time, we copy sample_id_all and sample_type inside the SampleId structure.
@@ -177,6 +186,8 @@ struct SampleRecord : public Record {
   PerfSampleCpuType cpu_data;             // Valid if PERF_SAMPLE_CPU.
   PerfSamplePeriodType period_data;       // Valid if PERF_SAMPLE_PERIOD.
 
+  PerfSampleBranchStackType branch_stack_data;  // Valid if PERF_SAMPLE_BRANCH_STACK.
+
   SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader);
 
  protected: