tp: split systrace trace parser into pieces

This is necessary for the upcoming work to parse system events from JSON
traces. The JSON code will use the line parser and tokenizer without
needing to use the trace parser.

This is a pure refactor without any functional changes.

Bug: 150213213
Change-Id: Idfd64b869f87be4922dd77781d0822e43b633a09
diff --git a/Android.bp b/Android.bp
index 4d4e23d..36dfe0c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6305,6 +6305,8 @@
     "src/trace_processor/importers/proto/system_probes_module.cc",
     "src/trace_processor/importers/proto/system_probes_parser.cc",
     "src/trace_processor/importers/proto/vulkan_memory_tracker.cc",
+    "src/trace_processor/importers/systrace/systrace_line_parser.cc",
+    "src/trace_processor/importers/systrace/systrace_line_tokenizer.cc",
     "src/trace_processor/importers/systrace/systrace_parser.cc",
     "src/trace_processor/importers/systrace/systrace_trace_parser.cc",
     "src/trace_processor/syscall_tracker.cc",
diff --git a/BUILD b/BUILD
index c870e14..fda4ad8 100644
--- a/BUILD
+++ b/BUILD
@@ -903,6 +903,11 @@
         "src/trace_processor/importers/proto/system_probes_parser.h",
         "src/trace_processor/importers/proto/vulkan_memory_tracker.cc",
         "src/trace_processor/importers/proto/vulkan_memory_tracker.h",
+        "src/trace_processor/importers/systrace/systrace_line.h",
+        "src/trace_processor/importers/systrace/systrace_line_parser.cc",
+        "src/trace_processor/importers/systrace/systrace_line_parser.h",
+        "src/trace_processor/importers/systrace/systrace_line_tokenizer.cc",
+        "src/trace_processor/importers/systrace/systrace_line_tokenizer.h",
         "src/trace_processor/importers/systrace/systrace_parser.cc",
         "src/trace_processor/importers/systrace/systrace_parser.h",
         "src/trace_processor/importers/systrace/systrace_trace_parser.cc",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 2aec4e3..1ba2369 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -244,6 +244,11 @@
     "importers/proto/system_probes_parser.h",
     "importers/proto/vulkan_memory_tracker.cc",
     "importers/proto/vulkan_memory_tracker.h",
+    "importers/systrace/systrace_line.h",
+    "importers/systrace/systrace_line_parser.cc",
+    "importers/systrace/systrace_line_parser.h",
+    "importers/systrace/systrace_line_tokenizer.cc",
+    "importers/systrace/systrace_line_tokenizer.h",
     "importers/systrace/systrace_parser.cc",
     "importers/systrace/systrace_parser.h",
     "importers/systrace/systrace_trace_parser.cc",
diff --git a/src/trace_processor/importers/systrace/systrace_line.h b/src/trace_processor/importers/systrace/systrace_line.h
new file mode 100644
index 0000000..817672b
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line.h
@@ -0,0 +1,41 @@
+/*
+ * 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_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_H_
+
+#include <inttypes.h>
+#include <string>
+
+namespace perfetto {
+namespace trace_processor {
+
+struct SystraceLine {
+  int64_t ts;
+  uint32_t pid;
+  uint32_t cpu;
+
+  std::string task;
+  std::string pid_str;
+  std::string tgid_str;
+  std::string event_name;
+  std::string args_str;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_H_
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.cc b/src/trace_processor/importers/systrace/systrace_line_parser.cc
new file mode 100644
index 0000000..be456a6
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.cc
@@ -0,0 +1,121 @@
+/*
+ * 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/trace_processor/importers/systrace/systrace_line_parser.h"
+
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/args_tracker.h"
+#include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/ftrace_utils.h"
+#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
+#include "src/trace_processor/importers/systrace/systrace_parser.h"
+#include "src/trace_processor/process_tracker.h"
+#include "src/trace_processor/slice_tracker.h"
+#include "src/trace_processor/track_tracker.h"
+
+#include <inttypes.h>
+#include <cctype>
+#include <string>
+#include <unordered_map>
+
+namespace perfetto {
+namespace trace_processor {
+
+SystraceLineParser::SystraceLineParser(TraceProcessorContext* ctx)
+    : context_(ctx),
+      sched_wakeup_name_id_(ctx->storage->InternString("sched_wakeup")),
+      cpuidle_name_id_(ctx->storage->InternString("cpuidle")) {}
+
+util::Status SystraceLineParser::ParseLine(const SystraceLine& line) {
+  context_->process_tracker->GetOrCreateThread(line.pid);
+
+  if (!line.tgid_str.empty() && line.tgid_str != "-----") {
+    base::Optional<uint32_t> tgid = base::StringToUInt32(line.tgid_str);
+    if (tgid) {
+      context_->process_tracker->UpdateThread(line.pid, tgid.value());
+    }
+  }
+
+  std::unordered_map<std::string, std::string> args;
+  for (base::StringSplitter ss(line.args_str, ' '); ss.Next();) {
+    std::string key;
+    std::string value;
+    for (base::StringSplitter inner(ss.cur_token(), '='); inner.Next();) {
+      if (key.empty()) {
+        key = inner.cur_token();
+      } else {
+        value = inner.cur_token();
+      }
+    }
+    args.emplace(std::move(key), std::move(value));
+  }
+  if (line.event_name == "sched_switch") {
+    auto prev_state_str = args["prev_state"];
+    int64_t prev_state =
+        ftrace_utils::TaskState(prev_state_str.c_str()).raw_state();
+
+    auto prev_pid = base::StringToUInt32(args["prev_pid"]);
+    auto prev_comm = base::StringView(args["prev_comm"]);
+    auto prev_prio = base::StringToInt32(args["prev_prio"]);
+    auto next_pid = base::StringToUInt32(args["next_pid"]);
+    auto next_comm = base::StringView(args["next_comm"]);
+    auto next_prio = base::StringToInt32(args["next_prio"]);
+
+    if (!(prev_pid.has_value() && prev_prio.has_value() &&
+          next_pid.has_value() && next_prio.has_value())) {
+      return util::Status("Could not parse sched_switch");
+    }
+
+    SchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
+        line.cpu, line.ts, prev_pid.value(), prev_comm, prev_prio.value(),
+        prev_state, next_pid.value(), next_comm, next_prio.value());
+  } else if (line.event_name == "tracing_mark_write" ||
+             line.event_name == "0" || line.event_name == "print") {
+    SystraceParser::GetOrCreate(context_)->ParsePrintEvent(
+        line.ts, line.pid, line.args_str.c_str());
+  } else if (line.event_name == "sched_wakeup") {
+    auto comm = args["comm"];
+    base::Optional<uint32_t> wakee_pid = base::StringToUInt32(args["pid"]);
+    if (!wakee_pid.has_value()) {
+      return util::Status("Could not convert wakee_pid");
+    }
+
+    StringId name_id = context_->storage->InternString(base::StringView(comm));
+    auto wakee_utid =
+        context_->process_tracker->UpdateThreadName(wakee_pid.value(), name_id);
+    context_->event_tracker->PushInstant(line.ts, sched_wakeup_name_id_,
+                                         wakee_utid, RefType::kRefUtid);
+  } else if (line.event_name == "cpu_idle") {
+    base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
+    base::Optional<double> new_state = base::StringToDouble(args["state"]);
+    if (!event_cpu.has_value()) {
+      return util::Status("Could not convert event cpu");
+    }
+    if (!event_cpu.has_value()) {
+      return util::Status("Could not convert state");
+    }
+
+    TrackId track = context_->track_tracker->InternCpuCounterTrack(
+        cpuidle_name_id_, event_cpu.value());
+    context_->event_tracker->PushCounter(line.ts, new_state.value(), track);
+  }
+
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.h b/src/trace_processor/importers/systrace/systrace_line_parser.h
new file mode 100644
index 0000000..ca1dafe
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.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_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_PARSER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_PARSER_H_
+
+#include "perfetto/trace_processor/status.h"
+
+#include "src/trace_processor/importers/systrace/systrace_line.h"
+#include "src/trace_processor/trace_parser.h"
+#include "src/trace_processor/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class SystraceLineParser {
+ public:
+  explicit SystraceLineParser(TraceProcessorContext*);
+
+  util::Status ParseLine(const SystraceLine&);
+
+ private:
+  TraceProcessorContext* const context_;
+  const StringId sched_wakeup_name_id_ = kNullStringId;
+  const StringId cpuidle_name_id_ = kNullStringId;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_PARSER_H_
diff --git a/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc b/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc
new file mode 100644
index 0000000..7f58e5a
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc
@@ -0,0 +1,98 @@
+/*
+ * 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/trace_processor/importers/systrace/systrace_line_tokenizer.h"
+
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+std::string SubstrTrim(const std::string& input) {
+  std::string s = input;
+  s.erase(s.begin(), std::find_if(s.begin(), s.end(),
+                                  [](char ch) { return !std::isspace(ch); }));
+  s.erase(std::find_if(s.rbegin(), s.rend(),
+                       [](char ch) { return !std::isspace(ch); })
+              .base(),
+          s.end());
+  return s;
+}
+}  // namespace
+
+SystraceLineTokenizer::SystraceLineTokenizer()
+    : line_matcher_(std::regex(R"(-(\d+)\s+\(?\s*(\d+|-+)?\)?\s?\[(\d+)\]\s*)"
+                               R"([a-zA-Z0-9.]{0,4}\s+(\d+\.\d+):\s+(\S+):)")) {
+}
+
+// TODO(hjd): This should be more robust to being passed random input.
+// This can happen if we mess up detecting a gzip trace for example.
+util::Status SystraceLineTokenizer::Tokenize(const std::string& buffer,
+                                             SystraceLine* line) {
+  // An example line from buffer looks something like the following:
+  // kworker/u16:1-77    (   77) [004] ....   316.196720: 0:
+  // B|77|__scm_call_armv8_64|0
+  //
+  // However, sometimes the tgid can be missing and buffer looks like this:
+  // <idle>-0     [000] ...2     0.002188: task_newtask: pid=1 ...
+  //
+  // Also the irq fields can be missing (we don't parse these anyway)
+  // <idle>-0     [000]  0.002188: task_newtask: pid=1 ...
+  //
+  // The task name can contain any characters e.g -:[(/ and for this reason
+  // it is much easier to use a regex (even though it is slower than parsing
+  // manually)
+
+  std::smatch matches;
+  bool matched = std::regex_search(buffer, matches, line_matcher_);
+  if (!matched) {
+    return util::ErrStatus("Not a known systrace event format (line: %s)",
+                           buffer.c_str());
+  }
+
+  std::string pid_str = matches[1].str();
+  std::string cpu_str = matches[3].str();
+  std::string ts_str = matches[4].str();
+
+  line->task = SubstrTrim(matches.prefix());
+  line->tgid_str = matches[2].str();
+  line->event_name = matches[5].str();
+  line->args_str = SubstrTrim(matches.suffix());
+
+  base::Optional<uint32_t> maybe_pid = base::StringToUInt32(pid_str);
+  if (!maybe_pid.has_value()) {
+    return util::Status("Could not convert pid " + pid_str);
+  }
+  line->pid = maybe_pid.value();
+
+  base::Optional<uint32_t> maybe_cpu = base::StringToUInt32(cpu_str);
+  if (!maybe_cpu.has_value()) {
+    return util::Status("Could not convert cpu " + cpu_str);
+  }
+  line->cpu = maybe_cpu.value();
+
+  base::Optional<double> maybe_ts = base::StringToDouble(ts_str);
+  if (!maybe_ts.has_value()) {
+    return util::Status("Could not convert ts");
+  }
+  line->ts = static_cast<int64_t>(maybe_ts.value() * 1e9);
+
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/systrace/systrace_line_tokenizer.h b/src/trace_processor/importers/systrace/systrace_line_tokenizer.h
new file mode 100644
index 0000000..10c4a28
--- /dev/null
+++ b/src/trace_processor/importers/systrace/systrace_line_tokenizer.h
@@ -0,0 +1,42 @@
+/*
+ * 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_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_TOKENIZER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_TOKENIZER_H_
+
+#include <regex>
+
+#include "perfetto/trace_processor/status.h"
+
+#include "src/trace_processor/importers/systrace/systrace_line.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class SystraceLineTokenizer {
+ public:
+  SystraceLineTokenizer();
+
+  util::Status Tokenize(const std::string& line, SystraceLine*);
+
+ private:
+  const std::regex line_matcher_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_SYSTRACE_SYSTRACE_LINE_TOKENIZER_H_
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.cc b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
index 4d76628..54c119b 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
@@ -16,16 +16,8 @@
 
 #include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
 
-#include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "src/trace_processor/args_tracker.h"
-#include "src/trace_processor/event_tracker.h"
-#include "src/trace_processor/ftrace_utils.h"
-#include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
-#include "src/trace_processor/importers/systrace/systrace_parser.h"
-#include "src/trace_processor/process_tracker.h"
-#include "src/trace_processor/slice_tracker.h"
-#include "src/trace_processor/track_tracker.h"
+#include "src/trace_processor/trace_sorter.h"
 
 #include <inttypes.h>
 #include <cctype>
@@ -35,26 +27,8 @@
 namespace perfetto {
 namespace trace_processor {
 
-namespace {
-std::string SubstrTrim(const std::string& input) {
-  std::string s = input;
-  s.erase(s.begin(), std::find_if(s.begin(), s.end(),
-                                  [](char ch) { return !std::isspace(ch); }));
-  s.erase(std::find_if(s.rbegin(), s.rend(),
-                       [](char ch) { return !std::isspace(ch); })
-              .base(),
-          s.end());
-  return s;
-}
-}  // namespace
-
 SystraceTraceParser::SystraceTraceParser(TraceProcessorContext* ctx)
-    : context_(ctx),
-      sched_wakeup_name_id_(ctx->storage->InternString("sched_wakeup")),
-      cpu_idle_name_id_(ctx->storage->InternString("cpuidle")),
-      line_matcher_(std::regex(R"(-(\d+)\s+\(?\s*(\d+|-+)?\)?\s?\[(\d+)\]\s*)"
-                               R"([a-zA-Z0-9.]{0,4}\s+(\d+\.\d+):\s+(\S+):)")) {
-}
+    : line_parser_(ctx) {}
 SystraceTraceParser::~SystraceTraceParser() = default;
 
 util::Status SystraceTraceParser::Parse(std::unique_ptr<uint8_t[]> owned_buf,
@@ -96,7 +70,11 @@
         state_ = ParseState::kEndOfSystrace;
         break;
       } else if (!base::StartsWith(buffer, "#")) {
-        ParseSingleSystraceEvent(buffer);
+        SystraceLine line;
+        util::Status status = line_tokenizer_.Tokenize(buffer, &line);
+        if (!status.ok())
+          return status;
+        line_parser_.ParseLine(std::move(line));
       }
     }
     start_it = line_it + 1;
@@ -109,131 +87,6 @@
   return util::OkStatus();
 }
 
-// TODO(hjd): This should be more robust to being passed random input.
-// This can happen if we mess up detecting a gzip trace for example.
-util::Status SystraceTraceParser::ParseSingleSystraceEvent(
-    const std::string& buffer) {
-  // An example line from buffer looks something like the following:
-  // kworker/u16:1-77    (   77) [004] ....   316.196720: 0:
-  // B|77|__scm_call_armv8_64|0
-  //
-  // However, sometimes the tgid can be missing and buffer looks like this:
-  // <idle>-0     [000] ...2     0.002188: task_newtask: pid=1 ...
-  //
-  // Also the irq fields can be missing (we don't parse these anyway)
-  // <idle>-0     [000]  0.002188: task_newtask: pid=1 ...
-  //
-  // The task name can contain any characters e.g -:[(/ and for this reason
-  // it is much easier to use a regex (even though it is slower than parsing
-  // manually)
-
-  std::smatch matches;
-  bool matched = std::regex_search(buffer, matches, line_matcher_);
-  if (!matched) {
-    return util::Status("Not a known systrace event format");
-  }
-
-  std::string task = SubstrTrim(matches.prefix());
-  std::string pid_str = matches[1].str();
-  std::string tgid_str = matches[2].str();
-  std::string cpu_str = matches[3].str();
-  std::string ts_str = matches[4].str();
-  std::string event_name = matches[5].str();
-  std::string args_str = SubstrTrim(matches.suffix());
-
-  base::Optional<uint32_t> maybe_pid = base::StringToUInt32(pid_str);
-  if (!maybe_pid.has_value()) {
-    return util::Status("Could not convert pid " + pid_str);
-  }
-  uint32_t pid = maybe_pid.value();
-  context_->process_tracker->GetOrCreateThread(pid);
-
-  if (tgid_str != "" && tgid_str != "-----") {
-    base::Optional<uint32_t> tgid = base::StringToUInt32(tgid_str);
-    if (tgid) {
-      context_->process_tracker->UpdateThread(pid, tgid.value());
-    }
-  }
-
-  base::Optional<uint32_t> maybe_cpu = base::StringToUInt32(cpu_str);
-  if (!maybe_cpu.has_value()) {
-    return util::Status("Could not convert cpu " + cpu_str);
-  }
-  uint32_t cpu = maybe_cpu.value();
-
-  base::Optional<double> maybe_ts = base::StringToDouble(ts_str);
-  if (!maybe_ts.has_value()) {
-    return util::Status("Could not convert ts");
-  }
-  int64_t ts = static_cast<int64_t>(maybe_ts.value() * 1e9);
-
-  std::unordered_map<std::string, std::string> args;
-  for (base::StringSplitter ss(args_str.c_str(), ' '); ss.Next();) {
-    std::string key;
-    std::string value;
-    for (base::StringSplitter inner(ss.cur_token(), '='); inner.Next();) {
-      if (key.empty()) {
-        key = inner.cur_token();
-      } else {
-        value = inner.cur_token();
-      }
-    }
-    args.emplace(std::move(key), std::move(value));
-  }
-  if (event_name == "sched_switch") {
-    auto prev_state_str = args["prev_state"];
-    int64_t prev_state =
-        ftrace_utils::TaskState(prev_state_str.c_str()).raw_state();
-
-    auto prev_pid = base::StringToUInt32(args["prev_pid"]);
-    auto prev_comm = base::StringView(args["prev_comm"]);
-    auto prev_prio = base::StringToInt32(args["prev_prio"]);
-    auto next_pid = base::StringToUInt32(args["next_pid"]);
-    auto next_comm = base::StringView(args["next_comm"]);
-    auto next_prio = base::StringToInt32(args["next_prio"]);
-
-    if (!(prev_pid.has_value() && prev_prio.has_value() &&
-          next_pid.has_value() && next_prio.has_value())) {
-      return util::Status("Could not parse sched_switch");
-    }
-
-    SchedEventTracker::GetOrCreate(context_)->PushSchedSwitch(
-        cpu, ts, prev_pid.value(), prev_comm, prev_prio.value(), prev_state,
-        next_pid.value(), next_comm, next_prio.value());
-  } else if (event_name == "tracing_mark_write" || event_name == "0" ||
-             event_name == "print") {
-    SystraceParser::GetOrCreate(context_)->ParsePrintEvent(ts, pid,
-                                                           args_str.c_str());
-  } else if (event_name == "sched_wakeup") {
-    auto comm = args["comm"];
-    base::Optional<uint32_t> wakee_pid = base::StringToUInt32(args["pid"]);
-    if (!wakee_pid.has_value()) {
-      return util::Status("Could not convert wakee_pid");
-    }
-
-    StringId name_id = context_->storage->InternString(base::StringView(comm));
-    auto wakee_utid =
-        context_->process_tracker->UpdateThreadName(wakee_pid.value(), name_id);
-    context_->event_tracker->PushInstant(ts, sched_wakeup_name_id_, wakee_utid,
-                                         RefType::kRefUtid);
-  } else if (event_name == "cpu_idle") {
-    base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
-    base::Optional<double> new_state = base::StringToDouble(args["state"]);
-    if (!event_cpu.has_value()) {
-      return util::Status("Could not convert event cpu");
-    }
-    if (!event_cpu.has_value()) {
-      return util::Status("Could not convert state");
-    }
-
-    TrackId track = context_->track_tracker->InternCpuCounterTrack(
-        cpu_idle_name_id_, event_cpu.value());
-    context_->event_tracker->PushCounter(ts, new_state.value(), track);
-  }
-
-  return util::OkStatus();
-}
-
 void SystraceTraceParser::NotifyEndOfFile() {}
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.h b/src/trace_processor/importers/systrace/systrace_trace_parser.h
index f9aac13..7b3c834 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.h
@@ -21,6 +21,8 @@
 #include <regex>
 
 #include "src/trace_processor/chunked_trace_reader.h"
+#include "src/trace_processor/importers/systrace/systrace_line_parser.h"
+#include "src/trace_processor/importers/systrace/systrace_line_tokenizer.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_processor_context.h"
 
@@ -45,18 +47,14 @@
     kEndOfSystrace,
   };
 
-  util::Status ParseSingleSystraceEvent(const std::string& buffer);
-
-  TraceProcessorContext* const context_;
-  const StringId sched_wakeup_name_id_ = kNullStringId;
-  const StringId cpu_idle_name_id_ = kNullStringId;
-  const std::regex line_matcher_;
-
   ParseState state_ = ParseState::kBeforeParse;
 
   // Used to glue together trace packets that span across two (or more)
   // Parse() boundaries.
   std::deque<uint8_t> partial_buf_;
+
+  SystraceLineTokenizer line_tokenizer_;
+  SystraceLineParser line_parser_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index 3bce734..e775f1a 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -217,7 +217,9 @@
 }
 
 TEST_F(TraceProcessorIntegrationTest, Clusterfuzz17805) {
-  ASSERT_TRUE(LoadTrace("clusterfuzz_17805", 4096).ok());
+  // This trace fails to load as it's detected as a systrace but is full of
+  // garbage data.
+  ASSERT_TRUE(!LoadTrace("clusterfuzz_17805", 4096).ok());
 }
 
 TEST_F(TraceProcessorIntegrationTest, RestoreInitialTables) {