178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
/* 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);
|
|
}
|