From cc793ab6084411404dc290e2a897f0fed2d429bd Mon Sep 17 00:00:00 2001 From: Ioannis Ilkos Date: Sun, 28 Nov 2021 22:10:16 +0000 Subject: 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 --- .../com/android/internal/os/DmabufInfoReader.java | 51 ------------ .../android/internal/os/KernelAllocationStats.java | 65 +++++++++++++++ core/jni/Android.bp | 2 +- core/jni/AndroidRuntime.cpp | 4 +- .../com_android_internal_os_DmabufInfoReader.cpp | 60 ------------- ...m_android_internal_os_KernelAllocationStats.cpp | 97 ++++++++++++++++++++++ .../server/stats/pull/StatsPullAtomService.java | 25 ++++-- 7 files changed, 181 insertions(+), 123 deletions(-) delete mode 100644 core/java/com/android/internal/os/DmabufInfoReader.java create mode 100644 core/java/com/android/internal/os/KernelAllocationStats.java 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/DmabufInfoReader.java deleted file mode 100644 index 786a6eedf343..000000000000 --- a/core/java/com/android/internal/os/DmabufInfoReader.java +++ /dev/null @@ -1,51 +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. - */ - -package com.android.internal.os; - -import android.annotation.Nullable; - -/** Wrapper around libdmabufinfo. */ -public final class DmabufInfoReader { - private DmabufInfoReader() {} - - /** Process dma-buf stats. */ - public static final class ProcessDmabuf { - /** Size of buffers retained by the process. */ - public final int retainedSizeKb; - /** Number of buffers retained by the process. */ - public final int retainedBuffersCount; - /** Size of buffers mapped to the address space. */ - public final int mappedSizeKb; - /** Count of buffers mapped to the address space. */ - public final int mappedBuffersCount; - - ProcessDmabuf(int retainedSizeKb, int retainedBuffersCount, - int mappedSizeKb, int mappedBuffersCount) { - this.retainedSizeKb = retainedSizeKb; - this.retainedBuffersCount = retainedBuffersCount; - this.mappedSizeKb = mappedSizeKb; - this.mappedBuffersCount = mappedBuffersCount; - } - } - - /** - * Return stats for DMA-BUFs retained by process pid or null if the DMA-BUF - * stats could not be read. - */ - @Nullable - public static native ProcessDmabuf getProcessStats(int pid); -} diff --git a/core/java/com/android/internal/os/KernelAllocationStats.java b/core/java/com/android/internal/os/KernelAllocationStats.java new file mode 100644 index 000000000000..1c3f8b0bf095 --- /dev/null +++ b/core/java/com/android/internal/os/KernelAllocationStats.java @@ -0,0 +1,65 @@ +/* + * 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. + */ + +package com.android.internal.os; + +import android.annotation.Nullable; + +/** 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 { + /** Size of buffers retained by the process. */ + public final int retainedSizeKb; + /** Number of buffers retained by the process. */ + public final int retainedBuffersCount; + /** Size of buffers mapped to the address space. */ + public final int mappedSizeKb; + /** Count of buffers mapped to the address space. */ + public final int mappedBuffersCount; + + ProcessDmabuf(int retainedSizeKb, int retainedBuffersCount, + int mappedSizeKb, int mappedBuffersCount) { + this.retainedSizeKb = retainedSizeKb; + this.retainedBuffersCount = retainedBuffersCount; + this.mappedSizeKb = mappedSizeKb; + this.mappedBuffersCount = mappedBuffersCount; + } + } + + /** + * Return stats for DMA-BUFs retained by process pid or null if the DMA-BUF + * stats could not be read. + */ + @Nullable + 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 1a1a8badf82b..da628635af36 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 18e85b6eb0be..04fafb441a28 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 4b0a6ac5b6c4..000000000000 --- 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 000000000000..e0a24430e739 --- /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 3182290509fc..48b39f5afb9f 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; } -- cgit v1.2.3-59-g8ed1b