blob: 098a4d8682690a969f181a26dd1f25467415f834 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "core_jni_helpers.h"
#include <sys/sysinfo.h>
#include <android-base/stringprintf.h>
#include <cputimeinstate.h>
namespace android {
static constexpr uint64_t NSEC_PER_MSEC = 1000000;
static struct {
jclass clazz;
jmethodID put;
jmethodID get;
} gSparseArrayClassInfo;
static jfieldID gmData;
static jlongArray getUidArray(JNIEnv *env, jobject sparseAr, uint32_t uid, jsize sz) {
jlongArray ar = (jlongArray)env->CallObjectMethod(sparseAr, gSparseArrayClassInfo.get, uid);
if (!ar) {
ar = env->NewLongArray(sz);
if (ar == nullptr) return ar;
env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
}
return ar;
}
static void copy2DVecToArray(JNIEnv *env, jlongArray ar, std::vector<std::vector<uint64_t>> &vec) {
jsize start = 0;
for (auto &subVec : vec) {
for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
env->SetLongArrayRegion(ar, start, subVec.size(),
reinterpret_cast<const jlong *>(subVec.data()));
start += subVec.size();
}
}
static jboolean KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv *env, jclass, jint startUid,
jint endUid) {
for (uint32_t uid = startUid; uid <= endUid; ++uid) {
if (!android::bpf::clearUidTimes(uid)) return false;
}
return true;
}
static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
static uint64_t lastUpdate = 0;
uint64_t newLastUpdate = lastUpdate;
auto sparseAr = env->GetObjectField(thiz, gmData);
if (sparseAr == nullptr) return false;
auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
if (!data.has_value()) return false;
jsize s = 0;
for (auto &[uid, times] : *data) {
if (s == 0) {
for (const auto &subVec : times) s += subVec.size();
}
jlongArray ar = getUidArray(env, sparseAr, uid, s);
if (ar == nullptr) return false;
copy2DVecToArray(env, ar, times);
}
lastUpdate = newLastUpdate;
return true;
}
static const JNINativeMethod gFreqTimeMethods[] = {
{"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange},
{"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData},
};
static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
static uint64_t lastUpdate = 0;
uint64_t newLastUpdate = lastUpdate;
auto sparseAr = env->GetObjectField(thiz, gmData);
if (sparseAr == nullptr) return false;
auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
if (!data.has_value()) return false;
for (auto &[uid, times] : *data) {
// TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
for (auto &time : times.active) time /= NSEC_PER_MSEC;
jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
if (ar == nullptr) return false;
env->SetLongArrayRegion(ar, 0, times.active.size(),
reinterpret_cast<const jlong *>(times.active.data()));
}
lastUpdate = newLastUpdate;
return true;
}
static jlongArray KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
jlong nCpus = get_nprocs_conf();
auto ar = env->NewLongArray(1);
if (ar != nullptr) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
return ar;
}
static const JNINativeMethod gActiveTimeMethods[] = {
{"readBpfData", "()Z", (void *)KernelCpuUidActiveTimeBpfMapReader_readBpfData},
{"getDataDimensions", "()[J", (void *)KernelCpuUidActiveTimeBpfMapReader_getDataDimensions},
};
static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
static uint64_t lastUpdate = 0;
uint64_t newLastUpdate = lastUpdate;
auto sparseAr = env->GetObjectField(thiz, gmData);
if (sparseAr == nullptr) return false;
auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
if (!data.has_value()) return false;
jsize s = 0;
for (auto &[uid, times] : *data) {
if (s == 0) {
for (const auto &subVec : times.policy) s += subVec.size();
}
jlongArray ar = getUidArray(env, sparseAr, uid, s);
if (ar == nullptr) return false;
copy2DVecToArray(env, ar, times.policy);
}
lastUpdate = newLastUpdate;
return true;
}
static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
auto times = android::bpf::getUidConcurrentTimes(0);
if (!times.has_value()) return nullptr;
std::vector<jlong> clusterCores;
for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
auto ar = env->NewLongArray(clusterCores.size());
if (ar != nullptr) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
return ar;
}
static const JNINativeMethod gClusterTimeMethods[] = {
{"readBpfData", "()Z", (void *)KernelCpuUidClusterTimeBpfMapReader_readBpfData},
{"getDataDimensions", "()[J",
(void *)KernelCpuUidClusterTimeBpfMapReader_getDataDimensions},
};
struct readerMethods {
const char *name;
const JNINativeMethod *methods;
int numMethods;
};
static const readerMethods gAllMethods[] = {
{"KernelCpuUidFreqTimeBpfMapReader", gFreqTimeMethods, NELEM(gFreqTimeMethods)},
{"KernelCpuUidActiveTimeBpfMapReader", gActiveTimeMethods, NELEM(gActiveTimeMethods)},
{"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)},
};
int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) {
gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray");
gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz);
gSparseArrayClassInfo.put =
GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "put", "(ILjava/lang/Object;)V");
gSparseArrayClassInfo.get =
GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;");
constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader";
auto c = FindClassOrDie(env, readerName);
gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;");
int ret = 0;
for (const auto &m : gAllMethods) {
auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name);
ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods);
if (ret < 0) break;
}
return ret;
}
} // namespace android