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
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
;
18 /** A bitmask of `ANDROID_DLEXT_` enum values. */
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`. */
28 /** Used by `ANDROID_DLEXT_WRITE_RELRO` and `ANDROID_DLEXT_USE_RELRO`. */
31 /** Used by `ANDROID_DLEXT_USE_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
;
39 enum { ANDROID_DLEXT_USE_NAMESPACE
= 0x200 };
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
{
51 explicit RailEnergy(RailEnergyData
* data
, const char* aRailName
,
52 const char* aSubsystemName
)
53 : BaseProfilerCount(aSubsystemName
, "power", aRailName
),
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),
68 .isSampleNew
= mDataPtr
->timestamp
!= mLastTimestamp
,
70 mLastTimestamp
= mDataPtr
->timestamp
;
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.
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
);
91 ALOG("failed to dlopen libc: %s", dlerror());
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",
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());
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());
126 constexpr size_t kMaxNumRails
= 32;
127 if (!mRailDescriptors
.resize(kMaxNumRails
)) {
128 ALOG("failed to grow mRailDescriptors");
131 size_t numRails
= mRailDescriptors
.length();
132 getAvailableRails(&mRailDescriptors
[0], &numRails
);
133 mRailDescriptors
.shrinkTo(numRails
);
134 ALOG("found %zu rails", numRails
);
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
142 if (!mRailEnergyData
.resize(numRails
)) {
143 ALOG("failed to grow mRailEnergyData");
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
,
150 RailEnergy
* railEnergy
=
151 new RailEnergy(&mRailEnergyData
[i
], rail
.rail_name
, rail
.subsys_name
);
152 if (!mCounters
.emplaceBack(railEnergy
)) {
157 mGetRailEnergyData
= reinterpret_cast<decltype(&GetRailEnergyData
)>(
158 dlsym(mLibperfettoModule
, "GetRailEnergyData"));
159 if (!mGetRailEnergyData
) {
160 ALOG("failed to get GetRailEnergyData pointer");
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
) {
176 size_t length
= mRailEnergyData
.length();
177 mGetRailEnergyData(&mRailEnergyData
[0], &length
);