profiling: Add FreePage.

Change-Id: If77bdfd16fdba39acf532706a9e8b621d88a4c96
diff --git a/src/profiling/memory/client.cc b/src/profiling/memory/client.cc
index d0e8f61..f8d57f7 100644
--- a/src/profiling/memory/client.cc
+++ b/src/profiling/memory/client.cc
@@ -26,6 +26,48 @@
 #include "src/profiling/memory/transport_data.h"
 
 namespace perfetto {
+namespace {
+
+std::atomic<uint64_t> global_sequence_number(0);
+constexpr size_t kFreePageBytes = base::kPageSize;
+constexpr size_t kFreePageSize = kFreePageBytes / sizeof(uint64_t);
+
+}  // namespace
+
+FreePage::FreePage() : free_page_(kFreePageSize) {
+  free_page_[0] = static_cast<uint64_t>(kFreePageBytes);
+  free_page_[1] = static_cast<uint64_t>(RecordType::Free);
+  offset_ = 2;
+  // Code in Add assumes that offset is aligned to 2.
+  PERFETTO_DCHECK(offset_ % 2 == 0);
+}
+
+void FreePage::Add(const uint64_t addr, SocketPool* pool) {
+  std::lock_guard<std::mutex> l(mtx_);
+  if (offset_ == kFreePageSize)
+    Flush(pool);
+  static_assert(kFreePageSize % 2 == 0,
+                "free page size needs to be divisible by two");
+  free_page_[offset_++] = ++global_sequence_number;
+  free_page_[offset_++] = addr;
+  PERFETTO_DCHECK(offset_ % 2 == 0);
+}
+
+void FreePage::Flush(SocketPool* pool) {
+  BorrowedSocket fd(pool->Borrow());
+  size_t written = 0;
+  do {
+    ssize_t wr = PERFETTO_EINTR(send(*fd, &free_page_[0] + written,
+                                     kFreePageBytes - written, MSG_NOSIGNAL));
+    if (wr == -1) {
+      fd.Close();
+      return;
+    }
+    written += static_cast<size_t>(wr);
+  } while (written < kFreePageBytes);
+  // Now that we have flushed, reset to after the header.
+  offset_ = 2;
+}
 
 BorrowedSocket::BorrowedSocket(base::ScopedFile fd, SocketPool* socket_pool)
     : fd_(std::move(fd)), socket_pool_(socket_pool) {}
@@ -59,6 +101,12 @@
 }
 
 void SocketPool::Return(base::ScopedFile sock) {
+  if (!sock) {
+    // TODO(fmayer): Handle reconnect or similar.
+    // This is just to prevent a deadlock.
+    PERFETTO_CHECK(++dead_sockets_ != sockets_.size());
+    return;
+  }
   std::unique_lock<std::mutex> lck_(mtx_);
   PERFETTO_CHECK(available_sockets_ < sockets_.size());
   sockets_[available_sockets_++] = std::move(sock);
diff --git a/src/profiling/memory/client.h b/src/profiling/memory/client.h
index a3aa5c6..7a4e459 100644
--- a/src/profiling/memory/client.h
+++ b/src/profiling/memory/client.h
@@ -18,6 +18,7 @@
 #define SRC_PROFILING_MEMORY_CLIENT_H_
 
 #include <stddef.h>
+
 #include <mutex>
 #include <vector>
 
@@ -27,6 +28,22 @@
 
 class SocketPool;
 
+class FreePage {
+ public:
+  FreePage();
+
+  // Can be called from any thread. Must not hold mtx_.`
+  void Add(const uint64_t addr, SocketPool* pool);
+
+ private:
+  // Needs to be called holding mtx_.
+  void Flush(SocketPool* pool);
+
+  std::vector<uint64_t> free_page_;
+  std::mutex mtx_;
+  size_t offset_;
+};
+
 class BorrowedSocket {
  public:
   BorrowedSocket(const BorrowedSocket&) = delete;
@@ -61,6 +78,7 @@
   std::condition_variable cv_;
   std::vector<base::ScopedFile> sockets_;
   size_t available_sockets_;
+  size_t dead_sockets_ = 0;
 };
 
 }  // namespace perfetto