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

Commit 777a06d3 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix issue #28641630: Service client dumps are timing out for no reason

Actually, no reason.  The reason is that to do the dump we are doing
synchronous calls out to each service, but that is with the activity
manager lock held, so they can get blocked if their main thread does
any call back in to the activity manager.

To fix this, re-organize the dumping code so that the "with client"
path is separate, and doesn't require holding a lock the entire
time.

Change-Id: Ia96861c10da81048b3d2ac93a25760b68623cf34
parent 3f84c5cb
Loading
Loading
Loading
Loading
+313 −168
Original line number Original line Diff line number Diff line
@@ -2805,28 +2805,63 @@ public final class ActiveServices {
    /**
    /**
     * Prints a list of ServiceRecords (dumpsys activity services)
     * Prints a list of ServiceRecords (dumpsys activity services)
     */
     */
    void dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
    List<ServiceRecord> collectServicesToDumpLocked(ItemMatcher matcher, String dumpPackage) {
            int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
        final ArrayList<ServiceRecord> services = new ArrayList<>();
        boolean needSep = false;
        final int[] users = mAm.mUserController.getUsers();
        boolean printedAnything = false;
        for (int user : users) {
            ServiceMap smap = getServiceMap(user);
            if (smap.mServicesByName.size() > 0) {
                for (int si=0; si<smap.mServicesByName.size(); si++) {
                    ServiceRecord r = smap.mServicesByName.valueAt(si);
                    if (!matcher.match(r, r.name)) {
                        continue;
                    }
                    if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
                        continue;
                    }
                    services.add(r);
                }
            }
        }


        ItemMatcher matcher = new ItemMatcher();
        return services;
    }

    final class ServiceDumper {
        private final FileDescriptor fd;
        private final PrintWriter pw;
        private final String[] args;
        private final int opti;
        private final boolean dumpAll;
        private final String dumpPackage;
        private final ItemMatcher matcher;
        private final ArrayList<ServiceRecord> services = new ArrayList<>();

        private final long nowReal = SystemClock.elapsedRealtime();

        private boolean needSep = false;
        private boolean printedAnything = false;
        private boolean printed = false;

        /**
         * Note: do not call directly, use {@link #newServiceDumperLocked} instead (this
         * must be called with the lock held).
         */
        ServiceDumper(FileDescriptor fd, PrintWriter pw, String[] args,
                int opti, boolean dumpAll, String dumpPackage) {
            this.fd = fd;
            this.pw = pw;
            this.args = args;
            this.opti = opti;
            this.dumpAll = dumpAll;
            this.dumpPackage = dumpPackage;
            matcher = new ItemMatcher();
            matcher.build(args, opti);
            matcher.build(args, opti);


        pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
            final int[] users = mAm.mUserController.getUsers();
        try {
            if (mLastAnrDump != null) {
                pw.println("  Last ANR service:");
                pw.print(mLastAnrDump);
                pw.println();
            }
            int[] users = mAm.mUserController.getUsers();
            for (int user : users) {
            for (int user : users) {
                ServiceMap smap = getServiceMap(user);
                ServiceMap smap = getServiceMap(user);
                boolean printed = false;
                if (smap.mServicesByName.size() > 0) {
                if (smap.mServicesByName.size() > 0) {
                    long nowReal = SystemClock.elapsedRealtime();
                    needSep = false;
                    for (int si=0; si<smap.mServicesByName.size(); si++) {
                    for (int si=0; si<smap.mServicesByName.size(); si++) {
                        ServiceRecord r = smap.mServicesByName.valueAt(si);
                        ServiceRecord r = smap.mServicesByName.valueAt(si);
                        if (!matcher.match(r, r.name)) {
                        if (!matcher.match(r, r.name)) {
@@ -2835,6 +2870,99 @@ public final class ActiveServices {
                        if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
                        if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
                            continue;
                            continue;
                        }
                        }
                        services.add(r);
                    }
                }
            }
        }

        private void dumpHeaderLocked() {
            pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
            if (mLastAnrDump != null) {
                pw.println("  Last ANR service:");
                pw.print(mLastAnrDump);
                pw.println();
            }
        }

        void dumpLocked() {
            dumpHeaderLocked();

            try {
                int[] users = mAm.mUserController.getUsers();
                for (int user : users) {
                    // Find the first service for this user.
                    int serviceIdx = 0;
                    while (serviceIdx < services.size() && services.get(serviceIdx).userId != user) {
                        serviceIdx++;
                    }
                    printed = false;
                    if (serviceIdx < services.size()) {
                        needSep = false;
                        while (serviceIdx < services.size()) {
                            ServiceRecord r = services.get(serviceIdx);
                            serviceIdx++;
                            if (r.userId != user) {
                                break;
                            }
                            dumpServiceLocalLocked(r);
                        }
                        needSep |= printed;
                    }

                    dumpUserRemainsLocked(user);
                }
            } catch (Exception e) {
                Slog.w(TAG, "Exception in dumpServicesLocked", e);
            }

            dumpRemainsLocked();
        }

        void dumpWithClient() {
            synchronized(mAm) {
                dumpHeaderLocked();
            }

            try {
                int[] users = mAm.mUserController.getUsers();
                for (int user : users) {
                    // Find the first service for this user.
                    int serviceIdx = 0;
                    while (serviceIdx < services.size() && services.get(serviceIdx).userId != user) {
                        serviceIdx++;
                    }
                    printed = false;
                    if (serviceIdx < services.size()) {
                        needSep = false;
                        while (serviceIdx < services.size()) {
                            ServiceRecord r = services.get(serviceIdx);
                            serviceIdx++;
                            if (r.userId != user) {
                                break;
                            }
                            synchronized(mAm) {
                                dumpServiceLocalLocked(r);
                            }
                            dumpServiceClient(r);
                        }
                        needSep |= printed;
                    }

                    synchronized(mAm) {
                        dumpUserRemainsLocked(user);
                    }
                }
            } catch (Exception e) {
                Slog.w(TAG, "Exception in dumpServicesLocked", e);
            }

            synchronized(mAm) {
                dumpRemainsLocked();
            }
        }

        private void dumpUserHeaderLocked(int user) {
            if (!printed) {
            if (!printed) {
                if (printedAnything) {
                if (printedAnything) {
                    pw.println();
                    pw.println();
@@ -2846,6 +2974,10 @@ public final class ActiveServices {
            if (needSep) {
            if (needSep) {
                pw.println();
                pw.println();
            }
            }
        }

        private void dumpServiceLocalLocked(ServiceRecord r) {
            dumpUserHeaderLocked(r.userId);
            pw.print("  * ");
            pw.print("  * ");
            pw.println(r);
            pw.println(r);
            if (dumpAll) {
            if (dumpAll) {
@@ -2876,14 +3008,23 @@ public final class ActiveServices {
                    }
                    }
                }
                }
            }
            }
                        if (dumpClient && r.app != null && r.app.thread != null) {
        }

        private void dumpServiceClient(ServiceRecord r) {
            final ProcessRecord proc = r.app;
            if (proc == null) {
                return;
            }
            final IApplicationThread thread = proc.thread;
            if (thread == null) {
                return;
            }
            pw.println("    Client:");
            pw.println("    Client:");
            pw.flush();
            pw.flush();
            try {
            try {
                TransferPipe tp = new TransferPipe();
                TransferPipe tp = new TransferPipe();
                try {
                try {
                                    r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
                    thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args);
                                            r, args);
                    tp.setBufferPrefix("      ");
                    tp.setBufferPrefix("      ");
                    // Short timeout, since blocking here can
                    // Short timeout, since blocking here can
                    // deadlock with the application.
                    // deadlock with the application.
@@ -2898,9 +3039,9 @@ public final class ActiveServices {
            }
            }
            needSep = true;
            needSep = true;
        }
        }
                    }

                    needSep |= printed;
        private void dumpUserRemainsLocked(int user) {
                }
            ServiceMap smap = getServiceMap(user);
            printed = false;
            printed = false;
            for (int si=0, SN=smap.mDelayedStartList.size(); si<SN; si++) {
            for (int si=0, SN=smap.mDelayedStartList.size(); si<SN; si++) {
                ServiceRecord r = smap.mDelayedStartList.get(si);
                ServiceRecord r = smap.mDelayedStartList.get(si);
@@ -2940,12 +3081,10 @@ public final class ActiveServices {
                pw.print("  * Starting bg "); pw.println(r);
                pw.print("  * Starting bg "); pw.println(r);
            }
            }
        }
        }
        } catch (Exception e) {
            Slog.w(TAG, "Exception in dumpServicesLocked", e);
        }


        private void dumpRemainsLocked() {
            if (mPendingServices.size() > 0) {
            if (mPendingServices.size() > 0) {
            boolean printed = false;
                printed = false;
                for (int i=0; i<mPendingServices.size(); i++) {
                for (int i=0; i<mPendingServices.size(); i++) {
                    ServiceRecord r = mPendingServices.get(i);
                    ServiceRecord r = mPendingServices.get(i);
                    if (!matcher.match(r, r.name)) {
                    if (!matcher.match(r, r.name)) {
@@ -2968,7 +3107,7 @@ public final class ActiveServices {
            }
            }


            if (mRestartingServices.size() > 0) {
            if (mRestartingServices.size() > 0) {
            boolean printed = false;
                printed = false;
                for (int i=0; i<mRestartingServices.size(); i++) {
                for (int i=0; i<mRestartingServices.size(); i++) {
                    ServiceRecord r = mRestartingServices.get(i);
                    ServiceRecord r = mRestartingServices.get(i);
                    if (!matcher.match(r, r.name)) {
                    if (!matcher.match(r, r.name)) {
@@ -2991,7 +3130,7 @@ public final class ActiveServices {
            }
            }


            if (mDestroyingServices.size() > 0) {
            if (mDestroyingServices.size() > 0) {
            boolean printed = false;
                printed = false;
                for (int i=0; i< mDestroyingServices.size(); i++) {
                for (int i=0; i< mDestroyingServices.size(); i++) {
                    ServiceRecord r = mDestroyingServices.get(i);
                    ServiceRecord r = mDestroyingServices.get(i);
                    if (!matcher.match(r, r.name)) {
                    if (!matcher.match(r, r.name)) {
@@ -3014,7 +3153,7 @@ public final class ActiveServices {
            }
            }


            if (dumpAll) {
            if (dumpAll) {
            boolean printed = false;
                printed = false;
                for (int ic=0; ic<mServiceConnections.size(); ic++) {
                for (int ic=0; ic<mServiceConnections.size(); ic++) {
                    ArrayList<ConnectionRecord> r = mServiceConnections.valueAt(ic);
                    ArrayList<ConnectionRecord> r = mServiceConnections.valueAt(ic);
                    for (int i=0; i<r.size(); i++) {
                    for (int i=0; i<r.size(); i++) {
@@ -3043,6 +3182,12 @@ public final class ActiveServices {
                pw.println("  (nothing)");
                pw.println("  (nothing)");
            }
            }
        }
        }
    }

    ServiceDumper newServiceDumperLocked(FileDescriptor fd, PrintWriter pw, String[] args,
            int opti, boolean dumpAll, String dumpPackage) {
        return new ServiceDumper(fd, pw, args, opti, dumpAll, dumpPackage);
    }


    /**
    /**
     * There are three ways to call this:
     * There are three ways to call this:
+105 −40
Original line number Original line Diff line number Diff line
@@ -13771,8 +13771,18 @@ public final class ActivityManagerService extends ActivityManagerNative
                    dumpAssociationsLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
                    dumpAssociationsLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
                }
                }
            } else if ("services".equals(cmd) || "s".equals(cmd)) {
            } else if ("services".equals(cmd) || "s".equals(cmd)) {
                if (dumpClient) {
                    ActiveServices.ServiceDumper dumper;
                    synchronized (this) {
                    synchronized (this) {
                    mServices.dumpServicesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
                        dumper = mServices.newServiceDumperLocked(fd, pw, args, opti, true,
                                dumpPackage);
                    }
                    dumper.dumpWithClient();
                } else {
                    synchronized (this) {
                        mServices.newServiceDumperLocked(fd, pw, args, opti, true,
                                dumpPackage).dumpLocked();
                    }
                }
                }
            } else if ("locks".equals(cmd)) {
            } else if ("locks".equals(cmd)) {
                LockGuard.dump(fd, pw, args);
                LockGuard.dump(fd, pw, args);
@@ -13794,6 +13804,8 @@ public final class ActivityManagerService extends ActivityManagerNative
        }
        }
        // No piece of data specified, dump everything.
        // No piece of data specified, dump everything.
        if (dumpClient) {
            ActiveServices.ServiceDumper sdumper;
            synchronized (this) {
            synchronized (this) {
                dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                pw.println();
                pw.println();
@@ -13815,8 +13827,12 @@ public final class ActivityManagerService extends ActivityManagerNative
                if (dumpAll) {
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                    pw.println("-------------------------------------------------------------------------------");
                }
                }
            mServices.dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
                sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll,
                        dumpPackage);
            }
            sdumper.dumpWithClient();
            pw.println();
            pw.println();
            synchronized (this) {
                if (dumpAll) {
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                    pw.println("-------------------------------------------------------------------------------");
                }
                }
@@ -13839,6 +13855,55 @@ public final class ActivityManagerService extends ActivityManagerNative
                }
                }
                dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage);
            }
            }
        } else {
            synchronized (this) {
                dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                pw.println();
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                }
                dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                pw.println();
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                }
                dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                pw.println();
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                }
                dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                pw.println();
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                }
                mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, dumpPackage)
                        .dumpLocked();
                pw.println();
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                }
                dumpRecentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
                pw.println();
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                }
                dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
                if (mAssociations.size() > 0) {
                    pw.println();
                    if (dumpAll) {
                        pw.println("-------------------------------------------------------------------------------");
                    }
                    dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
                }
                pw.println();
                if (dumpAll) {
                    pw.println("-------------------------------------------------------------------------------");
                }
                dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage);
            }
        }
        Binder.restoreCallingIdentity(origId);
        Binder.restoreCallingIdentity(origId);
    }
    }
@@ -16142,8 +16207,8 @@ public final class ActivityManagerService extends ActivityManagerNative
            catPw.println();
            catPw.println();
            dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null);
            dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null);
            catPw.println();
            catPw.println();
            mServices.dumpServicesLocked(null, catPw, emptyArgs, 0,
            mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0,
                    false, false, null);
                    false, null).dumpLocked();
            catPw.println();
            catPw.println();
            dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null);
            dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null);
            catPw.flush();
            catPw.flush();