heapprofd_client: avoid destruction re-entrancy issues, and global destructor

(1) use unhooked malloc functions for the g_client shared_ptr.
(2) wrap g_client in a base::NoDestructor to avoid the global destructor
  (and therefore the associated at-exit issues).

See bug & the contents of the patch for a detailed description of the issue.
Plus new e2e test that would've failed without this fix.

Bug: 129749217
Change-Id: I3d1c34ae133e9b9a43d2a6b6be2ceddbc90ba307
diff --git a/src/profiling/memory/client.h b/src/profiling/memory/client.h
index 9de433c..f39af55 100644
--- a/src/profiling/memory/client.h
+++ b/src/profiling/memory/client.h
@@ -27,6 +27,7 @@
 #include "perfetto/base/unix_socket.h"
 #include "src/profiling/memory/sampler.h"
 #include "src/profiling/memory/shared_ring_buffer.h"
+#include "src/profiling/memory/unhooked_allocator.h"
 #include "src/profiling/memory/wire_protocol.h"
 
 namespace perfetto {
@@ -43,15 +44,23 @@
 //
 // Methods of this class are thread-safe unless otherwise stated, in which case
 // the caller needs to synchronize calls behind a mutex or similar.
+//
+// Implementation warning: this class should not use any heap, as otherwise its
+// destruction would enter the possibly-hooked |free|, which can reference the
+// Client itself. If avoiding the heap is not possible, then look at using
+// UnhookedAllocator.
 class Client {
  public:
   // Returns a client that is ready for sampling allocations, using the given
   // socket (which should already be connected to heapprofd).
   //
   // Returns a shared_ptr since that is how the client will ultimately be used,
-  // and to take advantage of std::make_shared putting the object & the control
-  // block in one block of memory.
-  static std::shared_ptr<Client> CreateAndHandshake(base::UnixSocketRaw sock);
+  // and to take advantage of std::allocate_shared putting the object & the
+  // control block in one block of memory.
+  static std::shared_ptr<Client> CreateAndHandshake(
+      base::UnixSocketRaw sock,
+      UnhookedAllocator<Client> unhooked_allocator);
+
   static base::Optional<base::UnixSocketRaw> ConnectToHeapprofd(
       const std::string& sock_name);
 
@@ -72,8 +81,8 @@
     return sampler_.SampleSize(alloc_size);
   }
 
-  // Public for std::make_shared. Use CreateAndHandshake() to create instances
-  // instead.
+  // Public for std::allocate_shared. Use CreateAndHandshake() to create
+  // instances instead.
   Client(base::UnixSocketRaw sock,
          ClientConfiguration client_config,
          SharedRingBuffer shmem,