Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3be5773e authored by Richard Gaywood's avatar Richard Gaywood
Browse files

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
parent 379a8705
Loading
Loading
Loading
Loading
+17 −9
Original line number Diff line number Diff line
@@ -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 {
+1 −0
Original line number Diff line number Diff line
@@ -272,6 +272,7 @@ cc_library_shared {
                "libinput",
                "libcamera_client",
                "libcamera_metadata",
                "libprocinfo",
                "libsqlite",
                "libEGL",
                "libGLESv1_CM",
+127 −22
Original line number Diff line number Diff line
@@ -13,12 +13,19 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <dmabufinfo/dmabufinfo.h>
#include <jni.h>
#include <meminfo/sysmeminfo.h>
#include <procinfo/process.h>

#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<dmabufinfo::DmaBuffer> 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<DmaBuffer> buffers;

    if (!dmabufinfo::ReadDmaBufs(&buffers)) {
        return nullptr;
    }
    jint mappedSize = 0;
    jint mappedCount = buffers.size();
    for (const auto &buffer : buffers) {
        mappedSize += buffer.size();

    // Create a reverse map from pid to dmabufs
    // Store dmabuf inodes & sizes for later processing.
    std::unordered_map<pid_t, std::set<ino_t>> pidToInodes;
    std::unordered_map<ino_t, long> inodeToSize;
    for (auto &buf : buffers) {
        for (auto pid : buf.pids()) {
            pidToInodes[pid].insert(buf.inode());
        }
        inodeToSize[buf.inode()] = buf.size();
    }

    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<ino_t> 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<ino_t> otherProcessBufferInodes;

    // Find SurfaceFlinger pid & get cmdlines, oomScoreAdj, etc for each pid
    // holding any DMA buffers.
    std::unordered_map<pid_t, PidDmaInfo> pidDmaInfos;
    for (const auto &pidToInodeEntry : pidToInodes) {
        pid_t pid = pidToInodeEntry.first;

        android::procinfo::ProcessInfo processInfo;
        if (!android::procinfo::GetProcessInfo(pid, &processInfo)) {
            continue;
        }
    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();
        std::string cmdline;
        if (!ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline)) {
            continue;
        }
        retainedSize /= 1024;

        // 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;
        }
    return env->NewObject(gProcessDmabufClazz, gProcessDmabufCtor, retainedSize, retainedCount,
                          mappedSize, mappedCount);

        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<ino_t> 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, "<init>", "(IIII)V");
    gProcessDmabufCtor =
            GetMethodIDOrDie(env, gProcessDmabufClazz, "<init>", "(ILjava/lang/String;IIIII)V");

    clazz = FindClassOrDie(env, "com/android/internal/os/KernelAllocationStats$ProcessGpuMem");
    gProcessGpuMemClazz = MakeGlobalRefOrDie(env, clazz);
+18 −44
Original line number Diff line number Diff line
@@ -2338,51 +2338,25 @@ public class StatsPullAtomService extends SystemService {
    }

    int pullProcessDmabufMemory(int atomTag, List<StatsEvent> pulledData) {
        List<ProcessMemoryState> 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));
        }
        SparseArray<String> 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;
        KernelAllocationStats.ProcessDmabuf[] procBufs =
                KernelAllocationStats.getDmabufAllocations();

        if (procBufs == null) {
            return StatsManager.PULL_SKIP;
        }
            pulledData.add(
                    FrameworkStatsLog.buildStatsEvent(
        for (KernelAllocationStats.ProcessDmabuf procBuf : procBufs) {
            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));
                    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;
    }