trace_processor: migrate args to macro tables

Context: go/perfetto-tp-refactor
Bug: 135177627
Change-Id: I3e99a70a6628cb1444fed1f31e02fd31a76faaca
diff --git a/Android.bp b/Android.bp
index 5ed362b..75fe075 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5726,7 +5726,6 @@
 filegroup {
   name: "perfetto_src_trace_processor_lib",
   srcs: [
-    "src/trace_processor/args_table.cc",
     "src/trace_processor/filtered_row_index.cc",
     "src/trace_processor/gfp_flags.cc",
     "src/trace_processor/process_table.cc",
@@ -5830,6 +5829,7 @@
     "src/trace_processor/event_tracker.cc",
     "src/trace_processor/forwarding_trace_parser.cc",
     "src/trace_processor/ftrace_utils.cc",
+    "src/trace_processor/global_args_tracker.cc",
     "src/trace_processor/gzip_trace_parser.cc",
     "src/trace_processor/heap_profile_tracker.cc",
     "src/trace_processor/importers/ftrace/ftrace_module.cc",
@@ -5855,6 +5855,7 @@
     "src/trace_processor/trace_sorter.cc",
     "src/trace_processor/trace_storage.cc",
     "src/trace_processor/track_tracker.cc",
+    "src/trace_processor/variadic.cc",
     "src/trace_processor/virtual_destructors.cc",
   ],
 }
@@ -5876,7 +5877,6 @@
 filegroup {
   name: "perfetto_src_trace_processor_unittests",
   srcs: [
-    "src/trace_processor/args_table_unittest.cc",
     "src/trace_processor/clock_tracker_unittest.cc",
     "src/trace_processor/event_tracker_unittest.cc",
     "src/trace_processor/filtered_row_index_unittest.cc",
diff --git a/BUILD b/BUILD
index 5303bde..7842996 100644
--- a/BUILD
+++ b/BUILD
@@ -785,8 +785,6 @@
 filegroup(
     name = "src_trace_processor_lib",
     srcs = [
-        "src/trace_processor/args_table.cc",
-        "src/trace_processor/args_table.h",
         "src/trace_processor/filtered_row_index.cc",
         "src/trace_processor/filtered_row_index.h",
         "src/trace_processor/gfp_flags.cc",
@@ -891,6 +889,8 @@
         "src/trace_processor/forwarding_trace_parser.h",
         "src/trace_processor/ftrace_utils.cc",
         "src/trace_processor/ftrace_utils.h",
+        "src/trace_processor/global_args_tracker.cc",
+        "src/trace_processor/global_args_tracker.h",
         "src/trace_processor/gzip_trace_parser.cc",
         "src/trace_processor/gzip_trace_parser.h",
         "src/trace_processor/heap_profile_tracker.cc",
@@ -954,6 +954,7 @@
         "src/trace_processor/trace_storage.h",
         "src/trace_processor/track_tracker.cc",
         "src/trace_processor/track_tracker.h",
+        "src/trace_processor/variadic.cc",
         "src/trace_processor/variadic.h",
         "src/trace_processor/virtual_destructors.cc",
     ],
diff --git a/src/protozero/test/example_proto/test_messages.descriptor.h b/src/protozero/test/example_proto/test_messages.descriptor.h
index 9c2bcfa..f26c058 100644
--- a/src/protozero/test/example_proto/test_messages.descriptor.h
+++ b/src/protozero/test/example_proto/test_messages.descriptor.h
@@ -224,19 +224,19 @@
      0x72, 0x42, 0x61, 0x7a, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x72, 0x42,
      0x61, 0x7a, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x62, 0x61,
      0x72, 0x42, 0x61, 0x7a, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x6f, 0x6f, 0x4d,
-     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6d, 0x6f,
+     0x6f, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x4d, 0x6f,
      0x6f, 0x4d, 0x6f, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x52, 0x4c, 0x45,
      0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
-     0x52, 0x0a, 0x75, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
+     0x52, 0x0a, 0x55, 0x52, 0x4c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72,
      0x12, 0x12, 0x0a, 0x04, 0x58, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x04, 0x78, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
+     0x28, 0x08, 0x52, 0x04, 0x58, 0x4d, 0x61, 0x70, 0x12, 0x21, 0x0a, 0x0d,
      0x55, 0x72, 0x4c, 0x45, 0x5f, 0x6e, 0x63, 0x6f, 0x5f, 0x5f, 0x64, 0x65,
-     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x72, 0x4c,
+     0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x72, 0x4c,
      0x45, 0x4e, 0x63, 0x6f, 0x44, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x09, 0x5f,
      0x5f, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01,
-     0x28, 0x08, 0x52, 0x07, 0x62, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
+     0x28, 0x08, 0x52, 0x07, 0x42, 0x69, 0x67, 0x42, 0x61, 0x6e, 0x67, 0x12,
      0x0e, 0x0a, 0x02, 0x55, 0x32, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
-     0x02, 0x75, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
+     0x02, 0x55, 0x32, 0x12, 0x1a, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x67, 0x42,
      0x69, 0x67, 0x5f, 0x5f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
      0x62, 0x61, 0x6e, 0x67, 0x42, 0x69, 0x67, 0x22, 0x8f, 0x01, 0x0a, 0x14,
      0x50, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74,
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index a6fd002..1ba92b1 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -86,6 +86,8 @@
     "forwarding_trace_parser.cc",
     "forwarding_trace_parser.h",
     "ftrace_utils.h",
+    "global_args_tracker.cc",
+    "global_args_tracker.h",
     "gzip_trace_parser.cc",
     "gzip_trace_parser.h",
     "heap_profile_tracker.cc",
@@ -136,6 +138,7 @@
     "trace_storage.h",
     "track_tracker.cc",
     "track_tracker.h",
+    "variadic.cc",
     "variadic.h",
     "virtual_destructors.cc",
   ]
@@ -282,8 +285,6 @@
 
 source_set("lib") {
   sources = [
-    "args_table.cc",
-    "args_table.h",
     "filtered_row_index.cc",
     "filtered_row_index.h",
     "gfp_flags.cc",
@@ -368,7 +369,6 @@
 perfetto_unittest_source_set("unittests") {
   testonly = true
   sources = [
-    "args_table_unittest.cc",
     "clock_tracker_unittest.cc",
     "event_tracker_unittest.cc",
     "filtered_row_index_unittest.cc",
diff --git a/src/trace_processor/args_table.cc b/src/trace_processor/args_table.cc
deleted file mode 100644
index 64b407c..0000000
--- a/src/trace_processor/args_table.cc
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * 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 "src/trace_processor/args_table.h"
-
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-PERFETTO_ALWAYS_INLINE
-bool TreatedAsInteger(Variadic v) {
-  return v.type == Variadic::Type::kInt || v.type == Variadic::Type::kBool ||
-         v.type == Variadic::Type::kPointer || v.type == Variadic::Type::kUint;
-}
-
-PERFETTO_ALWAYS_INLINE
-bool TreatedAsString(Variadic v) {
-  return v.type == Variadic::Type::kString || v.type == Variadic::Type::kJson;
-}
-
-PERFETTO_ALWAYS_INLINE
-int64_t AsInt64(Variadic v) {
-  if (v.type == Variadic::Type::kInt)
-    return v.int_value;
-  if (v.type == Variadic::Type::kBool)
-    return static_cast<int64_t>(v.bool_value);
-  if (v.type == Variadic::Type::kUint)
-    return static_cast<int64_t>(v.uint_value);
-  if (v.type == Variadic::Type::kPointer)
-    return static_cast<int64_t>(v.pointer_value);
-  PERFETTO_FATAL("invalid Variadic type");
-}
-
-PERFETTO_ALWAYS_INLINE
-StringId AsStringId(Variadic v) {
-  if (v.type == Variadic::Type::kString)
-    return v.string_value;
-  if (v.type == Variadic::Type::kJson)
-    return v.json_value;
-  PERFETTO_FATAL("invalid Variadic type");
-}
-}  // namespace
-
-ArgsTable::ArgsTable(sqlite3*, const TraceStorage* storage)
-    : storage_(storage) {}
-
-void ArgsTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
-  SqliteTable::Register<ArgsTable>(db, storage, "args");
-}
-
-StorageSchema ArgsTable::CreateStorageSchema() {
-  const auto& args = storage_->args();
-  return StorageSchema::Builder()
-      .AddOrderedNumericColumn("arg_set_id", &args.set_ids())
-      .AddStringColumn("flat_key", &args.flat_keys(), &storage_->string_pool())
-      .AddStringColumn("key", &args.keys(), &storage_->string_pool())
-      .AddColumn<ValueColumn>("int_value", Variadic::Type::kInt, storage_)
-      .AddColumn<ValueColumn>("string_value", Variadic::Type::kString, storage_)
-      .AddColumn<ValueColumn>("real_value", Variadic::Type::kReal, storage_)
-      .Build({"arg_set_id", "key"});
-}
-
-uint32_t ArgsTable::RowCount() {
-  return static_cast<uint32_t>(storage_->args().args_count());
-}
-
-int ArgsTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
-  if (HasEqConstraint(qc, "arg_set_id")) {
-    info->estimated_cost = 1;
-  } else {
-    info->estimated_cost = static_cast<uint32_t>(storage_->args().args_count());
-  }
-  return SQLITE_OK;
-}
-
-ArgsTable::ValueColumn::ValueColumn(std::string col_name,
-                                    Variadic::Type type,
-                                    const TraceStorage* storage)
-    : StorageColumn(col_name, false /* hidden */),
-      type_(type),
-      storage_(storage) {
-  PERFETTO_CHECK(type == Variadic::Type::kInt ||
-                 type == Variadic::Type::kReal ||
-                 type == Variadic::Type::kString);
-}
-
-void ArgsTable::ValueColumn::ReportResult(sqlite3_context* ctx,
-                                          uint32_t row) const {
-  const auto& value = storage_->args().arg_values()[row];
-  switch (type_) {
-    // Integer column, returns all integer-like variadic values (as an int64_t).
-    case Variadic::Type::kInt: {
-      if (!TreatedAsInteger(value)) {
-        sqlite3_result_null(ctx);
-        return;
-      }
-      sqlite_utils::ReportSqliteResult(ctx, AsInt64(value));
-      return;
-    }
-
-      // Float column, returns only float values.
-    case Variadic::Type::kReal: {
-      if (value.type != Variadic::Type::kReal) {
-        sqlite3_result_null(ctx);
-        return;
-      }
-      sqlite_utils::ReportSqliteResult(ctx, value.real_value);
-      return;
-    }
-
-      // String column, returns string & json variadic values (as a string).
-    case Variadic::Type::kString: {
-      if (!TreatedAsString(value)) {
-        sqlite3_result_null(ctx);
-        return;
-      }
-      const char* str = storage_->GetString(AsStringId(value)).c_str();
-      sqlite3_result_text(ctx, str, -1, sqlite_utils::kSqliteStatic);
-      return;
-    }
-
-    case Variadic::Type::kBool:
-    case Variadic::Type::kUint:
-    case Variadic::Type::kPointer:
-    case Variadic::Type::kJson:
-      PERFETTO_FATAL("Unexpected column type");
-  }
-}
-
-ArgsTable::ValueColumn::Bounds ArgsTable::ValueColumn::BoundFilter(
-    int,
-    sqlite3_value*) const {
-  return Bounds{};
-}
-
-void ArgsTable::ValueColumn::Filter(int op,
-                                    sqlite3_value* value,
-                                    FilteredRowIndex* index) const {
-  switch (type_) {
-    // Integer column, returns all integer-like variadic values (as an int64_t).
-    case Variadic::Type::kInt: {
-      bool op_is_null = sqlite_utils::IsOpIsNull(op);
-      auto predicate = sqlite_utils::CreateNumericPredicate<int64_t>(op, value);
-      index->FilterRows(
-          [this, predicate, op_is_null](uint32_t row) PERFETTO_ALWAYS_INLINE {
-            const Variadic& arg = storage_->args().arg_values()[row];
-            if (!TreatedAsInteger(arg)) {
-              return op_is_null;
-            }
-            return predicate(AsInt64(arg));
-          });
-      break;
-    }
-
-    // Float column, returns only float values.
-    case Variadic::Type::kReal: {
-      bool op_is_null = sqlite_utils::IsOpIsNull(op);
-      auto predicate = sqlite_utils::CreateNumericPredicate<double>(op, value);
-      index->FilterRows(
-          [this, predicate, op_is_null](uint32_t row) PERFETTO_ALWAYS_INLINE {
-            const auto& arg = storage_->args().arg_values()[row];
-            return arg.type == Variadic::Type::kReal ? predicate(arg.real_value)
-                                                     : op_is_null;
-          });
-      break;
-    }
-
-    // String column, returns string & json variadic values (as a string).
-    case Variadic::Type::kString: {
-      auto predicate = sqlite_utils::CreateStringPredicate(op, value);
-      index->FilterRows(
-          [this, &predicate](uint32_t row) PERFETTO_ALWAYS_INLINE {
-            const auto& arg = storage_->args().arg_values()[row];
-            if (!TreatedAsString(arg)) {
-              return predicate(nullptr);
-            }
-            return predicate(storage_->GetString(AsStringId(arg)).c_str());
-          });
-      break;
-    }
-    case Variadic::Type::kBool:
-    case Variadic::Type::kUint:
-    case Variadic::Type::kPointer:
-    case Variadic::Type::kJson:
-      PERFETTO_FATAL("Unexpected column type");
-  }
-}
-
-ArgsTable::ValueColumn::Comparator ArgsTable::ValueColumn::Sort(
-    const QueryConstraints::OrderBy& ob) const {
-  if (ob.desc) {
-    return [this](uint32_t f, uint32_t s) { return -CompareRefsAsc(f, s); };
-  }
-  return [this](uint32_t f, uint32_t s) { return CompareRefsAsc(f, s); };
-}
-
-int ArgsTable::ValueColumn::CompareRefsAsc(uint32_t f, uint32_t s) const {
-  const auto& arg_f = storage_->args().arg_values()[f];
-  const auto& arg_s = storage_->args().arg_values()[s];
-  switch (type_) {
-    // Integer column, returns all integer-like variadic values (as an int64_t).
-    case Variadic::Type::kInt: {
-      if (TreatedAsInteger(arg_f) && TreatedAsInteger(arg_s)) {
-        return sqlite_utils::CompareValuesAsc(AsInt64(arg_f), AsInt64(arg_s));
-      } else if (TreatedAsInteger(arg_f)) {
-        return 1;  // second value treated as null
-      } else if (TreatedAsInteger(arg_s)) {
-        return -1;  // first value treated as null
-      }
-      return 0;
-    }
-
-    // Float column, returns only float values.
-    case Variadic::Type::kReal: {
-      if (arg_f.type == Variadic::Type::kReal &&
-          arg_s.type == Variadic::Type::kReal) {
-        return sqlite_utils::CompareValuesAsc(arg_f.real_value,
-                                              arg_s.real_value);
-      } else if (arg_f.type == Variadic::Type::kReal) {
-        return 1;  // second value treated as null
-      } else if (arg_s.type == Variadic::Type::kReal) {
-        return -1;  // first value treated as null
-      }
-      return 0;
-    }
-
-    // String column, returns string & json variadic values (as a string).
-    case Variadic::Type::kString: {
-      if (TreatedAsString(arg_f) && TreatedAsString(arg_s)) {
-        const auto& f_str = storage_->GetString(AsStringId(arg_f));
-        const auto& s_str = storage_->GetString(AsStringId(arg_s));
-        return sqlite_utils::CompareValuesAsc(f_str, s_str);
-      } else if (TreatedAsString(arg_f)) {
-        return 1;  // second value treated as null
-      } else if (TreatedAsString(arg_s)) {
-        return -1;  // first value treated as null
-      }
-      return 0;
-    }
-    case Variadic::Type::kBool:
-    case Variadic::Type::kUint:
-    case Variadic::Type::kPointer:
-    case Variadic::Type::kJson:
-      PERFETTO_FATAL("Unexpected column type");
-  }
-  PERFETTO_FATAL("Never reached");  // for gcc
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/args_table.h b/src/trace_processor/args_table.h
deleted file mode 100644
index 8243457..0000000
--- a/src/trace_processor/args_table.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef SRC_TRACE_PROCESSOR_ARGS_TABLE_H_
-#define SRC_TRACE_PROCESSOR_ARGS_TABLE_H_
-
-#include "src/trace_processor/storage_table.h"
-#include "src/trace_processor/trace_storage.h"
-#include "src/trace_processor/variadic.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class ArgsTable : public StorageTable {
- public:
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
-  ArgsTable(sqlite3*, const TraceStorage*);
-
-  // StorageTable implementation.
-  StorageSchema CreateStorageSchema() override;
-  uint32_t RowCount() override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
-
- private:
-  class ValueColumn final : public StorageColumn {
-   public:
-    ValueColumn(std::string col_name,
-                Variadic::Type type,
-                const TraceStorage* storage);
-
-    void ReportResult(sqlite3_context* ctx, uint32_t row) const override;
-
-    Bounds BoundFilter(int op, sqlite3_value* sqlite_val) const override;
-
-    void Filter(int op, sqlite3_value* value, FilteredRowIndex*) const override;
-
-    Comparator Sort(const QueryConstraints::OrderBy& ob) const override;
-
-    bool HasOrdering() const override { return false; }
-
-    SqlValue::Type GetType() const override {
-      switch (type_) {
-        case Variadic::Type::kInt:
-        case Variadic::Type::kUint:
-        case Variadic::Type::kPointer:
-        case Variadic::Type::kBool:
-          return SqlValue::Type::kLong;
-        case Variadic::Type::kJson:
-        case Variadic::Type::kString:
-          return SqlValue::Type::kString;
-        case Variadic::Type::kReal:
-          return SqlValue::Type::kDouble;
-      }
-      PERFETTO_FATAL("Not reached");  // For gcc
-    }
-
-   private:
-    int CompareRefsAsc(uint32_t f, uint32_t s) const;
-
-    Variadic::Type type_;
-    const TraceStorage* storage_ = nullptr;
-  };
-
-  const TraceStorage* const storage_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_ARGS_TABLE_H_
diff --git a/src/trace_processor/args_table_unittest.cc b/src/trace_processor/args_table_unittest.cc
deleted file mode 100644
index de6bec7..0000000
--- a/src/trace_processor/args_table_unittest.cc
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * 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 "src/trace_processor/args_table.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
-#include "src/trace_processor/trace_processor_context.h"
-#include "src/trace_processor/trace_storage.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-class ArgsTableUnittest : public ::testing::Test {
- public:
-  ArgsTableUnittest() {
-    sqlite3* db = nullptr;
-    PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
-    PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
-    db_.reset(db);
-
-    context_.storage.reset(new TraceStorage());
-    ArgsTable::RegisterTable(db_.get(), context_.storage.get());
-  }
-
-  void PrepareValidStatement(const std::string& sql) {
-    int size = static_cast<int>(sql.size());
-    sqlite3_stmt* stmt;
-    ASSERT_EQ(sqlite3_prepare_v2(*db_, sql.c_str(), size, &stmt, nullptr),
-              SQLITE_OK);
-    stmt_.reset(stmt);
-  }
-
-  const char* GetColumnAsText(int colId) {
-    return reinterpret_cast<const char*>(sqlite3_column_text(*stmt_, colId));
-  }
-
-  void AssertArgRowValues(int arg_set_id,
-                          const char* flat_key,
-                          const char* key,
-                          base::Optional<int64_t> int_value,
-                          base::Optional<const char*> string_value,
-                          base::Optional<double> real_value);
-
- protected:
-  TraceProcessorContext context_;
-  ScopedDb db_;
-  ScopedStmt stmt_;
-};
-
-// Test helper.
-void ArgsTableUnittest::AssertArgRowValues(
-    int arg_set_id,
-    const char* flat_key,
-    const char* key,
-    base::Optional<int64_t> int_value,
-    base::Optional<const char*> string_value,
-    base::Optional<double> real_value) {
-  ASSERT_EQ(sqlite3_column_int(*stmt_, 0), arg_set_id);
-  ASSERT_STREQ(GetColumnAsText(1), flat_key);
-  ASSERT_STREQ(GetColumnAsText(2), key);
-  if (int_value.has_value()) {
-    ASSERT_EQ(sqlite3_column_int64(*stmt_, 3), int_value.value());
-  } else {
-    ASSERT_EQ(sqlite3_column_type(*stmt_, 3), SQLITE_NULL);
-  }
-  if (string_value.has_value()) {
-    ASSERT_STREQ(GetColumnAsText(4), string_value.value());
-  } else {
-    ASSERT_EQ(sqlite3_column_type(*stmt_, 4), SQLITE_NULL);
-  }
-  if (real_value.has_value()) {
-    ASSERT_DOUBLE_EQ(sqlite3_column_double(*stmt_, 5), real_value.value());
-  } else {
-    ASSERT_EQ(sqlite3_column_type(*stmt_, 5), SQLITE_NULL);
-  }
-}
-
-TEST_F(ArgsTableUnittest, IntValue) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-  static const int kValue = 123;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString(kFlatKey);
-  arg.key = context_.storage->InternString(kKey);
-  arg.value = Variadic::Integer(kValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-
-  PrepareValidStatement("SELECT * FROM args");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, kValue, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, StringValue) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-  static const char kValue[] = "123";
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString(kFlatKey);
-  arg.key = context_.storage->InternString(kKey);
-  arg.value = Variadic::String(context_.storage->InternString(kValue));
-
-  context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-
-  PrepareValidStatement("SELECT * FROM args");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, base::nullopt, kValue, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, RealValue) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-  static const double kValue = 0.123;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString(kFlatKey);
-  arg.key = context_.storage->InternString(kKey);
-  arg.value = Variadic::Real(kValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-
-  PrepareValidStatement("SELECT * FROM args");
-
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, base::nullopt, base::nullopt, kValue);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, BoolValueTreatedAsInt) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-  static const bool kValue = true;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString(kFlatKey);
-  arg.key = context_.storage->InternString(kKey);
-  arg.value = Variadic::Boolean(kValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
-
-  // Boolean returned in the "int_value" column, and is comparable to an integer
-  // literal.
-  PrepareValidStatement("SELECT * FROM args WHERE int_value = 1");
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, kValue, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, PointerValueTreatedAsInt) {
-  static const uint64_t kSmallValue = 1ull << 30;
-  static const uint64_t kTopBitSetValue = 1ull << 63;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString("flat_key_small");
-  arg.key = context_.storage->InternString("key_small");
-  arg.value = Variadic::Pointer(kSmallValue);
-
-  TraceStorage::Args::Arg arg2;
-  arg2.flat_key = context_.storage->InternString("flat_key_large");
-  arg2.key = context_.storage->InternString("key_large");
-  arg2.value = Variadic::Pointer(kTopBitSetValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg, arg2}, 0, 2);
-
-  // Pointer returned in the "int_value" column, as a signed 64 bit. And is
-  // comparable to an integer literal.
-
-  static const int64_t kExpectedSmallValue = static_cast<int64_t>(kSmallValue);
-  PrepareValidStatement(std::string("SELECT * FROM args WHERE int_value = ") +
-                        std::to_string(kExpectedSmallValue));
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, "flat_key_small", "key_small", kExpectedSmallValue,
-                     base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-
-  static const int64_t kExpectedTopBitSetValue =
-      static_cast<int64_t>(kTopBitSetValue);  // negative
-  PrepareValidStatement(std::string("SELECT * FROM args WHERE int_value = ") +
-                        std::to_string(kExpectedTopBitSetValue));
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, "flat_key_large", "key_large", kExpectedTopBitSetValue,
-                     base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, UintValueTreatedAsInt) {
-  static const uint64_t kSmallValue = 1ull << 30;
-  static const uint64_t kTopBitSetValue = 1ull << 63;
-
-  TraceStorage::Args::Arg arg;
-  arg.flat_key = context_.storage->InternString("flat_key_small");
-  arg.key = context_.storage->InternString("key_small");
-  arg.value = Variadic::UnsignedInteger(kSmallValue);
-
-  TraceStorage::Args::Arg arg2;
-  arg2.flat_key = context_.storage->InternString("flat_key_large");
-  arg2.key = context_.storage->InternString("key_large");
-  arg2.value = Variadic::UnsignedInteger(kTopBitSetValue);
-
-  context_.storage->mutable_args()->AddArgSet({arg, arg2}, 0, 2);
-
-  // Unsigned returned in the "int_value" column, as a signed 64 bit. And is
-  // comparable to an integer literal.
-
-  static const int64_t kExpectedSmallValue = static_cast<int64_t>(kSmallValue);
-  PrepareValidStatement(std::string("SELECT * FROM args WHERE int_value = ") +
-                        std::to_string(kExpectedSmallValue));
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, "flat_key_small", "key_small", kExpectedSmallValue,
-                     base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-
-  static const int64_t kExpectedTopBitSetValue =
-      static_cast<int64_t>(kTopBitSetValue);  // negative
-  PrepareValidStatement(std::string("SELECT * FROM args WHERE int_value = ") +
-                        std::to_string(kExpectedTopBitSetValue));
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, "flat_key_large", "key_large", kExpectedTopBitSetValue,
-                     base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-TEST_F(ArgsTableUnittest, IntegerLikeValuesSortByIntRepresentation) {
-  static const char kFlatKey[] = "flat_key";
-  static const char kKey[] = "key";
-
-  TraceStorage::Args::Arg bool_arg_true;
-  bool_arg_true.flat_key = context_.storage->InternString(kFlatKey);
-  bool_arg_true.key = context_.storage->InternString(kKey);
-  bool_arg_true.value = Variadic::Boolean(true);
-
-  TraceStorage::Args::Arg bool_arg_false;
-  bool_arg_false.flat_key = context_.storage->InternString(kFlatKey);
-  bool_arg_false.key = context_.storage->InternString(kKey);
-  bool_arg_false.value = Variadic::Boolean(false);
-
-  TraceStorage::Args::Arg pointer_arg_42;
-  pointer_arg_42.flat_key = context_.storage->InternString(kFlatKey);
-  pointer_arg_42.key = context_.storage->InternString(kKey);
-  pointer_arg_42.value = Variadic::Pointer(42);
-
-  TraceStorage::Args::Arg unsigned_arg_10;
-  unsigned_arg_10.flat_key = context_.storage->InternString(kFlatKey);
-  unsigned_arg_10.key = context_.storage->InternString(kKey);
-  unsigned_arg_10.value = Variadic::UnsignedInteger(10);
-
-  // treated as null by the int_value column
-  TraceStorage::Args::Arg string_arg;
-  string_arg.flat_key = context_.storage->InternString(kFlatKey);
-  string_arg.key = context_.storage->InternString(kKey);
-  string_arg.value =
-      Variadic::String(context_.storage->InternString("string_content"));
-
-  context_.storage->mutable_args()->AddArgSet(
-      {bool_arg_true, bool_arg_false, pointer_arg_42, unsigned_arg_10,
-       string_arg},
-      0, 5);
-
-  // Ascending sort by int representations:
-  // { null (string), 0 (false), 1 (true), 10, 42 }
-  PrepareValidStatement("SELECT * FROM args ORDER BY int_value ASC");
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, base::nullopt, "string_content",
-                     base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 0, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 1, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 10, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 42, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-
-  // Desceding order.
-  PrepareValidStatement("SELECT * FROM args ORDER BY int_value DESC");
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 42, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 10, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 1, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, 0, base::nullopt, base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
-  AssertArgRowValues(1, kFlatKey, kKey, base::nullopt, "string_content",
-                     base::nullopt);
-  ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/args_tracker.cc b/src/trace_processor/args_tracker.cc
index 4f143c5..678bf57 100644
--- a/src/trace_processor/args_tracker.cc
+++ b/src/trace_processor/args_tracker.cc
@@ -43,7 +43,7 @@
 }
 
 void ArgsTracker::Flush() {
-  using Arg = TraceStorage::Args::Arg;
+  using Arg = GlobalArgsTracker::Arg;
 
   if (args_.empty())
     return;
@@ -69,7 +69,7 @@
     }
 
     ArgSetId set_id =
-        storage->mutable_args()->AddArgSet(args_, i, next_rid_idx);
+        context_->global_args_tracker->AddArgSet(args_, i, next_rid_idx);
     switch (table_id) {
       case TableId::kRawEvents:
         storage->mutable_raw_events()->set_arg_set_id(row, set_id);
diff --git a/src/trace_processor/args_tracker.h b/src/trace_processor/args_tracker.h
index 90062a8..3eebcb8 100644
--- a/src/trace_processor/args_tracker.h
+++ b/src/trace_processor/args_tracker.h
@@ -17,6 +17,7 @@
 #ifndef SRC_TRACE_PROCESSOR_ARGS_TRACKER_H_
 #define SRC_TRACE_PROCESSOR_ARGS_TRACKER_H_
 
+#include "src/trace_processor/global_args_tracker.h"
 #include "src/trace_processor/trace_processor_context.h"
 #include "src/trace_processor/trace_storage.h"
 #include "src/trace_processor/variadic.h"
@@ -67,7 +68,7 @@
   virtual void Flush();
 
  private:
-  std::vector<TraceStorage::Args::Arg> args_;
+  std::vector<GlobalArgsTracker::Arg> args_;
   TraceProcessorContext* const context_;
 };
 
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index fd9f6b2..fedfa7c 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -239,6 +239,7 @@
         return cmp(sparse_vector<T>().GetNonNull(idx)) >= 0;
       });
       break;
+    case FilterOp::kGlob:
     case FilterOp::kLike:
       rm->Intersect(RowMap());
       break;
@@ -312,10 +313,11 @@
         return v.data() != nullptr && compare::String(v, str_value) >= 0;
       });
       break;
+    case FilterOp::kGlob:
     case FilterOp::kLike:
       // TODO(lalitm): either call through to SQLite or reimplement
       // like ourselves.
-      PERFETTO_DLOG("Ignoring like constraint on string column");
+      PERFETTO_DLOG("Ignoring like/glob constraint on string column");
       break;
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
@@ -372,6 +374,7 @@
         return compare::Numeric(idx, id_value) >= 0;
       });
       break;
+    case FilterOp::kGlob:
     case FilterOp::kLike:
       rm->Intersect(RowMap());
       break;
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index ee12912..7ce1eeb 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -41,6 +41,7 @@
   kIsNull,
   kIsNotNull,
   kLike,
+  kGlob,
 };
 
 // Represents a constraint on a column.
@@ -451,6 +452,7 @@
         rm->Intersect(RowMap(beg, row_map().size()));
         return true;
       }
+      case FilterOp::kGlob:
       case FilterOp::kNe:
       case FilterOp::kIsNull:
       case FilterOp::kIsNotNull:
diff --git a/src/trace_processor/event_tracker_unittest.cc b/src/trace_processor/event_tracker_unittest.cc
index 4fadf41..75abbaa 100644
--- a/src/trace_processor/event_tracker_unittest.cc
+++ b/src/trace_processor/event_tracker_unittest.cc
@@ -35,6 +35,7 @@
  public:
   EventTrackerTest() {
     context.storage.reset(new TraceStorage());
+    context.global_args_tracker.reset(new GlobalArgsTracker(&context));
     context.args_tracker.reset(new ArgsTracker(&context));
     context.process_tracker.reset(new ProcessTracker(&context));
     context.event_tracker.reset(new EventTracker(&context));
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index 4aa62b9..54d3ffc 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -329,16 +329,18 @@
         nan_value_(Json::StaticString("NaN")),
         inf_value_(Json::StaticString("Infinity")),
         neg_inf_value_(Json::StaticString("-Infinity")) {
-    const TraceStorage::Args& args = storage->args();
-    if (args.args_count() == 0) {
+    const auto& arg_table = storage->arg_table();
+    uint32_t count = arg_table.row_count();
+    if (count == 0) {
       args_sets_.resize(1, empty_value_);
       return;
     }
-    args_sets_.resize(args.set_ids().back() + 1, empty_value_);
-    for (size_t i = 0; i < args.args_count(); ++i) {
-      ArgSetId set_id = args.set_ids()[i];
-      const char* key = GetNonNullString(storage_, args.keys()[i]);
-      Variadic value = args.arg_values()[i];
+    args_sets_.resize(arg_table.arg_set_id()[count - 1] + 1, empty_value_);
+
+    for (uint32_t i = 0; i < count; ++i) {
+      ArgSetId set_id = arg_table.arg_set_id()[i];
+      const char* key = GetNonNullString(storage_, arg_table.key()[i]);
+      Variadic value = storage_->GetArgValue(i);
       AppendArg(set_id, key, VariadicToJson(value));
     }
     PostprocessArgs();
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index 7cac7cd..58b03fb 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -65,6 +65,7 @@
 class ExportJsonTest : public ::testing::Test {
  public:
   ExportJsonTest() {
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.storage.reset(new TraceStorage());
     context_.track_tracker.reset(new TrackTracker(&context_));
@@ -415,11 +416,11 @@
       base::StringView("task.posted_from.file_name"));
   StringId arg_value_id =
       context_.storage->InternString(base::StringView(kSrc));
-  TraceStorage::Args::Arg arg;
+  GlobalArgsTracker::Arg arg;
   arg.flat_key = arg_key_id;
   arg.key = arg_key_id;
   arg.value = Variadic::String(arg_value_id);
-  ArgSetId args = context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -512,16 +513,15 @@
       base::StringView("debug.draw_duration_ms[0]"));
   StringId arg_key1_id = context_.storage->InternString(
       base::StringView("debug.draw_duration_ms[1]"));
-  TraceStorage::Args::Arg arg0;
+  GlobalArgsTracker::Arg arg0;
   arg0.flat_key = arg_flat_key_id;
   arg0.key = arg_key0_id;
   arg0.value = Variadic::Real(kValues[0]);
-  TraceStorage::Args::Arg arg1;
+  GlobalArgsTracker::Arg arg1;
   arg1.flat_key = arg_flat_key_id;
   arg1.key = arg_key1_id;
   arg1.value = Variadic::Real(kValues[1]);
-  ArgSetId args =
-      context_.storage->mutable_args()->AddArgSet({arg0, arg1}, 0, 2);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -560,16 +560,15 @@
       context_.storage->InternString(base::StringView("arg0"));
   StringId arg_key1_id =
       context_.storage->InternString(base::StringView("arg1"));
-  TraceStorage::Args::Arg arg0;
+  GlobalArgsTracker::Arg arg0;
   arg0.flat_key = arg_key0_id;
   arg0.key = arg_key0_id;
   arg0.value = Variadic::Pointer(kValue0);
-  TraceStorage::Args::Arg arg1;
+  GlobalArgsTracker::Arg arg1;
   arg1.flat_key = arg_key1_id;
   arg1.key = arg_key1_id;
   arg1.value = Variadic::Pointer(kValue1);
-  ArgSetId args =
-      context_.storage->mutable_args()->AddArgSet({arg0, arg1}, 0, 2);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -608,16 +607,15 @@
       context_.storage->InternString(base::StringView("a[0].b"));
   StringId arg_key1_id =
       context_.storage->InternString(base::StringView("a[1].b"));
-  TraceStorage::Args::Arg arg0;
+  GlobalArgsTracker::Arg arg0;
   arg0.flat_key = arg_flat_key_id;
   arg0.key = arg_key0_id;
   arg0.value = Variadic::Integer(kValues[0]);
-  TraceStorage::Args::Arg arg1;
+  GlobalArgsTracker::Arg arg1;
   arg1.flat_key = arg_flat_key_id;
   arg1.key = arg_key1_id;
   arg1.value = Variadic::Integer(kValues[1]);
-  ArgSetId args =
-      context_.storage->mutable_args()->AddArgSet({arg0, arg1}, 0, 2);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -657,16 +655,15 @@
       context_.storage->InternString(base::StringView("a[0][0]"));
   StringId arg_key1_id =
       context_.storage->InternString(base::StringView("a[0][1]"));
-  TraceStorage::Args::Arg arg0;
+  GlobalArgsTracker::Arg arg0;
   arg0.flat_key = arg_flat_key_id;
   arg0.key = arg_key0_id;
   arg0.value = Variadic::Integer(kValues[0]);
-  TraceStorage::Args::Arg arg1;
+  GlobalArgsTracker::Arg arg1;
   arg1.flat_key = arg_flat_key_id;
   arg1.key = arg_key1_id;
   arg1.value = Variadic::Integer(kValues[1]);
-  ArgSetId args =
-      context_.storage->mutable_args()->AddArgSet({arg0, arg1}, 0, 2);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg0, arg1}, 0, 2);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -703,11 +700,11 @@
   StringId arg_key_id = context_.storage->InternString(base::StringView("a"));
   StringId arg_value_id =
       context_.storage->InternString(base::StringView("{\"b\":123}"));
-  TraceStorage::Args::Arg arg;
+  GlobalArgsTracker::Arg arg;
   arg.flat_key = arg_key_id;
   arg.key = arg_key_id;
   arg.value = Variadic::Json(arg_value_id);
-  ArgSetId args = context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
@@ -813,11 +810,11 @@
       {kTimestamp, kDuration, track.value, cat_id, name_id, 0, 0, 0});
   StringId arg_key_id =
       context_.storage->InternString(base::StringView(kArgName));
-  TraceStorage::Args::Arg arg;
+  GlobalArgsTracker::Arg arg;
   arg.flat_key = arg_key_id;
   arg.key = arg_key_id;
   arg.value = Variadic::Integer(kArgValue);
-  ArgSetId args = context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   // Child event with same timestamps as first one.
@@ -1002,11 +999,11 @@
       {kTimestamp, 0, track.value, cat_id, name_id, 0, 0, 0});
   StringId arg_key_id =
       context_.storage->InternString(base::StringView("arg_name"));
-  TraceStorage::Args::Arg arg;
+  GlobalArgsTracker::Arg arg;
   arg.flat_key = arg_key_id;
   arg.key = arg_key_id;
   arg.value = Variadic::Integer(kArgValue);
-  ArgSetId args = context_.storage->mutable_args()->AddArgSet({arg}, 0, 1);
+  ArgSetId args = context_.global_args_tracker->AddArgSet({arg}, 0, 1);
   context_.storage->mutable_slice_table()->mutable_arg_set_id()->Set(0, args);
 
   base::TempFile temp_file = base::TempFile::Create();
diff --git a/src/trace_processor/global_args_tracker.cc b/src/trace_processor/global_args_tracker.cc
new file mode 100644
index 0000000..9966108
--- /dev/null
+++ b/src/trace_processor/global_args_tracker.cc
@@ -0,0 +1,26 @@
+/*
+ * 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/global_args_tracker.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+GlobalArgsTracker::GlobalArgsTracker(TraceProcessorContext* context)
+    : context_(context) {}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/global_args_tracker.h b/src/trace_processor/global_args_tracker.h
new file mode 100644
index 0000000..92784e1
--- /dev/null
+++ b/src/trace_processor/global_args_tracker.h
@@ -0,0 +1,141 @@
+/*
+ * 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_GLOBAL_ARGS_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_GLOBAL_ARGS_TRACKER_H_
+
+#include "src/trace_processor/trace_processor_context.h"
+#include "src/trace_processor/trace_storage.h"
+#include "src/trace_processor/variadic.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Interns args into the storage from all ArgsTrackers across trace processor.
+// Note: most users will want to use ArgsTracker to push args to the strorage
+// and not this class. This class is really intended for ArgsTracker to use for
+// that purpose.
+class GlobalArgsTracker {
+ public:
+  struct Arg {
+    StringId flat_key = 0;
+    StringId key = 0;
+    Variadic value = Variadic::Integer(0);
+
+    TableId table;
+    uint32_t row;
+  };
+
+  struct ArgHasher {
+    uint64_t operator()(const Arg& arg) const noexcept {
+      base::Hash hash;
+      hash.Update(arg.key);
+      // We don't hash arg.flat_key because it's a subsequence of arg.key.
+      switch (arg.value.type) {
+        case Variadic::Type::kInt:
+          hash.Update(arg.value.int_value);
+          break;
+        case Variadic::Type::kUint:
+          hash.Update(arg.value.uint_value);
+          break;
+        case Variadic::Type::kString:
+          hash.Update(arg.value.string_value);
+          break;
+        case Variadic::Type::kReal:
+          hash.Update(arg.value.real_value);
+          break;
+        case Variadic::Type::kPointer:
+          hash.Update(arg.value.pointer_value);
+          break;
+        case Variadic::Type::kBool:
+          hash.Update(arg.value.bool_value);
+          break;
+        case Variadic::Type::kJson:
+          hash.Update(arg.value.json_value);
+          break;
+      }
+      return hash.digest();
+    }
+  };
+
+  GlobalArgsTracker(TraceProcessorContext* context);
+
+  ArgSetId AddArgSet(const std::vector<Arg>& args,
+                     uint32_t begin,
+                     uint32_t end) {
+    base::Hash hash;
+    for (uint32_t i = begin; i < end; i++) {
+      hash.Update(ArgHasher()(args[i]));
+    }
+
+    auto* arg_table = context_->storage->mutable_arg_table();
+
+    ArgSetHash digest = hash.digest();
+    auto it = arg_row_for_hash_.find(digest);
+    if (it != arg_row_for_hash_.end())
+      return arg_table->arg_set_id()[it->second];
+
+    // The +1 ensures that nothing has an id == kInvalidArgSetId == 0.
+    ArgSetId id = static_cast<uint32_t>(arg_row_for_hash_.size()) + 1;
+    arg_row_for_hash_.emplace(digest, arg_table->row_count());
+    for (uint32_t i = begin; i < end; i++) {
+      const auto& arg = args[i];
+
+      tables::ArgTable::Row row;
+      row.arg_set_id = id;
+      row.flat_key = arg.flat_key;
+      row.key = arg.key;
+      switch (arg.value.type) {
+        case Variadic::Type::kInt:
+          row.int_value = arg.value.int_value;
+          break;
+        case Variadic::Type::kUint:
+          row.int_value = static_cast<int64_t>(arg.value.uint_value);
+          break;
+        case Variadic::Type::kString:
+          row.string_value = arg.value.string_value;
+          break;
+        case Variadic::Type::kReal:
+          row.real_value = arg.value.real_value;
+          break;
+        case Variadic::Type::kPointer:
+          row.int_value = static_cast<int64_t>(arg.value.pointer_value);
+          break;
+        case Variadic::Type::kBool:
+          row.int_value = arg.value.bool_value;
+          break;
+        case Variadic::Type::kJson:
+          row.string_value = arg.value.json_value;
+          break;
+      }
+      row.value_type = context_->storage->GetIdForVariadicType(arg.value.type);
+      arg_table->Insert(row);
+    }
+    return id;
+  }
+
+ private:
+  using ArgSetHash = uint64_t;
+
+  std::unordered_map<ArgSetHash, uint32_t> arg_row_for_hash_;
+
+  TraceProcessorContext* context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_GLOBAL_ARGS_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/args_table_utils_unittest.cc b/src/trace_processor/importers/proto/args_table_utils_unittest.cc
index 4cc03e9..d5e3eb1 100644
--- a/src/trace_processor/importers/proto/args_table_utils_unittest.cc
+++ b/src/trace_processor/importers/proto/args_table_utils_unittest.cc
@@ -44,24 +44,22 @@
   ArgsTableUtilsTest() {
     context_.storage.reset(new TraceStorage);
     storage_ = context_.storage.get();
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     sequence_state_.reset(new PacketSequenceState(&context_));
   }
 
   bool HasArg(ArgSetId set_id, const base::StringView& key, Variadic value) {
-    const auto& args = storage_->args();
+    const auto& args = storage_->arg_table();
     auto key_id = storage_->string_pool().GetId(key);
     EXPECT_TRUE(key_id);
-    auto rows =
-        std::equal_range(args.set_ids().begin(), args.set_ids().end(), set_id);
+
+    RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
     bool found = false;
-    for (; rows.first != rows.second; rows.first++) {
-      size_t index = static_cast<size_t>(
-          std::distance(args.set_ids().begin(), rows.first));
-      if (args.keys()[index] == key_id) {
-        EXPECT_EQ(args.flat_keys()[index], key_id);
-        if (args.flat_keys()[index] == key_id &&
-            args.arg_values()[index] == value) {
+    for (auto it = rm.IterateRows(); it; it.Next()) {
+      if (args.key()[it.row()] == key_id) {
+        EXPECT_EQ(args.flat_key()[it.row()], key_id);
+        if (storage_->GetArgValue(it.row()) == value) {
           found = true;
           break;
         }
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index f90418d..d6daa73 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -218,6 +218,7 @@
     storage_ = new NiceMock<MockTraceStorage>();
     context_.storage.reset(storage_);
     context_.track_tracker.reset(new TrackTracker(&context_));
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.metadata_tracker.reset(new MetadataTracker(&context_));
     event_ = new MockEventTracker(&context_);
@@ -260,22 +261,19 @@
   }
 
   bool HasArg(ArgSetId set_id, StringId key_id, Variadic value) {
-    const auto& args = storage_->args();
-    auto rows =
-        std::equal_range(args.set_ids().begin(), args.set_ids().end(), set_id);
-    for (; rows.first != rows.second; rows.first++) {
-      size_t index = static_cast<size_t>(
-          std::distance(args.set_ids().begin(), rows.first));
-      if (args.keys()[index] == key_id) {
-        EXPECT_EQ(args.flat_keys()[index], key_id);
-        EXPECT_EQ(args.arg_values()[index], value);
-        if (args.flat_keys()[index] == key_id &&
-            args.arg_values()[index] == value) {
-          return true;
+    const auto& args = storage_->arg_table();
+    RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
+    bool found = false;
+    for (auto it = rm.IterateRows(); it; it.Next()) {
+      if (args.key()[it.row()] == key_id) {
+        EXPECT_EQ(args.flat_key()[it.row()], key_id);
+        if (storage_->GetArgValue(it.row()) == value) {
+          found = true;
+          break;
         }
       }
     }
-    return false;
+    return found;
   }
 
  protected:
@@ -350,18 +348,16 @@
 
   const auto& raw = context_.storage->raw_events();
   ASSERT_EQ(raw.raw_event_count(), 2u);
-  const auto& args = context_.storage->args();
-  ASSERT_EQ(args.args_count(), 6u);
-  ASSERT_EQ(args.arg_values()[0].int_value, 123);
-  ASSERT_STREQ(
-      context_.storage->GetString(args.arg_values()[1].string_value).c_str(),
-      task_newtask);
-  ASSERT_EQ(args.arg_values()[2].int_value, 12);
-  ASSERT_EQ(args.arg_values()[3].int_value, 15);
-  ASSERT_EQ(args.arg_values()[4].int_value, 20);
-  ASSERT_STREQ(
-      context_.storage->GetString(args.arg_values()[5].string_value).c_str(),
-      buf_value);
+  const auto& args = context_.storage->arg_table();
+  ASSERT_EQ(args.row_count(), 6u);
+  ASSERT_EQ(args.int_value()[0], 123);
+  ASSERT_STREQ(context_.storage->GetString(args.string_value()[1]).c_str(),
+               task_newtask);
+  ASSERT_EQ(args.int_value()[2], 12);
+  ASSERT_EQ(args.int_value()[3], 15);
+  ASSERT_EQ(args.int_value()[4], 20);
+  ASSERT_STREQ(context_.storage->GetString(args.string_value()[5]).c_str(),
+               buf_value);
 
   // TODO(taylori): Add test ftrace event with all field types
   // and test here.
@@ -409,17 +405,15 @@
 
   auto set_id = raw.arg_set_ids().back();
 
-  const auto& args = storage_->args();
-  auto id_it =
-      std::equal_range(args.set_ids().begin(), args.set_ids().end(), set_id);
+  const auto& args = storage_->arg_table();
+  RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
 
   // Ignore string calls as they are handled by checking InternString calls
   // above.
 
-  auto it = id_it.first;
-  auto row = static_cast<size_t>(std::distance(args.set_ids().begin(), it));
-  ASSERT_EQ(args.arg_values()[++row].int_value, -2);
-  ASSERT_EQ(args.arg_values()[++row].int_value, 3);
+  auto row = rm.Get(0);
+  ASSERT_EQ(args.int_value()[++row], -2);
+  ASSERT_EQ(args.int_value()[++row], 3);
 }
 
 TEST_F(ProtoTraceParserTest, LoadMultipleEvents) {
@@ -2148,7 +2142,7 @@
   EXPECT_EQ(raw_events.utids()[0], 1u);
   EXPECT_EQ(raw_events.arg_set_ids()[0], 1u);
 
-  EXPECT_GE(storage_->args().args_count(), 13u);
+  EXPECT_GE(storage_->arg_table().row_count(), 13u);
 
   EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.category"),
                      Variadic::String(1u)));
@@ -2282,7 +2276,7 @@
             storage_->InternString("chrome_event.metadata"));
   EXPECT_EQ(raw_events.arg_set_ids()[0], 1u);
 
-  EXPECT_EQ(storage_->args().args_count(), 2u);
+  EXPECT_EQ(storage_->arg_table().row_count(), 2u);
   EXPECT_TRUE(HasArg(1u, storage_->InternString(kStringName),
                      Variadic::String(storage_->InternString(kStringValue))));
   EXPECT_TRUE(HasArg(1u, storage_->InternString(kIntName),
@@ -2316,7 +2310,7 @@
             storage_->InternString("chrome_event.legacy_system_trace"));
   EXPECT_EQ(raw_events.arg_set_ids()[0], 1u);
 
-  EXPECT_EQ(storage_->args().args_count(), 1u);
+  EXPECT_EQ(storage_->arg_table().row_count(), 1u);
   EXPECT_TRUE(HasArg(1u, storage_->InternString("data"),
                      Variadic::String(storage_->InternString(kFullData))));
 }
@@ -2347,7 +2341,7 @@
             storage_->InternString("chrome_event.legacy_user_trace"));
   EXPECT_EQ(raw_events.arg_set_ids()[0], 1u);
 
-  EXPECT_EQ(storage_->args().args_count(), 1u);
+  EXPECT_EQ(storage_->arg_table().row_count(), 1u);
   EXPECT_TRUE(
       HasArg(1u, storage_->InternString("data"),
              Variadic::String(storage_->InternString(kUserTraceEvent))));
@@ -2430,7 +2424,7 @@
   // The relevant arg sets have the info about the packages. To simplify test
   // structure, make an assumption that metadata storage is filled in in the
   // FIFO order of seen packages.
-  const auto& args = context_.storage->args();
+  const auto& args = context_.storage->arg_table();
   const auto& metadata = context_.storage->metadata_table();
 
   Table package_list = metadata.Filter(
@@ -2443,10 +2437,10 @@
 
   // helper to look up arg values
   auto find_arg = [&args, this](ArgSetId set_id, const char* arg_name) {
-    for (size_t i = 0; i < args.set_ids().size(); i++) {
-      if (args.set_ids()[i] == set_id &&
-          args.keys()[i] == storage_->InternString(arg_name))
-        return args.arg_values()[i];
+    for (uint32_t i = 0; i < args.row_count(); i++) {
+      if (args.arg_set_id()[i] == set_id &&
+          args.key()[i] == storage_->InternString(arg_name))
+        return storage_->GetArgValue(i);
     }
     PERFETTO_FATAL("Didn't find expected argument");
   };
diff --git a/src/trace_processor/process_tracker_unittest.cc b/src/trace_processor/process_tracker_unittest.cc
index 802ba42..6b6000e 100644
--- a/src/trace_processor/process_tracker_unittest.cc
+++ b/src/trace_processor/process_tracker_unittest.cc
@@ -34,6 +34,7 @@
  public:
   ProcessTrackerTest() {
     context.storage.reset(new TraceStorage());
+    context.global_args_tracker.reset(new GlobalArgsTracker(&context));
     context.args_tracker.reset(new ArgsTracker(&context));
     context.process_tracker.reset(new ProcessTracker(&context));
     context.event_tracker.reset(new EventTracker(&context));
diff --git a/src/trace_processor/raw_table.cc b/src/trace_processor/raw_table.cc
index ac8127f..f082695 100644
--- a/src/trace_processor/raw_table.cc
+++ b/src/trace_processor/raw_table.cc
@@ -121,12 +121,16 @@
 void RawTable::FormatSystraceArgs(NullTermStringView event_name,
                                   ArgSetId arg_set_id,
                                   base::StringWriter* writer) {
-  const auto& set_ids = storage_->args().set_ids();
-  auto lb = std::lower_bound(set_ids.begin(), set_ids.end(), arg_set_id);
-  auto ub = std::find(lb, set_ids.end(), arg_set_id + 1);
+  const auto& set_ids = storage_->arg_table().arg_set_id();
 
-  auto start_row = static_cast<uint32_t>(std::distance(set_ids.begin(), lb));
-  auto end_row = static_cast<uint32_t>(std::distance(set_ids.begin(), ub));
+  // TODO(lalitm): this code is quite hacky for performance reasons. We assume
+  // that the row map is a contiguous range (which is always the case
+  // because arg_set_ids are contiguous by definition). We also assume that
+  // the proto field order is also the order of insertion (which happens to
+  // be true but proabably shouldn't be relied on).
+  RowMap rm = storage_->arg_table().FilterToRowMap({set_ids.eq(arg_set_id)});
+
+  uint32_t start_row = rm.Get(0);
   using ValueWriter = std::function<void(const Variadic&)>;
   auto write_value = [this, writer](const Variadic& value) {
     switch (value.type) {
@@ -159,24 +163,22 @@
   };
   auto write_value_at_index = [this, start_row](uint32_t arg_idx,
                                                 ValueWriter value_fn) {
-    value_fn(storage_->args().arg_values()[start_row + arg_idx]);
+    value_fn(storage_->GetArgValue(start_row + arg_idx));
   };
   auto write_arg = [this, writer, start_row](uint32_t arg_idx,
                                              ValueWriter value_fn) {
     uint32_t arg_row = start_row + arg_idx;
-    const auto& args = storage_->args();
-    const auto& key = storage_->GetString(args.keys()[arg_row]);
+    const auto& args = storage_->arg_table();
+    const auto& key = storage_->GetString(args.key()[arg_row]);
+    auto value = storage_->GetArgValue(arg_row);
 
     writer->AppendChar(' ');
     writer->AppendString(key.c_str(), key.size());
     writer->AppendChar('=');
 
-    if (key == "gfp_flags" &&
-        ParseGfpFlags(args.arg_values()[arg_row], writer)) {
+    if (key == "gfp_flags" && ParseGfpFlags(value, writer))
       return;
-    }
-
-    value_fn(args.arg_values()[arg_row]);
+    value_fn(value);
   };
 
   if (event_name == "sched_switch") {
@@ -291,9 +293,8 @@
   } else if (event_name == "print") {
     // 'ip' may be the first field or it may be dropped. We only care
     // about the 'buf' field which will always appear last.
-    uint32_t arg_row = end_row - 1;
-    const auto& args = storage_->args();
-    const auto& value = args.arg_values()[arg_row];
+    uint32_t arg_row = rm.Get(rm.size() - 1);
+    const auto& value = storage_->GetArgValue(arg_row);
     const auto& str = storage_->GetString(value.string_value);
     // If the last character is a newline in a print, just drop it.
     auto chars_to_print = !str.empty() && str.c_str()[str.size() - 1] == '\n'
@@ -365,9 +366,8 @@
     return;
   }
 
-  uint32_t arg = 0;
-  for (auto it = lb; it != ub; it++) {
-    write_arg(arg++, write_value);
+  for (auto it = rm.IterateRows(); it; it.Next()) {
+    write_arg(it.index(), write_value);
   }
 }
 
diff --git a/src/trace_processor/sched_slice_table_unittest.cc b/src/trace_processor/sched_slice_table_unittest.cc
index f9131ae..7e624d0 100644
--- a/src/trace_processor/sched_slice_table_unittest.cc
+++ b/src/trace_processor/sched_slice_table_unittest.cc
@@ -41,6 +41,7 @@
     db_.reset(db);
 
     context_.storage.reset(new TraceStorage());
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.process_tracker.reset(new ProcessTracker(&context_));
     context_.event_tracker.reset(new EventTracker(&context_));
diff --git a/src/trace_processor/slice_tracker_unittest.cc b/src/trace_processor/slice_tracker_unittest.cc
index a38709e..fc74b01 100644
--- a/src/trace_processor/slice_tracker_unittest.cc
+++ b/src/trace_processor/slice_tracker_unittest.cc
@@ -73,6 +73,7 @@
 TEST(SliceTrackerTest, OneSliceWithArgs) {
   TraceProcessorContext context;
   context.storage.reset(new TraceStorage());
+  context.global_args_tracker.reset(new GlobalArgsTracker(&context));
   SliceTracker tracker(&context);
 
   constexpr TrackId track{22u};
@@ -97,15 +98,15 @@
   EXPECT_EQ(slices.depth()[0], 0u);
   auto set_id = slices.arg_set_id()[0];
 
-  auto args = context.storage->args();
-  EXPECT_EQ(args.set_ids()[0], set_id);
-  EXPECT_EQ(args.flat_keys()[0], 1u);
-  EXPECT_EQ(args.keys()[0], 2u);
-  EXPECT_EQ(args.arg_values()[0], Variadic::Integer(10));
-  EXPECT_EQ(args.set_ids()[1], set_id);
-  EXPECT_EQ(args.flat_keys()[1], 3u);
-  EXPECT_EQ(args.keys()[1], 4u);
-  EXPECT_EQ(args.arg_values()[1], Variadic::Integer(20));
+  const auto& args = context.storage->arg_table();
+  EXPECT_EQ(args.arg_set_id()[0], set_id);
+  EXPECT_EQ(args.flat_key()[0], 1u);
+  EXPECT_EQ(args.key()[0], 2u);
+  EXPECT_EQ(args.int_value()[0], 10);
+  EXPECT_EQ(args.arg_set_id()[1], set_id);
+  EXPECT_EQ(args.flat_key()[1], 3u);
+  EXPECT_EQ(args.key()[1], 4u);
+  EXPECT_EQ(args.int_value()[1], 20);
 }
 
 TEST(SliceTrackerTest, TwoSliceDetailed) {
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index 65ed66e..09c113d 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -45,6 +45,8 @@
       return FilterOp::kIsNotNull;
     case SQLITE_INDEX_CONSTRAINT_LIKE:
       return FilterOp::kLike;
+    case SQLITE_INDEX_CONSTRAINT_GLOB:
+      return FilterOp::kGlob;
     default:
       PERFETTO_FATAL("Currently unsupported constraint");
   }
diff --git a/src/trace_processor/tables/metadata_tables.h b/src/trace_processor/tables/metadata_tables.h
index 9a2095d..6e4e883 100644
--- a/src/trace_processor/tables/metadata_tables.h
+++ b/src/trace_processor/tables/metadata_tables.h
@@ -23,6 +23,19 @@
 namespace trace_processor {
 namespace tables {
 
+#define PERFETTO_TP_ARG_TABLE_DEF(NAME, PARENT, C) \
+  NAME(ArgTable, "args")                           \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                \
+  C(uint32_t, arg_set_id, Column::Flag::kSorted)   \
+  C(StringPool::Id, flat_key)                      \
+  C(StringPool::Id, key)                           \
+  C(base::Optional<int64_t>, int_value)            \
+  C(base::Optional<StringPool::Id>, string_value)  \
+  C(base::Optional<double>, real_value)            \
+  C(StringPool::Id, value_type)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_ARG_TABLE_DEF);
+
 #define PERFETTO_TP_METADATA_TABLE_DEF(NAME, PARENT, C) \
   NAME(MetadataTable, "metadata")                       \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                     \
diff --git a/src/trace_processor/thread_table_unittest.cc b/src/trace_processor/thread_table_unittest.cc
index 03c6647..fda86e1 100644
--- a/src/trace_processor/thread_table_unittest.cc
+++ b/src/trace_processor/thread_table_unittest.cc
@@ -38,6 +38,7 @@
     db_.reset(db);
 
     context_.storage.reset(new TraceStorage());
+    context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
     context_.args_tracker.reset(new ArgsTracker(&context_));
     context_.process_tracker.reset(new ProcessTracker(&context_));
     context_.event_tracker.reset(new EventTracker(&context_));
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index 52ec6de..bb5b6a0 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -20,6 +20,7 @@
 #include "src/trace_processor/chunked_trace_reader.h"
 #include "src/trace_processor/clock_tracker.h"
 #include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/global_args_tracker.h"
 #include "src/trace_processor/heap_profile_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
 #include "src/trace_processor/importers/json/json_trace_parser.h"
diff --git a/src/trace_processor/trace_processor_context.h b/src/trace_processor/trace_processor_context.h
index 10e6eb4..310005f 100644
--- a/src/trace_processor/trace_processor_context.h
+++ b/src/trace_processor/trace_processor_context.h
@@ -33,6 +33,7 @@
 class ClockTracker;
 class EventTracker;
 class FtraceModule;
+class GlobalArgsTracker;
 class HeapGraphTracker;
 class HeapProfileTracker;
 class MetadataTracker;
@@ -52,7 +53,6 @@
 
   std::unique_ptr<TraceStorage> storage;
   std::unique_ptr<TrackTracker> track_tracker;
-  std::unique_ptr<ArgsTracker> args_tracker;
   std::unique_ptr<SliceTracker> slice_tracker;
   std::unique_ptr<ProcessTracker> process_tracker;
   std::unique_ptr<EventTracker> event_tracker;
@@ -63,6 +63,11 @@
   std::unique_ptr<HeapProfileTracker> heap_profile_tracker;
   std::unique_ptr<MetadataTracker> metadata_tracker;
 
+  // Keep the global tracker before the args tracker as we access the global
+  // tracker in the destructor of the args tracker.
+  std::unique_ptr<GlobalArgsTracker> global_args_tracker;
+  std::unique_ptr<ArgsTracker> args_tracker;
+
   // These fields are stored as pointers to Destructible objects rather than
   // their actual type (a subclass of Destructible), as the concrete subclass
   // type is only available in the storage_full target. To access these fields,
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index c843df7..6863de2 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -23,7 +23,6 @@
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "src/trace_processor/args_table.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
 #include "src/trace_processor/process_table.h"
 #include "src/trace_processor/raw_table.h"
@@ -369,7 +368,6 @@
 
   SetupMetrics(this, *db_, &sql_metrics_);
 
-  ArgsTable::RegisterTable(*db_, context_.storage.get());
   ProcessTable::RegisterTable(*db_, context_.storage.get());
   SchedSliceTable::RegisterTable(*db_, context_.storage.get());
   SqlStatsTable::RegisterTable(*db_, context_.storage.get());
@@ -382,6 +380,9 @@
   // New style db-backed tables.
   const TraceStorage* storage = context_.storage.get();
 
+  DbSqliteTable::RegisterTable(*db_, &storage->arg_table(),
+                               storage->arg_table().table_name());
+
   DbSqliteTable::RegisterTable(*db_, &storage->slice_table(),
                                storage->slice_table().table_name());
   DbSqliteTable::RegisterTable(*db_, &storage->instant_table(),
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 0ad11ee..a8d8dee 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -48,6 +48,7 @@
   context_.clock_tracker.reset(new ClockTracker(&context_));
   context_.heap_profile_tracker.reset(new HeapProfileTracker(&context_));
   context_.metadata_tracker.reset(new MetadataTracker(&context_));
+  context_.global_args_tracker.reset(new GlobalArgsTracker(&context_));
 
   context_.modules.emplace_back(new FtraceModule());
   // Ftrace module is special, because it has one extra method for parsing
diff --git a/src/trace_processor/trace_storage.cc b/src/trace_processor/trace_storage.cc
index 4d96c7e..aa25f59 100644
--- a/src/trace_processor/trace_storage.cc
+++ b/src/trace_processor/trace_storage.cc
@@ -82,6 +82,10 @@
   // Upid/utid 0 is reserved for idle processes/threads.
   unique_processes_.emplace_back(0);
   unique_threads_.emplace_back(0);
+
+  for (uint32_t i = 0; i < variadic_type_ids_.size(); ++i) {
+    variadic_type_ids_[i] = InternString(Variadic::kTypeNames[i]);
+  }
 }
 
 TraceStorage::~TraceStorage() {}
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 83a609b..39f021f 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -137,96 +137,6 @@
     uint32_t tid = 0;
   };
 
-  // Generic key value storage which can be referenced by other tables.
-  class Args {
-   public:
-    struct Arg {
-      StringId flat_key = 0;
-      StringId key = 0;
-      Variadic value = Variadic::Integer(0);
-
-      TableId table;
-      uint32_t row;
-    };
-
-    struct ArgHasher {
-      uint64_t operator()(const Arg& arg) const noexcept {
-        base::Hash hash;
-        hash.Update(arg.key);
-        // We don't hash arg.flat_key because it's a subsequence of arg.key.
-        switch (arg.value.type) {
-          case Variadic::Type::kInt:
-            hash.Update(arg.value.int_value);
-            break;
-          case Variadic::Type::kUint:
-            hash.Update(arg.value.uint_value);
-            break;
-          case Variadic::Type::kString:
-            hash.Update(arg.value.string_value);
-            break;
-          case Variadic::Type::kReal:
-            hash.Update(arg.value.real_value);
-            break;
-          case Variadic::Type::kPointer:
-            hash.Update(arg.value.pointer_value);
-            break;
-          case Variadic::Type::kBool:
-            hash.Update(arg.value.bool_value);
-            break;
-          case Variadic::Type::kJson:
-            hash.Update(arg.value.json_value);
-            break;
-        }
-        return hash.digest();
-      }
-    };
-
-    const std::deque<ArgSetId>& set_ids() const { return set_ids_; }
-    const std::deque<StringId>& flat_keys() const { return flat_keys_; }
-    const std::deque<StringId>& keys() const { return keys_; }
-    const std::deque<Variadic>& arg_values() const { return arg_values_; }
-    uint32_t args_count() const {
-      return static_cast<uint32_t>(set_ids_.size());
-    }
-
-    ArgSetId AddArgSet(const std::vector<Arg>& args,
-                       uint32_t begin,
-                       uint32_t end) {
-      base::Hash hash;
-      for (uint32_t i = begin; i < end; i++) {
-        hash.Update(ArgHasher()(args[i]));
-      }
-
-      ArgSetHash digest = hash.digest();
-      auto it = arg_row_for_hash_.find(digest);
-      if (it != arg_row_for_hash_.end()) {
-        return set_ids_[it->second];
-      }
-
-      // The +1 ensures that nothing has an id == kInvalidArgSetId == 0.
-      ArgSetId id = static_cast<uint32_t>(arg_row_for_hash_.size()) + 1;
-      arg_row_for_hash_.emplace(digest, args_count());
-      for (uint32_t i = begin; i < end; i++) {
-        const auto& arg = args[i];
-        set_ids_.emplace_back(id);
-        flat_keys_.emplace_back(arg.flat_key);
-        keys_.emplace_back(arg.key);
-        arg_values_.emplace_back(arg.value);
-      }
-      return id;
-    }
-
-   private:
-    using ArgSetHash = uint64_t;
-
-    std::deque<ArgSetId> set_ids_;
-    std::deque<StringId> flat_keys_;
-    std::deque<StringId> keys_;
-    std::deque<Variadic> arg_values_;
-
-    std::unordered_map<ArgSetHash, uint32_t> arg_row_for_hash_;
-  };
-
   class Slices {
    public:
     inline size_t AddSlice(uint32_t cpu,
@@ -704,8 +614,8 @@
   }
   tables::MetadataTable* mutable_metadata_table() { return &metadata_table_; }
 
-  const Args& args() const { return args_; }
-  Args* mutable_args() { return &args_; }
+  const tables::ArgTable& arg_table() const { return arg_table_; }
+  tables::ArgTable* mutable_arg_table() { return &arg_table_; }
 
   const RawEvents& raw_events() const { return raw_events_; }
   RawEvents* mutable_raw_events() { return &raw_events_; }
@@ -828,6 +738,43 @@
     stack_profile_frame_index_[pair].emplace_back(row);
   }
 
+  Variadic GetArgValue(uint32_t row) const {
+    Variadic v;
+    v.type = *GetVariadicTypeForId(arg_table_.value_type()[row]);
+
+    // Force initialization of union to stop GCC complaining.
+    v.int_value = 0;
+
+    switch (v.type) {
+      case Variadic::Type::kBool:
+        v.bool_value = static_cast<bool>(*arg_table_.int_value()[row]);
+        break;
+      case Variadic::Type::kInt:
+        v.int_value = *arg_table_.int_value()[row];
+        break;
+      case Variadic::Type::kUint:
+        v.uint_value = static_cast<uint64_t>(*arg_table_.int_value()[row]);
+        break;
+      case Variadic::Type::kString:
+        v.string_value = arg_table_.string_value()[row];
+        break;
+      case Variadic::Type::kPointer:
+        v.pointer_value = static_cast<uint64_t>(*arg_table_.int_value()[row]);
+        break;
+      case Variadic::Type::kReal:
+        v.real_value = *arg_table_.real_value()[row];
+        break;
+      case Variadic::Type::kJson:
+        v.json_value = arg_table_.string_value()[row];
+        break;
+    }
+    return v;
+  }
+
+  StringId GetIdForVariadicType(Variadic::Type type) const {
+    return variadic_type_ids_[type];
+  }
+
  private:
   using StringHash = uint64_t;
 
@@ -837,6 +784,16 @@
   TraceStorage(TraceStorage&&) = delete;
   TraceStorage& operator=(TraceStorage&&) = delete;
 
+  base::Optional<Variadic::Type> GetVariadicTypeForId(StringId id) const {
+    auto it =
+        std::find(variadic_type_ids_.begin(), variadic_type_ids_.end(), id);
+    if (it == variadic_type_ids_.end())
+      return base::nullopt;
+
+    int64_t idx = std::distance(variadic_type_ids_.begin(), it);
+    return static_cast<Variadic::Type>(idx);
+  }
+
   // TODO(lalitm): remove this when we find a better home for this.
   using MappingKey = std::pair<StringId /* name */, StringId /* build id */>;
   std::map<MappingKey, std::vector<int64_t>> stack_profile_mapping_index_;
@@ -881,7 +838,7 @@
   Slices slices_;
 
   // Args for all other tables.
-  Args args_;
+  tables::ArgTable arg_table_{&string_pool_, nullptr};
 
   // One entry for each UniquePid, with UniquePid as the index.
   // Never hold on to pointers to Process, as vector resize will
@@ -942,6 +899,10 @@
 
   tables::VulkanMemoryAllocationsTable vulkan_memory_allocations_table_{
       &string_pool_, nullptr};
+
+  // The below array allow us to map between enums and their string
+  // representations.
+  std::array<StringId, Variadic::kMaxType + 1> variadic_type_ids_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/variadic.cc b/src/trace_processor/variadic.cc
new file mode 100644
index 0000000..edfde74
--- /dev/null
+++ b/src/trace_processor/variadic.cc
@@ -0,0 +1,25 @@
+/*
+ * 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 "src/trace_processor/variadic.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+constexpr const char* Variadic::kTypeNames[];
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/variadic.h b/src/trace_processor/variadic.h
index 0444721..58aa935 100644
--- a/src/trace_processor/variadic.h
+++ b/src/trace_processor/variadic.h
@@ -24,7 +24,19 @@
 
 // Variadic type representing value of different possible types.
 struct Variadic {
-  enum Type { kInt, kUint, kString, kReal, kPointer, kBool, kJson };
+  enum Type : size_t {
+    kInt,
+    kUint,
+    kString,
+    kReal,
+    kPointer,
+    kBool,
+    kJson,
+    kMaxType = kJson,
+  };
+
+  static constexpr const char* const kTypeNames[] = {
+      "int", "uint", "string", "real", "pointer", "bool", "json"};
 
   static Variadic Integer(int64_t int_value) {
     Variadic variadic;