PageRenderTime 58ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp

https://github.com/android/platform_frameworks_base
C++ | 333 lines | 236 code | 56 blank | 41 comment | 31 complexity | 4bb96bf1361091f6bfdc5798b560c2ac MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Copyright (C) 2020 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "core_jni_helpers.h"
  17. #include <cputimeinstate.h>
  18. #include <dirent.h>
  19. #include <android-base/file.h>
  20. #include <android-base/parseint.h>
  21. #include <android-base/stringprintf.h>
  22. #include <android-base/strings.h>
  23. #include <android_runtime/Log.h>
  24. #include <nativehelper/ScopedPrimitiveArray.h>
  25. namespace android {
  26. static constexpr uint16_t DEFAULT_THREAD_AGGREGATION_KEY = 0;
  27. static constexpr uint16_t SELECTED_THREAD_AGGREGATION_KEY = 1;
  28. static constexpr uint64_t NSEC_PER_MSEC = 1000000;
  29. // Number of milliseconds in a jiffy - the unit of time measurement for processes and threads
  30. static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK));
  31. // Abstract class for readers of CPU time-in-state. There are two implementations of
  32. // this class: BpfCpuTimeInStateReader and MockCpuTimeInStateReader. The former is used
  33. // by the production code. The latter is used by unit tests to provide mock
  34. // CPU time-in-state data via a Java implementation.
  35. class ICpuTimeInStateReader {
  36. public:
  37. virtual ~ICpuTimeInStateReader() {}
  38. // Returns the overall number of cluser-frequency combinations
  39. virtual size_t getCpuFrequencyCount();
  40. // Marks the CPU time-in-state tracking for threads of the specified TGID
  41. virtual bool startTrackingProcessCpuTimes(pid_t) = 0;
  42. // Marks the thread specified by its PID for CPU time-in-state tracking.
  43. virtual bool startAggregatingTaskCpuTimes(pid_t, uint16_t) = 0;
  44. // Retrieves the accumulated time-in-state data, which is organized as a map
  45. // from aggregation keys to vectors of vectors using the format:
  46. // { aggKey0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
  47. // aggKey1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
  48. // where ti_j_k is the ns tid i spent running on the jth cluster at the cluster's kth lowest
  49. // freq.
  50. virtual std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
  51. getAggregatedTaskCpuFreqTimes(pid_t, const std::vector<uint16_t> &);
  52. };
  53. // ICpuTimeInStateReader that uses eBPF to provide a map of aggregated CPU time-in-state values.
  54. // See cputtimeinstate.h/.cpp
  55. class BpfCpuTimeInStateReader : public ICpuTimeInStateReader {
  56. public:
  57. size_t getCpuFrequencyCount() {
  58. std::optional<std::vector<std::vector<uint32_t>>> cpuFreqs = android::bpf::getCpuFreqs();
  59. if (!cpuFreqs) {
  60. ALOGE("Cannot obtain CPU frequency count");
  61. return 0;
  62. }
  63. size_t freqCount = 0;
  64. for (auto cluster : *cpuFreqs) {
  65. freqCount += cluster.size();
  66. }
  67. return freqCount;
  68. }
  69. bool startTrackingProcessCpuTimes(pid_t tgid) {
  70. return android::bpf::startTrackingProcessCpuTimes(tgid);
  71. }
  72. bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
  73. return android::bpf::startAggregatingTaskCpuTimes(pid, aggregationKey);
  74. }
  75. std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
  76. getAggregatedTaskCpuFreqTimes(pid_t pid, const std::vector<uint16_t> &aggregationKeys) {
  77. return android::bpf::getAggregatedTaskCpuFreqTimes(pid, aggregationKeys);
  78. }
  79. };
  80. // ICpuTimeInStateReader that uses JNI to provide a map of aggregated CPU time-in-state
  81. // values.
  82. // This version of CpuTimeInStateReader is used exclusively for providing mock data in tests.
  83. class MockCpuTimeInStateReader : public ICpuTimeInStateReader {
  84. private:
  85. JNIEnv *mEnv;
  86. jobject mCpuTimeInStateReader;
  87. public:
  88. MockCpuTimeInStateReader(JNIEnv *env, jobject cpuTimeInStateReader)
  89. : mEnv(env), mCpuTimeInStateReader(cpuTimeInStateReader) {}
  90. size_t getCpuFrequencyCount();
  91. bool startTrackingProcessCpuTimes(pid_t tgid);
  92. bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey);
  93. std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
  94. getAggregatedTaskCpuFreqTimes(pid_t tgid, const std::vector<uint16_t> &aggregationKeys);
  95. };
  96. static ICpuTimeInStateReader *getCpuTimeInStateReader(JNIEnv *env,
  97. jobject cpuTimeInStateReaderObject) {
  98. if (cpuTimeInStateReaderObject) {
  99. return new MockCpuTimeInStateReader(env, cpuTimeInStateReaderObject);
  100. } else {
  101. return new BpfCpuTimeInStateReader();
  102. }
  103. }
  104. static jint getCpuFrequencyCount(JNIEnv *env, jclass, jobject cpuTimeInStateReaderObject) {
  105. std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
  106. getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
  107. return cpuTimeInStateReader->getCpuFrequencyCount();
  108. }
  109. static jboolean startTrackingProcessCpuTimes(JNIEnv *env, jclass, jint tgid,
  110. jobject cpuTimeInStateReaderObject) {
  111. std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
  112. getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
  113. return cpuTimeInStateReader->startTrackingProcessCpuTimes(tgid);
  114. }
  115. static jboolean startAggregatingThreadCpuTimes(JNIEnv *env, jclass, jintArray selectedThreadIdArray,
  116. jobject cpuTimeInStateReaderObject) {
  117. ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
  118. std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
  119. getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
  120. for (int i = 0; i < selectedThreadIds.size(); i++) {
  121. if (!cpuTimeInStateReader->startAggregatingTaskCpuTimes(selectedThreadIds[i],
  122. SELECTED_THREAD_AGGREGATION_KEY)) {
  123. return false;
  124. }
  125. }
  126. return true;
  127. }
  128. // Converts time-in-state data from a vector of vectors to a flat array.
  129. // Also converts from nanoseconds to milliseconds.
  130. static bool flattenTimeInStateData(ScopedLongArrayRW &cpuTimesMillis,
  131. const std::vector<std::vector<uint64_t>> &data) {
  132. size_t frequencyCount = cpuTimesMillis.size();
  133. size_t index = 0;
  134. for (const auto &cluster : data) {
  135. for (const uint64_t &timeNanos : cluster) {
  136. if (index < frequencyCount) {
  137. cpuTimesMillis[index] = timeNanos / NSEC_PER_MSEC;
  138. }
  139. index++;
  140. }
  141. }
  142. if (index != frequencyCount) {
  143. ALOGE("CPU time-in-state reader returned data for %zu frequencies; expected: %zu", index,
  144. frequencyCount);
  145. return false;
  146. }
  147. return true;
  148. }
  149. // Reads all CPU time-in-state data accumulated by BPF and aggregates per-frequency
  150. // time in state data for all threads. Also, separately aggregates time in state for
  151. // selected threads whose TIDs are passes as selectedThreadIds.
  152. static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jint pid,
  153. jlongArray threadCpuTimesMillisArray,
  154. jlongArray selectedThreadCpuTimesMillisArray,
  155. jobject cpuTimeInStateReaderObject) {
  156. ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray);
  157. ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray);
  158. std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
  159. getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
  160. const size_t frequencyCount = cpuTimeInStateReader->getCpuFrequencyCount();
  161. if (threadCpuTimesMillis.size() != frequencyCount) {
  162. ALOGE("Invalid threadCpuTimesMillis array length: %zu frequencies; expected: %zu",
  163. threadCpuTimesMillis.size(), frequencyCount);
  164. return false;
  165. }
  166. if (selectedThreadCpuTimesMillis.size() != frequencyCount) {
  167. ALOGE("Invalid selectedThreadCpuTimesMillis array length: %zu frequencies; expected: %zu",
  168. selectedThreadCpuTimesMillis.size(), frequencyCount);
  169. return false;
  170. }
  171. for (size_t i = 0; i < frequencyCount; i++) {
  172. threadCpuTimesMillis[i] = 0;
  173. selectedThreadCpuTimesMillis[i] = 0;
  174. }
  175. std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> data =
  176. cpuTimeInStateReader->getAggregatedTaskCpuFreqTimes(pid,
  177. {DEFAULT_THREAD_AGGREGATION_KEY,
  178. SELECTED_THREAD_AGGREGATION_KEY});
  179. if (!data) {
  180. ALOGE("Cannot read thread CPU times for PID %d", pid);
  181. return false;
  182. }
  183. if (!flattenTimeInStateData(threadCpuTimesMillis, (*data)[DEFAULT_THREAD_AGGREGATION_KEY])) {
  184. return false;
  185. }
  186. if (!flattenTimeInStateData(selectedThreadCpuTimesMillis,
  187. (*data)[SELECTED_THREAD_AGGREGATION_KEY])) {
  188. return false;
  189. }
  190. // threadCpuTimesMillis returns CPU times for _all_ threads, including the selected ones
  191. for (size_t i = 0; i < frequencyCount; i++) {
  192. threadCpuTimesMillis[i] += selectedThreadCpuTimesMillis[i];
  193. }
  194. return true;
  195. }
  196. static const JNINativeMethod g_single_methods[] = {
  197. {"getCpuFrequencyCount",
  198. "(Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)I",
  199. (void *)getCpuFrequencyCount},
  200. {"startTrackingProcessCpuTimes",
  201. "(ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
  202. (void *)startTrackingProcessCpuTimes},
  203. {"startAggregatingThreadCpuTimes",
  204. "([ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
  205. (void *)startAggregatingThreadCpuTimes},
  206. {"readProcessCpuUsage",
  207. "(I[J[J"
  208. "Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
  209. (void *)readProcessCpuUsage},
  210. };
  211. int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) {
  212. return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleProcessCpuThreadReader",
  213. g_single_methods, NELEM(g_single_methods));
  214. }
  215. size_t MockCpuTimeInStateReader::getCpuFrequencyCount() {
  216. jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
  217. jmethodID mid = mEnv->GetMethodID(cls, "getCpuFrequencyCount", "()I");
  218. if (mid == 0) {
  219. ALOGE("Couldn't find the method getCpuFrequencyCount");
  220. return false;
  221. }
  222. return (size_t)mEnv->CallIntMethod(mCpuTimeInStateReader, mid);
  223. }
  224. bool MockCpuTimeInStateReader::startTrackingProcessCpuTimes(pid_t tgid) {
  225. jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
  226. jmethodID mid = mEnv->GetMethodID(cls, "startTrackingProcessCpuTimes", "(I)Z");
  227. if (mid == 0) {
  228. ALOGE("Couldn't find the method startTrackingProcessCpuTimes");
  229. return false;
  230. }
  231. return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, tgid);
  232. }
  233. bool MockCpuTimeInStateReader::startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
  234. jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
  235. jmethodID mid = mEnv->GetMethodID(cls, "startAggregatingTaskCpuTimes", "(II)Z");
  236. if (mid == 0) {
  237. ALOGE("Couldn't find the method startAggregatingTaskCpuTimes");
  238. return false;
  239. }
  240. return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, pid, aggregationKey);
  241. }
  242. std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
  243. MockCpuTimeInStateReader::getAggregatedTaskCpuFreqTimes(
  244. pid_t pid, const std::vector<uint16_t> &aggregationKeys) {
  245. jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
  246. jmethodID mid =
  247. mEnv->GetMethodID(cls, "getAggregatedTaskCpuFreqTimes", "(I)[Ljava/lang/String;");
  248. if (mid == 0) {
  249. ALOGE("Couldn't find the method getAggregatedTaskCpuFreqTimes");
  250. return {};
  251. }
  252. std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map;
  253. jobjectArray stringArray =
  254. (jobjectArray)mEnv->CallObjectMethod(mCpuTimeInStateReader, mid, pid);
  255. int size = mEnv->GetArrayLength(stringArray);
  256. for (int i = 0; i < size; i++) {
  257. ScopedUtfChars line(mEnv, (jstring)mEnv->GetObjectArrayElement(stringArray, i));
  258. uint16_t aggregationKey;
  259. std::vector<std::vector<uint64_t>> times;
  260. // Each string is formatted like this: "aggKey:t0_0 t0_1...:t1_0 t1_1..."
  261. auto fields = android::base::Split(line.c_str(), ":");
  262. android::base::ParseUint(fields[0], &aggregationKey);
  263. for (int j = 1; j < fields.size(); j++) {
  264. auto numbers = android::base::Split(fields[j], " ");
  265. std::vector<uint64_t> chunk;
  266. for (int k = 0; k < numbers.size(); k++) {
  267. uint64_t time;
  268. android::base::ParseUint(numbers[k], &time);
  269. chunk.emplace_back(time);
  270. }
  271. times.emplace_back(chunk);
  272. }
  273. map.emplace(aggregationKey, times);
  274. }
  275. return map;
  276. }
  277. } // namespace android