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.systemui.shared.tracing; 18 19 import android.os.Trace; 20 import android.util.Log; 21 import android.view.Choreographer; 22 23 import com.android.internal.util.TraceBuffer; 24 25 import java.io.File; 26 import java.io.IOException; 27 import java.io.OutputStream; 28 import java.util.ArrayList; 29 import java.util.LinkedList; 30 import java.util.Queue; 31 import java.util.function.Consumer; 32 33 /** 34 * A proto tracer implementation that can be updated directly (upon state change), or on the next 35 * scheduled frame. 36 * 37 * @param <P> The class type of the proto provider 38 * @param <S> The proto class type of the encapsulating proto 39 * @param <T> The proto class type of the individual proto entries in the buffer 40 * @param <R> The proto class type of the entry root proto in the buffer 41 */ 42 public class FrameProtoTracer<P, S extends P, T extends P, R> 43 implements Choreographer.FrameCallback { 44 45 private static final String TAG = "FrameProtoTracer"; 46 private static final int BUFFER_CAPACITY = 1024 * 1024; 47 48 private final Object mLock = new Object(); 49 private final TraceBuffer<P, S, T> mBuffer; 50 private final File mTraceFile; 51 private final ProtoTraceParams<P, S, T, R> mParams; 52 private Choreographer mChoreographer; 53 private final Queue<T> mPool = new LinkedList<>(); 54 private final ArrayList<ProtoTraceable<R>> mTraceables = new ArrayList<>(); 55 private final ArrayList<ProtoTraceable<R>> mTmpTraceables = new ArrayList<>(); 56 57 private volatile boolean mEnabled; 58 private boolean mFrameScheduled; 59 60 private final TraceBuffer.ProtoProvider<P, S, T> mProvider = 61 new TraceBuffer.ProtoProvider<P, S, T>() { 62 @Override 63 public int getItemSize(P proto) { 64 return mParams.getProtoSize(proto); 65 } 66 67 @Override 68 public byte[] getBytes(P proto) { 69 return mParams.getProtoBytes(proto); 70 } 71 72 @Override 73 public void write(S encapsulatingProto, Queue<T> buffer, OutputStream os) 74 throws IOException { 75 os.write(mParams.serializeEncapsulatingProto(encapsulatingProto, buffer)); 76 } 77 }; 78 79 public interface ProtoTraceParams<P, S, T, R> { getTraceFile()80 File getTraceFile(); getEncapsulatingTraceProto()81 S getEncapsulatingTraceProto(); updateBufferProto(T reuseObj, ArrayList<ProtoTraceable<R>> traceables)82 T updateBufferProto(T reuseObj, ArrayList<ProtoTraceable<R>> traceables); serializeEncapsulatingProto(S encapsulatingProto, Queue<T> buffer)83 byte[] serializeEncapsulatingProto(S encapsulatingProto, Queue<T> buffer); getProtoBytes(P proto)84 byte[] getProtoBytes(P proto); getProtoSize(P proto)85 int getProtoSize(P proto); 86 } 87 FrameProtoTracer(ProtoTraceParams<P, S, T, R> params)88 public FrameProtoTracer(ProtoTraceParams<P, S, T, R> params) { 89 mParams = params; 90 mBuffer = new TraceBuffer<>(BUFFER_CAPACITY, mProvider, new Consumer<T>() { 91 @Override 92 public void accept(T t) { 93 onProtoDequeued(t); 94 } 95 }); 96 mTraceFile = params.getTraceFile(); 97 } 98 start()99 public void start() { 100 synchronized (mLock) { 101 if (mEnabled) { 102 return; 103 } 104 mBuffer.resetBuffer(); 105 mEnabled = true; 106 } 107 logState(); 108 } 109 stop()110 public void stop() { 111 synchronized (mLock) { 112 if (!mEnabled) { 113 return; 114 } 115 mEnabled = false; 116 } 117 writeToFile(); 118 } 119 isEnabled()120 public boolean isEnabled() { 121 return mEnabled; 122 } 123 add(ProtoTraceable<R> traceable)124 public void add(ProtoTraceable<R> traceable) { 125 synchronized (mLock) { 126 mTraceables.add(traceable); 127 } 128 } 129 remove(ProtoTraceable<R> traceable)130 public void remove(ProtoTraceable<R> traceable) { 131 synchronized (mLock) { 132 mTraceables.remove(traceable); 133 } 134 } 135 scheduleFrameUpdate()136 public void scheduleFrameUpdate() { 137 if (!mEnabled || mFrameScheduled) { 138 return; 139 } 140 141 // Schedule an update on the next frame 142 if (mChoreographer == null) { 143 mChoreographer = Choreographer.getMainThreadInstance(); 144 } 145 mChoreographer.postFrameCallback(this); 146 mFrameScheduled = true; 147 } 148 update()149 public void update() { 150 if (!mEnabled) { 151 return; 152 } 153 154 logState(); 155 } 156 getBufferUsagePct()157 public float getBufferUsagePct() { 158 return (float) mBuffer.getBufferSize() / BUFFER_CAPACITY; 159 } 160 161 @Override doFrame(long frameTimeNanos)162 public void doFrame(long frameTimeNanos) { 163 logState(); 164 } 165 onProtoDequeued(T proto)166 private void onProtoDequeued(T proto) { 167 mPool.add(proto); 168 } 169 logState()170 private void logState() { 171 synchronized (mLock) { 172 mTmpTraceables.addAll(mTraceables); 173 } 174 175 mBuffer.add(mParams.updateBufferProto(mPool.poll(), mTmpTraceables)); 176 mTmpTraceables.clear(); 177 mFrameScheduled = false; 178 } 179 writeToFile()180 private void writeToFile() { 181 try { 182 Trace.beginSection("ProtoTracer.writeToFile"); 183 mBuffer.writeTraceToFile(mTraceFile, mParams.getEncapsulatingTraceProto()); 184 } catch (IOException e) { 185 Log.e(TAG, "Unable to write buffer to file", e); 186 } finally { 187 Trace.endSection(); 188 } 189 } 190 } 191 192 193