Loading services/core/java/com/android/server/wm/WindowTracing.java +4 −1 Original line number Diff line number Diff line Loading @@ -56,8 +56,11 @@ abstract class WindowTracing { static WindowTracing createDefaultAndStartLooper(WindowManagerService service, Choreographer choreographer) { if (!android.tracing.Flags.perfettoWmTracing()) { return new WindowTracingLegacy(service, choreographer); } return new WindowTracingPerfetto(service, choreographer); } protected WindowTracing(WindowManagerService service, Choreographer choreographer, WindowManagerGlobalLock globalLock) { Loading services/core/java/com/android/server/wm/WindowTracingDataSource.java 0 → 100644 +204 −0 Original line number Diff line number Diff line /* * 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); } } services/core/java/com/android/server/wm/WindowTracingPerfetto.java 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * 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"); } } Loading
services/core/java/com/android/server/wm/WindowTracing.java +4 −1 Original line number Diff line number Diff line Loading @@ -56,8 +56,11 @@ abstract class WindowTracing { static WindowTracing createDefaultAndStartLooper(WindowManagerService service, Choreographer choreographer) { if (!android.tracing.Flags.perfettoWmTracing()) { return new WindowTracingLegacy(service, choreographer); } return new WindowTracingPerfetto(service, choreographer); } protected WindowTracing(WindowManagerService service, Choreographer choreographer, WindowManagerGlobalLock globalLock) { Loading
services/core/java/com/android/server/wm/WindowTracingDataSource.java 0 → 100644 +204 −0 Original line number Diff line number Diff line /* * 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); } }
services/core/java/com/android/server/wm/WindowTracingPerfetto.java 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * 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"); } }