summaryrefslogtreecommitdiffstats
path: root/tools/profiler/core/PowerCounters-android.cpp
blob: 5e784952b54938be67eb7f8ff70919b48d7caa68 (plain)
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, 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);
}