Parse the gfp_flags field based on kernel version

Added the system info to the metadata table to access it later.

Test: ?s=85868ac21895a821afcd72abe40b76ae5248cc1cd5429559a553519f115a7
Query: select to_ftrace(id) from raw

Bug:117966147
Change-Id: Id942c4e410783c42529634309e6970b7d29ceaae
diff --git a/Android.bp b/Android.bp
index 44db57c..be293bf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -4556,6 +4556,7 @@
     "src/trace_processor/counter_values_table.cc",
     "src/trace_processor/cpu_profile_stack_sample_table.cc",
     "src/trace_processor/filtered_row_index.cc",
+    "src/trace_processor/gfp_flags.cc",
     "src/trace_processor/heap_profile_allocation_table.cc",
     "src/trace_processor/instants_table.cc",
     "src/trace_processor/metadata_table.cc",
diff --git a/BUILD b/BUILD
index 57831e6..f17f2dd 100644
--- a/BUILD
+++ b/BUILD
@@ -685,6 +685,8 @@
         "src/trace_processor/cpu_profile_stack_sample_table.h",
         "src/trace_processor/filtered_row_index.cc",
         "src/trace_processor/filtered_row_index.h",
+        "src/trace_processor/gfp_flags.cc",
+        "src/trace_processor/gfp_flags.h",
         "src/trace_processor/heap_profile_allocation_table.cc",
         "src/trace_processor/heap_profile_allocation_table.h",
         "src/trace_processor/instants_table.cc",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index a096cd2..0cd6ff5 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -242,6 +242,8 @@
       "cpu_profile_stack_sample_table.h",
       "filtered_row_index.cc",
       "filtered_row_index.h",
+      "gfp_flags.cc",
+      "gfp_flags.h",
       "heap_profile_allocation_table.cc",
       "heap_profile_allocation_table.h",
       "instants_table.cc",
diff --git a/src/trace_processor/gfp_flags.cc b/src/trace_processor/gfp_flags.cc
new file mode 100644
index 0000000..2d5b587
--- /dev/null
+++ b/src/trace_processor/gfp_flags.cc
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+
+#include "src/trace_processor/gfp_flags.h"
+#include <array>
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+
+struct Flag {
+  uint64_t mask;
+  const char* flag_name;
+};
+
+using FlagArray = std::array<Flag, 37>;
+
+constexpr FlagArray v3_4 = {
+    {{(((0x10u) | (0x40u) | (0x80u) | (0x20000u) | (0x02u) | (0x08u)) |
+       (0x4000u) | (0x10000u) | (0x1000u) | (0x200u) | (0x400000u)),
+      "GFP_TRANSHUGE"},
+     {((0x10u) | (0x40u) | (0x80u) | (0x20000u) | (0x02u) | (0x08u)),
+      "GFP_HIGHUSER_MOVABLE"},
+     {((0x10u) | (0x40u) | (0x80u) | (0x20000u) | (0x02u)), "GFP_HIGHUSER"},
+     {((0x10u) | (0x40u) | (0x80u) | (0x20000u)), "GFP_USER"},
+     {((0x10u) | (0x40u) | (0x80u) | (0x80000u)), "GFP_TEMPORARY"},
+     {((0x10u) | (0x40u) | (0x80u)), "GFP_KERNEL"},
+     {((0x10u) | (0x40u)), "GFP_NOFS"},
+     {((0x20u)), "GFP_ATOMIC"},
+     {((0x10u)), "GFP_NOIO"},
+     {(0x20u), "GFP_HIGH"},
+     {(0x10u), "GFP_WAIT"},
+     {(0x40u), "GFP_IO"},
+     {(0x100u), "GFP_COLD"},
+     {(0x200u), "GFP_NOWARN"},
+     {(0x400u), "GFP_REPEAT"},
+     {(0x800u), "GFP_NOFAIL"},
+     {(0x1000u), "GFP_NORETRY"},
+     {(0x4000u), "GFP_COMP"},
+     {(0x8000u), "GFP_ZERO"},
+     {(0x10000u), "GFP_NOMEMALLOC"},
+     {(0x20000u), "GFP_HARDWALL"},
+     {(0x40000u), "GFP_THISNODE"},
+     {(0x80000u), "GFP_RECLAIMABLE"},
+     {(0x08u), "GFP_MOVABLE"},
+     {(0), "GFP_NOTRACK"},
+     {(0x400000u), "GFP_NO_KSWAPD"},
+     {(0x800000u), "GFP_OTHER_NODE"},
+     {0, nullptr}}};
+
+constexpr FlagArray v3_10 = {
+    {{(((0x10u) | (0x40u) | (0x80u) | (0x20000u) | (0x02u) | (0x08u)) |
+       (0x4000u) | (0x10000u) | (0x1000u) | (0x200u) | (0x400000u)),
+      "GFP_TRANSHUGE"},
+     {((0x10u) | (0x40u) | (0x80u) | (0x20000u) | (0x02u) | (0x08u)),
+      "GFP_HIGHUSER_MOVABLE"},
+     {((0x10u) | (0x40u) | (0x80u) | (0x20000u) | (0x02u)), "GFP_HIGHUSER"},
+     {((0x10u) | (0x40u) | (0x80u) | (0x20000u)), "GFP_USER"},
+     {((0x10u) | (0x40u) | (0x80u) | (0x80000u)), "GFP_TEMPORARY"},
+     {((0x10u) | (0x40u) | (0x80u)), "GFP_KERNEL"},
+     {((0x10u) | (0x40u)), "GFP_NOFS"},
+     {((0x20u)), "GFP_ATOMIC"},
+     {((0x10u)), "GFP_NOIO"},
+     {(0x20u), "GFP_HIGH"},
+     {(0x10u), "GFP_WAIT"},
+     {(0x40u), "GFP_IO"},
+     {(0x100u), "GFP_COLD"},
+     {(0x200u), "GFP_NOWARN"},
+     {(0x400u), "GFP_REPEAT"},
+     {(0x800u), "GFP_NOFAIL"},
+     {(0x1000u), "GFP_NORETRY"},
+     {(0x4000u), "GFP_COMP"},
+     {(0x8000u), "GFP_ZERO"},
+     {(0x10000u), "GFP_NOMEMALLOC"},
+     {(0x2000u), "GFP_MEMALLOC"},
+     {(0x20000u), "GFP_HARDWALL"},
+     {(0x40000u), "GFP_THISNODE"},
+     {(0x80000u), "GFP_RECLAIMABLE"},
+     {(0x100000u), "GFP_KMEMCG"},
+     {(0x08u), "GFP_MOVABLE"},
+     {(0x200000u), "GFP_NOTRACK"},
+     {(0x400000u), "GFP_NO_KSWAPD"},
+     {(0x800000u), "GFP_OTHER_NODE"},
+     {0, nullptr}}};
+
+constexpr FlagArray v4_4 = {
+    {{(((((((0x400000u | 0x2000000u)) | (0x40u) | (0x80u) | (0x20000u)) |
+          (0x02u)) |
+         (0x08u)) |
+        (0x4000u) | (0x10000u) | (0x1000u) | (0x200u)) &
+       ~(0x2000000u)),
+      "GFP_TRANSHUGE"},
+     {(((((0x400000u | 0x2000000u)) | (0x40u) | (0x80u) | (0x20000u)) |
+        (0x02u)) |
+       (0x08u)),
+      "GFP_HIGHUSER_MOVABLE"},
+     {((((0x400000u | 0x2000000u)) | (0x40u) | (0x80u) | (0x20000u)) | (0x02u)),
+      "GFP_HIGHUSER"},
+     {(((0x400000u | 0x2000000u)) | (0x40u) | (0x80u) | (0x20000u)),
+      "GFP_USER"},
+     {(((0x400000u | 0x2000000u)) | (0x40u) | (0x80u) | (0x10u)),
+      "GFP_TEMPORARY"},
+     {(((0x400000u | 0x2000000u)) | (0x40u) | (0x80u)), "GFP_KERNEL"},
+     {(((0x400000u | 0x2000000u)) | (0x40u)), "GFP_NOFS"},
+     {((0x20u) | (0x80000u) | (0x2000000u)), "GFP_ATOMIC"},
+     {(((0x400000u | 0x2000000u))), "GFP_NOIO"},
+     {(0x20u), "GFP_HIGH"},
+     {(0x80000u), "GFP_ATOMIC"},
+     {(0x40u), "GFP_IO"},
+     {(0x100u), "GFP_COLD"},
+     {(0x200u), "GFP_NOWARN"},
+     {(0x400u), "GFP_REPEAT"},
+     {(0x800u), "GFP_NOFAIL"},
+     {(0x1000u), "GFP_NORETRY"},
+     {(0x4000u), "GFP_COMP"},
+     {(0x8000u), "GFP_ZERO"},
+     {(0x10000u), "GFP_NOMEMALLOC"},
+     {(0x2000u), "GFP_MEMALLOC"},
+     {(0x20000u), "GFP_HARDWALL"},
+     {(0x40000u), "GFP_THISNODE"},
+     {(0x10u), "GFP_RECLAIMABLE"},
+     {(0x08u), "GFP_MOVABLE"},
+     {(0x200000u), "GFP_NOTRACK"},
+     {(0x400000u), "GFP_DIRECT_RECLAIM"},
+     {(0x2000000u), "GFP_KSWAPD_RECLAIM"},
+     {(0x800000u), "GFP_OTHER_NODE"},
+     {0, nullptr}}};
+
+constexpr FlagArray v4_14 = {
+    {{((((((((0x400000u | 0x1000000u)) | (0x40u) | (0x80u) | (0x20000u)) |
+           (0x02u)) |
+          (0x08u)) |
+         (0x4000u) | (0x10000u) | (0x200u)) &
+        ~((0x400000u | 0x1000000u))) |
+       (0x400000u)),
+      "GFP_TRANSHUGE"},
+     {(((((((0x400000u | 0x1000000u)) | (0x40u) | (0x80u) | (0x20000u)) |
+          (0x02u)) |
+         (0x08u)) |
+        (0x4000u) | (0x10000u) | (0x200u)) &
+       ~((0x400000u | 0x1000000u))),
+      "GFP_TRANSHUGE_LIGHT"},
+     {(((((0x400000u | 0x1000000u)) | (0x40u) | (0x80u) | (0x20000u)) |
+        (0x02u)) |
+       (0x08u)),
+      "GFP_HIGHUSER_MOVABLE"},
+     {((((0x400000u | 0x1000000u)) | (0x40u) | (0x80u) | (0x20000u)) | (0x02u)),
+      "GFP_HIGHUSER"},
+     {(((0x400000u | 0x1000000u)) | (0x40u) | (0x80u) | (0x20000u)),
+      "GFP_USER"},
+     {((((0x400000u | 0x1000000u)) | (0x40u) | (0x80u)) | (0x100000u)),
+      "GFP_KERNEL_ACCOUNT"},
+     {(((0x400000u | 0x1000000u)) | (0x40u) | (0x80u)), "GFP_KERNEL"},
+     {(((0x400000u | 0x1000000u)) | (0x40u)), "GFP_NOFS"},
+     {((0x20u) | (0x80000u) | (0x1000000u)), "GFP_ATOMIC"},
+     {(((0x400000u | 0x1000000u))), "GFP_NOIO"},
+     {((0x1000000u)), "GFP_NOWAIT"},
+     {(0x01u), "GFP_DMA"},
+     {(0x02u), "__GFP_HIGHMEM"},
+     {(0x04u), "GFP_DMA32"},
+     {(0x20u), "__GFP_HIGH"},
+     {(0x80000u), "__GFP_ATOMIC"},
+     {(0x40u), "__GFP_IO"},
+     {(0x80u), "__GFP_FS"},
+     {(0x100u), "__GFP_COLD"},
+     {(0x200u), "__GFP_NOWARN"},
+     {(0x400u), "__GFP_RETRY_MAYFAIL"},
+     {(0x800u), "__GFP_NOFAIL"},
+     {(0x1000u), "__GFP_NORETRY"},
+     {(0x4000u), "__GFP_COMP"},
+     {(0x8000u), "__GFP_ZERO"},
+     {(0x10000u), "__GFP_NOMEMALLOC"},
+     {(0x2000u), "__GFP_MEMALLOC"},
+     {(0x20000u), "__GFP_HARDWALL"},
+     {(0x40000u), "__GFP_THISNODE"},
+     {(0x10u), "__GFP_RECLAIMABLE"},
+     {(0x08u), "__GFP_MOVABLE"},
+     {(0x100000u), "__GFP_ACCOUNT"},
+     {(0x800000u), "__GFP_WRITE"},
+     {((0x400000u | 0x1000000u)), "__GFP_RECLAIM"},
+     {(0x400000u), "__GFP_DIRECT_RECLAIM"},
+     {(0x1000000u), "__GFP_KSWAPD_RECLAIM"},
+     {0, nullptr}}};
+
+// 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)) {
+    return &v3_4;
+  } else if (version >= std::make_tuple(3, 10) &&
+             version < std::make_tuple(4, 4)) {
+    return &v3_10;
+  } else if (version >= std::make_tuple(4, 4) &&
+             version < std::make_tuple(4, 14)) {
+    return &v4_4;
+  } else {  // version >= 4.14
+    // TODO(taylori): Add newer kernel versions once we have access to them.
+    return &v4_14;
+  }
+}
+}  // namespace
+
+void WriteGfpFlag(uint64_t value,
+                  std::tuple<uint32_t, uint32_t> version,
+                  base::StringWriter* writer) {
+  // On all kernel versions if this flag is not set, return GFP_NOWAIT.
+  if (value == 0)
+    writer->AppendString("GFP_NOWAIT");
+
+  std::string result;
+  const FlagArray* bitmasks = GetBitmaskVersion(version);
+
+  // Based on trace_print_flags_seq() in the kernel.
+  size_t i = 0;
+  while (bitmasks->at(i).flag_name != nullptr) {
+    size_t current = i++;
+    uint64_t mask = bitmasks->at(current).mask;
+    const char* str = bitmasks->at(current).flag_name;
+
+    if ((value & mask) != mask)
+      continue;
+    value &= ~mask;
+
+    result += str;
+    result += "|";
+  }
+
+  // Add any leftover flags.
+  if (value) {
+    writer->AppendString(result.c_str(), result.size());
+    writer->AppendString("0x", 2);
+    writer->AppendHexInt(value);
+  } else {
+    writer->AppendString(result.c_str(), result.size() - 1);
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/gfp_flags.h b/src/trace_processor/gfp_flags.h
new file mode 100644
index 0000000..05e3bdf
--- /dev/null
+++ b/src/trace_processor/gfp_flags.h
@@ -0,0 +1,35 @@
+/*
+ * 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_GFP_FLAGS_H_
+#define SRC_TRACE_PROCESSOR_GFP_FLAGS_H_
+
+#include "perfetto/ext/base/string_writer.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// GFP flags in ftrace events should be parsed and read differently depending
+// 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::StringWriter* writer);
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_GFP_FLAGS_H_
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index e93578c..7efcc6e 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -20,6 +20,7 @@
 #include "perfetto/ext/traced/sys_stats_counters.h"
 #include "perfetto/protozero/proto_decoder.h"
 #include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/metadata.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/syscall_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
@@ -296,6 +297,24 @@
     } else {
       PERFETTO_ELOG("Unknown architecture %s", machine.ToStdString().c_str());
     }
+
+    StringPool::Id sysname_id =
+        context_->storage->InternString(utsname.sysname());
+    StringPool::Id version_id =
+        context_->storage->InternString(utsname.version());
+    StringPool::Id release_id =
+        context_->storage->InternString(utsname.release());
+    StringPool::Id machine_id =
+        context_->storage->InternString(utsname.machine());
+
+    context_->storage->SetMetadata(metadata::system_name,
+                                   Variadic::String(sysname_id));
+    context_->storage->SetMetadata(metadata::system_version,
+                                   Variadic::String(version_id));
+    context_->storage->SetMetadata(metadata::system_release,
+                                   Variadic::String(release_id));
+    context_->storage->SetMetadata(metadata::system_machine,
+                                   Variadic::String(machine_id));
   }
 }
 
diff --git a/src/trace_processor/metadata.h b/src/trace_processor/metadata.h
index b5bef83..7faa8bc 100644
--- a/src/trace_processor/metadata.h
+++ b/src/trace_processor/metadata.h
@@ -40,7 +40,11 @@
   F(benchmark_story_tags,                kMulti,   Variadic::kString), \
   F(android_packages_list,               kMulti,   Variadic::kInt),    \
   F(statsd_triggering_subscription_id,   kSingle,  Variadic::kInt),    \
-  F(trace_uuid,                          kSingle,   Variadic::kString)
+  F(trace_uuid,                          kSingle,  Variadic::kString), \
+  F(system_name,                         kSingle,  Variadic::kString), \
+  F(system_version,                      kSingle,  Variadic::kString), \
+  F(system_release,                      kSingle,  Variadic::kString), \
+  F(system_machine,                      kSingle,  Variadic::kString)
 // clang-format on
 
 enum KeyType {
diff --git a/src/trace_processor/raw_table.cc b/src/trace_processor/raw_table.cc
index b4c2969..2b049f8 100644
--- a/src/trace_processor/raw_table.cc
+++ b/src/trace_processor/raw_table.cc
@@ -19,6 +19,8 @@
 #include <inttypes.h>
 
 #include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/gfp_flags.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 #include "src/trace_processor/variadic.h"
 
@@ -33,6 +35,21 @@
 namespace perfetto {
 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());
+}
+}  // namespace
+
 RawTable::RawTable(sqlite3* db, const TraceStorage* storage)
     : storage_(storage) {
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_FTRACE)
@@ -83,6 +100,27 @@
 }
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_FTRACE)
+bool RawTable::ParseGfpFlags(Variadic value, base::StringWriter* writer) {
+  if (!storage_->metadata().MetadataExists(metadata::KeyIDs::system_name) ||
+      !storage_->metadata().MetadataExists(metadata::KeyIDs::system_release)) {
+    return false;
+  }
+
+  const Variadic& name =
+      storage_->metadata().GetScalarMetadata(metadata::KeyIDs::system_name);
+  base::StringView system_name = storage_->GetString(name.string_value);
+  if (system_name != "Linux")
+    return false;
+
+  const Variadic& release =
+      storage_->metadata().GetScalarMetadata(metadata::KeyIDs::system_release);
+  base::StringView system_release = storage_->GetString(release.string_value);
+  auto version = ParseKernelReleaseVersion(system_release);
+
+  WriteGfpFlag(value.uint_value, version, writer);
+  return true;
+}
+
 void RawTable::FormatSystraceArgs(NullTermStringView event_name,
                                   ArgSetId arg_set_id,
                                   base::StringWriter* writer) {
@@ -131,12 +169,17 @@
     uint32_t arg_row = start_row + arg_idx;
     const auto& args = storage_->args();
     const auto& key = storage_->GetString(args.keys()[arg_row]);
-    const auto& value = args.arg_values()[arg_row];
 
     writer->AppendChar(' ');
     writer->AppendString(key.c_str(), key.size());
     writer->AppendChar('=');
-    value_fn(value);
+
+    if (key == "gfp_flags" &&
+        ParseGfpFlags(args.arg_values()[arg_row], writer)) {
+      return;
+    }
+
+    value_fn(args.arg_values()[arg_row]);
   };
 
   if (event_name == "sched_switch") {
diff --git a/src/trace_processor/raw_table.h b/src/trace_processor/raw_table.h
index 690c0ac..480188c 100644
--- a/src/trace_processor/raw_table.h
+++ b/src/trace_processor/raw_table.h
@@ -41,6 +41,7 @@
                           ArgSetId arg_set_id,
                           base::StringWriter* writer);
   void ToSystrace(sqlite3_context* ctx, int argc, sqlite3_value** argv);
+  bool ParseGfpFlags(Variadic value, base::StringWriter* writer);
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_FTRACE)
 
   const TraceStorage* const storage_;
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 2d40985..fbae94b 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -754,6 +754,15 @@
       return TraceStorage::CreateRowId(kMetadataTable, index);
     }
 
+    const Variadic& GetScalarMetadata(metadata::KeyIDs key) const {
+      PERFETTO_DCHECK(scalar_indices.count(key) == 1);
+      return values_.at(scalar_indices.at(key));
+    }
+
+    bool MetadataExists(metadata::KeyIDs key) const {
+      return scalar_indices.count(key) >= 1;
+    }
+
     void OverwriteMetadata(uint32_t index, Variadic value) {
       PERFETTO_DCHECK(index < values_.size());
       values_[index] = value;