From 5b15e53a36ac39ef4a84c5237af62ffb59bac04b Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Tue, 8 Jun 2021 09:32:29 +0800 Subject: [PATCH] Better dumpsys results of activity tasks Currently only the root task and the leaf task information is dumped. The intermediate tasks (if any) are not revealed. Dump all Tasks or TaskFragments in the hierarchy with proper indent. Bug: 189384393 Test: dumpsys activity a Test: dumpsys activity -p $package a Change-Id: I9a7fde37bae68074237ce3e95bad306323774dfb --- .../com/android/server/wm/ActivityRecord.java | 72 ++++++++++++ .../server/wm/ActivityTaskSupervisor.java | 76 +------------ .../core/java/com/android/server/wm/Task.java | 104 ++++-------------- .../com/android/server/wm/TaskFragment.java | 44 ++++++++ 4 files changed, 145 insertions(+), 151 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1652cc91d6cef..2bdd5aad81c98 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -319,6 +319,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; +import com.android.internal.os.TransferPipe; import com.android.internal.policy.AttributeCache; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ToBooleanFunction; @@ -342,6 +343,7 @@ import com.google.android.collect.Sets; import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -1142,6 +1144,76 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLetterboxUiController.dump(pw, prefix); } + static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r, + String prefix, String label, boolean complete, boolean brief, boolean client, + String dumpPackage, boolean needNL, Runnable header, Task lastTask) { + if (dumpPackage != null && !dumpPackage.equals(r.packageName)) { + return false; + } + + final boolean full = !brief && (complete || !r.isInHistory()); + if (needNL) { + pw.println(""); + } + if (header != null) { + header.run(); + } + + String innerPrefix = prefix + " "; + String[] args = new String[0]; + if (lastTask != r.getTask()) { + lastTask = r.getTask(); + pw.print(prefix); + pw.print(full ? "* " : " "); + pw.println(lastTask); + if (full) { + lastTask.dump(pw, prefix + " "); + } else if (complete) { + // Complete + brief == give a summary. Isn't that obvious?!? + if (lastTask.intent != null) { + pw.print(prefix); + pw.print(" "); + pw.println(lastTask.intent.toInsecureString()); + } + } + } + pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label); + pw.print(" #"); pw.print(index); pw.print(": "); + pw.println(r); + if (full) { + r.dump(pw, innerPrefix, true /* dumpAll */); + } else if (complete) { + // Complete + brief == give a summary. Isn't that obvious?!? + pw.print(innerPrefix); + pw.println(r.intent.toInsecureString()); + if (r.app != null) { + pw.print(innerPrefix); + pw.println(r.app); + } + } + if (client && r.attachedToProcess()) { + // flush anything that is already in the PrintWriter since the thread is going + // to write to the file descriptor directly + pw.flush(); + try { + TransferPipe tp = new TransferPipe(); + try { + r.app.getThread().dumpActivity( + tp.getWriteFd(), r.appToken, innerPrefix, args); + // Short timeout, since blocking here can deadlock with the application. + tp.go(fd, 2000); + } finally { + tp.kill(); + } + } catch (IOException e) { + pw.println(innerPrefix + "Failure while dumping the activity: " + e); + } catch (RemoteException e) { + pw.println(innerPrefix + "Got a RemoteException while dumping the activity"); + } + } + return true; + } + void setAppTimeTracker(AppTimeTracker att) { appTimeTracker = att; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 4d1488535e731..fde02ec804e7f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -137,7 +137,6 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; -import com.android.internal.os.TransferPipe; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledConsumer; @@ -147,7 +146,6 @@ import com.android.server.am.UserState; import com.android.server.wm.ActivityMetricsLogger.LaunchingState; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -1969,76 +1967,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List list, String prefix, String label, boolean complete, boolean brief, boolean client, String dumpPackage, boolean needNL, Runnable header, Task lastTask) { - String innerPrefix = null; - String[] args = null; boolean printed = false; - for (int i=list.size()-1; i>=0; i--) { + for (int i = list.size() - 1; i >= 0; i--) { final ActivityRecord r = list.get(i); - if (dumpPackage != null && !dumpPackage.equals(r.packageName)) { - continue; - } - if (innerPrefix == null) { - innerPrefix = prefix + " "; - args = new String[0]; - } - printed = true; - final boolean full = !brief && (complete || !r.isInHistory()); - if (needNL) { - pw.println(""); - needNL = false; - } - if (header != null) { - header.run(); - header = null; - } - if (lastTask != r.getTask()) { - lastTask = r.getTask(); - pw.print(prefix); - pw.print(full ? "* " : " "); - pw.println(lastTask); - if (full) { - lastTask.dump(pw, prefix + " "); - } else if (complete) { - // Complete + brief == give a summary. Isn't that obvious?!? - if (lastTask.intent != null) { - pw.print(prefix); pw.print(" "); - pw.println(lastTask.intent.toInsecureString()); - } - } - } - pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label); - pw.print(" #"); pw.print(i); pw.print(": "); - pw.println(r); - if (full) { - r.dump(pw, innerPrefix, true /* dumpAll */); - } else if (complete) { - // Complete + brief == give a summary. Isn't that obvious?!? - pw.print(innerPrefix); pw.println(r.intent.toInsecureString()); - if (r.app != null) { - pw.print(innerPrefix); pw.println(r.app); - } - } - if (client && r.attachedToProcess()) { - // flush anything that is already in the PrintWriter since the thread is going - // to write to the file descriptor directly - pw.flush(); - try { - TransferPipe tp = new TransferPipe(); - try { - r.app.getThread().dumpActivity( - tp.getWriteFd(), r.appToken, innerPrefix, args); - // Short timeout, since blocking here can deadlock with the application. - tp.go(fd, 2000); - } finally { - tp.kill(); - } - } catch (IOException e) { - pw.println(innerPrefix + "Failure while dumping the activity: " + e); - } catch (RemoteException e) { - pw.println(innerPrefix + "Got a RemoteException while dumping the activity"); - } - needNL = true; - } + ActivityRecord.dumpActivity(fd, pw, i, r, prefix, label, complete, brief, + client, dumpPackage, needNL, header, lastTask); + lastTask = r.getTask(); + header = null; + needNL = client && r.attachedToProcess(); } return printed; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index e97410d101153..0fb9739a609f7 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -99,7 +99,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; -import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList; import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; @@ -221,7 +220,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -5594,89 +5592,33 @@ class Task extends TaskFragment { boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient, String dumpPackage, final boolean needSep) { - Runnable headerPrinter = () -> { - if (needSep) { - pw.println(); - } - pw.println(" RootTask #" + getRootTaskId() - + ": type=" + activityTypeToString(getActivityType()) - + " mode=" + windowingModeToString(getWindowingMode())); - pw.println(" isSleeping=" + shouldSleepActivities()); - pw.println(" mBounds=" + getRequestedOverrideBounds()); - pw.println(" mCreatedByOrganizer=" + mCreatedByOrganizer); - }; - - boolean printed = false; - - if (dumpPackage == null) { - // If we are not filtering by package, we want to print absolutely everything, - // so always print the header even if there are no tasks/activities inside. - headerPrinter.run(); - headerPrinter = null; - printed = true; - } - - printed |= printThisActivity(pw, getTopPausingActivity(), dumpPackage, false, - " mPausingActivity: ", null); - printed |= printThisActivity(pw, getTopResumedActivity(), dumpPackage, false, - " mResumedActivity: ", null); - if (dumpAll) { - printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false, - " mLastPausedActivity: ", null); - } - - printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter); - - return printed; + return dump(" ", fd, pw, dumpAll, dumpClient, dumpPackage, needSep, null /* header */); } - private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, - boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) { - if (!hasChild()) { - return false; + @Override + void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) { + pw.print(prefix); pw.print("* "); pw.println(this); + pw.println(prefix + " mBounds=" + getRequestedOverrideBounds()); + pw.println(prefix + " mCreatedByOrganizer=" + mCreatedByOrganizer); + if (mLastNonFullscreenBounds != null) { + pw.print(prefix); pw.print(" mLastNonFullscreenBounds="); + pw.println(mLastNonFullscreenBounds); } - final AtomicBoolean printedHeader = new AtomicBoolean(false); - final AtomicBoolean printed = new AtomicBoolean(false); - forAllLeafTasks((task) -> { - final String prefix = " "; - Runnable headerPrinter = () -> { - printed.set(true); - if (!printedHeader.get()) { - if (needSep) { - pw.println(""); - } - if (header != null) { - header.run(); - } - printedHeader.set(true); - } - pw.print(prefix); pw.print("* "); pw.println(task); - pw.print(prefix); pw.print(" mBounds="); - pw.println(task.getRequestedOverrideBounds()); - pw.print(prefix); pw.print(" mMinWidth="); pw.print(task.mMinWidth); - pw.print(" mMinHeight="); pw.println(task.mMinHeight); - if (mLastNonFullscreenBounds != null) { - pw.print(prefix); - pw.print(" mLastNonFullscreenBounds="); - pw.println(task.mLastNonFullscreenBounds); - } - task.dump(pw, prefix + " "); - }; - if (dumpPackage == null) { - // If we are not filtering by package, we want to print absolutely everything, - // so always print the header even if there are no activities inside. - headerPrinter.run(); - headerPrinter = null; + if (dumpAll) { + printThisActivity(pw, mLastPausedActivity, dumpPackage, false, + prefix + " mLastPausedActivity: ", null); + } + if (isLeafTask()) { + pw.println(prefix + " isSleeping=" + shouldSleepActivities()); + printThisActivity(pw, getTopPausingActivity(), dumpPackage, false, + prefix + " topPausingActivity=", null); + printThisActivity(pw, getTopResumedActivity(), dumpPackage, false, + prefix + " topResumedActivity=", null); + if (mMinWidth != INVALID_MIN_SIZE || mMinHeight != INVALID_MIN_SIZE) { + pw.print(prefix); pw.print(" mMinWidth="); pw.print(mMinWidth); + pw.print(" mMinHeight="); pw.println(mMinHeight); } - final ArrayList activities = new ArrayList<>(); - // Add activities by traversing the hierarchy from bottom to top, since activities - // are dumped in reverse order in {@link ActivityTaskSupervisor#dumpHistoryList()}. - task.forAllActivities((Consumer) activities::add, - false /* traverseTopToBottom */); - dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient, - dumpPackage, false, headerPrinter, task); - }, true /* traverseTopToBottom */); - return printed.get(); + } } ArrayList getDumpActivitiesLocked(String name) { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 80914a6345514..b83029dec61d9 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -76,6 +76,8 @@ import com.android.internal.util.function.pooled.PooledFunction; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -1885,4 +1887,46 @@ class TaskFragment extends WindowContainer { } return getTopChild().getActivityType(); } + + boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll, + boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) { + boolean printed = false; + Runnable headerPrinter = () -> { + if (needSep) { + pw.println(); + } + if (header != null) { + header.run(); + } + + dumpInner(prefix, pw, dumpAll, dumpPackage); + }; + + if (dumpPackage == null) { + // If we are not filtering by package, we want to print absolutely everything, + // so always print the header even if there are no tasks/activities inside. + headerPrinter.run(); + headerPrinter = null; + printed = true; + } + + for (int i = mChildren.size() - 1; i >= 0; --i) { + WindowContainer child = mChildren.get(i); + if (child.asTaskFragment() != null) { + printed |= child.asTaskFragment().dump(prefix + " ", fd, pw, dumpAll, + dumpClient, dumpPackage, needSep, headerPrinter); + } else if (child.asActivityRecord() != null) { + ActivityRecord.dumpActivity(fd, pw, i, child.asActivityRecord(), prefix + " ", + "Hist ", true, !dumpAll, dumpClient, dumpPackage, false, headerPrinter, + getTask()); + } + } + + return printed; + } + + void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) { + pw.print(prefix); pw.print("* "); pw.println(this); + pw.println(prefix + " mBounds=" + getRequestedOverrideBounds()); + } }