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
17 #include "core_jni_helpers.h"
18
19 #include <sys/sysinfo.h>
20
21 #include <android-base/stringprintf.h>
22 #include <cputimeinstate.h>
23
24 namespace android {
25
26 static constexpr uint64_t NSEC_PER_MSEC = 1000000;
27
28 static struct {
29 jclass clazz;
30 jmethodID put;
31 jmethodID get;
32 } gSparseArrayClassInfo;
33
34 static jfieldID gmData;
35
getUidArray(JNIEnv * env,jobject sparseAr,uint32_t uid,jsize sz)36 static 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 == nullptr) return ar;
41 env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
42 }
43 return ar;
44 }
45
copy2DVecToArray(JNIEnv * env,jlongArray ar,std::vector<std::vector<uint64_t>> & vec)46 static 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
KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv * env,jclass,jint startUid,jint endUid)56 static 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
KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv * env,jobject thiz)64 static 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 == nullptr) 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 == nullptr) return false;
79 copy2DVecToArray(env, ar, times);
80 }
81 lastUpdate = newLastUpdate;
82 return true;
83 }
84
85 static const JNINativeMethod gFreqTimeMethods[] = {
86 {"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange},
87 {"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData},
88 };
89
KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv * env,jobject thiz)90 static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
91 static uint64_t lastUpdate = 0;
92 uint64_t newLastUpdate = lastUpdate;
93 auto sparseAr = env->GetObjectField(thiz, gmData);
94 if (sparseAr == nullptr) return false;
95 auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
96 if (!data.has_value()) return false;
97
98 for (auto &[uid, times] : *data) {
99 // TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
100 for (auto &time : times.active) time /= NSEC_PER_MSEC;
101 jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
102 if (ar == nullptr) return false;
103 env->SetLongArrayRegion(ar, 0, times.active.size(),
104 reinterpret_cast<const jlong *>(times.active.data()));
105 }
106 lastUpdate = newLastUpdate;
107 return true;
108 }
109
KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv * env,jobject)110 static jlongArray KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
111 jlong nCpus = get_nprocs_conf();
112
113 auto ar = env->NewLongArray(1);
114 if (ar != nullptr) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
115 return ar;
116 }
117
118 static const JNINativeMethod gActiveTimeMethods[] = {
119 {"readBpfData", "()Z", (void *)KernelCpuUidActiveTimeBpfMapReader_readBpfData},
120 {"getDataDimensions", "()[J", (void *)KernelCpuUidActiveTimeBpfMapReader_getDataDimensions},
121 };
122
KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv * env,jobject thiz)123 static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
124 static uint64_t lastUpdate = 0;
125 uint64_t newLastUpdate = lastUpdate;
126 auto sparseAr = env->GetObjectField(thiz, gmData);
127 if (sparseAr == nullptr) return false;
128 auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
129 if (!data.has_value()) return false;
130
131 jsize s = 0;
132 for (auto &[uid, times] : *data) {
133 if (s == 0) {
134 for (const auto &subVec : times.policy) s += subVec.size();
135 }
136 jlongArray ar = getUidArray(env, sparseAr, uid, s);
137 if (ar == nullptr) return false;
138 copy2DVecToArray(env, ar, times.policy);
139 }
140 lastUpdate = newLastUpdate;
141 return true;
142 }
143
KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv * env,jobject)144 static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
145 auto times = android::bpf::getUidConcurrentTimes(0);
146 if (!times.has_value()) return nullptr;
147
148 std::vector<jlong> clusterCores;
149 for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
150 auto ar = env->NewLongArray(clusterCores.size());
151 if (ar != nullptr) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
152 return ar;
153 }
154
155 static const JNINativeMethod gClusterTimeMethods[] = {
156 {"readBpfData", "()Z", (void *)KernelCpuUidClusterTimeBpfMapReader_readBpfData},
157 {"getDataDimensions", "()[J",
158 (void *)KernelCpuUidClusterTimeBpfMapReader_getDataDimensions},
159 };
160
161 struct readerMethods {
162 const char *name;
163 const JNINativeMethod *methods;
164 int numMethods;
165 };
166
167 static const readerMethods gAllMethods[] = {
168 {"KernelCpuUidFreqTimeBpfMapReader", gFreqTimeMethods, NELEM(gFreqTimeMethods)},
169 {"KernelCpuUidActiveTimeBpfMapReader", gActiveTimeMethods, NELEM(gActiveTimeMethods)},
170 {"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)},
171 };
172
register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv * env)173 int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) {
174 gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray");
175 gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz);
176 gSparseArrayClassInfo.put =
177 GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "put", "(ILjava/lang/Object;)V");
178 gSparseArrayClassInfo.get =
179 GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;");
180 constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader";
181 auto c = FindClassOrDie(env, readerName);
182 gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;");
183
184 int ret = 0;
185 for (const auto &m : gAllMethods) {
186 auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name);
187 ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods);
188 if (ret < 0) break;
189 }
190 return ret;
191 }
192
193 } // namespace android
194