Source code
Revision control
Copy as Markdown
Other Tools
/* 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
#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).
*/
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
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, nullptr, nullptr, "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);
}