Loading services/core/java/com/android/server/wm/WindowTracingDataSource.java +2 −4 Original line number Diff line number Diff line Loading @@ -38,8 +38,6 @@ import java.util.concurrent.atomic.AtomicBoolean; 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); Loading Loading @@ -78,8 +76,8 @@ public final class WindowTracingDataSource extends DataSource<WindowTracingDataS @NonNull private final WeakReference<WindowTracingPerfetto> mWindowTracing; public WindowTracingDataSource(WindowTracingPerfetto windowTracing) { super(DATA_SOURCE_NAME); public WindowTracingDataSource(WindowTracingPerfetto windowTracing, String dataSourceName) { super(dataSourceName); mWindowTracing = new WeakReference<>(windowTracing); Producer.init(InitArguments.DEFAULTS); Loading services/core/java/com/android/server/wm/WindowTracingPerfetto.java +5 −3 Original line number Diff line number Diff line Loading @@ -32,19 +32,21 @@ import java.util.concurrent.atomic.AtomicInteger; class WindowTracingPerfetto extends 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 mCountSessionsOnTransaction = new AtomicInteger(); private final WindowTracingDataSource mDataSource = new WindowTracingDataSource(this); private final WindowTracingDataSource mDataSource; WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) { this(service, choreographer, service.mGlobalLock); this(service, choreographer, service.mGlobalLock, PRODUCTION_DATA_SOURCE_NAME); } @VisibleForTesting WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer, WindowManagerGlobalLock globalLock) { WindowManagerGlobalLock globalLock, String dataSourceName) { super(service, choreographer, globalLock); mDataSource = new WindowTracingDataSource(this, dataSourceName); } @Override Loading services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java +118 −37 Original line number 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"); * 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.nio.file.Files.createTempDirectory; import android.os.ParcelFileDescriptor; import android.platform.test.annotations.Presubmit; import android.tools.ScenarioBuilder; import android.tools.traces.io.ResultWriter; Loading @@ -35,107 +36,187 @@ import android.tools.traces.monitors.PerfettoTraceMonitor; import android.view.Choreographer; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.google.protobuf.InvalidProtocolBufferException; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import perfetto.protos.PerfettoConfig.TracingServiceState; import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency; import java.io.FileInputStream; import java.io.IOException; import java.util.Optional; /** * Test class for {@link WindowTracingPerfetto}. */ @SmallTest @Presubmit public class WindowTracingPerfettoTest { private WindowManagerService mWmMock; private Choreographer mChoreographer; private WindowTracing mWindowTracing; private PerfettoTraceMonitor mTraceMonitor; private ResultWriter mWriter; private static final String TEST_DATA_SOURCE_NAME = "android.windowmanager.test"; @Before public void setUp() throws Exception { mWmMock = Mockito.mock(WindowManagerService.class); Mockito.doNothing().when(mWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt()); private static WindowManagerService sWmMock; private static Choreographer sChoreographer; private static WindowTracing sWindowTracing; mChoreographer = Mockito.mock(Choreographer.class); private PerfettoTraceMonitor mTraceMonitor; mWindowTracing = new WindowTracingPerfetto(mWmMock, mChoreographer, new WindowManagerGlobalLock()); @BeforeClass 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() .forScenario(new ScenarioBuilder() .forClass(createTempFile("temp", "").getName()).build()) .withOutputDir(createTempDirectory("temp").toFile()) .setRunComplete(); @Before public void setUp() throws IOException { Mockito.clearInvocations(sWmMock); } @After public void tearDown() throws Exception { public void tearDown() throws IOException { stopTracing(); } @Test public void isEnabled_returnsFalseByDefault() { assertFalse(mWindowTracing.isEnabled()); assertFalse(sWindowTracing.isEnabled()); } @Test public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() { public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() throws IOException { startTracing(false); assertTrue(mWindowTracing.isEnabled()); assertTrue(sWindowTracing.isEnabled()); stopTracing(); assertFalse(mWindowTracing.isEnabled()); assertFalse(sWindowTracing.isEnabled()); } @Test public void trace_ignoresLogStateCalls_ifTracingIsDisabled() { mWindowTracing.logState("where"); verifyZeroInteractions(mWmMock); sWindowTracing.logState("where"); verifyZeroInteractions(sWmMock); } @Test public void trace_writesInitialStateSnapshot_whenTracingStarts() throws Exception { public void trace_writesInitialStateSnapshot_whenTracingStarts() { startTracing(false); verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } @Test public void trace_writesStateSnapshot_onLogStateCall() throws Exception { public void trace_writesStateSnapshot_onLogStateCall() { startTracing(false); mWindowTracing.logState("where"); verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); sWindowTracing.logState("where"); verify(sWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } @Test public void dump_writesOneSingleStateSnapshot() throws Exception { public void dump_writesOneSingleStateSnapshot() { startTracing(true); mWindowTracing.logState("where"); verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); sWindowTracing.logState("where"); verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } private void startTracing(boolean isDump) { if (isDump) { mTraceMonitor = PerfettoTraceMonitor .newBuilder() .enableWindowManagerDump() .enableWindowManagerDump(TEST_DATA_SOURCE_NAME) .build(); } else { mTraceMonitor = PerfettoTraceMonitor .newBuilder() .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION) .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION, TEST_DATA_SOURCE_NAME) .build(); } mTraceMonitor.start(); } private void stopTracing() { private void stopTracing() throws IOException { if (mTraceMonitor == null || !mTraceMonitor.isEnabled()) { 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 Diff line number Diff line Loading @@ -38,8 +38,6 @@ import java.util.concurrent.atomic.AtomicBoolean; 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); Loading Loading @@ -78,8 +76,8 @@ public final class WindowTracingDataSource extends DataSource<WindowTracingDataS @NonNull private final WeakReference<WindowTracingPerfetto> mWindowTracing; public WindowTracingDataSource(WindowTracingPerfetto windowTracing) { super(DATA_SOURCE_NAME); public WindowTracingDataSource(WindowTracingPerfetto windowTracing, String dataSourceName) { super(dataSourceName); mWindowTracing = new WeakReference<>(windowTracing); Producer.init(InitArguments.DEFAULTS); Loading
services/core/java/com/android/server/wm/WindowTracingPerfetto.java +5 −3 Original line number Diff line number Diff line Loading @@ -32,19 +32,21 @@ import java.util.concurrent.atomic.AtomicInteger; class WindowTracingPerfetto extends 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 mCountSessionsOnTransaction = new AtomicInteger(); private final WindowTracingDataSource mDataSource = new WindowTracingDataSource(this); private final WindowTracingDataSource mDataSource; WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) { this(service, choreographer, service.mGlobalLock); this(service, choreographer, service.mGlobalLock, PRODUCTION_DATA_SOURCE_NAME); } @VisibleForTesting WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer, WindowManagerGlobalLock globalLock) { WindowManagerGlobalLock globalLock, String dataSourceName) { super(service, choreographer, globalLock); mDataSource = new WindowTracingDataSource(this, dataSourceName); } @Override Loading
services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java +118 −37 Original line number 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"); * 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.nio.file.Files.createTempDirectory; import android.os.ParcelFileDescriptor; import android.platform.test.annotations.Presubmit; import android.tools.ScenarioBuilder; import android.tools.traces.io.ResultWriter; Loading @@ -35,107 +36,187 @@ import android.tools.traces.monitors.PerfettoTraceMonitor; import android.view.Choreographer; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.google.protobuf.InvalidProtocolBufferException; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; import perfetto.protos.PerfettoConfig.TracingServiceState; import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency; import java.io.FileInputStream; import java.io.IOException; import java.util.Optional; /** * Test class for {@link WindowTracingPerfetto}. */ @SmallTest @Presubmit public class WindowTracingPerfettoTest { private WindowManagerService mWmMock; private Choreographer mChoreographer; private WindowTracing mWindowTracing; private PerfettoTraceMonitor mTraceMonitor; private ResultWriter mWriter; private static final String TEST_DATA_SOURCE_NAME = "android.windowmanager.test"; @Before public void setUp() throws Exception { mWmMock = Mockito.mock(WindowManagerService.class); Mockito.doNothing().when(mWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt()); private static WindowManagerService sWmMock; private static Choreographer sChoreographer; private static WindowTracing sWindowTracing; mChoreographer = Mockito.mock(Choreographer.class); private PerfettoTraceMonitor mTraceMonitor; mWindowTracing = new WindowTracingPerfetto(mWmMock, mChoreographer, new WindowManagerGlobalLock()); @BeforeClass 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() .forScenario(new ScenarioBuilder() .forClass(createTempFile("temp", "").getName()).build()) .withOutputDir(createTempDirectory("temp").toFile()) .setRunComplete(); @Before public void setUp() throws IOException { Mockito.clearInvocations(sWmMock); } @After public void tearDown() throws Exception { public void tearDown() throws IOException { stopTracing(); } @Test public void isEnabled_returnsFalseByDefault() { assertFalse(mWindowTracing.isEnabled()); assertFalse(sWindowTracing.isEnabled()); } @Test public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() { public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() throws IOException { startTracing(false); assertTrue(mWindowTracing.isEnabled()); assertTrue(sWindowTracing.isEnabled()); stopTracing(); assertFalse(mWindowTracing.isEnabled()); assertFalse(sWindowTracing.isEnabled()); } @Test public void trace_ignoresLogStateCalls_ifTracingIsDisabled() { mWindowTracing.logState("where"); verifyZeroInteractions(mWmMock); sWindowTracing.logState("where"); verifyZeroInteractions(sWmMock); } @Test public void trace_writesInitialStateSnapshot_whenTracingStarts() throws Exception { public void trace_writesInitialStateSnapshot_whenTracingStarts() { startTracing(false); verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } @Test public void trace_writesStateSnapshot_onLogStateCall() throws Exception { public void trace_writesStateSnapshot_onLogStateCall() { startTracing(false); mWindowTracing.logState("where"); verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); sWindowTracing.logState("where"); verify(sWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } @Test public void dump_writesOneSingleStateSnapshot() throws Exception { public void dump_writesOneSingleStateSnapshot() { startTracing(true); mWindowTracing.logState("where"); verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); sWindowTracing.logState("where"); verify(sWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL)); } private void startTracing(boolean isDump) { if (isDump) { mTraceMonitor = PerfettoTraceMonitor .newBuilder() .enableWindowManagerDump() .enableWindowManagerDump(TEST_DATA_SOURCE_NAME) .build(); } else { mTraceMonitor = PerfettoTraceMonitor .newBuilder() .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION) .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION, TEST_DATA_SOURCE_NAME) .build(); } mTraceMonitor.start(); } private void stopTracing() { private void stopTracing() throws IOException { if (mTraceMonitor == null || !mTraceMonitor.isEnabled()) { 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); } } }