Merge "Add support to log app kill stats into statsd" into sc-dev
This commit is contained in:
@@ -444,6 +444,13 @@ public final class ApplicationExitInfo implements Parcelable {
|
||||
*/
|
||||
private IParcelFileDescriptorRetriever mNativeTombstoneRetriever;
|
||||
|
||||
/**
|
||||
* Whether or not we've logged this into the statsd.
|
||||
*
|
||||
* for system internal use only, will not retain across processes.
|
||||
*/
|
||||
private boolean mLoggedInStatsd;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "REASON_" }, value = {
|
||||
REASON_UNKNOWN,
|
||||
@@ -890,6 +897,24 @@ public final class ApplicationExitInfo implements Parcelable {
|
||||
mNativeTombstoneRetriever = retriever;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #mLoggedInStatsd
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean isLoggedInStatsd() {
|
||||
return mLoggedInStatsd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #mLoggedInStatsd
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setLoggedInStatsd(boolean loggedInStatsd) {
|
||||
mLoggedInStatsd = loggedInStatsd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
syntax = "proto2";
|
||||
option java_multiple_files = true;
|
||||
|
||||
package android.app;
|
||||
|
||||
/**
|
||||
* The reason code that why app process is killed.
|
||||
*/
|
||||
enum AppExitReasonCode {
|
||||
/**
|
||||
* Application process died due to unknown reason.
|
||||
*/
|
||||
REASON_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* Application process exit normally by itself, for example,
|
||||
* via {@link android.os.Process#exit}; {@link #status} will specify the exit code.
|
||||
*
|
||||
* <p>Applications should normally not do this, as the system has a better knowledge
|
||||
* in terms of process management.</p>
|
||||
*/
|
||||
REASON_EXIT_SELF = 1;
|
||||
|
||||
/**
|
||||
* Application process died due to the result of an OS signal; for example,
|
||||
* {@link android.os.Process#SIGNAL_KILL}; {@link #status} will specify the signum.
|
||||
*/
|
||||
REASON_SIGNALED = 2;
|
||||
|
||||
/**
|
||||
* Application process was killed by the system low memory killer, meaning the system was
|
||||
* under memory pressure at the time of kill.
|
||||
*/
|
||||
REASON_LOW_MEMORY = 3;
|
||||
|
||||
/**
|
||||
* Application process died because of an unhandled exception in Java code.
|
||||
*/
|
||||
REASON_CRASH = 4;
|
||||
|
||||
/**
|
||||
* Application process died because it's crashed due to a native code crash.
|
||||
*/
|
||||
REASON_CRASH_NATIVE = 5;
|
||||
|
||||
/**
|
||||
* Application process was killed due to being unresponsive (ANR).
|
||||
*/
|
||||
REASON_ANR = 6;
|
||||
|
||||
/**
|
||||
* Application process was killed because it took too long to attach to the system
|
||||
* during the start.
|
||||
*/
|
||||
REASON_INITIALIZATION_FAILURE = 7;
|
||||
|
||||
/**
|
||||
* Application process was killed because of initialization failure,
|
||||
* for example, it took too long to attach to the system during the start,
|
||||
* or there was an error during initialization.
|
||||
*/
|
||||
REASON_PERMISSION_CHANGE = 8;
|
||||
|
||||
/**
|
||||
* Application process was killed by the activity manager due to excessive resource usage.
|
||||
*/
|
||||
REASON_EXCESSIVE_RESOURCE_USAGE = 9;
|
||||
|
||||
/**
|
||||
* Application process was killed because of the user request, for example,
|
||||
* user clicked the "Force stop" button of the application in the Settings,
|
||||
* or swiped away the application from Recents.
|
||||
*/
|
||||
REASON_USER_REQUESTED = 10;
|
||||
|
||||
/**
|
||||
* Application process was killed, because the user they are running as on devices
|
||||
* with mutlple users, was stopped.
|
||||
*/
|
||||
REASON_USER_STOPPED = 11;
|
||||
|
||||
/**
|
||||
* Application process was killed because its dependency was going away, for example,
|
||||
* a stable content provider connection's client will be killed if the provider is killed.
|
||||
*/
|
||||
REASON_DEPENDENCY_DIED = 12;
|
||||
|
||||
/**
|
||||
* Application process was killed by the system for various other reasons,
|
||||
* for example, the application package got disabled by the user;
|
||||
* {@link #description} will specify the cause given by the system.
|
||||
*/
|
||||
REASON_OTHER = 13;
|
||||
}
|
||||
|
||||
/**
|
||||
* The supplemental reason code that why app process is killed
|
||||
*/
|
||||
enum AppExitSubReasonCode {
|
||||
/**
|
||||
* Application process kills subReason is unknown.
|
||||
*/
|
||||
SUBREASON_UNKNOWN = 0;
|
||||
|
||||
/**
|
||||
* Application process was killed because user quit it on the "wait for debugger" dialog.
|
||||
*/
|
||||
SUBREASON_WAIT_FOR_DEBUGGER = 1;
|
||||
|
||||
/**
|
||||
* Application process was killed by the activity manager because there were too many cached
|
||||
* processes.
|
||||
*/
|
||||
SUBREASON_TOO_MANY_CACHED = 2;
|
||||
|
||||
/**
|
||||
* Application process was killed by the activity manager because there were too many empty
|
||||
* processes.
|
||||
*/
|
||||
SUBREASON_TOO_MANY_EMPTY = 3;
|
||||
|
||||
/**
|
||||
* Application process was killed by the activity manager because there were too many cached
|
||||
* processes and this process had been in empty state for a long time.
|
||||
*/
|
||||
SUBREASON_TRIM_EMPTY = 4;
|
||||
|
||||
/**
|
||||
* Application process was killed by the activity manager because system was on
|
||||
* memory pressure and this process took large amount of cached memory.
|
||||
*/
|
||||
SUBREASON_LARGE_CACHED = 5;
|
||||
|
||||
/**
|
||||
* Application process was killed by the activity manager because the system was on
|
||||
* low memory pressure for a significant amount of time since last idle.
|
||||
*/
|
||||
SUBREASON_MEMORY_PRESSURE = 6;
|
||||
|
||||
/**
|
||||
* Application process was killed by the activity manager due to excessive CPU usage.
|
||||
*/
|
||||
SUBREASON_EXCESSIVE_CPU = 7;
|
||||
|
||||
/**
|
||||
* System update has done (so the system update process should be killed).
|
||||
*/
|
||||
SUBREASON_SYSTEM_UPDATE_DONE = 8;
|
||||
|
||||
/**
|
||||
* Kill all foreground services, for now it only occurs when enabling the quiet
|
||||
* mode for the managed profile.
|
||||
*/
|
||||
SUBREASON_KILL_ALL_FG = 9;
|
||||
|
||||
/**
|
||||
* All background processes except certain ones were killed, for now it only occurs
|
||||
* when the density of the default display is changed.
|
||||
*/
|
||||
SUBREASON_KILL_ALL_BG_EXCEPT = 10;
|
||||
|
||||
/**
|
||||
* The process associated with the UID was explicitly killed, for example,
|
||||
* it could be because of permission changes.
|
||||
*/
|
||||
SUBREASON_KILL_UID = 11;
|
||||
|
||||
/**
|
||||
* The process was explicitly killed with its PID, typically because of
|
||||
* the low memory for surfaces.
|
||||
*/
|
||||
SUBREASON_KILL_PID = 12;
|
||||
|
||||
/**
|
||||
* The start of the process was invalid.
|
||||
*/
|
||||
SUBREASON_INVALID_START = 13;
|
||||
|
||||
/**
|
||||
* The process was killed because it's in an invalid state, typically
|
||||
* it's triggered from SHELL.
|
||||
*/
|
||||
SUBREASON_INVALID_STATE = 14;
|
||||
|
||||
/**
|
||||
* The process was killed when it's imperceptible to user, because it was
|
||||
* in a bad state.
|
||||
*/
|
||||
SUBREASON_IMPERCEPTIBLE = 15;
|
||||
|
||||
/**
|
||||
* The process was killed because it's being moved out from LRU list.
|
||||
*/
|
||||
SUBREASON_REMOVE_LRU = 16;
|
||||
|
||||
/**
|
||||
* The process was killed because it's isolated and was in a cached state.
|
||||
*/
|
||||
SUBREASON_ISOLATED_NOT_NEEDED = 17;
|
||||
|
||||
/**
|
||||
* The process was killed because it's in forced-app-standby state, and it's cached and
|
||||
* its uid state is idle; this would be set only when the reason is {@link #REASON_OTHER}.
|
||||
*/
|
||||
SUBREASON_CACHED_IDLE_FORCED_APP_STANDBY = 18;
|
||||
}
|
||||
|
||||
/** * The relative importance level that the system places on a process.
|
||||
* Keep sync with the definitions in
|
||||
* {@link android.app.ActivityManager.RunningAppProcessInfo}
|
||||
*/
|
||||
enum Importance {
|
||||
option allow_alias = true;
|
||||
|
||||
IMPORTANCE_FOREGROUND = 100;
|
||||
IMPORTANCE_FOREGROUND_SERVICE = 125;
|
||||
IMPORTANCE_TOP_SLEEPING_PRE_28 = 150;
|
||||
IMPORTANCE_VISIBLE = 200;
|
||||
IMPORTANCE_PERCEPTIBLE_PRE_26 = 130;
|
||||
IMPORTANCE_PERCEPTIBLE = 230;
|
||||
IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170;
|
||||
IMPORTANCE_SERVICE = 300;
|
||||
IMPORTANCE_TOP_SLEEPING = 325;
|
||||
IMPORTANCE_CANT_SAVE_STATE = 350;
|
||||
IMPORTANCE_CACHED = 400;
|
||||
IMPORTANCE_BACKGROUND = 400;
|
||||
IMPORTANCE_EMPTY = 500;
|
||||
IMPORTANCE_GONE = 1000;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ option java_multiple_files = true;
|
||||
package android.app;
|
||||
|
||||
import "frameworks/base/core/proto/android/privacy.proto";
|
||||
import "frameworks/base/core/proto/android/app/appexit_enums.proto";
|
||||
import "frameworks/proto_logging/stats/enums/app/enums.proto";
|
||||
|
||||
/**
|
||||
* An android.app.ApplicationExitInfo object.
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.ProcessMap;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.server.IoThread;
|
||||
import com.android.server.LocalServices;
|
||||
@@ -108,6 +109,13 @@ public final class AppExitInfoTracker {
|
||||
|
||||
private static final int APP_EXIT_RAW_INFO_POOL_SIZE = 8;
|
||||
|
||||
/**
|
||||
* How long we're going to hold before logging an app exit info into statsd;
|
||||
* we do this is because there could be multiple sources signaling an app exit, we'd like to
|
||||
* gather the most accurate information before logging into statsd.
|
||||
*/
|
||||
private static final long APP_EXIT_INFO_STATSD_LOG_DEBOUNCE = TimeUnit.SECONDS.toMillis(15);
|
||||
|
||||
@VisibleForTesting
|
||||
static final String APP_EXIT_STORE_DIR = "procexitstore";
|
||||
|
||||
@@ -384,6 +392,8 @@ public final class AppExitInfoTracker {
|
||||
ApplicationExitInfo.REASON_LOW_MEMORY);
|
||||
} else if (zygote != null) {
|
||||
updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null);
|
||||
} else {
|
||||
scheduleLogToStatsdLocked(info, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -398,7 +408,7 @@ public final class AppExitInfoTracker {
|
||||
raw.getPackageName(), raw.getPackageUid(), raw.getPid());
|
||||
|
||||
if (info == null) {
|
||||
addExitInfoLocked(raw);
|
||||
info = addExitInfoLocked(raw);
|
||||
} else {
|
||||
// always override the existing info since we are now more informational.
|
||||
info.setReason(raw.getReason());
|
||||
@@ -407,6 +417,7 @@ public final class AppExitInfoTracker {
|
||||
info.setTimestamp(System.currentTimeMillis());
|
||||
info.setDescription(raw.getDescription());
|
||||
}
|
||||
scheduleLogToStatsdLocked(info, true);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@@ -438,22 +449,29 @@ public final class AppExitInfoTracker {
|
||||
// if the record is way outdated, don't update it then (because of potential pid reuse)
|
||||
return;
|
||||
}
|
||||
boolean immediateLog = false;
|
||||
if (status != null) {
|
||||
if (OsConstants.WIFEXITED(status)) {
|
||||
info.setReason(ApplicationExitInfo.REASON_EXIT_SELF);
|
||||
info.setStatus(OsConstants.WEXITSTATUS(status));
|
||||
immediateLog = true;
|
||||
} else if (OsConstants.WIFSIGNALED(status)) {
|
||||
if (info.getReason() == ApplicationExitInfo.REASON_UNKNOWN) {
|
||||
info.setReason(ApplicationExitInfo.REASON_SIGNALED);
|
||||
info.setStatus(OsConstants.WTERMSIG(status));
|
||||
} else if (info.getReason() == ApplicationExitInfo.REASON_CRASH_NATIVE) {
|
||||
info.setStatus(OsConstants.WTERMSIG(status));
|
||||
immediateLog = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reason != null) {
|
||||
info.setReason(reason);
|
||||
if (reason == ApplicationExitInfo.REASON_LOW_MEMORY) {
|
||||
immediateLog = true;
|
||||
}
|
||||
}
|
||||
scheduleLogToStatsdLocked(info, immediateLog);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -836,6 +854,40 @@ public final class AppExitInfoTracker {
|
||||
container.addExitInfoLocked(info);
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void scheduleLogToStatsdLocked(ApplicationExitInfo info, boolean immediate) {
|
||||
if (info.isLoggedInStatsd()) {
|
||||
return;
|
||||
}
|
||||
if (immediate) {
|
||||
mKillHandler.removeMessages(KillHandler.MSG_STATSD_LOG, info);
|
||||
performLogToStatsdLocked(info);
|
||||
} else if (!mKillHandler.hasMessages(KillHandler.MSG_STATSD_LOG, info)) {
|
||||
mKillHandler.sendMessageDelayed(mKillHandler.obtainMessage(
|
||||
KillHandler.MSG_STATSD_LOG, info), APP_EXIT_INFO_STATSD_LOG_DEBOUNCE);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void performLogToStatsdLocked(ApplicationExitInfo info) {
|
||||
if (info.isLoggedInStatsd()) {
|
||||
return;
|
||||
}
|
||||
info.setLoggedInStatsd(true);
|
||||
final String pkgName = info.getPackageName();
|
||||
String processName = info.getProcessName();
|
||||
if (TextUtils.equals(pkgName, processName)) {
|
||||
// Omit the process name here to save space
|
||||
processName = null;
|
||||
} else if (processName != null && processName.startsWith(pkgName)) {
|
||||
// Strip the prefix to save space
|
||||
processName = processName.substring(pkgName.length());
|
||||
}
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.APP_PROCESS_DIED,
|
||||
info.getPackageUid(), processName, info.getReason(), info.getSubReason(),
|
||||
info.getImportance(), (int) info.getPss(), (int) info.getRss());
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void forEachPackageLocked(
|
||||
BiFunction<String, SparseArray<AppExitInfoContainer>, Integer> callback) {
|
||||
@@ -1532,6 +1584,7 @@ public final class AppExitInfoTracker {
|
||||
static final int MSG_CHILD_PROC_DIED = 4102;
|
||||
static final int MSG_PROC_DIED = 4103;
|
||||
static final int MSG_APP_KILL = 4104;
|
||||
static final int MSG_STATSD_LOG = 4105;
|
||||
|
||||
KillHandler(Looper looper) {
|
||||
super(looper, null, true);
|
||||
@@ -1564,6 +1617,12 @@ public final class AppExitInfoTracker {
|
||||
recycleRawRecord(raw);
|
||||
}
|
||||
break;
|
||||
case MSG_STATSD_LOG: {
|
||||
synchronized (mLock) {
|
||||
performLogToStatsdLocked((ApplicationExitInfo) msg.obj);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user