From 3be5773ec9372def609246d1857bf1c0cc5520b1 Mon Sep 17 00:00:00 2001 From: Richard Gaywood Date: Tue, 15 Mar 2022 20:37:58 +0000 Subject: [PATCH] Implement puller for new SurfaceFlinger dmabuf stats. Involves pushing most of the logic down out of StatsPullAtomService and into KernelAllocationStats.cpp, as well as changing some method signatures along the way. At the end of this work, two new fields in atoms.proto will be populated; these will allow us to tell how much of SurfaceFlinger's dmabuf memory usage is due to buffers shared with other processes and how much is due to private internal buffers. Test: statsd_testdrive 10105 Test: adb shell dmabuf_dump Bug: 220084049 Change-Id: Ia41cfd693d3db2bd06083801075cae290f67221f --- .../internal/os/KernelAllocationStats.java | 26 +-- core/jni/Android.bp | 1 + ...roid_internal_os_KernelAllocationStats.cpp | 149 +++++++++++++++--- .../stats/pull/StatsPullAtomService.java | 62 +++----- 4 files changed, 163 insertions(+), 75 deletions(-) diff --git a/core/java/com/android/internal/os/KernelAllocationStats.java b/core/java/com/android/internal/os/KernelAllocationStats.java index 1c3f8b0bf0958..58d51e327ade9 100644 --- a/core/java/com/android/internal/os/KernelAllocationStats.java +++ b/core/java/com/android/internal/os/KernelAllocationStats.java @@ -24,21 +24,29 @@ public final class KernelAllocationStats { /** Process dma-buf stats. */ public static final class ProcessDmabuf { + public final int uid; + public final String processName; + public final int oomScore; + /** 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; + /** Size of buffers shared with Surface Flinger. */ + public final int surfaceFlingerSizeKb; + /** Count of buffers shared with Surface Flinger. */ + public final int surfaceFlingerCount; - ProcessDmabuf(int retainedSizeKb, int retainedBuffersCount, - int mappedSizeKb, int mappedBuffersCount) { + ProcessDmabuf(int uid, String processName, int oomScore, int retainedSizeKb, + int retainedBuffersCount, int surfaceFlingerSizeKb, + int surfaceFlingerCount) { + this.uid = uid; + this.processName = processName; + this.oomScore = oomScore; this.retainedSizeKb = retainedSizeKb; this.retainedBuffersCount = retainedBuffersCount; - this.mappedSizeKb = mappedSizeKb; - this.mappedBuffersCount = mappedBuffersCount; + this.surfaceFlingerSizeKb = surfaceFlingerSizeKb; + this.surfaceFlingerCount = surfaceFlingerCount; } } @@ -47,7 +55,7 @@ public final class KernelAllocationStats { * stats could not be read. */ @Nullable - public static native ProcessDmabuf getDmabufAllocations(int pid); + public static native ProcessDmabuf[] getDmabufAllocations(); /** Pid to gpu memory size. */ public static final class ProcessGpuMem { diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 3a76745cc4d93..a1be88440c97a 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -272,6 +272,7 @@ cc_library_shared { "libinput", "libcamera_client", "libcamera_metadata", + "libprocinfo", "libsqlite", "libEGL", "libGLESv1_CM", diff --git a/core/jni/com_android_internal_os_KernelAllocationStats.cpp b/core/jni/com_android_internal_os_KernelAllocationStats.cpp index e0a24430e739e..5b104977f1d8b 100644 --- a/core/jni/com_android_internal_os_KernelAllocationStats.cpp +++ b/core/jni/com_android_internal_os_KernelAllocationStats.cpp @@ -13,12 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include +#include #include #include #include +#include #include "core_jni_helpers.h" +using DmaBuffer = ::android::dmabufinfo::DmaBuffer; +using android::base::ReadFileToString; +using android::base::StringPrintf; + namespace { static jclass gProcessDmabufClazz; static jmethodID gProcessDmabufCtor; @@ -28,30 +35,127 @@ static jmethodID gProcessGpuMemCtor; namespace android { -static jobject KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject, jint pid) { - std::vector buffers; - if (!dmabufinfo::ReadDmaBufMapRefs(pid, &buffers)) { +struct PidDmaInfo { + uid_t uid; + std::string cmdline; + int oomScoreAdj; +}; + +static jobjectArray KernelAllocationStats_getDmabufAllocations(JNIEnv *env, jobject) { + std::vector buffers; + + if (!dmabufinfo::ReadDmaBufs(&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(); + // Create a reverse map from pid to dmabufs + // Store dmabuf inodes & sizes for later processing. + std::unordered_map> pidToInodes; + std::unordered_map inodeToSize; + for (auto &buf : buffers) { + for (auto pid : buf.pids()) { + pidToInodes[pid].insert(buf.inode()); } - retainedSize /= 1024; + inodeToSize[buf.inode()] = buf.size(); } - return env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor, retainedSize, retainedCount, - mappedSize, mappedCount); + + pid_t surfaceFlingerPid = -1; + // The set of all inodes that are being retained by SurfaceFlinger. Buffers + // shared between another process and SF will appear in this set. + std::set surfaceFlingerBufferInodes; + // The set of all inodes that are being retained by any process other + // than SurfaceFlinger. Buffers shared between another process and SF will + // appear in this set. + std::set otherProcessBufferInodes; + + // Find SurfaceFlinger pid & get cmdlines, oomScoreAdj, etc for each pid + // holding any DMA buffers. + std::unordered_map pidDmaInfos; + for (const auto &pidToInodeEntry : pidToInodes) { + pid_t pid = pidToInodeEntry.first; + + android::procinfo::ProcessInfo processInfo; + if (!android::procinfo::GetProcessInfo(pid, &processInfo)) { + continue; + } + + std::string cmdline; + if (!ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline)) { + continue; + } + + // cmdline strings are null-delimited, so we split on \0 here + if (cmdline.substr(0, cmdline.find('\0')) == "/system/bin/surfaceflinger") { + if (surfaceFlingerPid == -1) { + surfaceFlingerPid = pid; + surfaceFlingerBufferInodes = pidToInodes[pid]; + } else { + LOG(ERROR) << "getDmabufAllocations found multiple SF processes; pid1: " << pid + << ", pid2:" << surfaceFlingerPid; + surfaceFlingerPid = -2; // Used as a sentinel value below + } + } else { + otherProcessBufferInodes.insert(pidToInodes[pid].begin(), pidToInodes[pid].end()); + } + + std::string oomScoreAdjStr; + if (!ReadFileToString(StringPrintf("/proc/%d/oom_score_adj", pid), &oomScoreAdjStr)) { + continue; + } + + pidDmaInfos[pid] = PidDmaInfo{.uid = processInfo.uid, + .cmdline = cmdline, + .oomScoreAdj = atoi(oomScoreAdjStr.c_str())}; + } + + if (surfaceFlingerPid < 0) { + LOG(ERROR) << "getDmabufAllocations could not identify SurfaceFlinger " + << "process via /proc/pid/cmdline"; + } + + jobjectArray ret = env->NewObjectArray(pidDmaInfos.size(), gProcessDmabufClazz, NULL); + int retArrayIndex = 0; + for (const auto &pidDmaInfosEntry : pidDmaInfos) { + pid_t pid = pidDmaInfosEntry.first; + + // For all processes apart from SurfaceFlinger, this set will store the + // dmabuf inodes that are shared with SF. For SF, it will store the inodes + // that are shared with any other process. + std::set sharedBuffers; + if (pid == surfaceFlingerPid) { + set_intersection(surfaceFlingerBufferInodes.begin(), surfaceFlingerBufferInodes.end(), + otherProcessBufferInodes.begin(), otherProcessBufferInodes.end(), + std::inserter(sharedBuffers, sharedBuffers.end())); + } else if (surfaceFlingerPid > 0) { + set_intersection(pidToInodes[pid].begin(), pidToInodes[pid].end(), + surfaceFlingerBufferInodes.begin(), surfaceFlingerBufferInodes.end(), + std::inserter(sharedBuffers, sharedBuffers.begin())); + } // If surfaceFlingerPid < 0; it means we failed to identify it, and + // the SF-related fields below should be left empty. + + long totalSize = 0; + long sharedBuffersSize = 0; + for (const auto &inode : pidToInodes[pid]) { + totalSize += inodeToSize[inode]; + if (sharedBuffers.count(inode)) { + sharedBuffersSize += inodeToSize[inode]; + } + } + + jobject obj = env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor, + /* uid */ pidDmaInfos[pid].uid, + /* process name */ + env->NewStringUTF(pidDmaInfos[pid].cmdline.c_str()), + /* oomscore */ pidDmaInfos[pid].oomScoreAdj, + /* retainedSize */ totalSize / 1024, + /* retainedCount */ pidToInodes[pid].size(), + /* sharedWithSurfaceFlinger size */ sharedBuffersSize / 1024, + /* sharedWithSurfaceFlinger count */ sharedBuffers.size()); + + env->SetObjectArrayElement(ret, retArrayIndex++, obj); + } + + return ret; } static jobject KernelAllocationStats_getGpuAllocations(JNIEnv *env) { @@ -74,7 +178,7 @@ static jobject KernelAllocationStats_getGpuAllocations(JNIEnv *env) { } static const JNINativeMethod methods[] = { - {"getDmabufAllocations", "(I)Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;", + {"getDmabufAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessDmabuf;", (void *)KernelAllocationStats_getDmabufAllocations}, {"getGpuAllocations", "()[Lcom/android/internal/os/KernelAllocationStats$ProcessGpuMem;", (void *)KernelAllocationStats_getGpuAllocations}, @@ -86,7 +190,8 @@ int register_com_android_internal_os_KernelAllocationStats(JNIEnv *env) { jclass clazz = FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessDmabuf"); gProcessDmabufClazz = MakeGlobalRefOrDie(env, clazz); - gProcessDmabufCtor = GetMethodIDOrDie(env, gProcessDmabufClazz, "", "(IIII)V"); + gProcessDmabufCtor = + GetMethodIDOrDie(env, gProcessDmabufClazz, "", "(ILjava/lang/String;IIIII)V"); clazz = FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessGpuMem"); gProcessGpuMemClazz = MakeGlobalRefOrDie(env, clazz); @@ -94,4 +199,4 @@ int register_com_android_internal_os_KernelAllocationStats(JNIEnv *env) { return res; } -} // namespace android +} // namespace android \ No newline at end of file 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 2e60f130bc4b4..52088a59b8613 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2338,51 +2338,25 @@ public class StatsPullAtomService extends SystemService { } int pullProcessDmabufMemory(int atomTag, List pulledData) { - List managedProcessList = - LocalServices.getService(ActivityManagerInternal.class) - .getMemoryStateForProcesses(); - for (ProcessMemoryState process : managedProcessList) { - KernelAllocationStats.ProcessDmabuf proc = - KernelAllocationStats.getDmabufAllocations(process.pid); - if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) { - continue; - } - pulledData.add( - FrameworkStatsLog.buildStatsEvent( - atomTag, - process.uid, - process.processName, - process.oomScore, - proc.retainedSizeKb, - proc.retainedBuffersCount, - proc.mappedSizeKb, - proc.mappedBuffersCount)); + KernelAllocationStats.ProcessDmabuf[] procBufs = + KernelAllocationStats.getDmabufAllocations(); + + if (procBufs == null) { + return StatsManager.PULL_SKIP; } - SparseArray processCmdlines = getProcessCmdlines(); - managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid)); - int size = processCmdlines.size(); - for (int i = 0; i < size; ++i) { - int pid = processCmdlines.keyAt(i); - int uid = getUidForPid(pid); - // ignore root processes (unlikely to be interesting) - if (uid <= 0) { - continue; - } - KernelAllocationStats.ProcessDmabuf proc = - KernelAllocationStats.getDmabufAllocations(pid); - if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) { - continue; - } - pulledData.add( - FrameworkStatsLog.buildStatsEvent( - atomTag, - uid, - processCmdlines.valueAt(i), - -1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/, - proc.retainedSizeKb, - proc.retainedBuffersCount, - proc.mappedSizeKb, - proc.mappedBuffersCount)); + for (KernelAllocationStats.ProcessDmabuf procBuf : procBufs) { + pulledData.add(FrameworkStatsLog.buildStatsEvent( + atomTag, + procBuf.uid, + procBuf.processName, + procBuf.oomScore, + procBuf.retainedSizeKb, + procBuf.retainedBuffersCount, + 0, /* mapped_dmabuf_kb - deprecated */ + 0, /* mapped_dmabuf_count - deprecated */ + procBuf.surfaceFlingerSizeKb, + procBuf.surfaceFlingerCount + )); } return StatsManager.PULL_SUCCESS; }