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