diff options
5 files changed, 204 insertions, 39 deletions
diff --git a/services/core/java/com/android/server/Dumpable.java b/services/core/java/com/android/server/Dumpable.java new file mode 100644 index 000000000000..d2bd66f59b62 --- /dev/null +++ b/services/core/java/com/android/server/Dumpable.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.IndentingPrintWriter; + +/** + * Interface used to dump {@link SystemServer} state that is not associated with any service. + */ +public interface Dumpable { + + /** + * Dumps the state. + */ + void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args); + + /** + * Gets the name of the dumpable. + * + * <p>If not overridden, will return the simple class name. + */ + default String getDumpableName() { + return Dumpable.this.getClass().getSimpleName(); + } +} diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java index c0611374679b..c23f1cab0614 100644 --- a/services/core/java/com/android/server/SystemServerInitThreadPool.java +++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java @@ -19,6 +19,7 @@ package com.android.server; import android.annotation.NonNull; import android.os.Build; import android.os.Process; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -44,7 +45,7 @@ import java.util.concurrent.TimeUnit; * * @hide */ -public class SystemServerInitThreadPool { +public final class SystemServerInitThreadPool implements Dumpable { private static final String TAG = SystemServerInitThreadPool.class.getSimpleName(); private static final int SHUTDOWN_TIMEOUT_MILLIS = 20000; private static final boolean IS_DEBUGGABLE = Build.IS_DEBUGGABLE; @@ -53,6 +54,7 @@ public class SystemServerInitThreadPool { @GuardedBy("LOCK") private static SystemServerInitThreadPool sInstance; + private final int mSize; // used by dump() only private final ExecutorService mService; @GuardedBy("mPendingTasks") @@ -62,9 +64,9 @@ public class SystemServerInitThreadPool { private boolean mShutDown; private SystemServerInitThreadPool() { - final int size = Runtime.getRuntime().availableProcessors(); - Slog.i(TAG, "Creating instance with " + size + " threads"); - mService = ConcurrentUtils.newFixedThreadPool(size, + mSize = Runtime.getRuntime().availableProcessors(); + Slog.i(TAG, "Creating instance with " + mSize + " threads"); + mService = ConcurrentUtils.newFixedThreadPool(mSize, "system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND); } @@ -123,11 +125,13 @@ public class SystemServerInitThreadPool { * * @throws IllegalStateException if it has been started already without being shut down yet. */ - static void start() { + static SystemServerInitThreadPool start() { + SystemServerInitThreadPool instance; synchronized (LOCK) { Preconditions.checkState(sInstance == null, TAG + " already started"); - sInstance = new SystemServerInitThreadPool(); + instance = sInstance = new SystemServerInitThreadPool(); } + return instance; } /** @@ -190,4 +194,22 @@ public class SystemServerInitThreadPool { ActivityManagerService.dumpStackTraces(pids, null, null, Watchdog.getInterestingNativePids(), null); } + + @Override + public void dump(IndentingPrintWriter pw, String[] args) { + synchronized (LOCK) { + pw.printf("has instance: %b\n", (sInstance != null)); + } + pw.printf("number of threads: %d\n", mSize); + pw.printf("service: %s\n", mService); + synchronized (mPendingTasks) { + pw.printf("is shutdown: %b\n", mShutDown); + final int pendingTasks = mPendingTasks.size(); + if (pendingTasks == 0) { + pw.println("no pending tasks"); + } else { + pw.printf("%d pending tasks: %s\n", pendingTasks, mPendingTasks); + } + } + } } diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 84d01ec3598d..6c81de6af402 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -200,21 +200,21 @@ public abstract class SystemService { /** * @hide */ - public void dump(@NonNull StringBuilder builder) { - builder.append(getUserIdentifier()); + public void dump(@NonNull PrintWriter pw) { + pw.print(getUserIdentifier()); if (!isFull() && !isManagedProfile()) return; - builder.append('('); + pw.print('('); boolean addComma = false; if (isFull()) { - builder.append("full"); + pw.print("full"); } if (isManagedProfile()) { - if (addComma) builder.append(','); - builder.append("mp"); + if (addComma) pw.print(','); + pw.print("mp"); } - builder.append(')'); + pw.print(')'); } } diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index ff2661b19b48..71a18218110e 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -27,6 +27,7 @@ import android.os.Trace; import android.os.UserManagerInternal; import android.util.ArrayMap; import android.util.EventLog; +import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; @@ -49,7 +50,7 @@ import java.util.ArrayList; * * {@hide} */ -public final class SystemServiceManager { +public final class SystemServiceManager implements Dumpable { private static final String TAG = SystemServiceManager.class.getSimpleName(); private static final boolean DEBUG = false; private static final int SERVICE_CALL_WARN_TIME_MS = 50; @@ -489,31 +490,39 @@ public final class SystemServiceManager { return sSystemDir; } - /** - * Outputs the state of this manager to the System log. - */ - public void dump() { - StringBuilder builder = new StringBuilder(); - builder.append("Current phase: ").append(mCurrentPhase).append('\n'); - builder.append("Services:\n"); - final int startedLen = mServices.size(); - for (int i = 0; i < startedLen; i++) { - final SystemService service = mServices.get(i); - builder.append("\t") - .append(service.getClass().getSimpleName()) - .append("\n"); - } + @Override + public void dump(IndentingPrintWriter pw, String[] args) { + pw.printf("Current phase: %d\n", mCurrentPhase); synchronized (mTargetUsers) { - builder.append("Current user: ").append(mCurrentUser).append('\n'); - builder.append("Target users: "); + if (mCurrentUser != null) { + pw.print("Current user: "); mCurrentUser.dump(pw); pw.println(); + } else { + pw.println("Current user not set!"); + } + final int targetUsersSize = mTargetUsers.size(); - for (int i = 0; i < targetUsersSize; i++) { - mTargetUsers.valueAt(i).dump(builder); - if (i != targetUsersSize - 1) builder.append(','); + if (targetUsersSize > 0) { + pw.printf("%d target users: ", targetUsersSize); + for (int i = 0; i < targetUsersSize; i++) { + mTargetUsers.valueAt(i).dump(pw); + if (i != targetUsersSize - 1) pw.print(", "); + } + pw.println(); + } else { + pw.println("No target users"); } - builder.append('\n'); } - - Slog.e(TAG, builder.toString()); + final int startedLen = mServices.size(); + if (startedLen > 0) { + pw.printf("%d started services:\n", startedLen); + pw.increaseIndent(); + for (int i = 0; i < startedLen; i++) { + final SystemService service = mServices.get(i); + pw.println(service.getClass().getCanonicalName()); + } + pw.decreaseIndent(); + } else { + pw.println("No started services"); + } } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 975e2265b60c..e116a353c723 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -76,14 +76,18 @@ import android.provider.Settings; import android.server.ServerProtoEnums; import android.sysprop.VoldProperties; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.EventLog; +import android.util.IndentingPrintWriter; import android.util.Pair; import android.util.Slog; +import android.util.TimeUtils; import android.view.contentcapture.ContentCaptureManager; import com.android.i18n.timezone.ZoneInfoDb; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; @@ -188,14 +192,20 @@ import dalvik.system.VMRuntime; import com.google.android.startop.iorap.IorapForwardingService; import java.io.File; +import java.io.FileDescriptor; import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; import java.util.LinkedList; import java.util.Locale; import java.util.Timer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; -public final class SystemServer { +/** + * Entry point to {@code system_server}. + */ +public final class SystemServer implements Dumpable { private static final String TAG = "SystemServer"; @@ -384,6 +394,9 @@ public final class SystemServer { private Future<?> mZygotePreload; private Future<?> mBlobStoreServiceStart; + private final SystemServerDumper mDumper = new SystemServerDumper(); + + /** * The pending WTF to be logged into dropbox. */ @@ -446,6 +459,75 @@ public final class SystemServer { mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed")); } + @Override + public void dump(IndentingPrintWriter pw, String[] args) { + pw.printf("Runtime restart: %b\n", mRuntimeRestart); + pw.printf("Start count: %d\n", mStartCount); + pw.print("Runtime start-up time: "); + TimeUtils.formatDuration(mRuntimeStartUptime, pw); pw.println(); + pw.print("Runtime start-elapsed time: "); + TimeUtils.formatDuration(mRuntimeStartElapsedTime, pw); pw.println(); + } + + private final class SystemServerDumper extends Binder { + + @GuardedBy("mDumpables") + private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>(4); + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + final boolean hasArgs = args != null && args.length > 0; + + synchronized (mDumpables) { + if (hasArgs && "--list".equals(args[0])) { + final int dumpablesSize = mDumpables.size(); + for (int i = 0; i < dumpablesSize; i++) { + pw.println(mDumpables.keyAt(i)); + } + return; + } + + if (hasArgs && "--name".equals(args[0])) { + if (args.length < 2) { + pw.println("Must pass at least one argument to --name"); + return; + } + final String name = args[1]; + final Dumpable dumpable = mDumpables.get(name); + if (dumpable == null) { + pw.printf("No dummpable named %s\n", name); + return; + } + + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + // Strip --name DUMPABLE from args + final String[] actualArgs = Arrays.copyOfRange(args, 2, args.length); + dumpable.dump(ipw, actualArgs); + } + return; + } + + final int dumpablesSize = mDumpables.size(); + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + for (int i = 0; i < dumpablesSize; i++) { + final Dumpable dumpable = mDumpables.valueAt(i); + ipw.printf("%s:\n", dumpable.getDumpableName()); + ipw.increaseIndent(); + dumpable.dump(ipw, args); + ipw.decreaseIndent(); + ipw.println(); + } + } + } + } + + private void addDumpable(@NonNull Dumpable dumpable) { + synchronized (mDumpables) { + mDumpables.put(dumpable.getDumpableName(), dumpable); + } + } + } + private void run() { TimingsTraceAndSlog t = new TimingsTraceAndSlog(); try { @@ -572,13 +654,21 @@ public final class SystemServer { // Call per-process mainline module initialization. ActivityThread.initializeMainlineModules(); + // Sets the dumper service + ServiceManager.addService("system_server_dumper", mDumper); + mDumper.addDumpable(this); + // Create the system service manager. mSystemServiceManager = new SystemServiceManager(mSystemContext); mSystemServiceManager.setStartInfo(mRuntimeRestart, mRuntimeStartElapsedTime, mRuntimeStartUptime); + mDumper.addDumpable(mSystemServiceManager); + LocalServices.addService(SystemServiceManager.class, mSystemServiceManager); // Prepare the thread pool for init tasks that can be parallelized - SystemServerInitThreadPool.start(); + SystemServerInitThreadPool tp = SystemServerInitThreadPool.start(); + mDumper.addDumpable(tp); + // Attach JVMTI agent if this is a debuggable build and the system property is set. if (Build.IS_DEBUGGABLE) { // Property is of the form "library_path=parameters". @@ -2321,7 +2411,11 @@ public final class SystemServer { if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { t.traceBegin("StartCarServiceHelperService"); - mSystemServiceManager.startService(CAR_SERVICE_HELPER_SERVICE_CLASS); + final SystemService cshs = mSystemServiceManager + .startService(CAR_SERVICE_HELPER_SERVICE_CLASS); + if (cshs instanceof Dumpable) { + mDumper.addDumpable((Dumpable) cshs); + } t.traceEnd(); } |