Have ftrace_reader use the auto generated FtraceConfig.cc file

Based on https://android-review.googlesource.com/c/platform/external/perfetto/+/595213
with a couple of small tweaks.

Bug: 72082266
Change-Id: Iec56d3c84f3b2f54ddcf7d52b8bd28b1fda46fec
Pair: azappone
diff --git a/Android.bp b/Android.bp
index ad0d4d1..710f63e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -33,6 +33,7 @@
     "src/ftrace_reader/event_info.cc",
     "src/ftrace_reader/event_info_constants.cc",
     "src/ftrace_reader/format_parser.cc",
+    "src/ftrace_reader/ftrace_config.cc",
     "src/ftrace_reader/ftrace_controller.cc",
     "src/ftrace_reader/ftrace_procfs.cc",
     "src/ftrace_reader/proto_translation_table.cc",
@@ -195,6 +196,7 @@
     "src/ftrace_reader/event_info.cc",
     "src/ftrace_reader/event_info_constants.cc",
     "src/ftrace_reader/format_parser.cc",
+    "src/ftrace_reader/ftrace_config.cc",
     "src/ftrace_reader/ftrace_controller.cc",
     "src/ftrace_reader/ftrace_procfs.cc",
     "src/ftrace_reader/ftrace_procfs_integrationtest.cc",
@@ -1801,6 +1803,8 @@
     "src/ftrace_reader/event_info_unittest.cc",
     "src/ftrace_reader/format_parser.cc",
     "src/ftrace_reader/format_parser_unittest.cc",
+    "src/ftrace_reader/ftrace_config.cc",
+    "src/ftrace_reader/ftrace_config_unittest.cc",
     "src/ftrace_reader/ftrace_controller.cc",
     "src/ftrace_reader/ftrace_controller_unittest.cc",
     "src/ftrace_reader/ftrace_procfs.cc",
diff --git a/include/perfetto/ftrace_reader/BUILD.gn b/include/perfetto/ftrace_reader/BUILD.gn
index afa9ebc..fecb7cd 100644
--- a/include/perfetto/ftrace_reader/BUILD.gn
+++ b/include/perfetto/ftrace_reader/BUILD.gn
@@ -18,6 +18,7 @@
   ]
   sources = [
     "format_parser.h",
+    "ftrace_config.h",
     "ftrace_controller.h",
   ]
 }
diff --git a/include/perfetto/ftrace_reader/ftrace_config.h b/include/perfetto/ftrace_reader/ftrace_config.h
new file mode 100644
index 0000000..7f6b264
--- /dev/null
+++ b/include/perfetto/ftrace_reader/ftrace_config.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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 <set>
+#include <string>
+
+#include "perfetto/tracing/core/data_source_config.h"
+
+namespace perfetto {
+
+// Alias FtraceConfig to avoid unnecessary typing.
+using FtraceConfig = DataSourceConfig::FtraceConfig;
+
+// Utility method for the common case where we don't care about atrace events.
+FtraceConfig CreateFtraceConfig(std::set<std::string> names);
+
+// Get the ftrace events for a config as a set.
+std::set<std::string> FtraceEventsAsSet(const FtraceConfig&);
+
+// Returns true iff the config has any atrace categories or apps.
+bool RequiresAtrace(const FtraceConfig&);
+
+// Returns true iff the config is 'valid'. Spesfically all the
+// event/categories/app names should not look like:
+// "../../some/kind/of/directory/escape".
+bool ValidConfig(const FtraceConfig& config);
+
+}  // namespace perfetto
diff --git a/include/perfetto/ftrace_reader/ftrace_controller.h b/include/perfetto/ftrace_reader/ftrace_controller.h
index e72e0d1..ce9c6cb 100644
--- a/include/perfetto/ftrace_reader/ftrace_controller.h
+++ b/include/perfetto/ftrace_reader/ftrace_controller.h
@@ -29,6 +29,7 @@
 #include "perfetto/base/scoped_file.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/weak_ptr.h"
+#include "perfetto/ftrace_reader/ftrace_config.h"
 #include "perfetto/protozero/protozero_message_handle.h"
 
 namespace perfetto {
@@ -47,36 +48,6 @@
 class FtraceProcfs;
 class EventFilter;
 
-class FtraceConfig {
- public:
-  explicit FtraceConfig(std::set<std::string> events);
-  FtraceConfig();
-  ~FtraceConfig();
-
-  void AddEvent(const std::string&);
-  void AddAtraceApp(const std::string& app);
-  void AddAtraceCategory(const std::string&);
-  bool RequiresAtrace() const;
-
-  const std::set<std::string>& events() const { return ftrace_events_; }
-  const std::set<std::string>& atrace_categories() const {
-    return atrace_categories_;
-  }
-  const std::set<std::string>& atrace_apps() const { return atrace_apps_; }
-
-  uint32_t buffer_size_kb() const { return buffer_size_kb_; }
-  uint32_t drain_period_ms() const { return drain_period_ms_; }
-  void set_buffer_size_kb(uint32_t v) { buffer_size_kb_ = v; }
-  void set_drain_period_ms(uint32_t v) { drain_period_ms_ = v; }
-
- private:
-  std::set<std::string> ftrace_events_;
-  std::set<std::string> atrace_categories_;
-  std::set<std::string> atrace_apps_;
-  uint32_t buffer_size_kb_ = 0;
-  uint32_t drain_period_ms_ = 0;
-};
-
 // To consume ftrace data clients implement a |FtraceSink::Delegate| and use it
 // to create a |FtraceSink|. While the FtraceSink lives FtraceController will
 // call |GetBundleForCpu|, write data into the bundle then call
@@ -129,8 +100,7 @@
   static std::unique_ptr<FtraceController> Create(base::TaskRunner*);
   virtual ~FtraceController();
 
-  std::unique_ptr<FtraceSink> CreateSink(const FtraceConfig&,
-                                         FtraceSink::Delegate*);
+  std::unique_ptr<FtraceSink> CreateSink(FtraceConfig, FtraceSink::Delegate*);
 
   void DisableAllEvents();
   void WriteTraceMarker(const std::string& s);
diff --git a/src/ftrace_reader/BUILD.gn b/src/ftrace_reader/BUILD.gn
index bbdb38d..5996811 100644
--- a/src/ftrace_reader/BUILD.gn
+++ b/src/ftrace_reader/BUILD.gn
@@ -55,6 +55,7 @@
     "cpu_reader_unittest.cc",
     "event_info_unittest.cc",
     "format_parser_unittest.cc",
+    "ftrace_config_unittest.cc",
     "ftrace_controller_unittest.cc",
     "proto_translation_table_unittest.cc",
   ]
@@ -87,6 +88,7 @@
     "../../gn:gtest_deps",
     "../../protos/perfetto/trace/ftrace:lite",
     "../base",
+    "../tracing",
   ]
   sources = [
     "end_to_end_integrationtest.cc",
@@ -99,6 +101,7 @@
     "../../gn:gtest_prod_config",
     "../../include/perfetto/ftrace_reader",
     "../../protos/perfetto/trace/ftrace:zero",
+    "../tracing",
   ]
   deps = [
     "../../gn:default_deps",
@@ -113,6 +116,7 @@
     "event_info_constants.cc",
     "event_info_constants.h",
     "format_parser.cc",
+    "ftrace_config.cc",
     "ftrace_controller.cc",
     "ftrace_procfs.cc",
     "ftrace_procfs.h",
diff --git a/src/ftrace_reader/end_to_end_integrationtest.cc b/src/ftrace_reader/end_to_end_integrationtest.cc
index ef5c8e5..9fe6fd9 100644
--- a/src/ftrace_reader/end_to_end_integrationtest.cc
+++ b/src/ftrace_reader/end_to_end_integrationtest.cc
@@ -17,10 +17,11 @@
 #include <fstream>
 #include <sstream>
 
-#include "ftrace_procfs.h"
 #include "gmock/gmock.h"
 #include "google/protobuf/text_format.h"
 #include "gtest/gtest.h"
+
+#include "ftrace_procfs.h"
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/unix_task_runner.h"
 #include "perfetto/base/utils.h"
@@ -106,7 +107,9 @@
 
   // Create a sink listening for our favorite events:
   std::unique_ptr<FtraceController> ftrace = FtraceController::Create(runner());
-  FtraceConfig config(std::set<std::string>({"print", "sched_switch"}));
+  FtraceConfig config;
+  *config.add_event_names() = "print";
+  *config.add_event_names() = "sched_switch";
   std::unique_ptr<FtraceSink> sink = ftrace->CreateSink(config, this);
 
   // Let some events build up.
@@ -138,8 +141,9 @@
 
   // Create a sink listening for our favorite events:
   std::unique_ptr<FtraceController> ftrace = FtraceController::Create(runner());
-  FtraceConfig config(std::set<std::string>({"sched_switch"}));
-  config.AddAtraceCategory("sched");
+  FtraceConfig config;
+  *config.add_event_names() = "print";
+  *config.add_event_names() = "sched_switch";
   std::unique_ptr<FtraceSink> sink = ftrace->CreateSink(config, this);
 
   // Let some events build up.
diff --git a/src/ftrace_reader/ftrace_config.cc b/src/ftrace_reader/ftrace_config.cc
new file mode 100644
index 0000000..cd841a1
--- /dev/null
+++ b/src/ftrace_reader/ftrace_config.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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 "perfetto/ftrace_reader/ftrace_config.h"
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace {
+
+bool IsGoodPunctuation(char c) {
+  return c == '_' || c == '.';
+}
+
+bool IsValid(const std::string& str) {
+  for (size_t i = 0; i < str.size(); i++) {
+    if (!isalnum(str[i]) && !IsGoodPunctuation(str[i]))
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+std::set<std::string> FtraceEventsAsSet(const FtraceConfig& config) {
+  std::set<std::string> events;
+  for (const std::string& event : config.event_names())
+    events.insert(event);
+  return events;
+}
+
+FtraceConfig CreateFtraceConfig(std::set<std::string> names) {
+  FtraceConfig config;
+  for (const std::string& name : names)
+    *config.add_event_names() = name;
+  return config;
+}
+
+bool RequiresAtrace(const FtraceConfig& config) {
+  return !config.atrace_categories().empty() || !config.atrace_apps().empty();
+}
+
+bool ValidConfig(const FtraceConfig& config) {
+  for (const std::string& event_name : config.event_names()) {
+    if (!IsValid(event_name)) {
+      PERFETTO_ELOG("Bad event name '%s'", event_name.c_str());
+      return false;
+    }
+  }
+  for (const std::string& category : config.atrace_categories()) {
+    if (!IsValid(category)) {
+      PERFETTO_ELOG("Bad category name '%s'", category.c_str());
+      return false;
+    }
+  }
+  for (const std::string& app : config.atrace_apps()) {
+    if (!IsValid(app)) {
+      PERFETTO_ELOG("Bad app '%s'", app.c_str());
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace perfetto
diff --git a/src/ftrace_reader/ftrace_config_unittest.cc b/src/ftrace_reader/ftrace_config_unittest.cc
new file mode 100644
index 0000000..37f25af
--- /dev/null
+++ b/src/ftrace_reader/ftrace_config_unittest.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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 "perfetto/ftrace_reader/ftrace_config.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using testing::Contains;
+
+namespace perfetto {
+namespace {
+
+TEST(ConfigTest, FtraceEventsAsSet) {
+  FtraceConfig config;
+  *config.add_event_names() = "aaa";
+  *config.add_event_names() = "bbb";
+  *config.add_event_names() = "aaa";
+
+  EXPECT_EQ(FtraceEventsAsSet(config), std::set<std::string>({
+                                           "aaa", "bbb",
+                                       }));
+}
+
+TEST(ConfigTest, CreateFtraceConfig) {
+  FtraceConfig config = CreateFtraceConfig({
+      "aaa", "bbb",
+  });
+
+  EXPECT_THAT(config.event_names(), Contains("aaa"));
+  EXPECT_THAT(config.event_names(), Contains("bbb"));
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/ftrace_reader/ftrace_controller.cc b/src/ftrace_reader/ftrace_controller.cc
index e7e8f95..2e2ec57 100644
--- a/src/ftrace_reader/ftrace_controller.cc
+++ b/src/ftrace_reader/ftrace_controller.cc
@@ -263,14 +263,18 @@
 }
 
 std::unique_ptr<FtraceSink> FtraceController::CreateSink(
-    const FtraceConfig& config,
+    FtraceConfig config,
     FtraceSink::Delegate* delegate) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   if (sinks_.size() >= kMaxSinks)
     return nullptr;
+  if (!ValidConfig(config))
+    return nullptr;
   auto controller_weak = weak_factory_.GetWeakPtr();
   auto filter = std::unique_ptr<EventFilter>(
-      new EventFilter(*table_.get(), config.events()));
+      new EventFilter(*table_.get(), FtraceEventsAsSet(config)));
+  for (const std::string& event : config.event_names())
+    PERFETTO_LOG("%s", event.c_str());
   auto sink = std::unique_ptr<FtraceSink>(new FtraceSink(
       std::move(controller_weak), config, std::move(filter), delegate));
   Register(sink.get());
@@ -281,7 +285,7 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   auto it_and_inserted = sinks_.insert(sink);
   PERFETTO_DCHECK(it_and_inserted.second);
-  if (sink->config().RequiresAtrace())
+  if (RequiresAtrace(sink->config()))
     StartAtrace(sink->config());
 
   StartIfNeeded();
@@ -320,7 +324,7 @@
 
   for (const std::string& name : sink->enabled_events())
     UnregisterForEvent(name);
-  if (sink->config().RequiresAtrace())
+  if (RequiresAtrace(sink->config()))
     StopAtrace();
   StopIfNeeded();
 }
@@ -371,35 +375,4 @@
   return filter_->enabled_names();
 }
 
-FtraceConfig::FtraceConfig() = default;
-FtraceConfig::FtraceConfig(std::set<std::string> events)
-    : ftrace_events_(std::move(events)) {}
-
-FtraceConfig::~FtraceConfig() = default;
-
-void FtraceConfig::AddEvent(const std::string& event) {
-  ftrace_events_.insert(event);
-}
-
-void FtraceConfig::AddAtraceApp(const std::string& app) {
-  // You implicitly need the print ftrace event if you
-  // are using atrace.
-  AddEvent("print");
-  atrace_apps_.insert(app);
-}
-
-void FtraceConfig::AddAtraceCategory(const std::string& category) {
-  // You implicitly need the print ftrace event if you
-  // are using atrace.
-  AddEvent("print");
-  if (category == "sched") {
-    AddEvent("sched_switch");
-  }
-  atrace_categories_.insert(category);
-}
-
-bool FtraceConfig::RequiresAtrace() const {
-  return !atrace_categories_.empty() || !atrace_apps_.empty();
-}
-
 }  // namespace perfetto
diff --git a/src/ftrace_reader/ftrace_controller_unittest.cc b/src/ftrace_reader/ftrace_controller_unittest.cc
index c0849ef..40caae3 100644
--- a/src/ftrace_reader/ftrace_controller_unittest.cc
+++ b/src/ftrace_reader/ftrace_controller_unittest.cc
@@ -24,6 +24,7 @@
 #include "ftrace_procfs.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "perfetto/ftrace_reader/ftrace_config.h"
 #include "proto_translation_table.h"
 
 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
@@ -135,7 +136,7 @@
 
 }  // namespace
 
-TEST(FtraceControllerTest, NoExistentEventsDontCrash) {
+TEST(FtraceControllerTest, NonExistentEventsDontCrash) {
   NiceMock<MockTaskRunner> task_runner;
   auto ftrace_procfs =
       std::unique_ptr<MockFtraceProcfs>(new NiceMock<MockFtraceProcfs>());
@@ -143,12 +144,23 @@
                                   FakeTable());
 
   MockDelegate delegate;
-  FtraceConfig config;
-  config.AddEvent("not_an_event");
+  FtraceConfig config = CreateFtraceConfig({"not_an_event"});
 
   std::unique_ptr<FtraceSink> sink = controller.CreateSink(config, &delegate);
 }
 
+TEST(FtraceControllerTest, RejectsBadEventNames) {
+  NiceMock<MockTaskRunner> task_runner;
+  auto ftrace_procfs =
+      std::unique_ptr<MockFtraceProcfs>(new NiceMock<MockFtraceProcfs>());
+  TestFtraceController controller(std::move(ftrace_procfs), &task_runner,
+                                  FakeTable());
+
+  MockDelegate delegate;
+  FtraceConfig config = CreateFtraceConfig({"../try/to/escape"});
+  EXPECT_FALSE(controller.CreateSink(config, &delegate));
+}
+
 TEST(FtraceControllerTest, OneSink) {
   MockTaskRunner task_runner;
   auto ftrace_procfs =
@@ -158,7 +170,7 @@
                                   FakeTable());
 
   MockDelegate delegate;
-  FtraceConfig config({"foo"});
+  FtraceConfig config = CreateFtraceConfig({"foo"});
 
   EXPECT_CALL(*raw_ftrace_procfs, WriteToFile("/root/tracing_on", "1"));
   EXPECT_CALL(task_runner, PostDelayedTask(_, _));
@@ -181,8 +193,8 @@
 
   MockDelegate delegate;
 
-  FtraceConfig configA({"foo"});
-  FtraceConfig configB({"foo", "bar"});
+  FtraceConfig configA = CreateFtraceConfig({"foo"});
+  FtraceConfig configB = CreateFtraceConfig({"foo", "bar"});
 
   EXPECT_CALL(*raw_ftrace_procfs, WriteToFile("/root/tracing_on", "1"));
   EXPECT_CALL(*raw_ftrace_procfs, WriteToFile("/root/buffer_size_kb", _));
@@ -210,7 +222,7 @@
       std::move(ftrace_procfs), &task_runner, FakeTable()));
 
   MockDelegate delegate;
-  FtraceConfig config({"foo"});
+  FtraceConfig config = CreateFtraceConfig({"foo"});
 
   EXPECT_CALL(*raw_ftrace_procfs, WriteToFile("/root/buffer_size_kb", _));
   EXPECT_CALL(*raw_ftrace_procfs, WriteToFile("/root/tracing_on", "1"));
@@ -237,7 +249,7 @@
   EXPECT_CALL(*raw_ftrace_procfs, WriteToFile(_, _)).Times(AnyNumber());
 
   MockDelegate delegate;
-  FtraceConfig config({"foo"});
+  FtraceConfig config = CreateFtraceConfig({"foo"});
 
   EXPECT_CALL(task_runner, PostDelayedTask(_, 100));
   std::unique_ptr<FtraceSink> sink = controller.CreateSink(config, &delegate);
@@ -273,7 +285,7 @@
   EXPECT_CALL(*raw_ftrace_procfs, WriteToFile(_, _)).Times(AnyNumber());
 
   MockDelegate delegate;
-  FtraceConfig config({"foo"});
+  FtraceConfig config = CreateFtraceConfig({"foo"});
 
   EXPECT_CALL(task_runner, PostDelayedTask(_, 100));
   std::unique_ptr<FtraceSink> sink_a = controller.CreateSink(config, &delegate);
@@ -311,7 +323,7 @@
     // 8192kb = 8mb
     EXPECT_CALL(*raw_ftrace_procfs,
                 WriteToFile("/root/buffer_size_kb", "4096"));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     auto sink = controller.CreateSink(config, &delegate);
   }
 
@@ -319,7 +331,7 @@
     // Way too big buffer size -> good default.
     EXPECT_CALL(*raw_ftrace_procfs,
                 WriteToFile("/root/buffer_size_kb", "4096"));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     config.set_buffer_size_kb(10 * 1024 * 1024);
     auto sink = controller.CreateSink(config, &delegate);
   }
@@ -328,7 +340,7 @@
     // The limit is 8mb, 9mb is too much.
     EXPECT_CALL(*raw_ftrace_procfs,
                 WriteToFile("/root/buffer_size_kb", "4096"));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     ON_CALL(*raw_ftrace_procfs, NumberOfCpus()).WillByDefault(Return(2));
     config.set_buffer_size_kb(9 * 1024);
     auto sink = controller.CreateSink(config, &delegate);
@@ -337,7 +349,7 @@
   {
     // Your size ends up with less than 1 page per cpu -> 1 page.
     EXPECT_CALL(*raw_ftrace_procfs, WriteToFile("/root/buffer_size_kb", "4"));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     config.set_buffer_size_kb(1);
     auto sink = controller.CreateSink(config, &delegate);
   }
@@ -345,7 +357,7 @@
   {
     // You picked a good size -> your size rounded to nearest page.
     EXPECT_CALL(*raw_ftrace_procfs, WriteToFile("/root/buffer_size_kb", "40"));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     config.set_buffer_size_kb(42);
     auto sink = controller.CreateSink(config, &delegate);
   }
@@ -353,7 +365,7 @@
   {
     // You picked a good size -> your size rounded to nearest page.
     EXPECT_CALL(*raw_ftrace_procfs, WriteToFile("/root/buffer_size_kb", "40"));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     ON_CALL(*raw_ftrace_procfs, NumberOfCpus()).WillByDefault(Return(2));
     config.set_buffer_size_kb(42);
     auto sink = controller.CreateSink(config, &delegate);
@@ -375,14 +387,14 @@
   {
     // No period -> good default.
     EXPECT_CALL(task_runner, PostDelayedTask(_, 100));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     auto sink = controller.CreateSink(config, &delegate);
   }
 
   {
     // Pick a tiny value -> good default.
     EXPECT_CALL(task_runner, PostDelayedTask(_, 100));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     config.set_drain_period_ms(0);
     auto sink = controller.CreateSink(config, &delegate);
   }
@@ -390,7 +402,7 @@
   {
     // Pick a huge value -> good default.
     EXPECT_CALL(task_runner, PostDelayedTask(_, 100));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     config.set_drain_period_ms(1000 * 60 * 60);
     auto sink = controller.CreateSink(config, &delegate);
   }
@@ -398,7 +410,7 @@
   {
     // Pick a resonable value -> get that value.
     EXPECT_CALL(task_runner, PostDelayedTask(_, 200));
-    FtraceConfig config({"foo"});
+    FtraceConfig config = CreateFtraceConfig({"foo"});
     config.set_drain_period_ms(200);
     auto sink = controller.CreateSink(config, &delegate);
   }
diff --git a/src/traced/probes/ftrace_producer.cc b/src/traced/probes/ftrace_producer.cc
index 53eb604..f111f78 100644
--- a/src/traced/probes/ftrace_producer.cc
+++ b/src/traced/probes/ftrace_producer.cc
@@ -32,21 +32,9 @@
 namespace perfetto {
 namespace {
 
-bool IsGoodPunctuation(char c) {
-  return c == '_' || c == '.';
-}
-
 uint64_t kInitialConnectionBackoffMs = 100;
 uint64_t kMaxConnectionBackoffMs = 30 * 1000;
 
-bool IsValid(const std::string& str) {
-  for (size_t i = 0; i < str.size(); i++) {
-    if (!isalnum(str[i]) && !IsGoodPunctuation(str[i]))
-      return false;
-  }
-  return true;
-}
-
 }  // namespace.
 
 // State transition diagram:
@@ -108,35 +96,7 @@
                source_config.target_buffer());
 
   // TODO(hjd): Would be nice if ftrace_reader could use the generated config.
-  const DataSourceConfig::FtraceConfig proto_config =
-      source_config.ftrace_config();
-
-  FtraceConfig config;
-  // TODO(b/72082266): We shouldn't have to do this.
-  for (const std::string& event_name : proto_config.event_names()) {
-    if (IsValid(event_name)) {
-      config.AddEvent(event_name.c_str());
-    } else {
-      PERFETTO_ELOG("Bad event name '%s'", event_name.c_str());
-    }
-  }
-  for (const std::string& category : proto_config.atrace_categories()) {
-    if (IsValid(category)) {
-      config.AddAtraceCategory(category.c_str());
-    } else {
-      PERFETTO_ELOG("Bad category name '%s'", category.c_str());
-    }
-  }
-  for (const std::string& app : proto_config.atrace_apps()) {
-    if (IsValid(app)) {
-      config.AddAtraceApp(app.c_str());
-    } else {
-      PERFETTO_ELOG("Bad app '%s'", app.c_str());
-    }
-  }
-
-  config.set_buffer_size_kb(proto_config.buffer_size_kb());
-  config.set_drain_period_ms(proto_config.drain_period_ms());
+  DataSourceConfig::FtraceConfig config = source_config.ftrace_config();
 
   // TODO(hjd): Static cast is bad, target_buffer() should return a BufferID.
   auto trace_writer = endpoint_->CreateTraceWriter(