diff options
| author | 2025-02-06 13:41:06 -0800 | |
|---|---|---|
| committer | 2025-02-06 13:41:06 -0800 | |
| commit | 4c8649b8ad2d97def7e4734e554b426c7aade07c (patch) | |
| tree | 5e709847f3531ba84a1ff8b07c527874df96fe45 | |
| parent | d5adc1d1f86dc53816a770c52621774091c0a876 (diff) | |
| parent | f2d5f7f127efd4fa8ca9e7aef6ae2099257b1d4b (diff) | |
Merge "Refactor compaction stats related code to its own classes" into main
11 files changed, 700 insertions, 443 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7db63a5c4a11..f34016905502 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10430,9 +10430,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized(this) { mConstants.dump(pw); - synchronized (mProcLock) { - mOomAdjuster.dumpCachedAppOptimizerSettings(pw); - } + mOomAdjuster.dumpCachedAppOptimizerSettings(pw); mOomAdjuster.dumpCacheOomRankerSettings(pw); pw.println(); if (dumpAll) { @@ -10806,7 +10804,7 @@ public class ActivityManagerService extends IActivityManager.Stub || DUMP_RECENTS_CMD.equals(cmd) || DUMP_RECENTS_SHORT_CMD.equals(cmd) || DUMP_TOP_RESUMED_ACTIVITY.equals(cmd) || DUMP_VISIBLE_ACTIVITIES.equals(cmd)) { - mAtmInternal.dump(cmd, fd, pw, args, opti, /* dumpAll= */ true , dumpClient, + mAtmInternal.dump(cmd, fd, pw, args, opti, /* dumpAll= */ true, dumpClient, dumpPackage, dumpDisplayId); } else if ("binder-proxies".equals(cmd)) { if (opti >= args.length) { @@ -10896,7 +10894,8 @@ public class ActivityManagerService extends IActivityManager.Stub name = args[opti]; opti++; newArgs = new String[args.length - opti]; - if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); } if (!mCpHelper.dumpProvider(fd, pw, name, newArgs, 0, dumpAll)) { pw.println("No providers match: " + name); @@ -10919,7 +10918,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } - int[] users = dumpUserId == UserHandle.USER_ALL ? null : new int[] { dumpUserId }; + int[] users = dumpUserId == UserHandle.USER_ALL ? null : new int[]{dumpUserId}; if (!mServices.dumpService(fd, pw, name, users, newArgs, 0, dumpAll)) { pw.println("No services match: " + name); pw.println("Use -h for help."); @@ -10948,9 +10947,10 @@ public class ActivityManagerService extends IActivityManager.Stub mConstants.dump(pw); } synchronized (mProcLock) { - mOomAdjuster.dumpCachedAppOptimizerSettings(pw); mOomAdjuster.dumpCacheOomRankerSettings(pw); } + } else if ("cao".equals(cmd)) { + mOomAdjuster.dumpCachedAppOptimizerSettings(pw); } else if ("timers".equals(cmd)) { AnrTimer.dump(pw, true); } else if ("services".equals(cmd) || "s".equals(cmd)) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index c237897f1229..5c2bd0a0e90f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -4344,6 +4344,7 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" lru: raw LRU process list"); pw.println(" binder-proxies: stats on binder objects and IPCs"); pw.println(" settings: currently applied config settings"); + pw.println(" cao: cached app optimizer state"); pw.println(" timers: the current ANR timer state"); pw.println(" service [COMP_SPEC]: service client-side state"); pw.println(" package [PACKAGE_NAME]: all state related to given package"); diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index ce526e510053..b677297dfef2 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -85,6 +85,8 @@ import com.android.internal.os.BinderfsStatsReader; import com.android.internal.os.ProcLocksReader; import com.android.internal.util.FrameworkStatsLog; import com.android.server.ServiceThread; +import com.android.server.am.compaction.CompactionStatsManager; +import com.android.server.am.compaction.SingleCompactionStats; import dalvik.annotation.optimization.NeverCompile; @@ -94,11 +96,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; -import java.util.EnumMap; import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.Executor; @@ -343,12 +341,6 @@ public class CachedAppOptimizer { // on swap resources as file. static final double COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD = 0.2; - // Size of history for the last 20 compactions for any process - static final int LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE = 20; - - // Amount of processes supported to record for their last compaction. - static final int LAST_COMPACTION_FOR_PROCESS_STATS_SIZE = 256; - static final int DO_FREEZE = 1; static final int REPORT_UNFREEZE = 2; @@ -521,6 +513,9 @@ public class CachedAppOptimizer { // Handler on which compaction runs. @VisibleForTesting Handler mCompactionHandler; + @VisibleForTesting + CompactionStatsManager mCompactStatsManager; + private Handler mFreezeHandler; @GuardedBy("mProcLock") private boolean mFreezerOverride = false; @@ -529,156 +524,6 @@ public class CachedAppOptimizer { @VisibleForTesting volatile long mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT; @VisibleForTesting volatile boolean mFreezerExemptInstPkg = DEFAULT_FREEZER_EXEMPT_INST_PKG; - // Maps process ID to last compaction statistics for processes that we've fully compacted. Used - // when evaluating throttles that we only consider for "full" compaction, so we don't store - // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and - // facilitate removal of the oldest entry. - @VisibleForTesting - @GuardedBy("mProcLock") - LinkedHashMap<Integer, SingleCompactionStats> mLastCompactionStats = - new LinkedHashMap<Integer, SingleCompactionStats>() { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > LAST_COMPACTION_FOR_PROCESS_STATS_SIZE; - } - }; - - LinkedList<SingleCompactionStats> mCompactionStatsHistory = - new LinkedList<SingleCompactionStats>() { - @Override - public boolean add(SingleCompactionStats e) { - if (size() >= LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE) { - this.remove(); - } - return super.add(e); - } - }; - - class AggregatedCompactionStats { - // Throttling stats - public long mFullCompactRequested; - public long mSomeCompactRequested; - public long mFullCompactPerformed; - public long mSomeCompactPerformed; - public long mProcCompactionsNoPidThrottled; - public long mProcCompactionsOomAdjThrottled; - public long mProcCompactionsTimeThrottled; - public long mProcCompactionsRSSThrottled; - public long mProcCompactionsMiscThrottled; - - // Memory stats - public long mTotalDeltaAnonRssKBs; - public long mTotalZramConsumedKBs; - public long mTotalAnonMemFreedKBs; - public long mSumOrigAnonRss; - public double mMaxCompactEfficiency; - public double mMaxSwapEfficiency; - - // Cpu time - public long mTotalCpuTimeMillis; - - public long getThrottledSome() { return mSomeCompactRequested - mSomeCompactPerformed; } - - public long getThrottledFull() { return mFullCompactRequested - mFullCompactPerformed; } - - public void addMemStats(long anonRssSaved, long zramConsumed, long memFreed, - long origAnonRss, long totalCpuTimeMillis) { - final double compactEfficiency = memFreed / (double) origAnonRss; - if (compactEfficiency > mMaxCompactEfficiency) { - mMaxCompactEfficiency = compactEfficiency; - } - final double swapEfficiency = anonRssSaved / (double) origAnonRss; - if (swapEfficiency > mMaxSwapEfficiency) { - mMaxSwapEfficiency = swapEfficiency; - } - mTotalDeltaAnonRssKBs += anonRssSaved; - mTotalZramConsumedKBs += zramConsumed; - mTotalAnonMemFreedKBs += memFreed; - mSumOrigAnonRss += origAnonRss; - mTotalCpuTimeMillis += totalCpuTimeMillis; - } - - @NeverCompile - public void dump(PrintWriter pw) { - long totalCompactRequested = mSomeCompactRequested + mFullCompactRequested; - long totalCompactPerformed = mSomeCompactPerformed + mFullCompactPerformed; - pw.println(" Performed / Requested:"); - pw.println(" Some: (" + mSomeCompactPerformed + "/" + mSomeCompactRequested + ")"); - pw.println(" Full: (" + mFullCompactPerformed + "/" + mFullCompactRequested + ")"); - - long throttledSome = getThrottledSome(); - long throttledFull = getThrottledFull(); - - if (throttledSome > 0 || throttledFull > 0) { - pw.println(" Throttled:"); - pw.println(" Some: " + throttledSome + " Full: " + throttledFull); - pw.println(" Throttled by Type:"); - final long compactionsThrottled = totalCompactRequested - totalCompactPerformed; - // Any throttle that was not part of the previous categories - final long unaccountedThrottled = compactionsThrottled - - mProcCompactionsNoPidThrottled - mProcCompactionsOomAdjThrottled - - mProcCompactionsTimeThrottled - mProcCompactionsRSSThrottled - - mProcCompactionsMiscThrottled; - pw.println(" NoPid: " + mProcCompactionsNoPidThrottled - + " OomAdj: " + mProcCompactionsOomAdjThrottled + " Time: " - + mProcCompactionsTimeThrottled + " RSS: " + mProcCompactionsRSSThrottled - + " Misc: " + mProcCompactionsMiscThrottled - + " Unaccounted: " + unaccountedThrottled); - final double compactThrottlePercentage = - (compactionsThrottled / (double) totalCompactRequested) * 100.0; - pw.println(" Throttle Percentage: " + compactThrottlePercentage); - } - - if (mFullCompactPerformed > 0) { - pw.println(" -----Memory Stats----"); - pw.println(" Total Delta Anon RSS (KB) : " + mTotalDeltaAnonRssKBs); - pw.println(" Total Physical ZRAM Consumed (KB): " + mTotalZramConsumedKBs); - // Anon Mem Freed = Delta Anon RSS - ZRAM Consumed - pw.println(" Total Anon Memory Freed (KB): " + mTotalAnonMemFreedKBs); - pw.println(" Avg Swap Efficiency (KB) (Delta Anon RSS/Orig Anon RSS): " - + (mTotalDeltaAnonRssKBs / (double) mSumOrigAnonRss)); - pw.println(" Max Swap Efficiency: " + mMaxSwapEfficiency); - // This tells us how much anon memory we were able to free thanks to running - // compaction - pw.println(" Avg Compaction Efficiency (Anon Freed/Anon RSS): " - + (mTotalAnonMemFreedKBs / (double) mSumOrigAnonRss)); - pw.println(" Max Compaction Efficiency: " + mMaxCompactEfficiency); - // This tells us how effective is the compression algorithm in physical memory - pw.println(" Avg Compression Ratio (1 - ZRAM Consumed/DeltaAnonRSS): " - + (1.0 - mTotalZramConsumedKBs / (double) mTotalDeltaAnonRssKBs)); - long avgKBsPerProcCompact = mFullCompactPerformed > 0 - ? (mTotalAnonMemFreedKBs / mFullCompactPerformed) - : 0; - pw.println(" Avg Anon Mem Freed/Compaction (KB) : " + avgKBsPerProcCompact); - double compactionCost = - mTotalCpuTimeMillis / (mTotalAnonMemFreedKBs / 1024.0); // ms/MB - pw.println(" Compaction Cost (ms/MB): " + compactionCost); - } - } - } - - class AggregatedProcessCompactionStats extends AggregatedCompactionStats { - public final String processName; - - AggregatedProcessCompactionStats(String processName) { this.processName = processName; } - } - - class AggregatedSourceCompactionStats extends AggregatedCompactionStats { - public final CompactSource sourceType; - - AggregatedSourceCompactionStats(CompactSource sourceType) { this.sourceType = sourceType; } - } - - private final LinkedHashMap<String, AggregatedProcessCompactionStats> mPerProcessCompactStats = - new LinkedHashMap<>(256); - private final EnumMap<CompactSource, AggregatedSourceCompactionStats> mPerSourceCompactStats = - new EnumMap<>(CompactSource.class); - private long mTotalCompactionDowngrades; - private long mSystemCompactionsPerformed; - private long mSystemTotalMemFreed; - private EnumMap<CancelCompactReason, Integer> mTotalCompactionsCancelled = - new EnumMap<>(CancelCompactReason.class); - private final ProcessDependencies mProcessDependencies; private final ProcLocksReader mProcLocksReader; @@ -758,10 +603,15 @@ public class CachedAppOptimizer { } } - @GuardedBy("mProcLock") @NeverCompile void dump(PrintWriter pw) { - pw.println("CachedAppOptimizer settings"); + dumpCompact(pw); + dumpFreezer(pw); + } + + @NeverCompile + void dumpCompact(PrintWriter pw) { + pw.println("Compaction settings"); synchronized (mPhenotypeFlagLock) { pw.println(" " + KEY_USE_COMPACTION + "=" + mUseCompaction); pw.println(" " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome); @@ -775,66 +625,30 @@ public class CachedAppOptimizer { + mFullAnonRssThrottleKb); pw.println(" " + KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + "=" + mFullDeltaRssThrottleKb); - pw.println(" " + KEY_COMPACT_PROC_STATE_THROTTLE + "=" + pw.println(" " + KEY_COMPACT_PROC_STATE_THROTTLE + "=" + Arrays.toString(mProcStateThrottle.toArray(new Integer[0]))); + } - pw.println(" Per-Process Compaction Stats"); - long totalCompactPerformedSome = 0; - long totalCompactPerformedFull = 0; - for (AggregatedProcessCompactionStats stats : mPerProcessCompactStats.values()) { - pw.println("-----" + stats.processName + "-----"); - totalCompactPerformedSome += stats.mSomeCompactPerformed; - totalCompactPerformedFull += stats.mFullCompactPerformed; - stats.dump(pw); - pw.println(); - } - pw.println(); - pw.println(" Per-Source Compaction Stats"); - for (AggregatedSourceCompactionStats stats : mPerSourceCompactStats.values()) { - pw.println("-----" + stats.sourceType + "-----"); - stats.dump(pw); - pw.println(); - } - pw.println(); - - pw.println("Total Compactions Performed by profile: " + totalCompactPerformedSome - + " some, " + totalCompactPerformedFull + " full"); - pw.println("Total compactions downgraded: " + mTotalCompactionDowngrades); - pw.println("Total compactions cancelled by reason: "); - for (CancelCompactReason reason : mTotalCompactionsCancelled.keySet()) { - pw.println(" " + reason + ": " + mTotalCompactionsCancelled.get(reason)); - } - pw.println(); - - pw.println(" System Compaction Memory Stats"); - pw.println(" Compactions Performed: " + mSystemCompactionsPerformed); - pw.println(" Total Memory Freed (KB): " + mSystemTotalMemFreed); - double avgKBsPerSystemCompact = mSystemCompactionsPerformed > 0 - ? mSystemTotalMemFreed / mSystemCompactionsPerformed - : 0; - pw.println(" Avg Mem Freed per Compact (KB): " + avgKBsPerSystemCompact); - pw.println(); - pw.println(" Tracking last compaction stats for " + mLastCompactionStats.size() - + " processes."); - pw.println("Last Compaction per process stats:"); - pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs" - + ",SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj," - + "oomAdjReason)"); - for (Map.Entry<Integer, SingleCompactionStats> entry : - mLastCompactionStats.entrySet()) { - SingleCompactionStats stats = entry.getValue(); - stats.dump(pw); - } - pw.println(); - pw.println("Last 20 Compactions Stats:"); - pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs," - + "SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj," - + "oomAdjReason)"); - for (SingleCompactionStats stats : mCompactionStatsHistory) { - stats.dump(pw); + mCompactStatsManager.dump(pw); + + synchronized (mProcLock) { + if (!mPendingCompactionProcesses.isEmpty()) { + pw.println(" Pending compactions:"); + int size = mPendingCompactionProcesses.size(); + for (int i = 0; i < size; i++) { + ProcessRecord app = mPendingCompactionProcesses.get(i); + pw.println(" pid: " + app.getPid() + ". name: " + app.processName + + ". hasPendingCompact: " + app.mOptRecord.hasPendingCompact()); + } } - pw.println(); + } + pw.println(); + } + @NeverCompile + void dumpFreezer(PrintWriter pw) { + pw.println("Freezer settings"); + synchronized (mPhenotypeFlagLock) { pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer); pw.println(" " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate); pw.println(" " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout); @@ -858,16 +672,6 @@ public class CachedAppOptimizer { + " " + app.processName + (app.mOptRecord.isFreezeSticky() ? " (sticky)" : "")); } - - if (!mPendingCompactionProcesses.isEmpty()) { - pw.println(" Pending compactions:"); - size = mPendingCompactionProcesses.size(); - for (int i = 0; i < size; i++) { - ProcessRecord app = mPendingCompactionProcesses.get(i); - pw.println(" pid: " + app.getPid() + ". name: " + app.processName - + ". hasPendingCompact: " + app.mOptRecord.hasPendingCompact()); - } - } } } } @@ -877,26 +681,14 @@ public class CachedAppOptimizer { ProcessRecord app, CompactProfile compactProfile, CompactSource source, boolean force) { app.mOptRecord.setReqCompactSource(source); app.mOptRecord.setReqCompactProfile(compactProfile); - AggregatedSourceCompactionStats perSourceStats = getPerSourceAggregatedCompactStat(source); - AggregatedCompactionStats perProcStats = - getPerProcessAggregatedCompactStat(app.processName); - switch (compactProfile) { - case SOME: - ++perProcStats.mSomeCompactRequested; - ++perSourceStats.mSomeCompactRequested; - break; - case FULL: - ++perProcStats.mFullCompactRequested; - ++perSourceStats.mFullCompactRequested; - break; - default: - Slog.e(TAG_AM, - "Unimplemented compaction type, consider adding it."); - return false; + + if(compactProfile == null || compactProfile.equals(CompactProfile.NONE)) { + return false; } + final String processName = (app.processName != null ? app.processName : ""); + mCompactStatsManager.logCompactionRequested(source, compactProfile, processName); if (!app.mOptRecord.hasPendingCompact()) { - final String processName = (app.processName != null ? app.processName : ""); if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "compactApp " + app.mOptRecord.getReqCompactSource().name() + " " @@ -925,29 +717,6 @@ public class CachedAppOptimizer { COMPACT_NATIVE_MSG, pid, compactProfile.ordinal())); } - private AggregatedProcessCompactionStats getPerProcessAggregatedCompactStat( - String processName) { - if (processName == null) { - processName = ""; - } - AggregatedProcessCompactionStats stats = mPerProcessCompactStats.get(processName); - if (stats == null) { - stats = new AggregatedProcessCompactionStats(processName); - mPerProcessCompactStats.put(processName, stats); - } - return stats; - } - - private AggregatedSourceCompactionStats getPerSourceAggregatedCompactStat( - CompactSource source) { - AggregatedSourceCompactionStats stats = mPerSourceCompactStats.get(source); - if (stats == null) { - stats = new AggregatedSourceCompactionStats(source); - mPerSourceCompactStats.put(source, stats); - } - return stats; - } - void compactAllSystem() { if (useCompaction()) { if (DEBUG_COMPACTION) { @@ -1008,6 +777,7 @@ public class CachedAppOptimizer { } mCompactionHandler = new MemCompactionHandler(); + mCompactStatsManager = CompactionStatsManager.getInstance(); Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), Process.THREAD_GROUP_SYSTEM); @@ -1667,12 +1437,7 @@ public class CachedAppOptimizer { cancelled = true; } if (cancelled) { - if (mTotalCompactionsCancelled.containsKey(cancelReason)) { - int count = mTotalCompactionsCancelled.get(cancelReason); - mTotalCompactionsCancelled.put(cancelReason, count + 1); - } else { - mTotalCompactionsCancelled.put(cancelReason, 1); - } + mCompactStatsManager.logCompactionCancelled(cancelReason); if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "Cancelled pending or running compactions for process: " + @@ -1722,8 +1487,7 @@ public class CachedAppOptimizer { // Downgrade compaction under swap memory pressure if (swapFreePercent < COMPACT_DOWNGRADE_FREE_SWAP_THRESHOLD) { profile = CompactProfile.SOME; - - ++mTotalCompactionDowngrades; + mCompactStatsManager.logCompactionDowngrade(); if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "Downgraded compaction to "+ profile +" due to low swap." @@ -1753,72 +1517,6 @@ public class CachedAppOptimizer { } } - @VisibleForTesting - static final class SingleCompactionStats { - private static final float STATSD_SAMPLE_RATE = 0.1f; - private static final Random mRandom = new Random(); - private final long[] mRssAfterCompaction; - public CompactSource mSourceType; - public String mProcessName; - public final int mUid; - public long mDeltaAnonRssKBs; - public long mZramConsumedKBs; - public long mAnonMemFreedKBs; - public float mCpuTimeMillis; - public long mOrigAnonRss; - public int mProcState; - public int mOomAdj; - public @OomAdjReason int mOomAdjReason; - - SingleCompactionStats(long[] rss, CompactSource source, String processName, - long deltaAnonRss, long zramConsumed, long anonMemFreed, long origAnonRss, - long cpuTimeMillis, int procState, int oomAdj, - @OomAdjReason int oomAdjReason, int uid) { - mRssAfterCompaction = rss; - mSourceType = source; - mProcessName = processName; - mUid = uid; - mDeltaAnonRssKBs = deltaAnonRss; - mZramConsumedKBs = zramConsumed; - mAnonMemFreedKBs = anonMemFreed; - mCpuTimeMillis = cpuTimeMillis; - mOrigAnonRss = origAnonRss; - mProcState = procState; - mOomAdj = oomAdj; - mOomAdjReason = oomAdjReason; - } - - double getCompactEfficiency() { return mAnonMemFreedKBs / (double) mOrigAnonRss; } - - double getSwapEfficiency() { return mDeltaAnonRssKBs / (double) mOrigAnonRss; } - - double getCompactCost() { - // mCpuTimeMillis / (anonMemFreedKBs/1024) and metric is in (ms/MB) - return mCpuTimeMillis / (double) mAnonMemFreedKBs * 1024; - } - - long[] getRssAfterCompaction() { - return mRssAfterCompaction; - } - - @NeverCompile - void dump(PrintWriter pw) { - pw.println(" (" + mProcessName + "," + mSourceType.name() + "," + mDeltaAnonRssKBs - + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + "," - + getSwapEfficiency() + "," + getCompactEfficiency() - + "," + getCompactCost() + "," + mProcState + "," + mOomAdj + "," - + OomAdjuster.oomAdjReasonToString(mOomAdjReason) + ")"); - } - - void sendStat() { - if (mRandom.nextFloat() < STATSD_SAMPLE_RATE) { - FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED_V2, mUid, mProcState, - mOomAdj, mDeltaAnonRssKBs, mZramConsumedKBs, mCpuTimeMillis, mOrigAnonRss, - mOomAdjReason); - } - } - } - private final class MemCompactionHandler extends Handler { private MemCompactionHandler() { super(mCachedAppOptimizerThread.getLooper()); @@ -1909,7 +1607,8 @@ public class CachedAppOptimizer { private boolean shouldRssThrottleCompaction( CompactProfile profile, int pid, String name, long[] rssBefore) { long anonRssBefore = rssBefore[RSS_ANON_INDEX]; - SingleCompactionStats lastCompactionStats = mLastCompactionStats.get(pid); + SingleCompactionStats lastCompactionStats = + mCompactStatsManager.getLastCompactionStats(pid); if (rssBefore[RSS_TOTAL_INDEX] == 0 && rssBefore[RSS_FILE_INDEX] == 0 && rssBefore[RSS_ANON_INDEX] == 0 && rssBefore[RSS_SWAP_INDEX] == 0) { @@ -1988,43 +1687,43 @@ public class CachedAppOptimizer { oomAdjReason = opt.getLastOomAdjChangeReason(); } - AggregatedSourceCompactionStats perSourceStats = - getPerSourceAggregatedCompactStat(opt.getReqCompactSource()); - AggregatedProcessCompactionStats perProcessStats = - getPerProcessAggregatedCompactStat(name); - long[] rssBefore; if (pid == 0) { // not a real process, either one being launched or one being killed if (DEBUG_COMPACTION) { Slog.d(TAG_AM, "Compaction failed, pid is 0"); } - ++perSourceStats.mProcCompactionsNoPidThrottled; - ++perProcessStats.mProcCompactionsNoPidThrottled; + mCompactStatsManager.logCompactionThrottled( + CompactionStatsManager.COMPACT_THROTTLE_REASON_NO_PID, + compactSource, name); return; } if (!forceCompaction) { if (shouldOomAdjThrottleCompaction(proc)) { - ++perProcessStats.mProcCompactionsOomAdjThrottled; - ++perSourceStats.mProcCompactionsOomAdjThrottled; + mCompactStatsManager.logCompactionThrottled( + CompactionStatsManager.COMPACT_THROTTLE_REASON_OOM_ADJ, + compactSource, name); return; } if (shouldTimeThrottleCompaction( proc, start, requestedProfile, compactSource)) { - ++perProcessStats.mProcCompactionsTimeThrottled; - ++perSourceStats.mProcCompactionsTimeThrottled; + mCompactStatsManager.logCompactionThrottled( + CompactionStatsManager.COMPACT_THROTTLE_REASON_TIME_TOO_SOON, + compactSource, name); return; } if (shouldThrottleMiscCompaction(proc, procState)) { - ++perProcessStats.mProcCompactionsMiscThrottled; - ++perSourceStats.mProcCompactionsMiscThrottled; + mCompactStatsManager.logCompactionThrottled( + CompactionStatsManager.COMPACT_THROTTLE_REASON_PROC_STATE, + compactSource, name); return; } rssBefore = mProcessDependencies.getRss(pid); if (shouldRssThrottleCompaction(requestedProfile, pid, name, rssBefore)) { - ++perProcessStats.mProcCompactionsRSSThrottled; - ++perSourceStats.mProcCompactionsRSSThrottled; + mCompactStatsManager.logCompactionThrottled( + CompactionStatsManager.COMPACT_THROTTLE_REASON_DELTA_RSS, + compactSource, name); return; } } else { @@ -2065,40 +1764,19 @@ public class CachedAppOptimizer { long deltaSwapRss = rssAfter[RSS_SWAP_INDEX] - rssBefore[RSS_SWAP_INDEX]; switch (opt.getReqCompactProfile()) { case SOME: - ++perSourceStats.mSomeCompactPerformed; - ++perProcessStats.mSomeCompactPerformed; + mCompactStatsManager.logSomeCompactionPerformed(compactSource, + name); break; case FULL: - ++perSourceStats.mFullCompactPerformed; - ++perProcessStats.mFullCompactPerformed; long anonRssSavings = -deltaAnonRss; long zramConsumed = zramUsedKbAfter - zramUsedKbBefore; long memFreed = anonRssSavings - zramConsumed; long totalCpuTimeMillis = deltaCpuTimeNanos / 1000000; long origAnonRss = rssBefore[RSS_ANON_INDEX]; - - // Negative stats would skew averages and will likely be due to - // noise of system doing other things so we put a floor at 0 to - // avoid negative values. - anonRssSavings = anonRssSavings > 0 ? anonRssSavings : 0; - zramConsumed = zramConsumed > 0 ? zramConsumed : 0; - memFreed = memFreed > 0 ? memFreed : 0; - - perProcessStats.addMemStats(anonRssSavings, zramConsumed, memFreed, - origAnonRss, totalCpuTimeMillis); - perSourceStats.addMemStats(anonRssSavings, zramConsumed, memFreed, - origAnonRss, totalCpuTimeMillis); - SingleCompactionStats memStats = new SingleCompactionStats(rssAfter, - compactSource, name, anonRssSavings, zramConsumed, memFreed, - origAnonRss, totalCpuTimeMillis, procState, newOomAdj, - oomAdjReason, proc.uid); - mLastCompactionStats.remove(pid); - mLastCompactionStats.put(pid, memStats); - mCompactionStatsHistory.add(memStats); - if (!forceCompaction) { - // Avoid polluting field metrics with forced compactions. - memStats.sendStat(); - } + mCompactStatsManager.logFullCompactionPerformed(compactSource, name, + anonRssSavings, zramConsumed, memFreed, origAnonRss, + totalCpuTimeMillis, rssAfter, procState, newOomAdj, + oomAdjReason, proc.uid, pid, !forceCompaction); break; default: // We likely missed adding this category, it needs to be added @@ -2129,12 +1807,12 @@ public class CachedAppOptimizer { break; } case COMPACT_SYSTEM_MSG: { - ++mSystemCompactionsPerformed; Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem"); long memFreedBefore = getMemoryFreedCompaction(); compactSystem(); long memFreedAfter = getMemoryFreedCompaction(); - mSystemTotalMemFreed += memFreedAfter - memFreedBefore; + long memFreed = memFreedAfter - memFreedBefore; + mCompactStatsManager.logSystemCompactionPerformed(memFreed); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 363ba82cb332..cd40905b01da 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -4057,7 +4057,6 @@ public class OomAdjuster { + " mNewNumServiceProcs=" + mNewNumServiceProcs); } - @GuardedBy("mProcLock") void dumpCachedAppOptimizerSettings(PrintWriter pw) { mCachedAppOptimizer.dump(pw); } diff --git a/services/core/java/com/android/server/am/compaction/AggregatedCompactionStats.java b/services/core/java/com/android/server/am/compaction/AggregatedCompactionStats.java new file mode 100644 index 000000000000..836670cd10eb --- /dev/null +++ b/services/core/java/com/android/server/am/compaction/AggregatedCompactionStats.java @@ -0,0 +1,124 @@ +/* + * 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.server.am.compaction; + +import dalvik.annotation.optimization.NeverCompile; + +import java.io.PrintWriter; + +class AggregatedCompactionStats { + // Throttling stats + public long mFullCompactRequested; + public long mSomeCompactRequested; + public long mFullCompactPerformed; + public long mSomeCompactPerformed; + public long mProcCompactionsNoPidThrottled; + public long mProcCompactionsOomAdjThrottled; + public long mProcCompactionsTimeThrottled; + public long mProcCompactionsRSSThrottled; + public long mProcCompactionsMiscThrottled; + + // Memory stats + public long mTotalDeltaAnonRssKBs; + public long mTotalZramConsumedKBs; + public long mTotalAnonMemFreedKBs; + public long mSumOrigAnonRss; + public double mMaxCompactEfficiency; + public double mMaxSwapEfficiency; + + // Cpu time + public long mTotalCpuTimeMillis; + + public long getThrottledSome() { return mSomeCompactRequested - mSomeCompactPerformed; } + + public long getThrottledFull() { return mFullCompactRequested - mFullCompactPerformed; } + + public void addMemStats(long anonRssSaved, long zramConsumed, long memFreed, + long origAnonRss, long totalCpuTimeMillis) { + final double compactEfficiency = memFreed / (double) origAnonRss; + if (compactEfficiency > mMaxCompactEfficiency) { + mMaxCompactEfficiency = compactEfficiency; + } + final double swapEfficiency = anonRssSaved / (double) origAnonRss; + if (swapEfficiency > mMaxSwapEfficiency) { + mMaxSwapEfficiency = swapEfficiency; + } + mTotalDeltaAnonRssKBs += anonRssSaved; + mTotalZramConsumedKBs += zramConsumed; + mTotalAnonMemFreedKBs += memFreed; + mSumOrigAnonRss += origAnonRss; + mTotalCpuTimeMillis += totalCpuTimeMillis; + } + + @NeverCompile + public void dump(PrintWriter pw) { + long totalCompactRequested = mSomeCompactRequested + mFullCompactRequested; + long totalCompactPerformed = mSomeCompactPerformed + mFullCompactPerformed; + pw.println(" Performed / Requested:"); + pw.println(" Some: (" + mSomeCompactPerformed + "/" + mSomeCompactRequested + ")"); + pw.println(" Full: (" + mFullCompactPerformed + "/" + mFullCompactRequested + ")"); + + long throttledSome = getThrottledSome(); + long throttledFull = getThrottledFull(); + + if (throttledSome > 0 || throttledFull > 0) { + pw.println(" Throttled:"); + pw.println(" Some: " + throttledSome + " Full: " + throttledFull); + pw.println(" Throttled by Type:"); + final long compactionsThrottled = totalCompactRequested - totalCompactPerformed; + // Any throttle that was not part of the previous categories + final long unaccountedThrottled = compactionsThrottled + - mProcCompactionsNoPidThrottled - mProcCompactionsOomAdjThrottled + - mProcCompactionsTimeThrottled - mProcCompactionsRSSThrottled + - mProcCompactionsMiscThrottled; + pw.println(" NoPid: " + mProcCompactionsNoPidThrottled + + " OomAdj: " + mProcCompactionsOomAdjThrottled + " Time: " + + mProcCompactionsTimeThrottled + " RSS: " + mProcCompactionsRSSThrottled + + " Misc: " + mProcCompactionsMiscThrottled + + " Unaccounted: " + unaccountedThrottled); + final double compactThrottlePercentage = + (compactionsThrottled / (double) totalCompactRequested) * 100.0; + pw.println(" Throttle Percentage: " + compactThrottlePercentage); + } + + if (mFullCompactPerformed > 0) { + pw.println(" -----Memory Stats----"); + pw.println(" Total Delta Anon RSS (KB) : " + mTotalDeltaAnonRssKBs); + pw.println(" Total Physical ZRAM Consumed (KB): " + mTotalZramConsumedKBs); + // Anon Mem Freed = Delta Anon RSS - ZRAM Consumed + pw.println(" Total Anon Memory Freed (KB): " + mTotalAnonMemFreedKBs); + pw.println(" Avg Swap Efficiency (KB) (Delta Anon RSS/Orig Anon RSS): " + + (mTotalDeltaAnonRssKBs / (double) mSumOrigAnonRss)); + pw.println(" Max Swap Efficiency: " + mMaxSwapEfficiency); + // This tells us how much anon memory we were able to free thanks to running + // compaction + pw.println(" Avg Compaction Efficiency (Anon Freed/Anon RSS): " + + (mTotalAnonMemFreedKBs / (double) mSumOrigAnonRss)); + pw.println(" Max Compaction Efficiency: " + mMaxCompactEfficiency); + // This tells us how effective is the compression algorithm in physical memory + pw.println(" Avg Compression Ratio (1 - ZRAM Consumed/DeltaAnonRSS): " + + (1.0 - mTotalZramConsumedKBs / (double) mTotalDeltaAnonRssKBs)); + long avgKBsPerProcCompact = mFullCompactPerformed > 0 + ? (mTotalAnonMemFreedKBs / mFullCompactPerformed) + : 0; + pw.println(" Avg Anon Mem Freed/Compaction (KB) : " + avgKBsPerProcCompact); + double compactionCost = + mTotalCpuTimeMillis / (mTotalAnonMemFreedKBs / 1024.0); // ms/MB + pw.println(" Compaction Cost (ms/MB): " + compactionCost); + } + } +} diff --git a/services/core/java/com/android/server/am/compaction/AggregatedProcessCompactionStats.java b/services/core/java/com/android/server/am/compaction/AggregatedProcessCompactionStats.java new file mode 100644 index 000000000000..53019f93267a --- /dev/null +++ b/services/core/java/com/android/server/am/compaction/AggregatedProcessCompactionStats.java @@ -0,0 +1,23 @@ +/* + * 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.server.am.compaction; + +final class AggregatedProcessCompactionStats extends AggregatedCompactionStats { + public final String mProcessName; + + public AggregatedProcessCompactionStats(String processName) { this.mProcessName = processName; } +} diff --git a/services/core/java/com/android/server/am/compaction/AggregatedSourceCompactionStats.java b/services/core/java/com/android/server/am/compaction/AggregatedSourceCompactionStats.java new file mode 100644 index 000000000000..39a10d52a217 --- /dev/null +++ b/services/core/java/com/android/server/am/compaction/AggregatedSourceCompactionStats.java @@ -0,0 +1,27 @@ +/* + * 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.server.am.compaction; + +import com.android.server.am.CachedAppOptimizer; + +final class AggregatedSourceCompactionStats extends AggregatedCompactionStats { + public final CachedAppOptimizer.CompactSource mSourceType; + + public AggregatedSourceCompactionStats(CachedAppOptimizer.CompactSource sourceType) { + this.mSourceType = sourceType; + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/compaction/CompactionStatsManager.java b/services/core/java/com/android/server/am/compaction/CompactionStatsManager.java new file mode 100644 index 000000000000..98368dacb86d --- /dev/null +++ b/services/core/java/com/android/server/am/compaction/CompactionStatsManager.java @@ -0,0 +1,320 @@ +/* + * 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.server.am.compaction; + +import android.annotation.IntDef; +import android.app.ActivityManagerInternal; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.am.CachedAppOptimizer; + +import dalvik.annotation.optimization.NeverCompile; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.EnumMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; + +public final class CompactionStatsManager { + private static CompactionStatsManager sInstance; + + private static String TAG = "CompactionStatsManager"; + + // Size of history for the last 20 compactions for any process + static final int LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE = 20; + + // Amount of processes supported to record for their last compaction. + static final int LAST_COMPACTION_FOR_PROCESS_STATS_SIZE = 256; + + public static final int COMPACT_THROTTLE_REASON_NO_PID = 0; + public static final int COMPACT_THROTTLE_REASON_OOM_ADJ = 1; + public static final int COMPACT_THROTTLE_REASON_TIME_TOO_SOON = 2; + public static final int COMPACT_THROTTLE_REASON_PROC_STATE = 3; + public static final int COMPACT_THROTTLE_REASON_DELTA_RSS = 4; + @IntDef(value = { + COMPACT_THROTTLE_REASON_NO_PID, COMPACT_THROTTLE_REASON_OOM_ADJ, + COMPACT_THROTTLE_REASON_TIME_TOO_SOON, COMPACT_THROTTLE_REASON_PROC_STATE, + COMPACT_THROTTLE_REASON_DELTA_RSS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CompactThrottleReason {} + + private final LinkedHashMap<String, AggregatedProcessCompactionStats> mPerProcessCompactStats = + new LinkedHashMap<>(256); + private final EnumMap<CachedAppOptimizer.CompactSource, AggregatedSourceCompactionStats> + mPerSourceCompactStats = + new EnumMap<>(CachedAppOptimizer.CompactSource.class); + + private long mTotalCompactionDowngrades; + private long mSystemCompactionsPerformed; + private long mSystemTotalMemFreed; + private EnumMap<CachedAppOptimizer.CancelCompactReason, Integer> mTotalCompactionsCancelled = + new EnumMap<>(CachedAppOptimizer.CancelCompactReason.class); + + // Maps process ID to last compaction statistics for processes that we've fully compacted. Used + // when evaluating throttles that we only consider for "full" compaction, so we don't store + // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and + // facilitate removal of the oldest entry. + @VisibleForTesting + @GuardedBy("mProcLock") + LinkedHashMap<Integer, SingleCompactionStats> mLastCompactionStats = + new LinkedHashMap<Integer, SingleCompactionStats>() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > LAST_COMPACTION_FOR_PROCESS_STATS_SIZE; + } + }; + + LinkedList<SingleCompactionStats> mCompactionStatsHistory = + new LinkedList<SingleCompactionStats>() { + @Override + public boolean add(SingleCompactionStats e) { + if (size() >= LAST_COMPACTED_ANY_PROCESS_STATS_HISTORY_SIZE) { + this.remove(); + } + return super.add(e); + } + }; + + public static CompactionStatsManager getInstance() { + if (sInstance == null) { + sInstance = new CompactionStatsManager(); + } + return sInstance; + } + + public SingleCompactionStats getLastCompactionStats(int pid) { + return mLastCompactionStats.get(pid); + } + + @VisibleForTesting + public LinkedHashMap<Integer, SingleCompactionStats> getLastCompactionStats() { + return mLastCompactionStats; + } + + @VisibleForTesting + public void reinit() { + sInstance = new CompactionStatsManager(); + } + + + public void logCompactionRequested(CachedAppOptimizer.CompactSource source, + CachedAppOptimizer.CompactProfile compactProfile, String processName) { + AggregatedSourceCompactionStats perSourceStats = getPerSourceAggregatedCompactStat(source); + AggregatedCompactionStats perProcStats = + getPerProcessAggregatedCompactStat(processName); + + switch (compactProfile) { + case SOME: + ++perProcStats.mSomeCompactRequested; + ++perSourceStats.mSomeCompactRequested; + break; + case FULL: + ++perProcStats.mFullCompactRequested; + ++perSourceStats.mFullCompactRequested; + break; + default: + Slog.e(TAG, + "Stats cannot be logged for compaction type."+compactProfile); + } + } + public void logCompactionThrottled(@CompactThrottleReason int reason, + CachedAppOptimizer.CompactSource source, String processName) { + AggregatedSourceCompactionStats perSourceStats = + getPerSourceAggregatedCompactStat(source); + AggregatedProcessCompactionStats perProcessStats = + getPerProcessAggregatedCompactStat(processName); + + switch(reason) { + case COMPACT_THROTTLE_REASON_NO_PID: + ++perSourceStats.mProcCompactionsNoPidThrottled; + ++perProcessStats.mProcCompactionsNoPidThrottled; + break; + case COMPACT_THROTTLE_REASON_OOM_ADJ: + ++perProcessStats.mProcCompactionsOomAdjThrottled; + ++perSourceStats.mProcCompactionsOomAdjThrottled; + break; + case COMPACT_THROTTLE_REASON_TIME_TOO_SOON: + ++perProcessStats.mProcCompactionsTimeThrottled; + ++perSourceStats.mProcCompactionsTimeThrottled; + break; + case COMPACT_THROTTLE_REASON_PROC_STATE: + ++perProcessStats.mProcCompactionsMiscThrottled; + ++perSourceStats.mProcCompactionsMiscThrottled; + break; + case COMPACT_THROTTLE_REASON_DELTA_RSS: + ++perProcessStats.mProcCompactionsRSSThrottled; + ++perSourceStats.mProcCompactionsRSSThrottled; + break; + default: + break; + } + } + + public void logSomeCompactionPerformed(CachedAppOptimizer.CompactSource source, + String processName) { + AggregatedSourceCompactionStats perSourceStats = + getPerSourceAggregatedCompactStat(source); + AggregatedProcessCompactionStats perProcessStats = + getPerProcessAggregatedCompactStat(processName); + + ++perSourceStats.mSomeCompactPerformed; + ++perProcessStats.mSomeCompactPerformed; + } + + public void logFullCompactionPerformed( + CachedAppOptimizer.CompactSource source, String processName, long anonRssSavings, + long zramConsumed, long memFreed, long origAnonRss, long totalCpuTimeMillis, + long[] rssAfterCompact, int procState, int newOomAdj, + @ActivityManagerInternal.OomAdjReason int oomAdjReason, int uid, int pid, + boolean logFieldMetric) { + AggregatedSourceCompactionStats perSourceStats = + getPerSourceAggregatedCompactStat(source); + AggregatedProcessCompactionStats perProcessStats = + getPerProcessAggregatedCompactStat(processName); + + ++perSourceStats.mFullCompactPerformed; + ++perProcessStats.mFullCompactPerformed; + + // Negative stats would skew averages and will likely be due to + // noise of system doing other things so we put a floor at 0 to + // avoid negative values. + anonRssSavings = anonRssSavings > 0 ? anonRssSavings : 0; + zramConsumed = zramConsumed > 0 ? zramConsumed : 0; + memFreed = memFreed > 0 ? memFreed : 0; + + perProcessStats.addMemStats(anonRssSavings, zramConsumed, memFreed, + origAnonRss, totalCpuTimeMillis); + perSourceStats.addMemStats(anonRssSavings, zramConsumed, memFreed, + origAnonRss, totalCpuTimeMillis); + SingleCompactionStats memStats = new SingleCompactionStats(rssAfterCompact, + source, processName, anonRssSavings, zramConsumed, memFreed, + origAnonRss, totalCpuTimeMillis, procState, newOomAdj, + oomAdjReason, uid); + mLastCompactionStats.remove(pid); + mLastCompactionStats.put(pid, memStats); + mCompactionStatsHistory.add(memStats); + if (!logFieldMetric) { + memStats.sendStat(); + } + } + + public void logCompactionDowngrade() { + ++mTotalCompactionDowngrades; + } + + public void logSystemCompactionPerformed(long memFreed) { + ++mSystemCompactionsPerformed; + mSystemTotalMemFreed += memFreed; + } + + public void logCompactionCancelled(CachedAppOptimizer.CancelCompactReason cancelReason) { + if (mTotalCompactionsCancelled.containsKey(cancelReason)) { + int count = mTotalCompactionsCancelled.get(cancelReason); + mTotalCompactionsCancelled.put(cancelReason, count + 1); + } else { + mTotalCompactionsCancelled.put(cancelReason, 1); + } + } + + private AggregatedProcessCompactionStats getPerProcessAggregatedCompactStat( + String processName) { + if (processName == null) { + processName = ""; + } + AggregatedProcessCompactionStats stats = mPerProcessCompactStats.get(processName); + if (stats == null) { + stats = new AggregatedProcessCompactionStats(processName); + mPerProcessCompactStats.put(processName, stats); + } + return stats; + } + + private AggregatedSourceCompactionStats getPerSourceAggregatedCompactStat( + CachedAppOptimizer.CompactSource source) { + AggregatedSourceCompactionStats stats = mPerSourceCompactStats.get(source); + if (stats == null) { + stats = new AggregatedSourceCompactionStats(source); + mPerSourceCompactStats.put(source, stats); + } + return stats; + } + + @NeverCompile + public void dump(PrintWriter pw) { + pw.println(" Per-Process Compaction Stats"); + long totalCompactPerformedSome = 0; + long totalCompactPerformedFull = 0; + for (AggregatedProcessCompactionStats stats : mPerProcessCompactStats.values()) { + pw.println("-----" + stats.mProcessName + "-----"); + totalCompactPerformedSome += stats.mSomeCompactPerformed; + totalCompactPerformedFull += stats.mFullCompactPerformed; + stats.dump(pw); + pw.println(); + } + pw.println(); + pw.println(" Per-Source Compaction Stats"); + for (AggregatedSourceCompactionStats stats : mPerSourceCompactStats.values()) { + pw.println("-----" + stats.mSourceType + "-----"); + stats.dump(pw); + pw.println(); + } + pw.println(); + + pw.println("Total Compactions Performed by profile: " + totalCompactPerformedSome + + " some, " + totalCompactPerformedFull + " full"); + pw.println("Total compactions downgraded: " + mTotalCompactionDowngrades); + pw.println("Total compactions cancelled by reason: "); + for (CachedAppOptimizer.CancelCompactReason reason : mTotalCompactionsCancelled.keySet()) { + pw.println(" " + reason + ": " + mTotalCompactionsCancelled.get(reason)); + } + pw.println(); + + pw.println(" System Compaction Memory Stats"); + pw.println(" Compactions Performed: " + mSystemCompactionsPerformed); + pw.println(" Total Memory Freed (KB): " + mSystemTotalMemFreed); + double avgKBsPerSystemCompact = mSystemCompactionsPerformed > 0 + ? mSystemTotalMemFreed / mSystemCompactionsPerformed + : 0; + pw.println(" Avg Mem Freed per Compact (KB): " + avgKBsPerSystemCompact); + pw.println(); + pw.println(" Tracking last compaction stats for " + mLastCompactionStats.size() + + " processes."); + pw.println("Last Compaction per process stats:"); + pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs" + + ",SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj," + + "oomAdjReason)"); + for (Map.Entry<Integer, SingleCompactionStats> entry : + mLastCompactionStats.entrySet()) { + SingleCompactionStats stats = entry.getValue(); + stats.dump(pw); + } + pw.println(); + pw.println("Last 20 Compactions Stats:"); + pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs," + + "SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj," + + "oomAdjReason)"); + for (SingleCompactionStats stats : mCompactionStatsHistory) { + stats.dump(pw); + } + } +} diff --git a/services/core/java/com/android/server/am/compaction/OWNERS b/services/core/java/com/android/server/am/compaction/OWNERS new file mode 100644 index 000000000000..54253b49daa3 --- /dev/null +++ b/services/core/java/com/android/server/am/compaction/OWNERS @@ -0,0 +1,5 @@ +edgararriaga@google.com +shayba@google.com + +# Fallback +include /PERFORMANCE_OWNERS diff --git a/services/core/java/com/android/server/am/compaction/SingleCompactionStats.java b/services/core/java/com/android/server/am/compaction/SingleCompactionStats.java new file mode 100644 index 000000000000..c20087ac973e --- /dev/null +++ b/services/core/java/com/android/server/am/compaction/SingleCompactionStats.java @@ -0,0 +1,94 @@ +/* + * 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.server.am.compaction; + +import android.app.ActivityManagerInternal; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.am.CachedAppOptimizer; +import com.android.server.am.OomAdjuster; + +import dalvik.annotation.optimization.NeverCompile; + +import java.io.PrintWriter; +import java.util.Random; + +public final class SingleCompactionStats { + private static final float STATSD_SAMPLE_RATE = 0.1f; + private static final Random mRandom = new Random(); + private final long[] mRssAfterCompaction; + public CachedAppOptimizer.CompactSource mSourceType; + public String mProcessName; + public final int mUid; + public long mDeltaAnonRssKBs; + public long mZramConsumedKBs; + public long mAnonMemFreedKBs; + public float mCpuTimeMillis; + public long mOrigAnonRss; + public int mProcState; + public int mOomAdj; + public @ActivityManagerInternal.OomAdjReason int mOomAdjReason; + + SingleCompactionStats(long[] rss, CachedAppOptimizer.CompactSource source, String processName, + long deltaAnonRss, long zramConsumed, long anonMemFreed, long origAnonRss, + long cpuTimeMillis, int procState, int oomAdj, + @ActivityManagerInternal.OomAdjReason int oomAdjReason, int uid) { + mRssAfterCompaction = rss; + mSourceType = source; + mProcessName = processName; + mUid = uid; + mDeltaAnonRssKBs = deltaAnonRss; + mZramConsumedKBs = zramConsumed; + mAnonMemFreedKBs = anonMemFreed; + mCpuTimeMillis = cpuTimeMillis; + mOrigAnonRss = origAnonRss; + mProcState = procState; + mOomAdj = oomAdj; + mOomAdjReason = oomAdjReason; + } + + double getCompactEfficiency() { return mAnonMemFreedKBs / (double) mOrigAnonRss; } + + double getSwapEfficiency() { return mDeltaAnonRssKBs / (double) mOrigAnonRss; } + + double getCompactCost() { + // mCpuTimeMillis / (anonMemFreedKBs/1024) and metric is in (ms/MB) + return mCpuTimeMillis / (double) mAnonMemFreedKBs * 1024; + } + + public long[] getRssAfterCompaction() { + return mRssAfterCompaction; + } + + @NeverCompile + void dump(PrintWriter pw) { + pw.println(" (" + mProcessName + "," + mSourceType.name() + "," + mDeltaAnonRssKBs + + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + "," + + getSwapEfficiency() + "," + getCompactEfficiency() + + "," + getCompactCost() + "," + mProcState + "," + mOomAdj + "," + + OomAdjuster.oomAdjReasonToString(mOomAdjReason) + ")"); + } + + void sendStat() { + if (mRandom.nextFloat() < STATSD_SAMPLE_RATE) { + FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPACTED_V2, mUid, mProcState, + mOomAdj, mDeltaAnonRssKBs, mZramConsumedKBs, mCpuTimeMillis, mOrigAnonRss, + mOomAdjReason); + } + } +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java index d203de537b81..fa5847560782 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java @@ -142,6 +142,9 @@ public final class CachedAppOptimizerTest { }, mProcessDependencies); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); + + mCachedAppOptimizerUnderTest.init(); + mCachedAppOptimizerUnderTest.mCompactStatsManager.reinit(); } @After @@ -168,7 +171,6 @@ public final class CachedAppOptimizerTest { @Test public void init_setsDefaults() { - mCachedAppOptimizerUnderTest.init(); synchronized (mCachedAppOptimizerUnderTest.mPhenotypeFlagLock) { assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo( CachedAppOptimizer.DEFAULT_USE_COMPACTION); @@ -304,7 +306,6 @@ public final class CachedAppOptimizerTest { assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo( CachedAppOptimizer.DEFAULT_USE_COMPACTION); // When we call init and change some the flag value... - mCachedAppOptimizerUnderTest.init(); mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_USE_COMPACTION, "true", false); @@ -331,7 +332,6 @@ public final class CachedAppOptimizerTest { // The freezer DeviceConfig property is read at boot only DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, CachedAppOptimizer.KEY_USE_FREEZER, "true", false); - mCachedAppOptimizerUnderTest.init(); assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue(); mCountDown = new CountDownLatch(1); @@ -363,7 +363,6 @@ public final class CachedAppOptimizerTest { public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException { assertThat(mCachedAppOptimizerUnderTest.useCompaction()).isEqualTo( CachedAppOptimizer.DEFAULT_USE_COMPACTION); - mCachedAppOptimizerUnderTest.init(); // When we push an invalid flag value... mCountDown = new CountDownLatch(1); @@ -392,8 +391,6 @@ public final class CachedAppOptimizerTest { @Test public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When we override new reasonable throttle values after init... mCountDown = new CountDownLatch(8); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -440,8 +437,6 @@ public final class CachedAppOptimizerTest { @Test public void compactThrottle_listensToDeviceConfigChangesBadValues() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When one of the throttles is overridden with a bad value... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -526,8 +521,6 @@ public final class CachedAppOptimizerTest { @Test public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When we override mCompactStatsdSampleRate with a reasonable value ... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -554,8 +547,6 @@ public final class CachedAppOptimizerTest { @Test public void statsdSampleRate_listensToDeviceConfigChangesBadValues() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When we override mCompactStatsdSampleRate with an unreasonable value ... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -580,8 +571,6 @@ public final class CachedAppOptimizerTest { @Test public void statsdSampleRate_listensToDeviceConfigChangesOutOfRangeValues() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When we override mCompactStatsdSampleRate with an value outside of [0..1]... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -624,8 +613,6 @@ public final class CachedAppOptimizerTest { @Test public void fullCompactionRssThrottleKb_listensToDeviceConfigChanges() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When we override mStatsdSampleRate with a reasonable value ... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -641,8 +628,6 @@ public final class CachedAppOptimizerTest { @Test public void fullCompactionRssThrottleKb_listensToDeviceConfigChangesBadValues() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When we override mStatsdSampleRate with an unreasonable value ... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -666,8 +651,6 @@ public final class CachedAppOptimizerTest { @Test public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChanges() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When we override mStatsdSampleRate with a reasonable value ... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -684,8 +667,6 @@ public final class CachedAppOptimizerTest { @Test public void fullCompactionDeltaRssThrottleKb_listensToDeviceConfigChangesBadValues() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); - // When we override mStatsdSampleRate with an unreasonable value ... mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -709,7 +690,6 @@ public final class CachedAppOptimizerTest { @Test public void procStateThrottle_listensToDeviceConfigChanges() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); mCountDown = new CountDownLatch(1); DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false); @@ -726,7 +706,6 @@ public final class CachedAppOptimizerTest { @Test public void procStateThrottle_listensToDeviceConfigChangesBadValues() throws InterruptedException { - mCachedAppOptimizerUnderTest.init(); Set<Integer> expected = new HashSet<>(); for (String s : TextUtils.split( @@ -774,7 +753,6 @@ public final class CachedAppOptimizerTest { public void processWithDeltaRSSTooSmall_notFullCompacted() throws Exception { // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS // throttle to 12000. - mCachedAppOptimizerUnderTest.init(); setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true); setFlag(CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "12000", false); initActivityManagerService(); @@ -810,9 +788,10 @@ public final class CachedAppOptimizerTest { false); waitForHandler(); // THEN process IS compacted. - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); - valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( - pid).getRssAfterCompaction(); + assertThat(mCachedAppOptimizerUnderTest.mCompactStatsManager.getLastCompactionStats(pid)) + .isNotNull(); + valuesAfter = mCachedAppOptimizerUnderTest.mCompactStatsManager.getLastCompactionStats(pid) + .getRssAfterCompaction(); assertThat(valuesAfter).isEqualTo(rssAfter1); // WHEN delta is below threshold (500). @@ -828,9 +807,10 @@ public final class CachedAppOptimizerTest { waitForHandler(); // THEN process IS NOT compacted - values after compaction for process 1 should remain the // same as from the last compaction. - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); - valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( - pid).getRssAfterCompaction(); + assertThat(mCachedAppOptimizerUnderTest.mCompactStatsManager. + getLastCompactionStats(pid)).isNotNull(); + valuesAfter = mCachedAppOptimizerUnderTest.mCompactStatsManager. + getLastCompactionStats(pid).getRssAfterCompaction(); assertThat(valuesAfter).isEqualTo(rssAfter1); // WHEN delta is above threshold (13000). @@ -845,9 +825,10 @@ public final class CachedAppOptimizerTest { false); waitForHandler(); // THEN process IS compacted - values after compaction for process 1 should be updated. - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); - valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( - pid).getRssAfterCompaction(); + assertThat(mCachedAppOptimizerUnderTest. + mCompactStatsManager.getLastCompactionStats(pid)).isNotNull(); + valuesAfter = mCachedAppOptimizerUnderTest. + mCompactStatsManager.getLastCompactionStats(pid).getRssAfterCompaction(); assertThat(valuesAfter).isEqualTo(rssAfter3); } @@ -856,7 +837,7 @@ public final class CachedAppOptimizerTest { public void processWithAnonRSSTooSmall_notFullCompacted() throws Exception { // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS // throttle to 8000. - mCachedAppOptimizerUnderTest.init(); + setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true); setFlag(CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "8000", false); initActivityManagerService(); @@ -888,7 +869,8 @@ public final class CachedAppOptimizerTest { false); waitForHandler(); // THEN process IS NOT compacted. - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull(); + assertThat(mCachedAppOptimizerUnderTest. + mCompactStatsManager.getLastCompactionStats(pid)).isNull(); // GIVEN we simulate RSS memory before above threshold. mProcessDependencies.setRss(rssAboveThreshold); @@ -899,9 +881,10 @@ public final class CachedAppOptimizerTest { false); waitForHandler(); // THEN process IS compacted. - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); - long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get( - pid).getRssAfterCompaction(); + assertThat(mCachedAppOptimizerUnderTest. + mCompactStatsManager.getLastCompactionStats(pid)).isNotNull(); + long[] valuesAfter = mCachedAppOptimizerUnderTest.mCompactStatsManager. + getLastCompactionStats(pid).getRssAfterCompaction(); assertThat(valuesAfter).isEqualTo(rssAboveThresholdAfter); } @@ -910,7 +893,6 @@ public final class CachedAppOptimizerTest { public void processWithOomAdjTooSmall_notFullCompacted() throws Exception { // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set Min and // Max OOM_Adj throttles. - mCachedAppOptimizerUnderTest.init(); setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true); setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, Long.toString(920), true); setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, Long.toString(950), true); @@ -934,9 +916,10 @@ public final class CachedAppOptimizerTest { mCachedAppOptimizerUnderTest.onProcessFrozen(processRecord); waitForHandler(); // THEN process IS compacted. - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); - long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats - .get(pid) + assertThat(mCachedAppOptimizerUnderTest.mCompactStatsManager + .getLastCompactionStats(pid)).isNotNull(); + long[] valuesAfter = mCachedAppOptimizerUnderTest.mCompactStatsManager + .getLastCompactionStats(pid) .getRssAfterCompaction(); assertThat(valuesAfter).isEqualTo(rssAfter); } @@ -944,7 +927,7 @@ public final class CachedAppOptimizerTest { @SuppressWarnings("GuardedBy") @Test public void process_forceCompacted() throws Exception { - mCachedAppOptimizerUnderTest.init(); + setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true); setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MIN_OOM_ADJ, Long.toString(920), true); setFlag(CachedAppOptimizer.KEY_COMPACT_THROTTLE_MAX_OOM_ADJ, Long.toString(950), true); @@ -970,7 +953,8 @@ public final class CachedAppOptimizerTest { false); waitForHandler(); // the process is not compacted - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull(); + assertThat(mCachedAppOptimizerUnderTest.mCompactStatsManager. + getLastCompactionStats(pid)).isNull(); // Compact process some mCachedAppOptimizerUnderTest.compactApp(processRecord, @@ -978,7 +962,8 @@ public final class CachedAppOptimizerTest { false); waitForHandler(); // the process is not compacted - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull(); + assertThat(mCachedAppOptimizerUnderTest.mCompactStatsManager + .getLastCompactionStats(pid)).isNull(); processRecord.mState.setSetAdj(100); processRecord.mState.setCurAdj(100); @@ -989,9 +974,10 @@ public final class CachedAppOptimizerTest { true); waitForHandler(); // then process is compacted. - assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull(); + assertThat(mCachedAppOptimizerUnderTest + .mCompactStatsManager.getLastCompactionStats(pid)).isNotNull(); - mCachedAppOptimizerUnderTest.mLastCompactionStats.clear(); + mCachedAppOptimizerUnderTest.mCompactStatsManager.getLastCompactionStats().clear(); if (CachedAppOptimizer.ENABLE_SHARED_AND_CODE_COMPACT) { // We force a some compaction |