1 /* 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 17 package com.android.server.wm; 18 19 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER; 20 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H; 21 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L; 22 23 import android.util.proto.ProtoOutputStream; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 27 import java.io.File; 28 import java.io.FileOutputStream; 29 import java.io.IOException; 30 import java.io.OutputStream; 31 import java.util.ArrayDeque; 32 import java.util.Arrays; 33 import java.util.Queue; 34 35 /** 36 * Buffer used for window tracing. 37 */ 38 class WindowTraceBuffer { 39 private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; 40 41 private final Object mBufferLock = new Object(); 42 43 private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>(); 44 private int mBufferUsedSize; 45 private int mBufferCapacity; 46 WindowTraceBuffer(int bufferCapacity)47 WindowTraceBuffer(int bufferCapacity) { 48 mBufferCapacity = bufferCapacity; 49 resetBuffer(); 50 } 51 getAvailableSpace()52 int getAvailableSpace() { 53 return mBufferCapacity - mBufferUsedSize; 54 } 55 size()56 int size() { 57 return mBuffer.size(); 58 } 59 setCapacity(int capacity)60 void setCapacity(int capacity) { 61 mBufferCapacity = capacity; 62 } 63 64 /** 65 * Inserts the specified element into this buffer. 66 * 67 * @param proto the element to add 68 * @throws IllegalStateException if the element cannot be added because it is larger 69 * than the buffer size. 70 */ add(ProtoOutputStream proto)71 void add(ProtoOutputStream proto) { 72 int protoLength = proto.getRawSize(); 73 if (protoLength > mBufferCapacity) { 74 throw new IllegalStateException("Trace object too large for the buffer. Buffer size:" 75 + mBufferCapacity + " Object size: " + protoLength); 76 } 77 synchronized (mBufferLock) { 78 discardOldest(protoLength); 79 mBuffer.add(proto); 80 mBufferUsedSize += protoLength; 81 mBufferLock.notify(); 82 } 83 } 84 contains(byte[] other)85 boolean contains(byte[] other) { 86 return mBuffer.stream() 87 .anyMatch(p -> Arrays.equals(p.getBytes(), other)); 88 } 89 90 /** 91 * Writes the trace buffer to disk. 92 */ writeTraceToFile(File traceFile)93 void writeTraceToFile(File traceFile) throws IOException { 94 synchronized (mBufferLock) { 95 traceFile.delete(); 96 try (OutputStream os = new FileOutputStream(traceFile)) { 97 traceFile.setReadable(true /* readable */, false /* ownerOnly */); 98 ProtoOutputStream proto = new ProtoOutputStream(); 99 proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); 100 os.write(proto.getBytes()); 101 for (ProtoOutputStream protoOutputStream : mBuffer) { 102 proto = protoOutputStream; 103 byte[] protoBytes = proto.getBytes(); 104 os.write(protoBytes); 105 } 106 os.flush(); 107 } 108 } 109 } 110 111 /** 112 * Checks if the element can be added to the buffer. The element is already certain to be 113 * smaller than the overall buffer size. 114 * 115 * @param protoLength byte array representation of the Proto object to add 116 */ discardOldest(int protoLength)117 private void discardOldest(int protoLength) { 118 long availableSpace = getAvailableSpace(); 119 120 while (availableSpace < protoLength) { 121 122 ProtoOutputStream item = mBuffer.poll(); 123 if (item == null) { 124 throw new IllegalStateException("No element to discard from buffer"); 125 } 126 mBufferUsedSize -= item.getRawSize(); 127 availableSpace = getAvailableSpace(); 128 } 129 } 130 131 /** 132 * Removes all elements form the buffer 133 */ resetBuffer()134 void resetBuffer() { 135 synchronized (mBufferLock) { 136 mBuffer.clear(); 137 mBufferUsedSize = 0; 138 } 139 } 140 141 @VisibleForTesting getBufferSize()142 int getBufferSize() { 143 return mBufferUsedSize; 144 } 145 getStatus()146 String getStatus() { 147 synchronized (mBufferLock) { 148 return "Buffer size: " 149 + mBufferCapacity 150 + " bytes" 151 + "\n" 152 + "Buffer usage: " 153 + mBufferUsedSize 154 + " bytes" 155 + "\n" 156 + "Elements in the buffer: " 157 + mBuffer.size(); 158 } 159 } 160 } 161