Loading cmds/am/src/com/android/commands/am/Am.java +20 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,10 @@ public class Am { runStartService(); } else if (op.equals("force-stop")) { runForceStop(); } else if (op.equals("kill")) { runKill(); } else if (op.equals("kill-all")) { runKillAll(); } else if (op.equals("instrument")) { runInstrument(); } else if (op.equals("broadcast")) { Loading Loading @@ -484,6 +488,14 @@ public class Am { mAm.forceStopPackage(nextArgRequired()); } private void runKill() throws Exception { mAm.killBackgroundProcesses(nextArgRequired()); } private void runKillAll() throws Exception { mAm.killAllBackgroundProcesses(); } private void sendBroadcast() throws Exception { Intent intent = makeIntent(); IntentReceiver receiver = new IntentReceiver(); Loading Loading @@ -1179,6 +1191,8 @@ public class Am { " [--R COUNT] [-S] <INTENT>\n" + " am startservice <INTENT>\n" + " am force-stop <PACKAGE>\n" + " am kill <PACKAGE>\n" + " am kill-all\n" + " am broadcast <INTENT>\n" + " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" + " [--no-window-animation] <COMPONENT>\n" + Loading @@ -1202,6 +1216,12 @@ public class Am { "\n" + "am force-stop: force stop everything associated with <PACKAGE>.\n" + "\n" + "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" + " processes that are safe to kill -- that is, will not impact the user\n" + " experience.\n" + "\n" + "am kill-all: Kill all background processes.\n" + "\n" + "am broadcast: send a broadcast Intent.\n" + "\n" + "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" + Loading core/java/android/app/ActivityManagerNative.java +19 −2 Original line number Diff line number Diff line Loading @@ -1093,6 +1093,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } case KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); killAllBackgroundProcesses(); reply.writeNoException(); return true; } case FORCE_STOP_PACKAGE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String packageName = data.readString(); Loading Loading @@ -2918,6 +2925,16 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } public void killAllBackgroundProcesses() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); mRemote.transact(KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); } public void forceStopPackage(String packageName) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); Loading core/java/android/app/IActivityManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -234,6 +234,7 @@ public interface IActivityManager extends IInterface { public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) throws RemoteException; public void killBackgroundProcesses(final String packageName) throws RemoteException; public void killAllBackgroundProcesses() throws RemoteException; public void forceStopPackage(final String packageName) throws RemoteException; // Note: probably don't want to allow applications access to these. Loading Loading @@ -605,4 +606,5 @@ public interface IActivityManager extends IInterface { int GET_PROCESS_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+136; int SHOW_BOOT_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137; int DISMISS_KEYGUARD_ON_NEXT_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+138; int KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+139; } services/java/com/android/server/am/ActivityManagerService.java +84 −20 Original line number Diff line number Diff line Loading @@ -1220,6 +1220,8 @@ public final class ActivityManagerService extends ActivityManagerNative } Thread thread = new Thread() { @Override public void run() { StringBuilder dropBuilder = new StringBuilder(1024); StringBuilder logBuilder = new StringBuilder(1024); try { java.lang.Process proc = Runtime.getRuntime().exec(new String[] { "procrank", }); Loading @@ -1233,16 +1235,29 @@ public final class ActivityManagerService extends ActivityManagerNative break; } if (line.length() > 0) { Slog.i(TAG, line); logBuilder.append(line); logBuilder.append('\n'); } dropBuilder.append(line); dropBuilder.append('\n'); } converter.close(); } catch (IOException e) { } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); dumpApplicationMemoryUsage(null, pw, " ", new String[] { }, true); Slog.i(TAG, sw.toString()); StringWriter catSw = new StringWriter(); PrintWriter catPw = new PrintWriter(catSw); dumpApplicationMemoryUsage(null, pw, " ", new String[] { }, true, catPw); String memUsage = sw.toString(); dropBuilder.append('\n'); dropBuilder.append(memUsage); dropBuilder.append(catSw.toString()); logBuilder.append(memUsage); addErrorToDropBox("watchdog", null, "system_server", null, null, "Low on memory -- no more background processes", dropBuilder.toString(), null, null); Slog.i(TAG, logBuilder.toString()); synchronized (ActivityManagerService.this) { long now = SystemClock.uptimeMillis(); if (mLastMemUsageReportTime < now) { Loading Loading @@ -1394,7 +1409,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false); mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null); } } Loading Loading @@ -3192,7 +3207,49 @@ public final class ActivityManagerService extends ActivityManagerNative return; } killPackageProcessesLocked(packageName, pkgUid, ProcessList.SERVICE_ADJ, false, true, true, false); ProcessList.SERVICE_ADJ, false, true, true, false, "kill background"); } } finally { Binder.restoreCallingIdentity(callingId); } } public void killAllBackgroundProcesses() { if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: killAllBackgroundProcesses() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES; Slog.w(TAG, msg); throw new SecurityException(msg); } long callingId = Binder.clearCallingIdentity(); try { synchronized(this) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { final int NA = apps.size(); for (int ia=0; ia<NA; ia++) { ProcessRecord app = apps.valueAt(ia); if (app.persistent) { // we don't kill persistent processes continue; } if (app.removed) { procs.add(app); } else if (app.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { app.removed = true; procs.add(app); } } } int N = procs.size(); for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), false, true, "kill all background"); } } } finally { Binder.restoreCallingIdentity(callingId); Loading Loading @@ -3364,7 +3421,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean killPackageProcessesLocked(String packageName, int uid, int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit, boolean evenPersistent) { boolean evenPersistent, String reason) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // Remove all processes this package may have touched: all with the Loading Loading @@ -3399,7 +3456,7 @@ public final class ActivityManagerService extends ActivityManagerNative int N = procs.size(); for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), callerWillRestart, allowRestart); removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); } return N > 0; } Loading Loading @@ -3430,7 +3487,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean didSomething = killPackageProcessesLocked(name, uid, -100, callerWillRestart, false, doit, evenPersistent); callerWillRestart, false, doit, evenPersistent, "force stop"); TaskRecord lastTask = null; for (i=0; i<mMainStack.mHistory.size(); i++) { Loading Loading @@ -3518,11 +3575,11 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart) { boolean callerWillRestart, boolean allowRestart, String reason) { final String name = app.processName; final int uid = app.info.uid; if (DEBUG_PROCESSES) Slog.d( TAG, "Force removing process " + app + " (" + name TAG, "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")"); mProcessNames.remove(name, uid); Loading @@ -3537,9 +3594,10 @@ public final class ActivityManagerService extends ActivityManagerNative mPidsSelfLocked.remove(pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } Slog.i(TAG, "Killing proc " + app.toShortString() + ": " + reason); handleAppDiedLocked(app, true, allowRestart); mLruProcesses.remove(app); Process.killProcess(pid); Process.killProcessQuiet(pid); if (app.persistent) { if (!callerWillRestart) { Loading Loading @@ -6846,7 +6904,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=procsToKill.size()-1; i>=0; i--) { ProcessRecord proc = procsToKill.get(i); Slog.i(TAG, "Removing system update proc: " + proc); removeProcessLocked(proc, true, false); removeProcessLocked(proc, true, false, "system update done"); } } Loading Loading @@ -7042,7 +7100,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Don't let services in this process be restarted and potentially // annoy the user repeatedly. Unless it is persistent, since those // processes run critical code. removeProcessLocked(app, false, false); removeProcessLocked(app, false, false, "crash"); mMainStack.resumeTopActivityLocked(null); return false; } Loading Loading @@ -9298,8 +9356,10 @@ public final class ActivityManagerService extends ActivityManagerNative } final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, String[] args, boolean brief) { PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) { boolean dumpAll = false; boolean oomOnly = false; int opti = 0; while (opti < args.length) { Loading @@ -9310,9 +9370,12 @@ public final class ActivityManagerService extends ActivityManagerNative opti++; if ("-a".equals(opt)) { dumpAll = true; } else if ("--oom".equals(opt)) { oomOnly = true; } else if ("-h".equals(opt)) { pw.println("meminfo dump options: [-a] [process]"); pw.println("meminfo dump options: [-a] [--oom] [process]"); pw.println(" -a: include all available information for each process."); pw.println(" --oom: only show processes organized by oom adj."); pw.println("If [process] is specified it can be the name or "); pw.println("pid of a specific process to dump."); return; Loading Loading @@ -9438,7 +9501,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (!brief) { if (!brief && !oomOnly) { pw.println(); pw.println("Total PSS by process:"); dumpMemItems(pw, " ", procMems, true); Loading @@ -9446,10 +9509,11 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println("Total PSS by OOM adjustment:"); dumpMemItems(pw, " ", oomMems, false); if (!brief) { pw.println(); pw.println("Total PSS by category:"); dumpMemItems(pw, " ", catMems, true); if (!oomOnly) { PrintWriter out = categoryPw != null ? categoryPw : pw; out.println(); out.println("Total PSS by category:"); dumpMemItems(out, " ", catMems, true); } pw.println(); pw.print("Total PSS: "); pw.print(totalPss); pw.println(" Kb"); Loading Loading
cmds/am/src/com/android/commands/am/Am.java +20 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,10 @@ public class Am { runStartService(); } else if (op.equals("force-stop")) { runForceStop(); } else if (op.equals("kill")) { runKill(); } else if (op.equals("kill-all")) { runKillAll(); } else if (op.equals("instrument")) { runInstrument(); } else if (op.equals("broadcast")) { Loading Loading @@ -484,6 +488,14 @@ public class Am { mAm.forceStopPackage(nextArgRequired()); } private void runKill() throws Exception { mAm.killBackgroundProcesses(nextArgRequired()); } private void runKillAll() throws Exception { mAm.killAllBackgroundProcesses(); } private void sendBroadcast() throws Exception { Intent intent = makeIntent(); IntentReceiver receiver = new IntentReceiver(); Loading Loading @@ -1179,6 +1191,8 @@ public class Am { " [--R COUNT] [-S] <INTENT>\n" + " am startservice <INTENT>\n" + " am force-stop <PACKAGE>\n" + " am kill <PACKAGE>\n" + " am kill-all\n" + " am broadcast <INTENT>\n" + " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" + " [--no-window-animation] <COMPONENT>\n" + Loading @@ -1202,6 +1216,12 @@ public class Am { "\n" + "am force-stop: force stop everything associated with <PACKAGE>.\n" + "\n" + "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" + " processes that are safe to kill -- that is, will not impact the user\n" + " experience.\n" + "\n" + "am kill-all: Kill all background processes.\n" + "\n" + "am broadcast: send a broadcast Intent.\n" + "\n" + "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" + Loading
core/java/android/app/ActivityManagerNative.java +19 −2 Original line number Diff line number Diff line Loading @@ -1093,6 +1093,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } case KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); killAllBackgroundProcesses(); reply.writeNoException(); return true; } case FORCE_STOP_PACKAGE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String packageName = data.readString(); Loading Loading @@ -2918,6 +2925,16 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } public void killAllBackgroundProcesses() throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); mRemote.transact(KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); } public void forceStopPackage(String packageName) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); Loading
core/java/android/app/IActivityManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -234,6 +234,7 @@ public interface IActivityManager extends IInterface { public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) throws RemoteException; public void killBackgroundProcesses(final String packageName) throws RemoteException; public void killAllBackgroundProcesses() throws RemoteException; public void forceStopPackage(final String packageName) throws RemoteException; // Note: probably don't want to allow applications access to these. Loading Loading @@ -605,4 +606,5 @@ public interface IActivityManager extends IInterface { int GET_PROCESS_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+136; int SHOW_BOOT_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137; int DISMISS_KEYGUARD_ON_NEXT_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+138; int KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+139; }
services/java/com/android/server/am/ActivityManagerService.java +84 −20 Original line number Diff line number Diff line Loading @@ -1220,6 +1220,8 @@ public final class ActivityManagerService extends ActivityManagerNative } Thread thread = new Thread() { @Override public void run() { StringBuilder dropBuilder = new StringBuilder(1024); StringBuilder logBuilder = new StringBuilder(1024); try { java.lang.Process proc = Runtime.getRuntime().exec(new String[] { "procrank", }); Loading @@ -1233,16 +1235,29 @@ public final class ActivityManagerService extends ActivityManagerNative break; } if (line.length() > 0) { Slog.i(TAG, line); logBuilder.append(line); logBuilder.append('\n'); } dropBuilder.append(line); dropBuilder.append('\n'); } converter.close(); } catch (IOException e) { } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); dumpApplicationMemoryUsage(null, pw, " ", new String[] { }, true); Slog.i(TAG, sw.toString()); StringWriter catSw = new StringWriter(); PrintWriter catPw = new PrintWriter(catSw); dumpApplicationMemoryUsage(null, pw, " ", new String[] { }, true, catPw); String memUsage = sw.toString(); dropBuilder.append('\n'); dropBuilder.append(memUsage); dropBuilder.append(catSw.toString()); logBuilder.append(memUsage); addErrorToDropBox("watchdog", null, "system_server", null, null, "Low on memory -- no more background processes", dropBuilder.toString(), null, null); Slog.i(TAG, logBuilder.toString()); synchronized (ActivityManagerService.this) { long now = SystemClock.uptimeMillis(); if (mLastMemUsageReportTime < now) { Loading Loading @@ -1394,7 +1409,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false); mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null); } } Loading Loading @@ -3192,7 +3207,49 @@ public final class ActivityManagerService extends ActivityManagerNative return; } killPackageProcessesLocked(packageName, pkgUid, ProcessList.SERVICE_ADJ, false, true, true, false); ProcessList.SERVICE_ADJ, false, true, true, false, "kill background"); } } finally { Binder.restoreCallingIdentity(callingId); } } public void killAllBackgroundProcesses() { if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: killAllBackgroundProcesses() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES; Slog.w(TAG, msg); throw new SecurityException(msg); } long callingId = Binder.clearCallingIdentity(); try { synchronized(this) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { final int NA = apps.size(); for (int ia=0; ia<NA; ia++) { ProcessRecord app = apps.valueAt(ia); if (app.persistent) { // we don't kill persistent processes continue; } if (app.removed) { procs.add(app); } else if (app.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { app.removed = true; procs.add(app); } } } int N = procs.size(); for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), false, true, "kill all background"); } } } finally { Binder.restoreCallingIdentity(callingId); Loading Loading @@ -3364,7 +3421,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean killPackageProcessesLocked(String packageName, int uid, int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit, boolean evenPersistent) { boolean evenPersistent, String reason) { ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); // Remove all processes this package may have touched: all with the Loading Loading @@ -3399,7 +3456,7 @@ public final class ActivityManagerService extends ActivityManagerNative int N = procs.size(); for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), callerWillRestart, allowRestart); removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); } return N > 0; } Loading Loading @@ -3430,7 +3487,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean didSomething = killPackageProcessesLocked(name, uid, -100, callerWillRestart, false, doit, evenPersistent); callerWillRestart, false, doit, evenPersistent, "force stop"); TaskRecord lastTask = null; for (i=0; i<mMainStack.mHistory.size(); i++) { Loading Loading @@ -3518,11 +3575,11 @@ public final class ActivityManagerService extends ActivityManagerNative } private final boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart) { boolean callerWillRestart, boolean allowRestart, String reason) { final String name = app.processName; final int uid = app.info.uid; if (DEBUG_PROCESSES) Slog.d( TAG, "Force removing process " + app + " (" + name TAG, "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")"); mProcessNames.remove(name, uid); Loading @@ -3537,9 +3594,10 @@ public final class ActivityManagerService extends ActivityManagerNative mPidsSelfLocked.remove(pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } Slog.i(TAG, "Killing proc " + app.toShortString() + ": " + reason); handleAppDiedLocked(app, true, allowRestart); mLruProcesses.remove(app); Process.killProcess(pid); Process.killProcessQuiet(pid); if (app.persistent) { if (!callerWillRestart) { Loading Loading @@ -6846,7 +6904,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=procsToKill.size()-1; i>=0; i--) { ProcessRecord proc = procsToKill.get(i); Slog.i(TAG, "Removing system update proc: " + proc); removeProcessLocked(proc, true, false); removeProcessLocked(proc, true, false, "system update done"); } } Loading Loading @@ -7042,7 +7100,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Don't let services in this process be restarted and potentially // annoy the user repeatedly. Unless it is persistent, since those // processes run critical code. removeProcessLocked(app, false, false); removeProcessLocked(app, false, false, "crash"); mMainStack.resumeTopActivityLocked(null); return false; } Loading Loading @@ -9298,8 +9356,10 @@ public final class ActivityManagerService extends ActivityManagerNative } final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, String[] args, boolean brief) { PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) { boolean dumpAll = false; boolean oomOnly = false; int opti = 0; while (opti < args.length) { Loading @@ -9310,9 +9370,12 @@ public final class ActivityManagerService extends ActivityManagerNative opti++; if ("-a".equals(opt)) { dumpAll = true; } else if ("--oom".equals(opt)) { oomOnly = true; } else if ("-h".equals(opt)) { pw.println("meminfo dump options: [-a] [process]"); pw.println("meminfo dump options: [-a] [--oom] [process]"); pw.println(" -a: include all available information for each process."); pw.println(" --oom: only show processes organized by oom adj."); pw.println("If [process] is specified it can be the name or "); pw.println("pid of a specific process to dump."); return; Loading Loading @@ -9438,7 +9501,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (!brief) { if (!brief && !oomOnly) { pw.println(); pw.println("Total PSS by process:"); dumpMemItems(pw, " ", procMems, true); Loading @@ -9446,10 +9509,11 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println("Total PSS by OOM adjustment:"); dumpMemItems(pw, " ", oomMems, false); if (!brief) { pw.println(); pw.println("Total PSS by category:"); dumpMemItems(pw, " ", catMems, true); if (!oomOnly) { PrintWriter out = categoryPw != null ? categoryPw : pw; out.println(); out.println("Total PSS by category:"); dumpMemItems(out, " ", catMems, true); } pw.println(); pw.print("Total PSS: "); pw.print(totalPss); pw.println(" Kb"); Loading