From cc793ab6084411404dc290e2a897f0fed2d429bd Mon Sep 17 00:00:00 2001 From: Ioannis Ilkos Date: Sun, 28 Nov 2021 22:10:16 +0000 Subject: [PATCH] Read per-process GPU memory when pulling ProcessMemorySnapshot This is an eBPF usermap read via libmeminfo. System server already has permissions to do so (as it already does so for the total GPU mem stats). Change-Id: I9e3c4dc9144708c630215e309b7ff394abbdcd76 Test: manual, statsd_testdrive 10064 Bug: 208440056 --- ...Reader.java => KernelAllocationStats.java} | 22 ++++- core/jni/Android.bp | 2 +- core/jni/AndroidRuntime.cpp | 4 +- ...m_android_internal_os_DmabufInfoReader.cpp | 60 ------------ ...roid_internal_os_KernelAllocationStats.cpp | 97 +++++++++++++++++++ .../stats/pull/StatsPullAtomService.java | 25 +++-- 6 files changed, 134 insertions(+), 76 deletions(-) rename core/java/com/android/internal/os/{DmabufInfoReader.java => KernelAllocationStats.java} (72%) delete mode 100644 core/jni/com_android_internal_os_DmabufInfoReader.cpp create mode 100644 core/jni/com_android_internal_os_KernelAllocationStats.cpp diff --git a/core/java/com/android/internal/os/DmabufInfoReader.java b/core/java/com/android/internal/os/KernelAllocationStats.java similarity index 72% rename from core/java/com/android/internal/os/DmabufInfoReader.java rename to core/java/com/android/internal/os/KernelAllocationStats.java index 786a6eedf3438..1c3f8b0bf0958 100644 --- a/core/java/com/android/internal/os/DmabufInfoReader.java +++ b/core/java/com/android/internal/os/KernelAllocationStats.java @@ -18,9 +18,9 @@ package com.android.internal.os; import android.annotation.Nullable; -/** Wrapper around libdmabufinfo. */ -public final class DmabufInfoReader { - private DmabufInfoReader() {} +/** JNI wrapper around libmeminfo for kernel memory allocation stats (dmabufs, gpu driver). */ +public final class KernelAllocationStats { + private KernelAllocationStats() {} /** Process dma-buf stats. */ public static final class ProcessDmabuf { @@ -47,5 +47,19 @@ public final class DmabufInfoReader { * stats could not be read. */ @Nullable - public static native ProcessDmabuf getProcessStats(int pid); + public static native ProcessDmabuf getDmabufAllocations(int pid); + + /** Pid to gpu memory size. */ + public static final class ProcessGpuMem { + public final int pid; + public final int gpuMemoryKb; + + ProcessGpuMem(int pid, int gpuMemoryKb) { + this.pid = pid; + this.gpuMemoryKb = gpuMemoryKb; + } + } + + /** Return list of pid to gpu memory size. */ + public static native ProcessGpuMem[] getGpuAllocations(); } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 1a1a8badf82b1..da628635af368 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -210,8 +210,8 @@ cc_library_shared { "com_android_internal_content_om_OverlayConfig.cpp", "com_android_internal_net_NetworkUtilsInternal.cpp", "com_android_internal_os_ClassLoaderFactory.cpp", - "com_android_internal_os_DmabufInfoReader.cpp", "com_android_internal_os_FuseAppLoop.cpp", + "com_android_internal_os_KernelAllocationStats.cpp", "com_android_internal_os_KernelCpuBpfTracking.cpp", "com_android_internal_os_KernelCpuTotalBpfMapReader.cpp", "com_android_internal_os_KernelCpuUidBpfMapReader.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 18e85b6eb0be0..04fafb441a28f 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -197,8 +197,8 @@ extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env); extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env); extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env); -extern int register_com_android_internal_os_DmabufInfoReader(JNIEnv* env); extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env); +extern int register_com_android_internal_os_KernelAllocationStats(JNIEnv* env); extern int register_com_android_internal_os_KernelCpuBpfTracking(JNIEnv* env); extern int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv* env); extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env); @@ -1655,8 +1655,8 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_security_Scrypt), REG_JNI(register_com_android_internal_content_F2fsUtils), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), - REG_JNI(register_com_android_internal_os_DmabufInfoReader), REG_JNI(register_com_android_internal_os_FuseAppLoop), + REG_JNI(register_com_android_internal_os_KernelAllocationStats), REG_JNI(register_com_android_internal_os_KernelCpuBpfTracking), REG_JNI(register_com_android_internal_os_KernelCpuTotalBpfMapReader), REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader), diff --git a/core/jni/com_android_internal_os_DmabufInfoReader.cpp b/core/jni/com_android_internal_os_DmabufInfoReader.cpp deleted file mode 100644 index 4b0a6ac5b6c40..0000000000000 --- a/core/jni/com_android_internal_os_DmabufInfoReader.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2021 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 -#include "core_jni_helpers.h" - -namespace android { - -static jobject DmabufInfoReader_getProcessStats(JNIEnv *env, jobject, jint pid) { - std::vector buffers; - if (!dmabufinfo::ReadDmaBufMapRefs(pid, &buffers)) { - return nullptr; - } - jint mappedSize = 0; - jint mappedCount = buffers.size(); - for (const auto &buffer : buffers) { - mappedSize += buffer.size(); - } - mappedSize /= 1024; - - jint retainedSize = -1; - jint retainedCount = -1; - if (dmabufinfo::ReadDmaBufFdRefs(pid, &buffers)) { - retainedCount = buffers.size(); - retainedSize = 0; - for (const auto &buffer : buffers) { - retainedSize += buffer.size(); - } - retainedSize /= 1024; - } - - jclass clazz = FindClassOrDie(env, "com/android/internal/os/DmabufInfoReader$ProcessDmabuf"); - jmethodID constructID = GetMethodIDOrDie(env, clazz, "", "(IIII)V"); - return env->NewObject(clazz, constructID, retainedSize, retainedCount, mappedSize, mappedCount); -} - -static const JNINativeMethod methods[] = { - {"getProcessStats", "(I)Lcom/android/internal/os/DmabufInfoReader$ProcessDmabuf;", - (void *)DmabufInfoReader_getProcessStats}, -}; - -int register_com_android_internal_os_DmabufInfoReader(JNIEnv *env) { - return RegisterMethodsOrDie(env, "com/android/internal/os/DmabufInfoReader", methods, - NELEM(methods)); -} - -} // namespace android diff --git a/core/jni/com_android_internal_os_KernelAllocationStats.cpp b/core/jni/com_android_internal_os_KernelAllocationStats.cpp new file mode 100644 index 0000000000000..e0a24430e739e --- /dev/null +++ b/core/jni/com_android_internal_os_KernelAllocationStats.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2021 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 +#include +#include + +#include "core_jni_helpers.h" + +namespace { +static jclass gProcessDmabufClazz; +static jmethodID gProcessDmabufCtor; +static jclass gProcessGpuMemClazz; +static jmethodID gProcessGpuMemCtor; +} // namespace + +namespace android { + +static jobject KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject, jint pid) { + std::vector buffers; + if (!dmabufinfo::ReadDmaBufMapRefs(pid, &buffers)) { + return nullptr; + } + jint mappedSize = 0; + jint mappedCount = buffers.size(); + for (const auto &buffer : buffers) { + mappedSize += buffer.size(); + } + mappedSize /= 1024; + + jint retainedSize = -1; + jint retainedCount = -1; + if (dmabufinfo::ReadDmaBufFdRefs(pid, &buffers)) { + retainedCount = buffers.size(); + retainedSize = 0; + for (const auto &buffer : buffers) { + retainedSize += buffer.size(); + } + retainedSize /= 1024; + } + return env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor, retainedSize, retainedCount, + mappedSize, mappedCount); +} + +static jobject KernelAllocationStats_getGpuAllocations(JNIEnv *env) { + std::unordered_map out; + meminfo::ReadPerProcessGpuMem(&out); + jobjectArray result = env->NewObjectArray(out.size(), gProcessGpuMemClazz, nullptr); + if (result == NULL) { + jniThrowRuntimeException(env, "Cannot create result array"); + return nullptr; + } + int idx = 0; + for (const auto &entry : out) { + jobject pidStats = + env->NewObject(gProcessGpuMemClazz, gProcessGpuMemCtor, entry.first, entry.second); + env->SetObjectArrayElement(result, idx, pidStats); + env->DeleteLocalRef(pidStats); + ++idx; + } + return result; +} + +static const JNINativeMethod methods[] = { + {"getDmabufAllocations", "(I)Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;", + (void *)KernelAllocationStats_getDmabufAllocations}, + {"getGpuAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessGpuMem;", + (void *)KernelAllocationStats_getGpuAllocations}, +}; + +int register_com_android_internal_os_KernelAllocationStats(JNIEnv *env) { + int res = RegisterMethodsOrDie(env, "com/android/internal/os/KernelAllocationStats", methods, + NELEM(methods)); + jclass clazz = + FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessDmabuf"); + gProcessDmabufClazz = MakeGlobalRefOrDie(env, clazz); + gProcessDmabufCtor = GetMethodIDOrDie(env, gProcessDmabufClazz, "", "(IIII)V"); + + clazz = FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessGpuMem"); + gProcessGpuMemClazz = MakeGlobalRefOrDie(env, clazz); + gProcessGpuMemCtor = GetMethodIDOrDie(env, gProcessGpuMemClazz, "", "(II)V"); + return res; +} + +} // namespace android diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 3182290509fc9..48b39f5afb9f1 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -166,6 +166,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.StatsEvent; import android.util.proto.ProtoOutputStream; import android.view.Display; @@ -177,7 +178,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BinderCallsStats.ExportedCallStat; -import com.android.internal.os.DmabufInfoReader; +import com.android.internal.os.KernelAllocationStats; import com.android.internal.os.KernelCpuBpfTracking; import com.android.internal.os.KernelCpuThreadReader; import com.android.internal.os.KernelCpuThreadReaderDiff; @@ -418,7 +419,6 @@ public class StatsPullAtomService extends SystemService { private final Object mSystemUptimeLock = new Object(); private final Object mProcessMemoryStateLock = new Object(); private final Object mProcessMemoryHighWaterMarkLock = new Object(); - private final Object mProcessMemorySnapshotLock = new Object(); private final Object mSystemIonHeapSizeLock = new Object(); private final Object mIonHeapSizeLock = new Object(); private final Object mProcessSystemIonHeapSizeLock = new Object(); @@ -556,9 +556,7 @@ public class StatsPullAtomService extends SystemService { return pullProcessMemoryHighWaterMarkLocked(atomTag, data); } case FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT: - synchronized (mProcessMemorySnapshotLock) { - return pullProcessMemorySnapshotLocked(atomTag, data); - } + return pullProcessMemorySnapshot(atomTag, data); case FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE: synchronized (mSystemIonHeapSizeLock) { return pullSystemIonHeapSizeLocked(atomTag, data); @@ -2211,10 +2209,16 @@ public class StatsPullAtomService extends SystemService { ); } - int pullProcessMemorySnapshotLocked(int atomTag, List pulledData) { + int pullProcessMemorySnapshot(int atomTag, List pulledData) { List managedProcessList = LocalServices.getService(ActivityManagerInternal.class) .getMemoryStateForProcesses(); + KernelAllocationStats.ProcessGpuMem[] gpuAllocations = + KernelAllocationStats.getGpuAllocations(); + SparseIntArray gpuMemPerPid = new SparseIntArray(gpuAllocations.length); + for (KernelAllocationStats.ProcessGpuMem processGpuMem : gpuAllocations) { + gpuMemPerPid.put(processGpuMem.pid, processGpuMem.gpuMemoryKb); + } for (ProcessMemoryState managedProcess : managedProcessList) { final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid); if (snapshot == null) { @@ -2223,7 +2227,8 @@ public class StatsPullAtomService extends SystemService { pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, managedProcess.uid, managedProcess.processName, managedProcess.pid, managedProcess.oomScore, snapshot.rssInKilobytes, snapshot.anonRssInKilobytes, snapshot.swapInKilobytes, - snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)); + snapshot.anonRssInKilobytes + snapshot.swapInKilobytes, + gpuMemPerPid.get(managedProcess.pid))); } // Complement the data with native system processes. Given these measurements can be taken // in response to LMKs happening, we want to first collect the managed app stats (to @@ -2241,7 +2246,8 @@ public class StatsPullAtomService extends SystemService { processCmdlines.valueAt(i), pid, -1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/, snapshot.rssInKilobytes, snapshot.anonRssInKilobytes, snapshot.swapInKilobytes, - snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)); + snapshot.anonRssInKilobytes + snapshot.swapInKilobytes, + gpuMemPerPid.get(pid))); } return StatsManager.PULL_SUCCESS; } @@ -2321,7 +2327,8 @@ public class StatsPullAtomService extends SystemService { if (process.uid == Process.SYSTEM_UID) { continue; } - DmabufInfoReader.ProcessDmabuf proc = DmabufInfoReader.getProcessStats(process.pid); + KernelAllocationStats.ProcessDmabuf proc = + KernelAllocationStats.getDmabufAllocations(process.pid); if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) { continue; }