libtimeinstate: tolerate multiple calls to startTrackingUidTimes

system_server instantiates 2 of each type of KernelCpuUidTimeReader,
but the logic in startTrackingUidTimes() must run exactly once. Revise
startTrackingUidTimes() to return immediately if tracking is already
active. This makes it safe to call from the KernelCpuUidTimeReader
constructor, ensuring that tracking will start as early as possible.

Bug: 138317993
Test: boot & check that time_in_state programs have been attached
exactly once
Change-Id: Ic64438a3270874b398a8db07531521fe1d02a3de
Signed-off-by: Connor O'Brien <connoro@google.com>
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 037846b..1465296 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -49,6 +49,8 @@
 
 static std::mutex gInitializedMutex;
 static bool gInitialized = false;
+static std::mutex gTrackingMutex;
+static bool gTracking = false;
 static uint32_t gNPolicies = 0;
 static uint32_t gNCpus = 0;
 static std::vector<std::vector<uint32_t>> gPolicyFreqs;
@@ -161,7 +163,9 @@
 // This function should *not* be called while tracking is already active; doing so is unnecessary
 // and can lead to accounting errors.
 bool startTrackingUidTimes() {
+    std::lock_guard<std::mutex> guard(gTrackingMutex);
     if (!initGlobals()) return false;
+    if (gTracking) return true;
 
     unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
     if (cpuPolicyFd < 0) return false;
@@ -209,8 +213,9 @@
         if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false;
     }
 
-    return attachTracepointProgram("sched", "sched_switch") &&
+    gTracking = attachTracepointProgram("sched", "sched_switch") &&
             attachTracepointProgram("power", "cpu_frequency");
+    return gTracking;
 }
 
 std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() {