diff options
7 files changed, 303 insertions, 471 deletions
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c747c6d95fcd..0d9fce28e73b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5081,11 +5081,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void startWindowTrace(){ - try { - mWindowTracing.startTrace(null /* printwriter */); - } catch (IOException e) { - throw new RuntimeException(e); - } + mWindowTracing.startTrace(null /* printwriter */); } @Override diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java index e4461ea90c91..2ce6e6c1d049 100644 --- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java +++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java @@ -20,7 +20,6 @@ import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER; import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H; import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L; -import android.os.Trace; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -36,24 +35,30 @@ import java.util.Queue; /** * Buffer used for window tracing. */ -abstract class WindowTraceBuffer { +class WindowTraceBuffer { private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; - final Object mBufferLock = new Object(); - final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>(); - final File mTraceFile; - int mBufferSize; - private final int mBufferCapacity; + private final Object mBufferLock = new Object(); - WindowTraceBuffer(int size, File traceFile) throws IOException { - mBufferCapacity = size; - mTraceFile = traceFile; + private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>(); + private int mBufferUsedSize; + private int mBufferCapacity; - initTraceFile(); + WindowTraceBuffer(int bufferCapacity) { + mBufferCapacity = bufferCapacity; + resetBuffer(); } int getAvailableSpace() { - return mBufferCapacity - mBufferSize; + return mBufferCapacity - mBufferUsedSize; + } + + int size() { + return mBuffer.size(); + } + + void setCapacity(int capacity) { + mBufferCapacity = capacity; } /** @@ -70,42 +75,37 @@ abstract class WindowTraceBuffer { + mBufferCapacity + " Object size: " + protoLength); } synchronized (mBufferLock) { - boolean canAdd = canAdd(protoLength); - if (canAdd) { - mBuffer.add(proto); - mBufferSize += protoLength; - } + discardOldest(protoLength); + mBuffer.add(proto); + mBufferUsedSize += protoLength; mBufferLock.notify(); } } - /** - * Stops the buffer execution and flush all buffer content to the disk. - * - * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile} - */ - void dump() throws IOException, InterruptedException { - try { - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile"); - writeTraceToFile(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - } - } - - @VisibleForTesting boolean contains(byte[] other) { return mBuffer.stream() .anyMatch(p -> Arrays.equals(p.getBytes(), other)); } - private void initTraceFile() throws IOException { - mTraceFile.delete(); - try (OutputStream os = new FileOutputStream(mTraceFile)) { - mTraceFile.setReadable(true, false); - ProtoOutputStream proto = new ProtoOutputStream(os); - proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); - proto.flush(); + /** + * Writes the trace buffer to disk. + */ + void writeTraceToFile(File traceFile) throws IOException { + synchronized (mBufferLock) { + traceFile.delete(); + traceFile.setReadable(true, false); + try (OutputStream os = new FileOutputStream(traceFile)) { + ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + os.write(proto.getBytes()); + while (!mBuffer.isEmpty()) { + proto = mBuffer.poll(); + mBufferUsedSize -= proto.getRawSize(); + byte[] protoBytes = proto.getBytes(); + os.write(protoBytes); + } + os.flush(); + } } } @@ -114,59 +114,48 @@ abstract class WindowTraceBuffer { * smaller than the overall buffer size. * * @param protoLength byte array representation of the Proto object to add - * @return {@code true} if the element can be added to the buffer or not - */ - abstract boolean canAdd(int protoLength); - - /** - * Flush all buffer content to the disk. - * - * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile} */ - abstract void writeTraceToFile() throws IOException, InterruptedException; - - /** - * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceRingBuffer} for - * continuous mode or a {@link WindowTraceQueueBuffer} otherwise - */ - static class Builder { - private boolean mContinuous; - private File mTraceFile; - private int mBufferCapacity; - - Builder setContinuousMode(boolean continuous) { - mContinuous = continuous; - return this; - } + private void discardOldest(int protoLength) { + long availableSpace = getAvailableSpace(); - Builder setTraceFile(File traceFile) { - mTraceFile = traceFile; - return this; - } + while (availableSpace < protoLength) { - Builder setBufferCapacity(int size) { - mBufferCapacity = size; - return this; + ProtoOutputStream item = mBuffer.poll(); + if (item == null) { + throw new IllegalStateException("No element to discard from buffer"); + } + mBufferUsedSize -= item.getRawSize(); + availableSpace = getAvailableSpace(); } + } - File getFile() { - return mTraceFile; + /** + * Removes all elements form the buffer + */ + void resetBuffer() { + synchronized (mBufferLock) { + mBuffer.clear(); + mBufferUsedSize = 0; } + } - WindowTraceBuffer build() throws IOException { - if (mBufferCapacity <= 0) { - throw new IllegalStateException("Buffer capacity must be greater than 0."); - } - - if (mTraceFile == null) { - throw new IllegalArgumentException("A valid trace file must be specified."); - } + @VisibleForTesting + int getBufferSize() { + return mBufferUsedSize; + } - if (mContinuous) { - return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile); - } else { - return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile); - } + String getStatus() { + synchronized (mBufferLock) { + return "Buffer size: " + + mBufferCapacity + + " bytes" + + "\n" + + "Buffer usage: " + + mBufferUsedSize + + " bytes" + + "\n" + + "Elements in the buffer: " + + mBuffer.size(); } } } diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java deleted file mode 100644 index 5888b7a799cf..000000000000 --- a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2019 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.os.Build.IS_USER; - -import android.util.Log; -import android.util.proto.ProtoOutputStream; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first - * {@code #size size} bytes of window trace elements. - * Once the buffer is full it will no longer accepts new elements. - */ -class WindowTraceQueueBuffer extends WindowTraceBuffer { - private static final String TAG = "WindowTracing"; - - private Thread mConsumerThread; - private boolean mCancel; - - @VisibleForTesting - WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread) - throws IOException { - super(size, traceFile); - if (startConsumerThread) { - initializeConsumerThread(); - } - } - - WindowTraceQueueBuffer(int size, File traceFile) throws IOException { - this(size, traceFile, !IS_USER); - } - - private void initializeConsumerThread() { - mCancel = false; - mConsumerThread = new Thread(() -> { - try { - loop(); - } catch (InterruptedException e) { - Log.i(TAG, "Interrupting trace consumer thread"); - } catch (IOException e) { - Log.e(TAG, "Failed to execute trace consumer thread", e); - } - }, "window_tracing"); - mConsumerThread.start(); - } - - private void loop() throws IOException, InterruptedException { - while (!mCancel) { - ProtoOutputStream proto; - synchronized (mBufferLock) { - mBufferLock.wait(); - proto = mBuffer.poll(); - if (proto != null) { - mBufferSize -= proto.getRawSize(); - } - } - if (proto != null) { - try (OutputStream os = new FileOutputStream(mTraceFile, true)) { - byte[] protoBytes = proto.getBytes(); - os.write(protoBytes); - } - } - } - } - - @Override - boolean canAdd(int protoLength) { - long availableSpace = getAvailableSpace(); - return availableSpace >= protoLength; - } - - @Override - void writeTraceToFile() throws InterruptedException { - synchronized (mBufferLock) { - mCancel = true; - mBufferLock.notify(); - } - if (mConsumerThread != null) { - mConsumerThread.join(); - mConsumerThread = null; - } - } -} diff --git a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java b/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java deleted file mode 100644 index 77d30be816bc..000000000000 --- a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 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.util.proto.ProtoOutputStream; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * A ring buffer to store the {@code #size size} bytes of window trace data. - * The buffer operates on a trace entry level, that is, if the new trace data is larger than the - * available buffer space, the buffer will discard as many full trace entries as necessary to fit - * the new trace. - */ -class WindowTraceRingBuffer extends WindowTraceBuffer { - WindowTraceRingBuffer(int size, File traceFile) throws IOException { - super(size, traceFile); - } - - @Override - boolean canAdd(int protoLength) { - long availableSpace = getAvailableSpace(); - - while (availableSpace < protoLength) { - discardOldest(); - availableSpace = getAvailableSpace(); - } - - return true; - } - - @Override - void writeTraceToFile() throws IOException { - synchronized (mBufferLock) { - try (OutputStream os = new FileOutputStream(mTraceFile, true)) { - while (!mBuffer.isEmpty()) { - ProtoOutputStream proto = mBuffer.poll(); - mBufferSize -= proto.getRawSize(); - byte[] protoBytes = proto.getBytes(); - os.write(protoBytes); - } - } - } - } - - private void discardOldest() { - ProtoOutputStream item = mBuffer.poll(); - if (item == null) { - throw new IllegalStateException("No element to discard from buffer"); - } - mBufferSize -= item.getRawSize(); - } -} diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java index abc474d756b7..0ce215c88dad 100644 --- a/services/core/java/com/android/server/wm/WindowTracing.java +++ b/services/core/java/com/android/server/wm/WindowTracing.java @@ -31,8 +31,6 @@ import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.Choreographer; -import com.android.internal.annotations.VisibleForTesting; - import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -47,139 +45,191 @@ class WindowTracing { * Maximum buffer size, currently defined as 512 KB * Size was experimentally defined to fit between 100 to 150 elements. */ - private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024; + private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024; + private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024; + private static final int BUFFER_CAPACITY_ALL = 4096 * 1024; + private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb"; private static final String TAG = "WindowTracing"; private final WindowManagerService mService; private final Choreographer mChoreographer; private final WindowManagerGlobalLock mGlobalLock; - private final Object mLock = new Object(); - private final WindowTraceBuffer.Builder mBufferBuilder; - - private WindowTraceBuffer mTraceBuffer; + private final Object mEnabledLock = new Object(); + private final File mTraceFile; + private final WindowTraceBuffer mBuffer; + private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) -> + log("onFrame" /* where */); - private @WindowTraceLogLevel int mWindowTraceLogLevel = WindowTraceLogLevel.TRIM; - private boolean mContinuousMode; + private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM; + private boolean mLogOnFrame = false; private boolean mEnabled; private volatile boolean mEnabledLockFree; private boolean mScheduled; - private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) -> - log("onFrame" /* where */); - private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) { - this(file, service, choreographer, service.mGlobalLock); + static WindowTracing createDefaultAndStartLooper(WindowManagerService service, + Choreographer choreographer) { + File file = new File(TRACE_FILENAME); + return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM); } - @VisibleForTesting - WindowTracing(File file, WindowManagerService service, Choreographer choreographer, - WindowManagerGlobalLock globalLock) { - mBufferBuilder = new WindowTraceBuffer.Builder() - .setTraceFile(file) - .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE); + private WindowTracing(File file, WindowManagerService service, Choreographer choreographer, + int bufferCapacity) { + this(file, service, choreographer, service.mGlobalLock, bufferCapacity); + } + WindowTracing(File file, WindowManagerService service, Choreographer choreographer, + WindowManagerGlobalLock globalLock, int bufferCapacity) { mChoreographer = choreographer; mService = service; mGlobalLock = globalLock; + mTraceFile = file; + mBuffer = new WindowTraceBuffer(bufferCapacity); + setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */); } - void startTrace(@Nullable PrintWriter pw) throws IOException { + void startTrace(@Nullable PrintWriter pw) { if (IS_USER) { logAndPrintln(pw, "Error: Tracing is not supported on user builds."); return; } - synchronized (mLock) { - logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + "."); - if (mTraceBuffer != null) { - writeTraceToFileLocked(); - } - mTraceBuffer = mBufferBuilder - .setContinuousMode(mContinuousMode) - .build(); + synchronized (mEnabledLock) { + logAndPrintln(pw, "Start tracing to " + mTraceFile + "."); + mBuffer.resetBuffer(); mEnabled = mEnabledLockFree = true; } } - private void logAndPrintln(@Nullable PrintWriter pw, String msg) { - Log.i(TAG, msg); - if (pw != null) { - pw.println(msg); - pw.flush(); - } - } - void stopTrace(@Nullable PrintWriter pw) { if (IS_USER) { logAndPrintln(pw, "Error: Tracing is not supported on user builds."); return; } - synchronized (mLock) { - logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile() - + ". Waiting for traces to flush."); + synchronized (mEnabledLock) { + logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush."); mEnabled = mEnabledLockFree = false; - synchronized (mLock) { - if (mEnabled) { - logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush."); - throw new IllegalStateException("tracing enabled while waiting for flush."); - } - writeTraceToFileLocked(); - mTraceBuffer = null; + if (mEnabled) { + logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush."); + throw new IllegalStateException("tracing enabled while waiting for flush."); } - logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + "."); + writeTraceToFileLocked(); + logAndPrintln(pw, "Trace written to " + mTraceFile + "."); } } - @VisibleForTesting - void setContinuousMode(boolean continuous, PrintWriter pw) { - logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous); + private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing log level to " + logLevel); + mLogLevel = logLevel; - if (mEnabled) { - logAndPrintln(pw, "Trace is currently active, change will take effect once the " - + "trace is restarted."); + switch (logLevel) { + case WindowTraceLogLevel.ALL: { + setBufferCapacity(BUFFER_CAPACITY_ALL, pw); + break; + } + case WindowTraceLogLevel.TRIM: { + setBufferCapacity(BUFFER_CAPACITY_TRIM, pw); + break; + } + case WindowTraceLogLevel.CRITICAL: { + setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw); + break; + } } - mContinuousMode = continuous; - mWindowTraceLogLevel = (continuous) ? WindowTraceLogLevel.CRITICAL : - WindowTraceLogLevel.TRIM; } - boolean isEnabled() { - return mEnabledLockFree; + private void setLogFrequency(boolean onFrame, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing log frequency to " + + ((onFrame) ? "frame" : "transaction")); + mLogOnFrame = onFrame; } - static WindowTracing createDefaultAndStartLooper(WindowManagerService service, - Choreographer choreographer) { - File file = new File("/data/misc/wmtrace/wm_trace.pb"); - return new WindowTracing(file, service, choreographer); + private void setBufferCapacity(int capacity, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes"); + mBuffer.setCapacity(capacity); + } + + boolean isEnabled() { + return mEnabledLockFree; } int onShellCommand(ShellCommand shell) { PrintWriter pw = shell.getOutPrintWriter(); - try { - String cmd = shell.getNextArgRequired(); - switch (cmd) { - case "start": - startTrace(pw); - return 0; - case "stop": - stopTrace(pw); - return 0; - case "continuous": - setContinuousMode(Boolean.valueOf(shell.getNextArgRequired()), pw); - return 0; - default: - pw.println("Unknown command: " + cmd); - return -1; - } - } catch (IOException e) { - logAndPrintln(pw, e.toString()); - throw new RuntimeException(e); + String cmd = shell.getNextArgRequired(); + switch (cmd) { + case "start": + startTrace(pw); + return 0; + case "stop": + stopTrace(pw); + return 0; + case "status": + logAndPrintln(pw, getStatus()); + return 0; + case "frame": + setLogFrequency(true /* onFrame */, pw); + mBuffer.resetBuffer(); + return 0; + case "transaction": + setLogFrequency(false /* onFrame */, pw); + mBuffer.resetBuffer(); + return 0; + case "level": + String logLevelStr = shell.getNextArgRequired().toLowerCase(); + switch (logLevelStr) { + case "all": { + setLogLevel(WindowTraceLogLevel.ALL, pw); + break; + } + case "trim": { + setLogLevel(WindowTraceLogLevel.TRIM, pw); + break; + } + case "critical": { + setLogLevel(WindowTraceLogLevel.CRITICAL, pw); + break; + } + default: { + setLogLevel(WindowTraceLogLevel.TRIM, pw); + break; + } + } + mBuffer.resetBuffer(); + return 0; + case "size": + setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw); + mBuffer.resetBuffer(); + return 0; + default: + pw.println("Unknown command: " + cmd); + pw.println("Window manager trace options:"); + pw.println(" start: Start logging"); + pw.println(" stop: Stop logging"); + pw.println(" frame: Log trace once per frame"); + pw.println(" transaction: Log each transaction"); + pw.println(" size: Set the maximum log size (in KB)"); + pw.println(" level [lvl]: Set the log level between"); + pw.println(" lvl may be one of:"); + pw.println(" critical: Only visible windows with reduced information"); + pw.println(" trim: All windows with reduced"); + pw.println(" all: All window and information"); + return -1; } } + private String getStatus() { + return "Status: " + + ((isEnabled()) ? "Enabled" : "Disabled") + + "\n" + + "Log level: " + + mLogLevel + + "\n" + + mBuffer.getStatus(); + } + /** * If tracing is enabled, log the current state or schedule the next frame to be logged, - * according to {@link #mContinuousMode}. + * according to {@link #mLogOnFrame}. * * @param where Logging point descriptor */ @@ -188,7 +238,7 @@ class WindowTracing { return; } - if (mContinuousMode) { + if (mLogOnFrame) { schedule(); } else { log(where); @@ -215,25 +265,24 @@ class WindowTracing { private void log(String where) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked"); try { - synchronized (mGlobalLock) { - ProtoOutputStream os = new ProtoOutputStream(); - long tokenOuter = os.start(ENTRY); - os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); - os.write(WHERE, where); + ProtoOutputStream os = new ProtoOutputStream(); + long tokenOuter = os.start(ENTRY); + os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); + os.write(WHERE, where); + long tokenInner = os.start(WINDOW_MANAGER_SERVICE); + synchronized (mGlobalLock) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked"); try { - long tokenInner = os.start(WINDOW_MANAGER_SERVICE); - mService.writeToProtoLocked(os, mWindowTraceLogLevel); - os.end(tokenInner); + mService.writeToProtoLocked(os, mLogLevel); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } - os.end(tokenOuter); - mTraceBuffer.add(os); - - mScheduled = false; } + os.end(tokenInner); + os.end(tokenOuter); + mBuffer.add(os); + mScheduled = false; } catch (Exception e) { Log.wtf(TAG, "Exception while tracing state", e); } finally { @@ -242,31 +291,37 @@ class WindowTracing { } /** - * Writes the trace buffer to disk. This method has no internal synchronization and should be - * externally synchronized + * Writes the trace buffer to new file for the bugreport. + * + * This method is synchronized with {@code #startTrace(PrintWriter)} and + * {@link #stopTrace(PrintWriter)}. */ - private void writeTraceToFileLocked() { - if (mTraceBuffer == null) { - return; + void writeTraceToFile() { + synchronized (mEnabledLock) { + writeTraceToFileLocked(); } + } - try { - mTraceBuffer.dump(); - } catch (IOException e) { - Log.e(TAG, "Unable to write buffer to file", e); - } catch (InterruptedException e) { - Log.e(TAG, "Unable to interrupt window tracing file write thread", e); + private void logAndPrintln(@Nullable PrintWriter pw, String msg) { + Log.i(TAG, msg); + if (pw != null) { + pw.println(msg); + pw.flush(); } } /** - * Writes the trace buffer to disk and clones it into a new file for the bugreport. - * This method is synchronized with {@code #startTrace(PrintWriter)} and - * {@link #stopTrace(PrintWriter)}. + * Writes the trace buffer to disk. This method has no internal synchronization and should be + * externally synchronized */ - void writeTraceToFile() { - synchronized (mLock) { - writeTraceToFileLocked(); + private void writeTraceToFileLocked() { + try { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked"); + mBuffer.writeTraceToFile(mTraceFile); + } catch (IOException e) { + Log.e(TAG, "Unable to write buffer to file", e); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } -} +}
\ No newline at end of file diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java index 2b8e307e23b7..b299f0dd7253 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java @@ -36,11 +36,10 @@ import org.junit.Before; import org.junit.Test; import java.io.File; -import java.io.IOException; /** - * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}. + * Test class for {@link WindowTraceBuffer}. * * Build/Install/Run: * atest WmTests:WindowTraceBufferTest @@ -49,12 +48,15 @@ import java.io.IOException; @Presubmit public class WindowTraceBufferTest { private File mFile; + private WindowTraceBuffer mBuffer; @Before public void setUp() throws Exception { final Context testContext = getInstrumentation().getContext(); mFile = testContext.getFileStreamPath("tracing_test.dat"); mFile.delete(); + + mBuffer = new WindowTraceBuffer(10); } @After @@ -63,145 +65,112 @@ public class WindowTraceBufferTest { } @Test - public void testTraceQueueBuffer_addItem() throws Exception { - ProtoOutputStream toWrite1 = getDummy(1); - ProtoOutputStream toWrite2 = getDummy(2); - ProtoOutputStream toWrite3 = getDummy(3); - final int objectSize = toWrite1.getRawSize(); - final int bufferCapacity = objectSize * 2; - - final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity); - - buffer.add(toWrite1); - byte[] toWrite1Bytes = toWrite1.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - - buffer.add(toWrite2); - byte[] toWrite2Bytes = toWrite2.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); - - buffer.add(toWrite3); - byte[] toWrite3Bytes = toWrite3.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); - assertTrue("Third element should not be in the list", - !buffer.contains(toWrite3Bytes)); - - assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2); - assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity); - assertEquals("Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 0); - } - - @Test - public void testTraceRingBuffer_addItem() throws Exception { + public void test_addItem() { ProtoOutputStream toWrite = getDummy(1); final int objectSize = toWrite.getRawSize(); + mBuffer.setCapacity(objectSize); + mBuffer.resetBuffer(); - final WindowTraceBuffer buffer = buildRingBuffer(objectSize); - - Preconditions.checkArgument(buffer.mBuffer.isEmpty()); + Preconditions.checkArgument(mBuffer.size() == 0); - buffer.add(toWrite); + mBuffer.add(toWrite); - assertEquals("Item was not added to the buffer", buffer.mBuffer.size(), 1); + assertEquals("Item was not added to the buffer", 1, mBuffer.size()); assertEquals("Total buffer getSize differs from inserted object", - buffer.mBufferSize, objectSize); - assertEquals("Available buffer space does not match used one", - buffer.getAvailableSpace(), 0); + mBuffer.getBufferSize(), objectSize); + assertEquals("Available buffer space does not match used one", 0, + mBuffer.getAvailableSpace()); } @Test - public void testTraceRingBuffer_addItemMustOverwriteOne() throws Exception { + public void test_addItemMustOverwriteOne() { ProtoOutputStream toWrite1 = getDummy(1); ProtoOutputStream toWrite2 = getDummy(2); ProtoOutputStream toWrite3 = getDummy(3); final int objectSize = toWrite1.getRawSize(); - final int bufferCapacity = objectSize * 2 + 1; - final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity); + mBuffer.setCapacity(bufferCapacity); + mBuffer.resetBuffer(); - buffer.add(toWrite1); + mBuffer.add(toWrite1); byte[] toWrite1Bytes = toWrite1.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); + mBuffer.contains(toWrite1Bytes)); - buffer.add(toWrite2); + mBuffer.add(toWrite2); byte[] toWrite2Bytes = toWrite2.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); + mBuffer.contains(toWrite1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); + mBuffer.contains(toWrite2Bytes)); - buffer.add(toWrite3); + mBuffer.add(toWrite3); byte[] toWrite3Bytes = toWrite3.getBytes(); assertTrue("First element should not be in the list", - !buffer.contains(toWrite1Bytes)); + !mBuffer.contains(toWrite1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); + mBuffer.contains(toWrite2Bytes)); assertTrue("Third element should be in the list", - buffer.contains(toWrite3Bytes)); - assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2); + mBuffer.contains(toWrite3Bytes)); + assertEquals("Buffer should have 2 elements", 2, mBuffer.size()); assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity - 1); - assertEquals(" Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 1); + mBuffer.getBufferSize(), bufferCapacity - 1); + assertEquals(" Buffer is full, available space should be 0", 1, + mBuffer.getAvailableSpace()); } @Test - public void testTraceRingBuffer_addItemMustOverwriteMultiple() throws Exception { + public void test_addItemMustOverwriteMultiple() { ProtoOutputStream toWriteSmall1 = getDummy(1); ProtoOutputStream toWriteSmall2 = getDummy(2); final int objectSize = toWriteSmall1.getRawSize(); - final int bufferCapacity = objectSize * 2; - final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity); + mBuffer.setCapacity(bufferCapacity); + mBuffer.resetBuffer(); ProtoOutputStream toWriteBig = new ProtoOutputStream(); toWriteBig.write(MAGIC_NUMBER, 1); toWriteBig.write(MAGIC_NUMBER, 2); - buffer.add(toWriteSmall1); + mBuffer.add(toWriteSmall1); byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWriteSmall1Bytes)); + mBuffer.contains(toWriteSmall1Bytes)); - buffer.add(toWriteSmall2); + mBuffer.add(toWriteSmall2); byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWriteSmall1Bytes)); + mBuffer.contains(toWriteSmall1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWriteSmall2Bytes)); + mBuffer.contains(toWriteSmall2Bytes)); - buffer.add(toWriteBig); + mBuffer.add(toWriteBig); byte[] toWriteBigBytes = toWriteBig.getBytes(); assertTrue("Third element should overwrite all others", - !buffer.contains(toWriteSmall1Bytes)); + !mBuffer.contains(toWriteSmall1Bytes)); assertTrue("Third element should overwrite all others", - !buffer.contains(toWriteSmall2Bytes)); + !mBuffer.contains(toWriteSmall2Bytes)); assertTrue("Third element should overwrite all others", - buffer.contains(toWriteBigBytes)); + mBuffer.contains(toWriteBigBytes)); - assertEquals(" Buffer should have only 1 big element", buffer.mBuffer.size(), 1); + assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size()); assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity); - assertEquals(" Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 0); + mBuffer.getBufferSize(), bufferCapacity); + assertEquals(" Buffer is full, available space should be 0", 0, + mBuffer.getAvailableSpace()); } - private WindowTraceBuffer buildRingBuffer(int capacity) throws IOException { - return new WindowTraceBuffer.Builder() - .setContinuousMode(true) - .setBufferCapacity(capacity) - .setTraceFile(mFile) - .build(); + @Test + public void test_startResetsBuffer() { + ProtoOutputStream toWrite = getDummy(1); + mBuffer.resetBuffer(); + Preconditions.checkArgument(mBuffer.size() == 0); + + mBuffer.add(toWrite); + assertEquals("Item was not added to the buffer", 1, mBuffer.size()); + mBuffer.resetBuffer(); + assertEquals("Buffer should be empty after reset", 0, mBuffer.size()); + assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize()); } private ProtoOutputStream getDummy(int value) { @@ -212,7 +181,4 @@ public class WindowTraceBufferTest { return toWrite; } - private WindowTraceBuffer buildQueueBuffer(int size) throws IOException { - return new WindowTraceQueueBuffer(size, mFile, false); - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java index 3c6e2405adff..8358fdd18e0e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java @@ -88,8 +88,7 @@ public class WindowTracingTest { mFile.delete(); mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer, - new WindowManagerGlobalLock()); - mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */); + new WindowManagerGlobalLock(), 1024); } @After @@ -103,13 +102,13 @@ public class WindowTracingTest { } @Test - public void isEnabled_returnsTrueAfterStart() throws Exception { + public void isEnabled_returnsTrueAfterStart() { mWindowTracing.startTrace(mock(PrintWriter.class)); assertTrue(mWindowTracing.isEnabled()); } @Test - public void isEnabled_returnsFalseAfterStop() throws Exception { + public void isEnabled_returnsFalseAfterStop() { mWindowTracing.startTrace(mock(PrintWriter.class)); mWindowTracing.stopTrace(mock(PrintWriter.class)); assertFalse(mWindowTracing.isEnabled()); @@ -133,6 +132,8 @@ public class WindowTracingTest { mWindowTracing.startTrace(mock(PrintWriter.class)); mWindowTracing.stopTrace(mock(PrintWriter.class)); + assertTrue("Trace file should exist", mFile.exists()); + byte[] header = new byte[MAGIC_HEADER.length]; try (InputStream is = new FileInputStream(mFile)) { assertEquals(MAGIC_HEADER.length, is.read(header)); |