Add PSS info to dumpsys output.

We now track the last 12 hours of PSS measurements in SysUI
and emit them in bugreports.

While we're at it, tidy up some of the heap dumping code
(dump on separate thread, update tile state, tweak sharing
extras to fix filename when sharing to Drive).

Bug: 133671238
Test: adb shell dumpsys activity service SystemUI
Change-Id: I862f6e78ed49cf669786610fec0768ebb3b24438
This commit is contained in:
Dan Sandler
2019-06-07 15:51:27 -04:00
parent 6d371e092e
commit 646d66b09b
2 changed files with 123 additions and 22 deletions

View File

@@ -16,6 +16,8 @@
package com.android.systemui.util.leak;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
@@ -47,10 +49,11 @@ public class DumpTruck {
private static final String FILEPROVIDER_PATH = "leak";
private static final String TAG = "DumpTruck";
private static final int BUFSIZ = 512 * 1024; // 512K
private static final int BUFSIZ = 1024 * 1024; // 1MB
private final Context context;
private Uri hprofUri;
private long pss;
final StringBuilder body = new StringBuilder();
public DumpTruck(Context context) {
@@ -89,6 +92,7 @@ public class DumpTruck {
.append(info.currentPss)
.append(" uss=")
.append(info.currentUss);
pss = info.currentPss;
}
}
if (pid == myPid) {
@@ -114,6 +118,7 @@ public class DumpTruck {
if (DumpTruck.zipUp(zipfile, paths)) {
final File pathFile = new File(zipfile);
hprofUri = FileProvider.getUriForFile(context, FILEPROVIDER_AUTHORITY, pathFile);
Log.v(TAG, "Heap dump accessible at URI: " + hprofUri);
}
} catch (IOException e) {
Log.e(TAG, "unable to zip up heapdumps", e);
@@ -138,16 +143,27 @@ public class DumpTruck {
* @return share intent
*/
public Intent createShareIntent() {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
Intent shareIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.putExtra(Intent.EXTRA_SUBJECT, "SystemUI memory dump");
shareIntent.putExtra(Intent.EXTRA_SUBJECT,
String.format("SystemUI memory dump (pss=%dM)", pss / 1024));
shareIntent.putExtra(Intent.EXTRA_TEXT, body.toString());
if (hprofUri != null) {
final ArrayList<Uri> uriList = new ArrayList<>();
uriList.add(hprofUri);
shareIntent.setType("application/zip");
shareIntent.putExtra(Intent.EXTRA_STREAM, hprofUri);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList);
// Include URI in ClipData also, so that grantPermission picks it up.
// We don't use setData here because some apps interpret this as "to:".
ClipData clipdata = new ClipData(new ClipDescription("content",
new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
new ClipData.Item(hprofUri));
shareIntent.setClipData(clipdata);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
return shareIntent;
}

View File

@@ -16,9 +16,13 @@
package com.android.systemui.util.leak;
import static android.service.quicksettings.Tile.STATE_ACTIVE;
import static android.telephony.ims.feature.ImsFeature.STATE_UNAVAILABLE;
import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN;
import static com.android.systemui.Dependency.BG_LOOPER_NAME;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -38,11 +42,11 @@ import android.os.Message;
import android.os.Process;
import android.os.SystemProperties;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.LongSparseArray;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
@@ -50,6 +54,8 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import javax.inject.Inject;
@@ -59,7 +65,7 @@ import javax.inject.Singleton;
/**
*/
@Singleton
public class GarbageMonitor {
public class GarbageMonitor implements Dumpable {
private static final boolean LEAK_REPORTING_ENABLED =
Build.IS_DEBUGGABLE
&& SystemProperties.getBoolean("debug.enable_leak_reporting", false);
@@ -77,12 +83,15 @@ public class GarbageMonitor {
private static final long GARBAGE_INSPECTION_INTERVAL =
15 * DateUtils.MINUTE_IN_MILLIS; // 15 min
private static final long HEAP_TRACK_INTERVAL = 1 * DateUtils.MINUTE_IN_MILLIS; // 1 min
private static final int HEAP_TRACK_HISTORY_LEN = 720; // 12 hours
private static final int DO_GARBAGE_INSPECTION = 1000;
private static final int DO_HEAP_TRACK = 3000;
private static final int GARBAGE_ALLOWANCE = 5;
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Handler mHandler;
private final TrackedGarbage mTrackedGarbage;
private final LeakReporter mLeakReporter;
@@ -180,7 +189,7 @@ public class GarbageMonitor {
sb.append(p);
sb.append(" ");
}
Log.v(TAG, sb.toString());
if (DEBUG) Log.v(TAG, sb.toString());
}
private void update() {
@@ -189,18 +198,18 @@ public class GarbageMonitor {
for (int i = 0; i < dinfos.length; i++) {
Debug.MemoryInfo dinfo = dinfos[i];
if (i > mPids.size()) {
Log.e(TAG, "update: unknown process info received: " + dinfo);
if (DEBUG) Log.e(TAG, "update: unknown process info received: " + dinfo);
break;
}
final long pid = mPids.get(i).intValue();
final ProcessMemInfo info = mData.get(pid);
info.head = (info.head + 1) % info.pss.length;
info.pss[info.head] = info.currentPss = dinfo.getTotalPss();
info.uss[info.head] = info.currentUss = dinfo.getTotalPrivateDirty();
info.head = (info.head + 1) % info.pss.length;
if (info.currentPss > info.max) info.max = info.currentPss;
if (info.currentUss > info.max) info.max = info.currentUss;
if (info.currentPss == 0) {
Log.v(TAG, "update: pid " + pid + " has pss=0, it probably died");
if (DEBUG) Log.v(TAG, "update: pid " + pid + " has pss=0, it probably died");
mData.remove(pid);
}
}
@@ -230,11 +239,36 @@ public class GarbageMonitor {
return b + SUFFIXES[i];
}
private void dumpHprofAndShare() {
final Intent share = mDumpTruck.captureHeaps(getTrackedProcesses()).createShareIntent();
mContext.startActivity(share);
private Intent dumpHprofAndGetShareIntent() {
return mDumpTruck.captureHeaps(getTrackedProcesses()).createShareIntent();
}
@Override
public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
pw.println("GarbageMonitor params:");
pw.println(String.format(" mHeapLimit=%d KB", mHeapLimit));
pw.println(String.format(" GARBAGE_INSPECTION_INTERVAL=%d (%.1f mins)",
GARBAGE_INSPECTION_INTERVAL,
(float) GARBAGE_INSPECTION_INTERVAL / DateUtils.MINUTE_IN_MILLIS));
final float htiMins = HEAP_TRACK_INTERVAL / DateUtils.MINUTE_IN_MILLIS;
pw.println(String.format(" HEAP_TRACK_INTERVAL=%d (%.1f mins)",
HEAP_TRACK_INTERVAL,
htiMins));
pw.println(String.format(" HEAP_TRACK_HISTORY_LEN=%d (%.1f hr total)",
HEAP_TRACK_HISTORY_LEN,
(float) HEAP_TRACK_HISTORY_LEN * htiMins / 60f));
pw.println("GarbageMonitor tracked processes:");
for (long pid : mPids) {
final ProcessMemInfo pmi = mData.get(pid);
if (pmi != null) {
pmi.dump(fd, pw, args);
}
}
}
private static class MemoryIconDrawable extends Drawable {
long pss, limit;
final Drawable baseIcon;
@@ -244,7 +278,7 @@ public class GarbageMonitor {
MemoryIconDrawable(Context context) {
baseIcon = context.getDrawable(R.drawable.ic_memory).mutate();
dp = context.getResources().getDisplayMetrics().density;
paint.setColor(QSTileImpl.getColorForState(context, Tile.STATE_ACTIVE));
paint.setColor(QSTileImpl.getColorForState(context, STATE_ACTIVE));
}
public void setPss(long pss) {
@@ -354,6 +388,7 @@ public class GarbageMonitor {
private final GarbageMonitor gm;
private ProcessMemInfo pmi;
private boolean dumpInProgress;
@Inject
public MemoryTile(QSHost host) {
@@ -373,8 +408,26 @@ public class GarbageMonitor {
@Override
protected void handleClick() {
getHost().collapsePanels();
mHandler.post(gm::dumpHprofAndShare);
if (dumpInProgress) return;
dumpInProgress = true;
refreshState();
new Thread("HeapDumpThread") {
@Override
public void run() {
try {
// wait for animations & state changes
Thread.sleep(500);
} catch (InterruptedException ignored) { }
final Intent shareIntent = gm.dumpHprofAndGetShareIntent();
mHandler.post(() -> {
dumpInProgress = false;
refreshState();
getHost().collapsePanels();
mContext.startActivity(shareIntent);
});
}
}.start();
}
@Override
@@ -404,9 +457,12 @@ public class GarbageMonitor {
pmi = gm.getMemInfo(Process.myPid());
final MemoryGraphIcon icon = new MemoryGraphIcon();
icon.setHeapLimit(gm.mHeapLimit);
state.state = dumpInProgress ? STATE_UNAVAILABLE : STATE_ACTIVE;
state.label = dumpInProgress
? "Dumping..."
: mContext.getString(R.string.heap_dump_tile_name);
if (pmi != null) {
icon.setPss(pmi.currentPss);
state.label = mContext.getString(R.string.heap_dump_tile_name);
state.secondaryLabel =
String.format(
"pss: %s / %s",
@@ -414,7 +470,6 @@ public class GarbageMonitor {
formatBytes(gm.mHeapLimit * 1024));
} else {
icon.setPss(0);
state.label = "Dump SysUI";
state.secondaryLabel = null;
}
state.icon = icon;
@@ -433,13 +488,14 @@ public class GarbageMonitor {
}
}
public static class ProcessMemInfo {
/** */
public static class ProcessMemInfo implements Dumpable {
public long pid;
public String name;
public long startTime;
public long currentPss, currentUss;
public long[] pss = new long[256];
public long[] uss = new long[256];
public long[] pss = new long[HEAP_TRACK_HISTORY_LEN];
public long[] uss = new long[HEAP_TRACK_HISTORY_LEN];
public long max = 1;
public int head = 0;
@@ -452,9 +508,33 @@ public class GarbageMonitor {
public long getUptime() {
return System.currentTimeMillis() - startTime;
}
@Override
public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
pw.print("{ \"pid\": ");
pw.print(pid);
pw.print(", \"name\": \"");
pw.print(name.replace('"', '-'));
pw.print("\", \"start\": ");
pw.print(startTime);
pw.print(", \"pss\": [");
// write pss values starting from the oldest, which is pss[head], wrapping around to
// pss[(head-1) % pss.length]
for (int i = 0; i < pss.length; i++) {
if (i > 0) pw.print(",");
pw.print(pss[(head + i) % pss.length]);
}
pw.print("], \"uss\": [");
for (int i = 0; i < uss.length; i++) {
if (i > 0) pw.print(",");
pw.print(uss[(head + i) % uss.length]);
}
pw.println("] }");
}
}
public static class Service extends SystemUI {
/** */
public static class Service extends SystemUI implements Dumpable {
private GarbageMonitor mGarbageMonitor;
@Override
@@ -472,6 +552,11 @@ public class GarbageMonitor {
mGarbageMonitor.startHeapTracking();
}
}
@Override
public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
if (mGarbageMonitor != null) mGarbageMonitor.dump(fd, pw, args);
}
}
private class BackgroundHeapCheckHandler extends Handler {