blob: bff405c0bee6a97815e6bf97130851195647a524 [file] [log] [blame]
Adrian Roos91250682017-02-06 14:48:15 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.util.leak;
18
Dan Sandler646d66b2019-06-07 15:51:27 -040019import static android.service.quicksettings.Tile.STATE_ACTIVE;
20import static android.telephony.ims.feature.ImsFeature.STATE_UNAVAILABLE;
21
Dan Sandler4d90d1e2018-03-23 16:29:06 -040022import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN;
Adrian Roos91250682017-02-06 14:48:15 -080023
Dan Sandler646d66b2019-06-07 15:51:27 -040024import android.annotation.Nullable;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040025import android.app.ActivityManager;
26import android.content.Context;
27import android.content.Intent;
28import android.content.res.ColorStateList;
29import android.graphics.Canvas;
30import android.graphics.ColorFilter;
31import android.graphics.Paint;
32import android.graphics.PixelFormat;
33import android.graphics.PorterDuff;
34import android.graphics.Rect;
35import android.graphics.drawable.Drawable;
Adrian Roos91250682017-02-06 14:48:15 -080036import android.os.Build;
37import android.os.Handler;
38import android.os.Looper;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040039import android.os.Message;
40import android.os.Process;
Adrian Roosfb2bb3f2017-02-14 17:19:16 +010041import android.os.SystemProperties;
Jason Monk3b9357f2017-07-14 09:40:54 -040042import android.provider.Settings;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040043import android.text.format.DateUtils;
44import android.util.Log;
45import android.util.LongSparseArray;
Adrian Roos91250682017-02-06 14:48:15 -080046
Dan Sandler646d66b2019-06-07 15:51:27 -040047import com.android.systemui.Dumpable;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040048import com.android.systemui.R;
Adrian Roos91250682017-02-06 14:48:15 -080049import com.android.systemui.SystemUI;
Dave Mankofff4736812019-10-18 17:25:50 -040050import com.android.systemui.dagger.qualifiers.BgLooper;
Fabian Kozynski8f703632019-09-27 10:26:44 -040051import com.android.systemui.plugins.ActivityStarter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040052import com.android.systemui.plugins.qs.QSTile;
53import com.android.systemui.qs.QSHost;
54import com.android.systemui.qs.tileimpl.QSTileImpl;
55
Dan Sandler646d66b2019-06-07 15:51:27 -040056import java.io.FileDescriptor;
57import java.io.PrintWriter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040058import java.util.ArrayList;
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -040059import java.util.List;
Adrian Roos91250682017-02-06 14:48:15 -080060
Jason Monk196d6392018-12-20 13:25:34 -050061import javax.inject.Inject;
Jason Monk196d6392018-12-20 13:25:34 -050062import javax.inject.Singleton;
63
64/**
65 */
66@Singleton
Dan Sandler646d66b2019-06-07 15:51:27 -040067public class GarbageMonitor implements Dumpable {
Dan Sandler4d90d1e2018-03-23 16:29:06 -040068 private static final boolean LEAK_REPORTING_ENABLED =
69 Build.IS_DEBUGGABLE
70 && SystemProperties.getBoolean("debug.enable_leak_reporting", false);
71 private static final String FORCE_ENABLE_LEAK_REPORTING = "sysui_force_enable_leak_reporting";
72
73 private static final boolean HEAP_TRACKING_ENABLED = Build.IS_DEBUGGABLE;
Dan Sandler1e5d0122019-06-06 23:35:22 -040074
75 // whether to use ActivityManager.setHeapLimit
76 private static final boolean ENABLE_AM_HEAP_LIMIT = Build.IS_DEBUGGABLE;
77 // heap limit value, in KB (overrides R.integer.watch_heap_limit)
78 private static final String SETTINGS_KEY_AM_HEAP_LIMIT = "systemui_am_heap_limit";
Adrian Roos91250682017-02-06 14:48:15 -080079
80 private static final String TAG = "GarbageMonitor";
81
Dan Sandler4d90d1e2018-03-23 16:29:06 -040082 private static final long GARBAGE_INSPECTION_INTERVAL =
83 15 * DateUtils.MINUTE_IN_MILLIS; // 15 min
84 private static final long HEAP_TRACK_INTERVAL = 1 * DateUtils.MINUTE_IN_MILLIS; // 1 min
Dan Sandler646d66b2019-06-07 15:51:27 -040085 private static final int HEAP_TRACK_HISTORY_LEN = 720; // 12 hours
Dan Sandler4d90d1e2018-03-23 16:29:06 -040086
87 private static final int DO_GARBAGE_INSPECTION = 1000;
88 private static final int DO_HEAP_TRACK = 3000;
89
Adrian Roos91250682017-02-06 14:48:15 -080090 private static final int GARBAGE_ALLOWANCE = 5;
91
Dan Sandler646d66b2019-06-07 15:51:27 -040092 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
93
Adrian Roos91250682017-02-06 14:48:15 -080094 private final Handler mHandler;
95 private final TrackedGarbage mTrackedGarbage;
96 private final LeakReporter mLeakReporter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -040097 private final Context mContext;
98 private final ActivityManager mAm;
99 private MemoryTile mQSTile;
100 private DumpTruck mDumpTruck;
Adrian Roos91250682017-02-06 14:48:15 -0800101
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400102 private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>();
103 private final ArrayList<Long> mPids = new ArrayList<>();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400104
105 private long mHeapLimit;
106
Jason Monk196d6392018-12-20 13:25:34 -0500107 /**
108 */
109 @Inject
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400110 public GarbageMonitor(
111 Context context,
Dave Mankofff4736812019-10-18 17:25:50 -0400112 @BgLooper Looper bgLooper,
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400113 LeakDetector leakDetector,
Adrian Roos91250682017-02-06 14:48:15 -0800114 LeakReporter leakReporter) {
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400115 mContext = context.getApplicationContext();
116 mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
117
118 mHandler = new BackgroundHeapCheckHandler(bgLooper);
119
Adrian Roos91250682017-02-06 14:48:15 -0800120 mTrackedGarbage = leakDetector.getTrackedGarbage();
121 mLeakReporter = leakReporter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400122
123 mDumpTruck = new DumpTruck(mContext);
124
125 if (ENABLE_AM_HEAP_LIMIT) {
Dan Sandler1e5d0122019-06-06 23:35:22 -0400126 mHeapLimit = Settings.Global.getInt(context.getContentResolver(),
127 SETTINGS_KEY_AM_HEAP_LIMIT,
128 mContext.getResources().getInteger(R.integer.watch_heap_limit));
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400129 }
Adrian Roos91250682017-02-06 14:48:15 -0800130 }
131
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400132 public void startLeakMonitor() {
Adrian Roos91250682017-02-06 14:48:15 -0800133 if (mTrackedGarbage == null) {
134 return;
135 }
136
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400137 mHandler.sendEmptyMessage(DO_GARBAGE_INSPECTION);
Adrian Roos91250682017-02-06 14:48:15 -0800138 }
139
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400140 public void startHeapTracking() {
141 startTrackingProcess(
142 android.os.Process.myPid(), mContext.getPackageName(), System.currentTimeMillis());
143 mHandler.sendEmptyMessage(DO_HEAP_TRACK);
Adrian Roos91250682017-02-06 14:48:15 -0800144 }
145
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400146 private boolean gcAndCheckGarbage() {
Adrian Roos91250682017-02-06 14:48:15 -0800147 if (mTrackedGarbage.countOldGarbage() > GARBAGE_ALLOWANCE) {
148 Runtime.getRuntime().gc();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400149 return true;
Adrian Roos91250682017-02-06 14:48:15 -0800150 }
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400151 return false;
Adrian Roos91250682017-02-06 14:48:15 -0800152 }
153
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400154 void reinspectGarbageAfterGc() {
Adrian Roos91250682017-02-06 14:48:15 -0800155 int count = mTrackedGarbage.countOldGarbage();
156 if (count > GARBAGE_ALLOWANCE) {
157 mLeakReporter.dumpLeak(count);
158 }
159 }
160
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400161 public ProcessMemInfo getMemInfo(int pid) {
162 return mData.get(pid);
163 }
164
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400165 public List<Long> getTrackedProcesses() {
166 return mPids;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400167 }
168
169 public void startTrackingProcess(long pid, String name, long start) {
170 synchronized (mPids) {
171 if (mPids.contains(pid)) return;
172
173 mPids.add(pid);
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400174 logPids();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400175
176 mData.put(pid, new ProcessMemInfo(pid, name, start));
177 }
178 }
179
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400180 private void logPids() {
181 if (DEBUG) {
182 StringBuffer sb = new StringBuffer("Now tracking processes: ");
183 for (int i = 0; i < mPids.size(); i++) {
184 final int p = mPids.get(i).intValue();
185 sb.append(" ");
186 }
187 Log.v(TAG, sb.toString());
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400188 }
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400189 }
190
191 private void update() {
192 synchronized (mPids) {
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400193 for (int i = 0; i < mPids.size(); i++) {
194 final int pid = mPids.get(i).intValue();
195 // rssValues contains [VmRSS, RssFile, RssAnon, VmSwap].
196 long[] rssValues = Process.getRss(pid);
197 if (rssValues == null && rssValues.length == 0) {
198 if (DEBUG) Log.e(TAG, "update: Process.getRss() didn't provide any values.");
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400199 break;
200 }
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400201 long rss = rssValues[0];
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400202 final ProcessMemInfo info = mData.get(pid);
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400203 info.rss[info.head] = info.currentRss = rss;
204 info.head = (info.head + 1) % info.rss.length;
205 if (info.currentRss > info.max) info.max = info.currentRss;
206 if (info.currentRss == 0) {
207 if (DEBUG) Log.v(TAG, "update: pid " + pid + " has rss=0, it probably died");
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400208 mData.remove(pid);
209 }
210 }
211 for (int i = mPids.size() - 1; i >= 0; i--) {
212 final long pid = mPids.get(i).intValue();
213 if (mData.get(pid) == null) {
214 mPids.remove(i);
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400215 logPids();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400216 }
217 }
218 }
219 if (mQSTile != null) mQSTile.update();
220 }
221
222 private void setTile(MemoryTile tile) {
223 mQSTile = tile;
224 if (tile != null) tile.update();
225 }
226
227 private static String formatBytes(long b) {
228 String[] SUFFIXES = {"B", "K", "M", "G", "T"};
229 int i;
230 for (i = 0; i < SUFFIXES.length; i++) {
231 if (b < 1024) break;
232 b /= 1024;
233 }
234 return b + SUFFIXES[i];
235 }
236
Dan Sandler646d66b2019-06-07 15:51:27 -0400237 private Intent dumpHprofAndGetShareIntent() {
238 return mDumpTruck.captureHeaps(getTrackedProcesses()).createShareIntent();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400239 }
240
Dan Sandler646d66b2019-06-07 15:51:27 -0400241 @Override
242 public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
243 pw.println("GarbageMonitor params:");
244 pw.println(String.format(" mHeapLimit=%d KB", mHeapLimit));
245 pw.println(String.format(" GARBAGE_INSPECTION_INTERVAL=%d (%.1f mins)",
246 GARBAGE_INSPECTION_INTERVAL,
247 (float) GARBAGE_INSPECTION_INTERVAL / DateUtils.MINUTE_IN_MILLIS));
248 final float htiMins = HEAP_TRACK_INTERVAL / DateUtils.MINUTE_IN_MILLIS;
249 pw.println(String.format(" HEAP_TRACK_INTERVAL=%d (%.1f mins)",
250 HEAP_TRACK_INTERVAL,
251 htiMins));
252 pw.println(String.format(" HEAP_TRACK_HISTORY_LEN=%d (%.1f hr total)",
253 HEAP_TRACK_HISTORY_LEN,
254 (float) HEAP_TRACK_HISTORY_LEN * htiMins / 60f));
255
256 pw.println("GarbageMonitor tracked processes:");
257
258 for (long pid : mPids) {
259 final ProcessMemInfo pmi = mData.get(pid);
260 if (pmi != null) {
261 pmi.dump(fd, pw, args);
262 }
263 }
264 }
265
266
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400267 private static class MemoryIconDrawable extends Drawable {
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400268 long rss, limit;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400269 final Drawable baseIcon;
270 final Paint paint = new Paint();
271 final float dp;
272
273 MemoryIconDrawable(Context context) {
274 baseIcon = context.getDrawable(R.drawable.ic_memory).mutate();
275 dp = context.getResources().getDisplayMetrics().density;
Dan Sandler646d66b2019-06-07 15:51:27 -0400276 paint.setColor(QSTileImpl.getColorForState(context, STATE_ACTIVE));
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400277 }
278
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400279 public void setRss(long rss) {
280 if (rss != this.rss) {
281 this.rss = rss;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400282 invalidateSelf();
283 }
284 }
285
286 public void setLimit(long limit) {
287 if (limit != this.limit) {
288 this.limit = limit;
289 invalidateSelf();
290 }
291 }
292
293 @Override
294 public void draw(Canvas canvas) {
295 baseIcon.draw(canvas);
296
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400297 if (limit > 0 && rss > 0) {
298 float frac = Math.min(1f, (float) rss / limit);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400299
300 final Rect bounds = getBounds();
301 canvas.translate(bounds.left + 8 * dp, bounds.top + 5 * dp);
302 //android:pathData="M16.0,5.0l-8.0,0.0l0.0,14.0l8.0,0.0z"
303 canvas.drawRect(0, 14 * dp * (1 - frac), 8 * dp + 1, 14 * dp + 1, paint);
304 }
305 }
306
307 @Override
308 public void setBounds(int left, int top, int right, int bottom) {
309 super.setBounds(left, top, right, bottom);
310 baseIcon.setBounds(left, top, right, bottom);
311 }
312
313 @Override
314 public int getIntrinsicHeight() {
315 return baseIcon.getIntrinsicHeight();
316 }
317
318 @Override
319 public int getIntrinsicWidth() {
320 return baseIcon.getIntrinsicWidth();
321 }
322
323 @Override
324 public void setAlpha(int i) {
325 baseIcon.setAlpha(i);
326 }
327
328 @Override
329 public void setColorFilter(ColorFilter colorFilter) {
330 baseIcon.setColorFilter(colorFilter);
331 paint.setColorFilter(colorFilter);
332 }
333
334 @Override
335 public void setTint(int tint) {
336 super.setTint(tint);
337 baseIcon.setTint(tint);
338 }
339
340 @Override
341 public void setTintList(ColorStateList tint) {
342 super.setTintList(tint);
343 baseIcon.setTintList(tint);
344 }
345
346 @Override
347 public void setTintMode(PorterDuff.Mode tintMode) {
348 super.setTintMode(tintMode);
349 baseIcon.setTintMode(tintMode);
350 }
351
352 @Override
353 public int getOpacity() {
354 return PixelFormat.TRANSLUCENT;
355 }
356 }
357
358 private static class MemoryGraphIcon extends QSTile.Icon {
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400359 long rss, limit;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400360
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400361 public void setRss(long rss) {
362 this.rss = rss;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400363 }
364
365 public void setHeapLimit(long limit) {
366 this.limit = limit;
367 }
368
369 @Override
370 public Drawable getDrawable(Context context) {
371 final MemoryIconDrawable drawable = new MemoryIconDrawable(context);
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400372 drawable.setRss(rss);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400373 drawable.setLimit(limit);
374 return drawable;
375 }
376 }
377
378 public static class MemoryTile extends QSTileImpl<QSTile.State> {
379 public static final String TILE_SPEC = "dbg:mem";
380
Dan Sandler1e5d0122019-06-06 23:35:22 -0400381 // Tell QSTileHost.java to toss this into the default tileset?
382 public static final boolean ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS = true;
383
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400384 private final GarbageMonitor gm;
Fabian Kozynski8f703632019-09-27 10:26:44 -0400385 private final ActivityStarter mActivityStarter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400386 private ProcessMemInfo pmi;
Dan Sandler646d66b2019-06-07 15:51:27 -0400387 private boolean dumpInProgress;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400388
Jason Monk5d577202018-12-26 15:43:06 -0500389 @Inject
Fabian Kozynski8f703632019-09-27 10:26:44 -0400390 public MemoryTile(QSHost host, GarbageMonitor monitor, ActivityStarter starter) {
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400391 super(host);
Robert Snoeberger81d76152019-09-30 15:43:34 -0400392 gm = monitor;
Fabian Kozynski8f703632019-09-27 10:26:44 -0400393 mActivityStarter = starter;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400394 }
395
396 @Override
397 public State newTileState() {
398 return new QSTile.State();
399 }
400
401 @Override
402 public Intent getLongClickIntent() {
403 return new Intent();
404 }
405
406 @Override
407 protected void handleClick() {
Dan Sandler646d66b2019-06-07 15:51:27 -0400408 if (dumpInProgress) return;
409
410 dumpInProgress = true;
411 refreshState();
412 new Thread("HeapDumpThread") {
413 @Override
414 public void run() {
415 try {
416 // wait for animations & state changes
417 Thread.sleep(500);
418 } catch (InterruptedException ignored) { }
419 final Intent shareIntent = gm.dumpHprofAndGetShareIntent();
420 mHandler.post(() -> {
421 dumpInProgress = false;
422 refreshState();
423 getHost().collapsePanels();
Fabian Kozynski8f703632019-09-27 10:26:44 -0400424 mActivityStarter.postStartActivityDismissingKeyguard(shareIntent, 0);
Dan Sandler646d66b2019-06-07 15:51:27 -0400425 });
426 }
427 }.start();
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400428 }
429
430 @Override
431 public int getMetricsCategory() {
432 return VIEW_UNKNOWN;
433 }
434
435 @Override
436 public void handleSetListening(boolean listening) {
437 if (gm != null) gm.setTile(listening ? this : null);
438
439 final ActivityManager am = mContext.getSystemService(ActivityManager.class);
440 if (listening && gm.mHeapLimit > 0) {
441 am.setWatchHeapLimit(1024 * gm.mHeapLimit); // why is this in bytes?
442 } else {
443 am.clearWatchHeapLimit();
444 }
445 }
446
447 @Override
448 public CharSequence getTileLabel() {
449 return getState().label;
450 }
451
452 @Override
453 protected void handleUpdateState(State state, Object arg) {
454 pmi = gm.getMemInfo(Process.myPid());
455 final MemoryGraphIcon icon = new MemoryGraphIcon();
456 icon.setHeapLimit(gm.mHeapLimit);
Dan Sandler646d66b2019-06-07 15:51:27 -0400457 state.state = dumpInProgress ? STATE_UNAVAILABLE : STATE_ACTIVE;
458 state.label = dumpInProgress
459 ? "Dumping..."
460 : mContext.getString(R.string.heap_dump_tile_name);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400461 if (pmi != null) {
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400462 icon.setRss(pmi.currentRss);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400463 state.secondaryLabel =
464 String.format(
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400465 "rss: %s / %s",
466 formatBytes(pmi.currentRss * 1024),
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400467 formatBytes(gm.mHeapLimit * 1024));
468 } else {
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400469 icon.setRss(0);
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400470 state.secondaryLabel = null;
471 }
472 state.icon = icon;
473 }
474
475 public void update() {
476 refreshState();
477 }
478
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400479 public long getRss() {
480 return pmi != null ? pmi.currentRss : 0;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400481 }
482
483 public long getHeapLimit() {
484 return gm != null ? gm.mHeapLimit : 0;
485 }
486 }
487
Dan Sandler646d66b2019-06-07 15:51:27 -0400488 /** */
489 public static class ProcessMemInfo implements Dumpable {
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400490 public long pid;
491 public String name;
492 public long startTime;
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400493 public long currentRss;
494 public long[] rss = new long[HEAP_TRACK_HISTORY_LEN];
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400495 public long max = 1;
496 public int head = 0;
497
498 public ProcessMemInfo(long pid, String name, long start) {
499 this.pid = pid;
500 this.name = name;
501 this.startTime = start;
502 }
503
504 public long getUptime() {
505 return System.currentTimeMillis() - startTime;
506 }
Dan Sandler646d66b2019-06-07 15:51:27 -0400507
508 @Override
509 public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
510 pw.print("{ \"pid\": ");
511 pw.print(pid);
512 pw.print(", \"name\": \"");
513 pw.print(name.replace('"', '-'));
514 pw.print("\", \"start\": ");
515 pw.print(startTime);
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400516 pw.print(", \"rss\": [");
517 // write rss values starting from the oldest, which is rss[head], wrapping around to
518 // rss[(head-1) % rss.length]
519 for (int i = 0; i < rss.length; i++) {
Dan Sandler646d66b2019-06-07 15:51:27 -0400520 if (i > 0) pw.print(",");
Robert Snoebergerd78a8cf2019-10-04 16:28:31 -0400521 pw.print(rss[(head + i) % rss.length]);
Dan Sandler646d66b2019-06-07 15:51:27 -0400522 }
523 pw.println("] }");
524 }
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400525 }
526
Dan Sandler646d66b2019-06-07 15:51:27 -0400527 /** */
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400528 @Singleton
Dan Sandler646d66b2019-06-07 15:51:27 -0400529 public static class Service extends SystemUI implements Dumpable {
Robert Snoeberger81d76152019-09-30 15:43:34 -0400530 private final GarbageMonitor mGarbageMonitor;
531
532 @Inject
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400533 public Service(Context context, GarbageMonitor garbageMonitor) {
534 super(context);
Robert Snoeberger81d76152019-09-30 15:43:34 -0400535 mGarbageMonitor = garbageMonitor;
536 }
Adrian Roos91250682017-02-06 14:48:15 -0800537
538 @Override
539 public void start() {
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400540 boolean forceEnable =
541 Settings.Secure.getInt(
542 mContext.getContentResolver(), FORCE_ENABLE_LEAK_REPORTING, 0)
543 != 0;
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400544 if (LEAK_REPORTING_ENABLED || forceEnable) {
545 mGarbageMonitor.startLeakMonitor();
546 }
547 if (HEAP_TRACKING_ENABLED || forceEnable) {
548 mGarbageMonitor.startHeapTracking();
549 }
550 }
Dan Sandler646d66b2019-06-07 15:51:27 -0400551
552 @Override
553 public void dump(@Nullable FileDescriptor fd, PrintWriter pw, @Nullable String[] args) {
554 if (mGarbageMonitor != null) mGarbageMonitor.dump(fd, pw, args);
555 }
Dan Sandler4d90d1e2018-03-23 16:29:06 -0400556 }
557
558 private class BackgroundHeapCheckHandler extends Handler {
559 BackgroundHeapCheckHandler(Looper onLooper) {
560 super(onLooper);
561 if (Looper.getMainLooper().equals(onLooper)) {
562 throw new RuntimeException(
563 "BackgroundHeapCheckHandler may not run on the ui thread");
564 }
565 }
566
567 @Override
568 public void handleMessage(Message m) {
569 switch (m.what) {
570 case DO_GARBAGE_INSPECTION:
571 if (gcAndCheckGarbage()) {
572 postDelayed(GarbageMonitor.this::reinspectGarbageAfterGc, 100);
573 }
574
575 removeMessages(DO_GARBAGE_INSPECTION);
576 sendEmptyMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
577 break;
578
579 case DO_HEAP_TRACK:
580 update();
581 removeMessages(DO_HEAP_TRACK);
582 sendEmptyMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
583 break;
584 }
Adrian Roos91250682017-02-06 14:48:15 -0800585 }
586 }
587}