Support gcov flushing in libhidlbase.

Create an additional sysprop callback for flushing gcov data when
available (guarded by ro.vts.coverage). Iterate through all libs loaded
into the process and call the flush function.

Test: vts_sancov_configure flush android.hardware.light@2.0
Bug: 67998360
Change-Id: Iad138d70f89cf3c0e68895c7dd0a7bf7207bd85d
diff --git a/base/HidlInternal.cpp b/base/HidlInternal.cpp
index b41c0b9..64b4f26 100644
--- a/base/HidlInternal.cpp
+++ b/base/HidlInternal.cpp
@@ -24,10 +24,14 @@
 #ifdef LIBHIDL_TARGET_DEBUGGABLE
 #include <dirent.h>
 #include <dlfcn.h>
-#include <regex>
+#include <link.h>
 #include <utils/misc.h>
+#include <regex>
 
 extern "C" __attribute__((weak)) void __sanitizer_cov_dump();
+const char* kSysPropHalCoverage = "hal.coverage.enable";
+const char* kGcovPrefixEnvVar = "GCOV_PREFIX";
+const char* kGcovPrefixPath = "/data/misc/trace/";
 #endif
 
 namespace android {
@@ -49,13 +53,41 @@
     if (__sanitizer_cov_dump != nullptr) {
         ::android::add_sysprop_change_callback(
             []() {
-                bool enableCoverage = property_get_bool("hal.coverage.enable", false);
+                bool enableCoverage = property_get_bool(kSysPropHalCoverage, false);
                 if (enableCoverage) {
                     __sanitizer_cov_dump();
                 }
             },
             0);
     }
+    if (property_get_bool("ro.vts.coverage", false)) {
+        const std::string gcovPath = kGcovPrefixPath + std::to_string(getpid());
+        setenv(kGcovPrefixEnvVar, gcovPath.c_str(), true /* overwrite */);
+        ::android::add_sysprop_change_callback(
+            []() {
+                const bool enableCoverage = property_get_bool(kSysPropHalCoverage, false);
+                if (enableCoverage) {
+                    dl_iterate_phdr(
+                        [](struct dl_phdr_info* info, size_t /* size */, void* /* data */) {
+                            if (strlen(info->dlpi_name) == 0) return 0;
+
+                            void* handle = dlopen(info->dlpi_name, RTLD_LAZY);
+                            if (handle == nullptr) {
+                                LOG(INFO) << "coverage dlopen failed: " << dlerror();
+                                return 0;
+                            }
+                            void (*flush)() = (void (*)())dlsym(handle, "__gcov_flush");
+                            if (flush == nullptr) {
+                                return 0;
+                            }
+                            flush();
+                            return 0;
+                        },
+                        nullptr /* data */);
+                }
+            },
+            0 /* priority */);
+    }
 #endif
 }