Loading services/core/java/com/android/server/wm/WindowManagerService.java +6 −0 Original line number Diff line number Diff line Loading @@ -6056,6 +6056,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(" d[isplays]: active display contents"); pw.println(" t[okens]: token list"); pw.println(" w[indows]: window list"); pw.println(" trace: write Winscope trace to file"); pw.println(" cmd may also be a NAME to dump windows. NAME may"); pw.println(" be a partial substring in a window name, a"); pw.println(" Window hex object identifier, or"); Loading Loading @@ -6129,6 +6130,11 @@ public class WindowManagerService extends IWindowManager.Stub mRoot.forAllWindows(w -> {pw.println(w);}, true /* traverseTopToBottom */); } return; } else if ("trace".equals(cmd)) { synchronized (mGlobalLock) { mWindowTracing.writeTraceToFile(); } return; } else { // Dumping a single name? if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { Loading services/core/java/com/android/server/wm/WindowManagerShellCommand.java +1 −2 Original line number Diff line number Diff line Loading @@ -72,8 +72,7 @@ public class WindowManagerShellCommand extends ShellCommand { // XXX this should probably be changed to use openFileForSystem() to create // the output trace file, so the shell gets the correct semantics for where // trace files can be written. return mInternal.mWindowTracing.onShellCommand(this, getNextArgRequired()); return mInternal.mWindowTracing.onShellCommand(this); case "set-user-rotation": return runSetDisplayUserRotation(pw); case "set-fix-to-user-rotation": Loading services/core/java/com/android/server/wm/WindowTraceBuffer.java +40 −48 Original line number Diff line number Diff line Loading @@ -23,12 +23,15 @@ 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; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Queue; /** * Buffer used for window tracing. Loading @@ -36,16 +39,15 @@ import java.util.concurrent.LinkedBlockingQueue; abstract class WindowTraceBuffer { private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; final Object mBufferSizeLock = new Object(); final BlockingQueue<byte[]> mBuffer; final Object mBufferLock = new Object(); final Queue<byte[]> mBuffer = new ArrayDeque<>(); final File mTraceFile; int mBufferSize; private final int mBufferCapacity; private final File mTraceFile; WindowTraceBuffer(int size, File traceFile) throws IOException { mBufferCapacity = size; mTraceFile = traceFile; mBuffer = new LinkedBlockingQueue<>(); initTraceFile(); } Loading @@ -57,65 +59,45 @@ abstract class WindowTraceBuffer { /** * Inserts the specified element into this buffer. * * This method is synchronized with {@code #take()} and {@code #clear()} * for consistency. * * @param proto the element to add * @return {@code true} if the inserted item was inserted into the buffer * @throws IllegalStateException if the element cannot be added because it is larger * than the buffer size. */ boolean add(ProtoOutputStream proto) throws InterruptedException { void add(ProtoOutputStream proto) { byte[] protoBytes = proto.getBytes(); int protoLength = protoBytes.length; if (protoLength > mBufferCapacity) { throw new IllegalStateException("Trace object too large for the buffer. Buffer size:" + mBufferCapacity + " Object size: " + protoLength); } synchronized (mBufferSizeLock) { boolean canAdd = canAdd(protoBytes); synchronized (mBufferLock) { boolean canAdd = canAdd(protoLength); if (canAdd) { mBuffer.offer(protoBytes); mBufferSize += protoLength; } return canAdd; } mBufferLock.notify(); } void writeNextBufferElementToFile() throws IOException { byte[] proto; try { proto = take(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } /** * 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, "writeToFile"); try (OutputStream os = new FileOutputStream(mTraceFile, true)) { os.write(proto); } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile"); writeTraceToFile(); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } /** * Retrieves and removes the head of this queue, waiting if necessary * until an element becomes available. * * This method is synchronized with {@code #add(ProtoOutputStream)} and {@code #clear()} * for consistency. * * @return the head of this buffer, or {@code null} if this buffer is empty */ private byte[] take() throws InterruptedException { byte[] item = mBuffer.take(); synchronized (mBufferSizeLock) { mBufferSize -= item.length; return item; } @VisibleForTesting boolean contains(byte[] other) { return mBuffer.stream() .anyMatch(p -> Arrays.equals(p, other)); } private void initTraceFile() throws IOException { Loading @@ -132,25 +114,31 @@ abstract class WindowTraceBuffer { * Checks if the element can be added to the buffer. The element is already certain to be * smaller than the overall buffer size. * * @param protoBytes byte array representation of the Proto object to add * @return <tt>true<</tt> if the element can be added to the buffer or not * @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(byte[] protoBytes) throws InterruptedException; 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 writeToDisk() throws IOException, InterruptedException; abstract void writeTraceToFile() throws IOException, InterruptedException; /** * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceQueueBuffer} * 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; } Builder setTraceFile(File traceFile) { mTraceFile = traceFile; Loading @@ -175,7 +163,11 @@ abstract class WindowTraceBuffer { throw new IllegalArgumentException("A valid trace file must be specified."); } if (mContinuous) { return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile); } else { return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile); } } } } services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java +43 −25 Original line number Diff line number Diff line Loading @@ -18,10 +18,14 @@ package com.android.server.wm; import static android.os.Build.IS_USER; import android.util.Log; 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 Loading @@ -29,14 +33,17 @@ import java.io.IOException; * Once the buffer is full it will no longer accepts new elements. */ class WindowTraceQueueBuffer extends WindowTraceBuffer { private Thread mWriterThread; private static final String TAG = "WindowTracing"; private Thread mConsumerThread; private boolean mCancel; @VisibleForTesting WindowTraceQueueBuffer(int size, File traceFile, boolean startWriterThread) throws IOException { WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread) throws IOException { super(size, traceFile); if (startWriterThread) { initializeWriterThread(); if (startConsumerThread) { initializeConsumerThread(); } } Loading @@ -44,45 +51,56 @@ class WindowTraceQueueBuffer extends WindowTraceBuffer { this(size, traceFile, !IS_USER); } private void initializeWriterThread() { private void initializeConsumerThread() { mCancel = false; mWriterThread = new Thread(() -> { mConsumerThread = new Thread(() -> { try { loop(); } catch (InterruptedException e) { Log.i(TAG, "Interrupting trace consumer thread"); } catch (IOException e) { throw new IllegalStateException("Failed to execute trace write loop thread", e); Log.e(TAG, "Failed to execute trace consumer thread", e); } }, "window_tracing"); mWriterThread.start(); mConsumerThread.start(); } private void loop() throws IOException { private void loop() throws IOException, InterruptedException { while (!mCancel) { writeNextBufferElementToFile(); byte[] proto; synchronized (mBufferLock) { mBufferLock.wait(); proto = mBuffer.poll(); if (proto != null) { mBufferSize -= proto.length; } } private void restartWriterThread() throws InterruptedException { if (mWriterThread != null) { mCancel = true; mWriterThread.interrupt(); mWriterThread.join(); initializeWriterThread(); if (proto != null) { try (OutputStream os = new FileOutputStream(mTraceFile, true)) { os.write(proto); } } } } @Override boolean canAdd(byte[] protoBytes) { boolean canAdd(int protoLength) { long availableSpace = getAvailableSpace(); return availableSpace >= protoBytes.length; return availableSpace >= protoLength; } @Override void writeToDisk() throws InterruptedException { while (!mBuffer.isEmpty()) { mBufferSizeLock.wait(); mBufferSizeLock.notify(); void writeTraceToFile() throws InterruptedException { synchronized (mBufferLock) { mCancel = true; mBufferLock.notify(); } if (mConsumerThread != null) { mConsumerThread.join(); mConsumerThread = null; } restartWriterThread(); } } services/core/java/com/android/server/wm/WindowTraceRingBuffer.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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 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()) { byte[] proto; proto = mBuffer.poll(); mBufferSize -= proto.length; os.write(proto); } } } } private void discardOldest() { byte[] item = mBuffer.poll(); if (item == null) { throw new IllegalStateException("No element to discard from buffer"); } mBufferSize -= item.length; } } Loading
services/core/java/com/android/server/wm/WindowManagerService.java +6 −0 Original line number Diff line number Diff line Loading @@ -6056,6 +6056,7 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(" d[isplays]: active display contents"); pw.println(" t[okens]: token list"); pw.println(" w[indows]: window list"); pw.println(" trace: write Winscope trace to file"); pw.println(" cmd may also be a NAME to dump windows. NAME may"); pw.println(" be a partial substring in a window name, a"); pw.println(" Window hex object identifier, or"); Loading Loading @@ -6129,6 +6130,11 @@ public class WindowManagerService extends IWindowManager.Stub mRoot.forAllWindows(w -> {pw.println(w);}, true /* traverseTopToBottom */); } return; } else if ("trace".equals(cmd)) { synchronized (mGlobalLock) { mWindowTracing.writeTraceToFile(); } return; } else { // Dumping a single name? if (!dumpWindows(pw, cmd, args, opti, dumpAll)) { Loading
services/core/java/com/android/server/wm/WindowManagerShellCommand.java +1 −2 Original line number Diff line number Diff line Loading @@ -72,8 +72,7 @@ public class WindowManagerShellCommand extends ShellCommand { // XXX this should probably be changed to use openFileForSystem() to create // the output trace file, so the shell gets the correct semantics for where // trace files can be written. return mInternal.mWindowTracing.onShellCommand(this, getNextArgRequired()); return mInternal.mWindowTracing.onShellCommand(this); case "set-user-rotation": return runSetDisplayUserRotation(pw); case "set-fix-to-user-rotation": Loading
services/core/java/com/android/server/wm/WindowTraceBuffer.java +40 −48 Original line number Diff line number Diff line Loading @@ -23,12 +23,15 @@ 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; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.ArrayDeque; import java.util.Arrays; import java.util.Queue; /** * Buffer used for window tracing. Loading @@ -36,16 +39,15 @@ import java.util.concurrent.LinkedBlockingQueue; abstract class WindowTraceBuffer { private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; final Object mBufferSizeLock = new Object(); final BlockingQueue<byte[]> mBuffer; final Object mBufferLock = new Object(); final Queue<byte[]> mBuffer = new ArrayDeque<>(); final File mTraceFile; int mBufferSize; private final int mBufferCapacity; private final File mTraceFile; WindowTraceBuffer(int size, File traceFile) throws IOException { mBufferCapacity = size; mTraceFile = traceFile; mBuffer = new LinkedBlockingQueue<>(); initTraceFile(); } Loading @@ -57,65 +59,45 @@ abstract class WindowTraceBuffer { /** * Inserts the specified element into this buffer. * * This method is synchronized with {@code #take()} and {@code #clear()} * for consistency. * * @param proto the element to add * @return {@code true} if the inserted item was inserted into the buffer * @throws IllegalStateException if the element cannot be added because it is larger * than the buffer size. */ boolean add(ProtoOutputStream proto) throws InterruptedException { void add(ProtoOutputStream proto) { byte[] protoBytes = proto.getBytes(); int protoLength = protoBytes.length; if (protoLength > mBufferCapacity) { throw new IllegalStateException("Trace object too large for the buffer. Buffer size:" + mBufferCapacity + " Object size: " + protoLength); } synchronized (mBufferSizeLock) { boolean canAdd = canAdd(protoBytes); synchronized (mBufferLock) { boolean canAdd = canAdd(protoLength); if (canAdd) { mBuffer.offer(protoBytes); mBufferSize += protoLength; } return canAdd; } mBufferLock.notify(); } void writeNextBufferElementToFile() throws IOException { byte[] proto; try { proto = take(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } /** * 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, "writeToFile"); try (OutputStream os = new FileOutputStream(mTraceFile, true)) { os.write(proto); } Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile"); writeTraceToFile(); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } /** * Retrieves and removes the head of this queue, waiting if necessary * until an element becomes available. * * This method is synchronized with {@code #add(ProtoOutputStream)} and {@code #clear()} * for consistency. * * @return the head of this buffer, or {@code null} if this buffer is empty */ private byte[] take() throws InterruptedException { byte[] item = mBuffer.take(); synchronized (mBufferSizeLock) { mBufferSize -= item.length; return item; } @VisibleForTesting boolean contains(byte[] other) { return mBuffer.stream() .anyMatch(p -> Arrays.equals(p, other)); } private void initTraceFile() throws IOException { Loading @@ -132,25 +114,31 @@ abstract class WindowTraceBuffer { * Checks if the element can be added to the buffer. The element is already certain to be * smaller than the overall buffer size. * * @param protoBytes byte array representation of the Proto object to add * @return <tt>true<</tt> if the element can be added to the buffer or not * @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(byte[] protoBytes) throws InterruptedException; 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 writeToDisk() throws IOException, InterruptedException; abstract void writeTraceToFile() throws IOException, InterruptedException; /** * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceQueueBuffer} * 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; } Builder setTraceFile(File traceFile) { mTraceFile = traceFile; Loading @@ -175,7 +163,11 @@ abstract class WindowTraceBuffer { throw new IllegalArgumentException("A valid trace file must be specified."); } if (mContinuous) { return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile); } else { return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile); } } } }
services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java +43 −25 Original line number Diff line number Diff line Loading @@ -18,10 +18,14 @@ package com.android.server.wm; import static android.os.Build.IS_USER; import android.util.Log; 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 Loading @@ -29,14 +33,17 @@ import java.io.IOException; * Once the buffer is full it will no longer accepts new elements. */ class WindowTraceQueueBuffer extends WindowTraceBuffer { private Thread mWriterThread; private static final String TAG = "WindowTracing"; private Thread mConsumerThread; private boolean mCancel; @VisibleForTesting WindowTraceQueueBuffer(int size, File traceFile, boolean startWriterThread) throws IOException { WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread) throws IOException { super(size, traceFile); if (startWriterThread) { initializeWriterThread(); if (startConsumerThread) { initializeConsumerThread(); } } Loading @@ -44,45 +51,56 @@ class WindowTraceQueueBuffer extends WindowTraceBuffer { this(size, traceFile, !IS_USER); } private void initializeWriterThread() { private void initializeConsumerThread() { mCancel = false; mWriterThread = new Thread(() -> { mConsumerThread = new Thread(() -> { try { loop(); } catch (InterruptedException e) { Log.i(TAG, "Interrupting trace consumer thread"); } catch (IOException e) { throw new IllegalStateException("Failed to execute trace write loop thread", e); Log.e(TAG, "Failed to execute trace consumer thread", e); } }, "window_tracing"); mWriterThread.start(); mConsumerThread.start(); } private void loop() throws IOException { private void loop() throws IOException, InterruptedException { while (!mCancel) { writeNextBufferElementToFile(); byte[] proto; synchronized (mBufferLock) { mBufferLock.wait(); proto = mBuffer.poll(); if (proto != null) { mBufferSize -= proto.length; } } private void restartWriterThread() throws InterruptedException { if (mWriterThread != null) { mCancel = true; mWriterThread.interrupt(); mWriterThread.join(); initializeWriterThread(); if (proto != null) { try (OutputStream os = new FileOutputStream(mTraceFile, true)) { os.write(proto); } } } } @Override boolean canAdd(byte[] protoBytes) { boolean canAdd(int protoLength) { long availableSpace = getAvailableSpace(); return availableSpace >= protoBytes.length; return availableSpace >= protoLength; } @Override void writeToDisk() throws InterruptedException { while (!mBuffer.isEmpty()) { mBufferSizeLock.wait(); mBufferSizeLock.notify(); void writeTraceToFile() throws InterruptedException { synchronized (mBufferLock) { mCancel = true; mBufferLock.notify(); } if (mConsumerThread != null) { mConsumerThread.join(); mConsumerThread = null; } restartWriterThread(); } }
services/core/java/com/android/server/wm/WindowTraceRingBuffer.java 0 → 100644 +68 −0 Original line number Diff line number Diff line /* * 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 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()) { byte[] proto; proto = mBuffer.poll(); mBufferSize -= proto.length; os.write(proto); } } } } private void discardOldest() { byte[] item = mBuffer.poll(); if (item == null) { throw new IllegalStateException("No element to discard from buffer"); } mBufferSize -= item.length; } }