Add activitymanager debug option to freeze a process
This makes testing paths that rely on freezer such as compaction etc doable in a simple way as well as allowing instrumentation to triggering them. Bug: 274177499 Test: am freeze [--sticky] <processname> Test: am unfreeze [--sticky] <processname> Change-Id: I832795b59d9a1078665a5474506732c3c7039abe
This commit is contained in:
@@ -247,6 +247,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
|
||||
return runSendBroadcast(pw);
|
||||
case "compact":
|
||||
return runCompact(pw);
|
||||
case "freeze":
|
||||
return runFreeze(pw);
|
||||
case "unfreeze":
|
||||
return runUnfreeze(pw);
|
||||
case "instrument":
|
||||
getOutPrintWriter().println("Error: must be invoked through 'am instrument'.");
|
||||
return -1;
|
||||
@@ -1074,20 +1078,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
|
||||
boolean isFullCompact = op.equals("full");
|
||||
boolean isSomeCompact = op.equals("some");
|
||||
if (isFullCompact || isSomeCompact) {
|
||||
String processName = getNextArgRequired();
|
||||
synchronized (mInternal.mProcLock) {
|
||||
// Default to current user
|
||||
int userId = mInterface.getCurrentUserId();
|
||||
String userOpt = getNextOption();
|
||||
if (userOpt != null && "--user".equals(userOpt)) {
|
||||
int inputUserId = UserHandle.parseUserArg(getNextArgRequired());
|
||||
if (inputUserId != UserHandle.USER_CURRENT) {
|
||||
userId = inputUserId;
|
||||
}
|
||||
}
|
||||
final int uid =
|
||||
mInternal.getPackageManagerInternal().getPackageUid(processName, 0, userId);
|
||||
app = mInternal.getProcessRecordLocked(processName, uid);
|
||||
app = getProcessFromShell();
|
||||
if (app == null) {
|
||||
getErrPrintWriter().println("Error: could not find process");
|
||||
return -1;
|
||||
}
|
||||
pw.println("Process record found pid: " + app.mPid);
|
||||
if (isFullCompact) {
|
||||
@@ -1143,6 +1137,93 @@ final class ActivityManagerShellCommand extends ShellCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NeverCompile
|
||||
int runFreeze(PrintWriter pw) throws RemoteException {
|
||||
String freezerOpt = getNextOption();
|
||||
boolean isSticky = false;
|
||||
if (freezerOpt != null) {
|
||||
isSticky = freezerOpt.equals("--sticky");
|
||||
}
|
||||
ProcessRecord app = getProcessFromShell();
|
||||
if (app == null) {
|
||||
getErrPrintWriter().println("Error: could not find process");
|
||||
return -1;
|
||||
}
|
||||
pw.println("Freezing pid: " + app.mPid + " sticky=" + isSticky);
|
||||
synchronized (mInternal) {
|
||||
synchronized (mInternal.mProcLock) {
|
||||
app.mOptRecord.setFreezeSticky(isSticky);
|
||||
mInternal.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncInternalLSP(app, 0, true);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NeverCompile
|
||||
int runUnfreeze(PrintWriter pw) throws RemoteException {
|
||||
String freezerOpt = getNextOption();
|
||||
boolean isSticky = false;
|
||||
if (freezerOpt != null) {
|
||||
isSticky = freezerOpt.equals("--sticky");
|
||||
}
|
||||
ProcessRecord app = getProcessFromShell();
|
||||
if (app == null) {
|
||||
getErrPrintWriter().println("Error: could not find process");
|
||||
return -1;
|
||||
}
|
||||
pw.println("Unfreezing pid: " + app.mPid);
|
||||
synchronized (mInternal) {
|
||||
synchronized (mInternal.mProcLock) {
|
||||
synchronized (mInternal.mOomAdjuster.mCachedAppOptimizer.mFreezerLock) {
|
||||
app.mOptRecord.setFreezeSticky(isSticky);
|
||||
mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(app, 0,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses from the shell the process name and user id if provided and provides the corresponding
|
||||
* {@link ProcessRecord)} If no user is provided, it will fallback to current user.
|
||||
* Example usage: {@code <processname> --user current} or {@code <processname>}
|
||||
* @return process record of process, null if none found.
|
||||
* @throws RemoteException
|
||||
*/
|
||||
@NeverCompile
|
||||
ProcessRecord getProcessFromShell() throws RemoteException {
|
||||
ProcessRecord app;
|
||||
String processName = getNextArgRequired();
|
||||
synchronized (mInternal.mProcLock) {
|
||||
// Default to current user
|
||||
int userId = getUserIdFromShellOrFallback();
|
||||
final int uid =
|
||||
mInternal.getPackageManagerInternal().getPackageUid(processName, 0, userId);
|
||||
app = mInternal.getProcessRecordLocked(processName, uid);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User id from command line provided in the form of
|
||||
* {@code --user <userid|current|all>} and if the argument is not found it will fallback
|
||||
* to current user.
|
||||
* @throws RemoteException
|
||||
*/
|
||||
@NeverCompile
|
||||
int getUserIdFromShellOrFallback() throws RemoteException {
|
||||
int userId = mInterface.getCurrentUserId();
|
||||
String userOpt = getNextOption();
|
||||
if (userOpt != null && "--user".equals(userOpt)) {
|
||||
int inputUserId = UserHandle.parseUserArg(getNextArgRequired());
|
||||
if (inputUserId != UserHandle.USER_CURRENT) {
|
||||
userId = inputUserId;
|
||||
}
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
int runDumpHeap(PrintWriter pw) throws RemoteException {
|
||||
final PrintWriter err = getErrPrintWriter();
|
||||
boolean managed = true;
|
||||
@@ -4061,6 +4142,14 @@ final class ActivityManagerShellCommand extends ShellCommand {
|
||||
pw.println(" Perform a native compaction for process with <pid>.");
|
||||
pw.println(" some: execute file compaction.");
|
||||
pw.println(" full: execute anon + file compaction.");
|
||||
pw.println(" freeze [--sticky] <processname> [--user <USER_ID>]");
|
||||
pw.println(" Freeze a process.");
|
||||
pw.println(" --sticky: persists the frozen state for the process lifetime or");
|
||||
pw.println(" until an unfreeze is triggered via shell");
|
||||
pw.println(" unfreeze [--sticky] <processname> [--user <USER_ID>]");
|
||||
pw.println(" Unfreeze a process.");
|
||||
pw.println(" --sticky: persists the unfrozen state for the process lifetime or");
|
||||
pw.println(" until a freeze is triggered via shell");
|
||||
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
|
||||
pw.println(" [--user <USER_ID> | current]");
|
||||
pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
|
||||
|
||||
@@ -289,7 +289,7 @@ public final class CachedAppOptimizer {
|
||||
|
||||
private final ActivityManagerGlobalLock mProcLock;
|
||||
|
||||
private final Object mFreezerLock = new Object();
|
||||
public final Object mFreezerLock = new Object();
|
||||
|
||||
private final OnPropertiesChangedListener mOnFlagsChangedListener =
|
||||
new OnPropertiesChangedListener() {
|
||||
@@ -710,8 +710,9 @@ public final class CachedAppOptimizer {
|
||||
pw.println(" Apps frozen: " + size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
ProcessRecord app = mFrozenProcesses.valueAt(i);
|
||||
pw.println(" " + app.mOptRecord.getFreezeUnfreezeTime()
|
||||
+ ": " + app.getPid() + " " + app.processName);
|
||||
pw.println(" " + app.mOptRecord.getFreezeUnfreezeTime() + ": " + app.getPid()
|
||||
+ " " + app.processName
|
||||
+ (app.mOptRecord.isFreezeSticky() ? " (sticky)" : ""));
|
||||
}
|
||||
|
||||
if (!mPendingCompactionProcesses.isEmpty()) {
|
||||
@@ -1230,12 +1231,26 @@ public final class CachedAppOptimizer {
|
||||
|
||||
@GuardedBy({"mAm", "mProcLock"})
|
||||
void freezeAppAsyncLSP(ProcessRecord app) {
|
||||
freezeAppAsyncInternalLSP(app, mFreezerDebounceTimeout, false);
|
||||
}
|
||||
|
||||
@GuardedBy({"mAm", "mProcLock"})
|
||||
void freezeAppAsyncInternalLSP(ProcessRecord app, long delayMillis, boolean force) {
|
||||
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
|
||||
if (opt.isPendingFreeze()) {
|
||||
// Skip redundant DO_FREEZE message
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt.isFreezeSticky() && !force) {
|
||||
if (DEBUG_FREEZER) {
|
||||
Slog.d(TAG_AM,
|
||||
"Skip freezing because unfrozen state is sticky pid=" + app.getPid() + " "
|
||||
+ app.processName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAm.mConstants.USE_MODERN_TRIM
|
||||
&& app.mState.getSetAdj() >= ProcessList.CACHED_APP_MIN_ADJ) {
|
||||
final IApplicationThread thread = app.getThread();
|
||||
@@ -1248,9 +1263,8 @@ public final class CachedAppOptimizer {
|
||||
}
|
||||
}
|
||||
mFreezeHandler.sendMessageDelayed(
|
||||
mFreezeHandler.obtainMessage(
|
||||
SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
|
||||
mFreezerDebounceTimeout);
|
||||
mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
|
||||
delayMillis);
|
||||
opt.setPendingFreeze(true);
|
||||
if (DEBUG_FREEZER) {
|
||||
Slog.d(TAG_AM, "Async freezing " + app.getPid() + " " + app.processName);
|
||||
@@ -1258,9 +1272,19 @@ public final class CachedAppOptimizer {
|
||||
}
|
||||
|
||||
@GuardedBy({"mAm", "mProcLock", "mFreezerLock"})
|
||||
void unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason) {
|
||||
void unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force) {
|
||||
final int pid = app.getPid();
|
||||
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
|
||||
boolean sticky = opt.isFreezeSticky();
|
||||
if (sticky && !force) {
|
||||
// Sticky freezes will not change their state unless forced out of it.
|
||||
if (DEBUG_FREEZER) {
|
||||
Slog.d(TAG_AM,
|
||||
"Skip unfreezing because frozen state is sticky pid=" + pid + " "
|
||||
+ app.processName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (opt.isPendingFreeze()) {
|
||||
// Remove pending DO_FREEZE message
|
||||
mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
|
||||
@@ -1354,7 +1378,7 @@ public final class CachedAppOptimizer {
|
||||
@GuardedBy({"mAm", "mProcLock"})
|
||||
void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) {
|
||||
synchronized (mFreezerLock) {
|
||||
unfreezeAppInternalLSP(app, reason);
|
||||
unfreezeAppInternalLSP(app, reason, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2012,15 +2036,6 @@ public final class CachedAppOptimizer {
|
||||
|
||||
synchronized (mProcLock) {
|
||||
pid = proc.getPid();
|
||||
if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ
|
||||
|| opt.shouldNotFreeze()) {
|
||||
if (DEBUG_FREEZER) {
|
||||
Slog.d(TAG_AM, "Skipping freeze for process " + pid
|
||||
+ " " + name + " curAdj = " + proc.mState.getCurAdj()
|
||||
+ ", shouldNotFreeze = " + opt.shouldNotFreeze());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFreezerOverride) {
|
||||
opt.setFreezerOverride(true);
|
||||
|
||||
@@ -72,6 +72,15 @@ final class ProcessCachedOptimizerRecord {
|
||||
@GuardedBy("mProcLock")
|
||||
private boolean mFrozen;
|
||||
|
||||
/**
|
||||
* If set to true it will make the (un)freeze decision sticky which means that the freezer
|
||||
* decision will remain the same unless a freeze is forced via {@link #mForceFreezeOps}.
|
||||
* This property is usually set to true when external user wants to maintain a (un)frozen state
|
||||
* after being applied.
|
||||
*/
|
||||
@GuardedBy("mProcLock")
|
||||
private boolean mFreezeSticky;
|
||||
|
||||
/**
|
||||
* Set to false after the process has been frozen.
|
||||
* Set to true after we have collected PSS for the frozen process.
|
||||
@@ -193,6 +202,15 @@ final class ProcessCachedOptimizerRecord {
|
||||
void setFrozen(boolean frozen) {
|
||||
mFrozen = frozen;
|
||||
}
|
||||
@GuardedBy("mProcLock")
|
||||
void setFreezeSticky(boolean sticky) {
|
||||
mFreezeSticky = sticky;
|
||||
}
|
||||
|
||||
@GuardedBy("mProcLock")
|
||||
boolean isFreezeSticky() {
|
||||
return mFreezeSticky;
|
||||
}
|
||||
|
||||
boolean skipPSSCollectionBecauseFrozen() {
|
||||
boolean collected = mHasCollectedFrozenPSS;
|
||||
|
||||
Reference in New Issue
Block a user