Merge "tp: binder_tracker: fix performance issue due to insert/erase heavy workload + FlatHashMap" into main
diff --git a/Android.bp b/Android.bp
index 73b4ae5..438f7d6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13463,6 +13463,7 @@
         "src/trace_redaction/filter_sched_waking_events.cc",
         "src/trace_redaction/filter_task_rename.cc",
         "src/trace_redaction/find_package_uid.cc",
+        "src/trace_redaction/modify_process_trees.cc",
         "src/trace_redaction/optimize_timeline.cc",
         "src/trace_redaction/populate_allow_lists.cc",
         "src/trace_redaction/process_thread_timeline.cc",
diff --git a/src/trace_redaction/BUILD.gn b/src/trace_redaction/BUILD.gn
index 037545d..8ff31a3 100644
--- a/src/trace_redaction/BUILD.gn
+++ b/src/trace_redaction/BUILD.gn
@@ -47,6 +47,8 @@
     "find_package_uid.cc",
     "find_package_uid.h",
     "frame_cookie.h",
+    "modify_process_trees.cc",
+    "modify_process_trees.h",
     "optimize_timeline.cc",
     "optimize_timeline.h",
     "populate_allow_lists.cc",
diff --git a/src/trace_redaction/modify_process_trees.cc b/src/trace_redaction/modify_process_trees.cc
new file mode 100644
index 0000000..099890b
--- /dev/null
+++ b/src/trace_redaction/modify_process_trees.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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_redaction/modify_process_trees.h"
+
+#include <string>
+
+#include "perfetto/base/status.h"
+#include "perfetto/protozero/field.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "src/trace_redaction/proto_util.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+
+#include "protos/perfetto/trace/ps/process_tree.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto::trace_redaction {
+
+base::Status ModifyProcessTree::VerifyContext(const Context&) const {
+  return base::OkStatus();
+}
+
+base::Status ModifyProcessTree::Transform(const Context& context,
+                                          std::string* packet) const {
+  protozero::ProtoDecoder decoder(*packet);
+
+  auto process_tree =
+      decoder.FindField(protos::pbzero::TracePacket::kProcessTreeFieldNumber);
+
+  if (!process_tree.valid()) {
+    return base::OkStatus();
+  }
+
+  auto timestamp =
+      decoder.FindField(protos::pbzero::TracePacket::kTimestampFieldNumber);
+
+  protozero::HeapBuffered<protos::pbzero::TracePacket> packet_message;
+
+  for (auto field = decoder.ReadField(); field.valid();
+       field = decoder.ReadField()) {
+    if (field.id() == protos::pbzero::TracePacket::kProcessTreeFieldNumber) {
+      TransformProcessTree(context, timestamp, field,
+                           packet_message->set_process_tree());
+    } else {
+      proto_util::AppendField(field, packet_message.get());
+    }
+  }
+
+  packet->assign(packet_message.SerializeAsString());
+
+  return base::OkStatus();
+}
+
+void ModifyProcessTree::TransformProcess(
+    const Context&,
+    const protozero::Field&,
+    const protozero::Field& process,
+    protos::pbzero::ProcessTree* process_tree) const {
+  PERFETTO_DCHECK(process.id() ==
+                  protos::pbzero::ProcessTree::kProcessesFieldNumber);
+  proto_util::AppendField(process, process_tree);
+}
+
+void ModifyProcessTree::TransformThread(
+    const Context&,
+    const protozero::Field&,
+    const protozero::Field& thread,
+    protos::pbzero::ProcessTree* process_tree) const {
+  PERFETTO_DCHECK(thread.id() ==
+                  protos::pbzero::ProcessTree::kThreadsFieldNumber);
+  proto_util::AppendField(thread, process_tree);
+}
+
+void ModifyProcessTree::TransformProcessTree(
+    const Context& context,
+    const protozero::Field& timestamp,
+    const protozero::Field& process_tree,
+    protos::pbzero::ProcessTree* message) const {
+  protozero::ProtoDecoder decoder(process_tree.as_bytes());
+
+  for (auto field = decoder.ReadField(); field.valid();
+       field = decoder.ReadField()) {
+    switch (field.id()) {
+      case protos::pbzero::ProcessTree::kProcessesFieldNumber:
+        TransformProcess(context, timestamp, field, message);
+        break;
+
+      case protos::pbzero::ProcessTree::kThreadsFieldNumber:
+        TransformThread(context, timestamp, field, message);
+        break;
+
+      default:
+        proto_util::AppendField(field, message);
+        break;
+    }
+  }
+
+  // TODO(vaage): Call the handler to add extra fields to the process tree.
+}
+
+}  // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/modify_process_trees.h b/src/trace_redaction/modify_process_trees.h
new file mode 100644
index 0000000..e36223e
--- /dev/null
+++ b/src/trace_redaction/modify_process_trees.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 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_REDACTION_MODIFY_PROCESS_TREES_H_
+#define SRC_TRACE_REDACTION_MODIFY_PROCESS_TREES_H_
+
+#include <string>
+
+#include "perfetto/base/status.h"
+#include "src/trace_redaction/trace_redaction_framework.h"
+
+#include "protos/perfetto/trace/ps/process_tree.pbzero.h"
+
+namespace perfetto::trace_redaction {
+
+// Walk through process trees, calling process and thread handlers to add new
+// process and threads messages to the process tree. If the default handler is
+// not replaced, the thread/process will be added to the parent.
+class ModifyProcessTree : public TransformPrimitive {
+ public:
+  base::Status Transform(const Context& context,
+                         std::string* packet) const override;
+
+ protected:
+  // Verifies that the context contains required values. No-op by default.
+  virtual base::Status VerifyContext(const Context& context) const;
+
+  // Modifies a process before adding it back to the process tree. Appends the
+  // field to the process tree without modification by default.
+  virtual void TransformProcess(
+      const Context& context,
+      const protozero::Field& timestamp,
+      const protozero::Field& process,
+      protos::pbzero::ProcessTree* process_tree) const;
+
+  // Modifies a thread before adding it back to the process tree. Appends the
+  // field to the process tree without modification by default.
+  virtual void TransformThread(
+      const Context& context,
+      const protozero::Field& timestamp,
+      const protozero::Field& thread,
+      protos::pbzero::ProcessTree* process_trees) const;
+
+  // TODO(vaage): Add a handler that is called the process tree is populated so
+  // that fields can be added to process tree (e.g. creating new threads -
+  // needed for thread merging).
+
+ private:
+  void TransformProcessTree(const Context& context,
+                            const protozero::Field& timestamp,
+                            const protozero::Field& process_tree,
+                            protos::pbzero::ProcessTree* message) const;
+};
+
+}  // namespace perfetto::trace_redaction
+
+#endif  // SRC_TRACE_REDACTION_MODIFY_PROCESS_TREES_H_
diff --git a/src/trace_redaction/populate_allow_lists.cc b/src/trace_redaction/populate_allow_lists.cc
index b2c9431..11d895b 100644
--- a/src/trace_redaction/populate_allow_lists.cc
+++ b/src/trace_redaction/populate_allow_lists.cc
@@ -84,24 +84,38 @@
     context->trace_packet_allow_list.insert(item);
   }
 
+  // FTRACE EVENT NOTES
+  //
+  //    Dma events (kDmaHeapStatFieldNumber) are global events and are not
+  //    emitted within a process context (they are centrally allocated by the
+  //    HAL process). We drop them for now as we don't have the required
+  //    attribution info in the trace.
+  //
+  //    ION events (e.g. kIonBufferCreateFieldNumber, kIonHeapGrowFieldNumber,
+  //    etc.) are global events are not emitted within a process context (they
+  //    are centrally allocated by the HAL process). We drop them for now as we
+  //    don't have the required attribution info in the trace.
+  //
+  //    TODO(vaage): The allowed rss stat events (i.e. kRssStatFieldNumber,
+  //    kRssStatThrottledFieldNumber) are process-scoped. It is non-trivial to
+  //    merge events, so all events outside of the target package should be
+  //    dropped.
+  //
+  //    TODO(vaage): kSchedBlockedReasonFieldNumber contains two pids, an outer
+  //    and inner pid. A primitive is needed to further redact these events.
+
   std::initializer_list<uint32_t> ftrace_events = {
-      protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber,
       protos::pbzero::FtraceEvent::kCpuFrequencyFieldNumber,
       protos::pbzero::FtraceEvent::kCpuIdleFieldNumber,
+      protos::pbzero::FtraceEvent::kPrintFieldNumber,
+      protos::pbzero::FtraceEvent::kRssStatFieldNumber,
+      protos::pbzero::FtraceEvent::kRssStatThrottledFieldNumber,
       protos::pbzero::FtraceEvent::kSchedBlockedReasonFieldNumber,
+      protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber,
+      protos::pbzero::FtraceEvent::kSchedSwitchFieldNumber,
       protos::pbzero::FtraceEvent::kSchedWakingFieldNumber,
       protos::pbzero::FtraceEvent::kTaskNewtaskFieldNumber,
       protos::pbzero::FtraceEvent::kTaskRenameFieldNumber,
-      protos::pbzero::FtraceEvent::kSchedProcessFreeFieldNumber,
-      protos::pbzero::FtraceEvent::kRssStatFieldNumber,
-      protos::pbzero::FtraceEvent::kIonHeapShrinkFieldNumber,
-      protos::pbzero::FtraceEvent::kIonHeapGrowFieldNumber,
-      protos::pbzero::FtraceEvent::kIonStatFieldNumber,
-      protos::pbzero::FtraceEvent::kIonBufferCreateFieldNumber,
-      protos::pbzero::FtraceEvent::kIonBufferDestroyFieldNumber,
-      protos::pbzero::FtraceEvent::kDmaHeapStatFieldNumber,
-      protos::pbzero::FtraceEvent::kRssStatThrottledFieldNumber,
-      protos::pbzero::FtraceEvent::kPrintFieldNumber,
   };
 
   for (auto item : ftrace_events) {
diff --git a/src/trace_redaction/scrub_process_trees.cc b/src/trace_redaction/scrub_process_trees.cc
index d8baf99..989d094 100644
--- a/src/trace_redaction/scrub_process_trees.cc
+++ b/src/trace_redaction/scrub_process_trees.cc
@@ -16,9 +16,6 @@
 
 #include "src/trace_redaction/scrub_process_trees.h"
 
-#include <cstdint>
-#include <string>
-
 #include "perfetto/base/status.h"
 #include "perfetto/protozero/field.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
@@ -29,149 +26,90 @@
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
 namespace perfetto::trace_redaction {
+
 namespace {
 
-constexpr auto kThreadsFieldNumber =
-    protos::pbzero::ProcessTree::kThreadsFieldNumber;
-constexpr auto kTimestampFieldNumber =
-    protos::pbzero::TracePacket::kTimestampFieldNumber;
-constexpr auto kProcessTreeFieldNumber =
-    protos::pbzero::TracePacket::kProcessTreeFieldNumber;
-constexpr auto kProcessesFieldNumber =
-    protos::pbzero::ProcessTree::kProcessesFieldNumber;
-
-// Skips the cmdline fields.
-void ClearProcessName(protozero::ConstBytes bytes,
-                      protos::pbzero::ProcessTree::Process* message) {
-  protozero::ProtoDecoder decoder(bytes);
-
-  for (auto field = decoder.ReadField(); field; field = decoder.ReadField()) {
-    if (field.id() !=
-        protos::pbzero::ProcessTree::Process::kCmdlineFieldNumber) {
-      proto_util::AppendField(field, message);
-    }
-  }
-}
-
-void ScrubProcess(protozero::Field field,
-                  const ProcessThreadTimeline& timeline,
-                  uint64_t now,
-                  uint64_t uid,
-                  protos::pbzero::ProcessTree* message) {
-  if (field.id() != kProcessesFieldNumber) {
-    PERFETTO_FATAL(
-        "ScrubProcess() should only be called with a ProcessTree::Processes");
+// Appends a value to the message if (and only if) the pid belongs to the target
+// package.
+void TryAppendPid(const Context& context,
+                  const protozero::Field& timestamp,
+                  const protozero::Field& pid,
+                  const protozero::Field& value,
+                  protozero::Message* message) {
+  // All valid processes with have a time and pid/tid values. However, if
+  // they're missing values, the trace is corrupt. To avoid making this work by
+  // dropping too much data, drop the cmdline for all processes.
+  if (!timestamp.valid() || !pid.valid()) {
+    return;
   }
 
-  protos::pbzero::ProcessTree::Process::Decoder decoder(field.as_bytes());
-  auto slice = timeline.Search(now, decoder.pid());
+  auto slice = context.timeline->Search(timestamp.as_uint64(), pid.as_int32());
 
-  if (NormalizeUid(slice.uid) == NormalizeUid(uid)) {
-    proto_util::AppendField(field, message);
-  } else {
-    ClearProcessName(field.as_bytes(), message->add_processes());
-  }
-}
-
-// The thread name is unused, but it's safer to remove it.
-void ClearThreadName(protozero::ConstBytes bytes,
-                     protos::pbzero::ProcessTree::Thread* message) {
-  protozero::ProtoDecoder decoder(bytes);
-
-  for (auto field = decoder.ReadField(); field; field = decoder.ReadField()) {
-    if (field.id() != protos::pbzero::ProcessTree::Thread::kNameFieldNumber) {
-      proto_util::AppendField(field, message);
-    }
-  }
-}
-
-void ScrubThread(protozero::Field field,
-                 const ProcessThreadTimeline& timeline,
-                 uint64_t now,
-                 uint64_t uid,
-                 protos::pbzero::ProcessTree* message) {
-  if (field.id() != kThreadsFieldNumber) {
-    PERFETTO_FATAL(
-        "ScrubThread() should only be called with a ProcessTree::Threads");
+  // Only keep the target process cmdline.
+  if (NormalizeUid(slice.uid) != NormalizeUid(context.package_uid.value())) {
+    return;
   }
 
-  protos::pbzero::ProcessTree::Thread::Decoder thread_decoder(field.as_bytes());
-  auto slice = timeline.Search(now, thread_decoder.tid());
-
-  if (NormalizeUid(slice.uid) == NormalizeUid(uid)) {
-    proto_util::AppendField(field, message);
-  } else {
-    ClearThreadName(field.as_bytes(), message->add_threads());
-  }
+  proto_util::AppendField(value, message);
 }
 
 }  // namespace
 
-base::Status ScrubProcessTrees::Transform(const Context& context,
-                                          std::string* packet) const {
+base::Status ScrubProcessTrees::VerifyContext(const Context& context) const {
   if (!context.package_uid.has_value()) {
     return base::ErrStatus("ScrubProcessTrees: missing package uid.");
   }
 
-  if (context.timeline == nullptr) {
+  if (!context.timeline) {
     return base::ErrStatus("ScrubProcessTrees: missing timeline.");
   }
 
-  protozero::ProtoDecoder decoder(*packet);
-
-  if (!decoder.FindField(kProcessTreeFieldNumber).valid()) {
-    return base::OkStatus();
-  }
-
-  auto timestamp_field = decoder.FindField(kTimestampFieldNumber);
-
-  if (!timestamp_field.valid()) {
-    return base::ErrStatus("ScrubProcessTrees: trace packet missing timestamp");
-  }
-
-  auto timestamp = timestamp_field.as_uint64();
-
-  auto uid = context.package_uid.value();
-
-  const auto& timeline = *context.timeline.get();
-
-  protozero::HeapBuffered<protos::pbzero::TracePacket> message;
-
-  for (auto packet_field = decoder.ReadField(); packet_field.valid();
-       packet_field = decoder.ReadField()) {
-    if (packet_field.id() != kProcessTreeFieldNumber) {
-      proto_util::AppendField(packet_field, message.get());
-      continue;
-    }
-
-    auto* process_tree_message = message->set_process_tree();
-
-    protozero::ProtoDecoder process_tree_decoder(packet_field.as_bytes());
-
-    for (auto process_tree_field = process_tree_decoder.ReadField();
-         process_tree_field.valid();
-         process_tree_field = process_tree_decoder.ReadField()) {
-      switch (process_tree_field.id()) {
-        case kProcessesFieldNumber:
-          ScrubProcess(process_tree_field, timeline, timestamp, uid,
-                       process_tree_message);
-          break;
-
-        case kThreadsFieldNumber:
-          ScrubThread(process_tree_field, timeline, timestamp, uid,
-                      process_tree_message);
-          break;
-
-        default:
-          proto_util::AppendField(process_tree_field, process_tree_message);
-          break;
-      }
-    }
-  }
-
-  packet->assign(message.SerializeAsString());
-
   return base::OkStatus();
 }
 
+void ScrubProcessTrees::TransformProcess(
+    const Context& context,
+    const protozero::Field& timestamp,
+    const protozero::Field& process,
+    protos::pbzero::ProcessTree* process_tree) const {
+  protozero::ProtoDecoder decoder(process.as_bytes());
+
+  auto pid =
+      decoder.FindField(protos::pbzero::ProcessTree::Process::kPidFieldNumber);
+
+  auto* process_message = process_tree->add_processes();
+
+  for (auto field = decoder.ReadField(); field.valid();
+       field = decoder.ReadField()) {
+    if (field.id() ==
+        protos::pbzero::ProcessTree::Process::kCmdlineFieldNumber) {
+      TryAppendPid(context, timestamp, pid, field, process_message);
+    } else {
+      proto_util::AppendField(field, process_message);
+    }
+  }
+}
+
+void ScrubProcessTrees::TransformThread(
+    const Context& context,
+    const protozero::Field& timestamp,
+    const protozero::Field& thread,
+    protos::pbzero::ProcessTree* process_tree) const {
+  protozero::ProtoDecoder decoder(thread.as_bytes());
+
+  auto tid =
+      decoder.FindField(protos::pbzero::ProcessTree::Thread::kTidFieldNumber);
+
+  auto* thread_message = process_tree->add_threads();
+
+  for (auto field = decoder.ReadField(); field.valid();
+       field = decoder.ReadField()) {
+    if (field.id() == protos::pbzero::ProcessTree::Thread::kNameFieldNumber) {
+      TryAppendPid(context, timestamp, tid, field, thread_message);
+    } else {
+      proto_util::AppendField(field, thread_message);
+    }
+  }
+}
+
 }  // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/scrub_process_trees.h b/src/trace_redaction/scrub_process_trees.h
index 61cb85a..7c2b07a 100644
--- a/src/trace_redaction/scrub_process_trees.h
+++ b/src/trace_redaction/scrub_process_trees.h
@@ -17,19 +17,32 @@
 #ifndef SRC_TRACE_REDACTION_SCRUB_PROCESS_TREES_H_
 #define SRC_TRACE_REDACTION_SCRUB_PROCESS_TREES_H_
 
-#include <string>
-
 #include "perfetto/base/status.h"
+#include "perfetto/protozero/field.h"
+#include "src/trace_redaction/modify_process_trees.h"
 #include "src/trace_redaction/trace_redaction_framework.h"
 
+#include "protos/perfetto/trace/ps/process_tree.pbzero.h"
+
 namespace perfetto::trace_redaction {
 
 // Removes process names and thread names from process_trees if their pids/tids
 // are not connected to the target package.
-class ScrubProcessTrees final : public TransformPrimitive {
- public:
-  base::Status Transform(const Context& context,
-                         std::string* packet) const override;
+class ScrubProcessTrees : public ModifyProcessTree {
+ protected:
+  base::Status VerifyContext(const Context& context) const override;
+
+  void TransformProcess(
+      const Context& context,
+      const protozero::Field& timestamp,
+      const protozero::Field& process,
+      protos::pbzero::ProcessTree* process_trees) const override;
+
+  void TransformThread(
+      const Context& context,
+      const protozero::Field& timestamp,
+      const protozero::Field& thread,
+      protos::pbzero::ProcessTree* process_tree) const override;
 };
 
 }  // namespace perfetto::trace_redaction