Loading packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java +20 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -89,6 +92,7 @@ public class DumpTruck { .append(info.currentPss) .append(" uss=") .append(info.currentUss); pss = info.currentPss; } } if (pid == myPid) { Loading @@ -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); Loading @@ -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; } Loading packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +103 −18 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -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; Loading Loading @@ -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() { Loading @@ -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); } } Loading Loading @@ -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; Loading @@ -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) { Loading Loading @@ -354,6 +388,7 @@ public class GarbageMonitor { private final GarbageMonitor gm; private ProcessMemInfo pmi; private boolean dumpInProgress; @Inject public MemoryTile(QSHost host) { Loading @@ -373,8 +408,26 @@ public class GarbageMonitor { @Override protected void handleClick() { 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(); mHandler.post(gm::dumpHprofAndShare); mContext.startActivity(shareIntent); }); } }.start(); } @Override Loading Loading @@ -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", Loading @@ -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; Loading @@ -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; Loading @@ -452,9 +508,33 @@ public class GarbageMonitor { public long getUptime() { return System.currentTimeMillis() - startTime; } } public static class Service extends SystemUI { @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 implements Dumpable { private GarbageMonitor mGarbageMonitor; @Override Loading @@ -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 { Loading Loading
packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java +20 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -89,6 +92,7 @@ public class DumpTruck { .append(info.currentPss) .append(" uss=") .append(info.currentUss); pss = info.currentPss; } } if (pid == myPid) { Loading @@ -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); Loading @@ -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; } Loading
packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +103 −18 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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); Loading @@ -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; Loading Loading @@ -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() { Loading @@ -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); } } Loading Loading @@ -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; Loading @@ -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) { Loading Loading @@ -354,6 +388,7 @@ public class GarbageMonitor { private final GarbageMonitor gm; private ProcessMemInfo pmi; private boolean dumpInProgress; @Inject public MemoryTile(QSHost host) { Loading @@ -373,8 +408,26 @@ public class GarbageMonitor { @Override protected void handleClick() { 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(); mHandler.post(gm::dumpHprofAndShare); mContext.startActivity(shareIntent); }); } }.start(); } @Override Loading Loading @@ -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", Loading @@ -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; Loading @@ -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; Loading @@ -452,9 +508,33 @@ public class GarbageMonitor { public long getUptime() { return System.currentTimeMillis() - startTime; } } public static class Service extends SystemUI { @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 implements Dumpable { private GarbageMonitor mGarbageMonitor; @Override Loading @@ -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 { Loading