mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1888483 - Support power profiling on Android, r=profiler-reviewers,canaltinova.
Differential Revision: https://phabricator.services.mozilla.com/D206038
This commit is contained in:
parent
7c9f1121a8
commit
fdaacfde35
182
tools/profiler/core/PowerCounters-android.cpp
Normal file
182
tools/profiler/core/PowerCounters-android.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
/* 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, 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);
|
||||
}
|
||||
for (auto* railEnergy : mCounters) {
|
||||
delete railEnergy;
|
||||
}
|
||||
mCounters.clear();
|
||||
}
|
||||
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);
|
||||
}
|
@ -19,10 +19,39 @@ class ProcessPower;
|
||||
#if defined(GP_PLAT_amd64_darwin)
|
||||
class RAPL;
|
||||
#endif
|
||||
#if defined(GP_PLAT_arm64_android)
|
||||
|
||||
/*
|
||||
* These declarations come from:
|
||||
* https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/src/android_internal/power_stats.h;l=34-52;drc=1777bdef274bcfbccd4e6f8b6d00a1bac48a8645
|
||||
*/
|
||||
|
||||
struct RailDescriptor {
|
||||
// Index corresponding to the rail
|
||||
uint32_t index;
|
||||
// Name of the rail
|
||||
char rail_name[64];
|
||||
// Name of the subsystem to which this rail belongs
|
||||
char subsys_name[64];
|
||||
// Hardware sampling rate
|
||||
uint32_t sampling_rate;
|
||||
};
|
||||
|
||||
struct RailEnergyData {
|
||||
// Index corresponding to RailDescriptor.index
|
||||
uint32_t index;
|
||||
// Time since device boot(CLOCK_BOOTTIME) in milli-seconds
|
||||
uint64_t timestamp;
|
||||
// Accumulated energy since device boot in microwatt-seconds (uWs)
|
||||
uint64_t energy;
|
||||
};
|
||||
bool GetRailEnergyData(RailEnergyData*, size_t* size_of_arr);
|
||||
#endif
|
||||
|
||||
class PowerCounters {
|
||||
public:
|
||||
#if defined(_MSC_VER) || defined(GP_OS_darwin) || defined(GP_PLAT_amd64_linux)
|
||||
#if defined(_MSC_VER) || defined(GP_OS_darwin) || \
|
||||
defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_arm64_android)
|
||||
explicit PowerCounters();
|
||||
~PowerCounters();
|
||||
void Sample();
|
||||
@ -47,6 +76,12 @@ class PowerCounters {
|
||||
#if defined(GP_PLAT_amd64_darwin)
|
||||
RAPL* mRapl;
|
||||
#endif
|
||||
#if defined(GP_PLAT_arm64_android)
|
||||
void* mLibperfettoModule = nullptr;
|
||||
decltype(&GetRailEnergyData) mGetRailEnergyData = nullptr;
|
||||
mozilla::Vector<RailDescriptor> mRailDescriptors;
|
||||
mozilla::Vector<RailEnergyData> mRailEnergyData;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* ndef TOOLS_POWERCOUNTERS_H_ */
|
||||
|
@ -85,6 +85,10 @@ if CONFIG["MOZ_GECKO_PROFILER"]:
|
||||
UNIFIED_SOURCES += [
|
||||
"core/PowerCounters-linux.cpp",
|
||||
]
|
||||
elif CONFIG["TARGET_CPU"] == "aarch64" and CONFIG["OS_TARGET"] == "Android":
|
||||
SOURCES += [
|
||||
"core/PowerCounters-android.cpp",
|
||||
]
|
||||
if CONFIG["TARGET_CPU"] == "arm" and CONFIG["OS_TARGET"] != "FreeBSD":
|
||||
SOURCES += [
|
||||
"core/EHABIStackWalk.cpp",
|
||||
|
Loading…
Reference in New Issue
Block a user