1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.am; 18 19 import static android.os.Process.THREAD_PRIORITY_FOREGROUND; 20 21 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_COMPACTION; 22 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 23 24 import android.app.ActivityManager; 25 import android.app.ActivityThread; 26 import android.os.Debug; 27 import android.os.Handler; 28 import android.os.Message; 29 import android.os.Process; 30 import android.os.SystemClock; 31 import android.os.Trace; 32 import android.provider.DeviceConfig; 33 import android.provider.DeviceConfig.OnPropertiesChangedListener; 34 import android.provider.DeviceConfig.Properties; 35 import android.text.TextUtils; 36 import android.util.EventLog; 37 import android.util.Slog; 38 import android.util.StatsLog; 39 40 import com.android.internal.annotations.GuardedBy; 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.server.ServiceThread; 43 44 import java.io.FileOutputStream; 45 import java.io.PrintWriter; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.HashSet; 49 import java.util.LinkedHashMap; 50 import java.util.Map; 51 import java.util.Random; 52 import java.util.Set; 53 54 public final class AppCompactor { 55 56 // Flags stored in the DeviceConfig API. 57 @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction"; 58 @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1"; 59 @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2"; 60 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1"; 61 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2"; 62 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3"; 63 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; 64 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5"; 65 @VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6"; 66 @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE = 67 "compact_statsd_sample_rate"; 68 @VisibleForTesting static final String KEY_COMPACT_FULL_RSS_THROTTLE_KB = 69 "compact_full_rss_throttle_kb"; 70 @VisibleForTesting static final String KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB = 71 "compact_full_delta_rss_throttle_kb"; 72 @VisibleForTesting static final String KEY_COMPACT_PROC_STATE_THROTTLE = 73 "compact_proc_state_throttle"; 74 75 // Phenotype sends int configurations and we map them to the strings we'll use on device, 76 // preventing a weird string value entering the kernel. 77 private static final int COMPACT_ACTION_FILE_FLAG = 1; 78 private static final int COMPACT_ACTION_ANON_FLAG = 2; 79 private static final int COMPACT_ACTION_FULL_FLAG = 3; 80 private static final int COMPACT_ACTION_NONE_FLAG = 4; 81 private static final String COMPACT_ACTION_NONE = ""; 82 private static final String COMPACT_ACTION_FILE = "file"; 83 private static final String COMPACT_ACTION_ANON = "anon"; 84 private static final String COMPACT_ACTION_FULL = "all"; 85 86 // Defaults for phenotype flags. 87 @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false; 88 @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE_FLAG; 89 @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL_FLAG; 90 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000; 91 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000; 92 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500; 93 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000; 94 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000; 95 @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000; 96 // The sampling rate to push app compaction events into statsd for upload. 97 @VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f; 98 @VisibleForTesting static final long DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB = 12_000L; 99 @VisibleForTesting static final long DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB = 8_000L; 100 // Format of this string should be a comma separated list of integers. 101 @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE = 102 String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER); 103 104 @VisibleForTesting 105 interface PropertyChangedCallbackForTest { onPropertyChanged()106 void onPropertyChanged(); 107 } 108 private PropertyChangedCallbackForTest mTestCallback; 109 110 // Handler constants. 111 static final int COMPACT_PROCESS_SOME = 1; 112 static final int COMPACT_PROCESS_FULL = 2; 113 static final int COMPACT_PROCESS_PERSISTENT = 3; 114 static final int COMPACT_PROCESS_BFGS = 4; 115 static final int COMPACT_PROCESS_MSG = 1; 116 static final int COMPACT_SYSTEM_MSG = 2; 117 118 /** 119 * This thread must be moved to the system background cpuset. 120 * If that doesn't happen, it's probably going to draw a lot of power. 121 * However, this has to happen after the first updateOomAdjLocked, because 122 * that will wipe out the cpuset assignment for system_server threads. 123 * Accordingly, this is in the AMS constructor. 124 */ 125 final ServiceThread mCompactionThread; 126 127 private final ArrayList<ProcessRecord> mPendingCompactionProcesses = 128 new ArrayList<ProcessRecord>(); 129 private final ActivityManagerService mAm; 130 private final OnPropertiesChangedListener mOnFlagsChangedListener = 131 new OnPropertiesChangedListener() { 132 @Override 133 public void onPropertiesChanged(Properties properties) { 134 synchronized (mPhenotypeFlagLock) { 135 for (String name : properties.getKeyset()) { 136 if (KEY_USE_COMPACTION.equals(name)) { 137 updateUseCompaction(); 138 } else if (KEY_COMPACT_ACTION_1.equals(name) 139 || KEY_COMPACT_ACTION_2.equals(name)) { 140 updateCompactionActions(); 141 } else if (KEY_COMPACT_THROTTLE_1.equals(name) 142 || KEY_COMPACT_THROTTLE_2.equals(name) 143 || KEY_COMPACT_THROTTLE_3.equals(name) 144 || KEY_COMPACT_THROTTLE_4.equals(name)) { 145 updateCompactionThrottles(); 146 } else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) { 147 updateStatsdSampleRate(); 148 } else if (KEY_COMPACT_FULL_RSS_THROTTLE_KB.equals(name)) { 149 updateFullRssThrottle(); 150 } else if (KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB.equals(name)) { 151 updateFullDeltaRssThrottle(); 152 } else if (KEY_COMPACT_PROC_STATE_THROTTLE.equals(name)) { 153 updateProcStateThrottle(); 154 } 155 } 156 } 157 if (mTestCallback != null) { 158 mTestCallback.onPropertyChanged(); 159 } 160 } 161 }; 162 163 private final Object mPhenotypeFlagLock = new Object(); 164 165 // Configured by phenotype. Updates from the server take effect immediately. 166 @GuardedBy("mPhenotypeFlagLock") 167 @VisibleForTesting volatile String mCompactActionSome = 168 compactActionIntToString(DEFAULT_COMPACT_ACTION_1); 169 @GuardedBy("mPhenotypeFlagLock") 170 @VisibleForTesting volatile String mCompactActionFull = 171 compactActionIntToString(DEFAULT_COMPACT_ACTION_2); 172 @GuardedBy("mPhenotypeFlagLock") 173 @VisibleForTesting volatile long mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1; 174 @GuardedBy("mPhenotypeFlagLock") 175 @VisibleForTesting volatile long mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2; 176 @GuardedBy("mPhenotypeFlagLock") 177 @VisibleForTesting volatile long mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3; 178 @GuardedBy("mPhenotypeFlagLock") 179 @VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4; 180 @GuardedBy("mPhenotypeFlagLock") 181 @VisibleForTesting volatile long mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5; 182 @GuardedBy("mPhenotypeFlagLock") 183 @VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; 184 @GuardedBy("mPhenotypeFlagLock") 185 private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION; 186 private final Random mRandom = new Random(); 187 @GuardedBy("mPhenotypeFlagLock") 188 @VisibleForTesting volatile float mStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE; 189 @GuardedBy("mPhenotypeFlagLock") 190 @VisibleForTesting volatile long mFullAnonRssThrottleKb = 191 DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB; 192 @GuardedBy("mPhenoypeFlagLock") 193 @VisibleForTesting volatile long mFullDeltaRssThrottleKb = 194 DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB; 195 @GuardedBy("mPhenoypeFlagLock") 196 @VisibleForTesting final Set<Integer> mProcStateThrottle; 197 198 // Handler on which compaction runs. 199 private Handler mCompactionHandler; 200 201 // Maps process ID to last compaction statistics for processes that we've fully compacted. Used 202 // when evaluating throttles that we only consider for "full" compaction, so we don't store 203 // data for "some" compactions. 204 private Map<Integer, LastCompactionStats> mLastCompactionStats = 205 new LinkedHashMap<Integer, LastCompactionStats>() { 206 @Override 207 protected boolean removeEldestEntry(Map.Entry eldest) { 208 return size() > 100; 209 } 210 }; 211 212 private int mSomeCompactionCount; 213 private int mFullCompactionCount; 214 private int mPersistentCompactionCount; 215 private int mBfgsCompactionCount; 216 AppCompactor(ActivityManagerService am)217 public AppCompactor(ActivityManagerService am) { 218 mAm = am; 219 mCompactionThread = new ServiceThread("CompactionThread", 220 THREAD_PRIORITY_FOREGROUND, true); 221 mProcStateThrottle = new HashSet<>(); 222 } 223 224 @VisibleForTesting AppCompactor(ActivityManagerService am, PropertyChangedCallbackForTest callback)225 AppCompactor(ActivityManagerService am, PropertyChangedCallbackForTest callback) { 226 this(am); 227 mTestCallback = callback; 228 } 229 230 /** 231 * Reads phenotype config to determine whether app compaction is enabled or not and 232 * starts the background thread if necessary. 233 */ init()234 public void init() { 235 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 236 ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener); 237 synchronized (mPhenotypeFlagLock) { 238 updateUseCompaction(); 239 updateCompactionActions(); 240 updateCompactionThrottles(); 241 updateStatsdSampleRate(); 242 updateFullRssThrottle(); 243 updateFullDeltaRssThrottle(); 244 updateProcStateThrottle(); 245 } 246 Process.setThreadGroupAndCpuset(mCompactionThread.getThreadId(), 247 Process.THREAD_GROUP_SYSTEM); 248 } 249 250 /** 251 * Returns whether compaction is enabled. 252 */ useCompaction()253 public boolean useCompaction() { 254 synchronized (mPhenotypeFlagLock) { 255 return mUseCompaction; 256 } 257 } 258 259 @GuardedBy("mAm") dump(PrintWriter pw)260 void dump(PrintWriter pw) { 261 pw.println("AppCompactor settings"); 262 synchronized (mPhenotypeFlagLock) { 263 pw.println(" " + KEY_USE_COMPACTION + "=" + mUseCompaction); 264 pw.println(" " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome); 265 pw.println(" " + KEY_COMPACT_ACTION_2 + "=" + mCompactActionFull); 266 pw.println(" " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome); 267 pw.println(" " + KEY_COMPACT_THROTTLE_2 + "=" + mCompactThrottleSomeFull); 268 pw.println(" " + KEY_COMPACT_THROTTLE_3 + "=" + mCompactThrottleFullSome); 269 pw.println(" " + KEY_COMPACT_THROTTLE_4 + "=" + mCompactThrottleFullFull); 270 pw.println(" " + KEY_COMPACT_THROTTLE_5 + "=" + mCompactThrottleBFGS); 271 pw.println(" " + KEY_COMPACT_THROTTLE_6 + "=" + mCompactThrottlePersistent); 272 pw.println(" " + KEY_COMPACT_STATSD_SAMPLE_RATE + "=" + mStatsdSampleRate); 273 pw.println(" " + KEY_COMPACT_FULL_RSS_THROTTLE_KB + "=" 274 + mFullAnonRssThrottleKb); 275 pw.println(" " + KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB + "=" 276 + mFullDeltaRssThrottleKb); 277 pw.println(" " + KEY_COMPACT_PROC_STATE_THROTTLE + "=" 278 + Arrays.toString(mProcStateThrottle.toArray(new Integer[0]))); 279 280 pw.println(" " + mSomeCompactionCount + " some, " + mFullCompactionCount 281 + " full, " + mPersistentCompactionCount + " persistent, " 282 + mBfgsCompactionCount + " BFGS compactions."); 283 284 pw.println(" Tracking last compaction stats for " + mLastCompactionStats.size() 285 + " processes."); 286 if (DEBUG_COMPACTION) { 287 for (Map.Entry<Integer, LastCompactionStats> entry 288 : mLastCompactionStats.entrySet()) { 289 int pid = entry.getKey(); 290 LastCompactionStats stats = entry.getValue(); 291 pw.println(" " + pid + ": " 292 + Arrays.toString(stats.getRssAfterCompaction())); 293 } 294 } 295 } 296 } 297 298 @GuardedBy("mAm") compactAppSome(ProcessRecord app)299 void compactAppSome(ProcessRecord app) { 300 app.reqCompactAction = COMPACT_PROCESS_SOME; 301 mPendingCompactionProcesses.add(app); 302 mCompactionHandler.sendMessage( 303 mCompactionHandler.obtainMessage( 304 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState)); 305 } 306 307 @GuardedBy("mAm") compactAppFull(ProcessRecord app)308 void compactAppFull(ProcessRecord app) { 309 app.reqCompactAction = COMPACT_PROCESS_FULL; 310 mPendingCompactionProcesses.add(app); 311 mCompactionHandler.sendMessage( 312 mCompactionHandler.obtainMessage( 313 COMPACT_PROCESS_MSG, app.setAdj, app.setProcState)); 314 315 } 316 317 @GuardedBy("mAm") compactAppPersistent(ProcessRecord app)318 void compactAppPersistent(ProcessRecord app) { 319 app.reqCompactAction = COMPACT_PROCESS_PERSISTENT; 320 mPendingCompactionProcesses.add(app); 321 mCompactionHandler.sendMessage( 322 mCompactionHandler.obtainMessage( 323 COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); 324 } 325 326 @GuardedBy("mAm") shouldCompactPersistent(ProcessRecord app, long now)327 boolean shouldCompactPersistent(ProcessRecord app, long now) { 328 return (app.lastCompactTime == 0 329 || (now - app.lastCompactTime) > mCompactThrottlePersistent); 330 } 331 332 @GuardedBy("mAm") compactAppBfgs(ProcessRecord app)333 void compactAppBfgs(ProcessRecord app) { 334 app.reqCompactAction = COMPACT_PROCESS_BFGS; 335 mPendingCompactionProcesses.add(app); 336 mCompactionHandler.sendMessage( 337 mCompactionHandler.obtainMessage( 338 COMPACT_PROCESS_MSG, app.curAdj, app.setProcState)); 339 } 340 341 @GuardedBy("mAm") shouldCompactBFGS(ProcessRecord app, long now)342 boolean shouldCompactBFGS(ProcessRecord app, long now) { 343 return (app.lastCompactTime == 0 344 || (now - app.lastCompactTime) > mCompactThrottleBFGS); 345 } 346 347 @GuardedBy("mAm") compactAllSystem()348 void compactAllSystem() { 349 if (mUseCompaction) { 350 mCompactionHandler.sendMessage(mCompactionHandler.obtainMessage( 351 COMPACT_SYSTEM_MSG)); 352 } 353 } 354 compactSystem()355 private native void compactSystem(); 356 357 /** 358 * Reads the flag value from DeviceConfig to determine whether app compaction 359 * should be enabled, and starts the compaction thread if needed. 360 */ 361 @GuardedBy("mPhenotypeFlagLock") updateUseCompaction()362 private void updateUseCompaction() { 363 mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 364 KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION); 365 if (mUseCompaction && !mCompactionThread.isAlive()) { 366 mCompactionThread.start(); 367 mCompactionHandler = new MemCompactionHandler(); 368 } 369 } 370 371 @GuardedBy("mPhenotypeFlagLock") updateCompactionActions()372 private void updateCompactionActions() { 373 int compactAction1 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 374 KEY_COMPACT_ACTION_1, DEFAULT_COMPACT_ACTION_1); 375 376 int compactAction2 = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 377 KEY_COMPACT_ACTION_2, DEFAULT_COMPACT_ACTION_2); 378 379 mCompactActionSome = compactActionIntToString(compactAction1); 380 mCompactActionFull = compactActionIntToString(compactAction2); 381 } 382 383 @GuardedBy("mPhenotypeFlagLock") updateCompactionThrottles()384 private void updateCompactionThrottles() { 385 boolean useThrottleDefaults = false; 386 String throttleSomeSomeFlag = 387 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 388 KEY_COMPACT_THROTTLE_1); 389 String throttleSomeFullFlag = 390 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 391 KEY_COMPACT_THROTTLE_2); 392 String throttleFullSomeFlag = 393 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 394 KEY_COMPACT_THROTTLE_3); 395 String throttleFullFullFlag = 396 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 397 KEY_COMPACT_THROTTLE_4); 398 String throttleBFGSFlag = 399 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 400 KEY_COMPACT_THROTTLE_5); 401 String throttlePersistentFlag = 402 DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 403 KEY_COMPACT_THROTTLE_6); 404 405 if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag) 406 || TextUtils.isEmpty(throttleFullSomeFlag) 407 || TextUtils.isEmpty(throttleFullFullFlag) 408 || TextUtils.isEmpty(throttleBFGSFlag) 409 || TextUtils.isEmpty(throttlePersistentFlag)) { 410 // Set defaults for all if any are not set. 411 useThrottleDefaults = true; 412 } else { 413 try { 414 mCompactThrottleSomeSome = Integer.parseInt(throttleSomeSomeFlag); 415 mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag); 416 mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag); 417 mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag); 418 mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag); 419 mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag); 420 } catch (NumberFormatException e) { 421 useThrottleDefaults = true; 422 } 423 } 424 425 if (useThrottleDefaults) { 426 mCompactThrottleSomeSome = DEFAULT_COMPACT_THROTTLE_1; 427 mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2; 428 mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3; 429 mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4; 430 mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5; 431 mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6; 432 } 433 } 434 435 @GuardedBy("mPhenotypeFlagLock") updateStatsdSampleRate()436 private void updateStatsdSampleRate() { 437 mStatsdSampleRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 438 KEY_COMPACT_STATSD_SAMPLE_RATE, DEFAULT_STATSD_SAMPLE_RATE); 439 mStatsdSampleRate = Math.min(1.0f, Math.max(0.0f, mStatsdSampleRate)); 440 } 441 442 @GuardedBy("mPhenotypeFlagLock") updateFullRssThrottle()443 private void updateFullRssThrottle() { 444 mFullAnonRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 445 KEY_COMPACT_FULL_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB); 446 447 // Don't allow negative values. 0 means don't apply the throttle. 448 if (mFullAnonRssThrottleKb < 0) { 449 mFullAnonRssThrottleKb = DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB; 450 } 451 } 452 453 @GuardedBy("mPhenotypeFlagLock") updateFullDeltaRssThrottle()454 private void updateFullDeltaRssThrottle() { 455 mFullDeltaRssThrottleKb = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, 456 KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB); 457 458 if (mFullDeltaRssThrottleKb < 0) { 459 mFullDeltaRssThrottleKb = DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB; 460 } 461 } 462 463 @GuardedBy("mPhenotypeFlagLock") updateProcStateThrottle()464 private void updateProcStateThrottle() { 465 String procStateThrottleString = DeviceConfig.getString( 466 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_PROC_STATE_THROTTLE, 467 DEFAULT_COMPACT_PROC_STATE_THROTTLE); 468 if (!parseProcStateThrottle(procStateThrottleString)) { 469 Slog.w(TAG_AM, "Unable to parse app compact proc state throttle \"" 470 + procStateThrottleString + "\" falling back to default."); 471 if (!parseProcStateThrottle(DEFAULT_COMPACT_PROC_STATE_THROTTLE)) { 472 Slog.wtf(TAG_AM, 473 "Unable to parse default app compact proc state throttle " 474 + DEFAULT_COMPACT_PROC_STATE_THROTTLE); 475 } 476 } 477 } 478 parseProcStateThrottle(String procStateThrottleString)479 private boolean parseProcStateThrottle(String procStateThrottleString) { 480 String[] procStates = TextUtils.split(procStateThrottleString, ","); 481 mProcStateThrottle.clear(); 482 for (String procState : procStates) { 483 try { 484 mProcStateThrottle.add(Integer.parseInt(procState)); 485 } catch (NumberFormatException e) { 486 Slog.e(TAG_AM, "Failed to parse default app compaction proc state: " 487 + procState); 488 return false; 489 } 490 } 491 return true; 492 } 493 494 @VisibleForTesting compactActionIntToString(int action)495 static String compactActionIntToString(int action) { 496 switch(action) { 497 case COMPACT_ACTION_NONE_FLAG: 498 return COMPACT_ACTION_NONE; 499 case COMPACT_ACTION_FILE_FLAG: 500 return COMPACT_ACTION_FILE; 501 case COMPACT_ACTION_ANON_FLAG: 502 return COMPACT_ACTION_ANON; 503 case COMPACT_ACTION_FULL_FLAG: 504 return COMPACT_ACTION_FULL; 505 default: 506 return COMPACT_ACTION_NONE; 507 } 508 } 509 510 private static final class LastCompactionStats { 511 private final long[] mRssAfterCompaction; 512 LastCompactionStats(long[] rss)513 LastCompactionStats(long[] rss) { 514 mRssAfterCompaction = rss; 515 } 516 getRssAfterCompaction()517 long[] getRssAfterCompaction() { 518 return mRssAfterCompaction; 519 } 520 } 521 522 private final class MemCompactionHandler extends Handler { MemCompactionHandler()523 private MemCompactionHandler() { 524 super(mCompactionThread.getLooper()); 525 } 526 527 @Override handleMessage(Message msg)528 public void handleMessage(Message msg) { 529 switch (msg.what) { 530 case COMPACT_PROCESS_MSG: { 531 long start = SystemClock.uptimeMillis(); 532 ProcessRecord proc; 533 int pid; 534 String action; 535 final String name; 536 int pendingAction, lastCompactAction; 537 long lastCompactTime; 538 LastCompactionStats lastCompactionStats; 539 int lastOomAdj = msg.arg1; 540 int procState = msg.arg2; 541 synchronized (mAm) { 542 proc = mPendingCompactionProcesses.remove(0); 543 544 pendingAction = proc.reqCompactAction; 545 pid = proc.pid; 546 name = proc.processName; 547 548 // don't compact if the process has returned to perceptible 549 // and this is only a cached/home/prev compaction 550 if ((pendingAction == COMPACT_PROCESS_SOME 551 || pendingAction == COMPACT_PROCESS_FULL) 552 && (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) { 553 if (DEBUG_COMPACTION) { 554 Slog.d(TAG_AM, 555 "Skipping compaction as process " + name + " is " 556 + "now perceptible."); 557 } 558 return; 559 } 560 561 lastCompactAction = proc.lastCompactAction; 562 lastCompactTime = proc.lastCompactTime; 563 // remove rather than get so that insertion order will be updated when we 564 // put the post-compaction stats back into the map. 565 lastCompactionStats = mLastCompactionStats.remove(pid); 566 } 567 568 if (pid == 0) { 569 // not a real process, either one being launched or one being killed 570 return; 571 } 572 573 // basic throttling 574 // use the Phenotype flag knobs to determine whether current/prevous 575 // compaction combo should be throtted or not 576 577 // Note that we explicitly don't take mPhenotypeFlagLock here as the flags 578 // should very seldom change, and taking the risk of using the wrong action is 579 // preferable to taking the lock for every single compaction action. 580 if (lastCompactTime != 0) { 581 if (pendingAction == COMPACT_PROCESS_SOME) { 582 if ((lastCompactAction == COMPACT_PROCESS_SOME 583 && (start - lastCompactTime < mCompactThrottleSomeSome)) 584 || (lastCompactAction == COMPACT_PROCESS_FULL 585 && (start - lastCompactTime 586 < mCompactThrottleSomeFull))) { 587 if (DEBUG_COMPACTION) { 588 Slog.d(TAG_AM, "Skipping some compaction for " + name 589 + ": too soon. throttle=" + mCompactThrottleSomeSome 590 + "/" + mCompactThrottleSomeFull + " last=" 591 + (start - lastCompactTime) + "ms ago"); 592 } 593 return; 594 } 595 } else if (pendingAction == COMPACT_PROCESS_FULL) { 596 if ((lastCompactAction == COMPACT_PROCESS_SOME 597 && (start - lastCompactTime < mCompactThrottleFullSome)) 598 || (lastCompactAction == COMPACT_PROCESS_FULL 599 && (start - lastCompactTime 600 < mCompactThrottleFullFull))) { 601 if (DEBUG_COMPACTION) { 602 Slog.d(TAG_AM, "Skipping full compaction for " + name 603 + ": too soon. throttle=" + mCompactThrottleFullSome 604 + "/" + mCompactThrottleFullFull + " last=" 605 + (start - lastCompactTime) + "ms ago"); 606 } 607 return; 608 } 609 } else if (pendingAction == COMPACT_PROCESS_PERSISTENT) { 610 if (start - lastCompactTime < mCompactThrottlePersistent) { 611 if (DEBUG_COMPACTION) { 612 Slog.d(TAG_AM, "Skipping persistent compaction for " + name 613 + ": too soon. throttle=" + mCompactThrottlePersistent 614 + " last=" + (start - lastCompactTime) + "ms ago"); 615 } 616 return; 617 } 618 } else if (pendingAction == COMPACT_PROCESS_BFGS) { 619 if (start - lastCompactTime < mCompactThrottleBFGS) { 620 if (DEBUG_COMPACTION) { 621 Slog.d(TAG_AM, "Skipping bfgs compaction for " + name 622 + ": too soon. throttle=" + mCompactThrottleBFGS 623 + " last=" + (start - lastCompactTime) + "ms ago"); 624 } 625 return; 626 } 627 } 628 } 629 630 switch (pendingAction) { 631 case COMPACT_PROCESS_SOME: 632 action = mCompactActionSome; 633 break; 634 // For the time being, treat these as equivalent. 635 case COMPACT_PROCESS_FULL: 636 case COMPACT_PROCESS_PERSISTENT: 637 case COMPACT_PROCESS_BFGS: 638 action = mCompactActionFull; 639 break; 640 default: 641 action = COMPACT_ACTION_NONE; 642 break; 643 } 644 645 if (COMPACT_ACTION_NONE.equals(action)) { 646 return; 647 } 648 649 if (mProcStateThrottle.contains(procState)) { 650 if (DEBUG_COMPACTION) { 651 Slog.d(TAG_AM, "Skipping full compaction for process " + name 652 + "; proc state is " + procState); 653 } 654 return; 655 } 656 657 long[] rssBefore = Process.getRss(pid); 658 long anonRssBefore = rssBefore[2]; 659 660 if (rssBefore[0] == 0 && rssBefore[1] == 0 && rssBefore[2] == 0 661 && rssBefore[3] == 0) { 662 if (DEBUG_COMPACTION) { 663 Slog.d(TAG_AM, "Skipping compaction for" + "process " + pid 664 + " with no memory usage. Dead?"); 665 } 666 return; 667 } 668 669 if (action.equals(COMPACT_ACTION_FULL) || action.equals(COMPACT_ACTION_ANON)) { 670 if (mFullAnonRssThrottleKb > 0L 671 && anonRssBefore < mFullAnonRssThrottleKb) { 672 if (DEBUG_COMPACTION) { 673 Slog.d(TAG_AM, "Skipping full compaction for process " 674 + name + "; anon RSS is too small: " + anonRssBefore 675 + "KB."); 676 } 677 return; 678 } 679 680 if (lastCompactionStats != null && mFullDeltaRssThrottleKb > 0L) { 681 long[] lastRss = lastCompactionStats.getRssAfterCompaction(); 682 long absDelta = Math.abs(rssBefore[1] - lastRss[1]) 683 + Math.abs(rssBefore[2] - lastRss[2]) 684 + Math.abs(rssBefore[3] - lastRss[3]); 685 if (absDelta <= mFullDeltaRssThrottleKb) { 686 if (DEBUG_COMPACTION) { 687 Slog.d(TAG_AM, "Skipping full compaction for process " 688 + name + "; abs delta is too small: " + absDelta 689 + "KB."); 690 } 691 return; 692 } 693 } 694 } 695 696 // Now we've passed through all the throttles and are going to compact, update 697 // bookkeeping. 698 switch (pendingAction) { 699 case COMPACT_PROCESS_SOME: 700 mSomeCompactionCount++; 701 break; 702 case COMPACT_PROCESS_FULL: 703 mFullCompactionCount++; 704 break; 705 case COMPACT_PROCESS_PERSISTENT: 706 mPersistentCompactionCount++; 707 break; 708 case COMPACT_PROCESS_BFGS: 709 mBfgsCompactionCount++; 710 break; 711 default: 712 break; 713 } 714 715 try { 716 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " 717 + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") 718 + ": " + name); 719 long zramFreeKbBefore = Debug.getZramFreeKb(); 720 FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim"); 721 fos.write(action.getBytes()); 722 fos.close(); 723 long[] rssAfter = Process.getRss(pid); 724 long end = SystemClock.uptimeMillis(); 725 long time = end - start; 726 long zramFreeKbAfter = Debug.getZramFreeKb(); 727 EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action, 728 rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], 729 rssAfter[0] - rssBefore[0], rssAfter[1] - rssBefore[1], 730 rssAfter[2] - rssBefore[2], rssAfter[3] - rssBefore[3], time, 731 lastCompactAction, lastCompactTime, lastOomAdj, procState, 732 zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore); 733 734 // Note that as above not taking mPhenoTypeFlagLock here to avoid locking 735 // on every single compaction for a flag that will seldom change and the 736 // impact of reading the wrong value here is low. 737 if (mRandom.nextFloat() < mStatsdSampleRate) { 738 StatsLog.write(StatsLog.APP_COMPACTED, pid, name, pendingAction, 739 rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], 740 rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time, 741 lastCompactAction, lastCompactTime, lastOomAdj, 742 ActivityManager.processStateAmToProto(procState), 743 zramFreeKbBefore, zramFreeKbAfter); 744 } 745 746 synchronized (mAm) { 747 proc.lastCompactTime = end; 748 proc.lastCompactAction = pendingAction; 749 } 750 751 if (action.equals(COMPACT_ACTION_FULL) 752 || action.equals(COMPACT_ACTION_ANON)) { 753 mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter)); 754 } 755 } catch (Exception e) { 756 // nothing to do, presumably the process died 757 } finally { 758 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 759 } 760 break; 761 } 762 case COMPACT_SYSTEM_MSG: { 763 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem"); 764 compactSystem(); 765 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 766 break; 767 } 768 } 769 } 770 } 771 } 772