Parse task state based on kernel version

Bug:155987623
Change-Id: I3e64d9e473822626dd422235e7c7d81a15380bec
diff --git a/Android.bp b/Android.bp
index ccae72b..d1412eb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6543,6 +6543,7 @@
     "src/trace_processor/importers/common/global_args_tracker.cc",
     "src/trace_processor/importers/common/process_tracker.cc",
     "src/trace_processor/importers/common/slice_tracker.cc",
+    "src/trace_processor/importers/common/system_info_tracker.cc",
     "src/trace_processor/importers/common/track_tracker.cc",
   ],
 }
diff --git a/BUILD b/BUILD
index 578133a..7340a49 100644
--- a/BUILD
+++ b/BUILD
@@ -731,6 +731,8 @@
         "src/trace_processor/importers/common/process_tracker.h",
         "src/trace_processor/importers/common/slice_tracker.cc",
         "src/trace_processor/importers/common/slice_tracker.h",
+        "src/trace_processor/importers/common/system_info_tracker.cc",
+        "src/trace_processor/importers/common/system_info_tracker.h",
         "src/trace_processor/importers/common/track_tracker.cc",
         "src/trace_processor/importers/common/track_tracker.h",
     ],
@@ -881,6 +883,7 @@
         "src/trace_processor/types/trace_processor_context.h",
         "src/trace_processor/types/variadic.cc",
         "src/trace_processor/types/variadic.h",
+        "src/trace_processor/types/version_number.h",
     ],
 )
 
diff --git a/src/trace_processor/importers/BUILD.gn b/src/trace_processor/importers/BUILD.gn
index debece7..082f7bb 100644
--- a/src/trace_processor/importers/BUILD.gn
+++ b/src/trace_processor/importers/BUILD.gn
@@ -28,6 +28,8 @@
     "common/process_tracker.h",
     "common/slice_tracker.cc",
     "common/slice_tracker.h",
+    "common/system_info_tracker.cc",
+    "common/system_info_tracker.h",
     "common/track_tracker.cc",
     "common/track_tracker.h",
   ]
diff --git a/src/trace_processor/importers/common/system_info_tracker.cc b/src/trace_processor/importers/common/system_info_tracker.cc
new file mode 100644
index 0000000..aac30fd
--- /dev/null
+++ b/src/trace_processor/importers/common/system_info_tracker.cc
@@ -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.
+ */
+
+#include "src/trace_processor/importers/common/system_info_tracker.h"
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+SystemInfoTracker::SystemInfoTracker(TraceProcessorContext*) {}
+SystemInfoTracker::~SystemInfoTracker() = default;
+
+void SystemInfoTracker::SetKernelVersion(base::StringView name,
+                                         base::StringView release) {
+  if (name.empty() || release.empty() || name != "Linux") {
+    version_ = base::nullopt;
+    return;
+  }
+
+  size_t first_dot_pos = release.find(".");
+  size_t second_dot_pos = release.find(".", first_dot_pos + 1);
+  auto major_version =
+      base::StringToUInt32(release.substr(0, first_dot_pos).ToStdString());
+  auto minor_version = base::StringToUInt32(
+      release.substr(first_dot_pos + 1, second_dot_pos - (first_dot_pos + 1))
+          .ToStdString());
+  version_ = VersionNumber{major_version.value(), minor_version.value()};
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/common/system_info_tracker.h b/src/trace_processor/importers/common/system_info_tracker.h
new file mode 100644
index 0000000..1295db6
--- /dev/null
+++ b/src/trace_processor/importers/common/system_info_tracker.h
@@ -0,0 +1,55 @@
+/*
+ * 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_COMMON_SYSTEM_INFO_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SYSTEM_INFO_TRACKER_H_
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/types/version_number.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class SystemInfoTracker : public Destructible {
+ public:
+  ~SystemInfoTracker() override;
+
+  SystemInfoTracker(const SystemInfoTracker&) = delete;
+  SystemInfoTracker& operator=(const SystemInfoTracker&) = delete;
+
+  static SystemInfoTracker* GetOrCreate(TraceProcessorContext* context) {
+    if (!context->system_info_tracker) {
+      context->system_info_tracker.reset(new SystemInfoTracker(context));
+    }
+    return static_cast<SystemInfoTracker*>(context->system_info_tracker.get());
+  }
+
+  void SetKernelVersion(base::StringView name, base::StringView release);
+
+  base::Optional<VersionNumber> GetKernelVersion() { return version_; }
+
+ private:
+  explicit SystemInfoTracker(TraceProcessorContext*);
+
+  base::Optional<VersionNumber> version_;
+};
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SYSTEM_INFO_TRACKER_H_
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
index 111c091..5a43d54 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -22,6 +22,7 @@
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/system_info_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
 #include "src/trace_processor/storage/stats.h"
 #include "src/trace_processor/types/task_state.h"
@@ -239,7 +240,10 @@
   // We store the state as a uint16 as we only consider values up to 2048
   // when unpacking the information inside; this allows savings of 48 bits
   // per slice.
-  auto task_state = ftrace_utils::TaskState(static_cast<uint16_t>(prev_state));
+  auto kernel_version =
+      SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
+  auto task_state = ftrace_utils::TaskState(static_cast<uint16_t>(prev_state),
+                                            kernel_version);
   if (!task_state.is_valid()) {
     context_->storage->IncrementStats(stats::task_state_invalid);
   }
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 6ca53a4..57b7d5c 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -23,6 +23,7 @@
 #include "perfetto/protozero/proto_decoder.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/system_info_tracker.h"
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
 #include "src/trace_processor/importers/syscalls/syscall_tracker.h"
 #include "src/trace_processor/storage/metadata.h"
@@ -361,6 +362,10 @@
       PERFETTO_ELOG("Unknown architecture %s", machine.ToStdString().c_str());
     }
 
+    SystemInfoTracker* system_info_tracker =
+        SystemInfoTracker::GetOrCreate(context_);
+    system_info_tracker->SetKernelVersion(utsname.sysname(), utsname.release());
+
     StringPool::Id sysname_id =
         context_->storage->InternString(utsname.sysname());
     StringPool::Id version_id =
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index e983510..f2f4bb2 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -48,6 +48,7 @@
       "../../../protos/perfetto/trace/ftrace:zero",
       "../../base",
       "../db:lib",
+      "../importers:common",
       "../storage",
       "../types",
     ]
diff --git a/src/trace_processor/sqlite/sqlite_raw_table.cc b/src/trace_processor/sqlite/sqlite_raw_table.cc
index ee7d33e..e13c3af 100644
--- a/src/trace_processor/sqlite/sqlite_raw_table.cc
+++ b/src/trace_processor/sqlite/sqlite_raw_table.cc
@@ -20,6 +20,7 @@
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/common/system_info_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 #include "src/trace_processor/types/gfp_flags.h"
@@ -39,18 +40,6 @@
 namespace trace_processor {
 
 namespace {
-std::tuple<uint32_t, uint32_t> ParseKernelReleaseVersion(
-    base::StringView system_release) {
-  size_t first_dot_pos = system_release.find(".");
-  size_t second_dot_pos = system_release.find(".", first_dot_pos + 1);
-  auto major_version = base::StringToUInt32(
-      system_release.substr(0, first_dot_pos).ToStdString());
-  auto minor_version = base::StringToUInt32(
-      system_release
-          .substr(first_dot_pos + 1, second_dot_pos - (first_dot_pos + 1))
-          .ToStdString());
-  return std::make_tuple(major_version.value(), minor_version.value());
-}
 
 struct FtraceTime {
   FtraceTime(int64_t ns)
@@ -62,7 +51,7 @@
 
 class ArgsSerializer {
  public:
-  ArgsSerializer(const TraceStorage*,
+  ArgsSerializer(TraceProcessorContext*,
                  ArgSetId arg_set_id,
                  NullTermStringView event_name,
                  std::vector<uint32_t>* field_id_to_arg_index,
@@ -100,8 +89,6 @@
 
   void WriteValue(const Variadic& variadic);
 
-  bool ParseGfpFlags(Variadic value);
-
   uint32_t FieldIdToRow(uint32_t field_id) {
     PERFETTO_DCHECK(field_id > 0);
     PERFETTO_DCHECK(field_id < field_id_to_arg_index_->size());
@@ -110,6 +97,7 @@
   }
 
   const TraceStorage* storage_ = nullptr;
+  TraceProcessorContext* context_ = nullptr;
   ArgSetId arg_set_id_ = kInvalidArgSetId;
   NullTermStringView event_name_;
   std::vector<uint32_t>* field_id_to_arg_index_;
@@ -120,16 +108,17 @@
   base::StringWriter* writer_ = nullptr;
 };
 
-ArgsSerializer::ArgsSerializer(const TraceStorage* storage,
+ArgsSerializer::ArgsSerializer(TraceProcessorContext* context,
                                ArgSetId arg_set_id,
                                NullTermStringView event_name,
                                std::vector<uint32_t>* field_id_to_arg_index,
                                base::StringWriter* writer)
-    : storage_(storage),
+    : context_(context),
       arg_set_id_(arg_set_id),
       event_name_(event_name),
       field_id_to_arg_index_(field_id_to_arg_index),
       writer_(writer) {
+  storage_ = context_->storage.get();
   const auto& args = storage_->arg_table();
   const auto& set_ids = args.arg_set_id();
 
@@ -185,8 +174,10 @@
     WriteArgForField(SS::kPrevStateFieldNumber, [this](const Variadic& value) {
       PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
       auto state = static_cast<uint16_t>(value.int_value);
+      auto kernel_version =
+          SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
       writer_->AppendString(
-          ftrace_utils::TaskState(state).ToString('|').data());
+          ftrace_utils::TaskState(state, kernel_version).ToString('|').data());
     });
     writer_->AppendLiteral(" ==>");
     WriteArgForField(SS::kNextCommFieldNumber);
@@ -400,8 +391,12 @@
   writer_->AppendString(key.c_str(), key.size());
   writer_->AppendChar('=');
 
-  if (key == "gfp_flags" && ParseGfpFlags(value))
+  if (key == "gfp_flags") {
+    auto kernel_version =
+        SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
+    WriteGfpFlag(value.uint_value, kernel_version, writer_);
     return;
+  }
   writer(value);
 }
 
@@ -435,36 +430,14 @@
   }
 }
 
-bool ArgsSerializer::ParseGfpFlags(Variadic value) {
-  const auto& metadata_table = storage_->metadata_table();
-
-  auto opt_name_idx = metadata_table.name().IndexOf(
-      metadata::kNames[metadata::KeyIDs::system_name]);
-  auto opt_release_idx = metadata_table.name().IndexOf(
-      metadata::kNames[metadata::KeyIDs::system_release]);
-  if (!opt_name_idx || !opt_release_idx)
-    return false;
-
-  const auto& str_value = metadata_table.str_value();
-  base::StringView system_name = str_value.GetString(*opt_name_idx);
-  if (system_name != "Linux")
-    return false;
-
-  base::StringView system_release = str_value.GetString(*opt_release_idx);
-  auto version = ParseKernelReleaseVersion(system_release);
-
-  WriteGfpFlag(value.uint_value, version, writer_);
-  return true;
-}
-
 }  // namespace
 
 SqliteRawTable::SqliteRawTable(sqlite3* db, Context context)
     : DbSqliteTable(
           db,
           {context.cache, tables::RawTable::Schema(), TableComputation::kStatic,
-           &context.storage->raw_table(), nullptr}),
-      serializer_(context.storage) {
+           &context.context->storage->raw_table(), nullptr}),
+      serializer_(context.context) {
   auto fn = [](sqlite3_context* ctx, int argc, sqlite3_value** argv) {
     auto* thiz = static_cast<SqliteRawTable*>(sqlite3_user_data(ctx));
     thiz->ToSystrace(ctx, argc, argv);
@@ -478,8 +451,8 @@
 
 void SqliteRawTable::RegisterTable(sqlite3* db,
                                    QueryCache* cache,
-                                   const TraceStorage* storage) {
-  SqliteTable::Register<SqliteRawTable, Context>(db, Context{cache, storage},
+                                   TraceProcessorContext* context) {
+  SqliteTable::Register<SqliteRawTable, Context>(db, Context{cache, context},
                                                  "raw");
 }
 
@@ -496,8 +469,10 @@
   sqlite3_result_text(ctx, str.release(), -1, free);
 }
 
-SystraceSerializer::SystraceSerializer(const TraceStorage* storage)
-    : storage_(storage) {}
+SystraceSerializer::SystraceSerializer(TraceProcessorContext* context)
+    : context_(context) {
+  storage_ = context_->storage.get();
+}
 
 SystraceSerializer::ScopedCString SystraceSerializer::SerializeToString(
     uint32_t raw_row) {
@@ -518,7 +493,7 @@
   }
   writer.AppendChar(':');
 
-  ArgsSerializer serializer(storage_, raw.arg_set_id()[raw_row], event_name,
+  ArgsSerializer serializer(context_, raw.arg_set_id()[raw_row], event_name,
                             &proto_id_to_arg_index_by_event_[event_name_id],
                             &writer);
   serializer.SerializeArgs();
diff --git a/src/trace_processor/sqlite/sqlite_raw_table.h b/src/trace_processor/sqlite/sqlite_raw_table.h
index 4c8190e..2e6d774 100644
--- a/src/trace_processor/sqlite/sqlite_raw_table.h
+++ b/src/trace_processor/sqlite/sqlite_raw_table.h
@@ -21,6 +21,7 @@
 #include "perfetto/ext/base/string_writer.h"
 #include "src/trace_processor/sqlite/db_sqlite_table.h"
 #include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/types/variadic.h"
 
 namespace perfetto {
@@ -30,7 +31,7 @@
  public:
   using ScopedCString = std::unique_ptr<char, void (*)(void*)>;
 
-  SystraceSerializer(const TraceStorage* storage);
+  SystraceSerializer(TraceProcessorContext* context);
 
   ScopedCString SerializeToString(uint32_t raw_row);
 
@@ -41,19 +42,20 @@
 
   StringIdMap proto_id_to_arg_index_by_event_;
   const TraceStorage* storage_ = nullptr;
+  TraceProcessorContext* context_ = nullptr;
 };
 
 class SqliteRawTable : public DbSqliteTable {
  public:
   struct Context {
     QueryCache* cache;
-    const TraceStorage* storage;
+    TraceProcessorContext* context;
   };
 
   SqliteRawTable(sqlite3*, Context);
   virtual ~SqliteRawTable();
 
-  static void RegisterTable(sqlite3* db, QueryCache*, const TraceStorage*);
+  static void RegisterTable(sqlite3* db, QueryCache*, TraceProcessorContext*);
 
  private:
   void ToSystrace(sqlite3_context* ctx, int argc, sqlite3_value** argv);
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index ea0764c..f3a97bc 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -558,8 +558,7 @@
   WindowOperatorTable::RegisterTable(*db_, storage);
 
   // New style tables but with some custom logic.
-  SqliteRawTable::RegisterTable(*db_, query_cache_.get(),
-                                context_.storage.get());
+  SqliteRawTable::RegisterTable(*db_, query_cache_.get(), &context_);
 
   // Tables dynamically generated at query time.
   RegisterDynamicTable(std::unique_ptr<ExperimentalFlamegraphGenerator>(
diff --git a/src/trace_processor/types/BUILD.gn b/src/trace_processor/types/BUILD.gn
index cb719b5..a2a3a97 100644
--- a/src/trace_processor/types/BUILD.gn
+++ b/src/trace_processor/types/BUILD.gn
@@ -23,6 +23,7 @@
     "trace_processor_context.h",
     "variadic.cc",
     "variadic.h",
+    "version_number.h",
   ]
   deps = [
     "../../../gn:default_deps",
diff --git a/src/trace_processor/types/gfp_flags.cc b/src/trace_processor/types/gfp_flags.cc
index 74f7511..30a5932 100644
--- a/src/trace_processor/types/gfp_flags.cc
+++ b/src/trace_processor/types/gfp_flags.cc
@@ -199,14 +199,13 @@
 
 // Get the bitmask closest to the kernel version. For versions less than 3.4
 // and greater than 4.14 this may end up being inaccurate.
-const FlagArray* GetBitmaskVersion(std::tuple<int32_t, int32_t> version) {
-  if (version < std::make_tuple(3, 10)) {
+const FlagArray* GetBitmaskVersion(VersionNumber version = VersionNumber{4,
+                                                                         4}) {
+  if (version < VersionNumber{3, 10}) {
     return &v3_4;
-  } else if (version >= std::make_tuple(3, 10) &&
-             version < std::make_tuple(4, 4)) {
+  } else if (version >= VersionNumber{3, 10} && version < VersionNumber{4, 4}) {
     return &v3_10;
-  } else if (version >= std::make_tuple(4, 4) &&
-             version < std::make_tuple(4, 14)) {
+  } else if (version >= VersionNumber{4, 4} && version < VersionNumber{4, 14}) {
     return &v4_4;
   } else {  // version >= 4.14
     // TODO(taylori): Add newer kernel versions once we have access to them.
@@ -216,14 +215,18 @@
 }  // namespace
 
 void WriteGfpFlag(uint64_t value,
-                  std::tuple<uint32_t, uint32_t> version,
+                  base::Optional<VersionNumber> version,
                   base::StringWriter* writer) {
   // On all kernel versions if this flag is not set, return GFP_NOWAIT.
-  if (value == 0)
+  if (value == 0) {
     writer->AppendString("GFP_NOWAIT");
+    return;
+  }
 
   std::string result;
-  const FlagArray* bitmasks = GetBitmaskVersion(version);
+  const FlagArray* bitmasks = version.has_value()
+                                  ? GetBitmaskVersion(version.value())
+                                  : GetBitmaskVersion();
 
   // Based on trace_print_flags_seq() in the kernel.
   size_t i = 0;
diff --git a/src/trace_processor/types/gfp_flags.h b/src/trace_processor/types/gfp_flags.h
index 837b284..fb0ae29 100644
--- a/src/trace_processor/types/gfp_flags.h
+++ b/src/trace_processor/types/gfp_flags.h
@@ -17,8 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_TYPES_GFP_FLAGS_H_
 #define SRC_TRACE_PROCESSOR_TYPES_GFP_FLAGS_H_
 
-#include <tuple>
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_writer.h"
+#include "src/trace_processor/types/version_number.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -27,7 +28,7 @@
 // the kernel version. This function writes a human readable version of the
 // flag.
 void WriteGfpFlag(uint64_t value,
-                  std::tuple<uint32_t, uint32_t> version,
+                  base::Optional<VersionNumber> version,
                   base::StringWriter* writer);
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/types/task_state.cc b/src/trace_processor/types/task_state.cc
index 0c8193b..30a23f4 100644
--- a/src/trace_processor/types/task_state.cc
+++ b/src/trace_processor/types/task_state.cc
@@ -26,11 +26,38 @@
 namespace trace_processor {
 namespace ftrace_utils {
 
-TaskState::TaskState(uint16_t raw_state) {
-  if (raw_state > kMaxState) {
+TaskState::TaskState(uint16_t raw_state,
+                     base::Optional<VersionNumber> opt_version) {
+  auto version = VersionNumber{4, 4};
+  if (opt_version) {
+    version = opt_version.value();
+  }
+  max_state_ = version < VersionNumber{4, 9} ? 2048 : 4096;
+
+  if (raw_state > max_state_) {
     state_ = 0;
   } else {
-    state_ = raw_state | kValid;
+    state_ |= kValid;
+  }
+
+  if (version < VersionNumber{4, 14}) {
+    state_ |= raw_state;
+    return;
+  }
+  // All values below kTaskDead are consistent between kernels.
+  state_ |= raw_state & (kTaskDead - 1);
+
+  // Only values up to 0x80 (plus max_state) are relevant in kernels >= 4.14.
+  // See
+  // https://android.googlesource.com/kernel/msm.git/+/refs/heads/android-msm-coral-4.14-android10-qpr1/include/trace/events/sched.h#219
+  if (raw_state & 0x40) {
+    state_ |= kParked;
+  }
+  if (raw_state & 0x80) {
+    state_ |= kTaskDead;
+  }
+  if (raw_state & max_state_) {
+    state_ |= max_state_;
   }
 }
 
@@ -44,7 +71,7 @@
       invalid_char = true;
       break;
     } else if (c == '+') {
-      state_ |= kMaxState;
+      state_ |= max_state_;
       continue;
     }
 
@@ -77,7 +104,7 @@
       state_ |= Atom::kExitDead;
     else if (c == 'Z')
       state_ |= Atom::kExitZombie;
-    else if (c == 'x')
+    else if (c == 'x' || c == 'I')
       state_ |= Atom::kTaskDead;
     else if (c == 'K')
       state_ |= Atom::kWakeKill;
@@ -94,9 +121,8 @@
       break;
     }
   }
-
   bool no_state = !is_runnable && state_ == 0;
-  if (invalid_char || no_state || state_ > kMaxState) {
+  if (invalid_char || no_state || state_ > max_state_) {
     state_ = 0;
   } else {
     state_ |= kValid;
@@ -113,6 +139,7 @@
 
   // This mapping is given by the file
   // https://android.googlesource.com/kernel/msm.git/+/android-msm-wahoo-4.4-pie-qpr1/include/trace/events/sched.h#155
+  // Some of these flags are ignored in later kernels but we output them anyway.
   if (is_runnable()) {
     buffer[pos++] = 'R';
   } else {
diff --git a/src/trace_processor/types/task_state.h b/src/trace_processor/types/task_state.h
index 0b79277..b078951 100644
--- a/src/trace_processor/types/task_state.h
+++ b/src/trace_processor/types/task_state.h
@@ -19,10 +19,13 @@
 
 #include <stddef.h>
 #include <array>
+#include <utility>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/string_writer.h"
+#include "src/trace_processor/types/version_number.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -49,49 +52,43 @@
     kWaking = 256,
     kParked = 512,
     kNoLoad = 1024,
+    // This was added in kernel v4.9 but is never used.
+    kTaskNew = 2048,
 
-    // kMaxState is used by sched switch to show a task was kernel preempted.
-    // The difficult thing is that in newer kernels a task_new state is added
-    // and kMaxState increases to 4096. Without the kernel version and the
-    // mapping of this to the correct version of this enum we cannot be
-    // accurate. Since task_new is rare we ignore it for now.
-    kTaskNewOrMaxState = 2048,
-    kMaxState = 4096,
     kValid = 0x8000,
   };
 
   TaskState() = default;
-  explicit TaskState(uint16_t raw_state);
+  explicit TaskState(uint16_t raw_state,
+                     base::Optional<VersionNumber> = VersionNumber{4, 4});
   explicit TaskState(const char* state_str);
 
   // Returns if this TaskState has a valid representation.
   bool is_valid() const { return state_ & kValid; }
 
   // Returns the string representation of this (valid) TaskState. This array
-  // is null terminated. |seperator| specifies if a separator should be printed
+  // is null terminated. |separator| specifies if a separator should be printed
   // between the atoms (default: \0 meaning no separator).
   // Note: This function CHECKs that |is_valid()| is true.
   TaskStateStr ToString(char separator = '\0') const;
 
-  // Returns the raw state this class was created from.
+  // Returns the raw state this class can be recreated from.
   uint16_t raw_state() const {
     PERFETTO_DCHECK(is_valid());
     return state_ & ~kValid;
   }
 
   // Returns if this TaskState is runnable.
-  bool is_runnable() const {
-    return ((state_ & (kMaxState - 1)) == 0) ||
-           ((state_ & (kTaskNewOrMaxState - 1)) == 0);
-  }
+  bool is_runnable() const { return ((state_ & (max_state_ - 1)) == 0); }
 
   // Returns whether kernel preemption caused the exit state.
-  bool is_kernel_preempt() const {
-    return state_ & kTaskNewOrMaxState || state_ & kMaxState;
-  }
+  bool is_kernel_preempt() const { return state_ & max_state_; }
 
  private:
+  // One of Atom - based on given raw_state and version. Will have isValid set
+  // if valid.
   uint16_t state_ = 0;
+  uint16_t max_state_ = 2048;
 };
 
 }  // namespace ftrace_utils
diff --git a/src/trace_processor/types/task_state_unittests.cc b/src/trace_processor/types/task_state_unittests.cc
index a6037ac..09f44af 100644
--- a/src/trace_processor/types/task_state_unittests.cc
+++ b/src/trace_processor/types/task_state_unittests.cc
@@ -49,8 +49,6 @@
 }
 
 TEST(TaskStateUnittest, MultipleState) {
-  ASSERT_STREQ(TaskState(2048).ToString().data(), "R+");
-  ASSERT_STREQ(TaskState(4096).ToString().data(), "R+");
   ASSERT_STREQ(TaskState(130).ToString().data(), "DK");
   ASSERT_STREQ(TaskState(258).ToString().data(), "DW");
 
@@ -58,6 +56,35 @@
   ASSERT_EQ(TaskState("D|W").raw_state(), 258);
 }
 
+TEST(TaskStateUnittest, KernelVersion) {
+  auto state = TaskState(static_cast<uint16_t>(0u), VersionNumber{4, 14});
+  ASSERT_TRUE(state.is_valid());
+
+  ASSERT_STREQ(state.ToString().data(), "R");
+  ASSERT_STREQ(TaskState(1, VersionNumber{4, 14}).ToString().data(), "S");
+  ASSERT_STREQ(TaskState(2, VersionNumber{4, 14}).ToString().data(), "D");
+  ASSERT_STREQ(TaskState(4, VersionNumber{4, 14}).ToString().data(), "T");
+  ASSERT_STREQ(TaskState(8, VersionNumber{4, 14}).ToString().data(), "t");
+  ASSERT_STREQ(TaskState(16, VersionNumber{4, 14}).ToString().data(), "X");
+  ASSERT_STREQ(TaskState(32, VersionNumber{4, 14}).ToString().data(), "Z");
+  ASSERT_STREQ(TaskState(64, VersionNumber{4, 14}).ToString().data(), "P");
+  ASSERT_STREQ(TaskState(128, VersionNumber{4, 14}).ToString().data(), "x");
+
+  // Any without a specific state but less than max are runnable in this kernel.
+  ASSERT_STREQ(TaskState(256, VersionNumber{4, 14}).ToString().data(), "R");
+  ASSERT_STREQ(TaskState(512, VersionNumber{4, 14}).ToString().data(), "R");
+  ASSERT_STREQ(TaskState(1024, VersionNumber{4, 14}).ToString().data(), "R");
+  ASSERT_STREQ(TaskState(2048, VersionNumber{4, 14}).ToString().data(), "R");
+}
+
+TEST(TaskStateUnittest, MaxValueKernelVersion) {
+  // Max value means pre-empted but is different for each kernel version.
+  ASSERT_STREQ(TaskState(2048).ToString().data(), "R+");
+  ASSERT_STREQ(TaskState(2048, VersionNumber{4, 8}).ToString().data(), "R+");
+  ASSERT_STREQ(TaskState(4096, VersionNumber{4, 14}).ToString().data(), "R+");
+  ASSERT_STREQ(TaskState(4096, VersionNumber{4, 19}).ToString().data(), "R+");
+}
+
 }  // namespace
 }  // namespace ftrace_utils
 }  // namespace trace_processor
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 8801cd3..2f188ad 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -86,6 +86,7 @@
   std::unique_ptr<Destructible> systrace_parser;         // SystraceParser
   std::unique_ptr<Destructible> heap_graph_tracker;      // HeapGraphTracker
   std::unique_ptr<Destructible> json_tracker;            // JsonTracker
+  std::unique_ptr<Destructible> system_info_tracker;     // SystemInfoTracker
 
   // These fields are trace readers which will be called by |forwarding_parser|
   // once the format of the trace is discovered. They are placed here as they
diff --git a/src/trace_processor/types/version_number.h b/src/trace_processor/types/version_number.h
new file mode 100644
index 0000000..9119cea
--- /dev/null
+++ b/src/trace_processor/types/version_number.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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_TYPES_VERSION_NUMBER_H_
+#define SRC_TRACE_PROCESSOR_TYPES_VERSION_NUMBER_H_
+
+#include <tuple>
+
+namespace perfetto {
+namespace trace_processor {
+
+struct VersionNumber {
+  uint32_t major;
+  uint32_t minor;
+
+  bool operator<(const VersionNumber& other) {
+    return std::tie(major, minor) < std::tie(other.major, other.minor);
+  }
+  bool operator>=(const VersionNumber& other) {
+    return std::tie(major, minor) >= std::tie(other.major, other.minor);
+  }
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_TYPES_VERSION_NUMBER_H_
diff --git a/test/trace_processor/print_systrace_unsigned.out b/test/trace_processor/print_systrace_unsigned.out
index e0acbcc..43dbb63 100644
--- a/test/trace_processor/print_systrace_unsigned.out
+++ b/test/trace_processor/print_systrace_unsigned.out
@@ -1,4 +1,4 @@
 "to_ftrace(id)"
 "       <unknown>-10    (   10) [000] .... 0.000000: kfree: call_site=16 ptr=32"
 "       <unknown>-10    (   10) [000] .... 0.000000: kfree: call_site=18446744073709551600 ptr=18446744073709551584"
-"       <unknown>-10    (   10) [000] .... 0.000000: kmalloc: gfp_flags=0 call_site=18446744073709551600 ptr=18446744073709551584 bytes_alloc=32 bytes_req=16"
+"       <unknown>-10    (   10) [000] .... 0.000000: kmalloc: gfp_flags=GFP_NOWAIT call_site=18446744073709551600 ptr=18446744073709551584 bytes_alloc=32 bytes_req=16"