blob: 2f672f24cc4f5f9e19acacc3339bb1236bc012fe [file] [log] [blame]
Nataniel Borges5a38bad2019-01-03 10:32:03 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
19import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
20import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
21import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
22
23import android.os.Trace;
24import android.util.proto.ProtoOutputStream;
25
Nataniel Borges98d92aa2019-01-03 14:22:44 -080026import com.android.internal.annotations.VisibleForTesting;
27
Nataniel Borges5a38bad2019-01-03 10:32:03 -080028import java.io.File;
29import java.io.FileOutputStream;
30import java.io.IOException;
31import java.io.OutputStream;
Nataniel Borges98d92aa2019-01-03 14:22:44 -080032import java.util.ArrayDeque;
33import java.util.Arrays;
34import java.util.Queue;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080035
36/**
37 * Buffer used for window tracing.
38 */
39abstract class WindowTraceBuffer {
40 private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
41
Nataniel Borges98d92aa2019-01-03 14:22:44 -080042 final Object mBufferLock = new Object();
43 final Queue<byte[]> mBuffer = new ArrayDeque<>();
44 final File mTraceFile;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080045 int mBufferSize;
46 private final int mBufferCapacity;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080047
48 WindowTraceBuffer(int size, File traceFile) throws IOException {
49 mBufferCapacity = size;
50 mTraceFile = traceFile;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080051
52 initTraceFile();
53 }
54
55 int getAvailableSpace() {
56 return mBufferCapacity - mBufferSize;
57 }
58
59 /**
60 * Inserts the specified element into this buffer.
61 *
Nataniel Borges5a38bad2019-01-03 10:32:03 -080062 * @param proto the element to add
Nataniel Borges5a38bad2019-01-03 10:32:03 -080063 * @throws IllegalStateException if the element cannot be added because it is larger
64 * than the buffer size.
65 */
Nataniel Borges98d92aa2019-01-03 14:22:44 -080066 void add(ProtoOutputStream proto) {
Nataniel Borges5a38bad2019-01-03 10:32:03 -080067 byte[] protoBytes = proto.getBytes();
68 int protoLength = protoBytes.length;
69 if (protoLength > mBufferCapacity) {
70 throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
71 + mBufferCapacity + " Object size: " + protoLength);
72 }
Nataniel Borges98d92aa2019-01-03 14:22:44 -080073 synchronized (mBufferLock) {
74 boolean canAdd = canAdd(protoLength);
Nataniel Borges5a38bad2019-01-03 10:32:03 -080075 if (canAdd) {
76 mBuffer.offer(protoBytes);
77 mBufferSize += protoLength;
78 }
Nataniel Borges98d92aa2019-01-03 14:22:44 -080079 mBufferLock.notify();
Nataniel Borges5a38bad2019-01-03 10:32:03 -080080 }
81 }
82
Nataniel Borges98d92aa2019-01-03 14:22:44 -080083 /**
84 * Stops the buffer execution and flush all buffer content to the disk.
85 *
86 * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
87 */
88 void dump() throws IOException, InterruptedException {
Nataniel Borges5a38bad2019-01-03 10:32:03 -080089 try {
Nataniel Borges98d92aa2019-01-03 14:22:44 -080090 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile");
91 writeTraceToFile();
Nataniel Borges5a38bad2019-01-03 10:32:03 -080092 } finally {
93 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
94 }
95 }
96
Nataniel Borges98d92aa2019-01-03 14:22:44 -080097 @VisibleForTesting
98 boolean contains(byte[] other) {
99 return mBuffer.stream()
100 .anyMatch(p -> Arrays.equals(p, other));
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800101 }
102
103 private void initTraceFile() throws IOException {
104 mTraceFile.delete();
105 try (OutputStream os = new FileOutputStream(mTraceFile)) {
106 mTraceFile.setReadable(true, false);
107 ProtoOutputStream proto = new ProtoOutputStream(os);
108 proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
109 proto.flush();
110 }
111 }
112
113 /**
114 * Checks if the element can be added to the buffer. The element is already certain to be
115 * smaller than the overall buffer size.
116 *
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800117 * @param protoLength byte array representation of the Proto object to add
118 * @return {@code true} if the element can be added to the buffer or not
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800119 */
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800120 abstract boolean canAdd(int protoLength);
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800121
122 /**
123 * Flush all buffer content to the disk.
124 *
125 * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
126 */
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800127 abstract void writeTraceToFile() throws IOException, InterruptedException;
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800128
129 /**
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800130 * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceRingBuffer} for
131 * continuous mode or a {@link WindowTraceQueueBuffer} otherwise
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800132 */
133 static class Builder {
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800134 private boolean mContinuous;
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800135 private File mTraceFile;
136 private int mBufferCapacity;
137
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800138 Builder setContinuousMode(boolean continuous) {
139 mContinuous = continuous;
140 return this;
141 }
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800142
143 Builder setTraceFile(File traceFile) {
144 mTraceFile = traceFile;
145 return this;
146 }
147
148 Builder setBufferCapacity(int size) {
149 mBufferCapacity = size;
150 return this;
151 }
152
153 File getFile() {
154 return mTraceFile;
155 }
156
157 WindowTraceBuffer build() throws IOException {
158 if (mBufferCapacity <= 0) {
159 throw new IllegalStateException("Buffer capacity must be greater than 0.");
160 }
161
162 if (mTraceFile == null) {
163 throw new IllegalArgumentException("A valid trace file must be specified.");
164 }
165
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800166 if (mContinuous) {
167 return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile);
168 } else {
169 return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile);
170 }
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800171 }
172 }
173}