1 /*
2  * Copyright (C) 2023 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 android.tracing.perfetto;
18 
19 import android.util.proto.ProtoInputStream;
20 
21 /**
22  * Templated base class meant to be derived by embedders to create a custom data
23  * source.
24  *
25  * @param <DataSourceInstanceType> The type for the DataSource instances that will be created from
26  *                                 this DataSource type.
27  * @param <TlsStateType> The type of the custom TLS state, if any is used.
28  * @param <IncrementalStateType> The type of the custom incremental state, if any is used.
29  *
30  * @hide
31  */
32 public abstract class DataSource<DataSourceInstanceType extends DataSourceInstance,
33         TlsStateType, IncrementalStateType> {
34     protected final long mNativeObj;
35 
36     public final String name;
37 
38     /**
39      * A function implemented by each datasource to create a new data source instance.
40      *
41      * @param configStream A ProtoInputStream to read the tracing instance's config.
42      * @return A new data source instance setup with the provided config.
43      */
createInstance( ProtoInputStream configStream, int instanceIndex)44     public abstract DataSourceInstanceType createInstance(
45             ProtoInputStream configStream, int instanceIndex);
46 
47     /**
48      * Constructor for datasource base class.
49      *
50      * @param name The fully qualified name of the datasource.
51      */
DataSource(String name)52     public DataSource(String name) {
53         this.name = name;
54         this.mNativeObj = nativeCreate(this, name);
55     }
56 
57     /**
58      * The main tracing method. Tracing code should call this passing a lambda as
59      * argument, with the following signature: void(TraceContext).
60      * <p>
61      * The lambda will be called synchronously (i.e., always before trace()
62      * returns) only if tracing is enabled and the data source has been enabled in
63      * the tracing config.
64      * <p>
65      * The lambda can be called more than once per trace() call, in the case of
66      * concurrent tracing sessions (or even if the data source is instantiated
67      * twice within the same trace config).
68      *
69      * @param fun The tracing lambda that will be called with the tracing contexts of each active
70      *            tracing instance.
71      */
trace( TraceFunction<DataSourceInstanceType, TlsStateType, IncrementalStateType> fun)72     public final void trace(
73             TraceFunction<DataSourceInstanceType, TlsStateType, IncrementalStateType> fun) {
74         boolean startedIterator = nativePerfettoDsTraceIterateBegin(mNativeObj);
75 
76         if (!startedIterator) {
77             return;
78         }
79 
80         try {
81             do {
82                 int instanceIndex = nativeGetPerfettoDsInstanceIndex(mNativeObj);
83 
84                 TracingContext<DataSourceInstanceType, TlsStateType, IncrementalStateType> ctx =
85                         new TracingContext<>(this, instanceIndex);
86                 fun.trace(ctx);
87 
88                 nativeWritePackets(mNativeObj, ctx.getAndClearAllPendingTracePackets());
89             } while (nativePerfettoDsTraceIterateNext(mNativeObj));
90         } finally {
91             nativePerfettoDsTraceIterateBreak(mNativeObj);
92         }
93     }
94 
95     /**
96      * Flush any trace data from this datasource that has not yet been flushed.
97      */
flush()98     public final void flush() {
99         nativeFlushAll(mNativeObj);
100     }
101 
102     /**
103      * Override this method to create a custom TlsState object for your DataSource. A new instance
104      * will be created per trace instance per thread.
105      *
106      */
createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args)107     public TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) {
108         return null;
109     }
110 
111     /**
112      * Override this method to create and use a custom IncrementalState object for your DataSource.
113      *
114      */
createIncrementalState( CreateIncrementalStateArgs<DataSourceInstanceType> args)115     public IncrementalStateType createIncrementalState(
116             CreateIncrementalStateArgs<DataSourceInstanceType> args) {
117         return null;
118     }
119 
120     /**
121      * Registers the data source on all tracing backends, including ones that
122      * connect after the registration. Doing so enables the data source to receive
123      * Setup/Start/Stop notifications and makes the trace() method work when
124      * tracing is enabled and the data source is selected.
125      * <p>
126      * NOTE: Once registered, we cannot unregister the data source. Therefore, we should avoid
127      * creating and registering data source where not strictly required. This is a fundamental
128      * limitation of Perfetto itself.
129      *
130      * @param params Params to initialize the datasource with.
131      */
register(DataSourceParams params)132     public void register(DataSourceParams params) {
133         nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy,
134                 params.willNotifyOnStop, params.noFlush);
135     }
136 
137     /**
138      * Gets the datasource instance with a specified index.
139      * IMPORTANT: releaseDataSourceInstance must be called after using the datasource instance.
140      * @param instanceIndex The index of the datasource to lock and get.
141      * @return The DataSourceInstance at index instanceIndex.
142      *         Null if the datasource instance at the requested index doesn't exist.
143      */
getDataSourceInstanceLocked(int instanceIndex)144     public DataSourceInstanceType getDataSourceInstanceLocked(int instanceIndex) {
145         return (DataSourceInstanceType) nativeGetPerfettoInstanceLocked(mNativeObj, instanceIndex);
146     }
147 
148     /**
149      * Unlock the datasource at the specified index.
150      * @param instanceIndex The index of the datasource to unlock.
151      */
releaseDataSourceInstance(int instanceIndex)152     protected void releaseDataSourceInstance(int instanceIndex) {
153         nativeReleasePerfettoInstanceLocked(mNativeObj, instanceIndex);
154     }
155 
156     /**
157      * Called from native side when a new tracing instance starts.
158      *
159      * @param rawConfig byte array of the PerfettoConfig encoded proto.
160      * @return A new Java DataSourceInstance object.
161      */
createInstance(byte[] rawConfig, int instanceIndex)162     private DataSourceInstanceType createInstance(byte[] rawConfig, int instanceIndex) {
163         final ProtoInputStream inputStream = new ProtoInputStream(rawConfig);
164         return this.createInstance(inputStream, instanceIndex);
165     }
166 
nativeRegisterDataSource(long dataSourcePtr, int bufferExhaustedPolicy, boolean willNotifyOnStop, boolean noFlush)167     private static native void nativeRegisterDataSource(long dataSourcePtr,
168             int bufferExhaustedPolicy, boolean willNotifyOnStop, boolean noFlush);
169 
nativeCreate(DataSource thiz, String name)170     private static native long nativeCreate(DataSource thiz, String name);
nativeFlushAll(long nativeDataSourcePointer)171     private static native void nativeFlushAll(long nativeDataSourcePointer);
nativeGetFinalizer()172     private static native long nativeGetFinalizer();
173 
nativeGetPerfettoInstanceLocked( long dataSourcePtr, int dsInstanceIdx)174     private static native DataSourceInstance nativeGetPerfettoInstanceLocked(
175             long dataSourcePtr, int dsInstanceIdx);
nativeReleasePerfettoInstanceLocked( long dataSourcePtr, int dsInstanceIdx)176     private static native void nativeReleasePerfettoInstanceLocked(
177             long dataSourcePtr, int dsInstanceIdx);
178 
nativePerfettoDsTraceIterateBegin(long dataSourcePtr)179     private static native boolean nativePerfettoDsTraceIterateBegin(long dataSourcePtr);
nativePerfettoDsTraceIterateNext(long dataSourcePtr)180     private static native boolean nativePerfettoDsTraceIterateNext(long dataSourcePtr);
nativePerfettoDsTraceIterateBreak(long dataSourcePtr)181     private static native void nativePerfettoDsTraceIterateBreak(long dataSourcePtr);
nativeGetPerfettoDsInstanceIndex(long dataSourcePtr)182     private static native int nativeGetPerfettoDsInstanceIndex(long dataSourcePtr);
183 
nativeWritePackets(long dataSourcePtr, byte[][] packetData)184     private static native void nativeWritePackets(long dataSourcePtr, byte[][] packetData);
185 }
186