heapprofd_client: non-allocating logging

The logging functions are statically linked from bionic's async_safe logging
lib.

Bug: 122457555
Change-Id: If186a6005abdf6db2020397f3a2c14152b27d889
diff --git a/Android.bp b/Android.bp
index 31afc19..a22d077 100644
--- a/Android.bp
+++ b/Android.bp
@@ -174,12 +174,14 @@
     "libunwindstack",
   ],
   static_libs: [
+    "libasync_safe",
     "libgtest_prod",
   ],
   defaults: [
     "perfetto_defaults",
   ],
   cflags: [
+    "-DPERFETTO_ANDROID_ASYNC_SAFE_LOG",
     "-DPERFETTO_BUILD_WITH_ANDROID",
   ],
   include_dirs: [
@@ -347,15 +349,15 @@
     "liblog",
     "libutils",
   ],
+  static_libs: [
+    "libhealthhalutils",
+  ],
   defaults: [
     "perfetto_defaults",
   ],
   cflags: [
     "-DPERFETTO_BUILD_WITH_ANDROID",
   ],
-  static_libs: [
-    "libhealthhalutils",
-  ],
 }
 
 // GN target: //:perfetto
diff --git a/BUILD.gn b/BUILD.gn
index 36917d0..2b60803 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -279,9 +279,23 @@
   }
 }
 
+# WARNING: this builds correctly only when using the generated Android.bp.
+#
+# This library gets loaded into (and executes in) arbitrary android processes.
+# Logging must be non-allocating. This is achieved by defining
+# PERFETTO_ANDROID_ASYNC_SAFE_LOG, which needs to be set for all perfetto code
+# being compiled for this library. When generating Android.bp, the |cflags|
+# entry on this target is sufficient (as all sources are flattened into a
+# single bp target). However this is not correctly reflected in the gn
+# structure (which is a tree of targets) as the dependencies would not pick
+# up the flag (and thus use the wrong logging macro).
+#
+# This is deemed acceptable as, at the time of writing, there is no interest in
+# building this library standalone.
 if (perfetto_build_with_android) {
   # TODO(fmayer): Investigate shared library for common pieces.
   shared_library("heapprofd_client") {
+    cflags = [ "-DPERFETTO_ANDROID_ASYNC_SAFE_LOG" ]
     deps = [
       "src/profiling/memory:malloc_hooks",
     ]
diff --git a/include/perfetto/base/logging.h b/include/perfetto/base/logging.h
index 00e4671..1b1a268 100644
--- a/include/perfetto/base/logging.h
+++ b/include/perfetto/base/logging.h
@@ -37,7 +37,16 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/utils.h"
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#if defined(PERFETTO_ANDROID_ASYNC_SAFE_LOG)
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+#error "Async-safe logging is limited to Android tree builds"
+#endif
+// For binaries which need a very lightweight logging implementation.
+// Note that this header is incompatible with android/log.h.
+#include <async_safe/log.h>
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// Normal android logging.
 #include <android/log.h>
 #endif
 
@@ -87,9 +96,19 @@
           ##__VA_ARGS__)
 #endif
 
-// Let android log to both stderr and logcat. When part of the Android tree
-// stderr points to /dev/null so logcat is the only way to get some logging.
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
+    defined(PERFETTO_ANDROID_ASYNC_SAFE_LOG)
+#define PERFETTO_XLOG(level, fmt, ...)                                         \
+  do {                                                                         \
+    async_safe_format_log(                                                     \
+        (ANDROID_LOG_DEBUG + ::perfetto::base::LogLev::level), "perfetto",     \
+        "%s " fmt, ::perfetto::base::Basename(__FILE__ ":" PERFETTO_LOG_LINE), \
+        ##__VA_ARGS__);                                                        \
+  } while (0)
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// Standard logging marco on Android - log to both stderr and logcat. When part
+// of the Android tree, stderr points to /dev/null so logcat is the only way to
+// get some logging.
 #define PERFETTO_XLOG(level, fmt, ...)                                         \
   do {                                                                         \
     __android_log_print(                                                       \
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index e2c8ec1..6345d68 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -108,6 +108,7 @@
 additional_args = {
     'heapprofd_client': [
         ('include_dirs', ['bionic/libc']),
+        ('static_libs', ['libasync_safe']),
     ],
     'traced_probes': [
       ('required', ['libperfetto_android_internal']),
@@ -203,6 +204,8 @@
         self.defaults = []
         self.cflags = set()
         self.local_include_dirs = []
+        self.include_dirs = []
+        self.required = []
         self.user_debug_flag = False
 
     def to_string(self, output):
@@ -224,8 +227,8 @@
         self._output_field(output, 'defaults')
         self._output_field(output, 'cflags')
         self._output_field(output, 'local_include_dirs')
-        for key, value in additional_args.get(self.name, []):
-          self._write_value(output, key, value)
+        self._output_field(output, 'include_dirs')
+        self._output_field(output, 'required')
 
         disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
         if self.user_debug_flag or disable_pdk:
@@ -558,6 +561,14 @@
         if module.type == 'cc_library_static':
             module.export_generated_headers = module.generated_headers
 
+        # Merge in additional hardcoded arguments.
+        for key, add_val in additional_args.get(module.name, []):
+          curr = getattr(module, key)
+          if add_val and isinstance(add_val, list) and isinstance(curr, list):
+            curr.extend(add_val)
+          else:
+            raise Error('Unimplemented type of additional_args')
+
         blueprint.add_module(module)