MediaUtils: Use delayed / lazy library loading

Postpone loading libutilscallstack until needed
to save memory.

Test: atest library_tests
Test: adb shell 'showmap $(pgrep mediametrics)'
Bug: 227536784
Change-Id: I3d4c9e9b6d854b013185e18fc20d4bec320df14e
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 1756e47..3b079c6 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -28,7 +28,9 @@
         "AImageReaderUtils.cpp",
         "BatteryNotifier.cpp",
         "ISchedulingPolicyService.cpp",
+        "Library.cpp",
         "LimitProcessMemory.cpp",
+        "MediaUtilsDelayed.cpp",
         "MemoryLeakTrackUtil.cpp",
         "MethodStatistics.cpp",
         "ProcessInfo.cpp",
@@ -49,7 +51,6 @@
         "libcutils",
         "liblog",
         "libutils",
-        "libutilscallstack",
         "libhidlbase",
         "libpermission",
         "android.hardware.graphics.bufferqueue@1.0",
@@ -77,6 +78,10 @@
         "libpermission",
     ],
 
+    required: [
+        "libmediautils_delayed",  // lazy loaded
+    ],
+
     include_dirs: [
         // For DEBUGGER_SIGNAL
         "system/core/debuggerd/include",
@@ -86,6 +91,23 @@
 }
 
 cc_library {
+    name: "libmediautils_delayed", // match with MEDIAUTILS_DELAYED_LIBRARY_NAME
+    srcs: [
+        "MediaUtilsDelayedLibrary.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libutilscallstack",
+    ],
+}
+
+cc_library {
     name: "libmediautils_vendor",
     vendor_available: true,  // required for platform/hardware/interfaces
     srcs: [
diff --git a/media/utils/Library.cpp b/media/utils/Library.cpp
new file mode 100644
index 0000000..c1e22bf
--- /dev/null
+++ b/media/utils/Library.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Library"
+#include <utils/Log.h>
+#include <mediautils/Library.h>
+
+namespace {
+
+std::string dlerrorIfPresent() {
+    const char *dlerr = dlerror();
+    if (dlerr == nullptr) return "dlerror: none";
+    return std::string("dlerror: '").append(dlerr).append("'");
+}
+
+}
+namespace android::mediautils {
+
+std::shared_ptr<void> loadLibrary(const char *libraryName, int flags) {
+    std::shared_ptr<void> library{
+        dlopen(libraryName, flags),
+        [](void *lib) {
+            if (lib != nullptr) {
+                const int ret = dlclose(lib);
+                ALOGW_IF(ret !=0, "%s: dlclose(%p) == %d, %s",
+                        __func__, lib, ret, dlerrorIfPresent().c_str());
+            }
+        }
+    };
+
+    if (!library) {
+        ALOGW("%s: cannot load libraryName %s, %s",
+            __func__, libraryName, dlerrorIfPresent().c_str());
+        return {};
+    }
+    return library;
+}
+
+std::shared_ptr<void> getUntypedObjectFromLibrary(
+        const char *objectName, const std::shared_ptr<void>& library) {
+    if (!library) {
+        ALOGW("%s: null library, cannot load objectName %s", __func__, objectName);
+        return {};
+    }
+    void *ptr = dlsym(library.get(), objectName);
+    if (ptr == nullptr) {
+        ALOGW("%s: cannot load objectName %s, %s",
+                __func__, objectName, dlerrorIfPresent().c_str());
+        return {};
+    }
+
+    // Note: we use the "aliasing" constructor of the std:shared_ptr.
+    //
+    // https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
+    //
+    return { library, ptr };  // returns shared_ptr to ptr, but ref counted on library.
+}
+
+} // namespace android::mediautils
diff --git a/media/utils/MediaUtilsDelayed.cpp b/media/utils/MediaUtilsDelayed.cpp
new file mode 100644
index 0000000..c6c092d
--- /dev/null
+++ b/media/utils/MediaUtilsDelayed.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mediautils/Library.h>
+#include <mediautils/MediaUtilsDelayed.h>
+#include "MediaUtilsDelayedLibrary.h"
+
+#define LOG_TAG "MediaUtilsDelayed"
+#include <utils/Log.h>
+#include <memory>
+
+namespace android::mediautils {
+
+namespace {
+//  Specific implementation details for MediaUtils Delayed Library.
+
+// The following use static Meyer's singleton caches instead of letting
+// refcounted management as provided above. This is for speed.
+std::shared_ptr<void> getDelayedLibrary() {
+    static std::shared_ptr<void> library = loadLibrary(MEDIAUTILS_DELAYED_LIBRARY_NAME);
+    return library;
+}
+
+// Get the delayed dispatch table.  This is refcounted and keeps the underlying library alive.
+std::shared_ptr<delayed_library::DelayedDispatchTable> getDelayedDispatchTable() {
+    static auto delayedDispatchTable =
+            getObjectFromLibrary<delayed_library::DelayedDispatchTable>(
+                    MEDIAUTILS_DELAYED_DISPATCH_TABLE_SYMBOL_NAME, getDelayedLibrary());
+    return delayedDispatchTable;
+}
+
+} // namespace
+
+// Public implementations of methods here.
+
+std::string getCallStackStringForTid(pid_t tid) {
+    auto delayedDispatchTable = getDelayedDispatchTable();
+    if (!delayedDispatchTable) return {};  // on failure, return empty string
+    return delayedDispatchTable->getCallStackStringForTid(tid);
+}
+
+} // android::mediautils
diff --git a/media/utils/MediaUtilsDelayedLibrary.cpp b/media/utils/MediaUtilsDelayedLibrary.cpp
new file mode 100644
index 0000000..9054c1a
--- /dev/null
+++ b/media/utils/MediaUtilsDelayedLibrary.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MediaUtilsDelayedLibrary.h"
+#include <utils/CallStack.h>
+
+// Methods that are dynamically linked.
+namespace {
+
+std::string getCallStackStringForTid(pid_t tid) {
+    android::CallStack cs{};
+    cs.update(0 /* ignoreDepth */, tid);
+    return cs.toString().c_str();
+}
+
+} // namespace
+
+// leave global, this is picked up from dynamic linking
+android::mediautils::delayed_library::DelayedDispatchTable gDelayedDispatchTable {
+    getCallStackStringForTid,
+};
diff --git a/media/utils/MediaUtilsDelayedLibrary.h b/media/utils/MediaUtilsDelayedLibrary.h
new file mode 100644
index 0000000..3d72a3a
--- /dev/null
+++ b/media/utils/MediaUtilsDelayedLibrary.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unistd.h>
+
+// This should not be directly included by clients.
+// Use MediaUtilsDelayed.h instead.
+
+namespace android::mediautils::delayed_library {
+
+// Use a dispatch table to return methods from the delayed library
+struct DelayedDispatchTable {
+    std::string (*getCallStackStringForTid)(pid_t tid);
+};
+
+// Match with Android.bp and MediaUtilsDelayed.cpp.
+#define MEDIAUTILS_DELAYED_LIBRARY_NAME "libmediautils_delayed.so"
+
+// Match with MediaUtilsDelayed.cpp and MediaUtilsDelayedLibrary.cpp
+#define MEDIAUTILS_DELAYED_DISPATCH_TABLE_SYMBOL_NAME "gDelayedDispatchTable"
+
+} // namespace android::mediautils::delayed_library
diff --git a/media/utils/TimerThread.cpp b/media/utils/TimerThread.cpp
index 3556d7d..6de6b13 100644
--- a/media/utils/TimerThread.cpp
+++ b/media/utils/TimerThread.cpp
@@ -21,8 +21,8 @@
 #include <unistd.h>
 #include <vector>
 
+#include <mediautils/MediaUtilsDelayed.h>
 #include <mediautils/TimerThread.h>
-#include <utils/CallStack.h>
 #include <utils/ThreadDefs.h>
 
 namespace android::mediautils {
@@ -73,13 +73,13 @@
     if (analysis.timeoutTid != -1) {
         timeoutStack = std::string("\ntimeout(")
                 .append(std::to_string(analysis.timeoutTid)).append(") callstack [\n")
-                .append(tidCallStackString(analysis.timeoutTid)).append("]");
+                .append(getCallStackStringForTid(analysis.timeoutTid)).append("]");
     }
     std::string blockedStack;
     if (analysis.HALBlockedTid != -1) {
         blockedStack = std::string("\nblocked(")
                 .append(std::to_string(analysis.HALBlockedTid)).append(")  callstack [\n")
-                .append(tidCallStackString(analysis.HALBlockedTid)).append("]");
+                .append(getCallStackStringForTid(analysis.HALBlockedTid)).append("]");
     }
 
     return std::string("now ")
@@ -213,13 +213,6 @@
     return requestsToString(timeoutRequests);
 }
 
-/* static */
-std::string TimerThread::tidCallStackString(pid_t tid) {
-    CallStack cs{};
-    cs.update(0 /* ignoreDepth */, tid);
-    return cs.toString().c_str();
-}
-
 std::string TimerThread::Request::toString() const {
     const auto scheduledString = formatTime(scheduled);
     const auto deadlineString = formatTime(deadline);
diff --git a/media/utils/include/mediautils/Library.h b/media/utils/include/mediautils/Library.h
new file mode 100644
index 0000000..19cfc11
--- /dev/null
+++ b/media/utils/include/mediautils/Library.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dlfcn.h>
+#include <string>
+#include <unistd.h>
+
+namespace android::mediautils {
+
+/**
+ * Returns a shared pointer to the library instance.
+ *
+ * When the last reference to the library is removed, the library will be dlclose().
+ *
+ * Notes:
+ * 1) The Android bionic linker always uses RTLD_GLOBAL for executable linking
+ * which provides the symbols for other subsequent libraries.
+ *
+ * 2) RTLD_GLOBAL like RTLD_NODELETE disables unloading of the library
+ * when the reference count drops to zero.
+ *
+ * 3) RTLD_LOCAL is the default in the absence of RTLD_GLOBAL.
+ * RTLD_LOCAL may be ignored in some situations, for example:
+ * https://stackoverflow.com/questions/56808889/static-objects-destructed-before-dlclose
+ *
+ * 4) We default to use RTLD_LAZY to delay symbol relocations until needed.
+ * This flag may be ignored by Android.  RTLD_LAZY may allow
+ * unresolved symbols if not accessed, or symbols added later with another library
+ * loaded with RTLD_GLOBAL. See RTLD_NOW for comparison.
+ *
+ * 5) Avoid both staticly loading and dynamically loading the same library.
+ * This is known to cause double free issues as library symbols may map to
+ * the same location.  RTLD_DEEPBIND does not appear supported as of T.
+ * https://stackoverflow.com/questions/34073051/when-we-are-supposed-to-use-rtld-deepbind
+ * https://stackoverflow.com/questions/31209693/static-library-linked-two-times
+ *
+ * Details on Android linker and debugging here:
+ * See: adb shell setprop debug.ld.all dlerror,dlopen,dlsym
+ * See: https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md
+ *
+ * Some other relevant info:
+ * See: Soong double_loadable:true go/double_loadable
+ * See: https://en.wikipedia.org/wiki/One_Definition_Rule#Summary
+ *
+ * TODO(b/228093151): Consider moving to platform/system.
+ *
+ * \param libraryName
+ * \param flags one of the dlopen RTLD_* flags. https://linux.die.net/man/3/dlopen
+ * \return shared_ptr to the library. This will be nullptr if it isn't found.
+ */
+std::shared_ptr<void> loadLibrary(const char *libraryName, int flags = RTLD_LAZY);
+
+/**
+ * Returns a shared pointer to an object in the library
+ *
+ * The object will be a global variable or method in the library.
+ * The object reference counting is aliased to the library shared ptr.
+ *
+ * Note: If any internals of the shared library are exposed, for example by
+ * a method returning a pointer to library globals,
+ * or returning an object whose class definition is from the library,
+ * then the shared_ptr must be kept alive while such references to
+ * library internals exist to prevent library unloading.
+ *
+ * See usage of RTLD_NODELETE as a flag to prevent unloading.
+ *
+ * \param objectName of the library object.
+ * \param library a shared pointer to the library returned by loadLibrary().
+ * \return shared_ptr to the object, but whose refcount is
+ *         aliased to the library shared ptr.
+ */
+std::shared_ptr<void> getUntypedObjectFromLibrary(
+        const char *objectName, const std::shared_ptr<void>& library);
+
+/**
+ * Returns a shared pointer to an object in the library
+ *
+ * This is the template typed version of getUntypedObjectFromLibrary().
+ *
+ * \param objectName of the library object.
+ * \param library a shared pointer to the library
+ * \return shared_ptr to the object, but whose refcount is
+ *         aliased to the library shared ptr.
+ */
+template <typename T>
+std::shared_ptr<T> getObjectFromLibrary(
+        const char *objectName, const std::shared_ptr<void>& library) {
+    return std::static_pointer_cast<T>(getUntypedObjectFromLibrary(objectName, library));
+}
+
+} // android::mediautils
diff --git a/media/utils/include/mediautils/MediaUtilsDelayed.h b/media/utils/include/mediautils/MediaUtilsDelayed.h
new file mode 100644
index 0000000..1583e60
--- /dev/null
+++ b/media/utils/include/mediautils/MediaUtilsDelayed.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unistd.h>
+
+namespace android::mediautils {
+
+// These methods use lazy library loading.
+
+/**
+ * Returns a string callstack from the thread id tid.
+ */
+std::string getCallStackStringForTid(pid_t tid);
+
+} // android::mediautils
diff --git a/media/utils/include/mediautils/TimerThread.h b/media/utils/include/mediautils/TimerThread.h
index b69e02c..ffee602 100644
--- a/media/utils/include/mediautils/TimerThread.h
+++ b/media/utils/include/mediautils/TimerThread.h
@@ -125,11 +125,6 @@
         return s;
     }
 
-    /**
-     * Returns callstack of tid as a string.
-     */
-    static std::string tidCallStackString(pid_t tid);
-
   private:
     // To minimize movement of data, we pass around shared_ptrs to Requests.
     // These are allocated and deallocated outside of the lock.
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 5498ac5..30c10b7 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -7,6 +7,62 @@
     default_applicable_licenses: ["frameworks_av_license"],
 }
 
+cc_test_library {
+    name: "libsharedtest",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+
+    sanitize:{
+       address: true,
+       cfi: true,
+       integer_overflow: true,
+       memtag_heap: true,
+    },
+
+    shared_libs: [
+        "liblog",
+    ],
+
+    srcs: [
+        "sharedtest.cpp",
+    ]
+}
+
+cc_test {
+    name: "library_tests",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+
+    sanitize:{
+       address: true,
+       cfi: true,
+       integer_overflow: true,
+       memtag_heap: true,
+    },
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libmediautils",
+        "libutils",
+    ],
+
+    data_libs: [
+        "libsharedtest",
+    ],
+
+    srcs: [
+        "library_tests.cpp",
+    ],
+}
+
 cc_test {
     name: "media_synchronization_tests",
 
diff --git a/media/utils/tests/library_tests.cpp b/media/utils/tests/library_tests.cpp
new file mode 100644
index 0000000..c5c500c
--- /dev/null
+++ b/media/utils/tests/library_tests.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "library_tests"
+#include <utils/Log.h>
+
+#include <mediautils/Library.h>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+using namespace android::mediautils;
+
+namespace {
+
+static int32_t here = 0;  // accessed on same thread.
+
+TEST(library_tests, basic) {
+    std::string path = android::base::GetExecutableDirectory() + "/libsharedtest.so";
+    // The flags to loadLibrary should not include  RTLD_GLOBAL or RTLD_NODELETE
+    // which prevent unloading.
+    std::shared_ptr<void> library = loadLibrary(path.c_str(), RTLD_LAZY);
+    ASSERT_TRUE(library);
+    ASSERT_EQ(1, library.use_count());
+
+    std::shared_ptr<int32_t*> ptr = getObjectFromLibrary<int32_t*>("gPtr", library);
+    ASSERT_TRUE(ptr);
+    ASSERT_EQ(2, library.use_count());
+
+    ASSERT_EQ(nullptr, *ptr); // original contents are nullptr.
+
+    // There is a static object destructor in libsharedtest.so that will set the
+    // contents of the integer pointer (if non-null) to 1 when called.
+    // This is used to detect that the library is unloaded.
+    *ptr = &here;
+
+    ptr.reset();  // Note: this shared pointer uses library's refcount.
+    ASSERT_EQ(1, library.use_count());  // Verify library's refcount goes down by 1.
+    ASSERT_EQ(0, here);  // the shared library's object destructor hasn't been called.
+
+    // use weak_ptr to investigate whether the library is gone.
+    std::weak_ptr<void> wlibrary = library;
+    ASSERT_EQ(1, wlibrary.use_count());
+    library.reset();
+
+    // we should have released the last reference.
+    ASSERT_EQ(0, wlibrary.use_count());
+
+    // The library should unload and the global object destroyed.
+    // Note on Android, specifying RTLD_GLOBAL or RTLD_NODELETE in the flags
+    // will prevent unloading libraries.
+    ASSERT_EQ(1, here);
+}
+
+TEST(library_tests, sad_library) {
+    std::string path = android::base::GetExecutableDirectory()
+            + "/something_random_library_that_doesn't_exit.so";
+
+    std::shared_ptr<void> library = loadLibrary(path.c_str(), RTLD_LAZY);
+    // We shouldn't crash on an invalid library path, just return an empty shared pointer.
+    // Check the logcat for any error details.
+    ASSERT_FALSE(library);
+}
+
+} // namespace
diff --git a/media/utils/tests/sharedtest.cpp b/media/utils/tests/sharedtest.cpp
new file mode 100644
index 0000000..3888874
--- /dev/null
+++ b/media/utils/tests/sharedtest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+#define LOG_TAG "sharedtest"
+#include <utils/Log.h>
+
+// Test library which is dynamicly loaded by library_tests.
+
+// Static variable construction.
+// Calls A constructor on library load, A destructor on library unload.
+
+int32_t *gPtr = nullptr;  // this pointer is filled with the location to set memory
+                          // when ~A() is called.
+                          // we cannot use anything internal to this file as the
+                          // data segment may no longer exist after unloading the library.
+struct A {
+    A() {
+        ALOGD("%s: gPtr:%p", __func__, gPtr);
+    }
+
+    ~A() {
+        ALOGD("%s: gPtr:%p", __func__, gPtr);
+        if (gPtr != nullptr) {
+            *gPtr = 1;
+        }
+    }
+} gA;
+
+//  __attribute__((constructor)) methods occur before any static variable construction.
+// Libraries that use __attribute__((constructor)) should not rely on global constructors
+// before method call because they will not be initialized before use.
+// See heapprofd_client_api.
+// NOTE: is this right? Shouldn't it occur after construction?
+ __attribute__((constructor))
+void onConstruction() {
+    ALOGD("%s: in progress", __func__);  // for logcat analysis
+}
+
+// __attribute__((destructor)) methods occur before any static variable destruction.
+ __attribute__((destructor))
+void onDestruction() {
+    ALOGD("%s: in progress", __func__);  // for logcat analysis
+}