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

Commit cfc837f7 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Start really collecting PSS data for process stats.

The activity manager now uses some heuristics to try to
sample PSS data from processes so that it can get enough
data to over reasonable time have something useful, without
doing it too aggressively.

The current policy is:

1. Whenever a significant global change happens (memory state,
   sceen on or off), we collect PSS from all processes; this will
   not happen more than every 10 minutes.
2. When all activities become idle, we will collect PSS from the
   current top process; this will not happen more than every 2
   minutes per process.
3. We will sample the top-most process's PSS every 5 minutes.
4. When an process's oom adj changes and it has been more than
   30 minutes since PSS has been collected from it, we will
   collect a new PSS sample.
5. If a process changes from service A to service B (meaning it
   has been running a service for a long time), we will collect
   a PSS sample from it.
6. If someone explicitly requests PSS data (for running services
   UI or dumpsys), record that.

Also:

- Finish moving the procstats output all to the new format.
- Record information about processes being killed due to excessive
  wake locks or CPU use in procstats.
- Rework how we structure common vs. per-package process stats to
  make it simpler to deal with.
- Optimize the Debug.getPss() implementation (we use it a lot now).
  Should probably optimize it further at some point.

Change-Id: I179f1f7ae5852c7e567de4127d8457b50d27e0f0
parent 97e1d53d
Loading
Loading
Loading
Loading
+19 −14
Original line number Diff line number Diff line
@@ -552,7 +552,7 @@ public final class ActivityThread {
        private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";

        // Formatting for checkin service - update version if row format changes
        private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 2;
        private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;

        private void updatePendingConfiguration(Configuration config) {
            synchronized (mPackages) {
@@ -972,43 +972,48 @@ public final class ActivityThread {
                pw.print(memInfo.nativePss); pw.print(',');
                pw.print(memInfo.dalvikPss); pw.print(',');
                pw.print(memInfo.otherPss); pw.print(',');
                pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(',');
                pw.print(memInfo.getTotalPss()); pw.print(',');

                // Heap info - proportional set size
                // Heap info - swappable set size
                pw.print(memInfo.nativeSwappablePss); pw.print(',');
                pw.print(memInfo.dalvikSwappablePss); pw.print(',');
                pw.print(memInfo.otherSwappablePss); pw.print(',');
                pw.print(memInfo.nativeSwappablePss + memInfo.dalvikSwappablePss + memInfo.otherSwappablePss); pw.print(',');
                pw.print(memInfo.getTotalSwappablePss()); pw.print(',');

                // Heap info - shared dirty
                pw.print(memInfo.nativeSharedDirty); pw.print(',');
                pw.print(memInfo.dalvikSharedDirty); pw.print(',');
                pw.print(memInfo.otherSharedDirty); pw.print(',');
                pw.print(memInfo.nativeSharedDirty + memInfo.dalvikSharedDirty
                        + memInfo.otherSharedDirty); pw.print(',');
                pw.print(memInfo.getTotalSharedDirty()); pw.print(',');

                // Heap info - shared clean
                pw.print(memInfo.nativeSharedClean); pw.print(',');
                pw.print(memInfo.dalvikSharedClean); pw.print(',');
                pw.print(memInfo.otherSharedClean); pw.print(',');
                pw.print(memInfo.nativeSharedClean + memInfo.dalvikSharedClean
                        + memInfo.otherSharedClean); pw.print(',');
                pw.print(memInfo.getTotalSharedClean()); pw.print(',');

                // Heap info - private Dirty
                pw.print(memInfo.nativePrivateDirty); pw.print(',');
                pw.print(memInfo.dalvikPrivateDirty); pw.print(',');
                pw.print(memInfo.otherPrivateDirty); pw.print(',');
                pw.print(memInfo.nativePrivateDirty + memInfo.dalvikPrivateDirty
                        + memInfo.otherPrivateDirty); pw.print(',');

                pw.print(memInfo.getTotalPrivateDirty()); pw.print(',');

                // Heap info - private Clean
                pw.print(memInfo.nativePrivateClean); pw.print(',');
                pw.print(memInfo.dalvikPrivateClean); pw.print(',');
                pw.print(memInfo.otherPrivateClean); pw.print(',');
                pw.print(memInfo.nativePrivateClean + memInfo.dalvikPrivateClean
                        + memInfo.otherPrivateClean); pw.print(',');
                pw.print(memInfo.getTotalPrivateClean()); pw.print(',');

                // Heap info - other areas
                for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
                    pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(',');
                    pw.print(memInfo.getOtherPss(i)); pw.print(',');
                    pw.print(memInfo.getOtherSwappablePss(i)); pw.print(',');
                    pw.print(memInfo.getOtherSharedDirty(i)); pw.print(',');
                    pw.print(memInfo.getOtherSharedClean(i)); pw.print(',');
                    pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(',');
                    pw.print(memInfo.getOtherPrivateClean(i)); pw.print(',');
                }

                // Object counts
                pw.print(viewInstanceCount); pw.print(',');
+7 −3
Original line number Diff line number Diff line
@@ -394,12 +394,16 @@ static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid)
    if (fp == 0) return 0;

    while (true) {
        if (fgets(line, 1024, fp) == 0) {
        if (fgets(line, 1024, fp) == NULL) {
            break;
        }

        if (sscanf(line, "Pss: %d kB", &temp) == 1) {
            pss += temp;
        if (strncmp(line, "Pss: ", 5) == 0) {
            char* c = line + 5;
            while (*c != 0 && (*c < '0' || *c > '9')) {
                c++;
            }
            pss += atoi(c);
        }
    }

+229 −45
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.util.ArrayMap;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessStats;
import com.android.internal.os.TransferPipe;
@@ -266,6 +267,20 @@ public final class ActivityManagerService extends ActivityManagerNative
    // The minimum amount of time between successive GC requests for a process.
    static final int GC_MIN_INTERVAL = 60*1000;
    // The minimum amount of time between successive PSS requests for a process.
    static final int PSS_MIN_INTERVAL = 2*60*1000;
    // The amount of time we will sample PSS of the current top process while the
    // screen is on.
    static final int PSS_TOP_INTERVAL = 5*60*1000;
    // The maximum amount of time for a process to be around until we will take
    // a PSS snapshot on its next oom change.
    static final int PSS_MAX_INTERVAL = 30*60*1000;
    // The minimum amount of time between successive PSS requests for a process.
    static final int FULL_PSS_MIN_INTERVAL = 10*60*1000;
    // The rate at which we check for apps using excessive power -- 15 mins.
    static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000;
@@ -495,8 +510,17 @@ public final class ActivityManagerService extends ActivityManagerNative
    /**
     * List of processes that should gc as soon as things are idle.
     */
    final ArrayList<ProcessRecord> mProcessesToGc
            = new ArrayList<ProcessRecord>();
    final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
    /**
     * Processes we want to collect PSS data from.
     */
    final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
    /**
     * Last time we requested PSS data of all processes.
     */
    long mLastFullPssTime = SystemClock.uptimeMillis();
    /**
     * This is the process holding what we currently consider to be
@@ -1480,6 +1504,51 @@ public final class ActivityManagerService extends ActivityManagerNative
        }
    };
    static final int COLLECT_PSS_BG_MSG = 1;
    final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case COLLECT_PSS_BG_MSG: {
                int i=0;
                long start = SystemClock.uptimeMillis();
                do {
                    ProcessRecord proc;
                    int oomAdj;
                    int pid;
                    synchronized (ActivityManagerService.this) {
                        if (i >= mPendingPssProcesses.size()) {
                            Slog.i(TAG, "Collected PSS of " + i + " processes in "
                                    + (SystemClock.uptimeMillis()-start) + "ms");
                            mPendingPssProcesses.clear();
                            return;
                        }
                        proc = mPendingPssProcesses.get(i);
                        if (proc.thread != null) {
                            oomAdj = proc.setAdj;
                            pid = proc.pid;
                            i++;
                        } else {
                            proc = null;
                            oomAdj = 0;
                            pid = 0;
                        }
                    }
                    if (proc != null) {
                        long pss = Debug.getPss(pid);
                        synchronized (ActivityManagerService.this) {
                            if (proc.thread != null && proc.setAdj == oomAdj && proc.pid == pid) {
                                proc.baseProcessTracker.addPss(pss, true);
                            }
                        }
                    }
                } while (true);
            }
            }
        }
    };
    public static void setSystemProcess() {
        try {
            ActivityManagerService m = mSelf;
@@ -3906,8 +3975,24 @@ public final class ActivityManagerService extends ActivityManagerNative
        enforceNotIsolatedCaller("getProcessMemoryInfo");
        Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
        for (int i=pids.length-1; i>=0; i--) {
            ProcessRecord proc;
            int oomAdj;
            synchronized (this) {
                synchronized (mPidsSelfLocked) {
                    proc = mPidsSelfLocked.get(pids[i]);
                    oomAdj = proc != null ? proc.setAdj : 0;
                }
            }
            infos[i] = new Debug.MemoryInfo();
            Debug.getMemoryInfo(pids[i], infos[i]);
            if (proc != null) {
                synchronized (this) {
                    if (proc.thread != null && proc.setAdj == oomAdj) {
                        // Record this for posterity if the process has been stable.
                        proc.baseProcessTracker.addPss(infos[i].getTotalPss(), false);
                    }
                }
            }
        }
        return infos;
    }
@@ -3917,7 +4002,23 @@ public final class ActivityManagerService extends ActivityManagerNative
        enforceNotIsolatedCaller("getProcessPss");
        long[] pss = new long[pids.length];
        for (int i=pids.length-1; i>=0; i--) {
            ProcessRecord proc;
            int oomAdj;
            synchronized (this) {
                synchronized (mPidsSelfLocked) {
                    proc = mPidsSelfLocked.get(pids[i]);
                    oomAdj = proc != null ? proc.setAdj : 0;
                }
            }
            pss[i] = Debug.getPss(pids[i]);
            if (proc != null) {
                synchronized (this) {
                    if (proc.thread != null && proc.setAdj == oomAdj) {
                        // Record this for posterity if the process has been stable.
                        proc.baseProcessTracker.addPss(pss[i], false);
                    }
                }
            }
        }
        return pss;
    }
@@ -4350,7 +4451,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            thread.asBinder().linkToDeath(adr, 0);
            app.deathRecipient = adr;
        } catch (RemoteException e) {
            app.resetPackageList();
            app.resetPackageList(mProcessTracker);
            startProcessLocked(app, "link fail", processName);
            return false;
        }
@@ -4442,7 +4543,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            // an infinite loop of restarting processes...
            Slog.w(TAG, "Exception thrown during bind!", e);
            app.resetPackageList();
            app.resetPackageList(mProcessTracker);
            app.unlinkDeathRecipient();
            startProcessLocked(app, "bind fail", processName);
            return false;
@@ -4630,8 +4731,19 @@ public final class ActivityManagerService extends ActivityManagerNative
                        final int userId = mStartedUsers.keyAt(i);
                        Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
                        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                        broadcastIntentLocked(null, null, intent,
                                null, null, 0, null, null,
                        broadcastIntentLocked(null, null, intent, null,
                                new IIntentReceiver.Stub() {
                                    @Override
                                    public void performReceive(Intent intent, int resultCode,
                                            String data, Bundle extras, boolean ordered,
                                            boolean sticky, int sendingUser) {
                                        synchronized (ActivityManagerService.this) {
                                            requestPssAllProcsLocked(SystemClock.uptimeMillis(),
                                                    true);
                                        }
                                    }
                                },
                                0, null, null,
                                android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
                                AppOpsManager.OP_NONE, false, false, MY_PID, Process.SYSTEM_UID,
                                userId);
@@ -10934,7 +11046,7 @@ public final class ActivityManagerService extends ActivityManagerNative
        long uptime = SystemClock.uptimeMillis();
        long realtime = SystemClock.elapsedRealtime();
        if (procs.size() == 1 || isCheckinRequest) {
        if (!brief && !oomOnly && (procs.size() == 1 || isCheckinRequest)) {
            dumpDetails = true;
        }
@@ -10960,17 +11072,24 @@ public final class ActivityManagerService extends ActivityManagerNative
        long totalPss = 0;
        Debug.MemoryInfo mi = null;
        for (int i = procs.size() - 1 ; i >= 0 ; i--) {
            ProcessRecord r = procs.get(i);
            if (r.thread != null) {
            IApplicationThread thread;
            int oomAdj;
            synchronized (this) {
                thread = r.thread;
                oomAdj = r.setAdj;
            }
            if (thread != null) {
                if (!isCheckinRequest && dumpDetails) {
                    pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
                    pw.flush();
                }
                Debug.MemoryInfo mi = null;
                if (dumpDetails) {
                    try {
                        mi = r.thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs);
                        mi = null;
                        mi = thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs);
                    } catch (RemoteException e) {
                        if (!isCheckinRequest) {
                            pw.println("Got RemoteException!");
@@ -10978,21 +11097,31 @@ public final class ActivityManagerService extends ActivityManagerNative
                        }
                    }
                } else {
                    if (mi == null) {
                        mi = new Debug.MemoryInfo();
                    }
                    if (!brief && !oomOnly) {
                        Debug.getMemoryInfo(r.pid, mi);
                    } else {
                        mi.dalvikPss = (int)Debug.getPss(r.pid);
                    }
                }
                final long myTotalPss = mi.getTotalPss();
                synchronized (this) {
                    if (r.thread != null && oomAdj == r.setAdj) {
                        // Record this for posterity if the process has been stable.
                        r.baseProcessTracker.addPss(myTotalPss, true);
                    }
                }
                if (!isCheckinRequest && mi != null) {
                    long myTotalPss = mi.getTotalPss();
                    totalPss += myTotalPss;
                    MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")",
                            r.processName, myTotalPss, 0);
                    procMems.add(pssItem);
                    synchronized (this) {
                        r.baseProcessTracker.addPss(myTotalPss);
                    }
                    nativePss += mi.nativePss;
                    dalvikPss += mi.dalvikPss;
                    otherPss += mi.otherPss;
@@ -11070,7 +11199,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                            }
                        }
                        for (int j=0; j<miCat.subitems.size(); j++) {
                            MemItem mi = miCat.subitems.get(j);
                            MemItem memi = miCat.subitems.get(j);
                            if (j > 0) {
                                if (outTag != null) {
                                    outTag.append(" ");
@@ -11080,10 +11209,10 @@ public final class ActivityManagerService extends ActivityManagerNative
                                }
                            }
                            if (outTag != null && miCat.id <= ProcessList.FOREGROUND_APP_ADJ) {
                                appendMemBucket(outTag, mi.pss, mi.shortLabel, false);
                                appendMemBucket(outTag, memi.pss, memi.shortLabel, false);
                            }
                            if (outStack != null) {
                                appendMemBucket(outStack, mi.pss, mi.shortLabel, true);
                                appendMemBucket(outStack, memi.pss, memi.shortLabel, true);
                            }
                        }
                        if (outStack != null && miCat.id >= ProcessList.FOREGROUND_APP_ADJ) {
@@ -11109,7 +11238,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            }
            pw.println("Total PSS by OOM adjustment:");
            dumpMemItems(pw, "  ", oomMems, false);
            if (!oomOnly) {
            if (!brief && !oomOnly) {
                PrintWriter out = categoryPw != null ? categoryPw : pw;
                out.println();
                out.println("Total PSS by category:");
@@ -11117,6 +11246,7 @@ public final class ActivityManagerService extends ActivityManagerNative
            }
            pw.println();
            pw.print("Total PSS: "); pw.print(totalPss); pw.println(" kB");
            if (!brief) {
                final int[] SINGLE_LONG_FORMAT = new int[] {
                    Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG
                };
@@ -11142,6 +11272,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                        pw.print(voltile); pw.println(" kB volatile");
            }
        }
    }
    /**
     * Searches array of arguments for the specified string
@@ -11236,6 +11367,7 @@ public final class ActivityManagerService extends ActivityManagerNative
        }
        mProcessesToGc.remove(app);
        mPendingPssProcesses.remove(app);
        
        // Dismiss any open dialogs.
        if (app.crashDialog != null && !app.forceCrashReport) {
@@ -11254,7 +11386,7 @@ public final class ActivityManagerService extends ActivityManagerNative
        app.crashing = false;
        app.notResponding = false;
        
        app.resetPackageList();
        app.resetPackageList(mProcessTracker);
        app.unlinkDeathRecipient();
        app.thread = null;
        app.forcingToForeground = null;
@@ -13748,6 +13880,41 @@ public final class ActivityManagerService extends ActivityManagerNative
        return app.curRawAdj;
    }
    /**
     * Schedule PSS collection of a process.
     */
    void requestPssLocked(ProcessRecord proc, long now, boolean always) {
        if (!always && now < (proc.lastPssTime+PSS_MIN_INTERVAL)) {
            return;
        }
        if (mPendingPssProcesses.contains(proc)) {
            return;
        }
        proc.lastPssTime = now;
        if (mPendingPssProcesses.size() == 0) {
            mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
        }
        mPendingPssProcesses.add(proc);
    }
    /**
     * Schedule PSS collection of all processes.
     */
    void requestPssAllProcsLocked(long now, boolean always) {
        if (!always && now < (mLastFullPssTime+FULL_PSS_MIN_INTERVAL)) {
            return;
        }
        mLastFullPssTime = now;
        mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
        mPendingPssProcesses.clear();
        for (int i=mLruProcesses.size()-1; i>=0; i--) {
            ProcessRecord app = mLruProcesses.get(i);
            app.lastPssTime = now;
            mPendingPssProcesses.add(app);
        }
        mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
    }
    /**
     * Ask a given process to GC right now.
     */
@@ -13959,6 +14126,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                            + " during " + realtimeSince);
                    EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
                            app.processName, app.setAdj, "excessive wake lock");
                    app.baseProcessTracker.reportExcessiveWake(app.pkgList);
                    Process.killProcessQuiet(app.pid);
                } else if (doCpuKills && uptimeSince > 0
                        && ((cputimeUsed*100)/uptimeSince) >= 50) {
@@ -13971,6 +14139,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                            + " during " + uptimeSince);
                    EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
                            app.processName, app.setAdj, "excessive cpu");
                    app.baseProcessTracker.reportExcessiveCpu(app.pkgList);
                    Process.killProcessQuiet(app.pid);
                } else {
                    app.lastWakeTime = wtime;
@@ -14012,11 +14181,23 @@ public final class ActivityManagerService extends ActivityManagerNative
            app.setRawAdj = app.curRawAdj;
        }
        if (app == TOP_APP && now > (app.lastPssTime+PSS_TOP_INTERVAL)) {
            requestPssLocked(app, now, true);
        }
        if (app.curAdj != app.setAdj) {
            if (Process.setOomAdj(app.pid, app.curAdj)) {
                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
                    TAG, "Set " + app.pid + " " + app.processName +
                    " adj " + app.curAdj + ": " + app.adjType);
                if (app.setAdj == ProcessList.SERVICE_ADJ
                        && app.curAdj == ProcessList.SERVICE_B_ADJ) {
                    // If a service is dropping to the B list, it has been running for
                    // a while, take a PSS snapshot.
                    requestPssLocked(app, now, false);
                } else if (now > (app.lastPssTime+PSS_MAX_INTERVAL)) {
                    requestPssLocked(app, now, true);
                }
                app.setAdj = app.curAdj;
                app.setAdjChanged = true;
                if (!doingAll) {
@@ -14406,6 +14587,9 @@ public final class ActivityManagerService extends ActivityManagerNative
                }
            }
        }
        if (allChanged) {
            requestPssAllProcsLocked(now, false);
        }
        if (DEBUG_OOM_ADJ) {
            Slog.d(TAG, "Did OOM ADJ in " + (SystemClock.uptimeMillis()-now) + "ms");
+1 −0
Original line number Diff line number Diff line
@@ -1707,6 +1707,7 @@ public final class ActivityStackSupervisor {
                r.idle = true;
                if (allResumedActivitiesIdle()) {
                    mService.scheduleAppGcsLocked();
                    mService.requestPssLocked(r.app, SystemClock.uptimeMillis(), false);
                }
                if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
                    sendThumbnail = r.app.thread;
+18 −12

File changed.

Preview size limit exceeded, changes collapsed.

Loading