traced_probes: Auto discovery of vendor tracepoints

Change-Id: Idc0aa1e2fc0298e38858ea6815f71bbc0f49c26b
diff --git a/Android.bp b/Android.bp
index fd0c8c8..12a9318 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6445,6 +6445,7 @@
     "src/traced/probes/ftrace/compact_sched.cc",
     "src/traced/probes/ftrace/cpu_reader.cc",
     "src/traced/probes/ftrace/cpu_stats_parser.cc",
+    "src/traced/probes/ftrace/discover_vendor_tracepoints.cc",
     "src/traced/probes/ftrace/event_info.cc",
     "src/traced/probes/ftrace/event_info_constants.cc",
     "src/traced/probes/ftrace/ftrace_config_muxer.cc",
@@ -6601,6 +6602,7 @@
   srcs: [
     "src/traced/probes/ftrace/cpu_reader_unittest.cc",
     "src/traced/probes/ftrace/cpu_stats_parser_unittest.cc",
+    "src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc",
     "src/traced/probes/ftrace/event_info_unittest.cc",
     "src/traced/probes/ftrace/format_parser_unittest.cc",
     "src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc",
diff --git a/BUILD b/BUILD
index 427b2c1..8cb412c 100644
--- a/BUILD
+++ b/BUILD
@@ -1059,6 +1059,8 @@
         "src/traced/probes/ftrace/cpu_reader.h",
         "src/traced/probes/ftrace/cpu_stats_parser.cc",
         "src/traced/probes/ftrace/cpu_stats_parser.h",
+        "src/traced/probes/ftrace/discover_vendor_tracepoints.cc",
+        "src/traced/probes/ftrace/discover_vendor_tracepoints.h",
         "src/traced/probes/ftrace/event_info.cc",
         "src/traced/probes/ftrace/event_info.h",
         "src/traced/probes/ftrace/event_info_constants.cc",
diff --git a/src/android_internal/atrace_hal.cc b/src/android_internal/atrace_hal.cc
index 67e445d..486a44a 100644
--- a/src/android_internal/atrace_hal.cc
+++ b/src/android_internal/atrace_hal.cc
@@ -22,10 +22,11 @@
 namespace perfetto {
 namespace android_internal {
 
-using android::hardware::atrace::V1_0::IAtraceDevice;
-using android::hardware::atrace::V1_0::TracingCategory;
+using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 using android::hardware::Return;
+using android::hardware::atrace::V1_0::IAtraceDevice;
+using android::hardware::atrace::V1_0::TracingCategory;
 
 namespace {
 
@@ -40,7 +41,7 @@
 
 }  // namespace
 
-bool GetCategories(TracingVendorCategory* categories, size_t* size_of_arr) {
+bool ListCategories(TracingVendorCategory* categories, size_t* size_of_arr) {
   const size_t in_array_size = *size_of_arr;
   *size_of_arr = 0;
   if (!GetService())
@@ -64,5 +65,30 @@
   return true;
 }
 
+bool EnableCategories(const char** categories, size_t categories_count) {
+  if (!GetService())
+    return false;
+  std::vector<hidl_string> args;
+  args.resize(categories_count);
+  for (size_t i = 0; i < categories_count; ++i) {
+    args[i] = categories[i];
+  }
+  g_atraceHal->enableCategories(args);
+  // TODO(hjd): Check status.
+  return true;
+}
+
+bool DisableAllCategories() {
+  if (!GetService())
+    return false;
+  g_atraceHal->disableAllCategories();
+  // TODO(hjd): Check status.
+  return true;
+}
+
+void ForgetService() {
+  g_atraceHal = nullptr;
+}
+
 }  // namespace android_internal
 }  // namespace perfetto
diff --git a/src/android_internal/atrace_hal.h b/src/android_internal/atrace_hal.h
index 18d099d..782f800 100644
--- a/src/android_internal/atrace_hal.h
+++ b/src/android_internal/atrace_hal.h
@@ -42,7 +42,14 @@
 // These functions are not thread safe unless specified otherwise.
 
 bool __attribute__((visibility("default")))
-GetCategories(TracingVendorCategory*, size_t* size_of_arr);
+ListCategories(TracingVendorCategory*, size_t* size_of_arr);
+
+bool __attribute__((visibility("default")))
+EnableCategories(const char** categories, size_t categories_count);
+
+bool __attribute__((visibility("default"))) DisableAllCategories();
+
+void __attribute__((visibility("default"))) ForgetService();
 
 }  // extern "C"
 
diff --git a/src/traced/probes/ftrace/BUILD.gn b/src/traced/probes/ftrace/BUILD.gn
index 5d9cd70..0ffd645 100644
--- a/src/traced/probes/ftrace/BUILD.gn
+++ b/src/traced/probes/ftrace/BUILD.gn
@@ -60,6 +60,7 @@
   sources = [
     "cpu_reader_unittest.cc",
     "cpu_stats_parser_unittest.cc",
+    "discover_vendor_tracepoints_unittest.cc",
     "event_info_unittest.cc",
     "format_parser_unittest.cc",
     "ftrace_config_muxer_unittest.cc",
@@ -122,6 +123,8 @@
     "cpu_reader.h",
     "cpu_stats_parser.cc",
     "cpu_stats_parser.h",
+    "discover_vendor_tracepoints.cc",
+    "discover_vendor_tracepoints.h",
     "event_info.cc",
     "event_info.h",
     "event_info_constants.cc",
diff --git a/src/traced/probes/ftrace/atrace_hal_wrapper.cc b/src/traced/probes/ftrace/atrace_hal_wrapper.cc
index 11cdd89..48cf8f2 100644
--- a/src/traced/probes/ftrace/atrace_hal_wrapper.cc
+++ b/src/traced/probes/ftrace/atrace_hal_wrapper.cc
@@ -13,8 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
 
+#include "perfetto/base/build_config.h"
 #include "src/android_internal/atrace_hal.h"
 #include "src/android_internal/lazy_library_loader.h"
 
@@ -25,38 +27,83 @@
 }
 
 struct AtraceHalWrapper::DynamicLibLoader {
-  PERFETTO_LAZY_LOAD(android_internal::GetCategories, get_categories_);
+  PERFETTO_LAZY_LOAD(android_internal::ForgetService, forget_service_);
+  PERFETTO_LAZY_LOAD(android_internal::ListCategories, list_categories_);
+  PERFETTO_LAZY_LOAD(android_internal::EnableCategories, enable_categories_);
+  PERFETTO_LAZY_LOAD(android_internal::DisableAllCategories,
+                     disable_all_categories_);
 
-  std::vector<android_internal::TracingVendorCategory> GetCategories() {
-    if (!get_categories_)
-      return std::vector<android_internal::TracingVendorCategory>();
+  std::vector<std::string> ListCategories() {
+    std::vector<std::string> results;
+    if (!list_categories_)
+      return results;
 
     std::vector<android_internal::TracingVendorCategory> categories(
         kMaxNumCategories);
     size_t num_cat = categories.size();
-    get_categories_(&categories[0], &num_cat);
+    bool success = list_categories_(&categories[0], &num_cat);
+    if (!success)
+      return results;
     categories.resize(num_cat);
-    return categories;
+
+    for (const auto& category : categories) {
+      results.push_back(category.name);
+    }
+
+    return results;
+  }
+
+  bool EnableCategories(const std::vector<std::string>& categories) {
+    if (!enable_categories_)
+      return false;
+    std::vector<const char*> args;
+    for (const std::string& category : categories) {
+      args.push_back(category.c_str());
+    }
+    return enable_categories_(&args[0], args.size());
+  }
+
+  bool DisableAllCategories() {
+    if (!disable_all_categories_)
+      return false;
+    return disable_all_categories_();
+  }
+
+  void ForgetService() {
+    if (!forget_service_)
+      return;
+    forget_service_();
   }
 };
 
 AtraceHalWrapper::AtraceHalWrapper() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
   lib_.reset(new DynamicLibLoader());
+#endif
 }
 
-AtraceHalWrapper::~AtraceHalWrapper() = default;
+AtraceHalWrapper::~AtraceHalWrapper() {
+  if (lib_)
+    lib_->ForgetService();
+};
 
-std::vector<AtraceHalWrapper::TracingVendorCategory>
-AtraceHalWrapper::GetAvailableCategories() {
-  auto details = lib_->GetCategories();
-  std::vector<AtraceHalWrapper::TracingVendorCategory> result;
-  for (size_t i = 0; i < details.size(); i++) {
-    AtraceHalWrapper::TracingVendorCategory cat;
-    cat.name = details[i].name;
-    cat.description = details[i].description;
-    result.emplace_back(cat);
-  }
-  return result;
+std::vector<std::string> AtraceHalWrapper::ListCategories() {
+  if (!lib_)
+    return {};
+  return lib_->ListCategories();
+}
+
+bool AtraceHalWrapper::EnableCategories(
+    const std::vector<std::string>& categories) {
+  if (!lib_)
+    return true;
+  return lib_->EnableCategories(categories);
+}
+
+bool AtraceHalWrapper::DisableAllCategories() {
+  if (!lib_)
+    return true;
+  return lib_->DisableAllCategories();
 }
 
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/atrace_hal_wrapper.h b/src/traced/probes/ftrace/atrace_hal_wrapper.h
index db215ae..6f95fdf 100644
--- a/src/traced/probes/ftrace/atrace_hal_wrapper.h
+++ b/src/traced/probes/ftrace/atrace_hal_wrapper.h
@@ -28,17 +28,11 @@
 class AtraceHalWrapper {
  public:
   AtraceHalWrapper();
-  ~AtraceHalWrapper();
+  virtual ~AtraceHalWrapper();
 
-  struct TracingVendorCategory {
-    // The name identifying the category.
-    std::string name;
-
-    // A longer description of the category.
-    std::string description;
-  };
-
-  std::vector<TracingVendorCategory> GetAvailableCategories();
+  virtual std::vector<std::string> ListCategories();
+  virtual bool EnableCategories(const std::vector<std::string>& categories);
+  virtual bool DisableAllCategories();
 
  private:
   struct DynamicLibLoader;
diff --git a/src/traced/probes/ftrace/discover_vendor_tracepoints.cc b/src/traced/probes/ftrace/discover_vendor_tracepoints.cc
new file mode 100644
index 0000000..68625c0
--- /dev/null
+++ b/src/traced/probes/ftrace/discover_vendor_tracepoints.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/traced/probes/ftrace/discover_vendor_tracepoints.h"
+
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/traced/probes/ftrace/atrace_wrapper.h"
+
+namespace perfetto {
+namespace vendor_tracepoints {
+
+std::vector<GroupAndName> DiscoverTracepoints(AtraceHalWrapper* hal,
+                                              FtraceProcfs* ftrace,
+                                              const std::string& category) {
+  ftrace->DisableAllEvents();
+  hal->EnableCategories({category});
+
+  std::vector<GroupAndName> events;
+  for (const std::string& group_name : ftrace->ReadEnabledEvents()) {
+    size_t pos = group_name.find('/');
+    PERFETTO_CHECK(pos != std::string::npos);
+    events.push_back(
+        GroupAndName(group_name.substr(0, pos), group_name.substr(pos + 1)));
+  }
+
+  hal->DisableAllCategories();
+  ftrace->DisableAllEvents();
+  return events;
+}
+
+std::map<std::string, std::vector<GroupAndName>> DiscoverVendorTracepoints(
+    AtraceHalWrapper* hal,
+    FtraceProcfs* ftrace) {
+  std::map<std::string, std::vector<GroupAndName>> results;
+  for (const auto& category : hal->ListCategories()) {
+    results.emplace(category, DiscoverTracepoints(hal, ftrace, category));
+  }
+  return results;
+}
+
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/discover_vendor_tracepoints.h b/src/traced/probes/ftrace/discover_vendor_tracepoints.h
new file mode 100644
index 0000000..f5d791b
--- /dev/null
+++ b/src/traced/probes/ftrace/discover_vendor_tracepoints.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACED_PROBES_FTRACE_DISCOVER_VENDOR_TRACEPOINTS_H_
+#define SRC_TRACED_PROBES_FTRACE_DISCOVER_VENDOR_TRACEPOINTS_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
+#include "src/traced/probes/ftrace/ftrace_procfs.h"
+#include "src/traced/probes/ftrace/proto_translation_table.h"
+
+namespace perfetto {
+namespace vendor_tracepoints {
+
+// Exposed for testing.
+std::vector<GroupAndName> DiscoverTracepoints(AtraceHalWrapper* hal,
+                                              FtraceProcfs* ftrace,
+                                              const std::string& category);
+
+// Returns a map from vendor category to events we should enable
+std::map<std::string, std::vector<GroupAndName>> DiscoverVendorTracepoints(
+    AtraceHalWrapper* hal,
+    FtraceProcfs* ftrace);
+
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_FTRACE_DISCOVER_VENDOR_TRACEPOINTS_H_
diff --git a/src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc b/src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc
new file mode 100644
index 0000000..a859c15
--- /dev/null
+++ b/src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/traced/probes/ftrace/discover_vendor_tracepoints.h"
+
+#include <vector>
+
+#include "test/gtest_and_gmock.h"
+
+#include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
+#include "src/traced/probes/ftrace/atrace_wrapper.h"
+#include "src/traced/probes/ftrace/ftrace_procfs.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::ElementsAre;
+using testing::NiceMock;
+using testing::Return;
+using testing::Sequence;
+
+namespace perfetto {
+namespace vendor_tracepoints {
+namespace {
+
+class MockHal : public AtraceHalWrapper {
+ public:
+  MockHal() : AtraceHalWrapper() {}
+  MOCK_METHOD0(ListCategories, std::vector<std::string>());
+  MOCK_METHOD1(EnableCategories, bool(const std::vector<std::string>&));
+  MOCK_METHOD0(DisableAllCategories, bool());
+};
+
+class MockFtraceProcfs : public FtraceProcfs {
+ public:
+  MockFtraceProcfs() : FtraceProcfs("/root/") {
+    ON_CALL(*this, NumberOfCpus()).WillByDefault(Return(1));
+    ON_CALL(*this, WriteToFile(_, _)).WillByDefault(Return(true));
+    ON_CALL(*this, ClearFile(_)).WillByDefault(Return(true));
+    EXPECT_CALL(*this, NumberOfCpus()).Times(AnyNumber());
+  }
+
+  MOCK_METHOD2(WriteToFile,
+               bool(const std::string& path, const std::string& str));
+  MOCK_METHOD2(AppendToFile,
+               bool(const std::string& path, const std::string& str));
+  MOCK_METHOD1(ReadOneCharFromFile, char(const std::string& path));
+  MOCK_METHOD1(ClearFile, bool(const std::string& path));
+  MOCK_CONST_METHOD1(ReadFileIntoString, std::string(const std::string& path));
+  MOCK_METHOD0(ReadEnabledEvents, std::vector<std::string>());
+  MOCK_CONST_METHOD0(NumberOfCpus, size_t());
+  MOCK_CONST_METHOD1(GetEventNamesForGroup,
+                     const std::set<std::string>(const std::string& path));
+};
+
+TEST(DiscoverVendorTracepointsTest, DiscoverTracepointsTest) {
+  MockHal hal;
+  MockFtraceProcfs ftrace;
+  Sequence s;
+
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"))
+      .InSequence(s)
+      .WillOnce(Return(true));
+  EXPECT_CALL(hal, EnableCategories(ElementsAre("gfx")))
+      .InSequence(s)
+      .WillOnce(Return(true));
+  EXPECT_CALL(ftrace, ReadEnabledEvents())
+      .InSequence(s)
+      .WillOnce(Return(std::vector<std::string>({"foo/bar", "a/b"})));
+  EXPECT_CALL(hal, DisableAllCategories()).InSequence(s).WillOnce(Return(true));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"))
+      .InSequence(s)
+      .WillOnce(Return(true));
+
+  EXPECT_THAT(DiscoverTracepoints(&hal, &ftrace, "gfx"),
+              ElementsAre(GroupAndName("foo", "bar"), GroupAndName("a", "b")));
+}
+
+}  // namespace
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index 2907e95..596c5c9 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -152,6 +152,7 @@
         events.insert(GroupAndName("mdss", "mdp_sspp_change"));
         events.insert(GroupAndName("mdss", "mdp_sspp_set"));
         AddEventGroup(table, "mali_systrace", &events);
+
         AddEventGroup(table, "sde", &events);
         events.insert(GroupAndName("sde", "tracing_mark_write"));
         events.insert(GroupAndName("sde", "sde_perf_update_bus"));
@@ -417,9 +418,15 @@
   return pages;
 }
 
-FtraceConfigMuxer::FtraceConfigMuxer(FtraceProcfs* ftrace,
-                                     ProtoTranslationTable* table)
-    : ftrace_(ftrace), table_(table), current_state_(), ds_configs_() {}
+FtraceConfigMuxer::FtraceConfigMuxer(
+    FtraceProcfs* ftrace,
+    ProtoTranslationTable* table,
+    std::map<std::string, std::vector<GroupAndName>> vendor_events)
+    : ftrace_(ftrace),
+      table_(table),
+      current_state_(),
+      ds_configs_(),
+      vendor_events_(vendor_events) {}
 FtraceConfigMuxer::~FtraceConfigMuxer() = default;
 
 FtraceConfigId FtraceConfigMuxer::SetupConfig(const FtraceConfig& request) {
@@ -450,6 +457,18 @@
 
   std::set<GroupAndName> events = GetFtraceEvents(request, table_);
 
+  // Vendors can provide a set of extra ftrace categories to be enabled when a
+  // specific atrace category is used (e.g. "gfx" -> ["my_hw/my_custom_event",
+  // "my_hw/my_special_gpu"]). Merge them with the hard coded events for each
+  // categories.
+  for (const std::string& category : request.atrace_categories()) {
+    if (vendor_events_.count(category)) {
+      for (const GroupAndName& event : vendor_events_[category]) {
+        events.insert(event);
+      }
+    }
+  }
+
   if (RequiresAtrace(request))
     UpdateAtrace(request);
 
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.h b/src/traced/probes/ftrace/ftrace_config_muxer.h
index 16883c4..4c4ab02 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.h
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.h
@@ -68,7 +68,10 @@
  public:
   // The FtraceConfigMuxer and ProtoTranslationTable
   // should outlive this instance.
-  FtraceConfigMuxer(FtraceProcfs* ftrace, ProtoTranslationTable* table);
+  FtraceConfigMuxer(
+      FtraceProcfs* ftrace,
+      ProtoTranslationTable* table,
+      std::map<std::string, std::vector<GroupAndName>> vendor_events);
   virtual ~FtraceConfigMuxer();
 
   // Ask FtraceConfigMuxer to adjust ftrace procfs settings to
@@ -154,6 +157,8 @@
   // sizes and events, but don't enable ftrace (i.e. tracing_on).
   std::map<FtraceConfigId, FtraceDataSourceConfig> ds_configs_;
 
+  std::map<std::string, std::vector<GroupAndName>> vendor_events_;
+
   // Subset of |ds_configs_| that are currently active. At any time ftrace is
   // enabled iff |active_configs_| is not empty.
   std::set<FtraceConfigId> active_configs_;
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index 0b069c2..d9b5911 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -197,7 +197,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"power/cpu_frequency"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get());
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
 
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
@@ -244,7 +244,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"group_one/foo", "group_two/foo"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get());
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
 
   static constexpr int kEventId1 = 1;
   Event event1;
@@ -298,7 +298,7 @@
   EXPECT_CALL(ftrace,
               WriteToFile("/root/events/sched/sched_new_event/enable", "1"));
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get());
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
   std::set<std::string> n = {"sched_switch", "sched_new_event"};
   ON_CALL(ftrace, GetEventNamesForGroup("events/sched"))
       .WillByDefault(Return(n));
@@ -346,7 +346,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"group_one/*", "group_two/*"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get());
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
 
   std::set<std::string> event_names = {"foo"};
   ON_CALL(ftrace, GetEventNamesForGroup("events/group_one"))
@@ -395,7 +395,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"sched_switch", "foo"});
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
@@ -445,7 +445,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   // If someone is using ftrace already don't stomp on what they are doing.
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
@@ -461,7 +461,7 @@
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
   *config.add_atrace_categories() = "sched";
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
       .WillOnce(Return('0'));
@@ -501,7 +501,7 @@
   *config.add_atrace_apps() = "com.google.android.gms.persistent";
   *config.add_atrace_apps() = "com.google.android.gms";
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
       .WillOnce(Return('0'));
@@ -542,7 +542,7 @@
   *config_c.add_atrace_apps() = "app_c";
   *config_c.add_atrace_categories() = "cat_c";
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
                                                   "--only_userspace", "cat_a",
@@ -606,7 +606,7 @@
   *config_c.add_atrace_categories() = "cat_1";
   *config_c.add_atrace_categories() = "cat_3";
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(
       atrace,
@@ -661,7 +661,7 @@
   *config_b.add_atrace_apps() = "app_1";
   *config_b.add_atrace_categories() = "cat_1";
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
                                                   "--only_userspace", "cat_1",
@@ -695,7 +695,7 @@
   FtraceConfig config_d = CreateFtraceConfig({"sched/sched_cpu_hotplug"});
   *config_d.add_atrace_categories() = "d";
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   FtraceConfigId id_a = model.SetupConfig(config_a);
   ASSERT_TRUE(id_a);
@@ -735,7 +735,7 @@
   MockFtraceProcfs ftrace;
   FtraceConfig config;
 
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .Times(AnyNumber());
@@ -761,7 +761,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEvents) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
   std::set<GroupAndName> events =
@@ -773,7 +773,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtrace) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   FtraceConfig config = CreateFtraceConfig({});
   *config.add_atrace_categories() = "sched";
@@ -787,7 +787,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtraceCategories) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   FtraceConfig config = CreateFtraceConfig({});
   *config.add_atrace_categories() = "sched";
@@ -811,7 +811,7 @@
   MockFtraceProcfs ftrace;
   FtraceConfig config =
       CreateFtraceConfig({"sched/sched_switch", "cgroup/cgroup_mkdir"});
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
@@ -872,7 +872,7 @@
 
   NiceMock<MockFtraceProcfs> ftrace;
   table_ = CreateFakeTable(valid_compact_format);
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   // First data source - request compact encoding.
   FtraceConfig config_enabled = CreateFtraceConfig({"sched/sched_switch"});
@@ -903,7 +903,7 @@
 
 TEST_F(FtraceConfigMuxerTest, CompactSchedConfigWithInvalidFormat) {
   NiceMock<MockFtraceProcfs> ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get());
+  FtraceConfigMuxer model(&ftrace, table_.get(), {});
 
   // Request compact encoding.
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index a66ed05..c5a6851 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -34,8 +34,10 @@
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
+#include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
 #include "src/traced/probes/ftrace/cpu_reader.h"
 #include "src/traced/probes/ftrace/cpu_stats_parser.h"
+#include "src/traced/probes/ftrace/discover_vendor_tracepoints.h"
 #include "src/traced/probes/ftrace/event_info.h"
 #include "src/traced/probes/ftrace/ftrace_config_muxer.h"
 #include "src/traced/probes/ftrace/ftrace_data_source.h"
@@ -136,8 +138,12 @@
   if (!table)
     return nullptr;
 
+  AtraceHalWrapper hal;
+  auto vendor_evts =
+      vendor_tracepoints::DiscoverVendorTracepoints(&hal, ftrace_procfs.get());
+
   std::unique_ptr<FtraceConfigMuxer> model = std::unique_ptr<FtraceConfigMuxer>(
-      new FtraceConfigMuxer(ftrace_procfs.get(), table.get()));
+      new FtraceConfigMuxer(ftrace_procfs.get(), table.get(), vendor_evts));
   return std::unique_ptr<FtraceController>(
       new FtraceController(std::move(ftrace_procfs), std::move(table),
                            std::move(model), runner, observer));
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index 1f40c83..dca24c8 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -93,7 +93,7 @@
 std::unique_ptr<FtraceConfigMuxer> FakeModel(FtraceProcfs* ftrace,
                                              ProtoTranslationTable* table) {
   return std::unique_ptr<FtraceConfigMuxer>(
-      new FtraceConfigMuxer(ftrace, table));
+      new FtraceConfigMuxer(ftrace, table, {}));
 }
 
 class MockFtraceProcfs : public FtraceProcfs {
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 060f4cd..7cfcf2f 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -27,6 +27,8 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
 
 namespace perfetto {
@@ -107,6 +109,20 @@
   return ReadFileIntoString(path);
 }
 
+std::vector<std::string> FtraceProcfs::ReadEnabledEvents() {
+  std::string path = root_ + "set_event";
+  std::string s = ReadFileIntoString(path);
+  base::StringSplitter ss(s, '\n');
+  std::vector<std::string> events;
+  while (ss.Next()) {
+    std::string event = ss.cur_token();
+    if (event.size() == 0)
+      continue;
+    events.push_back(base::StripChars(event, ":", '/'));
+  }
+  return events;
+}
+
 std::string FtraceProcfs::ReadPageHeaderFormat() const {
   std::string path = root_ + "events/header_page";
   return ReadFileIntoString(path);
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index cd05adb..3d8186f 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "perfetto/ext/base/scoped_file.h"
 
@@ -91,6 +92,9 @@
   // Get all the available clocks.
   std::set<std::string> AvailableClocks();
 
+  // Get all the enabled events.
+  virtual std::vector<std::string> ReadEnabledEvents();
+
   // Open the raw pipe for |cpu|.
   virtual base::ScopedFile OpenPipeForCpu(size_t cpu);
 
@@ -98,7 +102,7 @@
       const std::string& path) const;
 
  protected:
-  // virtual and public for testing.
+  // virtual and protected for testing.
   virtual bool WriteToFile(const std::string& path, const std::string& str);
   virtual bool AppendToFile(const std::string& path, const std::string& str);
   virtual bool ClearFile(const std::string& path);
diff --git a/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc b/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
index b8bf269..8dd848f 100644
--- a/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
@@ -24,9 +24,11 @@
 #include "src/traced/probes/ftrace/ftrace_procfs.h"
 #include "test/gtest_and_gmock.h"
 
-using testing::HasSubstr;
-using testing::Not;
 using testing::Contains;
+using testing::HasSubstr;
+using testing::IsEmpty;
+using testing::Not;
+using testing::UnorderedElementsAre;
 
 namespace perfetto {
 namespace {
@@ -223,4 +225,27 @@
   EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("Hello")));
 }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#define MAYBE_ReadEnabledEvents ReadEnabledEvents
+#else
+#define MAYBE_ReadEnabledEvents DISABLED_ReadEnabledEvents
+#endif
+TEST(FtraceProcfsIntegrationTest, MAYBE_ReadEnabledEvents) {
+  FtraceProcfs ftrace(GetFtracePath());
+  ResetFtrace(&ftrace);
+
+  EXPECT_THAT(ftrace.ReadEnabledEvents(), IsEmpty());
+
+  ftrace.EnableEvent("sched", "sched_switch");
+  ftrace.EnableEvent("kmem", "kmalloc");
+
+  EXPECT_THAT(ftrace.ReadEnabledEvents(),
+              UnorderedElementsAre("sched/sched_switch", "kmem/kmalloc"));
+
+  ftrace.DisableEvent("sched", "sched_switch");
+  ftrace.DisableEvent("kmem", "kmalloc");
+
+  EXPECT_THAT(ftrace.ReadEnabledEvents(), IsEmpty());
+}
+
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/proto_translation_table.h b/src/traced/probes/ftrace/proto_translation_table.h
index f9c5be6..83dd19d 100644
--- a/src/traced/probes/ftrace/proto_translation_table.h
+++ b/src/traced/probes/ftrace/proto_translation_table.h
@@ -19,6 +19,7 @@
 
 #include <stdint.h>
 
+#include <iostream>
 #include <map>
 #include <memory>
 #include <set>
@@ -65,6 +66,10 @@
   std::string name_;
 };
 
+inline void PrintTo(const GroupAndName& event, ::std::ostream* os) {
+  *os << "GroupAndName(" << event.group() << ", " << event.name() << ")";
+}
+
 bool InferFtraceType(const std::string& type_and_name,
                      size_t size,
                      bool is_signed,