Show buffer overruns in ProfileProto.

Test: profile inputmethod.latin at inverval 1, observe overflow

Change-Id: I5553502de233f6f661edaf01c46416ca5422d0e0
Bug: 129054982
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index ed13053..67e1098 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -2513,6 +2513,10 @@
     // If false, the process outlived the profiling session.
     optional bool disconnected = 6;
 
+    // If disconnected, this disconnect was caused by the client overrunning
+    // the buffer.
+    optional bool buffer_overran = 7;
+
     optional ProcessStats stats = 5;
 
     repeated HeapSample samples = 2;
diff --git a/protos/perfetto/trace/profiling/profile_packet.proto b/protos/perfetto/trace/profiling/profile_packet.proto
index 4be89b1..1228057 100644
--- a/protos/perfetto/trace/profiling/profile_packet.proto
+++ b/protos/perfetto/trace/profiling/profile_packet.proto
@@ -105,6 +105,10 @@
     // If false, the process outlived the profiling session.
     optional bool disconnected = 6;
 
+    // If disconnected, this disconnect was caused by the client overrunning
+    // the buffer.
+    optional bool buffer_overran = 7;
+
     optional ProcessStats stats = 5;
 
     repeated HeapSample samples = 2;
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index 19eb2f9..b62f0a9 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -403,6 +403,7 @@
       proto->set_pid(static_cast<uint64_t>(pid));
       proto->set_from_startup(from_startup);
       proto->set_disconnected(process_state.disconnected);
+      proto->set_buffer_overran(process_state.buffer_overran);
       auto* stats = proto->set_stats();
       stats->set_unwinding_errors(process_state.unwinding_errors);
       stats->set_heap_samples(process_state.heap_samples);
@@ -739,11 +740,12 @@
 }
 
 void HeapprofdProducer::PostSocketDisconnected(DataSourceInstanceID ds_id,
-                                               pid_t pid) {
+                                               pid_t pid,
+                                               SharedRingBuffer::Stats stats) {
   auto weak_this = weak_factory_.GetWeakPtr();
-  task_runner_->PostTask([weak_this, ds_id, pid] {
+  task_runner_->PostTask([weak_this, ds_id, pid, stats] {
     if (weak_this)
-      weak_this->HandleSocketDisconnected(ds_id, pid);
+      weak_this->HandleSocketDisconnected(ds_id, pid, stats);
   });
 }
 
@@ -821,8 +823,10 @@
   }
 }
 
-void HeapprofdProducer::HandleSocketDisconnected(DataSourceInstanceID id,
-                                                 pid_t pid) {
+void HeapprofdProducer::HandleSocketDisconnected(
+    DataSourceInstanceID id,
+    pid_t pid,
+    SharedRingBuffer::Stats stats) {
   auto it = data_sources_.find(id);
   if (it == data_sources_.end())
     return;
@@ -833,6 +837,7 @@
     return;
   ProcessState& process_state = process_state_it->second;
   process_state.disconnected = true;
+  process_state.buffer_overran = stats.num_writes_overflow > 0;
 
   // TODO(fmayer): Dump on process disconnect rather than data source
   // destruction. This prevents us needing to hold onto the bookkeeping data
diff --git a/src/profiling/memory/heapprofd_producer.h b/src/profiling/memory/heapprofd_producer.h
index 148d71b..ae4a5fc 100644
--- a/src/profiling/memory/heapprofd_producer.h
+++ b/src/profiling/memory/heapprofd_producer.h
@@ -105,11 +105,15 @@
   // UnwindingWorker::Delegate impl:
   void PostAllocRecord(AllocRecord) override;
   void PostFreeRecord(FreeRecord) override;
-  void PostSocketDisconnected(DataSourceInstanceID, pid_t) override;
+  void PostSocketDisconnected(DataSourceInstanceID,
+                              pid_t,
+                              SharedRingBuffer::Stats) override;
 
   void HandleAllocRecord(AllocRecord);
   void HandleFreeRecord(FreeRecord);
-  void HandleSocketDisconnected(DataSourceInstanceID, pid_t);
+  void HandleSocketDisconnected(DataSourceInstanceID,
+                                pid_t,
+                                SharedRingBuffer::Stats);
 
   // Valid only if mode_ == kChild.
   void SetTargetProcess(pid_t target_pid,
@@ -163,6 +167,8 @@
   struct ProcessState {
     ProcessState(GlobalCallstackTrie* callsites) : heap_tracker(callsites) {}
     bool disconnected = false;
+    bool buffer_overran = false;
+
     uint64_t heap_samples = 0;
     uint64_t map_reparses = 0;
     uint64_t unwinding_errors = 0;
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc
index 53304c8..b05ff64 100644
--- a/src/profiling/memory/unwinding.cc
+++ b/src/profiling/memory/unwinding.cc
@@ -246,12 +246,32 @@
     return;
   }
   ClientData& client_data = it->second;
+  SharedRingBuffer& shmem = client_data.shmem;
+
+  // Currently, these stats are used to determine whether the application
+  // disconnected due to an error condition (i.e. buffer overflow) or
+  // volutarily. Because a buffer overflow leads to an immediate disconnect, we
+  // do not need these stats when heapprofd tears down the tracing session.
+  //
+  // TODO(fmayer): We should make it that normal disconnects also go through
+  // this code path, so we can write other stats to the result. This will also
+  // allow us to free the bookkeeping data earlier for processes that exit
+  // during the session. See TODO in
+  // HeapprofdProducer::HandleSocketDisconnected.
+  SharedRingBuffer::Stats stats = {};
+  {
+    auto lock = shmem.AcquireLock(ScopedSpinlock::Mode::Try);
+    if (lock.locked())
+      stats = shmem.GetStats(lock);
+    else
+      PERFETTO_ELOG("Failed to log shmem to get stats.");
+  }
   DataSourceInstanceID ds_id = client_data.data_source_instance_id;
   pid_t peer_pid = self->peer_pid();
   client_data_.erase(it);
   // The erase invalidates the self pointer.
   self = nullptr;
-  delegate_->PostSocketDisconnected(ds_id, peer_pid);
+  delegate_->PostSocketDisconnected(ds_id, peer_pid, stats);
 }
 
 void UnwindingWorker::OnDataAvailable(base::UnixSocket* self) {
diff --git a/src/profiling/memory/unwinding.h b/src/profiling/memory/unwinding.h
index af238db..374e49c 100644
--- a/src/profiling/memory/unwinding.h
+++ b/src/profiling/memory/unwinding.h
@@ -136,7 +136,9 @@
    public:
     virtual void PostAllocRecord(AllocRecord) = 0;
     virtual void PostFreeRecord(FreeRecord) = 0;
-    virtual void PostSocketDisconnected(DataSourceInstanceID, pid_t pid) = 0;
+    virtual void PostSocketDisconnected(DataSourceInstanceID,
+                                        pid_t pid,
+                                        SharedRingBuffer::Stats stats) = 0;
     virtual ~Delegate();
   };
 
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index cfc6781..b1f82bb 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -227,6 +227,11 @@
     GProfile cur_profile = profile;
     uint64_t pid = p.first;
     for (const ProfilePacket::ProcessHeapSamples* samples : p.second) {
+      if (samples->buffer_overran()) {
+        PERFETTO_ELOG("WARNING: The profile for %" PRIu64
+                      " ended early due to a buffer overrun.",
+                      pid);
+      }
       for (const ProfilePacket::HeapSample& sample : samples->samples()) {
         GSample* gsample = cur_profile.add_sample();
         auto it = callstack_lookup.find(sample.callstack_id());