Loading services/core/java/com/android/server/wm/WindowTracingDataSource.java +2 −4 Original line number Original line Diff line number Diff line Loading @@ -38,8 +38,6 @@ import java.util.concurrent.atomic.AtomicBoolean; public final class WindowTracingDataSource extends DataSource<WindowTracingDataSource.Instance, public final class WindowTracingDataSource extends DataSource<WindowTracingDataSource.Instance, WindowTracingDataSource.TlsState, Void> { WindowTracingDataSource.TlsState, Void> { public static final String DATA_SOURCE_NAME = "android.windowmanager"; public static class TlsState { public static class TlsState { public final Config mConfig; public final Config mConfig; public final AtomicBoolean mIsStarting = new AtomicBoolean(true); public final AtomicBoolean mIsStarting = new AtomicBoolean(true); Loading Loading @@ -78,8 +76,8 @@ public final class WindowTracingDataSource extends DataSource<WindowTracingDataS @NonNull @NonNull private final WeakReference<WindowTracingPerfetto> mWindowTracing; private final WeakReference<WindowTracingPerfetto> mWindowTracing; public WindowTracingDataSource(WindowTracingPerfetto windowTracing) { public WindowTracingDataSource(WindowTracingPerfetto windowTracing, String dataSourceName) { super(DATA_SOURCE_NAME); super(dataSourceName); mWindowTracing = new WeakReference<>(windowTracing); mWindowTracing = new WeakReference<>(windowTracing); Producer.init(InitArguments.DEFAULTS); Producer.init(InitArguments.DEFAULTS); Loading services/core/java/com/android/server/wm/WindowTracingPerfetto.java +5 −3 Original line number Original line Diff line number Diff line Loading @@ -32,19 +32,21 @@ import java.util.concurrent.atomic.AtomicInteger; class WindowTracingPerfetto extends WindowTracing { class WindowTracingPerfetto extends WindowTracing { private static final String TAG = "WindowTracing"; private static final String TAG = "WindowTracing"; private static final String PRODUCTION_DATA_SOURCE_NAME = "android.windowmanager"; private final AtomicInteger mCountSessionsOnFrame = new AtomicInteger(); private final AtomicInteger mCountSessionsOnFrame = new AtomicInteger(); private final AtomicInteger mCountSessionsOnTransaction = new AtomicInteger(); private final AtomicInteger mCountSessionsOnTransaction = new AtomicInteger(); private final WindowTracingDataSource mDataSource = new WindowTracingDataSource(this); private final WindowTracingDataSource mDataSource; WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) { WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) { this(service, choreographer, service.mGlobalLock); this(service, choreographer, service.mGlobalLock, PRODUCTION_DATA_SOURCE_NAME); } } @VisibleForTesting @VisibleForTesting WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer, WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer, WindowManagerGlobalLock globalLock) { WindowManagerGlobalLock globalLock, String dataSourceName) { super(service, choreographer, globalLock); super(service, choreographer, globalLock); mDataSource = new WindowTracingDataSource(this, dataSourceName); } } @Override @Override Loading services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java +118 −37 Original line number Original line Diff line number Diff line /* /* * Copyright (C) 2017 The Android Open Source Project * Copyright (C) 2024 The Android Open Source Project * * * Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License. Loading Loading @@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.eq; import static java.io.File.createTempFile; import static java.io.File.createTempFile; import static java.nio.file.Files.createTempDirectory; import static java.nio.file.Files.createTempDirectory; import android.os.ParcelFileDescriptor; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit; import android.tools.ScenarioBuilder; import android.tools.ScenarioBuilder; import android.tools.traces.io.ResultWriter; import android.tools.traces.io.ResultWriter; Loading @@ -35,107 +36,187 @@ import android.tools.traces.monitors.PerfettoTraceMonitor; import android.view.Choreographer; import android.view.Choreographer; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.google.protobuf.InvalidProtocolBufferException; import org.junit.After; import org.junit.After; import org.junit.Before; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.Test; import org.mockito.Mockito; import org.mockito.Mockito; import perfetto.protos.PerfettoConfig.TracingServiceState; import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency; import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency; import java.io.FileInputStream; import java.io.IOException; import java.util.Optional; /** /** * Test class for {@link WindowTracingPerfetto}. * Test class for {@link WindowTracingPerfetto}. */ */ @SmallTest @SmallTest @Presubmit @Presubmit public class WindowTracingPerfettoTest { public class WindowTracingPerfettoTest { private WindowManagerService mWmMock; private static final String TEST_DATA_SOURCE_NAME = "android.windowmanager.test"; private Choreographer mChoreographer; private WindowTracing mWindowTracing; private PerfettoTraceMonitor mTraceMonitor; private ResultWriter mWriter; @Before private static WindowManagerService sWmMock; public void setUp() throws Exception { private static Choreographer sChoreographer; mWmMock = Mockito.mock(WindowManagerService.class); private static WindowTracing sWindowTracing; Mockito.doNothing().when(mWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt()); mChoreographer = Mockito.mock(Choreographer.class); private PerfettoTraceMonitor mTraceMonitor; mWindowTracing = new WindowTracingPerfetto(mWmMock, mChoreographer, @BeforeClass new WindowManagerGlobalLock()); public static void setUpOnce() throws Exception { sWmMock = Mockito.mock(WindowManagerService.class); Mockito.doNothing().when(sWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt()); sChoreographer = Mockito.mock(Choreographer.class); sWindowTracing = new WindowTracingPerfetto(sWmMock, sChoreographer, new WindowManagerGlobalLock(), TEST_DATA_SOURCE_NAME); waitDataSourceIsAvailable(); } mWriter = new ResultWriter() @Before .forScenario(new ScenarioBuilder() public void setUp() throws IOException { .forClass(createTempFile("temp", "").getName()).build()) Mockito.clearInvocations(sWmMock); .withOutputDir(createTempDirectory("temp").toFile()) .setRunComplete(); } } @After @After public void tearDown() throws Exception { public void tearDown() throws IOException { stopTracing(); stopTracing(); } } @Test @Test public void isEnabled_returnsFalseByDefault() { public void isEnabled_returnsFalseByDefault() { assertFalse(mWindowTracing.isEnabled()); assertFalse(sWindowTracing.isEnabled()); } } @Test @Test public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() { public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() throws IOException { startTracing(false); startTracing(false); assertTrue(mWindowTracing.isEnabled()); assertTrue(sWindowTracing.isEnabled()); stopTracing(); stopTracing(); assertFalse(mWindowTracing.isEnabled()); assertFalse(sWindowTracing.isEnabled()); } } @Test @Test public void trace_ignoresLogStateCalls_ifTracingIsDisabled() { public void trace_ignoresLogStateCalls_ifTracingIsDisabled() { mWindowTracing.logState("where"); sWindowTracing.logState("where"); verifyZeroInteractions(mWmMock); verifyZeroInteractions(sWmMock); } } @Test @Test public void trace_writesInitialStateSnapshot_whenTracingStarts() throws Exception { public void trace_writesInitialStateSnapshot_whenTracingStarts() { startTracing(false); startTracing(false); verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } } @Test @Test public void trace_writesStateSnapshot_onLogStateCall() throws Exception { public void trace_writesStateSnapshot_onLogStateCall() { startTracing(false); startTracing(false); mWindowTracing.logState("where"); sWindowTracing.logState("where"); verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); verify(sWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } } @Test @Test public void dump_writesOneSingleStateSnapshot() throws Exception { public void dump_writesOneSingleStateSnapshot() { startTracing(true); startTracing(true); mWindowTracing.logState("where"); sWindowTracing.logState("where"); verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } } private void startTracing(boolean isDump) { private void startTracing(boolean isDump) { if (isDump) { if (isDump) { mTraceMonitor = PerfettoTraceMonitor mTraceMonitor = PerfettoTraceMonitor .newBuilder() .newBuilder() .enableWindowManagerDump() .enableWindowManagerDump(TEST_DATA_SOURCE_NAME) .build(); .build(); } else { } else { mTraceMonitor = PerfettoTraceMonitor mTraceMonitor = PerfettoTraceMonitor .newBuilder() .newBuilder() .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION) .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION, TEST_DATA_SOURCE_NAME) .build(); .build(); } } mTraceMonitor.start(); mTraceMonitor.start(); } } private void stopTracing() { private void stopTracing() throws IOException { if (mTraceMonitor == null || !mTraceMonitor.isEnabled()) { if (mTraceMonitor == null || !mTraceMonitor.isEnabled()) { return; return; } } mTraceMonitor.stop(mWriter); ResultWriter writer = new ResultWriter() .forScenario(new ScenarioBuilder() .forClass(createTempFile("temp", "").getName()).build()) .withOutputDir(createTempDirectory("temp").toFile()) .setRunComplete(); mTraceMonitor.stop(writer); } private static void waitDataSourceIsAvailable() { final int timeoutMs = 10000; final int busyWaitIntervalMs = 100; int elapsedMs = 0; while (!isDataSourceAvailable()) { try { Thread.sleep(busyWaitIntervalMs); elapsedMs += busyWaitIntervalMs; if (elapsedMs >= timeoutMs) { throw new RuntimeException("Data source didn't become available." + " Waited for: " + timeoutMs + " ms"); } } catch (InterruptedException e) { throw new RuntimeException(e); } } } private static boolean isDataSourceAvailable() { byte[] proto = executeShellCommand("perfetto --query-raw"); try { TracingServiceState state = TracingServiceState.parseFrom(proto); Optional<Integer> producerId = Optional.empty(); for (TracingServiceState.Producer producer : state.getProducersList()) { if (producer.getPid() == android.os.Process.myPid()) { producerId = Optional.of(producer.getId()); break; } } if (producerId.isEmpty()) { return false; } for (TracingServiceState.DataSource ds : state.getDataSourcesList()) { if (ds.getDsDescriptor().getName().equals(TEST_DATA_SOURCE_NAME) && ds.getProducerId() == producerId.get()) { return true; } } } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } return false; } private static byte[] executeShellCommand(String command) { try { ParcelFileDescriptor fd = InstrumentationRegistry.getInstrumentation().getUiAutomation() .executeShellCommand(command); FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(fd); return is.readAllBytes(); } catch (IOException e) { throw new RuntimeException(e); } } } } } Loading
services/core/java/com/android/server/wm/WindowTracingDataSource.java +2 −4 Original line number Original line Diff line number Diff line Loading @@ -38,8 +38,6 @@ import java.util.concurrent.atomic.AtomicBoolean; public final class WindowTracingDataSource extends DataSource<WindowTracingDataSource.Instance, public final class WindowTracingDataSource extends DataSource<WindowTracingDataSource.Instance, WindowTracingDataSource.TlsState, Void> { WindowTracingDataSource.TlsState, Void> { public static final String DATA_SOURCE_NAME = "android.windowmanager"; public static class TlsState { public static class TlsState { public final Config mConfig; public final Config mConfig; public final AtomicBoolean mIsStarting = new AtomicBoolean(true); public final AtomicBoolean mIsStarting = new AtomicBoolean(true); Loading Loading @@ -78,8 +76,8 @@ public final class WindowTracingDataSource extends DataSource<WindowTracingDataS @NonNull @NonNull private final WeakReference<WindowTracingPerfetto> mWindowTracing; private final WeakReference<WindowTracingPerfetto> mWindowTracing; public WindowTracingDataSource(WindowTracingPerfetto windowTracing) { public WindowTracingDataSource(WindowTracingPerfetto windowTracing, String dataSourceName) { super(DATA_SOURCE_NAME); super(dataSourceName); mWindowTracing = new WeakReference<>(windowTracing); mWindowTracing = new WeakReference<>(windowTracing); Producer.init(InitArguments.DEFAULTS); Producer.init(InitArguments.DEFAULTS); Loading
services/core/java/com/android/server/wm/WindowTracingPerfetto.java +5 −3 Original line number Original line Diff line number Diff line Loading @@ -32,19 +32,21 @@ import java.util.concurrent.atomic.AtomicInteger; class WindowTracingPerfetto extends WindowTracing { class WindowTracingPerfetto extends WindowTracing { private static final String TAG = "WindowTracing"; private static final String TAG = "WindowTracing"; private static final String PRODUCTION_DATA_SOURCE_NAME = "android.windowmanager"; private final AtomicInteger mCountSessionsOnFrame = new AtomicInteger(); private final AtomicInteger mCountSessionsOnFrame = new AtomicInteger(); private final AtomicInteger mCountSessionsOnTransaction = new AtomicInteger(); private final AtomicInteger mCountSessionsOnTransaction = new AtomicInteger(); private final WindowTracingDataSource mDataSource = new WindowTracingDataSource(this); private final WindowTracingDataSource mDataSource; WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) { WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) { this(service, choreographer, service.mGlobalLock); this(service, choreographer, service.mGlobalLock, PRODUCTION_DATA_SOURCE_NAME); } } @VisibleForTesting @VisibleForTesting WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer, WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer, WindowManagerGlobalLock globalLock) { WindowManagerGlobalLock globalLock, String dataSourceName) { super(service, choreographer, globalLock); super(service, choreographer, globalLock); mDataSource = new WindowTracingDataSource(this, dataSourceName); } } @Override @Override Loading
services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java +118 −37 Original line number Original line Diff line number Diff line /* /* * Copyright (C) 2017 The Android Open Source Project * Copyright (C) 2024 The Android Open Source Project * * * Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License. Loading Loading @@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.eq; import static java.io.File.createTempFile; import static java.io.File.createTempFile; import static java.nio.file.Files.createTempDirectory; import static java.nio.file.Files.createTempDirectory; import android.os.ParcelFileDescriptor; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit; import android.tools.ScenarioBuilder; import android.tools.ScenarioBuilder; import android.tools.traces.io.ResultWriter; import android.tools.traces.io.ResultWriter; Loading @@ -35,107 +36,187 @@ import android.tools.traces.monitors.PerfettoTraceMonitor; import android.view.Choreographer; import android.view.Choreographer; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.google.protobuf.InvalidProtocolBufferException; import org.junit.After; import org.junit.After; import org.junit.Before; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.Test; import org.mockito.Mockito; import org.mockito.Mockito; import perfetto.protos.PerfettoConfig.TracingServiceState; import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency; import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency; import java.io.FileInputStream; import java.io.IOException; import java.util.Optional; /** /** * Test class for {@link WindowTracingPerfetto}. * Test class for {@link WindowTracingPerfetto}. */ */ @SmallTest @SmallTest @Presubmit @Presubmit public class WindowTracingPerfettoTest { public class WindowTracingPerfettoTest { private WindowManagerService mWmMock; private static final String TEST_DATA_SOURCE_NAME = "android.windowmanager.test"; private Choreographer mChoreographer; private WindowTracing mWindowTracing; private PerfettoTraceMonitor mTraceMonitor; private ResultWriter mWriter; @Before private static WindowManagerService sWmMock; public void setUp() throws Exception { private static Choreographer sChoreographer; mWmMock = Mockito.mock(WindowManagerService.class); private static WindowTracing sWindowTracing; Mockito.doNothing().when(mWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt()); mChoreographer = Mockito.mock(Choreographer.class); private PerfettoTraceMonitor mTraceMonitor; mWindowTracing = new WindowTracingPerfetto(mWmMock, mChoreographer, @BeforeClass new WindowManagerGlobalLock()); public static void setUpOnce() throws Exception { sWmMock = Mockito.mock(WindowManagerService.class); Mockito.doNothing().when(sWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt()); sChoreographer = Mockito.mock(Choreographer.class); sWindowTracing = new WindowTracingPerfetto(sWmMock, sChoreographer, new WindowManagerGlobalLock(), TEST_DATA_SOURCE_NAME); waitDataSourceIsAvailable(); } mWriter = new ResultWriter() @Before .forScenario(new ScenarioBuilder() public void setUp() throws IOException { .forClass(createTempFile("temp", "").getName()).build()) Mockito.clearInvocations(sWmMock); .withOutputDir(createTempDirectory("temp").toFile()) .setRunComplete(); } } @After @After public void tearDown() throws Exception { public void tearDown() throws IOException { stopTracing(); stopTracing(); } } @Test @Test public void isEnabled_returnsFalseByDefault() { public void isEnabled_returnsFalseByDefault() { assertFalse(mWindowTracing.isEnabled()); assertFalse(sWindowTracing.isEnabled()); } } @Test @Test public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() { public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() throws IOException { startTracing(false); startTracing(false); assertTrue(mWindowTracing.isEnabled()); assertTrue(sWindowTracing.isEnabled()); stopTracing(); stopTracing(); assertFalse(mWindowTracing.isEnabled()); assertFalse(sWindowTracing.isEnabled()); } } @Test @Test public void trace_ignoresLogStateCalls_ifTracingIsDisabled() { public void trace_ignoresLogStateCalls_ifTracingIsDisabled() { mWindowTracing.logState("where"); sWindowTracing.logState("where"); verifyZeroInteractions(mWmMock); verifyZeroInteractions(sWmMock); } } @Test @Test public void trace_writesInitialStateSnapshot_whenTracingStarts() throws Exception { public void trace_writesInitialStateSnapshot_whenTracingStarts() { startTracing(false); startTracing(false); verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } } @Test @Test public void trace_writesStateSnapshot_onLogStateCall() throws Exception { public void trace_writesStateSnapshot_onLogStateCall() { startTracing(false); startTracing(false); mWindowTracing.logState("where"); sWindowTracing.logState("where"); verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); verify(sWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } } @Test @Test public void dump_writesOneSingleStateSnapshot() throws Exception { public void dump_writesOneSingleStateSnapshot() { startTracing(true); startTracing(true); mWindowTracing.logState("where"); sWindowTracing.logState("where"); verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } } private void startTracing(boolean isDump) { private void startTracing(boolean isDump) { if (isDump) { if (isDump) { mTraceMonitor = PerfettoTraceMonitor mTraceMonitor = PerfettoTraceMonitor .newBuilder() .newBuilder() .enableWindowManagerDump() .enableWindowManagerDump(TEST_DATA_SOURCE_NAME) .build(); .build(); } else { } else { mTraceMonitor = PerfettoTraceMonitor mTraceMonitor = PerfettoTraceMonitor .newBuilder() .newBuilder() .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION) .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION, TEST_DATA_SOURCE_NAME) .build(); .build(); } } mTraceMonitor.start(); mTraceMonitor.start(); } } private void stopTracing() { private void stopTracing() throws IOException { if (mTraceMonitor == null || !mTraceMonitor.isEnabled()) { if (mTraceMonitor == null || !mTraceMonitor.isEnabled()) { return; return; } } mTraceMonitor.stop(mWriter); ResultWriter writer = new ResultWriter() .forScenario(new ScenarioBuilder() .forClass(createTempFile("temp", "").getName()).build()) .withOutputDir(createTempDirectory("temp").toFile()) .setRunComplete(); mTraceMonitor.stop(writer); } private static void waitDataSourceIsAvailable() { final int timeoutMs = 10000; final int busyWaitIntervalMs = 100; int elapsedMs = 0; while (!isDataSourceAvailable()) { try { Thread.sleep(busyWaitIntervalMs); elapsedMs += busyWaitIntervalMs; if (elapsedMs >= timeoutMs) { throw new RuntimeException("Data source didn't become available." + " Waited for: " + timeoutMs + " ms"); } } catch (InterruptedException e) { throw new RuntimeException(e); } } } private static boolean isDataSourceAvailable() { byte[] proto = executeShellCommand("perfetto --query-raw"); try { TracingServiceState state = TracingServiceState.parseFrom(proto); Optional<Integer> producerId = Optional.empty(); for (TracingServiceState.Producer producer : state.getProducersList()) { if (producer.getPid() == android.os.Process.myPid()) { producerId = Optional.of(producer.getId()); break; } } if (producerId.isEmpty()) { return false; } for (TracingServiceState.DataSource ds : state.getDataSourcesList()) { if (ds.getDsDescriptor().getName().equals(TEST_DATA_SOURCE_NAME) && ds.getProducerId() == producerId.get()) { return true; } } } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } return false; } private static byte[] executeShellCommand(String command) { try { ParcelFileDescriptor fd = InstrumentationRegistry.getInstrumentation().getUiAutomation() .executeShellCommand(command); FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(fd); return is.readAllBytes(); } catch (IOException e) { throw new RuntimeException(e); } } } } }