blob: 7c68de5044171c8301689d7530b770a6f2ce2459 [file] [log] [blame]
Connor O'Brien5065db02019-09-12 14:09:26 -07001/*
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
17#include "core_jni_helpers.h"
18
19#include <sys/sysinfo.h>
20
21#include <android-base/stringprintf.h>
22#include <cputimeinstate.h>
23
24namespace android {
25
26static constexpr uint64_t NSEC_PER_MSEC = 1000000;
27
28static struct {
29 jclass clazz;
30 jmethodID put;
31 jmethodID get;
32} gSparseArrayClassInfo;
33
34static jfieldID gmData;
35
36static jlongArray getUidArray(JNIEnv *env, jobject sparseAr, uint32_t uid, jsize sz) {
37 jlongArray ar = (jlongArray)env->CallObjectMethod(sparseAr, gSparseArrayClassInfo.get, uid);
38 if (!ar) {
39 ar = env->NewLongArray(sz);
40 if (ar == NULL) return ar;
41 env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
42 }
43 return ar;
44}
45
46static void copy2DVecToArray(JNIEnv *env, jlongArray ar, std::vector<std::vector<uint64_t>> &vec) {
47 jsize start = 0;
48 for (auto &subVec : vec) {
49 for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
50 env->SetLongArrayRegion(ar, start, subVec.size(),
51 reinterpret_cast<const jlong *>(subVec.data()));
52 start += subVec.size();
53 }
54}
55
56static jboolean KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv *env, jclass, jint startUid,
57 jint endUid) {
58 for (uint32_t uid = startUid; uid <= endUid; ++uid) {
59 if (!android::bpf::clearUidTimes(uid)) return false;
60 }
61 return true;
62}
63
64static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
65 static uint64_t lastUpdate = 0;
66 uint64_t newLastUpdate = lastUpdate;
67 auto sparseAr = env->GetObjectField(thiz, gmData);
68 if (sparseAr == NULL) return false;
69 auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
70 if (!data.has_value()) return false;
71
72 jsize s = 0;
73 for (auto &[uid, times] : *data) {
74 if (s == 0) {
75 for (const auto &subVec : times) s += subVec.size();
76 }
77 jlongArray ar = getUidArray(env, sparseAr, uid, s);
78 if (ar == NULL) return false;
79 copy2DVecToArray(env, ar, times);
80 }
81 lastUpdate = newLastUpdate;
82 return true;
83}
84
85static jlongArray KernelCpuUidFreqTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
86 auto freqs = android::bpf::getCpuFreqs();
87 if (!freqs) return NULL;
88
89 std::vector<uint64_t> allFreqs;
90 for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
91
92 auto ar = env->NewLongArray(allFreqs.size());
93 if (ar != NULL) {
94 env->SetLongArrayRegion(ar, 0, allFreqs.size(),
95 reinterpret_cast<const jlong *>(allFreqs.data()));
96 }
97 return ar;
98}
99
100static const JNINativeMethod gFreqTimeMethods[] = {
101 {"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange},
102 {"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData},
103 {"getDataDimensions", "()[J", (void *)KernelCpuUidFreqTimeBpfMapReader_getDataDimensions},
104};
105
106static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
107 static uint64_t lastUpdate = 0;
108 uint64_t newLastUpdate = lastUpdate;
109 auto sparseAr = env->GetObjectField(thiz, gmData);
110 if (sparseAr == NULL) return false;
111 auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
112 if (!data.has_value()) return false;
113
114 for (auto &[uid, times] : *data) {
115 // TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
116 for (auto &time : times.active) time /= NSEC_PER_MSEC;
117 jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
118 if (ar == NULL) return false;
119 env->SetLongArrayRegion(ar, 0, times.active.size(),
120 reinterpret_cast<const jlong *>(times.active.data()));
121 }
122 lastUpdate = newLastUpdate;
123 return true;
124}
125
126static jlongArray KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
127 jlong nCpus = get_nprocs_conf();
128
129 auto ar = env->NewLongArray(1);
130 if (ar != NULL) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
131 return ar;
132}
133
134static const JNINativeMethod gActiveTimeMethods[] = {
135 {"readBpfData", "()Z", (void *)KernelCpuUidActiveTimeBpfMapReader_readBpfData},
136 {"getDataDimensions", "()[J", (void *)KernelCpuUidActiveTimeBpfMapReader_getDataDimensions},
137};
138
139static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
140 static uint64_t lastUpdate = 0;
141 uint64_t newLastUpdate = lastUpdate;
142 auto sparseAr = env->GetObjectField(thiz, gmData);
143 if (sparseAr == NULL) return false;
144 auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
145 if (!data.has_value()) return false;
146
147 jsize s = 0;
148 for (auto &[uid, times] : *data) {
149 if (s == 0) {
150 for (const auto &subVec : times.policy) s += subVec.size();
151 }
152 jlongArray ar = getUidArray(env, sparseAr, uid, s);
153 if (ar == NULL) return false;
154 copy2DVecToArray(env, ar, times.policy);
155 }
156 lastUpdate = newLastUpdate;
157 return true;
158}
159
160static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
161 auto times = android::bpf::getUidConcurrentTimes(0);
162 if (!times.has_value()) return NULL;
163
164 std::vector<jlong> clusterCores;
165 for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
166 auto ar = env->NewLongArray(clusterCores.size());
167 if (ar != NULL) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
168 return ar;
169}
170
171static const JNINativeMethod gClusterTimeMethods[] = {
172 {"readBpfData", "()Z", (void *)KernelCpuUidClusterTimeBpfMapReader_readBpfData},
173 {"getDataDimensions", "()[J",
174 (void *)KernelCpuUidClusterTimeBpfMapReader_getDataDimensions},
175};
176
177struct readerMethods {
178 const char *name;
179 const JNINativeMethod *methods;
180 int numMethods;
181};
182
183static const readerMethods gAllMethods[] = {
184 {"KernelCpuUidFreqTimeBpfMapReader", gFreqTimeMethods, NELEM(gFreqTimeMethods)},
185 {"KernelCpuUidActiveTimeBpfMapReader", gActiveTimeMethods, NELEM(gActiveTimeMethods)},
186 {"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)},
187};
188
189static jboolean KernelCpuUidBpfMapReader_startTrackingBpfTimes(JNIEnv *, jobject) {
190 return android::bpf::startTrackingUidTimes();
191}
192
193int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) {
194 gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray");
195 gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz);
196 gSparseArrayClassInfo.put =
197 GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "put", "(ILjava/lang/Object;)V");
198 gSparseArrayClassInfo.get =
199 GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;");
200 constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader";
201 constexpr JNINativeMethod method = {"startTrackingBpfTimes", "()Z",
202 (void *)KernelCpuUidBpfMapReader_startTrackingBpfTimes};
203
204 int ret = RegisterMethodsOrDie(env, readerName, &method, 1);
205 if (ret < 0) return ret;
206 auto c = FindClassOrDie(env, readerName);
207 gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;");
208
209 for (const auto &m : gAllMethods) {
210 auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name);
211 ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods);
212 if (ret < 0) break;
213 }
214 return ret;
215}
216
217} // namespace android