Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / tools / profiler / core / PowerCounters-android.cpp
blob895a1abcd56e72e3911efc8ec8c1144849fb2b94
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "PowerCounters.h"
6 #include "nsXULAppAPI.h" // for XRE_IsParentProcess
7 #include <dlfcn.h>
9 #define ALOG(args...) \
10 __android_log_print(ANDROID_LOG_INFO, "GeckoProfiler", ##args)
13 * The following declarations come from the dlext.h header (not in the ndk).
14 * https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/include/android/dlext.h;drc=655e430b28d7404f763e7ebefe84fba5a387666d
16 struct android_namespace_t;
17 typedef struct {
18 /** A bitmask of `ANDROID_DLEXT_` enum values. */
19 uint64_t flags;
21 /** Used by `ANDROID_DLEXT_RESERVED_ADDRESS` and
22 * `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`. */
23 void* _Nullable reserved_addr;
24 /** Used by `ANDROID_DLEXT_RESERVED_ADDRESS` and
25 * `ANDROID_DLEXT_RESERVED_ADDRESS_HINT`. */
26 size_t reserved_size;
28 /** Used by `ANDROID_DLEXT_WRITE_RELRO` and `ANDROID_DLEXT_USE_RELRO`. */
29 int relro_fd;
31 /** Used by `ANDROID_DLEXT_USE_LIBRARY_FD`. */
32 int library_fd;
33 /** Used by `ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET` */
34 off64_t library_fd_offset;
36 /** Used by `ANDROID_DLEXT_USE_NAMESPACE`. */
37 struct android_namespace_t* _Nullable library_namespace;
38 } android_dlextinfo;
39 enum { ANDROID_DLEXT_USE_NAMESPACE = 0x200 };
40 extern "C"
41 __attribute__((visibility("default"))) void* _Nullable android_dlopen_ext(
42 const char* _Nullable __filename, int __flags,
43 const android_dlextinfo* _Nullable __info);
45 // See also documentation at
46 // https://developer.android.com/studio/profile/power-profiler#power-rails
47 bool GetAvailableRails(RailDescriptor*, size_t* size_of_arr);
49 class RailEnergy final : public BaseProfilerCount {
50 public:
51 explicit RailEnergy(RailEnergyData* data, const char* aRailName,
52 const char* aSubsystemName)
53 : BaseProfilerCount(aSubsystemName, "power", aRailName),
54 mDataPtr(data),
55 mLastTimestamp(0) {}
57 ~RailEnergy() {}
59 RailEnergy(const RailEnergy&) = delete;
60 RailEnergy& operator=(const RailEnergy&) = delete;
62 CountSample Sample() override {
63 CountSample result = {
64 // RailEnergyData.energy is in microwatt-seconds (uWs)
65 // we need to return values in picowatt-hour.
66 .count = static_cast<int64_t>(mDataPtr->energy * 1e3 / 3.6),
67 .number = 0,
68 .isSampleNew = mDataPtr->timestamp != mLastTimestamp,
70 mLastTimestamp = mDataPtr->timestamp;
71 return result;
74 private:
75 RailEnergyData* mDataPtr;
76 uint64_t mLastTimestamp;
79 PowerCounters::PowerCounters() {
80 if (!XRE_IsParentProcess()) {
81 // Energy meters are global, so only sample them on the parent.
82 return;
85 // A direct dlopen call on libperfetto_android_internal.so fails with a
86 // namespace error because libperfetto_android_internal.so is missing in
87 // /etc/public.libraries.txt
88 // Instead, use android_dlopen_ext with the "default" namespace.
89 void* libcHandle = dlopen("libc.so", RTLD_LAZY);
90 if (!libcHandle) {
91 ALOG("failed to dlopen libc: %s", dlerror());
92 return;
95 struct android_namespace_t* (*android_get_exported_namespace)(const char*) =
96 reinterpret_cast<struct android_namespace_t* (*)(const char*)>(
97 dlsym(libcHandle, "__loader_android_get_exported_namespace"));
98 if (!android_get_exported_namespace) {
99 ALOG("failed to get __loader_android_get_exported_namespace: %s",
100 dlerror());
101 return;
104 struct android_namespace_t* ns = android_get_exported_namespace("default");
105 const android_dlextinfo dlextinfo = {
106 .flags = ANDROID_DLEXT_USE_NAMESPACE,
107 .library_namespace = ns,
110 mLibperfettoModule = android_dlopen_ext("libperfetto_android_internal.so",
111 RTLD_LOCAL | RTLD_LAZY, &dlextinfo);
112 MOZ_ASSERT(mLibperfettoModule);
113 if (!mLibperfettoModule) {
114 ALOG("failed to get libperfetto handle: %s", dlerror());
115 return;
118 decltype(&GetAvailableRails) getAvailableRails =
119 reinterpret_cast<decltype(&GetAvailableRails)>(
120 dlsym(mLibperfettoModule, "GetAvailableRails"));
121 if (!getAvailableRails) {
122 ALOG("failed to get GetAvailableRails pointer: %s", dlerror());
123 return;
126 constexpr size_t kMaxNumRails = 32;
127 if (!mRailDescriptors.resize(kMaxNumRails)) {
128 ALOG("failed to grow mRailDescriptors");
129 return;
131 size_t numRails = mRailDescriptors.length();
132 getAvailableRails(&mRailDescriptors[0], &numRails);
133 mRailDescriptors.shrinkTo(numRails);
134 ALOG("found %zu rails", numRails);
135 if (numRails == 0) {
136 // We will see 0 rails either if the device has no support for power
137 // profiling or if the SELinux policy blocks access (ie. on a non-rooted
138 // device).
139 return;
142 if (!mRailEnergyData.resize(numRails)) {
143 ALOG("failed to grow mRailEnergyData");
144 return;
146 for (size_t i = 0; i < numRails; ++i) {
147 RailDescriptor& rail = mRailDescriptors[i];
148 ALOG("rail %zu, name: %s, subsystem: %s", i, rail.rail_name,
149 rail.subsys_name);
150 RailEnergy* railEnergy =
151 new RailEnergy(&mRailEnergyData[i], rail.rail_name, rail.subsys_name);
152 if (!mCounters.emplaceBack(railEnergy)) {
153 delete railEnergy;
157 mGetRailEnergyData = reinterpret_cast<decltype(&GetRailEnergyData)>(
158 dlsym(mLibperfettoModule, "GetRailEnergyData"));
159 if (!mGetRailEnergyData) {
160 ALOG("failed to get GetRailEnergyData pointer");
161 return;
164 PowerCounters::~PowerCounters() {
165 if (mLibperfettoModule) {
166 dlclose(mLibperfettoModule);
169 void PowerCounters::Sample() {
170 // Energy meters are global, so only sample them on the parent.
171 // Also return early if we failed to access the GetRailEnergyData symbol.
172 if (!XRE_IsParentProcess() || !mGetRailEnergyData) {
173 return;
176 size_t length = mRailEnergyData.length();
177 mGetRailEnergyData(&mRailEnergyData[0], &length);