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