Merge "Simpleperf: support event type modifier."
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
index 338ae26..221f3fb 100644
--- a/simpleperf/cmd_list.cpp
+++ b/simpleperf/cmd_list.cpp
@@ -71,7 +71,7 @@
     }
   }
 
-  auto& event_types = EventTypeFactory::GetAllEventTypes();
+  auto& event_types = GetAllEventTypes();
 
   for (auto& name : names) {
     auto it = type_map.find(name);
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index e87a7ec..b59dcdc 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -61,8 +61,12 @@
             "    -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"
+            "    -e event[:modifier]\n"
+            "                 Select the event to sample. Use `simpleperf list` to find\n"
+            "                 all possible event names. Modifiers can be added to define\n"
+            "                 how the event should be monitored. Possible modifiers are:\n"
+            "                   u - monitor user space events only\n"
+            "                   k - monitor kernel space events only\n"
             "    -f freq      Set event sample frequency.\n"
             "    -F freq      Same as '-f freq'.\n"
             "    -g           Enables call-graph recording.\n"
@@ -88,7 +92,6 @@
         system_wide_collection_(false),
         branch_sampling_(0),
         callchain_sampling_(false),
-        measured_event_type_(nullptr),
         perf_mmap_pages_(256),
         record_filename_("perf.data") {
     signaled = false;
@@ -117,8 +120,8 @@
   bool system_wide_collection_;
   std::vector<pid_t> monitored_threads_;
   uint64_t branch_sampling_;
+  std::unique_ptr<EventTypeAndModifier> measured_event_type_modifier_;
   bool callchain_sampling_;
-  const EventType* measured_event_type_;
   EventSelectionSet event_selection_set_;
 
   // mmap pages used by each perf event file, should be power of 2.
@@ -136,7 +139,7 @@
   if (!ParseOptions(args, &workload_args)) {
     return false;
   }
-  if (measured_event_type_ == nullptr) {
+  if (measured_event_type_modifier_ == nullptr) {
     if (!SetMeasuredEventType(default_measured_event_type)) {
       return false;
     }
@@ -182,8 +185,9 @@
 
   // 4. Open record file writer, and dump kernel/modules/threads mmap information.
   record_file_writer_ = RecordFileWriter::CreateInstance(
-      record_filename_, event_selection_set_.FindEventAttrByType(*measured_event_type_),
-      event_selection_set_.FindEventFdsByType(*measured_event_type_));
+      record_filename_,
+      event_selection_set_.FindEventAttrByType(measured_event_type_modifier_->event_type),
+      event_selection_set_.FindEventFdsByType(measured_event_type_modifier_->event_type));
   if (record_file_writer_ == nullptr) {
     return false;
   }
@@ -320,16 +324,16 @@
 }
 
 bool RecordCommand::SetMeasuredEventType(const std::string& event_type_name) {
-  const EventType* event_type = EventTypeFactory::FindEventTypeByName(event_type_name);
-  if (event_type == nullptr) {
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType(event_type_name);
+  if (event_type_modifier == nullptr) {
     return false;
   }
-  measured_event_type_ = event_type;
+  measured_event_type_modifier_ = std::move(event_type_modifier);
   return true;
 }
 
 bool RecordCommand::SetEventSelection() {
-  event_selection_set_.AddEventType(*measured_event_type_);
+  event_selection_set_.AddEventType(*measured_event_type_modifier_);
   if (use_sample_freq_) {
     event_selection_set_.SetSampleFreq(sample_freq_);
   } else {
@@ -355,7 +359,8 @@
   if (!GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps)) {
     return false;
   }
-  const perf_event_attr& attr = event_selection_set_.FindEventAttrByType(*measured_event_type_);
+  const perf_event_attr& attr =
+      event_selection_set_.FindEventAttrByType(measured_event_type_modifier_->event_type);
   MmapRecord mmap_record = CreateMmapRecord(attr, true, UINT_MAX, 0, kernel_mmap.start_addr,
                                             kernel_mmap.len, kernel_mmap.pgoff, kernel_mmap.name);
   if (!record_file_writer_->WriteData(mmap_record.BinaryFormat())) {
@@ -380,7 +385,8 @@
   if (!GetThreadComms(&thread_comms)) {
     return false;
   }
-  const perf_event_attr& attr = event_selection_set_.FindEventAttrByType(*measured_event_type_);
+  const perf_event_attr& attr =
+      event_selection_set_.FindEventAttrByType(measured_event_type_modifier_->event_type);
   for (auto& thread : thread_comms) {
     CommRecord record = CreateCommRecord(attr, thread.tgid, thread.tid, thread.comm);
     if (!record_file_writer_->WriteData(record.BinaryFormat())) {
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index dcc0ad1..a4e2be6 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -103,6 +103,10 @@
   }
 }
 
+TEST(record_cmd, event_modifier) {
+  ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-cycles:u", "sleep", "1"}));
+}
+
 TEST(record_cmd, callchain_sampling) {
   ASSERT_TRUE(RecordCmd()->Run({"-g", "sleep", "1"}));
 }
diff --git a/simpleperf/cmd_report.cpp b/simpleperf/cmd_report.cpp
index 73452ab..cdb7169 100644
--- a/simpleperf/cmd_report.cpp
+++ b/simpleperf/cmd_report.cpp
@@ -312,8 +312,7 @@
 }
 
 void ReportCommand::PrintReportContext() {
-  const EventType* event_type =
-      EventTypeFactory::FindEventTypeByConfig(event_attr_.type, event_attr_.config);
+  const EventType* event_type = FindEventTypeByConfig(event_attr_.type, event_attr_.config);
   std::string event_type_name;
   if (event_type != nullptr) {
     event_type_name = event_type->name;
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index 8feb1a6..cba2873 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -51,8 +51,12 @@
                 "Usage: simpleperf stat [options] [command [command-args]]\n"
                 "    Gather performance counter information of running [command].\n"
                 "    -a           Collect system-wide information.\n"
-                "    -e event1,event2,... Select the event list to count. Use `simpleperf list`\n"
-                "                         to find all possible event names.\n"
+                "    -e event1[:modifier1],event2[:modifier2],...\n"
+                "                 Select the event list to count. Use `simpleperf list` to find\n"
+                "                 all possible event names. Modifiers can be added to define\n"
+                "                 how the event should be monitored. Possible modifiers are:\n"
+                "                   u - monitor user space events only\n"
+                "                   k - monitor kernel space events only\n"
                 "    -p pid1,pid2,...\n"
                 "                 Stat events on existing processes. Mutually exclusive with -a.\n"
                 "    -t tid1,tid2,...\n"
@@ -74,6 +78,7 @@
   bool ShowCounters(const std::map<const EventType*, std::vector<PerfCounter>>& counters_map,
                     std::chrono::steady_clock::duration counting_duration);
 
+  std::vector<std::pair<std::string, EventType>> measured_event_types_;
   EventSelectionSet event_selection_set_;
   bool verbose_mode_;
   bool system_wide_collection_;
@@ -207,12 +212,13 @@
 
 bool StatCommand::AddMeasuredEventType(const std::string& event_type_name,
                                        bool report_unsupported_type) {
-  const EventType* event_type =
-      EventTypeFactory::FindEventTypeByName(event_type_name, report_unsupported_type);
-  if (event_type == nullptr) {
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier =
+      ParseEventType(event_type_name, report_unsupported_type);
+  if (event_type_modifier == nullptr) {
     return false;
   }
-  event_selection_set_.AddEventType(*event_type);
+  measured_event_types_.push_back(std::make_pair(event_type_name, event_type_modifier->event_type));
+  event_selection_set_.AddEventType(*event_type_modifier);
   return true;
 }
 
@@ -263,8 +269,14 @@
                                             sum_counter.time_enabled / sum_counter.time_running);
       }
     }
+    std::string event_type_name;
+    for (auto& pair : measured_event_types_) {
+      if (pair.second.name == event_type->name) {
+        event_type_name = pair.first;
+      }
+    }
     printf("%'30" PRId64 "%s  %s\n", scaled_count, scaled ? "(scaled)" : "       ",
-           event_type->name.c_str());
+           event_type_name.c_str());
   }
   printf("\nTotal test time: %lf seconds.\n",
          std::chrono::duration_cast<std::chrono::duration<double>>(counting_duration).count());
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 36d79da..c6c4ef7 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -21,54 +21,51 @@
 #include "command.h"
 #include "test_util.h"
 
-class StatCommandTest : public ::testing::Test {
- protected:
-  virtual void SetUp() {
-    stat_cmd = CreateCommandInstance("stat");
-    ASSERT_TRUE(stat_cmd != nullptr);
-  }
-
- protected:
-  std::unique_ptr<Command> stat_cmd;
-};
-
-TEST_F(StatCommandTest, no_options) {
-  ASSERT_TRUE(stat_cmd->Run({"sleep", "1"}));
+static std::unique_ptr<Command> StatCmd() {
+  return CreateCommandInstance("stat");
 }
 
-TEST_F(StatCommandTest, event_option) {
-  ASSERT_TRUE(stat_cmd->Run({"-e", "cpu-clock,task-clock", "sleep", "1"}));
+TEST(stat_cmd, no_options) {
+  ASSERT_TRUE(StatCmd()->Run({"sleep", "1"}));
 }
 
-TEST_F(StatCommandTest, system_wide_option) {
-  ASSERT_TRUE(stat_cmd->Run({"-a", "sleep", "1"}));
+TEST(stat_cmd, event_option) {
+  ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-clock,task-clock", "sleep", "1"}));
 }
 
-TEST_F(StatCommandTest, verbose_option) {
-  ASSERT_TRUE(stat_cmd->Run({"--verbose", "sleep", "1"}));
+TEST(stat_cmd, system_wide_option) {
+  ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"}));
 }
 
-TEST_F(StatCommandTest, tracepoint_event) {
-  ASSERT_TRUE(stat_cmd->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
+TEST(stat_cmd, verbose_option) {
+  ASSERT_TRUE(StatCmd()->Run({"--verbose", "sleep", "1"}));
 }
 
-TEST_F(StatCommandTest, existing_processes) {
+TEST(stat_cmd, tracepoint_event) {
+  ASSERT_TRUE(StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
+}
+
+TEST(stat_cmd, event_modifier) {
+  ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-cycles:u,sched:sched_switch:k", "sleep", "1"}));
+}
+
+TEST(stat_cmd, existing_processes) {
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(2, &workloads);
   std::string pid_list =
       android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
-  ASSERT_TRUE(stat_cmd->Run({"-p", pid_list}));
+  ASSERT_TRUE(StatCmd()->Run({"-p", pid_list}));
 }
 
-TEST_F(StatCommandTest, existing_threads) {
+TEST(stat_cmd, existing_threads) {
   std::vector<std::unique_ptr<Workload>> workloads;
   CreateProcesses(2, &workloads);
   // Process id can be used as thread id in linux.
   std::string tid_list =
       android::base::StringPrintf("%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
-  ASSERT_TRUE(stat_cmd->Run({"-t", tid_list}));
+  ASSERT_TRUE(StatCmd()->Run({"-t", tid_list}));
 }
 
-TEST_F(StatCommandTest, no_monitored_threads) {
-  ASSERT_FALSE(stat_cmd->Run({""}));
+TEST(stat_cmd, no_monitored_threads) {
+  ASSERT_FALSE(StatCmd()->Run({""}));
 }
diff --git a/simpleperf/cpu_offline_test.cpp b/simpleperf/cpu_offline_test.cpp
index 608bdf2..723518a 100644
--- a/simpleperf/cpu_offline_test.cpp
+++ b/simpleperf/cpu_offline_test.cpp
@@ -25,11 +25,11 @@
 #include "event_type.h"
 
 static std::unique_ptr<EventFd> OpenHardwareEventOnCpu0() {
-  const EventType* event_type = EventTypeFactory::FindEventTypeByName("cpu-cycles");
-  if (event_type == nullptr) {
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
+  if (event_type_modifier == nullptr) {
     return nullptr;
   }
-  perf_event_attr attr = CreateDefaultPerfEventAttr(*event_type);
+  perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
   return EventFd::OpenEventFile(attr, getpid(), 0);
 }
 
diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp
index 5e02215..c9947c9 100644
--- a/simpleperf/event_attr.cpp
+++ b/simpleperf/event_attr.cpp
@@ -95,7 +95,7 @@
 
 void DumpPerfEventAttr(const perf_event_attr& attr, size_t indent) {
   std::string event_name = "unknown";
-  const EventType* event_type = EventTypeFactory::FindEventTypeByConfig(attr.type, attr.config);
+  const EventType* event_type = FindEventTypeByConfig(attr.type, attr.config);
   if (event_type != nullptr) {
     event_name = event_type->name;
   }
@@ -116,21 +116,21 @@
   PrintIndented(indent + 1, "read_format (0x%llx) %s\n", attr.read_format,
                 ReadFormatToString(attr.read_format).c_str());
 
-  PrintIndented(indent + 1, "disabled %llu, inherit %llu, pinned %llu, exclusive %llu\n",
+  PrintIndented(indent + 1, "disabled %u, inherit %u, pinned %u, exclusive %u\n",
                 attr.disabled, attr.inherit, attr.pinned, attr.exclusive);
 
-  PrintIndented(indent + 1, "exclude_user %llu, exclude_kernel %llu, exclude_hv %llu\n",
+  PrintIndented(indent + 1, "exclude_user %u, exclude_kernel %u, exclude_hv %u\n",
                 attr.exclude_user, attr.exclude_kernel, attr.exclude_hv);
 
-  PrintIndented(indent + 1, "exclude_idle %llu, mmap %llu, comm %llu, freq %llu\n",
+  PrintIndented(indent + 1, "exclude_idle %u, mmap %u, comm %u, freq %u\n",
                 attr.exclude_idle, attr.mmap, attr.comm, attr.freq);
 
-  PrintIndented(indent + 1, "inherit_stat %llu, enable_on_exec %llu, task %llu\n",
+  PrintIndented(indent + 1, "inherit_stat %u, enable_on_exec %u, task %u\n",
                 attr.inherit_stat, attr.enable_on_exec, attr.task);
 
-  PrintIndented(indent + 1, "watermark %llu, precise_ip %llu, mmap_data %llu\n", attr.watermark,
+  PrintIndented(indent + 1, "watermark %u, precise_ip %u, mmap_data %u\n", attr.watermark,
                 attr.precise_ip, attr.mmap_data);
 
-  PrintIndented(indent + 1, "sample_id_all %llu, exclude_host %llu, exclude_guest %llu\n",
+  PrintIndented(indent + 1, "sample_id_all %u, exclude_host %u, exclude_guest %u\n",
                 attr.sample_id_all, attr.exclude_host, attr.exclude_guest);
 }
diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp
index 9b06e50..547be29 100644
--- a/simpleperf/event_fd.cpp
+++ b/simpleperf/event_fd.cpp
@@ -42,8 +42,7 @@
                                                 bool report_error) {
   perf_event_attr perf_attr = attr;
   std::string event_name = "unknown event";
-  const EventType* event_type =
-      EventTypeFactory::FindEventTypeByConfig(perf_attr.type, perf_attr.config);
+  const EventType* event_type = FindEventTypeByConfig(perf_attr.type, perf_attr.config);
   if (event_type != nullptr) {
     event_name = event_type->name;
   }
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index f03286b..e89a19f 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -23,21 +23,27 @@
 #include "event_type.h"
 
 bool IsBranchSamplingSupported() {
-  const EventType* event_type = EventTypeFactory::FindEventTypeByName("cpu-cycles", false);
-  if (event_type == nullptr) {
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles", false);
+  if (event_type_modifier == nullptr) {
     return false;
   }
-  perf_event_attr attr = CreateDefaultPerfEventAttr(*event_type);
+  perf_event_attr attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
   attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
   attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
   auto event_fd = EventFd::OpenEventFile(attr, getpid(), -1, false);
   return event_fd != nullptr;
 }
 
-void EventSelectionSet::AddEventType(const EventType& event_type) {
+void EventSelectionSet::AddEventType(const EventTypeAndModifier& event_type_modifier) {
   EventSelection selection;
-  selection.event_type = &event_type;
-  selection.event_attr = CreateDefaultPerfEventAttr(event_type);
+  selection.event_type = event_type_modifier.event_type;
+  selection.event_attr = CreateDefaultPerfEventAttr(event_type_modifier.event_type);
+  selection.event_attr.exclude_user = event_type_modifier.exclude_user;
+  selection.event_attr.exclude_kernel = event_type_modifier.exclude_kernel;
+  selection.event_attr.exclude_hv = event_type_modifier.exclude_hv;
+  selection.event_attr.exclude_host = event_type_modifier.exclude_host;
+  selection.event_attr.exclude_guest = event_type_modifier.exclude_guest;
+  selection.event_attr.precise_ip = event_type_modifier.precise_ip;
   selections_.push_back(std::move(selection));
 }
 
@@ -122,7 +128,7 @@
     // 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()) {
-      PLOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type->name
+      PLOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type.name
                   << " on all cpus";
       return false;
     }
@@ -165,7 +171,7 @@
       }
       counters.push_back(counter);
     }
-    counters_map->insert(std::make_pair(selection.event_type, counters));
+    counters_map->insert(std::make_pair(&selection.event_type, counters));
   }
   return true;
 }
@@ -241,7 +247,7 @@
 EventSelectionSet::EventSelection* EventSelectionSet::FindSelectionByType(
     const EventType& event_type) {
   for (auto& selection : selections_) {
-    if (selection.event_type->name == event_type.name) {
+    if (selection.event_type.name == event_type.name) {
       return &selection;
     }
   }
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index 2e5b50c..6f7f8f4 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -25,10 +25,9 @@
 #include <base/macros.h>
 
 #include "event_fd.h"
+#include "event_type.h"
 #include "perf_event.h"
 
-struct EventType;
-
 // EventSelectionSet helps to monitor events.
 // Firstly, the user creates an EventSelectionSet, and adds the specific event types to monitor.
 // Secondly, the user defines how to monitor the events (by setting enable_on_exec flag,
@@ -47,7 +46,7 @@
     return selections_.empty();
   }
 
-  void AddEventType(const EventType& event_type);
+  void AddEventType(const EventTypeAndModifier& event_type_modifier);
 
   void SetEnableOnExec(bool enable);
   bool GetEnableOnExec();
@@ -71,7 +70,7 @@
 
  private:
   struct EventSelection {
-    const EventType* event_type;
+    EventType event_type;
     perf_event_attr event_attr;
     std::vector<std::unique_ptr<EventFd>> event_fds;
   };
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp
index 2b2e5b4..56a17b8 100644
--- a/simpleperf/event_type.cpp
+++ b/simpleperf/event_type.cpp
@@ -75,7 +75,7 @@
   return result;
 }
 
-const std::vector<EventType>& EventTypeFactory::GetAllEventTypes() {
+const std::vector<EventType>& 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(),
@@ -87,8 +87,16 @@
   return event_type_array;
 }
 
-const EventType* EventTypeFactory::FindEventTypeByName(const std::string& name,
-                                                       bool report_unsupported_type) {
+const EventType* FindEventTypeByConfig(uint32_t type, uint64_t config) {
+  for (auto& event_type : GetAllEventTypes()) {
+    if (event_type.type == type && event_type.config == config) {
+      return &event_type;
+    }
+  }
+  return nullptr;
+}
+
+static const EventType* FindEventTypeByName(const std::string& name, bool report_unsupported_type) {
   const EventType* result = nullptr;
   for (auto& event_type : GetAllEventTypes()) {
     if (event_type.name == name) {
@@ -109,11 +117,75 @@
   return result;
 }
 
-const EventType* EventTypeFactory::FindEventTypeByConfig(uint32_t type, uint64_t config) {
-  for (auto& event_type : GetAllEventTypes()) {
-    if (event_type.type == type && event_type.config == config) {
-      return &event_type;
+std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_type_str,
+                                                     bool report_unsupported_type) {
+  static std::string modifier_characters = "ukhGHp";
+  std::unique_ptr<EventTypeAndModifier> event_type_modifier(new EventTypeAndModifier);
+  std::string name = event_type_str;
+  std::string modifier;
+  size_t comm_pos = event_type_str.rfind(':');
+  if (comm_pos != std::string::npos) {
+    bool match_modifier = true;
+    for (size_t i = comm_pos + 1; i < event_type_str.size(); ++i) {
+      char c = event_type_str[i];
+      if (c != ' ' && modifier_characters.find(c) == std::string::npos) {
+        match_modifier = false;
+        break;
+      }
+    }
+    if (match_modifier) {
+      name = event_type_str.substr(0, comm_pos);
+      modifier = event_type_str.substr(comm_pos + 1);
     }
   }
-  return nullptr;
+  const EventType* event_type = FindEventTypeByName(name, report_unsupported_type);
+  if (event_type == nullptr) {
+    // Try if the modifier belongs to the event type name, like some tracepoint events.
+    if (!modifier.empty()) {
+      name = event_type_str;
+      modifier.clear();
+      event_type = FindEventTypeByName(name, report_unsupported_type);
+    }
+    if (event_type == nullptr) {
+      return nullptr;
+    }
+  }
+  event_type_modifier->event_type = *event_type;
+  if (modifier.find_first_of("ukh") != std::string::npos) {
+    event_type_modifier->exclude_user = true;
+    event_type_modifier->exclude_kernel = true;
+    event_type_modifier->exclude_hv = true;
+  }
+  if (modifier.find_first_of("GH") != std::string::npos) {
+    event_type_modifier->exclude_guest = true;
+    event_type_modifier->exclude_host = true;
+  }
+
+  for (auto& c : modifier) {
+    switch (c) {
+      case 'u':
+        event_type_modifier->exclude_user = false;
+        break;
+      case 'k':
+        event_type_modifier->exclude_kernel = false;
+        break;
+      case 'h':
+        event_type_modifier->exclude_hv = false;
+        break;
+      case 'G':
+        event_type_modifier->exclude_guest = false;
+        break;
+      case 'H':
+        event_type_modifier->exclude_host = false;
+        break;
+      case 'p':
+        event_type_modifier->precise_ip++;
+        break;
+      case ' ':
+        break;
+      default:
+        LOG(ERROR) << "Unknown event type modifier '" << c << "'";
+    }
+  }
+  return event_type_modifier;
 }
diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h
index 341d2c4..9c365fa 100644
--- a/simpleperf/event_type.h
+++ b/simpleperf/event_type.h
@@ -18,6 +18,7 @@
 #define SIMPLE_PERF_EVENT_H_
 
 #include <stdint.h>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -41,12 +42,29 @@
   uint64_t config;
 };
 
-class EventTypeFactory {
- public:
-  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);
+const std::vector<EventType>& GetAllEventTypes();
+const EventType* FindEventTypeByConfig(uint32_t type, uint64_t config);
+
+struct EventTypeAndModifier {
+  EventType event_type;
+  bool exclude_user;
+  bool exclude_kernel;
+  bool exclude_hv;
+  bool exclude_host;
+  bool exclude_guest;
+  int precise_ip : 2;
+
+  EventTypeAndModifier()
+      : exclude_user(false),
+        exclude_kernel(false),
+        exclude_hv(false),
+        exclude_host(false),
+        exclude_guest(false),
+        precise_ip(0) {
+  }
 };
 
+std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_type_str,
+                                                     bool report_unsupported_type = true);
+
 #endif  // SIMPLE_PERF_EVENT_H_
diff --git a/simpleperf/record_file_test.cpp b/simpleperf/record_file_test.cpp
index 2d59511..c4bb255 100644
--- a/simpleperf/record_file_test.cpp
+++ b/simpleperf/record_file_test.cpp
@@ -32,9 +32,9 @@
  protected:
   virtual void SetUp() {
     filename = "temporary.record_file";
-    const EventType* event_type = EventTypeFactory::FindEventTypeByName("cpu-cycles");
-    ASSERT_TRUE(event_type != nullptr);
-    event_attr = CreateDefaultPerfEventAttr(*event_type);
+    std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
+    ASSERT_TRUE(event_type_modifier != nullptr);
+    event_attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
     event_attr.sample_id_all = 1;
     event_attr.sample_type |= PERF_SAMPLE_TIME;
     std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(event_attr, getpid(), -1);
diff --git a/simpleperf/record_test.cpp b/simpleperf/record_test.cpp
index d9e9a4b..a15972b 100644
--- a/simpleperf/record_test.cpp
+++ b/simpleperf/record_test.cpp
@@ -24,9 +24,9 @@
 class RecordTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
-    const EventType* event_type = EventTypeFactory::FindEventTypeByName("cpu-cycles");
-    ASSERT_TRUE(event_type != nullptr);
-    event_attr = CreateDefaultPerfEventAttr(*event_type);
+    std::unique_ptr<EventTypeAndModifier> event_type_modifier = ParseEventType("cpu-cycles");
+    ASSERT_TRUE(event_type_modifier != nullptr);
+    event_attr = CreateDefaultPerfEventAttr(event_type_modifier->event_type);
   }
 
   template <class RecordType>