Merge "Isolate WMTrace buffer and limit it by size not number of elements"
This commit is contained in:
committed by
Android (Google) Code Review
commit
994bf9f577
181
services/core/java/com/android/server/wm/WindowTraceBuffer.java
Normal file
181
services/core/java/com/android/server/wm/WindowTraceBuffer.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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 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 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;
|
||||
|
||||
/**
|
||||
* Buffer used for window tracing.
|
||||
*/
|
||||
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;
|
||||
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();
|
||||
}
|
||||
|
||||
int getAvailableSpace() {
|
||||
return mBufferCapacity - mBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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);
|
||||
if (canAdd) {
|
||||
mBuffer.offer(protoBytes);
|
||||
mBufferSize += protoLength;
|
||||
}
|
||||
return canAdd;
|
||||
}
|
||||
}
|
||||
|
||||
void writeNextBufferElementToFile() throws IOException {
|
||||
byte[] proto;
|
||||
try {
|
||||
proto = take();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToFile");
|
||||
try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
|
||||
os.write(proto);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
abstract boolean canAdd(byte[] protoBytes) throws InterruptedException;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceQueueBuffer}
|
||||
*/
|
||||
static class Builder {
|
||||
private File mTraceFile;
|
||||
private int mBufferCapacity;
|
||||
|
||||
|
||||
Builder setTraceFile(File traceFile) {
|
||||
mTraceFile = traceFile;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setBufferCapacity(int size) {
|
||||
mBufferCapacity = size;
|
||||
return this;
|
||||
}
|
||||
|
||||
File getFile() {
|
||||
return mTraceFile;
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 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 Thread mWriterThread;
|
||||
private boolean mCancel;
|
||||
|
||||
@VisibleForTesting
|
||||
WindowTraceQueueBuffer(int size, File traceFile, boolean startWriterThread) throws IOException {
|
||||
super(size, traceFile);
|
||||
if (startWriterThread) {
|
||||
initializeWriterThread();
|
||||
}
|
||||
}
|
||||
|
||||
WindowTraceQueueBuffer(int size, File traceFile) throws IOException {
|
||||
this(size, traceFile, !IS_USER);
|
||||
}
|
||||
|
||||
private void initializeWriterThread() {
|
||||
mCancel = false;
|
||||
mWriterThread = new Thread(() -> {
|
||||
try {
|
||||
loop();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to execute trace write loop thread", e);
|
||||
}
|
||||
}, "window_tracing");
|
||||
mWriterThread.start();
|
||||
}
|
||||
|
||||
private void loop() throws IOException {
|
||||
while (!mCancel) {
|
||||
writeNextBufferElementToFile();
|
||||
}
|
||||
}
|
||||
|
||||
private void restartWriterThread() throws InterruptedException {
|
||||
if (mWriterThread != null) {
|
||||
mCancel = true;
|
||||
mWriterThread.interrupt();
|
||||
mWriterThread.join();
|
||||
initializeWriterThread();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canAdd(byte[] protoBytes) {
|
||||
long availableSpace = getAvailableSpace();
|
||||
return availableSpace >= protoBytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeToDisk() throws InterruptedException {
|
||||
while (!mBuffer.isEmpty()) {
|
||||
mBufferSizeLock.wait();
|
||||
mBufferSizeLock.notify();
|
||||
}
|
||||
restartWriterThread();
|
||||
}
|
||||
}
|
||||
@@ -17,31 +17,23 @@
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.os.Build.IS_USER;
|
||||
|
||||
import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY;
|
||||
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 static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
|
||||
import static com.android.server.wm.WindowManagerTraceProto.WHERE;
|
||||
import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.ShellCommand;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.annotation.Nullable;
|
||||
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;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* A class that allows window manager to dump its state continuously to a trace file, such that a
|
||||
@@ -49,35 +41,42 @@ import java.util.concurrent.BlockingQueue;
|
||||
*/
|
||||
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 String TAG = "WindowTracing";
|
||||
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final File mTraceFile;
|
||||
private final BlockingQueue<ProtoOutputStream> mWriteQueue = new ArrayBlockingQueue<>(200);
|
||||
private final WindowTraceBuffer.Builder mBufferBuilder;
|
||||
|
||||
private WindowTraceBuffer mTraceBuffer;
|
||||
|
||||
private boolean mEnabled;
|
||||
private volatile boolean mEnabledLockFree;
|
||||
|
||||
WindowTracing(File file) {
|
||||
mTraceFile = file;
|
||||
mBufferBuilder = new WindowTraceBuffer.Builder()
|
||||
.setTraceFile(file)
|
||||
.setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
void startTrace(@Nullable PrintWriter pw) throws IOException {
|
||||
if (IS_USER){
|
||||
if (IS_USER) {
|
||||
logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
|
||||
return;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
|
||||
mWriteQueue.clear();
|
||||
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();
|
||||
logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + ".");
|
||||
if (mTraceBuffer != null) {
|
||||
try {
|
||||
mTraceBuffer.writeToDisk();
|
||||
} catch (InterruptedException e) {
|
||||
logAndPrintln(pw, "Error: Unable to flush the previous buffer.");
|
||||
}
|
||||
}
|
||||
mTraceBuffer = mBufferBuilder.build();
|
||||
mEnabled = mEnabledLockFree = true;
|
||||
}
|
||||
}
|
||||
@@ -91,67 +90,42 @@ class WindowTracing {
|
||||
}
|
||||
|
||||
void stopTrace(@Nullable PrintWriter pw) {
|
||||
if (IS_USER){
|
||||
if (IS_USER) {
|
||||
logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
|
||||
return;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
|
||||
logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile()
|
||||
+ ". Waiting for traces to flush.");
|
||||
mEnabled = mEnabledLockFree = false;
|
||||
while (!mWriteQueue.isEmpty()) {
|
||||
|
||||
synchronized (mLock) {
|
||||
if (mEnabled) {
|
||||
logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
|
||||
throw new IllegalStateException("tracing enabled while waiting for flush.");
|
||||
}
|
||||
try {
|
||||
mLock.wait();
|
||||
mLock.notify();
|
||||
mTraceBuffer.writeToDisk();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to write buffer to file", e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
Log.e(TAG, "Unable to interrupt window tracing file write thread", e);
|
||||
}
|
||||
}
|
||||
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
|
||||
logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + ".");
|
||||
}
|
||||
}
|
||||
|
||||
void appendTraceEntry(ProtoOutputStream proto) {
|
||||
private void appendTraceEntry(ProtoOutputStream proto) {
|
||||
if (!mEnabledLockFree) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mWriteQueue.offer(proto)) {
|
||||
Log.e(TAG, "Dropping window trace entry, queue full");
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
for (;;) {
|
||||
loopOnce();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void loopOnce() {
|
||||
ProtoOutputStream proto;
|
||||
try {
|
||||
proto = mWriteQueue.take();
|
||||
mTraceBuffer.add(proto);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Unable to add element to trace", e);
|
||||
Thread.currentThread().interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToFile");
|
||||
try (OutputStream os = new FileOutputStream(mTraceFile, true /* append */)) {
|
||||
os.write(proto.getBytes());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to write file " + mTraceFile, e);
|
||||
} finally {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
|
||||
}
|
||||
mLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,11 +135,7 @@ class WindowTracing {
|
||||
|
||||
static WindowTracing createDefaultAndStartLooper(Context context) {
|
||||
File file = new File("/data/misc/wmtrace/wm_trace.pb");
|
||||
WindowTracing windowTracing = new WindowTracing(file);
|
||||
if (!IS_USER){
|
||||
new Thread(windowTracing::loop, "window_tracing").start();
|
||||
}
|
||||
return windowTracing;
|
||||
return new WindowTracing(file);
|
||||
}
|
||||
|
||||
int onShellCommand(ShellCommand shell, String cmd) {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WmTests:WindowTraceBufferTest
|
||||
*/
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
public class WindowTraceBufferTest {
|
||||
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
|
||||
|
||||
private File mFile;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final Context testContext = getInstrumentation().getContext();
|
||||
mFile = testContext.getFileStreamPath("tracing_test.dat");
|
||||
mFile.delete();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mFile.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTraceQueueBuffer_addItem() throws Exception {
|
||||
ProtoOutputStream toWrite1 = getDummy(1);
|
||||
ProtoOutputStream toWrite2 = getDummy(2);
|
||||
ProtoOutputStream toWrite3 = getDummy(3);
|
||||
byte[] toWrite1Bytes = toWrite1.getBytes();
|
||||
byte[] toWrite2Bytes = toWrite2.getBytes();
|
||||
byte[] toWrite3Bytes = toWrite3.getBytes();
|
||||
|
||||
final int objectSize = toWrite1.getBytes().length;
|
||||
final int bufferCapacity = objectSize * 2;
|
||||
|
||||
final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity);
|
||||
|
||||
buffer.add(toWrite1);
|
||||
assertTrue("First element should be in the list",
|
||||
buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite1Bytes)));
|
||||
|
||||
buffer.add(toWrite2);
|
||||
assertTrue("First element should be in the list",
|
||||
buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite1Bytes)));
|
||||
assertTrue("Second element should be in the list",
|
||||
buffer.mBuffer.stream().anyMatch(p -> Arrays.equals(p, toWrite2Bytes)));
|
||||
|
||||
buffer.add(toWrite3);
|
||||
|
||||
assertTrue("Third element should not be in the list",
|
||||
buffer.mBuffer.stream().noneMatch(p -> Arrays.equals(p, 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);
|
||||
}
|
||||
|
||||
private ProtoOutputStream getDummy(int value) {
|
||||
ProtoOutputStream toWrite = new ProtoOutputStream();
|
||||
toWrite.write(MAGIC_NUMBER, value);
|
||||
toWrite.flush();
|
||||
|
||||
return toWrite;
|
||||
}
|
||||
|
||||
private WindowTraceBuffer buildQueueBuffer(int size) throws IOException {
|
||||
return new WindowTraceQueueBuffer(size, mFile, false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user