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; }