summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Kean Mariotti <keanmariotti@google.com> 2024-06-21 13:06:00 +0000
committer Kean Mariotti <keanmariotti@google.com> 2024-07-12 11:04:02 +0000
commitfcfe6dfec755ca1481046bcd73dc0c64827cb7de (patch)
treeadd916b049cd8d116eb6c83b98891c7eb3319642
parent4e2b40239a5f213c3d266dfc0c4a93c51f55419a (diff)
wm tracing: migrate to perfetto
Bug: 323165543 Flag: android.tracing.perfetto_wm_tracing Test: presubmit Change-Id: Iec0e94d0b9bd029c9e3bfdca5d62d87ea45098ac
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowTracingDataSource.java204
-rw-r--r--services/core/java/com/android/server/wm/WindowTracingPerfetto.java163
3 files changed, 371 insertions, 1 deletions
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 21f7eca5627a..04d5c03e64a6 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -56,7 +56,10 @@ abstract class WindowTracing {
static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
Choreographer choreographer) {
- return new WindowTracingLegacy(service, choreographer);
+ if (!android.tracing.Flags.perfettoWmTracing()) {
+ return new WindowTracingLegacy(service, choreographer);
+ }
+ return new WindowTracingPerfetto(service, choreographer);
}
protected WindowTracing(WindowManagerService service, Choreographer choreographer,
diff --git a/services/core/java/com/android/server/wm/WindowTracingDataSource.java b/services/core/java/com/android/server/wm/WindowTracingDataSource.java
new file mode 100644
index 000000000000..3d2c0d3f79b9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTracingDataSource.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2024 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.wm;
+
+import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT;
+
+import android.annotation.NonNull;
+import android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
+import android.internal.perfetto.protos.WindowmanagerConfig.WindowManagerConfig;
+import android.tracing.perfetto.CreateTlsStateArgs;
+import android.tracing.perfetto.DataSource;
+import android.tracing.perfetto.DataSourceInstance;
+import android.tracing.perfetto.DataSourceParams;
+import android.tracing.perfetto.InitArguments;
+import android.tracing.perfetto.Producer;
+import android.tracing.perfetto.StartCallbackArguments;
+import android.tracing.perfetto.StopCallbackArguments;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+public final class WindowTracingDataSource extends DataSource<WindowTracingDataSource.Instance,
+ WindowTracingDataSource.TlsState, Void> {
+ public static final String DATA_SOURCE_NAME = "android.windowmanager";
+
+ public static class TlsState {
+ public final Config mConfig;
+ public final AtomicBoolean mIsStarting = new AtomicBoolean(true);
+
+ private TlsState(Config config) {
+ mConfig = config;
+ }
+ }
+
+ public static class Config {
+ public final @WindowTraceLogLevel int mLogLevel;
+ public final boolean mLogOnFrame;
+
+ private Config(@WindowTraceLogLevel int logLevel, boolean logOnFrame) {
+ mLogLevel = logLevel;
+ mLogOnFrame = logOnFrame;
+ }
+ }
+
+ public abstract static class Instance extends DataSourceInstance {
+ public final Config mConfig;
+
+ public Instance(DataSource dataSource, int instanceIndex, Config config) {
+ super(dataSource, instanceIndex);
+ mConfig = config;
+ }
+ }
+
+ private static final Config CONFIG_DEFAULT = new Config(WindowTraceLogLevel.TRIM, true);
+ private static final int CONFIG_VALUE_UNSPECIFIED = 0;
+ private static final String TAG = "WindowTracingDataSource";
+
+ @NonNull
+ private final Consumer<Config> mOnStartCallback;
+ @NonNull
+ private final Consumer<Config> mOnStopCallback;
+
+ public WindowTracingDataSource(@NonNull Consumer<Config> onStart,
+ @NonNull Consumer<Config> onStop) {
+ super(DATA_SOURCE_NAME);
+ mOnStartCallback = onStart;
+ mOnStopCallback = onStop;
+
+ Producer.init(InitArguments.DEFAULTS);
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .build();
+ register(params);
+ }
+
+ @Override
+ public Instance createInstance(ProtoInputStream configStream, int instanceIndex) {
+ final Config config = parseDataSourceConfig(configStream);
+
+ return new Instance(this, instanceIndex, config != null ? config : CONFIG_DEFAULT) {
+ @Override
+ protected void onStart(StartCallbackArguments args) {
+ mOnStartCallback.accept(mConfig);
+ }
+
+ @Override
+ protected void onStop(StopCallbackArguments args) {
+ mOnStopCallback.accept(mConfig);
+ }
+ };
+ }
+
+ @Override
+ public TlsState createTlsState(
+ CreateTlsStateArgs<Instance> args) {
+ try (Instance dsInstance = args.getDataSourceInstanceLocked()) {
+ if (dsInstance == null) {
+ // Datasource instance has been removed
+ return new TlsState(CONFIG_DEFAULT);
+ }
+ return new TlsState(dsInstance.mConfig);
+ }
+ }
+
+ private Config parseDataSourceConfig(ProtoInputStream stream) {
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (stream.getFieldNumber() != (int) DataSourceConfig.WINDOWMANAGER_CONFIG) {
+ continue;
+ }
+ return parseWindowManagerConfig(stream);
+ }
+ Log.w(TAG, "Received start request without config parameters. Will use defaults.");
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to parse DataSourceConfig", e);
+ }
+ return null;
+ }
+
+ private Config parseWindowManagerConfig(ProtoInputStream stream) {
+ int parsedLogLevel = CONFIG_VALUE_UNSPECIFIED;
+ int parsedLogFrequency = CONFIG_VALUE_UNSPECIFIED;
+
+ try {
+ final long token = stream.start(DataSourceConfig.WINDOWMANAGER_CONFIG);
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) WindowManagerConfig.LOG_LEVEL:
+ parsedLogLevel = stream.readInt(WindowManagerConfig.LOG_LEVEL);
+ break;
+ case (int) WindowManagerConfig.LOG_FREQUENCY:
+ parsedLogFrequency = stream.readInt(WindowManagerConfig.LOG_FREQUENCY);
+ break;
+ default:
+ Log.w(TAG, "Unrecognized WindowManagerConfig field number: "
+ + stream.getFieldNumber());
+ }
+ }
+ stream.end(token);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to parse WindowManagerConfig", e);
+ }
+
+ @WindowTraceLogLevel int logLevel;
+ switch(parsedLogLevel) {
+ case CONFIG_VALUE_UNSPECIFIED:
+ Log.w(TAG, "Unspecified log level. Defaulting to TRIM");
+ logLevel = WindowTraceLogLevel.TRIM;
+ break;
+ case WindowManagerConfig.LOG_LEVEL_VERBOSE:
+ logLevel = WindowTraceLogLevel.ALL;
+ break;
+ case WindowManagerConfig.LOG_LEVEL_DEBUG:
+ logLevel = WindowTraceLogLevel.TRIM;
+ break;
+ case WindowManagerConfig.LOG_LEVEL_CRITICAL:
+ logLevel = WindowTraceLogLevel.CRITICAL;
+ break;
+ default:
+ Log.w(TAG, "Unrecognized log level. Defaulting to TRIM");
+ logLevel = WindowTraceLogLevel.TRIM;
+ break;
+ }
+
+ boolean logOnFrame;
+ switch(parsedLogFrequency) {
+ case CONFIG_VALUE_UNSPECIFIED:
+ Log.w(TAG, "Unspecified log frequency. Defaulting to 'log on frame'");
+ logOnFrame = true;
+ break;
+ case WindowManagerConfig.LOG_FREQUENCY_FRAME:
+ logOnFrame = true;
+ break;
+ case WindowManagerConfig.LOG_FREQUENCY_TRANSACTION:
+ logOnFrame = false;
+ break;
+ default:
+ Log.w(TAG, "Unrecognized log frequency. Defaulting to 'log on frame'");
+ logOnFrame = true;
+ break;
+ }
+
+ return new Config(logLevel, logOnFrame);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
new file mode 100644
index 000000000000..653b6dac1537
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2024 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.wm;
+
+import android.annotation.Nullable;
+import android.internal.perfetto.protos.TracePacketOuterClass.TracePacket;
+import android.internal.perfetto.protos.WinscopeExtensionsImplOuterClass.WinscopeExtensionsImpl;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.Choreographer;
+
+import java.io.PrintWriter;
+import java.util.concurrent.atomic.AtomicInteger;
+
+class WindowTracingPerfetto extends WindowTracing {
+ private static final String TAG = "WindowTracing";
+
+ private final AtomicInteger mCountSessionsOnFrame = new AtomicInteger();
+ private final AtomicInteger mCountSessionsOnTransaction = new AtomicInteger();
+ private final WindowTracingDataSource mDataSource = new WindowTracingDataSource(
+ this::onStart, this::onStop);
+
+ WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) {
+ super(service, choreographer, service.mGlobalLock);
+ }
+
+ @Override
+ void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+ logAndPrintln(pw, "Log level must be configured through perfetto");
+ }
+
+ @Override
+ void setLogFrequency(boolean onFrame, PrintWriter pw) {
+ logAndPrintln(pw, "Log frequency must be configured through perfetto");
+ }
+
+ @Override
+ void setBufferCapacity(int capacity, PrintWriter pw) {
+ logAndPrintln(pw, "Buffer capacity must be configured through perfetto");
+ }
+
+ @Override
+ boolean isEnabled() {
+ return (mCountSessionsOnFrame.get() + mCountSessionsOnTransaction.get()) > 0;
+ }
+
+ @Override
+ int onShellCommand(ShellCommand shell) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ pw.println("Shell commands are ignored."
+ + " Any type of action should be performed through perfetto.");
+ return -1;
+ }
+
+ @Override
+ String getStatus() {
+ return "Status: "
+ + ((isEnabled()) ? "Enabled" : "Disabled")
+ + "\n"
+ + "Sessions logging 'on frame': " + mCountSessionsOnFrame.get()
+ + "\n"
+ + "Sessions logging 'on transaction': " + mCountSessionsOnTransaction.get()
+ + "\n";
+ }
+
+ @Override
+ protected void startTraceInternal(@Nullable PrintWriter pw) {
+ logAndPrintln(pw, "Tracing must be started through perfetto");
+ }
+
+ @Override
+ protected void stopTraceInternal(@Nullable PrintWriter pw) {
+ logAndPrintln(pw, "Tracing must be stopped through perfetto");
+ }
+
+ @Override
+ protected void saveForBugreportInternal(@Nullable PrintWriter pw) {
+ logAndPrintln(pw, "Tracing snapshot for bugreport must be handled through perfetto");
+ }
+
+ @Override
+ protected void log(String where) {
+ try {
+ boolean isStartLogEvent = where == WHERE_START_TRACING;
+ boolean isOnFrameLogEvent = where == WHERE_ON_FRAME;
+
+ mDataSource.trace((context) -> {
+ WindowTracingDataSource.Config dataSourceConfig =
+ context.getCustomTlsState().mConfig;
+
+ if (isStartLogEvent) {
+ boolean isDataSourceStarting = context.getCustomTlsState()
+ .mIsStarting.compareAndSet(true, false);
+ if (!isDataSourceStarting) {
+ return;
+ }
+ } else if (isOnFrameLogEvent != dataSourceConfig.mLogOnFrame) {
+ return;
+ }
+
+ ProtoOutputStream os = context.newTracePacket();
+ long timestamp = SystemClock.elapsedRealtimeNanos();
+ os.write(TracePacket.TIMESTAMP, timestamp);
+ final long tokenWinscopeExtensions =
+ os.start(TracePacket.WINSCOPE_EXTENSIONS);
+ final long tokenExtensionsField =
+ os.start(WinscopeExtensionsImpl.WINDOWMANAGER);
+ dumpToProto(os, dataSourceConfig.mLogLevel, where, timestamp);
+ os.end(tokenExtensionsField);
+ os.end(tokenWinscopeExtensions);
+ });
+ } catch (Exception e) {
+ Log.wtf(TAG, "Exception while tracing state", e);
+ }
+ }
+
+ @Override
+ protected boolean shouldLogOnFrame() {
+ return mCountSessionsOnFrame.get() > 0;
+ }
+
+ @Override
+ protected boolean shouldLogOnTransaction() {
+ return mCountSessionsOnTransaction.get() > 0;
+ }
+
+ private void onStart(WindowTracingDataSource.Config config) {
+ if (config.mLogOnFrame) {
+ mCountSessionsOnFrame.incrementAndGet();
+ } else {
+ mCountSessionsOnTransaction.incrementAndGet();
+ }
+
+ Log.i(TAG, "Started with logLevel: " + config.mLogLevel
+ + " logOnFrame: " + config.mLogOnFrame);
+ log(WHERE_START_TRACING);
+ }
+
+ private void onStop(WindowTracingDataSource.Config config) {
+ if (config.mLogOnFrame) {
+ mCountSessionsOnFrame.decrementAndGet();
+ } else {
+ mCountSessionsOnTransaction.decrementAndGet();
+ }
+ Log.i(TAG, "Stopped");
+ }
+}