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