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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<ActivityRecord> 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;
|
||||
}
|
||||
|
||||
@@ -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<ActivityRecord> 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<ActivityRecord>) activities::add,
|
||||
false /* traverseTopToBottom */);
|
||||
dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
|
||||
dumpPackage, false, headerPrinter, task);
|
||||
}, true /* traverseTopToBottom */);
|
||||
return printed.get();
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
|
||||
|
||||
@@ -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<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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user