Expose Perfetto DataSource to java
Bug: 309630341 Test: atest CoreTracingTests Change-Id: I9e9486ba406aa67fbc73922910ea97429ee4683c
This commit is contained in:
parent
72235db203
commit
6ecbbc746c
@ -149,6 +149,7 @@ filegroup {
|
|||||||
":framework-javastream-protos",
|
":framework-javastream-protos",
|
||||||
":statslog-framework-java-gen", // FrameworkStatsLog.java
|
":statslog-framework-java-gen", // FrameworkStatsLog.java
|
||||||
":audio_policy_configuration_V7_0",
|
":audio_policy_configuration_V7_0",
|
||||||
|
":perfetto_trace_javastream_protos",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
* @param <DataSourceInstanceType> The type of datasource instance this state applied to.
|
||||||
|
*/
|
||||||
|
public class CreateIncrementalStateArgs<DataSourceInstanceType extends DataSourceInstance> {
|
||||||
|
private final DataSource<DataSourceInstanceType, Object, Object> mDataSource;
|
||||||
|
private final int mInstanceIndex;
|
||||||
|
|
||||||
|
CreateIncrementalStateArgs(DataSource dataSource, int instanceIndex) {
|
||||||
|
this.mDataSource = dataSource;
|
||||||
|
this.mInstanceIndex = instanceIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the datasource instance for this state with a lock.
|
||||||
|
* releaseDataSourceInstanceLocked must be called before this can be called again.
|
||||||
|
* @return The data source instance for this state.
|
||||||
|
* Null if the datasource instance no longer exists.
|
||||||
|
*/
|
||||||
|
public @Nullable DataSourceInstanceType getDataSourceInstanceLocked() {
|
||||||
|
return mDataSource.getDataSourceInstanceLocked(mInstanceIndex);
|
||||||
|
}
|
||||||
|
}
|
43
core/java/android/tracing/perfetto/CreateTlsStateArgs.java
Normal file
43
core/java/android/tracing/perfetto/CreateTlsStateArgs.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
* @param <DataSourceInstanceType> The type of datasource instance this state applied to.
|
||||||
|
*/
|
||||||
|
public class CreateTlsStateArgs<DataSourceInstanceType extends DataSourceInstance> {
|
||||||
|
private final DataSource<DataSourceInstanceType, Object, Object> mDataSource;
|
||||||
|
private final int mInstanceIndex;
|
||||||
|
|
||||||
|
CreateTlsStateArgs(DataSource dataSource, int instanceIndex) {
|
||||||
|
this.mDataSource = dataSource;
|
||||||
|
this.mInstanceIndex = instanceIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the datasource instance for this state with a lock.
|
||||||
|
* releaseDataSourceInstanceLocked must be called before this can be called again.
|
||||||
|
* @return The data source instance for this state.
|
||||||
|
* Null if the datasource instance no longer exists.
|
||||||
|
*/
|
||||||
|
public @Nullable DataSourceInstanceType getDataSourceInstanceLocked() {
|
||||||
|
return mDataSource.getDataSourceInstanceLocked(mInstanceIndex);
|
||||||
|
}
|
||||||
|
}
|
163
core/java/android/tracing/perfetto/DataSource.java
Normal file
163
core/java/android/tracing/perfetto/DataSource.java
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import android.util.proto.ProtoInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Templated base class meant to be derived by embedders to create a custom data
|
||||||
|
* source.
|
||||||
|
*
|
||||||
|
* @param <DataSourceInstanceType> The type for the DataSource instances that will be created from
|
||||||
|
* this DataSource type.
|
||||||
|
* @param <TlsStateType> The type of the custom TLS state, if any is used.
|
||||||
|
* @param <IncrementalStateType> The type of the custom incremental state, if any is used.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public abstract class DataSource<DataSourceInstanceType extends DataSourceInstance,
|
||||||
|
TlsStateType, IncrementalStateType> {
|
||||||
|
protected final long mNativeObj;
|
||||||
|
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function implemented by each datasource to create a new data source instance.
|
||||||
|
*
|
||||||
|
* @param configStream A ProtoInputStream to read the tracing instance's config.
|
||||||
|
* @return A new data source instance setup with the provided config.
|
||||||
|
*/
|
||||||
|
public abstract DataSourceInstanceType createInstance(
|
||||||
|
ProtoInputStream configStream, int instanceIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for datasource base class.
|
||||||
|
*
|
||||||
|
* @param name The fully qualified name of the datasource.
|
||||||
|
*/
|
||||||
|
public DataSource(String name) {
|
||||||
|
this.name = name;
|
||||||
|
this.mNativeObj = nativeCreate(this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main tracing method. Tracing code should call this passing a lambda as
|
||||||
|
* argument, with the following signature: void(TraceContext).
|
||||||
|
* <p>
|
||||||
|
* The lambda will be called synchronously (i.e., always before trace()
|
||||||
|
* returns) only if tracing is enabled and the data source has been enabled in
|
||||||
|
* the tracing config.
|
||||||
|
* <p>
|
||||||
|
* The lambda can be called more than once per trace() call, in the case of
|
||||||
|
* concurrent tracing sessions (or even if the data source is instantiated
|
||||||
|
* twice within the same trace config).
|
||||||
|
*
|
||||||
|
* @param fun The tracing lambda that will be called with the tracing contexts of each active
|
||||||
|
* tracing instance.
|
||||||
|
*/
|
||||||
|
public final void trace(
|
||||||
|
TraceFunction<DataSourceInstanceType, TlsStateType, IncrementalStateType> fun) {
|
||||||
|
nativeTrace(mNativeObj, fun);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush any trace data from this datasource that has not yet been flushed.
|
||||||
|
*/
|
||||||
|
public final void flush() {
|
||||||
|
nativeFlushAll(mNativeObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this method to create a custom TlsState object for your DataSource. A new instance
|
||||||
|
* will be created per trace instance per thread.
|
||||||
|
*
|
||||||
|
* NOTE: Should only be called from native side.
|
||||||
|
*/
|
||||||
|
protected TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override this method to create and use a custom IncrementalState object for your DataSource.
|
||||||
|
*
|
||||||
|
* NOTE: Should only be called from native side.
|
||||||
|
*/
|
||||||
|
protected IncrementalStateType createIncrementalState(
|
||||||
|
CreateIncrementalStateArgs<DataSourceInstanceType> args) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the data source on all tracing backends, including ones that
|
||||||
|
* connect after the registration. Doing so enables the data source to receive
|
||||||
|
* Setup/Start/Stop notifications and makes the trace() method work when
|
||||||
|
* tracing is enabled and the data source is selected.
|
||||||
|
* <p>
|
||||||
|
* NOTE: Once registered, we cannot unregister the data source. Therefore, we should avoid
|
||||||
|
* creating and registering data source where not strictly required. This is a fundamental
|
||||||
|
* limitation of Perfetto itself.
|
||||||
|
*
|
||||||
|
* @param params Params to initialize the datasource with.
|
||||||
|
*/
|
||||||
|
public void register(DataSourceParams params) {
|
||||||
|
nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the datasource instance with a specified index.
|
||||||
|
* IMPORTANT: releaseDataSourceInstance must be called after using the datasource instance.
|
||||||
|
* @param instanceIndex The index of the datasource to lock and get.
|
||||||
|
* @return The DataSourceInstance at index instanceIndex.
|
||||||
|
* Null if the datasource instance at the requested index doesn't exist.
|
||||||
|
*/
|
||||||
|
public DataSourceInstanceType getDataSourceInstanceLocked(int instanceIndex) {
|
||||||
|
return (DataSourceInstanceType) nativeGetPerfettoInstanceLocked(mNativeObj, instanceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlock the datasource at the specified index.
|
||||||
|
* @param instanceIndex The index of the datasource to unlock.
|
||||||
|
*/
|
||||||
|
protected void releaseDataSourceInstance(int instanceIndex) {
|
||||||
|
nativeReleasePerfettoInstanceLocked(mNativeObj, instanceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from native side when a new tracing instance starts.
|
||||||
|
*
|
||||||
|
* @param rawConfig byte array of the PerfettoConfig encoded proto.
|
||||||
|
* @return A new Java DataSourceInstance object.
|
||||||
|
*/
|
||||||
|
private DataSourceInstanceType createInstance(byte[] rawConfig, int instanceIndex) {
|
||||||
|
final ProtoInputStream inputStream = new ProtoInputStream(rawConfig);
|
||||||
|
return this.createInstance(inputStream, instanceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void nativeRegisterDataSource(
|
||||||
|
long dataSourcePtr, int bufferExhaustedPolicy);
|
||||||
|
|
||||||
|
private static native long nativeCreate(DataSource thiz, String name);
|
||||||
|
private static native void nativeTrace(
|
||||||
|
long nativeDataSourcePointer, TraceFunction traceFunction);
|
||||||
|
private static native void nativeFlushAll(long nativeDataSourcePointer);
|
||||||
|
private static native long nativeGetFinalizer();
|
||||||
|
|
||||||
|
private static native DataSourceInstance nativeGetPerfettoInstanceLocked(
|
||||||
|
long dataSourcePtr, int dsInstanceIdx);
|
||||||
|
private static native void nativeReleasePerfettoInstanceLocked(
|
||||||
|
long dataSourcePtr, int dsInstanceIdx);
|
||||||
|
}
|
72
core/java/android/tracing/perfetto/DataSourceInstance.java
Normal file
72
core/java/android/tracing/perfetto/DataSourceInstance.java
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public abstract class DataSourceInstance implements AutoCloseable {
|
||||||
|
private final DataSource mDataSource;
|
||||||
|
private final int mInstanceIndex;
|
||||||
|
|
||||||
|
public DataSourceInstance(DataSource dataSource, int instanceIndex) {
|
||||||
|
this.mDataSource = dataSource;
|
||||||
|
this.mInstanceIndex = instanceIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executed when the tracing instance starts running.
|
||||||
|
* <p>
|
||||||
|
* NOTE: This callback executes on the Perfetto internal thread and is blocking.
|
||||||
|
* Anything that is run in this callback should execute quickly.
|
||||||
|
*
|
||||||
|
* @param args Start arguments.
|
||||||
|
*/
|
||||||
|
protected void onStart(StartCallbackArguments args) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executed when a flush is triggered.
|
||||||
|
* <p>
|
||||||
|
* NOTE: This callback executes on the Perfetto internal thread and is blocking.
|
||||||
|
* Anything that is run in this callback should execute quickly.
|
||||||
|
* @param args Flush arguments.
|
||||||
|
*/
|
||||||
|
protected void onFlush(FlushCallbackArguments args) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executed when the tracing instance is stopped.
|
||||||
|
* <p>
|
||||||
|
* NOTE: This callback executes on the Perfetto internal thread and is blocking.
|
||||||
|
* Anything that is run in this callback should execute quickly.
|
||||||
|
* @param args Stop arguments.
|
||||||
|
*/
|
||||||
|
protected void onStop(StopCallbackArguments args) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void close() {
|
||||||
|
this.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the lock on the datasource once you are finished using it.
|
||||||
|
* Only required to be called when instance was retrieved with
|
||||||
|
* `DataSource#getDataSourceInstanceLocked`.
|
||||||
|
*/
|
||||||
|
public final void release() {
|
||||||
|
mDataSource.releaseDataSourceInstance(mInstanceIndex);
|
||||||
|
}
|
||||||
|
}
|
57
core/java/android/tracing/perfetto/DataSourceParams.java
Normal file
57
core/java/android/tracing/perfetto/DataSourceParams.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataSource Parameters
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class DataSourceParams {
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@IntDef(value = {
|
||||||
|
PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP,
|
||||||
|
PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT,
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface PerfettoDsBufferExhausted {}
|
||||||
|
|
||||||
|
// If the data source runs out of space when trying to acquire a new chunk,
|
||||||
|
// it will drop data.
|
||||||
|
public static final int PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP = 0;
|
||||||
|
|
||||||
|
// If the data source runs out of space when trying to acquire a new chunk,
|
||||||
|
// it will stall, retry and eventually abort if a free chunk is not acquired
|
||||||
|
// after a while.
|
||||||
|
public static final int PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT = 1;
|
||||||
|
|
||||||
|
public static DataSourceParams DEFAULTS =
|
||||||
|
new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP);
|
||||||
|
|
||||||
|
public DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy) {
|
||||||
|
this.bufferExhaustedPolicy = bufferExhaustedPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final @PerfettoDsBufferExhausted int bufferExhaustedPolicy;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class FlushCallbackArguments {
|
||||||
|
}
|
54
core/java/android/tracing/perfetto/InitArguments.java
Normal file
54
core/java/android/tracing/perfetto/InitArguments.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import android.annotation.IntDef;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class InitArguments {
|
||||||
|
public final @PerfettoBackend int backends;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@IntDef(value = {
|
||||||
|
PERFETTO_BACKEND_IN_PROCESS,
|
||||||
|
PERFETTO_BACKEND_SYSTEM,
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface PerfettoBackend {}
|
||||||
|
|
||||||
|
// The in-process tracing backend. Keeps trace buffers in the process memory.
|
||||||
|
public static final int PERFETTO_BACKEND_IN_PROCESS = (1 << 0);
|
||||||
|
|
||||||
|
// The system tracing backend. Connects to the system tracing service (e.g.
|
||||||
|
// on Linux/Android/Mac uses a named UNIX socket).
|
||||||
|
public static final int PERFETTO_BACKEND_SYSTEM = (1 << 1);
|
||||||
|
|
||||||
|
public static InitArguments DEFAULTS = new InitArguments(PERFETTO_BACKEND_SYSTEM);
|
||||||
|
|
||||||
|
public static InitArguments TESTING = new InitArguments(PERFETTO_BACKEND_IN_PROCESS);
|
||||||
|
|
||||||
|
public InitArguments(@PerfettoBackend int backends) {
|
||||||
|
this.backends = backends;
|
||||||
|
}
|
||||||
|
}
|
34
core/java/android/tracing/perfetto/Producer.java
Normal file
34
core/java/android/tracing/perfetto/Producer.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class Producer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the global Perfetto producer.
|
||||||
|
*
|
||||||
|
* @param args arguments on how to initialize the Perfetto producer.
|
||||||
|
*/
|
||||||
|
public static void init(InitArguments args) {
|
||||||
|
nativePerfettoProducerInit(args.backends);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void nativePerfettoProducerInit(int backends);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class StartCallbackArguments {
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class StopCallbackArguments {
|
||||||
|
}
|
43
core/java/android/tracing/perfetto/TraceFunction.java
Normal file
43
core/java/android/tracing/perfetto/TraceFunction.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface for the trace function called from native on a trace call with a context.
|
||||||
|
*
|
||||||
|
* @param <DataSourceInstanceType> The type of DataSource this tracing context is for.
|
||||||
|
* @param <TlsStateType> The type of the custom TLS state, if any is used.
|
||||||
|
* @param <IncrementalStateType> The type of the custom incremental state, if any is used.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public interface TraceFunction<DataSourceInstanceType extends DataSourceInstance,
|
||||||
|
TlsStateType, IncrementalStateType> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function will be called synchronously (i.e., always before trace() returns) only if
|
||||||
|
* tracing is enabled and the data source has been enabled in the tracing config.
|
||||||
|
* It can be called more than once per trace() call, in the case of concurrent tracing sessions
|
||||||
|
* (or even if the data source is instantiated twice within the same trace config).
|
||||||
|
*
|
||||||
|
* @param ctx the tracing context to trace for in the trace function.
|
||||||
|
*/
|
||||||
|
void trace(TracingContext<DataSourceInstanceType, TlsStateType, IncrementalStateType> ctx)
|
||||||
|
throws IOException;
|
||||||
|
}
|
110
core/java/android/tracing/perfetto/TracingContext.java
Normal file
110
core/java/android/tracing/perfetto/TracingContext.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import android.util.proto.ProtoOutputStream;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Argument passed to the lambda function passed to Trace().
|
||||||
|
*
|
||||||
|
* @param <DataSourceInstanceType> The type of the datasource this tracing context is for.
|
||||||
|
* @param <TlsStateType> The type of the custom TLS state, if any is used.
|
||||||
|
* @param <IncrementalStateType> The type of the custom incremental state, if any is used.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class TracingContext<DataSourceInstanceType extends DataSourceInstance,
|
||||||
|
TlsStateType, IncrementalStateType> {
|
||||||
|
|
||||||
|
private final long mContextPtr;
|
||||||
|
private final TlsStateType mTlsState;
|
||||||
|
private final IncrementalStateType mIncrementalState;
|
||||||
|
private final List<ProtoOutputStream> mTracePackets = new ArrayList<>();
|
||||||
|
|
||||||
|
// Should only be created from the native side.
|
||||||
|
private TracingContext(long contextPtr, TlsStateType tlsState,
|
||||||
|
IncrementalStateType incrementalState) {
|
||||||
|
this.mContextPtr = contextPtr;
|
||||||
|
this.mTlsState = tlsState;
|
||||||
|
this.mIncrementalState = incrementalState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new output stream to be used to write a trace packet to. The output stream will be
|
||||||
|
* encoded to the proto binary representation when the callback trace function finishes and
|
||||||
|
* send over to the native side to be included in the proto buffer.
|
||||||
|
*
|
||||||
|
* @return A proto output stream to write a trace packet proto to
|
||||||
|
*/
|
||||||
|
public ProtoOutputStream newTracePacket() {
|
||||||
|
final ProtoOutputStream os = new ProtoOutputStream(0);
|
||||||
|
mTracePackets.add(os);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces a commit of the thread-local tracing data written so far to the
|
||||||
|
* service. This is almost never required (tracing data is periodically
|
||||||
|
* committed as trace pages are filled up) and has a non-negligible
|
||||||
|
* performance hit (requires an IPC + refresh of the current thread-local
|
||||||
|
* chunk). The only case when this should be used is when handling OnStop()
|
||||||
|
* asynchronously, to ensure sure that the data is committed before the
|
||||||
|
* Stop timeout expires.
|
||||||
|
*/
|
||||||
|
public void flush() {
|
||||||
|
nativeFlush(this, mContextPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can optionally be used to store custom per-sequence
|
||||||
|
* session data, which is not reset when incremental state is cleared
|
||||||
|
* (e.g. configuration options).
|
||||||
|
*
|
||||||
|
* @return The TlsState instance for the tracing thread and instance.
|
||||||
|
*/
|
||||||
|
public TlsStateType getCustomTlsState() {
|
||||||
|
return this.mTlsState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can optionally be used store custom per-sequence
|
||||||
|
* incremental data (e.g., interning tables).
|
||||||
|
*
|
||||||
|
* @return The current IncrementalState object instance.
|
||||||
|
*/
|
||||||
|
public IncrementalStateType getIncrementalState() {
|
||||||
|
return this.mIncrementalState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called from native to get trace packets
|
||||||
|
private byte[][] getAndClearAllPendingTracePackets() {
|
||||||
|
byte[][] res = new byte[mTracePackets.size()][];
|
||||||
|
for (int i = 0; i < mTracePackets.size(); i++) {
|
||||||
|
ProtoOutputStream tracePacket = mTracePackets.get(i);
|
||||||
|
res[i] = tracePacket.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
mTracePackets.clear();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private static native void nativeFlush(long nativeDataSourcePointer);
|
||||||
|
private static native void nativeFlush(TracingContext thiz, long ctxPointer);
|
||||||
|
}
|
@ -269,6 +269,9 @@ cc_library_shared_for_libandroid_runtime {
|
|||||||
"android_window_WindowInfosListener.cpp",
|
"android_window_WindowInfosListener.cpp",
|
||||||
"android_window_ScreenCapture.cpp",
|
"android_window_ScreenCapture.cpp",
|
||||||
"jni_common.cpp",
|
"jni_common.cpp",
|
||||||
|
"android_tracing_PerfettoDataSource.cpp",
|
||||||
|
"android_tracing_PerfettoDataSourceInstance.cpp",
|
||||||
|
"android_tracing_PerfettoProducer.cpp",
|
||||||
],
|
],
|
||||||
|
|
||||||
static_libs: [
|
static_libs: [
|
||||||
@ -282,6 +285,7 @@ cc_library_shared_for_libandroid_runtime {
|
|||||||
"libscrypt_static",
|
"libscrypt_static",
|
||||||
"libstatssocket_lazy",
|
"libstatssocket_lazy",
|
||||||
"libskia",
|
"libskia",
|
||||||
|
"libperfetto_client_experimental",
|
||||||
],
|
],
|
||||||
|
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
@ -355,6 +359,7 @@ cc_library_shared_for_libandroid_runtime {
|
|||||||
"server_configurable_flags",
|
"server_configurable_flags",
|
||||||
"libimage_io",
|
"libimage_io",
|
||||||
"libultrahdr",
|
"libultrahdr",
|
||||||
|
"libperfetto_c",
|
||||||
],
|
],
|
||||||
export_shared_lib_headers: [
|
export_shared_lib_headers: [
|
||||||
// our headers include libnativewindow's public headers
|
// our headers include libnativewindow's public headers
|
||||||
|
@ -220,6 +220,9 @@ extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
|
|||||||
extern int register_android_window_WindowInfosListener(JNIEnv* env);
|
extern int register_android_window_WindowInfosListener(JNIEnv* env);
|
||||||
extern int register_android_window_ScreenCapture(JNIEnv* env);
|
extern int register_android_window_ScreenCapture(JNIEnv* env);
|
||||||
extern int register_jni_common(JNIEnv* env);
|
extern int register_jni_common(JNIEnv* env);
|
||||||
|
extern int register_android_tracing_PerfettoDataSource(JNIEnv* env);
|
||||||
|
extern int register_android_tracing_PerfettoDataSourceInstance(JNIEnv* env);
|
||||||
|
extern int register_android_tracing_PerfettoProducer(JNIEnv* env);
|
||||||
|
|
||||||
// Namespace for Android Runtime flags applied during boot time.
|
// Namespace for Android Runtime flags applied during boot time.
|
||||||
static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
|
static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
|
||||||
@ -1675,6 +1678,10 @@ static const RegJNIRec gRegJNI[] = {
|
|||||||
REG_JNI(register_android_window_WindowInfosListener),
|
REG_JNI(register_android_window_WindowInfosListener),
|
||||||
REG_JNI(register_android_window_ScreenCapture),
|
REG_JNI(register_android_window_ScreenCapture),
|
||||||
REG_JNI(register_jni_common),
|
REG_JNI(register_jni_common),
|
||||||
|
|
||||||
|
REG_JNI(register_android_tracing_PerfettoDataSource),
|
||||||
|
REG_JNI(register_android_tracing_PerfettoDataSourceInstance),
|
||||||
|
REG_JNI(register_android_tracing_PerfettoProducer),
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
437
core/jni/android_tracing_PerfettoDataSource.cpp
Normal file
437
core/jni/android_tracing_PerfettoDataSource.cpp
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "Perfetto"
|
||||||
|
|
||||||
|
#include "android_tracing_PerfettoDataSource.h"
|
||||||
|
|
||||||
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
|
#include <android_runtime/Log.h>
|
||||||
|
#include <nativehelper/JNIHelp.h>
|
||||||
|
#include <perfetto/public/data_source.h>
|
||||||
|
#include <perfetto/public/producer.h>
|
||||||
|
#include <perfetto/public/protos/trace/test_event.pzc.h>
|
||||||
|
#include <perfetto/public/protos/trace/trace_packet.pzc.h>
|
||||||
|
#include <perfetto/tracing.h>
|
||||||
|
#include <utils/Log.h>
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "core_jni_helpers.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jclass clazz;
|
||||||
|
jmethodID createInstance;
|
||||||
|
jmethodID createTlsState;
|
||||||
|
jmethodID createIncrementalState;
|
||||||
|
} gPerfettoDataSourceClassInfo;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jclass clazz;
|
||||||
|
jmethodID init;
|
||||||
|
jmethodID getAndClearAllPendingTracePackets;
|
||||||
|
} gTracingContextClassInfo;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jclass clazz;
|
||||||
|
jmethodID init;
|
||||||
|
} gCreateTlsStateArgsClassInfo;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jclass clazz;
|
||||||
|
jmethodID init;
|
||||||
|
} gCreateIncrementalStateArgsClassInfo;
|
||||||
|
|
||||||
|
static JavaVM* gVm;
|
||||||
|
|
||||||
|
struct TlsState {
|
||||||
|
jobject jobj;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IncrementalState {
|
||||||
|
jobject jobj;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void traceAllPendingPackets(JNIEnv* env, jobject jCtx, PerfettoDsTracerIterator* ctx) {
|
||||||
|
jobjectArray packets =
|
||||||
|
(jobjectArray)env
|
||||||
|
->CallObjectMethod(jCtx,
|
||||||
|
gTracingContextClassInfo.getAndClearAllPendingTracePackets);
|
||||||
|
if (env->ExceptionOccurred()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
|
||||||
|
LOG_ALWAYS_FATAL("Failed to call java context finalize method");
|
||||||
|
}
|
||||||
|
|
||||||
|
int packets_count = env->GetArrayLength(packets);
|
||||||
|
for (int i = 0; i < packets_count; i++) {
|
||||||
|
jbyteArray packet_proto_buffer = (jbyteArray)env->GetObjectArrayElement(packets, i);
|
||||||
|
|
||||||
|
jbyte* raw_proto_buffer = env->GetByteArrayElements(packet_proto_buffer, 0);
|
||||||
|
int buffer_size = env->GetArrayLength(packet_proto_buffer);
|
||||||
|
|
||||||
|
struct PerfettoDsRootTracePacket trace_packet;
|
||||||
|
PerfettoDsTracerPacketBegin(ctx, &trace_packet);
|
||||||
|
PerfettoPbMsgAppendBytes(&trace_packet.msg.msg, (const uint8_t*)raw_proto_buffer,
|
||||||
|
buffer_size);
|
||||||
|
PerfettoDsTracerPacketEnd(ctx, &trace_packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfettoDataSource::PerfettoDataSource(JNIEnv* env, jobject javaDataSource,
|
||||||
|
std::string dataSourceName)
|
||||||
|
: dataSourceName(std::move(dataSourceName)),
|
||||||
|
mJavaDataSource(env->NewGlobalRef(javaDataSource)) {}
|
||||||
|
|
||||||
|
jobject PerfettoDataSource::newInstance(JNIEnv* env, void* ds_config, size_t ds_config_size,
|
||||||
|
PerfettoDsInstanceIndex inst_id) {
|
||||||
|
jbyteArray configArray = env->NewByteArray(ds_config_size);
|
||||||
|
|
||||||
|
void* temp = env->GetPrimitiveArrayCritical((jarray)configArray, 0);
|
||||||
|
memcpy(temp, ds_config, ds_config_size);
|
||||||
|
env->ReleasePrimitiveArrayCritical(configArray, temp, 0);
|
||||||
|
|
||||||
|
jobject instance =
|
||||||
|
env->CallObjectMethod(mJavaDataSource, gPerfettoDataSourceClassInfo.createInstance,
|
||||||
|
configArray, inst_id);
|
||||||
|
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
LOGE_EX(env);
|
||||||
|
env->ExceptionClear();
|
||||||
|
LOG_ALWAYS_FATAL("Failed to create new Java Perfetto datasource instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject PerfettoDataSource::createTlsStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id) {
|
||||||
|
ScopedLocalRef<jobject> args(env,
|
||||||
|
env->NewObject(gCreateTlsStateArgsClassInfo.clazz,
|
||||||
|
gCreateTlsStateArgsClassInfo.init, mJavaDataSource,
|
||||||
|
inst_id));
|
||||||
|
|
||||||
|
ScopedLocalRef<jobject> tslState(env,
|
||||||
|
env->CallObjectMethod(mJavaDataSource,
|
||||||
|
gPerfettoDataSourceClassInfo
|
||||||
|
.createTlsState,
|
||||||
|
args.get()));
|
||||||
|
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
LOGE_EX(env);
|
||||||
|
env->ExceptionClear();
|
||||||
|
LOG_ALWAYS_FATAL("Failed to create new Java Perfetto incremental state");
|
||||||
|
}
|
||||||
|
|
||||||
|
return env->NewGlobalRef(tslState.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject PerfettoDataSource::createIncrementalStateGlobalRef(JNIEnv* env,
|
||||||
|
PerfettoDsInstanceIndex inst_id) {
|
||||||
|
ScopedLocalRef<jobject> args(env,
|
||||||
|
env->NewObject(gCreateIncrementalStateArgsClassInfo.clazz,
|
||||||
|
gCreateIncrementalStateArgsClassInfo.init,
|
||||||
|
mJavaDataSource, inst_id));
|
||||||
|
|
||||||
|
ScopedLocalRef<jobject> incrementalState(env,
|
||||||
|
env->CallObjectMethod(mJavaDataSource,
|
||||||
|
gPerfettoDataSourceClassInfo
|
||||||
|
.createIncrementalState,
|
||||||
|
args.get()));
|
||||||
|
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
LOGE_EX(env);
|
||||||
|
env->ExceptionClear();
|
||||||
|
LOG_ALWAYS_FATAL("Failed to create Java Perfetto incremental state");
|
||||||
|
}
|
||||||
|
|
||||||
|
return env->NewGlobalRef(incrementalState.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfettoDataSource::trace(JNIEnv* env, jobject traceFunction) {
|
||||||
|
PERFETTO_DS_TRACE(dataSource, ctx) {
|
||||||
|
TlsState* tls_state =
|
||||||
|
reinterpret_cast<TlsState*>(PerfettoDsGetCustomTls(&dataSource, &ctx));
|
||||||
|
IncrementalState* incr_state = reinterpret_cast<IncrementalState*>(
|
||||||
|
PerfettoDsGetIncrementalState(&dataSource, &ctx));
|
||||||
|
|
||||||
|
ScopedLocalRef<jobject> jCtx(env,
|
||||||
|
env->NewObject(gTracingContextClassInfo.clazz,
|
||||||
|
gTracingContextClassInfo.init, &ctx,
|
||||||
|
tls_state->jobj, incr_state->jobj));
|
||||||
|
|
||||||
|
jclass objclass = env->GetObjectClass(traceFunction);
|
||||||
|
jmethodID method =
|
||||||
|
env->GetMethodID(objclass, "trace", "(Landroid/tracing/perfetto/TracingContext;)V");
|
||||||
|
if (method == 0) {
|
||||||
|
LOG_ALWAYS_FATAL("Failed to get method id");
|
||||||
|
}
|
||||||
|
|
||||||
|
env->ExceptionClear();
|
||||||
|
|
||||||
|
env->CallVoidMethod(traceFunction, method, jCtx.get());
|
||||||
|
if (env->ExceptionOccurred()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
env->ExceptionClear();
|
||||||
|
LOG_ALWAYS_FATAL("Failed to call java trace method");
|
||||||
|
}
|
||||||
|
|
||||||
|
traceAllPendingPackets(env, jCtx.get(), &ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfettoDataSource::flushAll() {
|
||||||
|
PERFETTO_DS_TRACE(dataSource, ctx) {
|
||||||
|
PerfettoDsTracerFlush(&ctx, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfettoDataSource::~PerfettoDataSource() {
|
||||||
|
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||||
|
env->DeleteWeakGlobalRef(mJavaDataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
jlong nativeCreate(JNIEnv* env, jclass clazz, jobject javaDataSource, jstring name) {
|
||||||
|
const char* nativeString = env->GetStringUTFChars(name, 0);
|
||||||
|
PerfettoDataSource* dataSource = new PerfettoDataSource(env, javaDataSource, nativeString);
|
||||||
|
env->ReleaseStringUTFChars(name, nativeString);
|
||||||
|
|
||||||
|
dataSource->incStrong((void*)nativeCreate);
|
||||||
|
|
||||||
|
return reinterpret_cast<jlong>(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nativeDestroy(void* ptr) {
|
||||||
|
PerfettoDataSource* dataSource = reinterpret_cast<PerfettoDataSource*>(ptr);
|
||||||
|
dataSource->decStrong((void*)nativeCreate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
|
||||||
|
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&nativeDestroy));
|
||||||
|
}
|
||||||
|
|
||||||
|
void nativeTrace(JNIEnv* env, jclass clazz, jlong dataSourcePtr, jobject traceFunctionInterface) {
|
||||||
|
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
|
||||||
|
|
||||||
|
datasource->trace(env, traceFunctionInterface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nativeFlush(JNIEnv* env, jclass clazz, jobject jCtx, jlong ctxPtr) {
|
||||||
|
auto* ctx = reinterpret_cast<struct PerfettoDsTracerIterator*>(ctxPtr);
|
||||||
|
traceAllPendingPackets(env, jCtx, ctx);
|
||||||
|
PerfettoDsTracerFlush(ctx, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nativeFlushAll(JNIEnv* env, jclass clazz, jlong ptr) {
|
||||||
|
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(ptr);
|
||||||
|
datasource->flushAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr,
|
||||||
|
int buffer_exhausted_policy) {
|
||||||
|
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(datasource_ptr);
|
||||||
|
|
||||||
|
struct PerfettoDsParams params = PerfettoDsParamsDefault();
|
||||||
|
params.buffer_exhausted_policy = (PerfettoDsBufferExhaustedPolicy)buffer_exhausted_policy;
|
||||||
|
|
||||||
|
params.user_arg = reinterpret_cast<void*>(datasource.get());
|
||||||
|
|
||||||
|
params.on_setup_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id,
|
||||||
|
void* ds_config, size_t ds_config_size, void* user_arg,
|
||||||
|
struct PerfettoDsOnSetupArgs*) -> void* {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
|
||||||
|
auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg);
|
||||||
|
|
||||||
|
ScopedLocalRef<jobject> java_data_source_instance(env,
|
||||||
|
datasource->newInstance(env, ds_config,
|
||||||
|
ds_config_size,
|
||||||
|
inst_id));
|
||||||
|
|
||||||
|
auto* datasource_instance =
|
||||||
|
new PerfettoDataSourceInstance(env, java_data_source_instance.get(), inst_id);
|
||||||
|
|
||||||
|
return static_cast<void*>(datasource_instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
params.on_create_tls_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
|
||||||
|
struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
|
||||||
|
auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg);
|
||||||
|
|
||||||
|
jobject java_tls_state = datasource->createTlsStateGlobalRef(env, inst_id);
|
||||||
|
|
||||||
|
auto* tls_state = new TlsState(java_tls_state);
|
||||||
|
|
||||||
|
return static_cast<void*>(tls_state);
|
||||||
|
};
|
||||||
|
|
||||||
|
params.on_delete_tls_cb = [](void* ptr) {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
|
||||||
|
TlsState* tls_state = reinterpret_cast<TlsState*>(ptr);
|
||||||
|
env->DeleteGlobalRef(tls_state->jobj);
|
||||||
|
delete tls_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
params.on_create_incr_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
|
||||||
|
struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
|
||||||
|
auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg);
|
||||||
|
jobject java_incr_state = datasource->createIncrementalStateGlobalRef(env, inst_id);
|
||||||
|
|
||||||
|
auto* incr_state = new IncrementalState(java_incr_state);
|
||||||
|
return static_cast<void*>(incr_state);
|
||||||
|
};
|
||||||
|
|
||||||
|
params.on_delete_incr_cb = [](void* ptr) {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
|
||||||
|
IncrementalState* incr_state = reinterpret_cast<IncrementalState*>(ptr);
|
||||||
|
env->DeleteGlobalRef(incr_state->jobj);
|
||||||
|
delete incr_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
params.on_start_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, void* inst_ctx,
|
||||||
|
struct PerfettoDsOnStartArgs*) {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
|
||||||
|
auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
|
||||||
|
datasource_instance->onStart(env);
|
||||||
|
};
|
||||||
|
|
||||||
|
params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, void* inst_ctx,
|
||||||
|
struct PerfettoDsOnFlushArgs*) {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
|
||||||
|
auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
|
||||||
|
datasource_instance->onFlush(env);
|
||||||
|
};
|
||||||
|
|
||||||
|
params.on_stop_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id, void* user_arg,
|
||||||
|
void* inst_ctx, struct PerfettoDsOnStopArgs*) {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
|
||||||
|
auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
|
||||||
|
datasource_instance->onStop(env);
|
||||||
|
};
|
||||||
|
|
||||||
|
params.on_destroy_cb = [](struct PerfettoDsImpl* ds_impl, void* user_arg,
|
||||||
|
void* inst_ctx) -> void {
|
||||||
|
auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
|
||||||
|
delete datasource_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
PerfettoDsRegister(&datasource->dataSource, datasource->dataSourceName.c_str(), params);
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject nativeGetPerfettoInstanceLocked(JNIEnv* env, jclass clazz, jlong dataSourcePtr,
|
||||||
|
PerfettoDsInstanceIndex instance_idx) {
|
||||||
|
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
|
||||||
|
auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(
|
||||||
|
PerfettoDsImplGetInstanceLocked(datasource->dataSource.impl, instance_idx));
|
||||||
|
|
||||||
|
if (datasource_instance == nullptr) {
|
||||||
|
// datasource instance doesn't exist
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return datasource_instance->GetJavaDataSourceInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nativeReleasePerfettoInstanceLocked(JNIEnv* env, jclass clazz, jlong dataSourcePtr,
|
||||||
|
PerfettoDsInstanceIndex instance_idx) {
|
||||||
|
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
|
||||||
|
PerfettoDsImplReleaseInstanceLocked(datasource->dataSource.impl, instance_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const JNINativeMethod gMethods[] = {
|
||||||
|
/* name, signature, funcPtr */
|
||||||
|
{"nativeCreate", "(Landroid/tracing/perfetto/DataSource;Ljava/lang/String;)J",
|
||||||
|
(void*)nativeCreate},
|
||||||
|
{"nativeTrace", "(JLandroid/tracing/perfetto/TraceFunction;)V", (void*)nativeTrace},
|
||||||
|
{"nativeFlushAll", "(J)V", (void*)nativeFlushAll},
|
||||||
|
{"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer},
|
||||||
|
{"nativeRegisterDataSource", "(JI)V", (void*)nativeRegisterDataSource},
|
||||||
|
{"nativeGetPerfettoInstanceLocked", "(JI)Landroid/tracing/perfetto/DataSourceInstance;",
|
||||||
|
(void*)nativeGetPerfettoInstanceLocked},
|
||||||
|
{"nativeReleasePerfettoInstanceLocked", "(JI)V",
|
||||||
|
(void*)nativeReleasePerfettoInstanceLocked},
|
||||||
|
};
|
||||||
|
|
||||||
|
const JNINativeMethod gMethodsTracingContext[] = {
|
||||||
|
/* name, signature, funcPtr */
|
||||||
|
{"nativeFlush", "(Landroid/tracing/perfetto/TracingContext;J)V", (void*)nativeFlush},
|
||||||
|
};
|
||||||
|
|
||||||
|
int register_android_tracing_PerfettoDataSource(JNIEnv* env) {
|
||||||
|
int res = jniRegisterNativeMethods(env, "android/tracing/perfetto/DataSource", gMethods,
|
||||||
|
NELEM(gMethods));
|
||||||
|
|
||||||
|
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
|
||||||
|
|
||||||
|
res = jniRegisterNativeMethods(env, "android/tracing/perfetto/TracingContext",
|
||||||
|
gMethodsTracingContext, NELEM(gMethodsTracingContext));
|
||||||
|
|
||||||
|
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
|
||||||
|
|
||||||
|
if (env->GetJavaVM(&gVm) != JNI_OK) {
|
||||||
|
LOG_ALWAYS_FATAL("Failed to get JavaVM from JNIEnv: %p", env);
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass clazz = env->FindClass("android/tracing/perfetto/DataSource");
|
||||||
|
gPerfettoDataSourceClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
|
||||||
|
gPerfettoDataSourceClassInfo.createInstance =
|
||||||
|
env->GetMethodID(gPerfettoDataSourceClassInfo.clazz, "createInstance",
|
||||||
|
"([BI)Landroid/tracing/perfetto/DataSourceInstance;");
|
||||||
|
gPerfettoDataSourceClassInfo.createTlsState =
|
||||||
|
env->GetMethodID(gPerfettoDataSourceClassInfo.clazz, "createTlsState",
|
||||||
|
"(Landroid/tracing/perfetto/CreateTlsStateArgs;)Ljava/lang/Object;");
|
||||||
|
gPerfettoDataSourceClassInfo.createIncrementalState =
|
||||||
|
env->GetMethodID(gPerfettoDataSourceClassInfo.clazz, "createIncrementalState",
|
||||||
|
"(Landroid/tracing/perfetto/CreateIncrementalStateArgs;)Ljava/lang/"
|
||||||
|
"Object;");
|
||||||
|
|
||||||
|
clazz = env->FindClass("android/tracing/perfetto/TracingContext");
|
||||||
|
gTracingContextClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
|
||||||
|
gTracingContextClassInfo.init = env->GetMethodID(gTracingContextClassInfo.clazz, "<init>",
|
||||||
|
"(JLjava/lang/Object;Ljava/lang/Object;)V");
|
||||||
|
gTracingContextClassInfo.getAndClearAllPendingTracePackets =
|
||||||
|
env->GetMethodID(gTracingContextClassInfo.clazz, "getAndClearAllPendingTracePackets",
|
||||||
|
"()[[B");
|
||||||
|
|
||||||
|
clazz = env->FindClass("android/tracing/perfetto/CreateTlsStateArgs");
|
||||||
|
gCreateTlsStateArgsClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
|
||||||
|
gCreateTlsStateArgsClassInfo.init =
|
||||||
|
env->GetMethodID(gCreateTlsStateArgsClassInfo.clazz, "<init>",
|
||||||
|
"(Landroid/tracing/perfetto/DataSource;I)V");
|
||||||
|
|
||||||
|
clazz = env->FindClass("android/tracing/perfetto/CreateIncrementalStateArgs");
|
||||||
|
gCreateIncrementalStateArgsClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
|
||||||
|
gCreateIncrementalStateArgsClassInfo.init =
|
||||||
|
env->GetMethodID(gCreateIncrementalStateArgsClassInfo.clazz, "<init>",
|
||||||
|
"(Landroid/tracing/perfetto/DataSource;I)V");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
59
core/jni/android_tracing_PerfettoDataSource.h
Normal file
59
core/jni/android_tracing_PerfettoDataSource.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "Perfetto"
|
||||||
|
|
||||||
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
|
#include <android_runtime/Log.h>
|
||||||
|
#include <nativehelper/JNIHelp.h>
|
||||||
|
#include <perfetto/public/data_source.h>
|
||||||
|
#include <perfetto/public/producer.h>
|
||||||
|
#include <perfetto/public/protos/trace/test_event.pzc.h>
|
||||||
|
#include <perfetto/public/protos/trace/trace_packet.pzc.h>
|
||||||
|
#include <perfetto/tracing.h>
|
||||||
|
#include <utils/Log.h>
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "android_tracing_PerfettoDataSourceInstance.h"
|
||||||
|
#include "core_jni_helpers.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
class PerfettoDataSource : public virtual RefBase {
|
||||||
|
public:
|
||||||
|
const std::string dataSourceName;
|
||||||
|
struct PerfettoDs dataSource = PERFETTO_DS_INIT();
|
||||||
|
|
||||||
|
PerfettoDataSource(JNIEnv* env, jobject java_data_source, std::string data_source_name);
|
||||||
|
~PerfettoDataSource();
|
||||||
|
|
||||||
|
jobject newInstance(JNIEnv* env, void* ds_config, size_t ds_config_size,
|
||||||
|
PerfettoDsInstanceIndex inst_id);
|
||||||
|
|
||||||
|
jobject createTlsStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id);
|
||||||
|
jobject createIncrementalStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id);
|
||||||
|
void trace(JNIEnv* env, jobject trace_function);
|
||||||
|
void flushAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
jobject mJavaDataSource;
|
||||||
|
std::map<PerfettoDsInstanceIndex, PerfettoDataSourceInstance*> mInstances;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace android
|
138
core/jni/android_tracing_PerfettoDataSourceInstance.cpp
Normal file
138
core/jni/android_tracing_PerfettoDataSourceInstance.cpp
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "Perfetto"
|
||||||
|
|
||||||
|
#include "android_tracing_PerfettoDataSourceInstance.h"
|
||||||
|
|
||||||
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
|
#include <android_runtime/Log.h>
|
||||||
|
#include <nativehelper/JNIHelp.h>
|
||||||
|
#include <perfetto/public/data_source.h>
|
||||||
|
#include <perfetto/public/producer.h>
|
||||||
|
#include <perfetto/public/protos/trace/test_event.pzc.h>
|
||||||
|
#include <perfetto/public/protos/trace/trace_packet.pzc.h>
|
||||||
|
#include <perfetto/tracing.h>
|
||||||
|
#include <utils/Log.h>
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "core_jni_helpers.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jclass clazz;
|
||||||
|
jmethodID init;
|
||||||
|
} gStartCallbackArgumentsClassInfo;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jclass clazz;
|
||||||
|
jmethodID init;
|
||||||
|
} gFlushCallbackArgumentsClassInfo;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
jclass clazz;
|
||||||
|
jmethodID init;
|
||||||
|
} gStopCallbackArgumentsClassInfo;
|
||||||
|
|
||||||
|
static JavaVM* gVm;
|
||||||
|
|
||||||
|
void callJavaMethodWithArgsObject(JNIEnv* env, jobject classRef, jmethodID method, jobject args) {
|
||||||
|
ScopedLocalRef<jobject> localClassRef(env, env->NewLocalRef(classRef));
|
||||||
|
|
||||||
|
if (localClassRef == nullptr) {
|
||||||
|
ALOGE("Weak reference went out of scope");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
env->CallVoidMethod(localClassRef.get(), method, args);
|
||||||
|
|
||||||
|
if (env->ExceptionCheck()) {
|
||||||
|
env->ExceptionDescribe();
|
||||||
|
LOGE_EX(env);
|
||||||
|
env->ExceptionClear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfettoDataSourceInstance::PerfettoDataSourceInstance(JNIEnv* env, jobject javaDataSourceInstance,
|
||||||
|
PerfettoDsInstanceIndex inst_idx)
|
||||||
|
: inst_idx(inst_idx), mJavaDataSourceInstance(env->NewGlobalRef(javaDataSourceInstance)) {}
|
||||||
|
|
||||||
|
PerfettoDataSourceInstance::~PerfettoDataSourceInstance() {
|
||||||
|
JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
|
||||||
|
env->DeleteGlobalRef(mJavaDataSourceInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfettoDataSourceInstance::onStart(JNIEnv* env) {
|
||||||
|
ScopedLocalRef<jobject> args(env,
|
||||||
|
env->NewObject(gStartCallbackArgumentsClassInfo.clazz,
|
||||||
|
gStartCallbackArgumentsClassInfo.init));
|
||||||
|
jclass cls = env->GetObjectClass(mJavaDataSourceInstance);
|
||||||
|
jmethodID mid = env->GetMethodID(cls, "onStart",
|
||||||
|
"(Landroid/tracing/perfetto/StartCallbackArguments;)V");
|
||||||
|
|
||||||
|
callJavaMethodWithArgsObject(env, mJavaDataSourceInstance, mid, args.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfettoDataSourceInstance::onFlush(JNIEnv* env) {
|
||||||
|
ScopedLocalRef<jobject> args(env,
|
||||||
|
env->NewObject(gFlushCallbackArgumentsClassInfo.clazz,
|
||||||
|
gFlushCallbackArgumentsClassInfo.init));
|
||||||
|
jclass cls = env->GetObjectClass(mJavaDataSourceInstance);
|
||||||
|
jmethodID mid = env->GetMethodID(cls, "onFlush",
|
||||||
|
"(Landroid/tracing/perfetto/FlushCallbackArguments;)V");
|
||||||
|
|
||||||
|
callJavaMethodWithArgsObject(env, mJavaDataSourceInstance, mid, args.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void PerfettoDataSourceInstance::onStop(JNIEnv* env) {
|
||||||
|
ScopedLocalRef<jobject> args(env,
|
||||||
|
env->NewObject(gStopCallbackArgumentsClassInfo.clazz,
|
||||||
|
gStopCallbackArgumentsClassInfo.init));
|
||||||
|
jclass cls = env->GetObjectClass(mJavaDataSourceInstance);
|
||||||
|
jmethodID mid =
|
||||||
|
env->GetMethodID(cls, "onStop", "(Landroid/tracing/perfetto/StopCallbackArguments;)V");
|
||||||
|
|
||||||
|
callJavaMethodWithArgsObject(env, mJavaDataSourceInstance, mid, args.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
int register_android_tracing_PerfettoDataSourceInstance(JNIEnv* env) {
|
||||||
|
if (env->GetJavaVM(&gVm) != JNI_OK) {
|
||||||
|
LOG_ALWAYS_FATAL("Failed to get JavaVM from JNIEnv: %p", env);
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass clazz = env->FindClass("android/tracing/perfetto/StartCallbackArguments");
|
||||||
|
gStartCallbackArgumentsClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
|
||||||
|
gStartCallbackArgumentsClassInfo.init =
|
||||||
|
env->GetMethodID(gStartCallbackArgumentsClassInfo.clazz, "<init>", "()V");
|
||||||
|
|
||||||
|
clazz = env->FindClass("android/tracing/perfetto/FlushCallbackArguments");
|
||||||
|
gFlushCallbackArgumentsClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
|
||||||
|
gFlushCallbackArgumentsClassInfo.init =
|
||||||
|
env->GetMethodID(gFlushCallbackArgumentsClassInfo.clazz, "<init>", "()V");
|
||||||
|
|
||||||
|
clazz = env->FindClass("android/tracing/perfetto/StopCallbackArguments");
|
||||||
|
gStopCallbackArgumentsClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
|
||||||
|
gStopCallbackArgumentsClassInfo.init =
|
||||||
|
env->GetMethodID(gStopCallbackArgumentsClassInfo.clazz, "<init>", "()V");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
59
core/jni/android_tracing_PerfettoDataSourceInstance.h
Normal file
59
core/jni/android_tracing_PerfettoDataSourceInstance.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "Perfetto"
|
||||||
|
|
||||||
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
|
#include <android_runtime/Log.h>
|
||||||
|
#include <nativehelper/JNIHelp.h>
|
||||||
|
#include <perfetto/public/data_source.h>
|
||||||
|
#include <perfetto/public/producer.h>
|
||||||
|
#include <perfetto/public/protos/trace/test_event.pzc.h>
|
||||||
|
#include <perfetto/public/protos/trace/trace_packet.pzc.h>
|
||||||
|
#include <perfetto/tracing.h>
|
||||||
|
#include <utils/Log.h>
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "core_jni_helpers.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
class PerfettoDataSourceInstance {
|
||||||
|
public:
|
||||||
|
PerfettoDataSourceInstance(JNIEnv* env, jobject javaDataSourceInstance,
|
||||||
|
PerfettoDsInstanceIndex inst_idx);
|
||||||
|
~PerfettoDataSourceInstance();
|
||||||
|
|
||||||
|
void onStart(JNIEnv* env);
|
||||||
|
void onFlush(JNIEnv* env);
|
||||||
|
void onStop(JNIEnv* env);
|
||||||
|
|
||||||
|
jobject GetJavaDataSourceInstance() {
|
||||||
|
return mJavaDataSourceInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
PerfettoDsInstanceIndex getIndex() {
|
||||||
|
return inst_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PerfettoDsInstanceIndex inst_idx;
|
||||||
|
jobject mJavaDataSourceInstance;
|
||||||
|
};
|
||||||
|
} // namespace android
|
58
core/jni/android_tracing_PerfettoProducer.cpp
Normal file
58
core/jni/android_tracing_PerfettoProducer.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "Perfetto"
|
||||||
|
|
||||||
|
#include <android_runtime/AndroidRuntime.h>
|
||||||
|
#include <android_runtime/Log.h>
|
||||||
|
#include <nativehelper/JNIHelp.h>
|
||||||
|
#include <perfetto/public/data_source.h>
|
||||||
|
#include <perfetto/public/producer.h>
|
||||||
|
#include <perfetto/public/protos/trace/test_event.pzc.h>
|
||||||
|
#include <perfetto/public/protos/trace/trace_packet.pzc.h>
|
||||||
|
#include <perfetto/tracing.h>
|
||||||
|
#include <utils/Log.h>
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "android_tracing_PerfettoDataSource.h"
|
||||||
|
#include "core_jni_helpers.h"
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
void perfettoProducerInit(JNIEnv* env, jclass clazz, int backends) {
|
||||||
|
struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
|
||||||
|
args.backends = (PerfettoBackendTypes)backends;
|
||||||
|
PerfettoProducerInit(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const JNINativeMethod gMethods[] = {
|
||||||
|
/* name, signature, funcPtr */
|
||||||
|
{"nativePerfettoProducerInit", "(I)V", (void*)perfettoProducerInit},
|
||||||
|
};
|
||||||
|
|
||||||
|
int register_android_tracing_PerfettoProducer(JNIEnv* env) {
|
||||||
|
int res = jniRegisterNativeMethods(env, "android/tracing/perfetto/Producer", gMethods,
|
||||||
|
NELEM(gMethods));
|
||||||
|
|
||||||
|
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
@ -83,6 +83,10 @@ android_test {
|
|||||||
"com.android.text.flags-aconfig-java",
|
"com.android.text.flags-aconfig-java",
|
||||||
"flag-junit",
|
"flag-junit",
|
||||||
"ravenwood-junit",
|
"ravenwood-junit",
|
||||||
|
"perfetto_trace_java_protos",
|
||||||
|
"flickerlib-parsers",
|
||||||
|
"flickerlib-trace_processor_shell",
|
||||||
|
"mockito-target-extended-minus-junit4",
|
||||||
],
|
],
|
||||||
|
|
||||||
libs: [
|
libs: [
|
||||||
|
@ -0,0 +1,664 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import static java.io.File.createTempFile;
|
||||||
|
import static java.nio.file.Files.createTempDirectory;
|
||||||
|
|
||||||
|
import static perfetto.protos.PerfettoTrace.TestEvent.PAYLOAD;
|
||||||
|
import static perfetto.protos.PerfettoTrace.TestEvent.TestPayload.SINGLE_INT;
|
||||||
|
import static perfetto.protos.PerfettoTrace.TracePacket.FOR_TESTING;
|
||||||
|
|
||||||
|
import android.tools.common.ScenarioBuilder;
|
||||||
|
import android.tools.common.Tag;
|
||||||
|
import android.tools.common.io.TraceType;
|
||||||
|
import android.tools.device.traces.TraceConfig;
|
||||||
|
import android.tools.device.traces.TraceConfigs;
|
||||||
|
import android.tools.device.traces.io.ResultReader;
|
||||||
|
import android.tools.device.traces.io.ResultWriter;
|
||||||
|
import android.tools.device.traces.monitors.PerfettoTraceMonitor;
|
||||||
|
import android.tools.device.traces.monitors.TraceMonitor;
|
||||||
|
import android.util.proto.ProtoInputStream;
|
||||||
|
import android.util.proto.ProtoOutputStream;
|
||||||
|
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.google.common.truth.Truth;
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import perfetto.protos.PerfettoConfig;
|
||||||
|
import perfetto.protos.PerfettoTrace;
|
||||||
|
import perfetto.protos.TracePacketOuterClass;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class DataSourceTest {
|
||||||
|
private final File mTracingDirectory = createTempDirectory("temp").toFile();
|
||||||
|
|
||||||
|
private final ResultWriter mWriter = new ResultWriter()
|
||||||
|
.forScenario(new ScenarioBuilder()
|
||||||
|
.forClass(createTempFile("temp", "").getName()).build())
|
||||||
|
.withOutputDir(mTracingDirectory)
|
||||||
|
.setRunComplete();
|
||||||
|
|
||||||
|
private final TraceConfigs mTraceConfig = new TraceConfigs(
|
||||||
|
new TraceConfig(false, true, false),
|
||||||
|
new TraceConfig(false, true, false),
|
||||||
|
new TraceConfig(false, true, false),
|
||||||
|
new TraceConfig(false, true, false)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static TestDataSource sTestDataSource;
|
||||||
|
|
||||||
|
private static TestDataSource.DataSourceInstanceProvider sInstanceProvider;
|
||||||
|
private static TestDataSource.TlsStateProvider sTlsStateProvider;
|
||||||
|
private static TestDataSource.IncrementalStateProvider sIncrementalStateProvider;
|
||||||
|
|
||||||
|
public DataSourceTest() throws IOException {}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeAll() {
|
||||||
|
Producer.init(InitArguments.DEFAULTS);
|
||||||
|
setupProviders();
|
||||||
|
sTestDataSource = new TestDataSource(
|
||||||
|
(ds, idx, configStream) -> sInstanceProvider.provide(ds, idx, configStream),
|
||||||
|
args -> sTlsStateProvider.provide(args),
|
||||||
|
args -> sIncrementalStateProvider.provide(args));
|
||||||
|
sTestDataSource.register(DataSourceParams.DEFAULTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupProviders() {
|
||||||
|
sInstanceProvider = (ds, idx, configStream) ->
|
||||||
|
new TestDataSource.TestDataSourceInstance(ds, idx);
|
||||||
|
sTlsStateProvider = args -> new TestDataSource.TestTlsState();
|
||||||
|
sIncrementalStateProvider = args -> new TestDataSource.TestIncrementalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
setupProviders();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canTraceData() throws InvalidProtocolBufferException {
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
|
||||||
|
long forTestingToken = protoOutputStream.start(FOR_TESTING);
|
||||||
|
long payloadToken = protoOutputStream.start(PAYLOAD);
|
||||||
|
protoOutputStream.write(SINGLE_INT, 10);
|
||||||
|
protoOutputStream.end(payloadToken);
|
||||||
|
protoOutputStream.end(forTestingToken);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
|
||||||
|
final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
|
||||||
|
assert rawProtoFromFile != null;
|
||||||
|
final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
|
||||||
|
.parseFrom(rawProtoFromFile);
|
||||||
|
|
||||||
|
Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
|
||||||
|
final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
|
||||||
|
.stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
|
||||||
|
final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
|
||||||
|
.filter(it -> it.getForTesting().getPayload().getSingleInt() == 10).toList();
|
||||||
|
Truth.assertThat(matchingPackets).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canUseTlsStateForCustomState() {
|
||||||
|
final int expectedStateTestValue = 10;
|
||||||
|
final AtomicInteger actualStateTestValue = new AtomicInteger();
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
TestDataSource.TestTlsState state = ctx.getCustomTlsState();
|
||||||
|
state.testStateValue = expectedStateTestValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
TestDataSource.TestTlsState state = ctx.getCustomTlsState();
|
||||||
|
actualStateTestValue.set(state.testStateValue);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Truth.assertThat(actualStateTestValue.get()).isEqualTo(expectedStateTestValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void eachInstanceHasOwnTlsState() {
|
||||||
|
final int[] expectedStateTestValues = new int[] { 1, 2 };
|
||||||
|
final int[] actualStateTestValues = new int[] { 0, 0 };
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
final TraceMonitor traceMonitor2 = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceMonitor1.start();
|
||||||
|
try {
|
||||||
|
traceMonitor2.start();
|
||||||
|
|
||||||
|
AtomicInteger index = new AtomicInteger(0);
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
TestDataSource.TestTlsState state = ctx.getCustomTlsState();
|
||||||
|
state.testStateValue = expectedStateTestValues[index.getAndIncrement()];
|
||||||
|
});
|
||||||
|
|
||||||
|
index.set(0);
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
TestDataSource.TestTlsState state = ctx.getCustomTlsState();
|
||||||
|
actualStateTestValues[index.getAndIncrement()] = state.testStateValue;
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
traceMonitor1.stop(mWriter);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
traceMonitor2.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Truth.assertThat(actualStateTestValues[0]).isEqualTo(expectedStateTestValues[0]);
|
||||||
|
Truth.assertThat(actualStateTestValues[1]).isEqualTo(expectedStateTestValues[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void eachThreadHasOwnTlsState() throws InterruptedException {
|
||||||
|
final int thread1ExpectedStateValue = 1;
|
||||||
|
final int thread2ExpectedStateValue = 2;
|
||||||
|
|
||||||
|
final AtomicInteger thread1ActualStateValue = new AtomicInteger();
|
||||||
|
final AtomicInteger thread2ActualStateValue = new AtomicInteger();
|
||||||
|
|
||||||
|
final CountDownLatch setUpLatch = new CountDownLatch(2);
|
||||||
|
final CountDownLatch setStateLatch = new CountDownLatch(2);
|
||||||
|
final CountDownLatch setOutStateLatch = new CountDownLatch(2);
|
||||||
|
|
||||||
|
final RunnableCreator createTask = (stateValue, stateOut) -> () -> {
|
||||||
|
Producer.init(InitArguments.DEFAULTS);
|
||||||
|
|
||||||
|
setUpLatch.countDown();
|
||||||
|
|
||||||
|
try {
|
||||||
|
setUpLatch.await(3, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
TestDataSource.TestTlsState state = ctx.getCustomTlsState();
|
||||||
|
state.testStateValue = stateValue;
|
||||||
|
setStateLatch.countDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
setStateLatch.await(3, TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
stateOut.set(ctx.getCustomTlsState().testStateValue);
|
||||||
|
setOutStateLatch.countDown();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
|
||||||
|
new Thread(
|
||||||
|
createTask.create(thread1ExpectedStateValue, thread1ActualStateValue)).start();
|
||||||
|
new Thread(
|
||||||
|
createTask.create(thread2ExpectedStateValue, thread2ActualStateValue)).start();
|
||||||
|
|
||||||
|
setOutStateLatch.await(3, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Truth.assertThat(thread1ActualStateValue.get()).isEqualTo(thread1ExpectedStateValue);
|
||||||
|
Truth.assertThat(thread2ActualStateValue.get()).isEqualTo(thread2ExpectedStateValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void incrementalStateIsReset() throws InterruptedException {
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build())
|
||||||
|
.setIncrementalTimeout(10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final AtomicInteger testStateValue = new AtomicInteger();
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
|
||||||
|
sTestDataSource.trace(ctx -> ctx.getIncrementalState().testStateValue = 1);
|
||||||
|
|
||||||
|
// Timeout to make sure the incremental state is cleared.
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
sTestDataSource.trace(ctx ->
|
||||||
|
testStateValue.set(ctx.getIncrementalState().testStateValue));
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Truth.assertThat(testStateValue.get()).isNotEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getInstanceConfigOnCreateInstance() throws IOException {
|
||||||
|
final int expectedDummyIntValue = 10;
|
||||||
|
AtomicReference<ProtoInputStream> configStream = new AtomicReference<>();
|
||||||
|
sInstanceProvider = (ds, idx, config) -> {
|
||||||
|
configStream.set(config);
|
||||||
|
return new TestDataSource.TestDataSourceInstance(ds, idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
final TraceMonitor monitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name)
|
||||||
|
.setForTesting(PerfettoConfig.TestConfig.newBuilder().setDummyFields(
|
||||||
|
PerfettoConfig.TestConfig.DummyFields.newBuilder()
|
||||||
|
.setFieldInt32(expectedDummyIntValue)
|
||||||
|
.build())
|
||||||
|
.build())
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
monitor.start();
|
||||||
|
} finally {
|
||||||
|
monitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int configDummyIntValue = 0;
|
||||||
|
while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) {
|
||||||
|
if (configStream.get().getFieldNumber()
|
||||||
|
== (int) PerfettoTrace.DataSourceConfig.FOR_TESTING) {
|
||||||
|
final long forTestingToken = configStream.get()
|
||||||
|
.start(PerfettoTrace.DataSourceConfig.FOR_TESTING);
|
||||||
|
while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) {
|
||||||
|
if (configStream.get().getFieldNumber()
|
||||||
|
== (int) PerfettoTrace.TestConfig.DUMMY_FIELDS) {
|
||||||
|
final long dummyFieldsToken = configStream.get()
|
||||||
|
.start(PerfettoTrace.TestConfig.DUMMY_FIELDS);
|
||||||
|
while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) {
|
||||||
|
if (configStream.get().getFieldNumber()
|
||||||
|
== (int) PerfettoTrace.TestConfig.DummyFields.FIELD_INT32) {
|
||||||
|
int val = configStream.get().readInt(
|
||||||
|
PerfettoTrace.TestConfig.DummyFields.FIELD_INT32);
|
||||||
|
if (val != 0) {
|
||||||
|
configDummyIntValue = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configStream.get().end(dummyFieldsToken);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configStream.get().end(forTestingToken);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Truth.assertThat(configDummyIntValue).isEqualTo(expectedDummyIntValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void multipleTraceInstances() throws IOException, InterruptedException {
|
||||||
|
final int instanceCount = 3;
|
||||||
|
|
||||||
|
final List<TraceMonitor> monitors = new ArrayList<>();
|
||||||
|
final List<ResultWriter> writers = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < instanceCount; i++) {
|
||||||
|
final ResultWriter writer = new ResultWriter()
|
||||||
|
.forScenario(new ScenarioBuilder()
|
||||||
|
.forClass(createTempFile("temp", "").getName()).build())
|
||||||
|
.withOutputDir(mTracingDirectory)
|
||||||
|
.setRunComplete();
|
||||||
|
writers.add(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start at 1 because 0 is considered null value so payload will be ignored in that case
|
||||||
|
TestDataSource.TestTlsState.lastIndex = 1;
|
||||||
|
|
||||||
|
final AtomicInteger traceCallCount = new AtomicInteger();
|
||||||
|
final CountDownLatch latch = new CountDownLatch(instanceCount);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Start instances
|
||||||
|
for (int i = 0; i < instanceCount; i++) {
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
monitors.add(traceMonitor);
|
||||||
|
traceMonitor.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace the stateIndex of the tracing instance.
|
||||||
|
sTestDataSource.trace(ctx -> {
|
||||||
|
final int testIntValue = ctx.getCustomTlsState().stateIndex;
|
||||||
|
traceCallCount.incrementAndGet();
|
||||||
|
|
||||||
|
final ProtoOutputStream os = ctx.newTracePacket();
|
||||||
|
long forTestingToken = os.start(FOR_TESTING);
|
||||||
|
long payloadToken = os.start(PAYLOAD);
|
||||||
|
os.write(SINGLE_INT, testIntValue);
|
||||||
|
os.end(payloadToken);
|
||||||
|
os.end(forTestingToken);
|
||||||
|
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
// Stop instances
|
||||||
|
for (int i = 0; i < instanceCount; i++) {
|
||||||
|
final TraceMonitor monitor = monitors.get(i);
|
||||||
|
final ResultWriter writer = writers.get(i);
|
||||||
|
monitor.stop(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.await(3, TimeUnit.SECONDS);
|
||||||
|
Truth.assertThat(traceCallCount.get()).isEqualTo(instanceCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < instanceCount; i++) {
|
||||||
|
final int expectedTracedValue = i + 1;
|
||||||
|
final ResultWriter writer = writers.get(i);
|
||||||
|
final ResultReader reader = new ResultReader(writer.write(), mTraceConfig);
|
||||||
|
final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
|
||||||
|
assert rawProtoFromFile != null;
|
||||||
|
final perfetto.protos.TraceOuterClass.Trace trace =
|
||||||
|
perfetto.protos.TraceOuterClass.Trace.parseFrom(rawProtoFromFile);
|
||||||
|
|
||||||
|
Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
|
||||||
|
final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
|
||||||
|
.stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
|
||||||
|
Truth.assertWithMessage("One packet has for testing data")
|
||||||
|
.that(tracePackets).hasSize(1);
|
||||||
|
|
||||||
|
final List<TracePacketOuterClass.TracePacket> matchingPackets =
|
||||||
|
tracePackets.stream()
|
||||||
|
.filter(it -> it.getForTesting().getPayload()
|
||||||
|
.getSingleInt() == expectedTracedValue).toList();
|
||||||
|
Truth.assertWithMessage(
|
||||||
|
"One packet has testing data with a payload with the expected value")
|
||||||
|
.that(matchingPackets).hasSize(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStartCallbackTriggered() throws InterruptedException {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
|
final AtomicBoolean callbackCalled = new AtomicBoolean(false);
|
||||||
|
sInstanceProvider = (ds, idx, config) -> new TestDataSource.TestDataSourceInstance(
|
||||||
|
ds,
|
||||||
|
idx,
|
||||||
|
(args) -> {
|
||||||
|
callbackCalled.set(true);
|
||||||
|
latch.countDown();
|
||||||
|
},
|
||||||
|
(args) -> {},
|
||||||
|
(args) -> {}
|
||||||
|
);
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
Truth.assertThat(callbackCalled.get()).isFalse();
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
latch.await(3, TimeUnit.SECONDS);
|
||||||
|
Truth.assertThat(callbackCalled.get()).isTrue();
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onFlushCallbackTriggered() throws InterruptedException {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicBoolean callbackCalled = new AtomicBoolean(false);
|
||||||
|
sInstanceProvider = (ds, idx, config) ->
|
||||||
|
new TestDataSource.TestDataSourceInstance(
|
||||||
|
ds,
|
||||||
|
idx,
|
||||||
|
(args) -> {},
|
||||||
|
(args) -> {
|
||||||
|
callbackCalled.set(true);
|
||||||
|
latch.countDown();
|
||||||
|
},
|
||||||
|
(args) -> {}
|
||||||
|
);
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
Truth.assertThat(callbackCalled.get()).isFalse();
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
|
||||||
|
long forTestingToken = protoOutputStream.start(FOR_TESTING);
|
||||||
|
long payloadToken = protoOutputStream.start(PAYLOAD);
|
||||||
|
protoOutputStream.write(SINGLE_INT, 10);
|
||||||
|
protoOutputStream.end(payloadToken);
|
||||||
|
protoOutputStream.end(forTestingToken);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.await(3, TimeUnit.SECONDS);
|
||||||
|
Truth.assertThat(callbackCalled.get()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStopCallbackTriggered() throws InterruptedException {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicBoolean callbackCalled = new AtomicBoolean(false);
|
||||||
|
sInstanceProvider = (ds, idx, config) ->
|
||||||
|
new TestDataSource.TestDataSourceInstance(
|
||||||
|
ds,
|
||||||
|
idx,
|
||||||
|
(args) -> {},
|
||||||
|
(args) -> {},
|
||||||
|
(args) -> {
|
||||||
|
callbackCalled.set(true);
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
Truth.assertThat(callbackCalled.get()).isFalse();
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
latch.await(3, TimeUnit.SECONDS);
|
||||||
|
Truth.assertThat(callbackCalled.get()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canUseDataSourceInstanceToCreateTlsState() throws InvalidProtocolBufferException {
|
||||||
|
final Object testObject = new Object();
|
||||||
|
|
||||||
|
sInstanceProvider = (ds, idx, configStream) -> {
|
||||||
|
final TestDataSource.TestDataSourceInstance dsInstance =
|
||||||
|
new TestDataSource.TestDataSourceInstance(ds, idx);
|
||||||
|
dsInstance.testObject = testObject;
|
||||||
|
return dsInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
sTlsStateProvider = args -> {
|
||||||
|
final TestDataSource.TestTlsState tlsState = new TestDataSource.TestTlsState();
|
||||||
|
|
||||||
|
try (TestDataSource.TestDataSourceInstance dataSourceInstance =
|
||||||
|
args.getDataSourceInstanceLocked()) {
|
||||||
|
if (dataSourceInstance != null) {
|
||||||
|
tlsState.testStateValue = dataSourceInstance.testObject.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlsState;
|
||||||
|
};
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
|
||||||
|
long forTestingToken = protoOutputStream.start(FOR_TESTING);
|
||||||
|
long payloadToken = protoOutputStream.start(PAYLOAD);
|
||||||
|
protoOutputStream.write(SINGLE_INT, ctx.getCustomTlsState().testStateValue);
|
||||||
|
protoOutputStream.end(payloadToken);
|
||||||
|
protoOutputStream.end(forTestingToken);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
|
||||||
|
final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
|
||||||
|
assert rawProtoFromFile != null;
|
||||||
|
final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
|
||||||
|
.parseFrom(rawProtoFromFile);
|
||||||
|
|
||||||
|
Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
|
||||||
|
final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
|
||||||
|
.stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
|
||||||
|
final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
|
||||||
|
.filter(it -> it.getForTesting().getPayload().getSingleInt()
|
||||||
|
== testObject.hashCode()).toList();
|
||||||
|
Truth.assertThat(matchingPackets).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void canUseDataSourceInstanceToCreateIncrementalState()
|
||||||
|
throws InvalidProtocolBufferException {
|
||||||
|
final Object testObject = new Object();
|
||||||
|
|
||||||
|
sInstanceProvider = (ds, idx, configStream) -> {
|
||||||
|
final TestDataSource.TestDataSourceInstance dsInstance =
|
||||||
|
new TestDataSource.TestDataSourceInstance(ds, idx);
|
||||||
|
dsInstance.testObject = testObject;
|
||||||
|
return dsInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
sIncrementalStateProvider = args -> {
|
||||||
|
final TestDataSource.TestIncrementalState incrementalState =
|
||||||
|
new TestDataSource.TestIncrementalState();
|
||||||
|
|
||||||
|
try (TestDataSource.TestDataSourceInstance dataSourceInstance =
|
||||||
|
args.getDataSourceInstanceLocked()) {
|
||||||
|
if (dataSourceInstance != null) {
|
||||||
|
incrementalState.testStateValue = dataSourceInstance.testObject.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return incrementalState;
|
||||||
|
};
|
||||||
|
|
||||||
|
final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
|
||||||
|
.enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
|
||||||
|
.setName(sTestDataSource.name).build()).build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
traceMonitor.start();
|
||||||
|
sTestDataSource.trace((ctx) -> {
|
||||||
|
final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
|
||||||
|
long forTestingToken = protoOutputStream.start(FOR_TESTING);
|
||||||
|
long payloadToken = protoOutputStream.start(PAYLOAD);
|
||||||
|
protoOutputStream.write(SINGLE_INT, ctx.getIncrementalState().testStateValue);
|
||||||
|
protoOutputStream.end(payloadToken);
|
||||||
|
protoOutputStream.end(forTestingToken);
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
traceMonitor.stop(mWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
|
||||||
|
final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
|
||||||
|
assert rawProtoFromFile != null;
|
||||||
|
final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
|
||||||
|
.parseFrom(rawProtoFromFile);
|
||||||
|
|
||||||
|
Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
|
||||||
|
final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
|
||||||
|
.stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
|
||||||
|
final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
|
||||||
|
.filter(it -> it.getForTesting().getPayload().getSingleInt()
|
||||||
|
== testObject.hashCode()).toList();
|
||||||
|
Truth.assertThat(matchingPackets).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RunnableCreator {
|
||||||
|
Runnable create(int state, AtomicInteger stateOut);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.tracing.perfetto;
|
||||||
|
|
||||||
|
import android.util.proto.ProtoInputStream;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class TestDataSource extends DataSource<TestDataSource.TestDataSourceInstance,
|
||||||
|
TestDataSource.TestTlsState, TestDataSource.TestIncrementalState> {
|
||||||
|
private final DataSourceInstanceProvider mDataSourceInstanceProvider;
|
||||||
|
private final TlsStateProvider mTlsStateProvider;
|
||||||
|
private final IncrementalStateProvider mIncrementalStateProvider;
|
||||||
|
|
||||||
|
interface DataSourceInstanceProvider {
|
||||||
|
TestDataSourceInstance provide(
|
||||||
|
TestDataSource dataSource, int instanceIndex, ProtoInputStream configStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TlsStateProvider {
|
||||||
|
TestTlsState provide(CreateTlsStateArgs<TestDataSourceInstance> args);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IncrementalStateProvider {
|
||||||
|
TestIncrementalState provide(CreateIncrementalStateArgs<TestDataSourceInstance> args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestDataSource() {
|
||||||
|
this((ds, idx, config) -> new TestDataSourceInstance(ds, idx),
|
||||||
|
args -> new TestTlsState(), args -> new TestIncrementalState());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestDataSource(
|
||||||
|
DataSourceInstanceProvider dataSourceInstanceProvider,
|
||||||
|
TlsStateProvider tlsStateProvider,
|
||||||
|
IncrementalStateProvider incrementalStateProvider
|
||||||
|
) {
|
||||||
|
super("android.tracing.perfetto.TestDataSource#" + UUID.randomUUID().toString());
|
||||||
|
this.mDataSourceInstanceProvider = dataSourceInstanceProvider;
|
||||||
|
this.mTlsStateProvider = tlsStateProvider;
|
||||||
|
this.mIncrementalStateProvider = incrementalStateProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestDataSourceInstance createInstance(ProtoInputStream configStream, int instanceIndex) {
|
||||||
|
return mDataSourceInstanceProvider.provide(this, instanceIndex, configStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestTlsState createTlsState(CreateTlsStateArgs args) {
|
||||||
|
return mTlsStateProvider.provide(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestIncrementalState createIncrementalState(CreateIncrementalStateArgs args) {
|
||||||
|
return mIncrementalStateProvider.provide(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestTlsState {
|
||||||
|
public int testStateValue;
|
||||||
|
public int stateIndex = lastIndex++;
|
||||||
|
|
||||||
|
public static int lastIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestIncrementalState {
|
||||||
|
public int testStateValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestDataSourceInstance extends DataSourceInstance {
|
||||||
|
public Object testObject;
|
||||||
|
Consumer<StartCallbackArguments> mStartCallback;
|
||||||
|
Consumer<FlushCallbackArguments> mFlushCallback;
|
||||||
|
Consumer<StopCallbackArguments> mStopCallback;
|
||||||
|
|
||||||
|
public TestDataSourceInstance(DataSource dataSource, int instanceIndex) {
|
||||||
|
this(dataSource, instanceIndex, args -> {}, args -> {}, args -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestDataSourceInstance(
|
||||||
|
DataSource dataSource,
|
||||||
|
int instanceIndex,
|
||||||
|
Consumer<StartCallbackArguments> startCallback,
|
||||||
|
Consumer<FlushCallbackArguments> flushCallback,
|
||||||
|
Consumer<StopCallbackArguments> stopCallback) {
|
||||||
|
super(dataSource, instanceIndex);
|
||||||
|
this.mStartCallback = startCallback;
|
||||||
|
this.mFlushCallback = flushCallback;
|
||||||
|
this.mStopCallback = stopCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart(StartCallbackArguments args) {
|
||||||
|
this.mStartCallback.accept(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFlush(FlushCallbackArguments args) {
|
||||||
|
this.mFlushCallback.accept(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop(StopCallbackArguments args) {
|
||||||
|
this.mStopCallback.accept(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user