OSDN Git Service

Support tracepoint event types in simpleperf.
authorYabin Cui <yabinc@google.com>
Thu, 30 Apr 2015 16:43:26 +0000 (09:43 -0700)
committerYabin Cui <yabinc@google.com>
Sat, 16 May 2015 00:14:00 +0000 (17:14 -0700)
Also support options in `simpleperf list`, add test about tracepoint event types.

Bug: 19483574

Change-Id: I2d2c2f300fe5e2968696228899084410aa9f29a4

13 files changed:
simpleperf/cmd_list.cpp
simpleperf/cmd_list_test.cpp
simpleperf/cmd_record_test.cpp
simpleperf/cmd_stat.cpp
simpleperf/cmd_stat_test.cpp
simpleperf/environment.h
simpleperf/event_attr.cpp
simpleperf/event_attr.h
simpleperf/event_fd.cpp
simpleperf/event_selection_set.cpp
simpleperf/event_type.cpp
simpleperf/event_type.h
simpleperf/record.cpp

index 923a884..18c3972 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <stdio.h>
+#include <map>
 #include <string>
 #include <vector>
 
 #include "event_type.h"
 #include "perf_event.h"
 
-static void PrintEventTypesOfType(uint32_t type, const char* type_name,
-                                  const std::vector<const EventType>& event_types) {
-  printf("List of %s:\n", type_name);
+static void PrintEventTypesOfType(uint32_t type, const std::string& type_name,
+                                  const std::vector<EventType>& event_types) {
+  printf("List of %s:\n", type_name.c_str());
   for (auto& event_type : event_types) {
     if (event_type.type == type && event_type.IsSupportedByKernel()) {
-      printf("  %s\n", event_type.name);
+      printf("  %s\n", event_type.name.c_str());
     }
   }
   printf("\n");
@@ -38,8 +39,8 @@ static void PrintEventTypesOfType(uint32_t type, const char* type_name,
 class ListCommand : public Command {
  public:
   ListCommand()
-      : Command("list", "list all available perf events",
-                "Usage: simpleperf list\n"
+      : Command("list", "list available event types",
+                "Usage: simpleperf list [hw|sw|cache|tracepoint]\n"
                 "    List all available perf events on this machine.\n") {
   }
 
@@ -47,16 +48,35 @@ class ListCommand : public Command {
 };
 
 bool ListCommand::Run(const std::vector<std::string>& args) {
-  if (args.size() != 1) {
-    LOG(ERROR) << "malformed command line: list subcommand needs no argument";
-    LOG(ERROR) << "try using \"help list\"";
-    return false;
+  static std::map<std::string, std::pair<int, std::string>> type_map = {
+      {"hw", {PERF_TYPE_HARDWARE, "hardware events"}},
+      {"sw", {PERF_TYPE_SOFTWARE, "software events"}},
+      {"cache", {PERF_TYPE_HW_CACHE, "hw-cache events"}},
+      {"tracepoint", {PERF_TYPE_TRACEPOINT, "tracepoint events"}},
+  };
+
+  std::vector<std::string> names;
+  if (args.size() == 1) {
+    for (auto& item : type_map) {
+      names.push_back(item.first);
+    }
+  } else {
+    for (size_t i = 1; i < args.size(); ++i) {
+      if (type_map.find(args[i]) != type_map.end()) {
+        names.push_back(args[i]);
+      } else {
+        LOG(ERROR) << "unknown event type category: " << args[i] << ", try using \"help list\"";
+        return false;
+      }
+    }
   }
+
   auto& event_types = EventTypeFactory::GetAllEventTypes();
 
-  PrintEventTypesOfType(PERF_TYPE_HARDWARE, "hardware events", event_types);
-  PrintEventTypesOfType(PERF_TYPE_SOFTWARE, "software events", event_types);
-  PrintEventTypesOfType(PERF_TYPE_HW_CACHE, "hw-cache events", event_types);
+  for (auto& name : names) {
+    auto it = type_map.find(name);
+    PrintEventTypesOfType(it->second.first, it->second.second, event_types);
+  }
   return true;
 }
 
index 4b873a1..ddc82ca 100644 (file)
 
 #include "command.h"
 
-TEST(cmd_list, smoke) {
-  Command* list_cmd = Command::FindCommandByName("list");
-  ASSERT_TRUE(list_cmd != nullptr);
+class ListCommandTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    list_cmd = Command::FindCommandByName("list");
+    ASSERT_TRUE(list_cmd != nullptr);
+  }
+
+  Command* list_cmd;
+};
+
+TEST_F(ListCommandTest, no_options) {
   ASSERT_TRUE(list_cmd->Run({"list"}));
 }
+
+TEST_F(ListCommandTest, one_option) {
+  ASSERT_TRUE(list_cmd->Run({"list", "sw"}));
+}
+
+TEST_F(ListCommandTest, multiple_options) {
+  ASSERT_TRUE(list_cmd->Run({"list", "hw", "tracepoint"}));
+}
index f0a8878..9e332bb 100644 (file)
@@ -86,3 +86,7 @@ TEST_F(RecordCommandTest, dump_build_id_feature) {
   ASSERT_TRUE(file_header->features[FEAT_BUILD_ID / 8] & (1 << (FEAT_BUILD_ID % 8)));
   ASSERT_GT(reader->FeatureSectionDescriptors().size(), 0u);
 }
+
+TEST_F(RecordCommandTest, tracepoint_event) {
+  ASSERT_TRUE(record_cmd->Run({"record", "-a", "-e", "sched:sched_switch", "sleep", "1"}));
+}
index c8e59d9..16af81e 100644 (file)
@@ -32,8 +32,9 @@
 #include "workload.h"
 
 static std::vector<std::string> default_measured_event_types{
-    "cpu-cycles", "stalled-cycles-frontend", "stalled-cycles-backend", "instructions",
-    "branch-instructions", "branch-misses", "task-clock", "context-switches", "page-faults",
+    "cpu-cycles",   "stalled-cycles-frontend", "stalled-cycles-backend",
+    "instructions", "branch-instructions",     "branch-misses",
+    "task-clock",   "context-switches",        "page-faults",
 };
 
 class StatCommandImpl {
@@ -210,7 +211,7 @@ bool StatCommandImpl::ShowCounters(
       }
     }
     printf("%'30" PRId64 "%s  %s\n", scaled_count, scaled ? "(scaled)" : "       ",
-           event_type->name);
+           event_type->name.c_str());
   }
   printf("\nTotal test time: %lf seconds.\n",
          std::chrono::duration_cast<std::chrono::duration<double>>(counting_duration).count());
index 6a7a1cd..0ed49bc 100644 (file)
@@ -44,3 +44,7 @@ TEST_F(StatCommandTest, system_wide_option) {
 TEST_F(StatCommandTest, verbose_option) {
   ASSERT_TRUE(stat_cmd->Run({"stat", "--verbose", "sleep", "1"}));
 }
+
+TEST_F(StatCommandTest, tracepoint_event) {
+  ASSERT_TRUE(stat_cmd->Run({"stat", "-a", "-e", "sched:sched_switch", "sleep", "1"}));
+}
index f81005c..6d81e98 100644 (file)
@@ -20,6 +20,7 @@
 #include <functional>
 #include <string>
 #include <vector>
+
 #include "build_id.h"
 
 std::vector<int> GetOnlineCpus();
index 2b05931..79ed4b8 100644 (file)
@@ -46,17 +46,17 @@ static std::string BitsToString(const std::string& name, uint64_t bits,
 
 static std::string SampleTypeToString(uint64_t sample_type) {
   static std::vector<std::pair<int, std::string>> sample_type_names = {
-      {PERF_SAMPLE_IP, "ip"},
-      {PERF_SAMPLE_TID, "tid"},
-      {PERF_SAMPLE_TIME, "time"},
       {PERF_SAMPLE_ADDR, "addr"},
-      {PERF_SAMPLE_READ, "read"},
       {PERF_SAMPLE_CALLCHAIN, "callchain"},
-      {PERF_SAMPLE_ID, "id"},
       {PERF_SAMPLE_CPU, "cpu"},
+      {PERF_SAMPLE_ID, "id"},
+      {PERF_SAMPLE_IP, "ip"},
       {PERF_SAMPLE_PERIOD, "period"},
-      {PERF_SAMPLE_STREAM_ID, "stream_id"},
       {PERF_SAMPLE_RAW, "raw"},
+      {PERF_SAMPLE_READ, "read"},
+      {PERF_SAMPLE_STREAM_ID, "stream_id"},
+      {PERF_SAMPLE_TID, "tid"},
+      {PERF_SAMPLE_TIME, "time"},
   };
   return BitsToString("sample_type", sample_type, sample_type_names);
 }
@@ -85,6 +85,11 @@ perf_event_attr CreateDefaultPerfEventAttr(const EventType& event_type) {
   attr.read_format =
       PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID;
   attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_PERIOD;
+
+  if (attr.type == PERF_TYPE_TRACEPOINT) {
+    attr.sample_freq = 0;
+    attr.sample_period = 1;
+  }
   return attr;
 }
 
index 52f4aca..79d3df4 100644 (file)
@@ -17,8 +17,7 @@
 #ifndef SIMPLE_PERF_EVENT_ATTR_H_
 #define SIMPLE_PERF_EVENT_ATTR_H_
 
-#include <stdint.h>
-#include <string>
+#include <stddef.h>
 
 #include "perf_event.h"
 
index 386685c..f0a1ad5 100644 (file)
@@ -22,6 +22,7 @@
 #include <sys/mman.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <atomic>
 #include <memory>
 
 #include <base/file.h>
index 61f1705..858afa6 100644 (file)
@@ -191,7 +191,7 @@ std::string EventSelectionSet::FindEventFileNameById(uint64_t id) {
 EventSelectionSet::EventSelection* EventSelectionSet::FindSelectionByType(
     const EventType& event_type) {
   for (auto& selection : selections_) {
-    if (strcmp(selection.event_type->name, event_type.name) == 0) {
+    if (selection.event_type->name == event_type.name) {
       return &selection;
     }
   }
index ee0e161..a3b8fd2 100644 (file)
 #include "event_type.h"
 
 #include <unistd.h>
+#include <algorithm>
 #include <string>
 #include <vector>
 
+#include <base/file.h>
 #include <base/logging.h>
 
 #include "event_attr.h"
 #include "event_fd.h"
+#include "utils.h"
 
 #define EVENT_TYPE_TABLE_ENTRY(name, type, config) \
   { name, type, config }                           \
   ,
 
-static std::vector<const EventType> event_type_array = {
+static const std::vector<EventType> static_event_type_array = {
 #include "event_type_table.h"
 };
 
@@ -42,14 +45,51 @@ bool EventType::IsSupportedByKernel() const {
   return IsEventTypeSupportedByKernel(*this);
 }
 
-const std::vector<const EventType>& EventTypeFactory::GetAllEventTypes() {
+static const std::vector<EventType> GetTracepointEventTypes() {
+  std::vector<EventType> result;
+  const std::string tracepoint_dirname = "/sys/kernel/debug/tracing/events";
+  std::vector<std::string> system_dirs;
+  GetEntriesInDir(tracepoint_dirname, nullptr, &system_dirs);
+  for (auto& system_name : system_dirs) {
+    std::string system_path = tracepoint_dirname + "/" + system_name;
+    std::vector<std::string> event_dirs;
+    GetEntriesInDir(system_path, nullptr, &event_dirs);
+    for (auto& event_name : event_dirs) {
+      std::string id_path = system_path + "/" + event_name + "/id";
+      std::string id_content;
+      if (!android::base::ReadFileToString(id_path, &id_content)) {
+        continue;
+      }
+      char* endptr;
+      uint64_t id = strtoull(id_content.c_str(), &endptr, 10);
+      if (endptr == id_content.c_str()) {
+        LOG(DEBUG) << "unexpected id '" << id_content << "' in " << id_path;
+        continue;
+      }
+      result.push_back(EventType(system_name + ":" + event_name, PERF_TYPE_TRACEPOINT, id));
+    }
+  }
+  std::sort(result.begin(), result.end(),
+            [](const EventType& type1, const EventType& type2) { return type1.name < type2.name; });
+  return result;
+}
+
+const std::vector<EventType>& EventTypeFactory::GetAllEventTypes() {
+  static std::vector<EventType> event_type_array;
+  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();
+    event_type_array.insert(event_type_array.end(), tracepoint_array.begin(),
+                            tracepoint_array.end());
+  }
   return event_type_array;
 }
 
 const EventType* EventTypeFactory::FindEventTypeByName(const std::string& name,
                                                        bool report_unsupported_type) {
   const EventType* result = nullptr;
-  for (auto& event_type : event_type_array) {
+  for (auto& event_type : GetAllEventTypes()) {
     if (event_type.name == name) {
       result = &event_type;
       break;
@@ -69,7 +109,7 @@ const EventType* EventTypeFactory::FindEventTypeByName(const std::string& name,
 }
 
 const EventType* EventTypeFactory::FindEventTypeByConfig(uint32_t type, uint64_t config) {
-  for (auto& event_type : event_type_array) {
+  for (auto& event_type : GetAllEventTypes()) {
     if (event_type.type == type && event_type.config == config) {
       return &event_type;
     }
index b486a29..341d2c4 100644 (file)
 // the event type is supported by the kernel.
 
 struct EventType {
+  EventType(const std::string& name, uint32_t type, uint64_t config)
+      : name(name), type(type), config(config) {
+  }
+
+  EventType() : type(0), config(0) {
+  }
+
   bool IsSupportedByKernel() const;
 
-  const char* name;
+  std::string name;
   uint32_t type;
   uint64_t config;
 };
 
 class EventTypeFactory {
  public:
-  static const std::vector<const EventType>& GetAllEventTypes();
+  static const std::vector<EventType>& GetAllEventTypes();
   static const EventType* FindEventTypeByName(const std::string& name,
                                               bool report_unsupported_type = true);
   static const EventType* FindEventTypeByConfig(uint32_t type, uint64_t config);
index 46910b9..71cb493 100644 (file)
 
 static std::string RecordTypeToString(int record_type) {
   static std::unordered_map<int, std::string> record_type_names = {
-      {PERF_RECORD_MMAP, "mmap"},
-      {PERF_RECORD_LOST, "lost"},
-      {PERF_RECORD_COMM, "comm"},
-      {PERF_RECORD_EXIT, "exit"},
-      {PERF_RECORD_THROTTLE, "throttle"},
-      {PERF_RECORD_UNTHROTTLE, "unthrottle"},
-      {PERF_RECORD_FORK, "fork"},
-      {PERF_RECORD_READ, "read"},
-      {PERF_RECORD_SAMPLE, "sample"},
-      {PERF_RECORD_BUILD_ID, "build_id"},
+      {PERF_RECORD_MMAP, "mmap"},         {PERF_RECORD_LOST, "lost"},
+      {PERF_RECORD_COMM, "comm"},         {PERF_RECORD_EXIT, "exit"},
+      {PERF_RECORD_THROTTLE, "throttle"}, {PERF_RECORD_UNTHROTTLE, "unthrottle"},
+      {PERF_RECORD_FORK, "fork"},         {PERF_RECORD_READ, "read"},
+      {PERF_RECORD_SAMPLE, "sample"},     {PERF_RECORD_BUILD_ID, "build_id"},
   };
 
   auto it = record_type_names.find(record_type);