Expose Perfetto DataSource to java

Bug: 309630341
Test: atest CoreTracingTests
Change-Id: I9e9486ba406aa67fbc73922910ea97429ee4683c
This commit is contained in:
Pablo Gamito 2023-11-06 17:49:36 +00:00
parent 72235db203
commit 6ecbbc746c
23 changed files with 2242 additions and 0 deletions

View File

@ -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",
], ],
} }

View 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 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);
}
}

View 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);
}
}

View 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);
}

View 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);
}
}

View 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;
}

View File

@ -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 {
}

View 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;
}
}

View 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);
}

View File

@ -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 {
}

View File

@ -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 {
}

View 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;
}

View 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);
}

View File

@ -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

View File

@ -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),
}; };
/* /*

View 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

View 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

View 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

View 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

View 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

View File

@ -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: [

View File

@ -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);
}
}

View File

@ -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);
}
}
}