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

Commit 40eba653 authored by Jing Ji's avatar Jing Ji
Browse files

Add support to log app kill stats into statsd

Bug: 184859606
Bug: 154511130
Test: statsd_testdrive
Test: atest CtsStatsdAtomHostTestCases:AppExitHostTest
Change-Id: Ia7856c95346c1f0f621948abd11d9648ffbdd7c5
parent d5c7dc04
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -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;
+0 −245
Original line number Diff line number Diff line
/*
 * 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;
}
+1 −1
Original line number Diff line number Diff line
@@ -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.
+60 −1
Original line number Diff line number Diff line
@@ -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);
            }