Merge changes Idcc16653,I6c774825

* changes:
  trace_processor: Import statsd atom trace events
  bazel: Fix arm64 build
diff --git a/Android.bp b/Android.bp
index 1c4d613..89c806c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2100,6 +2100,7 @@
         "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_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -8824,6 +8825,21 @@
     ],
 }
 
+// GN: //src/trace_processor/importers:gen_cc_statsd_atoms_descriptor
+genrule {
+    name: "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+    srcs: [
+        "src/trace_processor/importers/proto/atoms.descriptor",
+    ],
+    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/importers/atoms.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",
@@ -9199,6 +9215,7 @@
         "src/trace_processor/importers/proto/proto_trace_parser.cc",
         "src/trace_processor/importers/proto/proto_trace_reader.cc",
         "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
+        "src/trace_processor/importers/proto/statsd_module.cc",
         "src/trace_processor/importers/proto/track_event_module.cc",
         "src/trace_processor/importers/proto/track_event_parser.cc",
         "src/trace_processor/importers/proto/track_event_tokenizer.cc",
@@ -10808,6 +10825,7 @@
         "perfetto_src_trace_processor_gen_cc_test_messages_descriptor",
         "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_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -10990,6 +11008,7 @@
         "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_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -11164,6 +11183,7 @@
         "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_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 8d5d7c7..c7c272d 100644
--- a/BUILD
+++ b/BUILD
@@ -1152,6 +1152,16 @@
 )
 
 perfetto_cc_proto_descriptor(
+    name = "src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+    deps = [
+        "src/trace_processor/importers/proto/atoms.descriptor",
+    ],
+    outs = [
+        "src/trace_processor/importers/atoms.descriptor.h",
+    ],
+)
+
+perfetto_cc_proto_descriptor(
     name = "src_trace_processor_importers_gen_cc_track_event_descriptor",
     deps = [
         ":protos_perfetto_trace_track_event_descriptor",
@@ -1676,6 +1686,8 @@
         "src/trace_processor/importers/proto/proto_trace_reader.h",
         "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
         "src/trace_processor/importers/proto/proto_trace_tokenizer.h",
+        "src/trace_processor/importers/proto/statsd_module.cc",
+        "src/trace_processor/importers/proto/statsd_module.h",
         "src/trace_processor/importers/proto/track_event_module.cc",
         "src/trace_processor/importers/proto/track_event_module.h",
         "src/trace_processor/importers/proto/track_event_parser.cc",
@@ -4070,6 +4082,7 @@
                ":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_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -4177,6 +4190,7 @@
                ":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_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
@@ -4347,6 +4361,7 @@
                ":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_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
diff --git a/bazel/BUILD b/bazel/BUILD
index bf64b69..bb4a04b 100644
--- a/bazel/BUILD
+++ b/bazel/BUILD
@@ -21,6 +21,14 @@
 )
 
 config_setting(
+    name = "cpu_arm64",
+    constraint_values = [
+        "@platforms//cpu:arm64",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+config_setting(
     name = "os_linux",
     constraint_values = [
         "@platforms//os:linux",
diff --git a/bazel/zlib.BUILD b/bazel/zlib.BUILD
index cd4a6d3..9c203dd 100644
--- a/bazel/zlib.BUILD
+++ b/bazel/zlib.BUILD
@@ -46,7 +46,10 @@
         "zlib.h",
         "zutil.c",
         "zutil.h",
-    ],
+    ] + select({
+      "@perfetto//bazel:cpu_arm64": ["contrib/optimizations/slide_hash_neon.h"],
+      "//conditions:default": [],
+    }),
     hdrs = [
         "zlib.h",
     ],
diff --git a/gn/perfetto_cc_proto_descriptor.gni b/gn/perfetto_cc_proto_descriptor.gni
index 852dc0b..57efba7 100644
--- a/gn/perfetto_cc_proto_descriptor.gni
+++ b/gn/perfetto_cc_proto_descriptor.gni
@@ -15,18 +15,31 @@
 import("perfetto.gni")
 
 template("perfetto_cc_proto_descriptor") {
+  is_target = defined(invoker.descriptor_target)
+  is_path = defined(invoker.descriptor_path)
+
+  # You must define exactly one of:
+  # descriptor_target
+  # descriptor_path
+  assert((is_target || is_path) && !(is_target && is_path))
+
   config("${target_name}_config") {
     include_dirs = [ "${root_gen_dir}/${perfetto_root_path}" ]
   }
 
   action(target_name) {
     generated_header = "${target_gen_dir}/${invoker.descriptor_name}.h"
-    descriptor_file_path =
-        get_label_info(invoker.descriptor_target, "target_gen_dir") +
-        "/${invoker.descriptor_name}"
+    if (is_target) {
+      descriptor_file_path =
+          get_label_info(invoker.descriptor_target, "target_gen_dir") +
+          "/${invoker.descriptor_name}"
+      deps = [ invoker.descriptor_target ]
+    } else {
+      descriptor_file_path = invoker.descriptor_path
+      deps = []
+    }
 
     script = "$perfetto_root_path/tools/gen_cc_proto_descriptor.py"
-    deps = [ invoker.descriptor_target ]
     args = [
       "--gen_dir",
       rebase_path(root_gen_dir, root_build_dir),
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index c692f8a..17d5982 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -139,6 +139,10 @@
     "importers/proto/proto_trace_reader.h",
     "importers/proto/proto_trace_tokenizer.cc",
     "importers/proto/proto_trace_tokenizer.h",
+
+    # XXX: is this the correct place
+    "importers/proto/statsd_module.cc",
+    "importers/proto/statsd_module.h",
     "importers/proto/track_event_module.cc",
     "importers/proto/track_event_module.h",
     "importers/proto/track_event_parser.cc",
@@ -168,6 +172,7 @@
     "../protozero",
     "containers",
     "importers:gen_cc_chrome_track_event_descriptor",
+    "importers:gen_cc_statsd_atoms_descriptor",
     "importers:gen_cc_track_event_descriptor",
     "importers/common",
     "importers/memory_tracker:graph_processor",
@@ -195,6 +200,7 @@
     "../../protos/perfetto/trace/power:zero",
     "../../protos/perfetto/trace/profiling:zero",
     "../../protos/perfetto/trace/ps:zero",
+    "../../protos/perfetto/trace/statsd:zero",
     "../../protos/perfetto/trace/sys_stats:zero",
     "../../protos/perfetto/trace/system_info:zero",
     "../../protos/perfetto/trace/track_event:zero",
diff --git a/src/trace_processor/importers/BUILD.gn b/src/trace_processor/importers/BUILD.gn
index 908ae8d..ad01aac 100644
--- a/src/trace_processor/importers/BUILD.gn
+++ b/src/trace_processor/importers/BUILD.gn
@@ -14,6 +14,11 @@
 
 import("../../../gn/perfetto_cc_proto_descriptor.gni")
 
+perfetto_cc_proto_descriptor("gen_cc_statsd_atoms_descriptor") {
+  descriptor_name = "atoms.descriptor"
+  descriptor_path = "./proto/atoms.descriptor"
+}
+
 perfetto_cc_proto_descriptor("gen_cc_config_descriptor") {
   descriptor_name = "config.descriptor"
   descriptor_target = "../../../protos/perfetto/config:descriptor"
diff --git a/src/trace_processor/importers/additional_modules.cc b/src/trace_processor/importers/additional_modules.cc
index 5368ed9..7fbbeb5 100644
--- a/src/trace_processor/importers/additional_modules.cc
+++ b/src/trace_processor/importers/additional_modules.cc
@@ -19,6 +19,7 @@
 #include "src/trace_processor/importers/proto/android_probes_module.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"
 #include "src/trace_processor/importers/proto/system_probes_module.h"
 #include "src/trace_processor/importers/proto/translation_table_module.h"
 
@@ -31,9 +32,11 @@
   context->modules.emplace_back(new HeapGraphModule(context));
   context->modules.emplace_back(new SystemProbesModule(context));
   context->modules.emplace_back(new TranslationTableModule(context));
-  context->modules.emplace_back(new FtraceModuleImpl(context));
+  context->modules.emplace_back(new StatsdModule(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.
+  context->modules.emplace_back(new FtraceModuleImpl(context));
   context->ftrace_module =
       static_cast<FtraceModule*>(context->modules.back().get());
 }
diff --git a/src/trace_processor/importers/proto/statsd_module.cc b/src/trace_processor/importers/proto/statsd_module.cc
new file mode 100644
index 0000000..7b2d8aa
--- /dev/null
+++ b/src/trace_processor/importers/proto/statsd_module.cc
@@ -0,0 +1,249 @@
+/*
+ * 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 "src/trace_processor/importers/proto/statsd_module.h"
+
+#include "perfetto/ext/base/string_utils.h"
+#include "protos/perfetto/trace/statsd/statsd_atom.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/proto/async_track_set_tracker.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/timestamped_trace_piece.h"
+#include "src/trace_processor/trace_sorter.h"
+#include "src/trace_processor/util/descriptors.h"
+
+#include "src/trace_processor/importers/atoms.descriptor.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+constexpr const char* kAtomProtoName = ".android.os.statsd.Atom";
+
+using BoundInserter = ArgsTracker::BoundInserter;
+
+class InserterDelegate : public util::ProtoToArgsParser::Delegate {
+ public:
+  InserterDelegate(BoundInserter& inserter, TraceStorage& storage)
+      : inserter_(inserter), storage_(storage) {}
+  ~InserterDelegate() override = default;
+
+  using Key = util::ProtoToArgsParser::Key;
+
+  void AddInteger(const Key& key, int64_t value) override {
+    StringId flat_key_id =
+        storage_.InternString(base::StringView(key.flat_key));
+    StringId key_id = storage_.InternString(base::StringView(key.key));
+    Variadic variadic_val = Variadic::Integer(value);
+    inserter_.AddArg(flat_key_id, key_id, variadic_val);
+  }
+
+  void AddUnsignedInteger(const Key& key, uint64_t value) override {
+    StringId flat_key_id =
+        storage_.InternString(base::StringView(key.flat_key));
+    StringId key_id = storage_.InternString(base::StringView(key.key));
+    Variadic variadic_val = Variadic::UnsignedInteger(value);
+    inserter_.AddArg(flat_key_id, key_id, variadic_val);
+  }
+
+  void AddString(const Key& key, const protozero::ConstChars& value) override {
+    StringId flat_key_id =
+        storage_.InternString(base::StringView(key.flat_key));
+    StringId key_id = storage_.InternString(base::StringView(key.key));
+    Variadic variadic_val = Variadic::String(storage_.InternString(value));
+    inserter_.AddArg(flat_key_id, key_id, variadic_val);
+  }
+
+  void AddString(const Key& key, const std::string& value) override {
+    StringId flat_key_id =
+        storage_.InternString(base::StringView(key.flat_key));
+    StringId key_id = storage_.InternString(base::StringView(key.key));
+    Variadic variadic_val =
+        Variadic::String(storage_.InternString(base::StringView(value)));
+    inserter_.AddArg(flat_key_id, key_id, variadic_val);
+  }
+
+  void AddDouble(const Key& key, double value) override {
+    StringId flat_key_id =
+        storage_.InternString(base::StringView(key.flat_key));
+    StringId key_id = storage_.InternString(base::StringView(key.key));
+    Variadic variadic_val = Variadic::Real(value);
+    inserter_.AddArg(flat_key_id, key_id, variadic_val);
+  }
+
+  void AddPointer(const Key& key, const void* value) override {
+    StringId flat_key_id =
+        storage_.InternString(base::StringView(key.flat_key));
+    StringId key_id = storage_.InternString(base::StringView(key.key));
+    Variadic variadic_val =
+        Variadic::Pointer(reinterpret_cast<uintptr_t>(value));
+    inserter_.AddArg(flat_key_id, key_id, variadic_val);
+  }
+
+  void AddBoolean(const Key& key, bool value) override {
+    StringId flat_key_id =
+        storage_.InternString(base::StringView(key.flat_key));
+    StringId key_id = storage_.InternString(base::StringView(key.key));
+    Variadic variadic_val = Variadic::Boolean(value);
+    inserter_.AddArg(flat_key_id, key_id, variadic_val);
+  }
+
+  bool AddJson(const Key& key, const protozero::ConstChars& value) override {
+    auto json_value = json::ParseJsonString(value);
+    if (!json_value) {
+      return false;
+    }
+    return json::AddJsonValueToArgs(*json_value, base::StringView(key.flat_key),
+                                    base::StringView(key.key), &storage_,
+                                    &inserter_);
+  }
+
+  void AddNull(const Key& key) override {
+    StringId flat_key_id =
+        storage_.InternString(base::StringView(key.flat_key));
+    StringId key_id = storage_.InternString(base::StringView(key.key));
+    Variadic variadic_val = Variadic::Null();
+    inserter_.AddArg(flat_key_id, key_id, variadic_val);
+  }
+
+  size_t GetArrayEntryIndex(const std::string& array_key) override {
+    base::ignore_result(array_key);
+    return 0;
+  }
+
+  size_t IncrementArrayEntryIndex(const std::string& array_key) override {
+    base::ignore_result(array_key);
+    return 0;
+  }
+
+  PacketSequenceStateGeneration* seq_state() override { return nullptr; }
+
+ protected:
+  InternedMessageView* GetInternedMessageView(uint32_t field_id,
+                                              uint64_t iid) override {
+    base::ignore_result(field_id);
+    base::ignore_result(iid);
+    return nullptr;
+  }
+
+ private:
+  BoundInserter& inserter_;
+  TraceStorage& storage_;
+};
+
+}  // namespace
+
+using perfetto::protos::pbzero::TracePacket;
+
+PoolAndDescriptor::PoolAndDescriptor(const uint8_t* data,
+                                     size_t size,
+                                     const char* name) {
+  pool_.AddFromFileDescriptorSet(data, size);
+  base::Optional<uint32_t> opt_idx = pool_.FindDescriptorIdx(name);
+  if (opt_idx.has_value()) {
+    descriptor_ = &pool_.descriptors()[opt_idx.value()];
+  }
+}
+
+PoolAndDescriptor::~PoolAndDescriptor() = default;
+
+StatsdModule::StatsdModule(TraceProcessorContext* context)
+    : context_(context),
+      pool_(kAtomsDescriptor.data(), kAtomsDescriptor.size(), kAtomProtoName),
+      args_parser_(*(pool_.pool())) {
+  RegisterForField(TracePacket::kStatsdAtomFieldNumber, context);
+}
+
+StatsdModule::~StatsdModule() = default;
+
+void StatsdModule::ParsePacket(const TracePacket::Decoder& decoder,
+                               const TimestampedTracePiece& ttp,
+                               uint32_t field_id) {
+  if (field_id != TracePacket::kStatsdAtomFieldNumber) {
+    return;
+  }
+  int64_t ts = ttp.timestamp;
+  const auto& atoms_wrapper =
+      protos::pbzero::StatsdAtom::Decoder(decoder.statsd_atom());
+  for (auto it = atoms_wrapper.nested(); it; ++it) {
+    ParseAtom(ts, *it);
+  }
+}
+
+void StatsdModule::ParseAtom(int64_t ts, protozero::ConstBytes nested_bytes) {
+  // nested_bytes is an Atom proto. We (deliberately) don't generate
+  // decoding code for every kind of atom (or the parent Atom proto)
+  // and instead use the descriptor to parse the args/name.
+
+  // Atom is a giant oneof of all the possible 'kinds' of atom so here
+  // we use the protozero decoder implementation to grab the first
+  // field id which we we use to look up the field name:
+  protozero::ProtoDecoder nested_decoder(nested_bytes);
+  protozero::Field field = nested_decoder.ReadField();
+  uint32_t nested_field_id = 0;
+  if (field.valid()) {
+    nested_field_id = field.id();
+  }
+  StringId atom_name = GetAtomName(nested_field_id);
+
+  AsyncTrackSetTracker::TrackSetId track_set = InternAsyncTrackSetId();
+  TrackId track = context_->async_track_set_tracker->Scoped(track_set, ts, 0);
+  base::Optional<SliceId> opt_slice =
+      context_->slice_tracker->Scoped(ts, track, kNullStringId, atom_name, 0);
+  if (!opt_slice) {
+    return;
+  }
+  SliceId slice = opt_slice.value();
+  auto inserter = context_->args_tracker->AddArgsTo(slice);
+  InserterDelegate delgate(inserter, *context_->storage.get());
+  args_parser_.ParseMessage(nested_bytes, kAtomProtoName,
+                            nullptr /* parse all fields */, delgate);
+}
+
+StringId StatsdModule::GetAtomName(uint32_t atom_field_id) {
+  StringId* cached_name = atom_names_.Find(atom_field_id);
+  if (cached_name == nullptr) {
+    if (pool_.descriptor() == nullptr) {
+      return context_->storage->InternString("Could not load atom descriptor");
+    }
+
+    const auto& fields = pool_.descriptor()->fields();
+    const auto& field_it = fields.find(atom_field_id);
+    if (field_it == fields.end()) {
+      return context_->storage->InternString("Unknown atom");
+    }
+
+    const FieldDescriptor& field = field_it->second;
+    StringId name =
+        context_->storage->InternString(base::StringView(field.name()));
+    atom_names_[atom_field_id] = name;
+    return name;
+  }
+  return *cached_name;
+}
+
+AsyncTrackSetTracker::TrackSetId StatsdModule::InternAsyncTrackSetId() {
+  if (!track_set_id_) {
+    StringId name = context_->storage->InternString("Statsd Atoms");
+    track_set_id_ =
+        context_->async_track_set_tracker->InternGlobalTrackSet(name);
+  }
+  return track_set_id_.value();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/statsd_module.h b/src/trace_processor/importers/proto/statsd_module.h
new file mode 100644
index 0000000..50184c0
--- /dev/null
+++ b/src/trace_processor/importers/proto/statsd_module.h
@@ -0,0 +1,83 @@
+/*
+ * 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_STATSD_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STATSD_MODULE_H_
+
+#include <cstdint>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/optional.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/proto/async_track_set_tracker.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/slice_tables.h"
+#include "src/trace_processor/tables/track_tables.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/proto_to_args_parser.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Wraps a DescriptorPool and a pointer into that pool. This prevents
+// common bugs where moving/changing the pool invalidates the pointer.
+class PoolAndDescriptor {
+ public:
+  PoolAndDescriptor(const uint8_t* data, size_t size, const char* name);
+  virtual ~PoolAndDescriptor();
+
+  const DescriptorPool* pool() const { return &pool_; }
+
+  const ProtoDescriptor* descriptor() const { return descriptor_; }
+
+ private:
+  PoolAndDescriptor(const PoolAndDescriptor&) = delete;
+  PoolAndDescriptor& operator=(const PoolAndDescriptor&) = delete;
+  PoolAndDescriptor(PoolAndDescriptor&&) = delete;
+  PoolAndDescriptor& operator=(PoolAndDescriptor&&) = delete;
+
+  DescriptorPool pool_;
+  const ProtoDescriptor* descriptor_{};
+};
+
+class StatsdModule : public ProtoImporterModule {
+ public:
+  explicit StatsdModule(TraceProcessorContext* context);
+
+  ~StatsdModule() override;
+
+  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
+                   const TimestampedTracePiece& ttp,
+                   uint32_t field_id) override;
+
+ private:
+  void ParseAtom(int64_t ts, protozero::ConstBytes bytes);
+  StringId GetAtomName(uint32_t atom_field_id);
+  AsyncTrackSetTracker::TrackSetId InternAsyncTrackSetId();
+
+  TraceProcessorContext* context_;
+  base::FlatHashMap<uint32_t, StringId> atom_names_;
+  PoolAndDescriptor pool_;
+  util::ProtoToArgsParser args_parser_;
+  base::Optional<AsyncTrackSetTracker::TrackSetId> track_set_id_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STATSD_MODULE_H_
diff --git a/test/data/statsd_atoms.pb.sha256 b/test/data/statsd_atoms.pb.sha256
new file mode 100644
index 0000000..934ccb7
--- /dev/null
+++ b/test/data/statsd_atoms.pb.sha256
@@ -0,0 +1 @@
+d68011231cf87c73baf1b459b3629fb5a8b248a8dde03e90118349b7573b06da
\ No newline at end of file
diff --git a/test/trace_processor/parsing/all_atoms_test.sql b/test/trace_processor/parsing/all_atoms_test.sql
new file mode 100644
index 0000000..b92a851
--- /dev/null
+++ b/test/trace_processor/parsing/all_atoms_test.sql
@@ -0,0 +1 @@
+SELECT slice.name, args.key, args.display_value FROM track JOIN slice ON track.id = slice.track_id JOIN args USING(arg_set_id) WHERE track.name = "Statsd Atoms";
diff --git a/test/trace_processor/parsing/index b/test/trace_processor/parsing/index
index ada90d1..e1cbe46 100644
--- a/test/trace_processor/parsing/index
+++ b/test/trace_processor/parsing/index
@@ -153,3 +153,6 @@
 
 # Per-process Binder transaction metrics
 android_binder.py android_binder android_binder.out
+
+# Statsd Atoms
+../../data/statsd_atoms.pb all_atoms_test.sql statsd_atoms_all_atoms.out
diff --git a/test/trace_processor/parsing/statsd_atoms_all_atoms.out b/test/trace_processor/parsing/statsd_atoms_all_atoms.out
new file mode 100644
index 0000000..2848fcf
--- /dev/null
+++ b/test/trace_processor/parsing/statsd_atoms_all_atoms.out
@@ -0,0 +1,39 @@
+"name","key","display_value"
+"system_uptime","system_uptime.uptime_millis","24559955"
+"system_uptime","system_uptime.uptime_millis","24559955"
+"system_uptime","system_uptime.uptime_millis","24560959"
+"system_uptime","system_uptime.uptime_millis","24560959"
+"system_uptime","system_uptime.uptime_millis","24561963"
+"system_uptime","system_uptime.uptime_millis","24561963"
+"system_uptime","system_uptime.uptime_millis","24562967"
+"system_uptime","system_uptime.uptime_millis","24562967"
+"system_uptime","system_uptime.uptime_millis","24563972"
+"system_uptime","system_uptime.uptime_millis","24563972"
+"system_uptime","system_uptime.uptime_millis","24564984"
+"system_uptime","system_uptime.uptime_millis","24564984"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].uid","10218"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].tag",""
+"flashlight_state_changed","flashlight_state_changed.state","ON"
+"system_uptime","system_uptime.uptime_millis","24565988"
+"system_uptime","system_uptime.uptime_millis","24565988"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].uid","10218"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].tag",""
+"flashlight_state_changed","flashlight_state_changed.state","OFF"
+"system_uptime","system_uptime.uptime_millis","24566992"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].uid","10218"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].tag",""
+"flashlight_state_changed","flashlight_state_changed.state","ON"
+"system_uptime","system_uptime.uptime_millis","24566992"
+"system_uptime","system_uptime.uptime_millis","24567996"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].uid","10218"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].tag",""
+"flashlight_state_changed","flashlight_state_changed.state","OFF"
+"system_uptime","system_uptime.uptime_millis","24567996"
+"system_uptime","system_uptime.uptime_millis","24569000"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].uid","10218"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].tag",""
+"flashlight_state_changed","flashlight_state_changed.state","ON"
+"system_uptime","system_uptime.uptime_millis","24569000"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].uid","10218"
+"flashlight_state_changed","flashlight_state_changed.attribution_node[0].tag",""
+"flashlight_state_changed","flashlight_state_changed.state","OFF"
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index fccf537..a369ff7 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -793,6 +793,10 @@
   module.genrule_headers.add(module.name)
   module.srcs.update(
       ':' + label_to_module_name(dep) for dep in target.proto_deps)
+  module.srcs.update(
+      gn_utils.label_to_path(src)
+      for src in target.inputs
+      if "tmp.gn_utils" not in src)
   module.out.update(target.outputs)
   blueprint.add_module(module)
   return module
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 8754486..1c64257 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -141,6 +141,8 @@
   label = BazelLabel(
       get_bazel_label_name(target.name), 'perfetto_cc_proto_descriptor')
   label.deps += [':' + get_bazel_label_name(x) for x in target.proto_deps]
+  label.deps += [gn_utils.label_to_path(src) for src in target.inputs if "tmp.gn_utils" not in src]
+
   label.outs += target.outputs
   return [label]