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

Commit 78c1e444 authored by Misha Wagner's avatar Misha Wagner Committed by Android (Google) Code Review
Browse files

Merge changes from topic "CacheOomRanker-new-features-sc-qpr1-dev" into sc-qpr1-dev

* changes:
  Use Process.getRss instead of mProfile.getLastRss in CacheOomRanker.
  Change CacheOomRanker's "uses" feature.
  Fix race condition when using mPreserveTopNApps.
  Allow CacheOomRanker to rerank when there's <8 processes in the cache.
  Fix CacheOomRankerTest.
parents fe6f6a01 665e91e1
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -771,6 +771,11 @@ public class ActivityManager {
        return procState >= PROCESS_STATE_TRANSIENT_BACKGROUND;
    }

    /** @hide Should this process state be considered in the cache? */
    public static final boolean isProcStateCached(int procState) {
        return procState >= PROCESS_STATE_CACHED_ACTIVITY;
    }

    /** @hide Is this a foreground service type? */
    public static boolean isForegroundService(int procState) {
        return procState == PROCESS_STATE_FOREGROUND_SERVICE;
+208 −28
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.am;

import android.os.Process;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.Slog;

@@ -38,21 +40,40 @@ public class CacheOomRanker {
    private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
    @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
    @VisibleForTesting
    static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS =
            "oom_re_ranking_preserve_top_n_apps";
    @VisibleForTesting
    static final int DEFAULT_PRESERVE_TOP_N_APPS = 3;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_USE_FREQUENT_RSS = "oom_re_ranking_rss_use_frequent_rss";
    @VisibleForTesting
    static final boolean DEFAULT_USE_FREQUENT_RSS = true;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS = "oom_re_ranking_rss_update_rate_ms";
    @VisibleForTesting
    static final long DEFAULT_RSS_UPDATE_RATE_MS = 10_000; // 10 seconds
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
    @VisibleForTesting
    static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
    @VisibleForTesting
    static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
    @VisibleForTesting
    static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
    @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
    @VisibleForTesting
    static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;

    private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
            new ScoreComparator();
    private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR =
            new CacheUseComparator();
    private static final Comparator<RankedProcessRecord> RSS_COMPARATOR =
            new RssComparator();
    private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR =
            new LastRssComparator();
    private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR =
@@ -61,20 +82,33 @@ public class CacheOomRanker {
    private final Object mPhenotypeFlagLock = new Object();

    private final ActivityManagerService mService;
    private final ProcessDependencies mProcessDependencies;
    private final ActivityManagerGlobalLock mProcLock;
    private final Object mProfilerLock;

    @GuardedBy("mPhenotypeFlagLock")
    private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting
    int mPreserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting
    boolean mUseFrequentRss = DEFAULT_USE_FREQUENT_RSS;
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting
    long mRssUpdateRateMs = DEFAULT_RSS_UPDATE_RATE_MS;
    // Weight to apply to the LRU ordering.
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
    @VisibleForTesting
    float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
    // Weight to apply to the ordering by number of times the process has been added to the cache.
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
    @VisibleForTesting
    float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
    // Weight to apply to the ordering by RSS used by the processes.
    @GuardedBy("mPhenotypeFlagLock")
    @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
    @VisibleForTesting
    float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;

    // Positions to replace in the lru list.
    @GuardedBy("mPhenotypeFlagLock")
@@ -93,6 +127,12 @@ public class CacheOomRanker {
                                updateUseOomReranking();
                            } else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
                                updateNumberToReRank();
                            } else if (KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS.equals(name)) {
                                updatePreserveTopNApps();
                            } else if (KEY_OOM_RE_RANKING_USE_FREQUENT_RSS.equals(name)) {
                                updateUseFrequentRss();
                            } else if (KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS.equals(name)) {
                                updateRssUpdateRateMs();
                            } else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
                                updateLruWeight();
                            } else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
@@ -106,9 +146,15 @@ public class CacheOomRanker {
            };

    CacheOomRanker(final ActivityManagerService service) {
        this(service, new ProcessDependenciesImpl());
    }

    @VisibleForTesting
    CacheOomRanker(final ActivityManagerService service, ProcessDependencies processDependencies) {
        mService = service;
        mProcLock = service.mProcLock;
        mProfilerLock = service.mAppProfiler.mProfilerLock;
        mProcessDependencies = processDependencies;
    }

    /** Load settings from device config and register a listener for changes. */
@@ -159,6 +205,31 @@ public class CacheOomRanker {
        return mScoredProcessRecords == null ? 0 : mScoredProcessRecords.length;
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updatePreserveTopNApps() {
        int preserveTopNApps = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, DEFAULT_PRESERVE_TOP_N_APPS);
        if (preserveTopNApps < 0) {
            Slog.w(OomAdjuster.TAG,
                    "Found negative value for preserveTopNApps, setting to default: "
                            + preserveTopNApps);
            preserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
        }
        mPreserveTopNApps = preserveTopNApps;
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateRssUpdateRateMs() {
        mRssUpdateRateMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_OOM_RE_RANKING_RSS_UPDATE_RATE_MS, DEFAULT_RSS_UPDATE_RATE_MS);
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateUseFrequentRss() {
        mUseFrequentRss = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_OOM_RE_RANKING_USE_FREQUENT_RSS, DEFAULT_USE_FREQUENT_RSS);
    }

    @GuardedBy("mPhenotypeFlagLock")
    private void updateLruWeight() {
        mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -183,9 +254,39 @@ public class CacheOomRanker {
     */
    @GuardedBy({"mService", "mProcLock"})
    void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
        // The lruList is a list of processes ordered by how recently they were used. The
        // least-recently-used apps are at the beginning of the list. We keep track of two
        // indices in the lruList:
        //
        // getNumberToReRank=5, preserveTopNApps=3, lruProcessServiceStart=7,
        // lruList=
        //   0: app A       ^
        //   1: app B       | These apps are re-ranked, as they are the first five apps (see
        //   2: app C       | getNumberToReRank), excluding...
        //   3: app D       v
        //   4: app E       ^
        //   5: app F       | The three most-recently-used apps in the cache (see preserveTopNApps).
        //   6: app G       v
        //   7: service A   ^
        //   8: service B   | Everything beyond lruProcessServiceStart is ignored, as these aren't
        //   9: service C   | apps.
        //  10: activity A  |
        //      ...         |
        //
        // `numProcessesEvaluated` moves across the apps (indices 0-6) or until we've found enough
        // apps to re-rank, and made sure none of them are in the top `preserveTopNApps` apps.
        // Re-ranked apps are copied into `scoredProcessRecords`, where the re-ranking calculation
        // happens.
        //
        // Note that some apps in the `lruList` can be skipped, if they don't pass
        //`appCanBeReRanked`.

        float lruWeight;
        float usesWeight;
        float rssWeight;
        int preserveTopNApps;
        boolean useFrequentRss;
        long rssUpdateRateMs;
        int[] lruPositions;
        RankedProcessRecord[] scoredProcessRecords;

@@ -193,6 +294,9 @@ public class CacheOomRanker {
            lruWeight = mLruWeight;
            usesWeight = mUsesWeight;
            rssWeight = mRssWeight;
            preserveTopNApps = mPreserveTopNApps;
            useFrequentRss = mUseFrequentRss;
            rssUpdateRateMs = mRssUpdateRateMs;
            lruPositions = mLruPositions;
            scoredProcessRecords = mScoredProcessRecords;
        }
@@ -202,52 +306,98 @@ public class CacheOomRanker {
            return;
        }

        int numProcessesEvaluated = 0;
        // Collect the least recently used processes to re-rank, only rank cached
        // processes further down the list than mLruProcessServiceStart.
        int cachedProcessPos = 0;
        for (int i = 0; i < lruProcessServiceStart
                && cachedProcessPos < scoredProcessRecords.length; ++i) {
            ProcessRecord app = lruList.get(i);
        int numProcessesReRanked = 0;
        while (numProcessesEvaluated < lruProcessServiceStart
                && numProcessesReRanked < scoredProcessRecords.length) {
            ProcessRecord process = lruList.get(numProcessesEvaluated);
            // Processes that will be assigned a cached oom adj score.
            if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
                    >= ProcessList.UNKNOWN_ADJ) {
                scoredProcessRecords[cachedProcessPos].proc = app;
                scoredProcessRecords[cachedProcessPos].score = 0.0f;
                lruPositions[cachedProcessPos] = i;
                ++cachedProcessPos;
            if (appCanBeReRanked(process)) {
                scoredProcessRecords[numProcessesReRanked].proc = process;
                scoredProcessRecords[numProcessesReRanked].score = 0.0f;
                lruPositions[numProcessesReRanked] = numProcessesEvaluated;
                ++numProcessesReRanked;
            }
            ++numProcessesEvaluated;
        }

        // Count how many apps we're not re-ranking (up to preserveTopNApps).
        int numProcessesNotReRanked = 0;
        while (numProcessesEvaluated < lruProcessServiceStart
                && numProcessesNotReRanked < preserveTopNApps) {
            ProcessRecord process = lruList.get(numProcessesEvaluated);
            if (appCanBeReRanked(process)) {
                numProcessesNotReRanked++;
            }
            numProcessesEvaluated++;
        }
        // Exclude the top `preserveTopNApps` apps from re-ranking.
        if (numProcessesNotReRanked < preserveTopNApps) {
            numProcessesReRanked -= preserveTopNApps - numProcessesNotReRanked;
            if (numProcessesReRanked < 0) {
                numProcessesReRanked = 0;
            }
        }

        if (useFrequentRss) {
            // Update RSS values for re-ranked apps.
            long nowMs = SystemClock.elapsedRealtime();
            for (int i = 0; i < numProcessesReRanked; ++i) {
                RankedProcessRecord scoredProcessRecord = scoredProcessRecords[i];
                long sinceUpdateMs =
                        nowMs - scoredProcessRecord.proc.mState.getCacheOomRankerRssTimeMs();
                if (scoredProcessRecord.proc.mState.getCacheOomRankerRss() != 0
                        && sinceUpdateMs < rssUpdateRateMs) {
                    continue;
                }

                long[] rss = mProcessDependencies.getRss(scoredProcessRecord.proc.getPid());
                if (rss == null || rss.length == 0) {
                    Slog.e(
                            OomAdjuster.TAG,
                            "Process.getRss returned bad value, not re-ranking: "
                                    + Arrays.toString(rss));
                    return;
                }
                // First element is total RSS:
                // frameworks/base/core/jni/android_util_Process.cpp:1192
                scoredProcessRecord.proc.mState.setCacheOomRankerRss(rss[0], nowMs);
                scoredProcessRecord.proc.mProfile.setLastRss(rss[0]);
            }

        // TODO maybe ensure a certain number above this in the cache before re-ranking.
        if (cachedProcessPos < scoredProcessRecords.length)  {
            // Ignore we don't have enough processes to worry about re-ranking.
            return;
        }

        // Add scores for each of the weighted features we want to rank based on.
        if (lruWeight > 0.0f) {
            // This doesn't use the LRU list ordering as after the first re-ranking
            // that will no longer be lru.
            Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
            Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
                    LAST_ACTIVITY_TIME_COMPARATOR);
            addToScore(scoredProcessRecords, lruWeight);
        }
        if (rssWeight > 0.0f) {
            if (useFrequentRss) {
                Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, RSS_COMPARATOR);
            } else {
                synchronized (mService.mAppProfiler.mProfilerLock) {
                Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
                    Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
                }
            }
            addToScore(scoredProcessRecords, rssWeight);
        }
        if (usesWeight > 0.0f) {
            Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
            Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, CACHE_USE_COMPARATOR);
            addToScore(scoredProcessRecords, usesWeight);
        }

        // Re-rank by the new combined score.
        Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);
        Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
                SCORED_PROCESS_RECORD_COMPARATOR);

        if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
            boolean printedHeader = false;
            for (int i = 0; i < scoredProcessRecords.length; ++i) {
            for (int i = 0; i < numProcessesReRanked; ++i) {
                if (scoredProcessRecords[i].proc.getPid()
                        != lruList.get(lruPositions[i]).getPid()) {
                    if (!printedHeader) {
@@ -260,12 +410,18 @@ public class CacheOomRanker {
            }
        }

        for (int i = 0; i < scoredProcessRecords.length; ++i) {
        for (int i = 0; i < numProcessesReRanked; ++i) {
            lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
            scoredProcessRecords[i].proc = null;
        }
    }

    private static boolean appCanBeReRanked(ProcessRecord process) {
        return !process.isKilledByAm()
                && process.getThread() != null
                && process.mState.getCurAdj() >= ProcessList.UNKNOWN_ADJ;
    }

    private static void addToScore(RankedProcessRecord[] scores, float weight) {
        for (int i = 1; i < scores.length; ++i) {
            scores[i].score += i * weight;
@@ -305,6 +461,16 @@ public class CacheOomRanker {
        }
    }

    private static class RssComparator implements Comparator<RankedProcessRecord> {
        @Override
        public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
            // High RSS first to match least recently used.
            return Long.compare(
                    o2.proc.mState.getCacheOomRankerRss(),
                    o1.proc.mState.getCacheOomRankerRss());
        }
    }

    private static class LastRssComparator implements Comparator<RankedProcessRecord> {
        @Override
        public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
@@ -317,4 +483,18 @@ public class CacheOomRanker {
        public ProcessRecord proc;
        public float score;
    }

    /**
     * Interface for mocking {@link Process} static methods.
     */
    interface ProcessDependencies {
        long[] getRss(int pid);
    }

    private static class ProcessDependenciesImpl implements ProcessDependencies {
        @Override
        public long[] getRss(int pid) {
            return Process.getRss(pid);
        }
    }
}
+34 −6
Original line number Diff line number Diff line
@@ -342,6 +342,20 @@ final class ProcessStateRecord {
    @GuardedBy("mService")
    private int mCacheOomRankerUseCount;

    /**
     * Process memory usage (RSS).
     *
     * Periodically populated by {@code CacheOomRanker}, stored in this object to cache the values.
     */
    @GuardedBy("mService")
    private long mCacheOomRankerRss;

    /**
     * The last time, in milliseconds since boot, since {@link #mCacheOomRankerRss} was updated.
     */
    @GuardedBy("mService")
    private long mCacheOomRankerRssTimeMs;

    /**
     * Whether or not this process is reachable from given process.
     */
@@ -577,6 +591,10 @@ final class ProcessStateRecord {

    @GuardedBy({"mService", "mProcLock"})
    void setSetProcState(int setProcState) {
        if (ActivityManager.isProcStateCached(mSetProcState)
                && !ActivityManager.isProcStateCached(setProcState)) {
            mCacheOomRankerUseCount++;
        }
        mSetProcState = setProcState;
    }

@@ -840,12 +858,7 @@ final class ProcessStateRecord {

    @GuardedBy("mService")
    void setCached(boolean cached) {
        if (mCached != cached) {
        mCached = cached;
            if (cached) {
                ++mCacheOomRankerUseCount;
            }
        }
    }

    @GuardedBy("mService")
@@ -1151,6 +1164,21 @@ final class ProcessStateRecord {
        return mLastInvisibleTime;
    }

    public void setCacheOomRankerRss(long rss, long rssTimeMs) {
        mCacheOomRankerRss = rss;
        mCacheOomRankerRssTimeMs = rssTimeMs;
    }

    @GuardedBy("mService")
    public long getCacheOomRankerRss() {
        return mCacheOomRankerRss;
    }

    @GuardedBy("mService")
    public long getCacheOomRankerRssTimeMs() {
        return mCacheOomRankerRssTimeMs;
    }

    @GuardedBy({"mService", "mProcLock"})
    void dump(PrintWriter pw, String prefix, long nowUptime) {
        if (mReportedInteraction || mFgInteractionTime != 0) {
+436 −64

File changed.

Preview size limit exceeded, changes collapsed.