diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 240991e8fcbf2..8e43ca3c67390 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1538,6 +1538,12 @@ public final class ActivityThread extends ClientTransactionHandler { IoUtils.closeQuietly(pfd); } + @Override + public void dumpCacheInfo(ParcelFileDescriptor pfd, String[] args) { + PropertyInvalidatedCache.dumpCacheInfo(pfd.getFileDescriptor(), args); + IoUtils.closeQuietly(pfd); + } + private File getDatabasesDir(Context context) { // There's no simple way to get the databases/ path, so do it this way. return context.getDatabasePath("a").getParentFile(); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 1f6e4cac199a4..6e9157e2a8c32 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -119,6 +119,7 @@ oneway interface IApplicationThread { boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable, in String[] args); void dumpGfxInfo(in ParcelFileDescriptor fd, in String[] args); + void dumpCacheInfo(in ParcelFileDescriptor fd, in String[] args); void dumpProvider(in ParcelFileDescriptor fd, IBinder servicetoken, in String[] args); void dumpDbInfo(in ParcelFileDescriptor fd, in String[] args); diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 58705434f7745..01cf2b94a842c 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -26,13 +26,19 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FastPrintWriter; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.Random; +import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; @@ -234,6 +240,11 @@ public abstract class PropertyInvalidatedCache { */ private boolean mDisabled = false; + /** + * Maximum number of entries the cache will maintain. + */ + private final int mMaxEntries; + /** * Make a new property invalidated cache. * @@ -242,6 +253,7 @@ public abstract class PropertyInvalidatedCache { */ public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) { mPropertyName = propertyName; + mMaxEntries = maxEntries; mCache = new LinkedHashMap( 2 /* start small */, 0.75f /* default load factor */, @@ -728,11 +740,85 @@ public abstract class PropertyInvalidatedCache { } /** - * Return a list of caches alive at the current time. + * Returns a list of caches alive at the current time. */ public static ArrayList getActiveCaches() { synchronized (sCorkLock) { return new ArrayList(sCaches.keySet()); } } + + /** + * Returns a list of the active corks in a process. + */ + public static ArrayList> getActiveCorks() { + synchronized (sCorkLock) { + return new ArrayList>(sCorks.entrySet()); + } + } + + private void dumpContents(PrintWriter pw, String[] args) { + synchronized (mLock) { + pw.println(String.format(" Cache Property Name: %s", cacheName())); + pw.println(String.format(" Last Observed Nonce: %d", mLastSeenNonce)); + pw.println(String.format(" Current Size: %d, Max Size: %d", + mCache.entrySet().size(), mMaxEntries)); + pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true")); + + Set> cacheEntries = mCache.entrySet(); + if (cacheEntries.size() == 0) { + pw.println(""); + return; + } + + pw.println(""); + pw.println(" Contents:"); + for (Map.Entry entry : cacheEntries) { + String key = Objects.toString(entry.getKey()); + String value = Objects.toString(entry.getValue()); + + pw.println(String.format(" Key: %s\n Value: %s\n", key, value)); + } + } + } + + /** + * Dumps contents of every cache in the process to the provided FileDescriptor. + */ + public static void dumpCacheInfo(FileDescriptor fd, String[] args) { + ArrayList activeCaches; + ArrayList> activeCorks; + + try ( + FileOutputStream fout = new FileOutputStream(fd); + PrintWriter pw = new FastPrintWriter(fout); + ) { + if (!sEnabled) { + pw.println(" Caching is disabled in this process."); + return; + } + + synchronized (sCorkLock) { + activeCaches = getActiveCaches(); + activeCorks = getActiveCorks(); + + if (activeCorks.size() > 0) { + pw.println(" Corking Status:"); + for (int i = 0; i < activeCorks.size(); i++) { + Map.Entry entry = activeCorks.get(i); + pw.println(String.format(" Property Name: %s Count: %d", + entry.getKey(), entry.getValue())); + } + } + } + + for (int i = 0; i < activeCaches.size(); i++) { + PropertyInvalidatedCache currentCache = activeCaches.get(i); + currentCache.dumpContents(pw, args); + pw.flush(); + } + } catch (IOException e) { + Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances"); + } + } } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 3f8d9ef964db8..f11adef817936 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -562,6 +562,11 @@ public class TransactionParcelTests { throws RemoteException { } + @Override + public void dumpCacheInfo(ParcelFileDescriptor parcelFileDescriptor, String[] strings) + throws RemoteException { + } + @Override public void dumpProvider(ParcelFileDescriptor parcelFileDescriptor, IBinder iBinder, String[] strings) throws RemoteException { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2b86d7fe057ed..37f1ad100eee0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2098,6 +2098,7 @@ public class ActivityManagerService extends IActivityManager.Stub } ServiceManager.addService("permission", new PermissionController(this)); ServiceManager.addService("processinfo", new ProcessInfoService(this)); + ServiceManager.addService("cacheinfo", new CacheBinder(this)); ApplicationInfo info = mContext.getPackageManager().getApplicationInfo( "android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY); @@ -2191,16 +2192,18 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + try { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(false); + } - if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, - "meminfo", pw)) return; - PriorityDump.dump(mPriorityDumper, fd, pw, args); - - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "meminfo", pw)) return; + PriorityDump.dump(mPriorityDumper, fd, pw, args); + } finally { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(true); + } } } } @@ -2213,16 +2216,18 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + try { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(false); + } - if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, - "gfxinfo", pw)) return; - mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args); - - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "gfxinfo", pw)) return; + mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args); + } finally { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(true); + } } } } @@ -2235,16 +2240,18 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + try { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(false); + } - if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, - "dbinfo", pw)) return; - mActivityManagerService.dumpDbInfo(fd, pw, args); - - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "dbinfo", pw)) return; + mActivityManagerService.dumpDbInfo(fd, pw, args); + } finally { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(true); + } } } } @@ -2280,6 +2287,34 @@ public class ActivityManagerService extends IActivityManager.Stub } } + static class CacheBinder extends Binder { + ActivityManagerService mActivityManagerService; + + CacheBinder(ActivityManagerService activityManagerService) { + mActivityManagerService = activityManagerService; + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + try { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(false); + } + + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "cacheinfo", pw)) { + return; + } + + mActivityManagerService.dumpBinderCacheContents(fd, pw, args); + } finally { + if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { + Process.enableFreezer(true); + } + } + } + } + public static final class Lifecycle extends SystemService { private final ActivityManagerService mService; private static ActivityTaskManagerService sAtm; @@ -12722,6 +12757,39 @@ public class ActivityManagerService extends IActivityManager.Stub } } + final void dumpBinderCacheContents(FileDescriptor fd, PrintWriter pw, String[] args) { + ArrayList procs = collectProcesses(pw, 0, false, args); + if (procs == null) { + pw.println("No process found for: " + args[0]); + return; + } + + pw.println("Per-process Binder Cache Contents"); + + for (int i = procs.size() - 1; i >= 0; i--) { + ProcessRecord r = procs.get(i); + if (r.thread != null) { + pw.println("\n\n** Cache info for pid " + r.pid + " [" + r.processName + "] **"); + pw.flush(); + try { + TransferPipe tp = new TransferPipe(); + try { + r.thread.dumpCacheInfo(tp.getWriteFd(), args); + tp.go(fd); + } finally { + tp.kill(); + } + } catch (IOException e) { + pw.println("Failure while dumping the app " + r); + pw.flush(); + } catch (RemoteException e) { + pw.println("Got a RemoteException while dumping the app " + r); + pw.flush(); + } + } + } + } + final void dumpDbInfo(FileDescriptor fd, PrintWriter pw, String[] args) { ArrayList procs = collectProcesses(pw, 0, false, args); if (procs == null) {