Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ceefcec3 authored by Collin Fijalkovich's avatar Collin Fijalkovich
Browse files

Add cache debugging information to bugreports.

Following the model for dumpsys gfxinfo, this patchset adds a
CacheBinder service that dumps cache state information from each
process.

Bug: 153661880
Test: adb shell dumpsys cacheinfo
Test: adb bugreport

Change-Id: Ie7cce70e56777a200e3e3e92ab895126b6f29032
parent 8c239da8
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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();
+1 −0
Original line number Diff line number Diff line
@@ -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);
+87 −1
Original line number Diff line number Diff line
@@ -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<Query, Result> {
     */
    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<Query, Result> {
     */
    public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
        mPropertyName = propertyName;
        mMaxEntries = maxEntries;
        mCache = new LinkedHashMap<Query, Result>(
            2 /* start small */,
            0.75f /* default load factor */,
@@ -728,11 +740,85 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
    }

    /**
     * Return a list of caches alive at the current time.
     * Returns a list of caches alive at the current time.
     */
    public static ArrayList<PropertyInvalidatedCache> getActiveCaches() {
        synchronized (sCorkLock) {
            return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet());
        }
    }

    /**
     * Returns a list of the active corks in a process.
     */
    public static ArrayList<Map.Entry<String, Integer>> getActiveCorks() {
        synchronized (sCorkLock) {
            return new ArrayList<Map.Entry<String, Integer>>(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<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
            if (cacheEntries.size() == 0) {
                pw.println("");
                return;
            }

            pw.println("");
            pw.println("    Contents:");
            for (Map.Entry<Query, Result> 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<PropertyInvalidatedCache> activeCaches;
        ArrayList<Map.Entry<String, Integer>> 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<String, Integer> 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");
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -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 {
+95 −27
Original line number Diff line number Diff line
@@ -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,6 +2192,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            try {
                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
                    Process.enableFreezer(false);
                }
@@ -2198,12 +2200,13 @@ public class ActivityManagerService extends IActivityManager.Stub
                if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                        "meminfo", pw)) return;
                PriorityDump.dump(mPriorityDumper, fd, pw, args);
            } finally {
                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
                    Process.enableFreezer(true);
                }
            }
        }
    }
    static class GraphicsBinder extends Binder {
        ActivityManagerService mActivityManagerService;
@@ -2213,6 +2216,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            try {
                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
                    Process.enableFreezer(false);
                }
@@ -2220,12 +2224,13 @@ public class ActivityManagerService extends IActivityManager.Stub
                if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                        "gfxinfo", pw)) return;
                mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
            } finally {
                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
                    Process.enableFreezer(true);
                }
            }
        }
    }
    static class DbBinder extends Binder {
        ActivityManagerService mActivityManagerService;
@@ -2235,6 +2240,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            try {
                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
                    Process.enableFreezer(false);
                }
@@ -2242,12 +2248,13 @@ public class ActivityManagerService extends IActivityManager.Stub
                if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                        "dbinfo", pw)) return;
                mActivityManagerService.dumpDbInfo(fd, pw, args);
            } finally {
                if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
                    Process.enableFreezer(true);
                }
            }
        }
    }
    static class CpuBinder extends Binder {
        ActivityManagerService mActivityManagerService;
@@ -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;
@@ -12712,6 +12747,39 @@ public class ActivityManagerService extends IActivityManager.Stub
        }
    }
    final void dumpBinderCacheContents(FileDescriptor fd, PrintWriter pw, String[] args) {
        ArrayList<ProcessRecord> 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<ProcessRecord> procs = collectProcesses(pw, 0, false, args);
        if (procs == null) {