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

Commit f1a8ce65 authored by Edgar Arriaga's avatar Edgar Arriaga
Browse files

Add compaction memory diagnostics

This CL adds multiple memory diagnostics to app compation system
which allows to evaluate them per source and per app for evaluating
optimizations, bugfixes, throttles, etc.

In addition, an option is added to perform the system processes compaction
via am compact system command for testing.

Bug: 233415647
Test: dumpsys activity
Test: am compact system
Test: am compact full <app> <uid>
Test: atest CachedAppOptimizerTest

Change-Id: Ic5ccab33580473a3eb6ea056a818d94e53521623
parent 78c33bb4
Loading
Loading
Loading
Loading
+27 −16
Original line number Diff line number Diff line
@@ -989,26 +989,36 @@ final class ActivityManagerShellCommand extends ShellCommand {

    @NeverCompile
    int runCompact(PrintWriter pw) {
        ProcessRecord app;
        String op = getNextArgRequired();
        boolean isFullCompact = op.equals("full");
        boolean isSomeCompact = op.equals("some");
        if (isFullCompact || isSomeCompact) {
            String processName = getNextArgRequired();
            String uid = getNextArgRequired();
        String op = getNextArgRequired();
        ProcessRecord app;
            synchronized (mInternal.mProcLock) {
                app = mInternal.getProcessRecordLocked(processName, Integer.parseInt(uid));
            }
            pw.println("Process record found pid: " + app.mPid);
        if (op.equals("full")) {
            if (isFullCompact) {
                pw.println("Executing full compaction for " + app.mPid);
                synchronized (mInternal.mProcLock) {
                    mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppFull(app, true);
                }
                pw.println("Finished full compaction for " + app.mPid);
        } else if (op.equals("some")) {
            } else if (isSomeCompact) {
                pw.println("Executing some compaction for " + app.mPid);
                synchronized (mInternal.mProcLock) {
                    mInternal.mOomAdjuster.mCachedAppOptimizer.compactAppSome(app, true);
                }
                pw.println("Finished some compaction for " + app.mPid);
            }
        } else if (op.equals("system")) {
            pw.println("Executing system compaction");
            synchronized (mInternal.mProcLock) {
                mInternal.mOomAdjuster.mCachedAppOptimizer.compactAllSystem();
            }
            pw.println("Finished system compaction");
        } else {
            getErrPrintWriter().println("Error: unknown compact command '" + op + "'");
            return -1;
@@ -3570,10 +3580,11 @@ final class ActivityManagerShellCommand extends ShellCommand {
            pw.println("      --allow-background-activity-starts: The receiver may start activities");
            pw.println("          even if in the background.");
            pw.println("      --async: Send without waiting for the completion of the receiver.");
            pw.println("  compact <process_name> <Package UID> [some|full]");
            pw.println("  compact [some|full|system] <process_name> <Package UID>");
            pw.println("      Force process compaction.");
            pw.println("      some: execute file compaction.");
            pw.println("      full: execute anon + file compaction.");
            pw.println("      system: system compaction.");
            pw.println("  instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
            pw.println("          [--user <USER_ID> | current]");
            pw.println("          [--no-hidden-api-checks [--no-test-api-access]]");
+472 −253

File changed.

Preview size limit exceeded, changes collapsed.

+33 −14
Original line number Diff line number Diff line
@@ -39,16 +39,19 @@ final class ProcessCachedOptimizerRecord {
    private long mLastCompactTime;

    /**
     * The most recent compaction action requested for this app.
     * The most recent compaction profile requested for this app.
     */
    @GuardedBy("mProcLock")
    private int mReqCompactAction;
    @GuardedBy("mProcLock") private CachedAppOptimizer.CompactProfile mReqCompactProfile;

    /**
     * Source that requested the latest compaction for this app.
     */
    @GuardedBy("mProcLock") private CachedAppOptimizer.CompactSource mReqCompactSource;

    /**
     * The most recent compaction action performed for this app.
     */
    @GuardedBy("mProcLock")
    private int mLastCompactAction;
    @GuardedBy("mProcLock") private CachedAppOptimizer.CompactProfile mLastCompactProfile;

    /**
     * This process has been scheduled for a memory compaction.
@@ -105,23 +108,38 @@ final class ProcessCachedOptimizerRecord {
    }

    @GuardedBy("mProcLock")
    int getReqCompactAction() {
        return mReqCompactAction;
    CachedAppOptimizer.CompactProfile getReqCompactProfile() {
        return mReqCompactProfile;
    }

    @GuardedBy("mProcLock")
    void setReqCompactAction(int reqCompactAction) {
        mReqCompactAction = reqCompactAction;
    void setReqCompactProfile(CachedAppOptimizer.CompactProfile reqCompactProfile) {
        mReqCompactProfile = reqCompactProfile;
    }

    @GuardedBy("mProcLock")
    int getLastCompactAction() {
        return mLastCompactAction;
    CachedAppOptimizer.CompactSource getReqCompactSource() {
        return mReqCompactSource;
    }

    @GuardedBy("mProcLock")
    void setReqCompactSource(CachedAppOptimizer.CompactSource stat) {
        mReqCompactSource = stat;
    }

    @GuardedBy("mProcLock")
    CachedAppOptimizer.CompactProfile getLastCompactProfile() {
        if (mLastCompactProfile == null) {
            // The first compaction won't have a previous one, so assign one to avoid crashing.
            mLastCompactProfile = CachedAppOptimizer.CompactProfile.SOME;
        }

        return mLastCompactProfile;
    }

    @GuardedBy("mProcLock")
    void setLastCompactAction(int lastCompactAction) {
        mLastCompactAction = lastCompactAction;
    void setLastCompactProfile(CachedAppOptimizer.CompactProfile lastCompactProfile) {
        mLastCompactProfile = lastCompactProfile;
    }

    @GuardedBy("mProcLock")
@@ -216,7 +234,8 @@ final class ProcessCachedOptimizerRecord {
    @GuardedBy("mProcLock")
    void dump(PrintWriter pw, String prefix, long nowUptime) {
        pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
        pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
        pw.print(" lastCompactProfile=");
        pw.println(mLastCompactProfile);
        pw.print(prefix);
        pw.print("hasPendingCompaction=");
        pw.print(mPendingCompact);
+15 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include <linux/errno.h>
#include <log/log.h>
#include <meminfo/procmeminfo.h>
#include <meminfo/sysmeminfo.h>
#include <nativehelper/JNIHelp.h>
#include <processgroup/processgroup.h>
#include <stddef.h>
@@ -467,6 +468,16 @@ static jdouble com_android_server_am_CachedAppOptimizer_getFreeSwapPercent(JNIEn
    return (double)memoryInfo.freeswap / (double)memoryInfo.totalswap;
}

static jlong com_android_server_am_CachedAppOptimizer_getUsedZramMemory() {
    android::meminfo::SysMemInfo sysmeminfo;
    return sysmeminfo.mem_zram_kb();
}

static jlong com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction() {
    android::meminfo::SysMemInfo sysmeminfo;
    return sysmeminfo.mem_compacted_kb("/sys/block/zram0/");
}

static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
                                                                    jint compactionFlags) {
    compactProcessOrFallback(pid, compactionFlags);
@@ -522,6 +533,10 @@ static const JNINativeMethod sMethods[] = {
         (void*)com_android_server_am_CachedAppOptimizer_cancelCompaction},
        {"getFreeSwapPercent", "()D",
         (void*)com_android_server_am_CachedAppOptimizer_getFreeSwapPercent},
        {"getUsedZramMemory", "()J",
         (void*)com_android_server_am_CachedAppOptimizer_getUsedZramMemory},
        {"getMemoryFreedCompaction", "()J",
         (void*)com_android_server_am_CachedAppOptimizer_getMemoryFreedCompaction},
        {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
        {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
        {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
+80 −78
Original line number Diff line number Diff line
@@ -17,12 +17,9 @@
package com.android.server.am;

import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;

import static com.android.server.am.ActivityManagerService.Injector;
import static com.android.server.am.CachedAppOptimizer.compactActionIntToString;

import static com.android.server.am.CachedAppOptimizer.compactActionIntToAction;
import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;

@@ -37,15 +34,18 @@ import android.os.Process;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.text.TextUtils;

import androidx.test.platform.app.InstrumentationRegistry;

import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
@@ -55,13 +55,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Tests for {@link CachedAppOptimizer}.
 *
@@ -147,12 +140,15 @@ public final class CachedAppOptimizerTest {
    @Test
    public void init_setsDefaults() {
        mCachedAppOptimizerUnderTest.init();
        synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
            assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo(
                    CachedAppOptimizer.DEFAULT_USE_COMPACTION);
        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
                    .isEqualTo(
                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
                    .isEqualTo(
                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1);
            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeFull).isEqualTo(
@@ -177,6 +173,7 @@ public final class CachedAppOptimizerTest {
                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
            assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
                    CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ);
        }


        Set<Integer> expected = new HashSet<>();
@@ -192,6 +189,7 @@ public final class CachedAppOptimizerTest {
                CachedAppOptimizer.DEFAULT_USE_FREEZER);
    }

    @SuppressWarnings("GuardedBy")
    @Test
    public void init_withDeviceConfigSetsParameters() {
        // When the DeviceConfig already has a flag value stored (note this test will need to
@@ -256,11 +254,11 @@ public final class CachedAppOptimizerTest {
        assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isTrue();
        assertThat(mCachedAppOptimizerUnderTest.mCachedAppOptimizerThread.isAlive()).isTrue();

        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
                compactActionIntToString(
        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
                .isEqualTo(compactActionIntToAction(
                        (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1));
        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
                compactActionIntToString(
        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
                .isEqualTo(compactActionIntToAction(
                        (CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1));
        assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleSomeSome).isEqualTo(
                CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_1 + 1);
@@ -412,10 +410,12 @@ public final class CachedAppOptimizerTest {
            assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();

            // Then the updates are reflected in the flags.
            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
                    compactActionIntToString(expectedSome));
            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
                    compactActionIntToString(expectedFull));
            synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
                assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
                        .isEqualTo(compactActionIntToAction(expectedSome));
                assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
                        .isEqualTo(compactActionIntToAction(expectedFull));
            }
        }
    }

@@ -431,11 +431,15 @@ public final class CachedAppOptimizerTest {
                CachedAppOptimizer.KEY_COMPACT_ACTION_2, "foo", false);
        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();

        synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
            // Then the default values are reflected in the flag
        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
                    .isEqualTo(
                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
                    .isEqualTo(
                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
        }

        mCountDown = new CountDownLatch(2);
        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -444,10 +448,14 @@ public final class CachedAppOptimizerTest {
                CachedAppOptimizer.KEY_COMPACT_ACTION_2, "", false);
        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();

        assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome).isEqualTo(
                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
        assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull).isEqualTo(
                compactActionIntToString(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
        synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) {
            assertThat(mCachedAppOptimizerUnderTest.mCompactActionSome)
                    .isEqualTo(
                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_1));
            assertThat(mCachedAppOptimizerUnderTest.mCompactActionFull)
                    .isEqualTo(
                            compactActionIntToAction(CachedAppOptimizer.DEFAULT_COMPACT_ACTION_2));
        }
    }

    @Test
@@ -1008,22 +1016,16 @@ public final class CachedAppOptimizerTest {
        mProcessDependencies.setRss(rssBefore);
        mProcessDependencies.setRssAfterCompaction(rssAfter);

        // Compaction should occur if (setAdj < min for process || setAdj > max for process) &&
        // (MIN < curAdj <  MAX)
        // GIVEN OomAdj score below threshold.
        processRecord.mState.setSetAdj(899);
        processRecord.mState.setCurAdj(970);
        // WHEN we try to run compaction
        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
        // When moving within cached state
        mCachedAppOptimizerUnderTest.onOomAdjustChanged(
                ProcessList.CACHED_APP_MIN_ADJ, ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
        waitForHandler();
        // THEN process IS NOT compacted.
        assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();

        // GIVEN (setAdj < MIN || setAdj > MAX) && (MIN < curAdj <  MAX)
        processRecord.mState.setSetAdj(910);
        processRecord.mState.setCurAdj(930);
        // WHEN we try to run compaction
        mCachedAppOptimizerUnderTest.compactAppFull(processRecord, false);
        // When moving into cached state
        mCachedAppOptimizerUnderTest.onOomAdjustChanged(ProcessList.CACHED_APP_MIN_ADJ - 1,
                ProcessList.CACHED_APP_MIN_ADJ + 1, processRecord);
        waitForHandler();
        // THEN process IS compacted.
        assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
@@ -1083,10 +1085,9 @@ public final class CachedAppOptimizerTest {
        mCachedAppOptimizerUnderTest.compactAppSome(processRecord, true);
        waitForHandler();
        // then process is compacted.
        String executedCompactAction =
                compactActionIntToString(processRecord.mOptRecord.getLastCompactAction());
        assertThat(executedCompactAction)
                .isEqualTo(mCachedAppOptimizerUnderTest.mCompactActionSome);
        CachedAppOptimizer.CompactProfile executedCompactProfile =
                processRecord.mOptRecord.getLastCompactProfile();
        assertThat(executedCompactProfile).isEqualTo(CachedAppOptimizer.CompactProfile.SOME);
    }

    private void setFlag(String key, String value, boolean defaultValue) throws Exception {
@@ -1162,7 +1163,8 @@ public final class CachedAppOptimizerTest {
        }

        @Override
        public void performCompaction(String action, int pid) throws IOException {
        public void performCompaction(CachedAppOptimizer.CompactAction action, int pid)
                throws IOException {
            mRss = mRssAfterCompaction;
        }