From 777a06d3c932277649ebeb6ccb3f20e80aa867b3 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 16 May 2016 17:36:15 -0700 Subject: [PATCH] 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 --- .../com/android/server/am/ActiveServices.java | 547 +++++++++++------- .../server/am/ActivityManagerService.java | 147 +++-- 2 files changed, 452 insertions(+), 242 deletions(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 36f51fcca1f86..95dee019dfa93 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2805,28 +2805,63 @@ public final class ActiveServices { /** * Prints a list of ServiceRecords (dumpsys activity services) */ - void dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { - boolean needSep = false; - boolean printedAnything = false; - - ItemMatcher matcher = new ItemMatcher(); - matcher.build(args, opti); - - pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)"); - try { - if (mLastAnrDump != null) { - pw.println(" Last ANR service:"); - pw.print(mLastAnrDump); - pw.println(); + List collectServicesToDumpLocked(ItemMatcher matcher, String dumpPackage) { + final ArrayList services = new ArrayList<>(); + final int[] users = mAm.mUserController.getUsers(); + for (int user : users) { + ServiceMap smap = getServiceMap(user); + if (smap.mServicesByName.size() > 0) { + for (int si=0; si 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); + + final int[] users = mAm.mUserController.getUsers(); for (int user : users) { ServiceMap smap = getServiceMap(user); - boolean printed = false; if (smap.mServicesByName.size() > 0) { - long nowReal = SystemClock.elapsedRealtime(); - needSep = false; for (int si=0; si clist = r.connections.valueAt(conni); - for (int i = 0; i < clist.size(); i++) { - ConnectionRecord conn = clist.get(i); - pw.print(" "); - pw.print(conn.binding.intent.intent.getIntent() - .toShortString(false, false, false, false)); - pw.print(" -> "); - ProcessRecord proc = conn.binding.client; - pw.println(proc != null ? proc.toShortString() : "null"); - } - } + 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; } - } - if (dumpClient && r.app != null && r.app.thread != null) { - pw.println(" Client:"); - pw.flush(); - try { - TransferPipe tp = new TransferPipe(); - try { - r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(), - r, args); - tp.setBufferPrefix(" "); - // Short timeout, since blocking here can - // deadlock with the application. - tp.go(fd, 2000); - } finally { - tp.kill(); - } - } catch (IOException e) { - pw.println(" Failure while dumping the service: " + e); - } catch (RemoteException e) { - pw.println(" Got a RemoteException while dumping the service"); + synchronized(mAm) { + dumpServiceLocalLocked(r); } - needSep = true; + 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 (printedAnything) { + pw.println(); + } + pw.println(" User " + user + " active services:"); + printed = true; + } + printedAnything = true; + if (needSep) { + pw.println(); + } + } + + private void dumpServiceLocalLocked(ServiceRecord r) { + dumpUserHeaderLocked(r.userId); + pw.print(" * "); + pw.println(r); + if (dumpAll) { + r.dump(pw, " "); + needSep = true; + } else { + pw.print(" app="); + pw.println(r.app); + pw.print(" created="); + TimeUtils.formatDuration(r.createTime, nowReal, pw); + pw.print(" started="); + pw.print(r.startRequested); + pw.print(" connections="); + pw.println(r.connections.size()); + if (r.connections.size() > 0) { + pw.println(" Connections:"); + for (int conni=0; conni clist = r.connections.valueAt(conni); + for (int i = 0; i < clist.size(); i++) { + ConnectionRecord conn = clist.get(i); + pw.print(" "); + pw.print(conn.binding.intent.intent.getIntent() + .toShortString(false, false, false, false)); + pw.print(" -> "); + ProcessRecord proc = conn.binding.client; + pw.println(proc != null ? proc.toShortString() : "null"); } } - needSep |= printed; } + } + } + + 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.flush(); + try { + TransferPipe tp = new TransferPipe(); + try { + thread.dumpService(tp.getWriteFd().getFileDescriptor(), r, args); + tp.setBufferPrefix(" "); + // Short timeout, since blocking here can + // deadlock with the application. + tp.go(fd, 2000); + } finally { + tp.kill(); + } + } catch (IOException e) { + pw.println(" Failure while dumping the service: " + e); + } catch (RemoteException e) { + pw.println(" Got a RemoteException while dumping the service"); + } + needSep = true; + } + + private void dumpUserRemainsLocked(int user) { + ServiceMap smap = getServiceMap(user); + printed = false; + for (int si=0, SN=smap.mDelayedStartList.size(); si 0) { printed = false; - for (int si=0, SN=smap.mDelayedStartList.size(); si 0) { - boolean printed = false; - for (int i=0; i 0) { - boolean printed = false; - for (int i=0; i 0) { - boolean printed = false; - for (int i=0; i< mDestroyingServices.size(); i++) { - ServiceRecord r = mDestroyingServices.get(i); - if (!matcher.match(r, r.name)) { - continue; - } - if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { - continue; - } - printedAnything = true; - if (!printed) { - if (needSep) pw.println(); - needSep = true; - pw.println(" Destroying services:"); - printed = true; - } - pw.print(" * Destroy "); pw.println(r); - r.dump(pw, " "); - } - needSep = true; - } - - if (dumpAll) { - boolean printed = false; - for (int ic=0; ic r = mServiceConnections.valueAt(ic); - for (int i=0; i 0) { + printed = false; + for (int i=0; i 0) { + printed = false; + for (int i=0; i< mDestroyingServices.size(); i++) { + ServiceRecord r = mDestroyingServices.get(i); + if (!matcher.match(r, r.name)) { + continue; + } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + printedAnything = true; + if (!printed) { + if (needSep) pw.println(); + needSep = true; + pw.println(" Destroying services:"); + printed = true; + } + pw.print(" * Destroy "); pw.println(r); + r.dump(pw, " "); + } + needSep = true; + } + + if (dumpAll) { + printed = false; + for (int ic=0; ic r = mServiceConnections.valueAt(ic); + for (int i=0; i 0) { + if (dumpClient) { + ActiveServices.ServiceDumper sdumper; + synchronized (this) { + dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage); pw.println(); if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); + 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("-------------------------------------------------------------------------------"); + } + sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, + dumpPackage); } + sdumper.dumpWithClient(); pw.println(); - if (dumpAll) { - pw.println("-------------------------------------------------------------------------------"); + synchronized (this) { + 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); + } + + } 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); } - dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage); } Binder.restoreCallingIdentity(origId); } @@ -16142,8 +16207,8 @@ public final class ActivityManagerService extends ActivityManagerNative catPw.println(); dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); catPw.println(); - mServices.dumpServicesLocked(null, catPw, emptyArgs, 0, - false, false, null); + mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0, + false, null).dumpLocked(); catPw.println(); dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); catPw.flush();