Export trace proto content to SQL table


The feature is controlled by a new analyze_trace_proto_content Trace Processor Config field, and is disabled by default.

The new SQL table is experimental_proto_content (with the format being subject to arbitrary breaking changes for now), and it's effectively a mapping proto field path -> total size.

Once Perfetto UI supports arbitrary flamegraphs (b/227644078), we can change the SQL table format to make it flamegraph compatible.

Bug: b/238062136
Change-Id: I7e2b6921aba9e660f4c820de0047af7e00b5e9ee
diff --git a/Android.bp b/Android.bp
index 44a423b..56f9d47 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1917,6 +1917,7 @@
         ":perfetto_protos_perfetto_trace_translation_cpp_gen",
         ":perfetto_protos_perfetto_trace_translation_lite_gen",
         ":perfetto_protos_perfetto_trace_translation_zero_gen",
+        ":perfetto_protos_third_party_pprof_zero_gen",
         ":perfetto_protos_third_party_statsd_config_zero_gen",
         ":perfetto_src_android_internal_headers",
         ":perfetto_src_android_internal_lazy_library_loader",
@@ -1976,6 +1977,7 @@
         ":perfetto_src_trace_processor_util_descriptors",
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
+        ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
         ":perfetto_src_trace_processor_util_stack_traces_util",
@@ -2144,11 +2146,13 @@
         "perfetto_protos_perfetto_trace_translation_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_translation_lite_gen_headers",
         "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
+        "perfetto_protos_third_party_pprof_zero_gen_headers",
         "perfetto_protos_third_party_statsd_config_zero_gen_headers",
         "perfetto_src_base_version_gen_h",
         "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+        "perfetto_src_trace_processor_importers_gen_cc_trace_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -9039,6 +9043,21 @@
     ],
 }
 
+// GN: //src/trace_processor/importers:gen_cc_trace_descriptor
+genrule {
+    name: "perfetto_src_trace_processor_importers_gen_cc_trace_descriptor",
+    srcs: [
+        ":perfetto_protos_perfetto_trace_descriptor",
+    ],
+    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/importers/trace.descriptor.h",
+    ],
+    tool_files: [
+        "tools/gen_cc_proto_descriptor.py",
+    ],
+}
+
 // GN: //src/trace_processor/importers:gen_cc_track_event_descriptor
 genrule {
     name: "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
@@ -9391,6 +9410,7 @@
         "src/trace_processor/importers/proto/android_probes_module.cc",
         "src/trace_processor/importers/proto/android_probes_parser.cc",
         "src/trace_processor/importers/proto/android_probes_tracker.cc",
+        "src/trace_processor/importers/proto/content_analyzer.cc",
         "src/trace_processor/importers/proto/frame_timeline_event_parser.cc",
         "src/trace_processor/importers/proto/gpu_event_parser.cc",
         "src/trace_processor/importers/proto/graphics_event_module.cc",
@@ -11092,6 +11112,7 @@
         "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+        "perfetto_src_trace_processor_importers_gen_cc_trace_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -11194,6 +11215,7 @@
         ":perfetto_protos_perfetto_trace_system_info_zero_gen",
         ":perfetto_protos_perfetto_trace_track_event_zero_gen",
         ":perfetto_protos_perfetto_trace_translation_zero_gen",
+        ":perfetto_protos_third_party_pprof_zero_gen",
         ":perfetto_src_base_base",
         ":perfetto_src_base_http_http",
         ":perfetto_src_base_unix_socket",
@@ -11230,6 +11252,7 @@
         ":perfetto_src_trace_processor_util_descriptors",
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
+        ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
         ":perfetto_src_trace_processor_util_stack_traces_util",
@@ -11277,10 +11300,12 @@
         "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
         "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
         "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
+        "perfetto_protos_third_party_pprof_zero_gen_headers",
         "perfetto_src_base_version_gen_h",
         "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+        "perfetto_src_trace_processor_importers_gen_cc_trace_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -11411,6 +11436,7 @@
         ":perfetto_src_trace_processor_util_descriptors",
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
+        ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
         ":perfetto_src_trace_processor_util_stack_traces_util",
@@ -11467,6 +11493,7 @@
         "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+        "perfetto_src_trace_processor_importers_gen_cc_trace_descriptor",
         "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
diff --git a/BUILD b/BUILD
index cc6ab20..c66d42e 100644
--- a/BUILD
+++ b/BUILD
@@ -1192,6 +1192,16 @@
 )
 
 perfetto_cc_proto_descriptor(
+    name = "src_trace_processor_importers_gen_cc_trace_descriptor",
+    deps = [
+        ":protos_perfetto_trace_descriptor",
+    ],
+    outs = [
+        "src/trace_processor/importers/trace.descriptor.h",
+    ],
+)
+
+perfetto_cc_proto_descriptor(
     name = "src_trace_processor_importers_gen_cc_track_event_descriptor",
     deps = [
         ":protos_perfetto_trace_track_event_descriptor",
@@ -1461,6 +1471,7 @@
         "src/trace_processor/tables/profiler_tables.h",
         "src/trace_processor/tables/slice_tables.h",
         "src/trace_processor/tables/table_destructors.cc",
+        "src/trace_processor/tables/trace_proto_tables.h",
         "src/trace_processor/tables/track_tables.h",
     ],
 )
@@ -1510,6 +1521,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/util:proto_profiler
+perfetto_filegroup(
+    name = "src_trace_processor_util_proto_profiler",
+    srcs = [
+        "src/trace_processor/util/proto_profiler.cc",
+        "src/trace_processor/util/proto_profiler.h",
+    ],
+)
+
 # GN target: //src/trace_processor/util:proto_to_args_parser
 perfetto_filegroup(
     name = "src_trace_processor_util_proto_to_args_parser",
@@ -1673,6 +1693,8 @@
         "src/trace_processor/importers/proto/android_probes_parser.h",
         "src/trace_processor/importers/proto/android_probes_tracker.cc",
         "src/trace_processor/importers/proto/android_probes_tracker.h",
+        "src/trace_processor/importers/proto/content_analyzer.cc",
+        "src/trace_processor/importers/proto/content_analyzer.h",
         "src/trace_processor/importers/proto/frame_timeline_event_parser.cc",
         "src/trace_processor/importers/proto/frame_timeline_event_parser.h",
         "src/trace_processor/importers/proto/gpu_event_parser.cc",
@@ -4142,6 +4164,7 @@
         ":src_trace_processor_util_descriptors",
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
+        ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
         ":src_trace_processor_util_stack_traces_util",
@@ -4198,12 +4221,14 @@
                ":protos_perfetto_trace_system_info_zero",
                ":protos_perfetto_trace_track_event_zero",
                ":protos_perfetto_trace_translation_zero",
+               ":protos_third_party_pprof_zero",
                ":protozero",
                ":src_base_base",
                ":src_trace_processor_containers_containers",
                ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
                ":src_trace_processor_importers_gen_cc_config_descriptor",
                ":src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+               ":src_trace_processor_importers_gen_cc_trace_descriptor",
                ":src_trace_processor_importers_gen_cc_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -4262,6 +4287,7 @@
         ":src_trace_processor_util_descriptors",
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
+        ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
         ":src_trace_processor_util_stack_traces_util",
@@ -4309,6 +4335,7 @@
                ":protos_perfetto_trace_system_info_zero",
                ":protos_perfetto_trace_track_event_zero",
                ":protos_perfetto_trace_translation_zero",
+               ":protos_third_party_pprof_zero",
                ":protozero",
                ":src_base_base",
                ":src_base_http_http",
@@ -4317,6 +4344,7 @@
                ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
                ":src_trace_processor_importers_gen_cc_config_descriptor",
                ":src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+               ":src_trace_processor_importers_gen_cc_trace_descriptor",
                ":src_trace_processor_importers_gen_cc_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -4439,6 +4467,7 @@
         ":src_trace_processor_util_descriptors",
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
+        ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
         ":src_trace_processor_util_stack_traces_util",
@@ -4495,6 +4524,7 @@
                ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
                ":src_trace_processor_importers_gen_cc_config_descriptor",
                ":src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+               ":src_trace_processor_importers_gen_cc_trace_descriptor",
                ":src_trace_processor_importers_gen_cc_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
diff --git a/include/perfetto/trace_processor/basic_types.h b/include/perfetto/trace_processor/basic_types.h
index 58c261f..1ca649a 100644
--- a/include/perfetto/trace_processor/basic_types.h
+++ b/include/perfetto/trace_processor/basic_types.h
@@ -118,6 +118,15 @@
   // Any built-in metric proto or sql files matching these paths are skipped
   // during trace processor metric initialization.
   std::vector<std::string> skip_builtin_metric_paths;
+
+  // When set to true, the trace processor analyzes trace proto content, and
+  // exports the field path -> total size mapping into an SQL table.
+  //
+  // The analysis feature is hidden behind the flag so that the users who don't
+  // need this feature don't pay the performance costs.
+  //
+  // The flag has no impact on non-proto traces.
+  bool analyze_trace_proto_content = false;
 };
 
 // Represents a dynamically typed value returned by SQL.
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 600a8f5..05bcb4f 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -257,6 +257,8 @@
     "importers/proto/android_probes_parser.h",
     "importers/proto/android_probes_tracker.cc",
     "importers/proto/android_probes_tracker.h",
+    "importers/proto/content_analyzer.cc",
+    "importers/proto/content_analyzer.h",
     "importers/proto/frame_timeline_event_parser.cc",
     "importers/proto/frame_timeline_event_parser.h",
     "importers/proto/gpu_event_parser.cc",
@@ -294,6 +296,7 @@
     "../../protos/perfetto/trace/gpu:zero",
     "../../protos/perfetto/trace/interned_data:zero",
     "../protozero",
+    "importers:gen_cc_trace_descriptor",
     "importers/android_bugreport",
     "importers/common",
     "importers/proto:storage_full",
@@ -303,6 +306,7 @@
     "types",
     "util",
     "util:gzip",
+    "util:proto_profiler",
     "views",
   ]
   if (enable_perfetto_trace_processor_json) {
diff --git a/src/trace_processor/importers/additional_modules.cc b/src/trace_processor/importers/additional_modules.cc
index 7fbbeb5..b531a22 100644
--- a/src/trace_processor/importers/additional_modules.cc
+++ b/src/trace_processor/importers/additional_modules.cc
@@ -17,6 +17,7 @@
 #include "src/trace_processor/importers/additional_modules.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module_impl.h"
 #include "src/trace_processor/importers/proto/android_probes_module.h"
+#include "src/trace_processor/importers/proto/content_analyzer.h"
 #include "src/trace_processor/importers/proto/graphics_event_module.h"
 #include "src/trace_processor/importers/proto/heap_graph_module.h"
 #include "src/trace_processor/importers/proto/statsd_module.h"
@@ -33,6 +34,9 @@
   context->modules.emplace_back(new SystemProbesModule(context));
   context->modules.emplace_back(new TranslationTableModule(context));
   context->modules.emplace_back(new StatsdModule(context));
+  if (context->config.analyze_trace_proto_content) {
+    context->modules.emplace_back(new ContentAnalyzerModule(context));
+  }
 
   // Ftrace module is special, because it has one extra method for parsing
   // ftrace packets. So we need to store a pointer to it separately.
diff --git a/src/trace_processor/importers/proto/content_analyzer.cc b/src/trace_processor/importers/proto/content_analyzer.cc
new file mode 100644
index 0000000..eda59c6
--- /dev/null
+++ b/src/trace_processor/importers/proto/content_analyzer.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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 <numeric>
+#include <utility>
+
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/proto/content_analyzer.h"
+#include "src/trace_processor/importers/trace.descriptor.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+ContentAnalyzerModule::ContentAnalyzerModule(TraceProcessorContext* context)
+    : context_(context) {
+  base::Status status = pool_.AddFromFileDescriptorSet(kTraceDescriptor.data(),
+                                                       kTraceDescriptor.size());
+  if (!status.ok()) {
+    PERFETTO_ELOG("Could not add TracePacket proto descriptor %s",
+                  status.c_message());
+  }
+  RegisterForAllFields(context_);
+}
+
+ModuleResult ContentAnalyzerModule::TokenizePacket(
+    const protos::pbzero::TracePacket_Decoder&,
+    TraceBlobView* packet,
+    int64_t /*packet_timestamp*/,
+    PacketSequenceState*,
+    uint32_t /*field_id*/) {
+  util::SizeProfileComputer computer(&pool_);
+  auto packet_samples = computer.Compute(packet->data(), packet->length(),
+                                         ".perfetto.protos.TracePacket");
+  for (auto it = packet_samples.GetIterator(); it; ++it) {
+    auto& aggregated_samples_for_path = aggregated_samples_[it.key()];
+    aggregated_samples_for_path.insert(aggregated_samples_for_path.end(),
+                                       it.value().begin(), it.value().end());
+  }
+  return ModuleResult::Ignored();
+}
+
+void ContentAnalyzerModule::NotifyEndOfFile() {
+  // TODO(kraskevich): consider generating a flamegraph-compatable table once
+  // Perfetto UI supports custom flamegraphs (b/227644078).
+  for (auto it = aggregated_samples_.GetIterator(); it; ++it) {
+    auto field_path = base::Join(it.key(), ".");
+    auto total_size = std::accumulate(it.value().begin(), it.value().end(), 0L);
+    tables::ExperimentalProtoContentTable::Row row;
+    row.path = context_->storage->InternString(base::StringView(field_path));
+    row.total_size = total_size;
+    context_->storage->mutable_experimental_proto_content_table()->Insert(row);
+  }
+  aggregated_samples_.Clear();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/content_analyzer.h b/src/trace_processor/importers/proto/content_analyzer.h
new file mode 100644
index 0000000..7406fbb
--- /dev/null
+++ b/src/trace_processor/importers/proto/content_analyzer.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CONTENT_ANALYZER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CONTENT_ANALYZER_H_
+
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/proto_profiler.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Computes a trace proto size breakdown by field path, and exports the data to
+// an SQL table.
+class ContentAnalyzerModule : public ProtoImporterModule {
+ public:
+  explicit ContentAnalyzerModule(TraceProcessorContext* context);
+
+  ~ContentAnalyzerModule() override = default;
+
+  ModuleResult TokenizePacket(const protos::pbzero::TracePacket_Decoder&,
+                              TraceBlobView* packet,
+                              int64_t packet_timestamp,
+                              PacketSequenceState*,
+                              uint32_t field_id) override;
+
+  void NotifyEndOfFile() override;
+
+ private:
+  TraceProcessorContext* context_;
+  DescriptorPool pool_;
+  util::SizeProfileComputer::PathToSamplesMap aggregated_samples_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CONTENT_ANALYZER_H_
diff --git a/src/trace_processor/importers/proto/proto_importer_module.cc b/src/trace_processor/importers/proto/proto_importer_module.cc
index c171360..a25e744 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.cc
+++ b/src/trace_processor/importers/proto/proto_importer_module.cc
@@ -51,5 +51,9 @@
   context->modules_by_field[field_id].push_back(this);
 }
 
+void ProtoImporterModule::RegisterForAllFields(TraceProcessorContext* context) {
+  context->modules_for_all_fields.push_back(this);
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/proto_importer_module.h b/src/trace_processor/importers/proto/proto_importer_module.h
index c56f034..6064f0a 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.h
+++ b/src/trace_processor/importers/proto/proto_importer_module.h
@@ -131,6 +131,10 @@
 
  protected:
   void RegisterForField(uint32_t field_id, TraceProcessorContext*);
+  // Primarily intended for special modules that need to get all TracePacket's,
+  // for example for trace proto content analysis. Most modules need to register
+  // for specific fields using the method above.
+  void RegisterForAllFields(TraceProcessorContext*);
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 5732188..a5be0d1 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -107,6 +107,10 @@
   auto& modules = context_->modules_by_field;
   for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
     if (!modules[field_id].empty() && packet.Get(field_id).valid()) {
+      for (ProtoImporterModule* global_module :
+           context_->modules_for_all_fields) {
+        global_module->ParsePacket(packet, ttp, field_id);
+      }
       for (ProtoImporterModule* module : modules[field_id])
         module->ParsePacket(packet, ttp, field_id);
       return;
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index 9362756..e216886 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -198,6 +198,13 @@
   auto& modules = context_->modules_by_field;
   for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
     if (!modules[field_id].empty() && decoder.Get(field_id).valid()) {
+      for (ProtoImporterModule* global_module :
+           context_->modules_for_all_fields) {
+        ModuleResult res = global_module->TokenizePacket(
+            decoder, &packet, timestamp, state, field_id);
+        if (!res.ignored())
+          return res.ToStatus();
+      }
       for (ProtoImporterModule* module : modules[field_id]) {
         ModuleResult res = module->TokenizePacket(decoder, &packet, timestamp,
                                                   state, field_id);
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 3368f4d..04e3725 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -43,6 +43,7 @@
 #include "src/trace_processor/tables/metadata_tables.h"
 #include "src/trace_processor/tables/profiler_tables.h"
 #include "src/trace_processor/tables/slice_tables.h"
+#include "src/trace_processor/tables/trace_proto_tables.h"
 #include "src/trace_processor/tables/track_tables.h"
 #include "src/trace_processor/types/variadic.h"
 #include "src/trace_processor/views/slice_views.h"
@@ -660,6 +661,15 @@
     return &actual_frame_timeline_slice_table_;
   }
 
+  const tables::ExperimentalProtoContentTable&
+  experimental_proto_content_table() const {
+    return experimental_proto_content_table_;
+  }
+  tables::ExperimentalProtoContentTable*
+  mutable_experimental_proto_content_table() {
+    return &experimental_proto_content_table_;
+  }
+
   const views::ThreadSliceView& thread_slice_view() const {
     return thread_slice_view_;
   }
@@ -884,6 +894,9 @@
   tables::ActualFrameTimelineSliceTable actual_frame_timeline_slice_table_{
       &string_pool_, &slice_table_};
 
+  tables::ExperimentalProtoContentTable experimental_proto_content_table_{
+      &string_pool_, nullptr};
+
   views::ThreadSliceView thread_slice_view_{&slice_table_, &thread_track_table_,
                                             &thread_table_};
 
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index 0e10681..a9e29f3 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -26,6 +26,7 @@
     "profiler_tables.h",
     "slice_tables.h",
     "table_destructors.cc",
+    "trace_proto_tables.h",
     "track_tables.h",
   ]
   deps = [
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index c84fb9b..e1a074d 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -21,6 +21,7 @@
 #include "src/trace_processor/tables/metadata_tables.h"
 #include "src/trace_processor/tables/profiler_tables.h"
 #include "src/trace_processor/tables/slice_tables.h"
+#include "src/trace_processor/tables/trace_proto_tables.h"
 #include "src/trace_processor/tables/track_tables.h"
 
 namespace perfetto {
@@ -93,6 +94,9 @@
 GpuCounterTrackTable::~GpuCounterTrackTable() = default;
 PerfCounterTrackTable::~PerfCounterTrackTable() = default;
 
+// trace_proto_tables.h
+ExperimentalProtoContentTable::~ExperimentalProtoContentTable() = default;
+
 // memory_tables.h
 MemorySnapshotTable::~MemorySnapshotTable() = default;
 ProcessMemorySnapshotTable::~ProcessMemorySnapshotTable() = default;
diff --git a/src/trace_processor/tables/trace_proto_tables.h b/src/trace_processor/tables/trace_proto_tables.h
new file mode 100644
index 0000000..8c61194
--- /dev/null
+++ b/src/trace_processor/tables/trace_proto_tables.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_TABLES_TRACE_PROTO_TABLES_H_
+#define SRC_TRACE_PROCESSOR_TABLES_TRACE_PROTO_TABLES_H_
+
+#include "src/trace_processor/tables/macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace tables {
+
+// Experimental table, subject to arbitrary breaking changes.
+#define PERFETTO_TP_EXPERIMENTAL_PROTO_CONTENT_TABLE_DEF(NAME, PARENT, C) \
+  NAME(ExperimentalProtoContentTable, "experimental_proto_content")       \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                                       \
+  C(StringPool::Id, path)                                                 \
+  C(int64_t, total_size)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_EXPERIMENTAL_PROTO_CONTENT_TABLE_DEF);
+
+}  // namespace tables
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_TABLES_TRACE_PROTO_TABLES_H_
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 3500d24..c0233ad 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -1070,6 +1070,8 @@
   RegisterDbTable(storage->process_memory_snapshot_table());
   RegisterDbTable(storage->memory_snapshot_node_table());
   RegisterDbTable(storage->memory_snapshot_edge_table());
+
+  RegisterDbTable(storage->experimental_proto_content_table());
 }
 
 TraceProcessorImpl::~TraceProcessorImpl() = default;
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 95376c1..36d88f9 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -125,6 +125,9 @@
   // TracePacket.
   std::vector<std::vector<ProtoImporterModule*>> modules_by_field;
   std::vector<std::unique_ptr<ProtoImporterModule>> modules;
+  // Pointers to modules from the modules vector that need to be called for
+  // all fields.
+  std::vector<ProtoImporterModule*> modules_for_all_fields;
   FtraceModule* ftrace_module = nullptr;
 
   // Marks whether the uuid was read from the trace.
diff --git a/src/trace_processor/util/proto_profiler.cc b/src/trace_processor/util/proto_profiler.cc
index 95c896e..7bc79fe 100644
--- a/src/trace_processor/util/proto_profiler.cc
+++ b/src/trace_processor/util/proto_profiler.cc
@@ -49,12 +49,10 @@
 
 SizeProfileComputer::SizeProfileComputer(DescriptorPool* pool) : pool_(pool) {}
 
-base::FlatHashMap<SizeProfileComputer::FieldPath,
-                  SizeProfileComputer::SizeSamples,
-                  SizeProfileComputer::FieldPathHasher>
-SizeProfileComputer::Compute(const uint8_t* ptr,
-                             size_t size,
-                             const std::string& message_type) {
+SizeProfileComputer::PathToSamplesMap SizeProfileComputer::Compute(
+    const uint8_t* ptr,
+    size_t size,
+    const std::string& message_type) {
   ComputeInner(ptr, size, message_type);
   return std::move(path_to_samples_);
 }
diff --git a/src/trace_processor/util/proto_profiler.h b/src/trace_processor/util/proto_profiler.h
index 6541b21..6a5c858 100644
--- a/src/trace_processor/util/proto_profiler.h
+++ b/src/trace_processor/util/proto_profiler.h
@@ -46,13 +46,18 @@
     }
   };
 
+  using PathToSamplesMap =
+      base::FlatHashMap<FieldPath, SizeSamples, FieldPathHasher>;
+
   explicit SizeProfileComputer(DescriptorPool* pool);
 
   // Returns a list of samples (i.e. all encountered field sizes) for each
   // field path in trace proto.
   // TODO(kraskevich): consider switching to internal DescriptorPool.
-  base::FlatHashMap<FieldPath, SizeSamples, FieldPathHasher>
-  Compute(const uint8_t* ptr, size_t size, const std::string& message_type);
+
+  PathToSamplesMap Compute(const uint8_t* ptr,
+                           size_t size,
+                           const std::string& message_type);
 
  private:
   void ComputeInner(const uint8_t* ptr,
@@ -72,7 +77,7 @@
   std::vector<std::string> stack_;
 
   // Information about each field path seen.
-  base::FlatHashMap<FieldPath, SizeSamples, FieldPathHasher> path_to_samples_;
+  PathToSamplesMap path_to_samples_;
 };
 
 }  // namespace util