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

Commit 1b71342c authored by “Shadman's avatar “Shadman Committed by Shadman Shadid
Browse files

Add puller for memcg

Example atom pull: https://paste.googleplex.com/5314847696486400

Flag: com.android.server.stats.add_memcg_information_puller
Test: Manual
Bug: 434920925
Change-Id: I69e421df8c72f82f500eb98ff40eeb6d1d2e9e74
parent 5c134240
Loading
Loading
Loading
Loading
+118 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 static android.os.Process.PROC_OUT_LONG;

import android.annotation.Nullable;
import android.os.Process;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public final class MemcgProcMemoryUtil {

    private static final String TAG = "MemcgProcMemoryUtil";

    private static final int[] MEMCG_MEMORY_FORMAT = new int[] {PROC_OUT_LONG};
    private static final String CGROUP_ROOT = "/sys/fs/cgroup";
    private static final String PROC_ROOT = "/proc/";

    private MemcgProcMemoryUtil() {}

     /**
      * Reads memcg accounting of memory stats of a process.
      *
      * Returns values of memory.current, memory.swap in bytes or -1 if not available.
      */
    public static MemcgMemorySnapshot readMemcgMemorySnapshot(int pid) {
        String cgroupPath = getCgroupPathForPid(pid);
        if (cgroupPath == null) {
            return null;
        }
        return readMemcgMemorySnapshot(cgroupPath);
    }

    /**
     * Gets the cgroup v2 path for a given process ID (pid).
     *
     * @param pid The process ID.
     * @return The cgroup v2 path string, or null if not found or an error occurs.
     */
    @Nullable
    private static String getCgroupPathForPid(int pid) {
        Path cgroupPathFile = Paths.get(PROC_ROOT, String.valueOf(pid), "cgroup");

        try (BufferedReader reader = Files.newBufferedReader(cgroupPathFile)) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("0::")) {
                    return line.substring(3);
                }
            }
        } catch (IOException e) {
            Log.d(TAG, "Failed to read cgroup file for pid " + pid, e);
        }
        return null;
    }

    private static MemcgMemorySnapshot readMemcgMemorySnapshot(String cgroupPath) {
        Path fullMemcgPath = Paths.get(CGROUP_ROOT, cgroupPath);

        final MemcgMemorySnapshot snapshot = new MemcgMemorySnapshot();

        long[] currentMemoryOutput = new long[1];
        String memoryCurrentPath = fullMemcgPath.resolve("memory.current").toString();
        if (Process.readProcFile(
                memoryCurrentPath,
                MEMCG_MEMORY_FORMAT,
                null,
                currentMemoryOutput,
                null
        )) {
            snapshot.memcgMemoryInBytes = currentMemoryOutput[0];
        } else {
            Log.d(TAG, "Failed to read memory.current for " + cgroupPath);
            return null;
        }

        long[] currentSwapMemoryOutput = new long[1];
        String memorySwapPath =
                fullMemcgPath.resolve("memory.swap.current").toString();
        if (Process.readProcFile(
                memorySwapPath,
                MEMCG_MEMORY_FORMAT,
                null,
                currentSwapMemoryOutput,
                null
        )) {
            snapshot.memcgSwapMemoryInBytes = currentSwapMemoryOutput[0];
        } else {
            Log.d(TAG, "Failed to read memory.current for " + cgroupPath);
            return null;
        }
        return snapshot;
    }

    public static final class MemcgMemorySnapshot {
        public long memcgMemoryInBytes;
        public long memcgSwapMemoryInBytes;
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -19,3 +19,6 @@ per-file TimeoutRecord.java = file:/PERFORMANCE_OWNERS

# ApplicationSharedMemory
per-file *ApplicationSharedMemory* = file:/PERFORMANCE_OWNERS

# Memcg memory accounting
per-file MemcgProcMemoryUtil.java = file:/PERFORMANCE_OWNERS
+44 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
import static android.util.MathUtils.constrain;
import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;

import static com.android.internal.os.MemcgProcMemoryUtil.readMemcgMemorySnapshot;
import static com.android.internal.os.ProcfsMemoryUtil.DmaBufType;
import static com.android.internal.os.ProcfsMemoryUtil.getProcessCmdlines;
import static com.android.internal.os.ProcfsMemoryUtil.readCmdlineFromProcfs;
@@ -76,6 +77,7 @@ import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STA
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__TELEPHONY;
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN;
import static com.android.server.stats.Flags.addMobileBytesTransferByProcStatePuller;
import static com.android.server.stats.Flags.addMemcgInformationPuller;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
import static com.android.server.stats.pull.netstats.NetworkStatsUtils.fromPublicNetworkStats;
@@ -213,6 +215,7 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeRea
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.os.LooperStats;
import com.android.internal.os.MemcgProcMemoryUtil.MemcgMemorySnapshot;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.ProcfsMemoryUtil;
@@ -468,6 +471,10 @@ public class StatsPullAtomService extends SystemService {
     */
    public static final boolean ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER =
                addMobileBytesTransferByProcStatePuller();

    public static final boolean ENABLE_MEMCG_STATS_PULLER =
                addMemcgInformationPuller();

    private static final ArrayMap<String, Integer> mPreviousThermalThrottlingStatus =
            new ArrayMap<>();
    // Puller locks
@@ -853,6 +860,8 @@ public class StatsPullAtomService extends SystemService {
                        return pullCachedAppsHighWatermark(atomTag, data);
                    case FrameworkStatsLog.PRESSURE_STALL_INFORMATION:
                        return pullPressureStallInformation(atomTag, data);
                    case FrameworkStatsLog.MEMCG_MEMORY_SNAPSHOT:
                        return pullMemcgProcessMemoryInformation(atomTag, data);
                    default:
                        throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
                }
@@ -1053,6 +1062,9 @@ public class StatsPullAtomService extends SystemService {
        registerCachedAppsHighWatermarkPuller();
        registerPressureStallInformation();
        registerBatteryLife();
        if (ENABLE_MEMCG_STATS_PULLER) {
            registerMemcgInformation();
        }
    }

    private void initMobileDataStatsPuller() {
@@ -5234,6 +5246,38 @@ public class StatsPullAtomService extends SystemService {
        return StatsManager.PULL_SUCCESS;
    }

    private void registerMemcgInformation() {
        int tagId = FrameworkStatsLog.MEMCG_MEMORY_SNAPSHOT;
        mStatsManager.setPullAtomCallback(
                tagId,
                new PullAtomMetadata.Builder()
                        .setCoolDownMillis(MILLIS_PER_SEC)
                        .build(),
                DIRECT_EXECUTOR,
                mStatsCallbackImpl
        );
    }

    int pullMemcgProcessMemoryInformation(int atomTag, List<StatsEvent> pulledData) {
        List<ProcessMemoryState> managedProcessList =
                LocalServices.getService(ActivityManagerInternal.class)
                        .getMemoryStateForProcesses();
        for (ProcessMemoryState managedProcess : managedProcessList) {
            final MemcgMemorySnapshot snapshot = readMemcgMemorySnapshot(managedProcess.pid);
            if (snapshot == null) {
                continue;
            }
            pulledData.add(FrameworkStatsLog.buildStatsEvent(
                    atomTag,
                    managedProcess.uid,
                    managedProcess.processName,
                    snapshot.memcgMemoryInBytes / 1024,
                    snapshot.memcgSwapMemoryInBytes / 1024
            ));
        }
        return StatsManager.PULL_SUCCESS;
    }

    private int toProtoPsiResourceType(PsiData.ResourceType resourceType) {
        if (resourceType == PsiData.ResourceType.CPU) {
            return PRESSURE_STALL_INFORMATION__PSI_RESOURCE__PSI_RESOURCE_CPU;
+7 −0
Original line number Diff line number Diff line
@@ -41,3 +41,10 @@ flag {
    bug: "398327159"
    is_fixed_read_only: true
}

flag {
    name: "add_memcg_information_puller"
    namespace: "statsd"
    description: "Adds memcgv2 atom logging"
    bug: "434920925"
}
 No newline at end of file