blob: 0567960e05e4e76479913ab66917f984e3e16b59 [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
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020017package com.android.server.utils;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080018
Nataniel Borges5a38bad2019-01-03 10:32:03 -080019import android.util.proto.ProtoOutputStream;
20
Nataniel Borges98d92aa2019-01-03 14:22:44 -080021import com.android.internal.annotations.VisibleForTesting;
22
Nataniel Borges5a38bad2019-01-03 10:32:03 -080023import java.io.File;
24import java.io.FileOutputStream;
25import java.io.IOException;
26import java.io.OutputStream;
Nataniel Borges98d92aa2019-01-03 14:22:44 -080027import java.util.ArrayDeque;
28import java.util.Arrays;
29import java.util.Queue;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080030
31/**
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020032 * Buffer used for tracing and logging.
Nataniel Borges5a38bad2019-01-03 10:32:03 -080033 */
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020034public class TraceBuffer {
Nataniel Borges7d37ce22019-02-05 10:07:02 -080035 private final Object mBufferLock = new Object();
Nataniel Borges5a38bad2019-01-03 10:32:03 -080036
Nataniel Borges7d37ce22019-02-05 10:07:02 -080037 private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
38 private int mBufferUsedSize;
39 private int mBufferCapacity;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080040
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020041 public TraceBuffer(int bufferCapacity) {
Nataniel Borges7d37ce22019-02-05 10:07:02 -080042 mBufferCapacity = bufferCapacity;
43 resetBuffer();
Nataniel Borges5a38bad2019-01-03 10:32:03 -080044 }
45
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020046 public int getAvailableSpace() {
Nataniel Borges7d37ce22019-02-05 10:07:02 -080047 return mBufferCapacity - mBufferUsedSize;
48 }
49
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020050 /**
51 * Returns buffer size.
52 */
53 public int size() {
Nataniel Borges7d37ce22019-02-05 10:07:02 -080054 return mBuffer.size();
55 }
56
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020057 public void setCapacity(int capacity) {
Nataniel Borges7d37ce22019-02-05 10:07:02 -080058 mBufferCapacity = capacity;
Nataniel Borges5a38bad2019-01-03 10:32:03 -080059 }
60
61 /**
62 * Inserts the specified element into this buffer.
63 *
Nataniel Borges5a38bad2019-01-03 10:32:03 -080064 * @param proto the element to add
Nataniel Borges5a38bad2019-01-03 10:32:03 -080065 * @throws IllegalStateException if the element cannot be added because it is larger
66 * than the buffer size.
67 */
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020068 public void add(ProtoOutputStream proto) {
Nataniel Borges023ecb52019-01-16 14:15:43 -080069 int protoLength = proto.getRawSize();
Nataniel Borges5a38bad2019-01-03 10:32:03 -080070 if (protoLength > mBufferCapacity) {
71 throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
72 + mBufferCapacity + " Object size: " + protoLength);
73 }
Nataniel Borges98d92aa2019-01-03 14:22:44 -080074 synchronized (mBufferLock) {
Nataniel Borges7d37ce22019-02-05 10:07:02 -080075 discardOldest(protoLength);
76 mBuffer.add(proto);
77 mBufferUsedSize += protoLength;
Nataniel Borges98d92aa2019-01-03 14:22:44 -080078 mBufferLock.notify();
Nataniel Borges5a38bad2019-01-03 10:32:03 -080079 }
80 }
81
Nataniel Borges98d92aa2019-01-03 14:22:44 -080082 boolean contains(byte[] other) {
83 return mBuffer.stream()
Nataniel Borges023ecb52019-01-16 14:15:43 -080084 .anyMatch(p -> Arrays.equals(p.getBytes(), other));
Nataniel Borges5a38bad2019-01-03 10:32:03 -080085 }
86
Nataniel Borges7d37ce22019-02-05 10:07:02 -080087 /**
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020088 * Writes the trace buffer to disk inside the encapsulatingProto..
Nataniel Borges7d37ce22019-02-05 10:07:02 -080089 */
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020090 public void writeTraceToFile(File traceFile, ProtoOutputStream encapsulatingProto)
91 throws IOException {
Nataniel Borges7d37ce22019-02-05 10:07:02 -080092 synchronized (mBufferLock) {
93 traceFile.delete();
Nataniel Borges7d37ce22019-02-05 10:07:02 -080094 try (OutputStream os = new FileOutputStream(traceFile)) {
Nataniel Borges8c074c62019-03-12 15:16:46 -070095 traceFile.setReadable(true /* readable */, false /* ownerOnly */);
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020096 os.write(encapsulatingProto.getBytes());
Nataniel Borgesb24762202019-02-22 14:39:18 -080097 for (ProtoOutputStream protoOutputStream : mBuffer) {
Adam Pardyl0f1b3d42019-08-19 15:24:11 +020098 encapsulatingProto = protoOutputStream;
99 byte[] protoBytes = encapsulatingProto.getBytes();
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800100 os.write(protoBytes);
101 }
102 os.flush();
103 }
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800104 }
105 }
106
107 /**
108 * Checks if the element can be added to the buffer. The element is already certain to be
109 * smaller than the overall buffer size.
110 *
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800111 * @param protoLength byte array representation of the Proto object to add
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800112 */
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800113 private void discardOldest(int protoLength) {
114 long availableSpace = getAvailableSpace();
115
116 while (availableSpace < protoLength) {
117
118 ProtoOutputStream item = mBuffer.poll();
119 if (item == null) {
120 throw new IllegalStateException("No element to discard from buffer");
121 }
122 mBufferUsedSize -= item.getRawSize();
123 availableSpace = getAvailableSpace();
124 }
125 }
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800126
127 /**
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800128 * Removes all elements form the buffer
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800129 */
Adam Pardyl0f1b3d42019-08-19 15:24:11 +0200130 public void resetBuffer() {
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800131 synchronized (mBufferLock) {
132 mBuffer.clear();
133 mBufferUsedSize = 0;
Nataniel Borges98d92aa2019-01-03 14:22:44 -0800134 }
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800135 }
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800136
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800137 @VisibleForTesting
138 int getBufferSize() {
139 return mBufferUsedSize;
140 }
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800141
Adam Pardyl0f1b3d42019-08-19 15:24:11 +0200142 /**
143 * Returns the buffer status in human-readable form.
144 */
145 public String getStatus() {
Nataniel Borges7d37ce22019-02-05 10:07:02 -0800146 synchronized (mBufferLock) {
147 return "Buffer size: "
148 + mBufferCapacity
149 + " bytes"
150 + "\n"
151 + "Buffer usage: "
152 + mBufferUsedSize
153 + " bytes"
154 + "\n"
155 + "Elements in the buffer: "
156 + mBuffer.size();
Nataniel Borges5a38bad2019-01-03 10:32:03 -0800157 }
158 }
159}