summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Zimuzo Ezeozue <zezeozue@google.com> 2024-09-17 09:43:00 +0100
committer Zimuzo Ezeozue <zezeozue@google.com> 2024-12-18 17:07:43 +0000
commit4424d49087e4c366d68bedf46606ded2c5b4a43f (patch)
treebdc752ff0629d63ec7537634f39e2be203c315c6
parent41ef600b957a2e1625f51bec4f8b2c3aa76e24cb (diff)
Add a Java Perfetto SDK API
Utilizing Ibb08e8b6cab7d86ac2c1d57c621c9074a4cb4077 we add a JNI layer to create and reuse Perfetto tracing objects. To avoid GC churn, we reuse these objects from an LRUCache. The API is something like: PerfettoTraceExtras = PerfettoTraceExtras.addArg(..).addFlow(..); PerfettoTrace.instant(category, extras); There is support for the following Perfetto constructs: 1. Categories 2. DebugArgs (k,v pairs) 3. Flows 4. DynamicTracks (NamedTracks) 5. RegisteredTracks (for async slice and counters) 6. Counters (int and double) Arbitrary protos are not supported yet. If we go that far, we should perhaps serialize the protos directly into the shmem (or via an intermediate ring buffer) without going through JNI. Test: atest PerfettoTraceTest Change-Id: Icd23eed60d97cd2e9b65679d2aee0190aa7476b0 Bug: 303199244 Flag: android.os.perfetto_sdk_tracing_v2
-rw-r--r--core/java/android/os/PerfettoTrace.java395
-rw-r--r--core/java/android/os/PerfettoTrackEventExtra.java1081
-rw-r--r--core/java/android/os/flags.aconfig7
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/jni/android_os_PerfettoTrace.cpp143
-rw-r--r--core/jni/android_os_PerfettoTrackEventExtra.cpp536
-rw-r--r--core/tests/coretests/Android.bp2
-rw-r--r--core/tests/coretests/jni/Android.bp24
-rw-r--r--core/tests/coretests/jni/PerfettoTraceTest.cpp117
-rw-r--r--core/tests/coretests/src/android/os/PerfettoTraceTest.java600
11 files changed, 2911 insertions, 0 deletions
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
new file mode 100644
index 000000000000..164561acac32
--- /dev/null
+++ b/core/java/android/os/PerfettoTrace.java
@@ -0,0 +1,395 @@
+/*
+ * 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.os;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+/**
+ * Writes trace events to the perfetto trace buffer. These trace events can be
+ * collected and visualized using the Perfetto UI.
+ *
+ * <p>This tracing mechanism is independent of the method tracing mechanism
+ * offered by {@link Debug#startMethodTracing} or {@link Trace}.
+ *
+ * @hide
+ */
+public final class PerfettoTrace {
+ private static final String TAG = "PerfettoTrace";
+
+ // Keep in sync with C++
+ private static final int PERFETTO_TE_TYPE_SLICE_BEGIN = 1;
+ private static final int PERFETTO_TE_TYPE_SLICE_END = 2;
+ private static final int PERFETTO_TE_TYPE_INSTANT = 3;
+ private static final int PERFETTO_TE_TYPE_COUNTER = 4;
+
+ private static final boolean IS_FLAG_ENABLED = android.os.Flags.perfettoSdkTracingV2();
+
+ /**
+ * For fetching the next flow event id in a process.
+ */
+ private static final AtomicInteger sFlowEventId = new AtomicInteger();
+
+ /**
+ * Perfetto category a trace event belongs to.
+ * Registering a category is not sufficient to capture events within the category, it must
+ * also be enabled in the trace config.
+ */
+ public static final class Category implements PerfettoTrackEventExtra.PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ Category.class.getClassLoader(), native_delete());
+
+ private final long mPtr;
+ private final long mExtraPtr;
+ private final String mName;
+ private final String mTag;
+ private final String mSeverity;
+ private boolean mIsRegistered;
+
+ /**
+ * Category ctor.
+ *
+ * @param name The category name.
+ */
+ public Category(String name) {
+ this(name, null, null);
+ }
+
+ /**
+ * Category ctor.
+ *
+ * @param name The category name.
+ * @param tag An atrace tag name that this category maps to.
+ */
+ public Category(String name, String tag) {
+ this(name, tag, null);
+ }
+
+ /**
+ * Category ctor.
+ *
+ * @param name The category name.
+ * @param tag An atrace tag name that this category maps to.
+ * @param severity A Log style severity string for the category.
+ */
+ public Category(String name, String tag, String severity) {
+ mName = name;
+ mTag = tag;
+ mSeverity = severity;
+ mPtr = native_init(name, tag, severity);
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @FastNative
+ private static native long native_init(String name, String tag, String severity);
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native void native_register(long ptr);
+ @CriticalNative
+ private static native void native_unregister(long ptr);
+ @CriticalNative
+ private static native boolean native_is_enabled(long ptr);
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+
+ /**
+ * Register the category.
+ */
+ public Category register() {
+ native_register(mPtr);
+ mIsRegistered = true;
+ return this;
+ }
+
+ /**
+ * Unregister the category.
+ */
+ public Category unregister() {
+ native_unregister(mPtr);
+ mIsRegistered = false;
+ return this;
+ }
+
+ /**
+ * Whether the category is enabled or not.
+ */
+ public boolean isEnabled() {
+ return IS_FLAG_ENABLED && native_is_enabled(mPtr);
+ }
+
+ /**
+ * Whether the category is registered or not.
+ */
+ public boolean isRegistered() {
+ return mIsRegistered;
+ }
+
+ /**
+ * Returns the native pointer for the category.
+ */
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+ }
+
+ @FastNative
+ private static native void native_event(int type, long tag, String name, long ptr);
+
+ @CriticalNative
+ private static native long native_get_process_track_uuid();
+
+ @CriticalNative
+ private static native long native_get_thread_track_uuid(long tid);
+
+ @FastNative
+ private static native void native_activate_trigger(String name, int ttlMs);
+
+ /**
+ * Writes a trace message to indicate a given section of code was invoked.
+ *
+ * @param category The perfetto category pointer.
+ * @param eventName The event name to appear in the trace.
+ * @param extra The extra arguments.
+ */
+ public static void instant(Category category, String eventName, PerfettoTrackEventExtra extra) {
+ if (!category.isEnabled()) {
+ return;
+ }
+
+ native_event(PERFETTO_TE_TYPE_INSTANT, category.getPtr(), eventName, extra.getPtr());
+ extra.reset();
+ }
+
+ /**
+ * Writes a trace message to indicate a given section of code was invoked.
+ *
+ * @param category The perfetto category.
+ * @param eventName The event name to appear in the trace.
+ * @param extraConfig Consumer for the extra arguments.
+ */
+ public static void instant(Category category, String eventName,
+ Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
+ PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
+ extraConfig.accept(extra);
+ instant(category, eventName, extra.build());
+ }
+
+ /**
+ * Writes a trace message to indicate a given section of code was invoked.
+ *
+ * @param category The perfetto category.
+ * @param eventName The event name to appear in the trace.
+ */
+ public static void instant(Category category, String eventName) {
+ instant(category, eventName, PerfettoTrackEventExtra.builder().build());
+ }
+
+ /**
+ * Writes a trace message to indicate the start of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param eventName The event name to appear in the trace.
+ * @param extra The extra arguments.
+ */
+ public static void begin(Category category, String eventName, PerfettoTrackEventExtra extra) {
+ if (!category.isEnabled()) {
+ return;
+ }
+
+ native_event(PERFETTO_TE_TYPE_SLICE_BEGIN, category.getPtr(), eventName, extra.getPtr());
+ extra.reset();
+ }
+
+ /**
+ * Writes a trace message to indicate the start of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param eventName The event name to appear in the trace.
+ * @param extraConfig Consumer for the extra arguments.
+ */
+ public static void begin(Category category, String eventName,
+ Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
+ PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
+ extraConfig.accept(extra);
+ begin(category, eventName, extra.build());
+ }
+
+ /**
+ * Writes a trace message to indicate the start of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param eventName The event name to appear in the trace.
+ */
+ public static void begin(Category category, String eventName) {
+ begin(category, eventName, PerfettoTrackEventExtra.builder().build());
+ }
+
+ /**
+ * Writes a trace message to indicate the end of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param extra The extra arguments.
+ */
+ public static void end(Category category, PerfettoTrackEventExtra extra) {
+ if (!category.isEnabled()) {
+ return;
+ }
+
+ native_event(PERFETTO_TE_TYPE_SLICE_END, category.getPtr(), "", extra.getPtr());
+ extra.reset();
+ }
+
+ /**
+ * Writes a trace message to indicate the end of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param extraConfig Consumer for the extra arguments.
+ */
+ public static void end(Category category,
+ Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
+ PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
+ extraConfig.accept(extra);
+ end(category, extra.build());
+ }
+
+ /**
+ * Writes a trace message to indicate the end of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ */
+ public static void end(Category category) {
+ end(category, PerfettoTrackEventExtra.builder().build());
+ }
+
+ /**
+ * Writes a trace message to indicate the value of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param extra The extra arguments.
+ */
+ public static void counter(Category category, PerfettoTrackEventExtra extra) {
+ if (!category.isEnabled()) {
+ return;
+ }
+
+ native_event(PERFETTO_TE_TYPE_COUNTER, category.getPtr(), "", extra.getPtr());
+ extra.reset();
+ }
+
+ /**
+ * Writes a trace message to indicate the value of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param extraConfig Consumer for the extra arguments.
+ */
+ public static void counter(Category category,
+ Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
+ PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
+ extraConfig.accept(extra);
+ counter(category, extra.build());
+ }
+
+ /**
+ * Writes a trace message to indicate the value of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param trackName The trackName for the event.
+ * @param value The value of the counter.
+ */
+ public static void counter(Category category, String trackName, long value) {
+ PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
+ .usingCounterTrack(trackName, PerfettoTrace.getProcessTrackUuid())
+ .setCounter(value)
+ .build();
+ counter(category, extra);
+ }
+
+ /**
+ * Writes a trace message to indicate the value of a given section of code.
+ *
+ * @param category The perfetto category pointer.
+ * @param trackName The trackName for the event.
+ * @param value The value of the counter.
+ */
+ public static void counter(Category category, String trackName, double value) {
+ PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
+ .usingCounterTrack(trackName, PerfettoTrace.getProcessTrackUuid())
+ .setCounter(value)
+ .build();
+ counter(category, extra);
+ }
+
+ /**
+ * Returns the next flow id to be used.
+ */
+ public static int getFlowId() {
+ return sFlowEventId.incrementAndGet();
+ }
+
+ /**
+ * Returns the global track uuid that can be used as a parent track uuid.
+ */
+ public static long getGlobalTrackUuid() {
+ return 0;
+ }
+
+ /**
+ * Returns the process track uuid that can be used as a parent track uuid.
+ */
+ public static long getProcessTrackUuid() {
+ if (IS_FLAG_ENABLED) {
+ return 0;
+ }
+ return native_get_process_track_uuid();
+ }
+
+ /**
+ * Given a thread tid, returns the thread track uuid that can be used as a parent track uuid.
+ */
+ public static long getThreadTrackUuid(long tid) {
+ if (IS_FLAG_ENABLED) {
+ return 0;
+ }
+ return native_get_thread_track_uuid(tid);
+ }
+
+ /**
+ * Activates a trigger by name {@code triggerName} with expiry in {@code ttlMs}.
+ */
+ public static void activateTrigger(String triggerName, int ttlMs) {
+ if (IS_FLAG_ENABLED) {
+ return;
+ }
+ native_activate_trigger(triggerName, ttlMs);
+ }
+
+ /**
+ * Registers the process with Perfetto.
+ */
+ public static void register() {
+ Trace.registerWithPerfetto();
+ }
+}
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
new file mode 100644
index 000000000000..a219b3b5678b
--- /dev/null
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -0,0 +1,1081 @@
+/*
+ * 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.os;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+
+/**
+ * Holds extras to be passed to Perfetto track events in {@link PerfettoTrace}.
+ *
+ * @hide
+ */
+public final class PerfettoTrackEventExtra {
+ private static final int DEFAULT_EXTRA_CACHE_SIZE = 5;
+ private static final ThreadLocal<PerfettoTrackEventExtra> sTrackEventExtra =
+ new ThreadLocal<PerfettoTrackEventExtra>() {
+ @Override
+ protected PerfettoTrackEventExtra initialValue() {
+ return new PerfettoTrackEventExtra();
+ }
+ };
+ private static final AtomicLong sNamedTrackId = new AtomicLong();
+
+ private boolean mIsInUse;
+ private CounterInt64 mCounterInt64;
+ private CounterDouble mCounterDouble;
+ private Proto mProto;
+
+ /**
+ * Represents a native pointer to a Perfetto C SDK struct. E.g. PerfettoTeHlExtra.
+ */
+ public interface PerfettoPointer {
+ /**
+ * Returns the perfetto struct native pointer.
+ */
+ long getPtr();
+ }
+
+ /**
+ * Container for {@link Field} instances.
+ */
+ public interface FieldContainer {
+ /**
+ * Add {@link Field} to the container.
+ */
+ void addField(PerfettoPointer field);
+ }
+
+ /**
+ * RingBuffer implemented on top of a SparseArray.
+ *
+ * Bounds a SparseArray with a FIFO algorithm.
+ */
+ private static final class RingBuffer<T> {
+ private final int mCapacity;
+ private final int[] mKeyArray;
+ private final T[] mValueArray;
+ private int mWriteEnd = 0;
+
+ RingBuffer(int capacity) {
+ mCapacity = capacity;
+ mKeyArray = new int[capacity];
+ mValueArray = (T[]) new Object[capacity];
+ }
+
+ public void put(int key, T value) {
+ mKeyArray[mWriteEnd] = key;
+ mValueArray[mWriteEnd] = value;
+ mWriteEnd = (mWriteEnd + 1) % mCapacity;
+ }
+
+ public T get(int key) {
+ for (int i = 0; i < mCapacity; i++) {
+ if (mKeyArray[i] == key) {
+ return mValueArray[i];
+ }
+ }
+ return null;
+ }
+ }
+
+ private static final class Pool<T> {
+ private final int mCapacity;
+ private final T[] mValueArray;
+ private int mIdx = 0;
+
+ Pool(int capacity) {
+ mCapacity = capacity;
+ mValueArray = (T[]) new Object[capacity];
+ }
+
+ public void reset() {
+ mIdx = 0;
+ }
+
+ public T get(Supplier<T> supplier) {
+ if (mIdx >= mCapacity) {
+ return supplier.get();
+ }
+ if (mValueArray[mIdx] == null) {
+ mValueArray[mIdx] = supplier.get();
+ }
+ return mValueArray[mIdx++];
+ }
+ }
+
+ /**
+ * Builder for Perfetto track event extras.
+ */
+ public static final class Builder {
+ // For performance reasons, we hold a reference to mExtra as a holder for
+ // perfetto pointers being added. This way, we avoid an additional list to hold
+ // the pointers in Java and we can pass them down directly to native code.
+ private final PerfettoTrackEventExtra mExtra;
+ private boolean mIsBuilt;
+ private Builder mParent;
+ private FieldContainer mCurrentContainer;
+
+ private final CounterInt64 mCounterInt64;
+ private final CounterDouble mCounterDouble;
+ private final Proto mProto;
+
+ private final RingBuffer<NamedTrack> mNamedTrackCache;
+ private final RingBuffer<CounterTrack> mCounterTrackCache;
+ private final RingBuffer<ArgInt64> mArgInt64Cache;
+ private final RingBuffer<ArgBool> mArgBoolCache;
+ private final RingBuffer<ArgDouble> mArgDoubleCache;
+ private final RingBuffer<ArgString> mArgStringCache;
+
+ private final Pool<FieldInt64> mFieldInt64Cache;
+ private final Pool<FieldDouble> mFieldDoubleCache;
+ private final Pool<FieldString> mFieldStringCache;
+ private final Pool<FieldNested> mFieldNestedCache;
+ private final Pool<Flow> mFlowCache;
+ private final Pool<Builder> mBuilderCache;
+
+ private Builder() {
+ this(sTrackEventExtra.get(), null, null);
+ }
+
+ private Builder(PerfettoTrackEventExtra extra, Builder parent, FieldContainer container) {
+ mExtra = extra;
+ mParent = parent;
+ mCurrentContainer = container;
+
+ mNamedTrackCache = mExtra.mNamedTrackCache;
+ mCounterTrackCache = mExtra.mCounterTrackCache;
+ mArgInt64Cache = mExtra.mArgInt64Cache;
+ mArgDoubleCache = mExtra.mArgDoubleCache;
+ mArgBoolCache = mExtra.mArgBoolCache;
+ mArgStringCache = mExtra.mArgStringCache;
+ mFieldInt64Cache = mExtra.mFieldInt64Cache;
+ mFieldDoubleCache = mExtra.mFieldDoubleCache;
+ mFieldStringCache = mExtra.mFieldStringCache;
+ mFieldNestedCache = mExtra.mFieldNestedCache;
+ mFlowCache = mExtra.mFlowCache;
+ mBuilderCache = mExtra.mBuilderCache;
+
+ mCounterInt64 = mExtra.getCounterInt64();
+ mCounterDouble = mExtra.getCounterDouble();
+ mProto = mExtra.getProto();
+ }
+
+ /**
+ * Builds the track event extra.
+ */
+ public PerfettoTrackEventExtra build() {
+ checkParent();
+ mIsBuilt = true;
+
+ mFieldInt64Cache.reset();
+ mFieldDoubleCache.reset();
+ mFieldStringCache.reset();
+ mFieldNestedCache.reset();
+ mBuilderCache.reset();
+
+ return mExtra;
+ }
+
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
+ public Builder addArg(String name, long val) {
+ checkParent();
+ ArgInt64 arg = mArgInt64Cache.get(name.hashCode());
+ if (arg == null || !arg.getName().equals(name)) {
+ arg = new ArgInt64(name);
+ mArgInt64Cache.put(name.hashCode(), arg);
+ }
+ arg.setValue(val);
+ mExtra.addPerfettoPointer(arg);
+ return this;
+ }
+
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
+ public Builder addArg(String name, boolean val) {
+ checkParent();
+ ArgBool arg = mArgBoolCache.get(name.hashCode());
+ if (arg == null || !arg.getName().equals(name)) {
+ arg = new ArgBool(name);
+ mArgBoolCache.put(name.hashCode(), arg);
+ }
+ arg.setValue(val);
+ mExtra.addPerfettoPointer(arg);
+ return this;
+ }
+
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
+ public Builder addArg(String name, double val) {
+ checkParent();
+ ArgDouble arg = mArgDoubleCache.get(name.hashCode());
+ if (arg == null || !arg.getName().equals(name)) {
+ arg = new ArgDouble(name);
+ mArgDoubleCache.put(name.hashCode(), arg);
+ }
+ arg.setValue(val);
+ mExtra.addPerfettoPointer(arg);
+ return this;
+ }
+
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
+ public Builder addArg(String name, String val) {
+ checkParent();
+ ArgString arg = mArgStringCache.get(name.hashCode());
+ if (arg == null || !arg.getName().equals(name)) {
+ arg = new ArgString(name);
+ mArgStringCache.put(name.hashCode(), arg);
+ }
+ arg.setValue(val);
+ mExtra.addPerfettoPointer(arg);
+ return this;
+ }
+
+ /**
+ * Adds a flow with {@code id}.
+ */
+ public Builder addFlow(int id) {
+ checkParent();
+ Flow flow = mFlowCache.get(Flow::new);
+ flow.setProcessFlow(id);
+ mExtra.addPerfettoPointer(flow);
+ return this;
+ }
+
+ /**
+ * Adds a terminating flow with {@code id}.
+ */
+ public Builder addTerminatingFlow(int id) {
+ checkParent();
+ Flow flow = mFlowCache.get(Flow::new);
+ flow.setProcessTerminatingFlow(id);
+ mExtra.addPerfettoPointer(flow);
+ return this;
+ }
+
+ /**
+ * Adds the events to a named track instead of the thread track where the
+ * event occurred.
+ */
+ public Builder usingNamedTrack(String name, long parentUuid) {
+ checkParent();
+ NamedTrack track = mNamedTrackCache.get(name.hashCode());
+ if (track == null || !track.getName().equals(name)) {
+ track = new NamedTrack(name, parentUuid);
+ mNamedTrackCache.put(name.hashCode(), track);
+ }
+ mExtra.addPerfettoPointer(track);
+ return this;
+ }
+
+ /**
+ * Adds the events to a counter track instead. This is required for
+ * setting counter values.
+ *
+ */
+ public Builder usingCounterTrack(String name, long parentUuid) {
+ checkParent();
+ CounterTrack track = mCounterTrackCache.get(name.hashCode());
+ if (track == null || !track.getName().equals(name)) {
+ track = new CounterTrack(name, parentUuid);
+ mCounterTrackCache.put(name.hashCode(), track);
+ }
+ mExtra.addPerfettoPointer(track);
+ return this;
+ }
+
+ /**
+ * Sets a long counter value on the event.
+ *
+ */
+ public Builder setCounter(long val) {
+ checkParent();
+ mCounterInt64.setValue(val);
+ mExtra.addPerfettoPointer(mCounterInt64);
+ return this;
+ }
+
+ /**
+ * Sets a double counter value on the event.
+ *
+ */
+ public Builder setCounter(double val) {
+ checkParent();
+ mCounterDouble.setValue(val);
+ mExtra.addPerfettoPointer(mCounterDouble);
+ return this;
+ }
+
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
+ public Builder addField(long id, long val) {
+ checkContainer();
+ FieldInt64 field = mFieldInt64Cache.get(FieldInt64::new);
+ field.setValue(id, val);
+ mCurrentContainer.addField(field);
+ return this;
+ }
+
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
+ public Builder addField(long id, double val) {
+ checkContainer();
+ FieldDouble field = mFieldDoubleCache.get(FieldDouble::new);
+ field.setValue(id, val);
+ mCurrentContainer.addField(field);
+ return this;
+ }
+
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
+ public Builder addField(long id, String val) {
+ checkContainer();
+ FieldString field = mFieldStringCache.get(FieldString::new);
+ field.setValue(id, val);
+ mCurrentContainer.addField(field);
+ return this;
+ }
+
+ /**
+ * Begins a proto field with field
+ * Fields can be added from this point and there must be a corresponding
+ * {@link endProto}.
+ *
+ * The proto field is a singleton and all proto fields get added inside the
+ * one {@link beginProto} and {@link endProto} within the {@link Builder}.
+ */
+ public Builder beginProto() {
+ checkParent();
+ mProto.clearFields();
+ mExtra.addPerfettoPointer(mProto);
+ return mBuilderCache.get(Builder::new).init(this, mProto);
+ }
+
+ /**
+ * Ends a proto field.
+ */
+ public Builder endProto() {
+ if (mParent == null || mCurrentContainer == null) {
+ throw new IllegalStateException("No proto to end");
+ }
+ return mParent;
+ }
+
+ /**
+ * Begins a nested proto field with field id {@code id}.
+ * Fields can be added from this point and there must be a corresponding
+ * {@link endNested}.
+ */
+ public Builder beginNested(long id) {
+ checkContainer();
+ FieldNested field = mFieldNestedCache.get(FieldNested::new);
+ field.setId(id);
+ mCurrentContainer.addField(field);
+ return mBuilderCache.get(Builder::new).init(this, field);
+ }
+
+ /**
+ * Ends a nested proto field.
+ */
+ public Builder endNested() {
+ if (mParent == null || mCurrentContainer == null) {
+ throw new IllegalStateException("No nested field to end");
+ }
+ return mParent;
+ }
+
+ /**
+ * Initializes a {@link Builder}.
+ */
+ public Builder init(Builder parent, FieldContainer container) {
+ mParent = parent;
+ mCurrentContainer = container;
+ mIsBuilt = false;
+
+ if (mParent == null) {
+ if (mExtra.mIsInUse) {
+ throw new IllegalStateException("Cannot create a new builder when another"
+ + " extra is in use");
+ }
+ mExtra.mIsInUse = true;
+ }
+ return this;
+ }
+
+ private void checkState() {
+ if (mIsBuilt) {
+ throw new IllegalStateException(
+ "This builder has already been used. Create a new builder for another event.");
+ }
+ }
+
+ private void checkParent() {
+ checkState();
+ if (mParent != null) {
+ throw new IllegalStateException(
+ "This builder has already been used. Create a new builder for another event.");
+ }
+ }
+
+ private void checkContainer() {
+ checkState();
+ if (mCurrentContainer == null) {
+ throw new IllegalStateException(
+ "Field operations must be within beginProto/endProto block");
+ }
+ }
+ }
+
+ /**
+ * Start a {@link Builder} to build a {@link PerfettoTrackEventExtra}.
+ */
+ public static Builder builder() {
+ return sTrackEventExtra.get().mBuilderCache.get(Builder::new).init(null, null);
+ }
+
+ private final RingBuffer<NamedTrack> mNamedTrackCache =
+ new RingBuffer(DEFAULT_EXTRA_CACHE_SIZE);
+ private final RingBuffer<CounterTrack> mCounterTrackCache =
+ new RingBuffer(DEFAULT_EXTRA_CACHE_SIZE);
+
+ private final RingBuffer<ArgInt64> mArgInt64Cache = new RingBuffer(DEFAULT_EXTRA_CACHE_SIZE);
+ private final RingBuffer<ArgBool> mArgBoolCache = new RingBuffer(DEFAULT_EXTRA_CACHE_SIZE);
+ private final RingBuffer<ArgDouble> mArgDoubleCache = new RingBuffer(DEFAULT_EXTRA_CACHE_SIZE);
+ private final RingBuffer<ArgString> mArgStringCache = new RingBuffer(DEFAULT_EXTRA_CACHE_SIZE);
+
+ private final Pool<FieldInt64> mFieldInt64Cache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<FieldDouble> mFieldDoubleCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<FieldString> mFieldStringCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<FieldNested> mFieldNestedCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<Flow> mFlowCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<Builder> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ PerfettoTrackEventExtra.class.getClassLoader(), native_delete());
+
+ private final long mPtr;
+ private static final String TAG = "PerfettoTrackEventExtra";
+
+ private PerfettoTrackEventExtra() {
+ mPtr = native_init();
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ /**
+ * Returns the native pointer.
+ */
+ public long getPtr() {
+ return mPtr;
+ }
+
+ /**
+ * Adds a pointer representing a track event parameter.
+ */
+ public void addPerfettoPointer(PerfettoPointer extra) {
+ native_add_arg(mPtr, extra.getPtr());
+ }
+
+ /**
+ * Resets the track event extra.
+ */
+ public void reset() {
+ native_clear_args(mPtr);
+ mIsInUse = false;
+ }
+
+ private CounterInt64 getCounterInt64() {
+ if (mCounterInt64 == null) {
+ mCounterInt64 = new CounterInt64();
+ }
+ return mCounterInt64;
+ }
+
+ private CounterDouble getCounterDouble() {
+ if (mCounterDouble == null) {
+ mCounterDouble = new CounterDouble();
+ }
+ return mCounterDouble;
+ }
+
+ private Proto getProto() {
+ if (mProto == null) {
+ mProto = new Proto();
+ }
+ return mProto;
+ }
+
+ private static final class Flow implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ Flow.class.getClassLoader(), native_delete());
+
+ private final long mPtr;
+ private final long mExtraPtr;
+
+ Flow() {
+ mPtr = native_init();
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ public void setProcessFlow(long type) {
+ native_set_process_flow(mPtr, type);
+ }
+
+ public void setProcessTerminatingFlow(long id) {
+ native_set_process_terminating_flow(mPtr, id);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native void native_set_process_flow(long ptr, long type);
+ @CriticalNative
+ private static native void native_set_process_terminating_flow(long ptr, long id);
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ }
+
+ private static class NamedTrack implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ NamedTrack.class.getClassLoader(), native_delete());
+
+ private final long mPtr;
+ private final long mExtraPtr;
+ private final String mName;
+
+ NamedTrack(String name, long parentUuid) {
+ mPtr = native_init(sNamedTrackId.incrementAndGet(), name, parentUuid);
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ mName = name;
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ @FastNative
+ private static native long native_init(long id, String name, long parentUuid);
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ }
+
+ private static final class CounterTrack implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ CounterTrack.class.getClassLoader(), native_delete());
+
+ private final long mPtr;
+ private final long mExtraPtr;
+ private final String mName;
+
+ CounterTrack(String name, long parentUuid) {
+ mPtr = native_init(name, parentUuid);
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ mName = name;
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ @FastNative
+ private static native long native_init(String name, long parentUuid);
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ }
+
+ private static final class CounterInt64 implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ CounterInt64.class.getClassLoader(), native_delete());
+
+ private final long mPtr;
+ private final long mExtraPtr;
+
+ CounterInt64() {
+ mPtr = native_init();
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ public void setValue(long value) {
+ native_set_value(mPtr, value);
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native void native_set_value(long ptr, long value);
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ }
+
+ private static final class CounterDouble implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ CounterDouble.class.getClassLoader(), native_delete());
+
+ private final long mPtr;
+ private final long mExtraPtr;
+
+ CounterDouble() {
+ mPtr = native_init();
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ public void setValue(double value) {
+ native_set_value(mPtr, value);
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native void native_set_value(long ptr, double value);
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ }
+
+ private static final class ArgInt64 implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ ArgInt64.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mExtraPtr;
+
+ private final String mName;
+
+ ArgInt64(String name) {
+ mPtr = native_init(name);
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ mName = name;
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setValue(long val) {
+ native_set_value(mPtr, val);
+ }
+
+ @FastNative
+ private static native long native_init(String name);
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @CriticalNative
+ private static native void native_set_value(long ptr, long val);
+ }
+
+ private static final class ArgBool implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ ArgBool.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mExtraPtr;
+
+ private final String mName;
+
+ ArgBool(String name) {
+ mPtr = native_init(name);
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ mName = name;
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setValue(boolean val) {
+ native_set_value(mPtr, val);
+ }
+
+ @FastNative
+ private static native long native_init(String name);
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @CriticalNative
+ private static native void native_set_value(long ptr, boolean val);
+ }
+
+ private static final class ArgDouble implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ ArgDouble.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mExtraPtr;
+
+ private final String mName;
+
+ ArgDouble(String name) {
+ mPtr = native_init(name);
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ mName = name;
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setValue(double val) {
+ native_set_value(mPtr, val);
+ }
+
+ @FastNative
+ private static native long native_init(String name);
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @CriticalNative
+ private static native void native_set_value(long ptr, double val);
+ }
+
+ private static final class ArgString implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ ArgString.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mExtraPtr;
+
+ private final String mName;
+
+ ArgString(String name) {
+ mPtr = native_init(name);
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ mName = name;
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setValue(String val) {
+ native_set_value(mPtr, val);
+ }
+
+ @FastNative
+ private static native long native_init(String name);
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @FastNative
+ private static native void native_set_value(long ptr, String val);
+ }
+
+ private static final class Proto implements PerfettoPointer, FieldContainer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ Proto.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mExtraPtr;
+
+ Proto() {
+ mPtr = native_init();
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ @Override
+ public void addField(PerfettoPointer field) {
+ native_add_field(mPtr, field.getPtr());
+ }
+
+ public void clearFields() {
+ native_clear_fields(mPtr);
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @CriticalNative
+ private static native void native_add_field(long ptr, long extraPtr);
+ @CriticalNative
+ private static native void native_clear_fields(long ptr);
+ }
+
+ private static final class FieldInt64 implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ FieldInt64.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mFieldPtr;
+
+ FieldInt64() {
+ mPtr = native_init();
+ mFieldPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mFieldPtr;
+ }
+
+ public void setValue(long id, long val) {
+ native_set_value(mPtr, id, val);
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @CriticalNative
+ private static native void native_set_value(long ptr, long id, long val);
+ }
+
+ private static final class FieldDouble implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ FieldDouble.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mFieldPtr;
+
+ FieldDouble() {
+ mPtr = native_init();
+ mFieldPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mFieldPtr;
+ }
+
+ public void setValue(long id, double val) {
+ native_set_value(mPtr, id, val);
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @CriticalNative
+ private static native void native_set_value(long ptr, long id, double val);
+ }
+
+ private static final class FieldString implements PerfettoPointer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ FieldString.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mFieldPtr;
+
+ FieldString() {
+ mPtr = native_init();
+ mFieldPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mFieldPtr;
+ }
+
+ public void setValue(long id, String val) {
+ native_set_value(mPtr, id, val);
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @FastNative
+ private static native void native_set_value(long ptr, long id, String val);
+ }
+
+ private static final class FieldNested implements PerfettoPointer, FieldContainer {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ FieldNested.class.getClassLoader(), native_delete());
+
+ // Private pointer holding Perfetto object with metadata
+ private final long mPtr;
+
+ // Public pointer to Perfetto object itself
+ private final long mFieldPtr;
+
+ FieldNested() {
+ mPtr = native_init();
+ mFieldPtr = native_get_extra_ptr(mPtr);
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
+
+ @Override
+ public long getPtr() {
+ return mFieldPtr;
+ }
+
+ @Override
+ public void addField(PerfettoPointer field) {
+ native_add_field(mPtr, field.getPtr());
+ }
+
+ public void setId(long id) {
+ native_set_id(mPtr, id);
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ @CriticalNative
+ private static native void native_add_field(long ptr, long extraPtr);
+ @CriticalNative
+ private static native void native_set_id(long ptr, long id);
+ }
+
+ @CriticalNative
+ private static native long native_init();
+ @CriticalNative
+ private static native long native_delete();
+ @CriticalNative
+ private static native void native_add_arg(long ptr, long extraPtr);
+ @CriticalNative
+ private static native void native_clear_args(long ptr);
+}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 2a467386569d..e24f08b7dfe5 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -340,6 +340,13 @@ flag {
flag {
namespace: "system_performance"
+ name: "perfetto_sdk_tracing_v2"
+ description: "Tracing using Perfetto SDK API."
+ bug: "303199244"
+}
+
+flag {
+ namespace: "system_performance"
name: "telemetry_apis_framework_initialization"
is_exported: true
description: "Control framework initialization APIs of telemetry APIs feature."
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 027113a75f6b..67790592a2ba 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -179,6 +179,8 @@ cc_library_shared_for_libandroid_runtime {
"android_os_NativeHandle.cpp",
"android_os_MemoryFile.cpp",
"android_os_MessageQueue.cpp",
+ "android_os_PerfettoTrace.cpp",
+ "android_os_PerfettoTrackEventExtra.cpp",
"android_os_Parcel.cpp",
"android_os_PerformanceHintManager.cpp",
"android_os_SELinux.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 78d69f0714e0..aea1734918d6 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -158,6 +158,8 @@ extern int register_android_os_SELinux(JNIEnv* env);
extern int register_android_os_storage_StorageManager(JNIEnv* env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
+extern int register_android_os_PerfettoTrace(JNIEnv* env);
+extern int register_android_os_PerfettoTrackEventExtra(JNIEnv* env);
extern int register_android_os_Trace(JNIEnv* env);
extern int register_android_os_FileObserver(JNIEnv *env);
extern int register_android_os_UEventObserver(JNIEnv* env);
@@ -1605,6 +1607,8 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_GraphicsEnvironment),
REG_JNI(register_android_os_MessageQueue),
REG_JNI(register_android_os_SELinux),
+ REG_JNI(register_android_os_PerfettoTrace),
+ REG_JNI(register_android_os_PerfettoTrackEventExtra),
REG_JNI(register_android_os_Trace),
REG_JNI(register_android_os_UEventObserver),
REG_JNI(register_android_net_LocalSocketImpl),
diff --git a/core/jni/android_os_PerfettoTrace.cpp b/core/jni/android_os_PerfettoTrace.cpp
new file mode 100644
index 000000000000..988aea722be3
--- /dev/null
+++ b/core/jni/android_os_PerfettoTrace.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_local_ref.h>
+#include <nativehelper/scoped_primitive_array.h>
+#include <nativehelper/scoped_utf_chars.h>
+#include <tracing_sdk.h>
+
+namespace android {
+template <typename T>
+inline static T* toPointer(jlong ptr) {
+ return reinterpret_cast<T*>(static_cast<uintptr_t>(ptr));
+}
+
+template <typename T>
+inline static jlong toJLong(T* ptr) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
+}
+
+static const char* fromJavaString(JNIEnv* env, jstring jstr) {
+ if (!jstr) return "";
+ ScopedUtfChars chars(env, jstr);
+
+ if (!chars.c_str()) {
+ ALOGE("Failed extracting string");
+ return "";
+ }
+
+ return chars.c_str();
+}
+
+static void android_os_PerfettoTrace_event(JNIEnv* env, jclass, jint type, jlong cat_ptr,
+ jstring name, jlong extra_ptr) {
+ ScopedUtfChars name_utf(env, name);
+ if (!name_utf.c_str()) {
+ ALOGE("Failed extracting string");
+ }
+
+ tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(cat_ptr);
+ tracing_perfetto::trace_event(type, category->get(), name_utf.c_str(),
+ toPointer<tracing_perfetto::Extra>(extra_ptr));
+}
+
+static jlong android_os_PerfettoTrace_get_process_track_uuid() {
+ return tracing_perfetto::get_process_track_uuid();
+}
+
+static jlong android_os_PerfettoTrace_get_thread_track_uuid(jlong tid) {
+ return tracing_perfetto::get_thread_track_uuid(tid);
+}
+
+static void android_os_PerfettoTrace_activate_trigger(JNIEnv* env, jclass, jstring name,
+ jint ttl_ms) {
+ ScopedUtfChars name_utf(env, name);
+ if (!name_utf.c_str()) {
+ ALOGE("Failed extracting string");
+ return;
+ }
+
+ tracing_perfetto::activate_trigger(name_utf.c_str(), static_cast<uint32_t>(ttl_ms));
+}
+
+static jlong android_os_PerfettoTraceCategory_init(JNIEnv* env, jclass, jstring name, jstring tag,
+ jstring severity) {
+ return toJLong(new tracing_perfetto::Category(fromJavaString(env, name),
+ fromJavaString(env, tag),
+ fromJavaString(env, severity)));
+}
+
+static jlong android_os_PerfettoTraceCategory_delete() {
+ return toJLong(&tracing_perfetto::Category::delete_category);
+}
+
+static void android_os_PerfettoTraceCategory_register(jlong ptr) {
+ tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(ptr);
+ category->register_category();
+}
+
+static void android_os_PerfettoTraceCategory_unregister(jlong ptr) {
+ tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(ptr);
+ category->unregister_category();
+}
+
+static jboolean android_os_PerfettoTraceCategory_is_enabled(jlong ptr) {
+ tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(ptr);
+ return category->is_category_enabled();
+}
+
+static jlong android_os_PerfettoTraceCategory_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(ptr);
+ return toJLong(category->get());
+}
+
+static const JNINativeMethod gCategoryMethods[] = {
+ {"native_init", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
+ (void*)android_os_PerfettoTraceCategory_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTraceCategory_delete},
+ {"native_register", "(J)V", (void*)android_os_PerfettoTraceCategory_register},
+ {"native_unregister", "(J)V", (void*)android_os_PerfettoTraceCategory_unregister},
+ {"native_is_enabled", "(J)Z", (void*)android_os_PerfettoTraceCategory_is_enabled},
+ {"native_get_extra_ptr", "(J)J", (void*)android_os_PerfettoTraceCategory_get_extra_ptr},
+};
+
+static const JNINativeMethod gTraceMethods[] =
+ {{"native_event", "(IJLjava/lang/String;J)V", (void*)android_os_PerfettoTrace_event},
+ {"native_get_process_track_uuid", "()J",
+ (void*)android_os_PerfettoTrace_get_process_track_uuid},
+ {"native_get_thread_track_uuid", "(J)J",
+ (void*)android_os_PerfettoTrace_get_thread_track_uuid},
+ {"native_activate_trigger", "(Ljava/lang/String;I)V",
+ (void*)android_os_PerfettoTrace_activate_trigger}};
+
+int register_android_os_PerfettoTrace(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace", gTraceMethods,
+ NELEM(gTraceMethods));
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace$Category", gCategoryMethods,
+ NELEM(gCategoryMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_os_PerfettoTrackEventExtra.cpp b/core/jni/android_os_PerfettoTrackEventExtra.cpp
new file mode 100644
index 000000000000..9adad7bca940
--- /dev/null
+++ b/core/jni/android_os_PerfettoTrackEventExtra.cpp
@@ -0,0 +1,536 @@
+/*
+ * 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.
+ */
+
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_utf_chars.h>
+#include <tracing_sdk.h>
+
+static constexpr ssize_t kMaxStrLen = 4096;
+namespace android {
+template <typename T>
+inline static T* toPointer(jlong ptr) {
+ return reinterpret_cast<T*>(static_cast<uintptr_t>(ptr));
+}
+
+template <typename T>
+inline static jlong toJLong(T* ptr) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
+}
+
+static const char* fromJavaString(JNIEnv* env, jstring jstr) {
+ if (!jstr) return "";
+ ScopedUtfChars chars(env, jstr);
+
+ if (!chars.c_str()) {
+ ALOGE("Failed extracting string");
+ return "";
+ }
+
+ return chars.c_str();
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgInt64_init(JNIEnv* env, jclass, jstring name) {
+ return toJLong(new tracing_perfetto::DebugArg<int64_t>(fromJavaString(env, name)));
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgBool_init(JNIEnv* env, jclass, jstring name) {
+ return toJLong(new tracing_perfetto::DebugArg<bool>(fromJavaString(env, name)));
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgDouble_init(JNIEnv* env, jclass, jstring name) {
+ return toJLong(new tracing_perfetto::DebugArg<double>(fromJavaString(env, name)));
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgString_init(JNIEnv* env, jclass, jstring name) {
+ return toJLong(new tracing_perfetto::DebugArg<const char*>(fromJavaString(env, name)));
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgInt64_delete() {
+ return toJLong(&tracing_perfetto::DebugArg<int64_t>::delete_arg);
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgBool_delete() {
+ return toJLong(&tracing_perfetto::DebugArg<bool>::delete_arg);
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgDouble_delete() {
+ return toJLong(&tracing_perfetto::DebugArg<double>::delete_arg);
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgString_delete() {
+ return toJLong(&tracing_perfetto::DebugArg<const char*>::delete_arg);
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgInt64_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::DebugArg<int64_t>* arg = toPointer<tracing_perfetto::DebugArg<int64_t>>(ptr);
+ return toJLong(arg->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgBool_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::DebugArg<bool>* arg = toPointer<tracing_perfetto::DebugArg<bool>>(ptr);
+ return toJLong(arg->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgDouble_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::DebugArg<double>* arg = toPointer<tracing_perfetto::DebugArg<double>>(ptr);
+ return toJLong(arg->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraArgString_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::DebugArg<const char*>* arg =
+ toPointer<tracing_perfetto::DebugArg<const char*>>(ptr);
+ return toJLong(arg->get());
+}
+
+static void android_os_PerfettoTrackEventExtraArgInt64_set_value(jlong ptr, jlong val) {
+ tracing_perfetto::DebugArg<int64_t>* arg = toPointer<tracing_perfetto::DebugArg<int64_t>>(ptr);
+ arg->set_value(val);
+}
+
+static void android_os_PerfettoTrackEventExtraArgBool_set_value(jlong ptr, jboolean val) {
+ tracing_perfetto::DebugArg<bool>* arg = toPointer<tracing_perfetto::DebugArg<bool>>(ptr);
+ arg->set_value(val);
+}
+
+static void android_os_PerfettoTrackEventExtraArgDouble_set_value(jlong ptr, jdouble val) {
+ tracing_perfetto::DebugArg<double>* arg = toPointer<tracing_perfetto::DebugArg<double>>(ptr);
+ arg->set_value(val);
+}
+
+static void android_os_PerfettoTrackEventExtraArgString_set_value(JNIEnv* env, jclass, jlong ptr,
+ jstring val) {
+ tracing_perfetto::DebugArg<const char*>* arg =
+ toPointer<tracing_perfetto::DebugArg<const char*>>(ptr);
+ arg->set_value(strdup(fromJavaString(env, val)));
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldInt64_init() {
+ return toJLong(new tracing_perfetto::ProtoField<int64_t>());
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldDouble_init() {
+ return toJLong(new tracing_perfetto::ProtoField<double>());
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldString_init() {
+ return toJLong(new tracing_perfetto::ProtoField<const char*>());
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldNested_init() {
+ return toJLong(new tracing_perfetto::ProtoFieldNested());
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldInt64_delete() {
+ return toJLong(&tracing_perfetto::ProtoField<int64_t>::delete_field);
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldDouble_delete() {
+ return toJLong(&tracing_perfetto::ProtoField<double>::delete_field);
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldString_delete() {
+ return toJLong(&tracing_perfetto::ProtoField<const char*>::delete_field);
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldNested_delete() {
+ return toJLong(&tracing_perfetto::ProtoFieldNested::delete_field);
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldInt64_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::ProtoField<int64_t>* field =
+ toPointer<tracing_perfetto::ProtoField<int64_t>>(ptr);
+ return toJLong(field->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldDouble_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::ProtoField<double>* field =
+ toPointer<tracing_perfetto::ProtoField<double>>(ptr);
+ return toJLong(field->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldString_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::ProtoField<const char*>* field =
+ toPointer<tracing_perfetto::ProtoField<const char*>>(ptr);
+ return toJLong(field->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraFieldNested_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::ProtoFieldNested* field = toPointer<tracing_perfetto::ProtoFieldNested>(ptr);
+ return toJLong(field->get());
+}
+
+static void android_os_PerfettoTrackEventExtraFieldInt64_set_value(jlong ptr, jlong id, jlong val) {
+ tracing_perfetto::ProtoField<int64_t>* field =
+ toPointer<tracing_perfetto::ProtoField<int64_t>>(ptr);
+ field->set_value(id, val);
+}
+
+static void android_os_PerfettoTrackEventExtraFieldDouble_set_value(jlong ptr, jlong id,
+ jdouble val) {
+ tracing_perfetto::ProtoField<double>* field =
+ toPointer<tracing_perfetto::ProtoField<double>>(ptr);
+ field->set_value(id, val);
+}
+
+static void android_os_PerfettoTrackEventExtraFieldString_set_value(JNIEnv* env, jclass, jlong ptr,
+ jlong id, jstring val) {
+ tracing_perfetto::ProtoField<const char*>* field =
+ toPointer<tracing_perfetto::ProtoField<const char*>>(ptr);
+ field->set_value(id, strdup(fromJavaString(env, val)));
+}
+
+static void android_os_PerfettoTrackEventExtraFieldNested_add_field(jlong field_ptr,
+ jlong arg_ptr) {
+ tracing_perfetto::ProtoFieldNested* field =
+ toPointer<tracing_perfetto::ProtoFieldNested>(field_ptr);
+ field->add_field(toPointer<PerfettoTeHlProtoField>(arg_ptr));
+}
+
+static void android_os_PerfettoTrackEventExtraFieldNested_set_id(jlong ptr, jlong id) {
+ tracing_perfetto::ProtoFieldNested* field = toPointer<tracing_perfetto::ProtoFieldNested>(ptr);
+ field->set_id(id);
+}
+
+static jlong android_os_PerfettoTrackEventExtraFlow_init() {
+ return toJLong(new tracing_perfetto::Flow());
+}
+
+static void android_os_PerfettoTrackEventExtraFlow_set_process_flow(jlong ptr, jlong id) {
+ tracing_perfetto::Flow* flow = toPointer<tracing_perfetto::Flow>(ptr);
+ flow->set_process_flow(id);
+}
+
+static void android_os_PerfettoTrackEventExtraFlow_set_process_terminating_flow(jlong ptr,
+ jlong id) {
+ tracing_perfetto::Flow* flow = toPointer<tracing_perfetto::Flow>(ptr);
+ flow->set_process_terminating_flow(id);
+}
+
+static jlong android_os_PerfettoTrackEventExtraFlow_delete() {
+ return toJLong(&tracing_perfetto::Flow::delete_flow);
+}
+
+static jlong android_os_PerfettoTrackEventExtraFlow_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::Flow* flow = toPointer<tracing_perfetto::Flow>(ptr);
+ return toJLong(flow->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraNamedTrack_init(JNIEnv* env, jclass, jlong id,
+ jstring name, jlong parent_uuid) {
+ return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, fromJavaString(env, name)));
+}
+
+static jlong android_os_PerfettoTrackEventExtraNamedTrack_delete() {
+ return toJLong(&tracing_perfetto::NamedTrack::delete_track);
+}
+
+static jlong android_os_PerfettoTrackEventExtraNamedTrack_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::NamedTrack* track = toPointer<tracing_perfetto::NamedTrack>(ptr);
+ return toJLong(track->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterTrack_init(JNIEnv* env, jclass, jstring name,
+ jlong parent_uuid) {
+ return toJLong(
+ new tracing_perfetto::RegisteredTrack(1, parent_uuid, fromJavaString(env, name), true));
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterTrack_delete() {
+ return toJLong(&tracing_perfetto::RegisteredTrack::delete_track);
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterTrack_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::RegisteredTrack* track = toPointer<tracing_perfetto::RegisteredTrack>(ptr);
+ return toJLong(track->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterInt64_init() {
+ return toJLong(new tracing_perfetto::Counter<int64_t>());
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterInt64_delete() {
+ return toJLong(&tracing_perfetto::Counter<int64_t>::delete_counter);
+}
+
+static void android_os_PerfettoTrackEventExtraCounterInt64_set_value(jlong ptr, jlong val) {
+ tracing_perfetto::Counter<int64_t>* counter =
+ toPointer<tracing_perfetto::Counter<int64_t>>(ptr);
+ counter->set_value(val);
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterInt64_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::Counter<int64_t>* counter =
+ toPointer<tracing_perfetto::Counter<int64_t>>(ptr);
+ return toJLong(counter->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterDouble_init() {
+ return toJLong(new tracing_perfetto::Counter<double>());
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterDouble_delete() {
+ return toJLong(&tracing_perfetto::Counter<double>::delete_counter);
+}
+
+static void android_os_PerfettoTrackEventExtraCounterDouble_set_value(jlong ptr, jdouble val) {
+ tracing_perfetto::Counter<double>* counter = toPointer<tracing_perfetto::Counter<double>>(ptr);
+ counter->set_value(val);
+}
+
+static jlong android_os_PerfettoTrackEventExtraCounterDouble_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::Counter<double>* counter = toPointer<tracing_perfetto::Counter<double>>(ptr);
+ return toJLong(counter->get());
+}
+
+static jlong android_os_PerfettoTrackEventExtra_init() {
+ return toJLong(new tracing_perfetto::Extra());
+}
+
+static jlong android_os_PerfettoTrackEventExtra_delete() {
+ return toJLong(&tracing_perfetto::Extra::delete_extra);
+}
+
+static void android_os_PerfettoTrackEventExtra_add_arg(jlong extra_ptr, jlong arg_ptr) {
+ tracing_perfetto::Extra* extra = toPointer<tracing_perfetto::Extra>(extra_ptr);
+ extra->push_extra(toPointer<PerfettoTeHlExtra>(arg_ptr));
+}
+
+static void android_os_PerfettoTrackEventExtra_clear_args(jlong ptr) {
+ tracing_perfetto::Extra* extra = toPointer<tracing_perfetto::Extra>(ptr);
+ extra->clear_extras();
+}
+
+static jlong android_os_PerfettoTrackEventExtraProto_init() {
+ return toJLong(new tracing_perfetto::Proto());
+}
+
+static jlong android_os_PerfettoTrackEventExtraProto_delete() {
+ return toJLong(&tracing_perfetto::Proto::delete_proto);
+}
+
+static jlong android_os_PerfettoTrackEventExtraProto_get_extra_ptr(jlong ptr) {
+ tracing_perfetto::Proto* proto = toPointer<tracing_perfetto::Proto>(ptr);
+ return toJLong(proto->get());
+}
+
+static void android_os_PerfettoTrackEventExtraProto_add_field(long proto_ptr, jlong arg_ptr) {
+ tracing_perfetto::Proto* proto = toPointer<tracing_perfetto::Proto>(proto_ptr);
+ proto->add_field(toPointer<PerfettoTeHlProtoField>(arg_ptr));
+}
+
+static void android_os_PerfettoTrackEventExtraProto_clear_fields(jlong ptr) {
+ tracing_perfetto::Proto* proto = toPointer<tracing_perfetto::Proto>(ptr);
+ proto->clear_fields();
+}
+
+static const JNINativeMethod gExtraMethods[] =
+ {{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtra_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtra_delete},
+ {"native_add_arg", "(JJ)V", (void*)android_os_PerfettoTrackEventExtra_add_arg},
+ {"native_clear_args", "(J)V", (void*)android_os_PerfettoTrackEventExtra_clear_args}};
+
+static const JNINativeMethod gProtoMethods[] =
+ {{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraProto_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraProto_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraProto_get_extra_ptr},
+ {"native_add_field", "(JJ)V", (void*)android_os_PerfettoTrackEventExtraProto_add_field},
+ {"native_clear_fields", "(J)V",
+ (void*)android_os_PerfettoTrackEventExtraProto_clear_fields}};
+
+static const JNINativeMethod gArgInt64Methods[] = {
+ {"native_init", "(Ljava/lang/String;)J",
+ (void*)android_os_PerfettoTrackEventExtraArgInt64_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraArgInt64_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraArgInt64_get_extra_ptr},
+ {"native_set_value", "(JJ)V", (void*)android_os_PerfettoTrackEventExtraArgInt64_set_value},
+};
+
+static const JNINativeMethod gArgBoolMethods[] = {
+ {"native_init", "(Ljava/lang/String;)J",
+ (void*)android_os_PerfettoTrackEventExtraArgBool_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraArgBool_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraArgBool_get_extra_ptr},
+ {"native_set_value", "(JZ)V", (void*)android_os_PerfettoTrackEventExtraArgBool_set_value},
+};
+
+static const JNINativeMethod gArgDoubleMethods[] = {
+ {"native_init", "(Ljava/lang/String;)J",
+ (void*)android_os_PerfettoTrackEventExtraArgDouble_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraArgDouble_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraArgDouble_get_extra_ptr},
+ {"native_set_value", "(JD)V", (void*)android_os_PerfettoTrackEventExtraArgDouble_set_value},
+};
+
+static const JNINativeMethod gArgStringMethods[] = {
+ {"native_init", "(Ljava/lang/String;)J",
+ (void*)android_os_PerfettoTrackEventExtraArgString_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraArgString_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraArgString_get_extra_ptr},
+ {"native_set_value", "(JLjava/lang/String;)V",
+ (void*)android_os_PerfettoTrackEventExtraArgString_set_value},
+};
+
+static const JNINativeMethod gFieldInt64Methods[] = {
+ {"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraFieldInt64_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraFieldInt64_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraFieldInt64_get_extra_ptr},
+ {"native_set_value", "(JJJ)V",
+ (void*)android_os_PerfettoTrackEventExtraFieldInt64_set_value},
+};
+
+static const JNINativeMethod gFieldDoubleMethods[] = {
+ {"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraFieldDouble_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraFieldDouble_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraFieldDouble_get_extra_ptr},
+ {"native_set_value", "(JJD)V",
+ (void*)android_os_PerfettoTrackEventExtraFieldDouble_set_value},
+};
+
+static const JNINativeMethod gFieldStringMethods[] = {
+ {"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraFieldString_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraFieldString_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraFieldString_get_extra_ptr},
+ {"native_set_value", "(JJLjava/lang/String;)V",
+ (void*)android_os_PerfettoTrackEventExtraFieldString_set_value},
+};
+
+static const JNINativeMethod gFieldNestedMethods[] =
+ {{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraFieldNested_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraFieldNested_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraFieldNested_get_extra_ptr},
+ {"native_add_field", "(JJ)V",
+ (void*)android_os_PerfettoTrackEventExtraFieldNested_add_field},
+ {"native_set_id", "(JJ)V", (void*)android_os_PerfettoTrackEventExtraFieldNested_set_id}};
+
+static const JNINativeMethod gFlowMethods[] = {
+ {"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraFlow_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraFlow_delete},
+ {"native_set_process_flow", "(JJ)V",
+ (void*)android_os_PerfettoTrackEventExtraFlow_set_process_flow},
+ {"native_set_process_terminating_flow", "(JJ)V",
+ (void*)android_os_PerfettoTrackEventExtraFlow_set_process_terminating_flow},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraFlow_get_extra_ptr},
+};
+
+static const JNINativeMethod gNamedTrackMethods[] = {
+ {"native_init", "(JLjava/lang/String;J)J",
+ (void*)android_os_PerfettoTrackEventExtraNamedTrack_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraNamedTrack_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraNamedTrack_get_extra_ptr},
+};
+
+static const JNINativeMethod gCounterTrackMethods[] =
+ {{"native_init", "(Ljava/lang/String;J)J",
+ (void*)android_os_PerfettoTrackEventExtraCounterTrack_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraCounterTrack_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraCounterTrack_get_extra_ptr}};
+
+static const JNINativeMethod gCounterInt64Methods[] =
+ {{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraCounterInt64_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraCounterInt64_delete},
+ {"native_set_value", "(JJ)V",
+ (void*)android_os_PerfettoTrackEventExtraCounterInt64_set_value},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraCounterInt64_get_extra_ptr}};
+
+static const JNINativeMethod gCounterDoubleMethods[] =
+ {{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraCounterDouble_init},
+ {"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtraCounterDouble_delete},
+ {"native_set_value", "(JD)V",
+ (void*)android_os_PerfettoTrackEventExtraCounterDouble_set_value},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)android_os_PerfettoTrackEventExtraCounterDouble_get_extra_ptr}};
+
+int register_android_os_PerfettoTrackEventExtra(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$ArgInt64",
+ gArgInt64Methods, NELEM(gArgInt64Methods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register arg int64 native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$ArgBool",
+ gArgBoolMethods, NELEM(gArgBoolMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register arg bool native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$ArgDouble",
+ gArgDoubleMethods, NELEM(gArgDoubleMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register arg double native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$ArgString",
+ gArgStringMethods, NELEM(gArgStringMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register arg string native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$FieldInt64",
+ gFieldInt64Methods, NELEM(gFieldInt64Methods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register field int64 native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$FieldDouble",
+ gFieldDoubleMethods, NELEM(gFieldDoubleMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register field double native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$FieldString",
+ gFieldStringMethods, NELEM(gFieldStringMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register field string native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$FieldNested",
+ gFieldNestedMethods, NELEM(gFieldNestedMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register field nested native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra", gExtraMethods,
+ NELEM(gExtraMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register extra native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$Proto", gProtoMethods,
+ NELEM(gProtoMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register proto native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$Flow", gFlowMethods,
+ NELEM(gFlowMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register flow native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$NamedTrack",
+ gNamedTrackMethods, NELEM(gNamedTrackMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register named track native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$CounterTrack",
+ gCounterTrackMethods, NELEM(gCounterTrackMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register counter track native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$CounterInt64",
+ gCounterInt64Methods, NELEM(gCounterInt64Methods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register counter int64 native methods.");
+
+ res = jniRegisterNativeMethods(env, "android/os/PerfettoTrackEventExtra$CounterDouble",
+ gCounterDoubleMethods, NELEM(gCounterDoubleMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register counter double native methods.");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index c67a0f9659f0..3ef3dfdc0183 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -119,6 +119,7 @@ android_test {
"android.view.flags-aconfig-java",
],
jni_libs: [
+ "libperfetto_trace_test_jni",
"libpowermanagertest_jni",
"libviewRootImplTest_jni",
"libworksourceparceltest_jni",
@@ -260,6 +261,7 @@ android_ravenwood_test {
"compatibility-device-util-axt-ravenwood",
"flag-junit",
"platform-test-annotations",
+ "perfetto_trace_java_protos",
"flag-junit",
"testng",
],
diff --git a/core/tests/coretests/jni/Android.bp b/core/tests/coretests/jni/Android.bp
index d6379ca8c3e6..798ec90eb884 100644
--- a/core/tests/coretests/jni/Android.bp
+++ b/core/tests/coretests/jni/Android.bp
@@ -111,3 +111,27 @@ cc_test_library {
],
gtest: false,
}
+
+cc_test_library {
+ name: "libperfetto_trace_test_jni",
+ srcs: [
+ "PerfettoTraceTest.cpp",
+ ],
+ static_libs: [
+ "perfetto_trace_protos",
+ "libtracing_perfetto_test_utils",
+ ],
+ shared_libs: [
+ "liblog",
+ "libnativehelper",
+ "libperfetto_c",
+ "libprotobuf-cpp-lite",
+ "libtracing_perfetto",
+ ],
+ stl: "libc++_static",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ gtest: false,
+}
diff --git a/core/tests/coretests/jni/PerfettoTraceTest.cpp b/core/tests/coretests/jni/PerfettoTraceTest.cpp
new file mode 100644
index 000000000000..41d02ed70c9a
--- /dev/null
+++ b/core/tests/coretests/jni/PerfettoTraceTest.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "PerfettoTraceTest"
+
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+
+#include "jni.h"
+#include "perfetto/public/abi/data_source_abi.h"
+#include "perfetto/public/abi/heap_buffer.h"
+#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/abi/tracing_session_abi.h"
+#include "perfetto/public/abi/track_event_abi.h"
+#include "perfetto/public/compiler.h"
+#include "perfetto/public/data_source.h"
+#include "perfetto/public/pb_decoder.h"
+#include "perfetto/public/producer.h"
+#include "perfetto/public/protos/config/trace_config.pzc.h"
+#include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h"
+#include "perfetto/public/protos/trace/test_event.pzc.h"
+#include "perfetto/public/protos/trace/trace.pzc.h"
+#include "perfetto/public/protos/trace/trace_packet.pzc.h"
+#include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h"
+#include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h"
+#include "perfetto/public/protos/trace/track_event/track_event.pzc.h"
+#include "perfetto/public/protos/trace/trigger.pzc.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+#include "protos/perfetto/trace/interned_data/interned_data.pb.h"
+#include "protos/perfetto/trace/trace.pb.h"
+#include "protos/perfetto/trace/trace_packet.pb.h"
+#include "tracing_perfetto.h"
+#include "utils.h"
+
+namespace android {
+using ::perfetto::protos::EventCategory;
+using ::perfetto::protos::EventName;
+using ::perfetto::protos::FtraceEvent;
+using ::perfetto::protos::FtraceEventBundle;
+using ::perfetto::protos::InternedData;
+using ::perfetto::protos::Trace;
+using ::perfetto::protos::TracePacket;
+
+using ::perfetto::shlib::test_utils::TracingSession;
+
+struct TracingSessionHolder {
+ TracingSession tracing_session;
+};
+
+static void nativeRegisterPerfetto([[maybe_unused]] JNIEnv* env, jclass /* obj */) {
+ tracing_perfetto::registerWithPerfetto(false /* test */);
+}
+
+static jlong nativeStartTracing(JNIEnv* env, jclass /* obj */, jbyteArray configBytes) {
+ jsize length = env->GetArrayLength(configBytes);
+ std::vector<uint8_t> data;
+ data.reserve(length);
+ env->GetByteArrayRegion(configBytes, 0, length, reinterpret_cast<jbyte*>(data.data()));
+
+ TracingSession session = TracingSession::FromBytes(data.data(), length);
+ TracingSessionHolder* holder = new TracingSessionHolder(std::move(session));
+
+ return reinterpret_cast<long>(holder);
+}
+
+static jbyteArray nativeStopTracing([[maybe_unused]] JNIEnv* env, jclass /* obj */, jlong ptr) {
+ TracingSessionHolder* holder = reinterpret_cast<TracingSessionHolder*>(ptr);
+
+ // Stop
+ holder->tracing_session.FlushBlocking(5000);
+ holder->tracing_session.StopBlocking();
+
+ std::vector<uint8_t> data = holder->tracing_session.ReadBlocking();
+
+ delete holder;
+
+ jbyteArray bytes = env->NewByteArray(data.size());
+ env->SetByteArrayRegion(bytes, 0, data.size(), reinterpret_cast<jbyte*>(data.data()));
+ return bytes;
+}
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+ JNIEnv* env;
+ const JNINativeMethod methodTable[] = {/* name, signature, funcPtr */
+ {"nativeStartTracing", "([B)J",
+ (void*)nativeStartTracing},
+ {"nativeStopTracing", "(J)[B", (void*)nativeStopTracing},
+ {"nativeRegisterPerfetto", "()V",
+ (void*)nativeRegisterPerfetto}};
+
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ return JNI_ERR;
+ }
+
+ jniRegisterNativeMethods(env, "android/os/PerfettoTraceTest", methodTable,
+ sizeof(methodTable) / sizeof(JNINativeMethod));
+
+ return JNI_VERSION_1_6;
+}
+
+} /* namespace android */
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
new file mode 100644
index 000000000000..292f7500479b
--- /dev/null
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -0,0 +1,600 @@
+/*
+ * 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.os;
+
+import static android.os.PerfettoTrace.Category;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo.LatencyComponentType.COMPONENT_INPUT_EVENT_LATENCY_BEGIN_RWH;
+import static perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo.LatencyComponentType.COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo;
+import perfetto.protos.ChromeLatencyInfoOuterClass.ChromeLatencyInfo.ComponentInfo;
+import perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
+import perfetto.protos.DebugAnnotationOuterClass.DebugAnnotation;
+import perfetto.protos.DebugAnnotationOuterClass.DebugAnnotationName;
+import perfetto.protos.InternedDataOuterClass.InternedData;
+import perfetto.protos.SourceLocationOuterClass.SourceLocation;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.BufferConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.DataSource;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.TriggerConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.TriggerConfig.Trigger;
+import perfetto.protos.TraceOuterClass.Trace;
+import perfetto.protos.TracePacketOuterClass.TracePacket;
+import perfetto.protos.TrackDescriptorOuterClass.TrackDescriptor;
+import perfetto.protos.TrackEventConfigOuterClass.TrackEventConfig;
+import perfetto.protos.TrackEventOuterClass.EventCategory;
+import perfetto.protos.TrackEventOuterClass.EventName;
+import perfetto.protos.TrackEventOuterClass.TrackEvent;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class is used to test the native tracing support. Run this test
+ * while tracing on the emulator and then run traceview to view the trace.
+ */
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = PerfettoTrace.class)
+public class PerfettoTraceTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation());
+
+ private static final String TAG = "PerfettoTraceTest";
+ private static final String FOO = "foo";
+ private static final String BAR = "bar";
+
+ private static final Category FOO_CATEGORY = new Category(FOO);
+
+ private final Set<String> mCategoryNames = new ArraySet<>();
+ private final Set<String> mEventNames = new ArraySet<>();
+ private final Set<String> mDebugAnnotationNames = new ArraySet<>();
+ private final Set<String> mTrackNames = new ArraySet<>();
+
+ static {
+ try {
+ System.loadLibrary("perfetto_trace_test_jni");
+ Log.i(TAG, "Successfully loaded trace_test native library");
+ } catch (UnsatisfiedLinkError ule) {
+ Log.w(TAG, "Could not load trace_test native library");
+ }
+ }
+
+ @Before
+ public void setUp() {
+ PerfettoTrace.register();
+ nativeRegisterPerfetto();
+ FOO_CATEGORY.register();
+
+ mCategoryNames.clear();
+ mEventNames.clear();
+ mDebugAnnotationNames.clear();
+ mTrackNames.clear();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testDebugAnnotations() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
+ .addFlow(2)
+ .addTerminatingFlow(3)
+ .addArg("long_val", 10000000000L)
+ .addArg("bool_val", true)
+ .addArg("double_val", 3.14)
+ .addArg("string_val", FOO)
+ .build();
+ PerfettoTrace.instant(FOO_CATEGORY, "event", extra);
+
+ byte[] traceBytes = nativeStopTracing(ptr);
+
+ Trace trace = Trace.parseFrom(traceBytes);
+
+ boolean hasTrackEvent = false;
+ boolean hasDebugAnnotations = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType())
+ && event.getDebugAnnotationsCount() == 4 && event.getFlowIdsCount() == 1
+ && event.getTerminatingFlowIdsCount() == 1) {
+ hasDebugAnnotations = true;
+
+ List<DebugAnnotation> annotations = event.getDebugAnnotationsList();
+
+ assertThat(annotations.get(0).getIntValue()).isEqualTo(10000000000L);
+ assertThat(annotations.get(1).getBoolValue()).isTrue();
+ assertThat(annotations.get(2).getDoubleValue()).isEqualTo(3.14);
+ assertThat(annotations.get(3).getStringValue()).isEqualTo(FOO);
+ }
+ }
+
+ collectInternedData(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasDebugAnnotations).isTrue();
+ assertThat(mCategoryNames).contains(FOO);
+
+ assertThat(mDebugAnnotationNames).contains("long_val");
+ assertThat(mDebugAnnotationNames).contains("bool_val");
+ assertThat(mDebugAnnotationNames).contains("double_val");
+ assertThat(mDebugAnnotationNames).contains("string_val");
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testDebugAnnotationsWithLamda() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrace.instant(FOO_CATEGORY, "event", e -> e.addArg("long_val", 123L));
+
+ byte[] traceBytes = nativeStopTracing(ptr);
+
+ Trace trace = Trace.parseFrom(traceBytes);
+
+ boolean hasTrackEvent = false;
+ boolean hasDebugAnnotations = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType())
+ && event.getDebugAnnotationsCount() == 1) {
+ hasDebugAnnotations = true;
+
+ List<DebugAnnotation> annotations = event.getDebugAnnotationsList();
+ assertThat(annotations.get(0).getIntValue()).isEqualTo(123L);
+ }
+ }
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasDebugAnnotations).isTrue();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testNamedTrack() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrackEventExtra beginExtra = PerfettoTrackEventExtra.builder()
+ .usingNamedTrack(FOO, PerfettoTrace.getProcessTrackUuid())
+ .build();
+ PerfettoTrace.begin(FOO_CATEGORY, "event", beginExtra);
+
+ PerfettoTrackEventExtra endExtra = PerfettoTrackEventExtra.builder()
+ .usingNamedTrack("bar", PerfettoTrace.getThreadTrackUuid(Process.myTid()))
+ .build();
+ PerfettoTrace.end(FOO_CATEGORY, endExtra);
+
+ Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+
+ boolean hasTrackEvent = false;
+ boolean hasTrackUuid = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_SLICE_BEGIN.equals(event.getType())
+ && event.hasTrackUuid()) {
+ hasTrackUuid = true;
+ }
+
+ if (TrackEvent.Type.TYPE_SLICE_END.equals(event.getType())
+ && event.hasTrackUuid()) {
+ hasTrackUuid &= true;
+ }
+ }
+
+ collectInternedData(packet);
+ collectTrackNames(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasTrackUuid).isTrue();
+ assertThat(mCategoryNames).contains(FOO);
+ assertThat(mTrackNames).contains(FOO);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testCounter() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrackEventExtra intExtra = PerfettoTrackEventExtra.builder()
+ .usingCounterTrack(FOO, PerfettoTrace.getProcessTrackUuid())
+ .setCounter(16)
+ .build();
+ PerfettoTrace.counter(FOO_CATEGORY, intExtra);
+
+ PerfettoTrackEventExtra doubleExtra = PerfettoTrackEventExtra.builder()
+ .usingCounterTrack("bar", PerfettoTrace.getProcessTrackUuid())
+ .setCounter(3.14)
+ .build();
+ PerfettoTrace.counter(FOO_CATEGORY, doubleExtra);
+
+ Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+
+ boolean hasTrackEvent = false;
+ boolean hasCounterValue = false;
+ boolean hasDoubleCounterValue = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getCounterValue() == 16) {
+ hasCounterValue = true;
+ }
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getDoubleCounterValue() == 3.14) {
+ hasDoubleCounterValue = true;
+ }
+ }
+
+ collectTrackNames(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasCounterValue).isTrue();
+ assertThat(hasDoubleCounterValue).isTrue();
+ assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains(BAR);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testProto() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrackEventExtra extra5 = PerfettoTrackEventExtra.builder()
+ .beginProto()
+ .beginNested(33L)
+ .addField(4L, 2L)
+ .addField(3, "ActivityManagerService.java:11489")
+ .endNested()
+ .addField(2001, "AIDL::IActivityManager")
+ .endProto()
+ .build();
+ PerfettoTrace.instant(FOO_CATEGORY, "event_proto", extra5);
+
+ byte[] traceBytes = nativeStopTracing(ptr);
+
+ Trace trace = Trace.parseFrom(traceBytes);
+
+ boolean hasTrackEvent = false;
+ boolean hasSourceLocation = false;
+
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType())
+ && event.hasSourceLocation()) {
+ SourceLocation loc = event.getSourceLocation();
+ if ("ActivityManagerService.java:11489".equals(loc.getFunctionName())
+ && loc.getLineNumber() == 2) {
+ hasSourceLocation = true;
+ }
+ }
+ }
+
+ collectInternedData(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasSourceLocation).isTrue();
+ assertThat(mCategoryNames).contains(FOO);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testProtoNested() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrackEventExtra extra6 = PerfettoTrackEventExtra.builder()
+ .beginProto()
+ .beginNested(29L)
+ .beginNested(4L)
+ .addField(1L, 2)
+ .addField(2L, 20000)
+ .endNested()
+ .beginNested(4L)
+ .addField(1L, 1)
+ .addField(2L, 40000)
+ .endNested()
+ .endNested()
+ .endProto()
+ .build();
+ PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested", extra6);
+
+ byte[] traceBytes = nativeStopTracing(ptr);
+
+ Trace trace = Trace.parseFrom(traceBytes);
+
+ boolean hasTrackEvent = false;
+ boolean hasChromeLatencyInfo = false;
+
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_INSTANT.equals(event.getType())
+ && event.hasChromeLatencyInfo()) {
+ ChromeLatencyInfo latencyInfo = event.getChromeLatencyInfo();
+ if (latencyInfo.getComponentInfoCount() == 2) {
+ hasChromeLatencyInfo = true;
+ ComponentInfo cmpInfo1 = latencyInfo.getComponentInfo(0);
+ assertThat(cmpInfo1.getComponentType())
+ .isEqualTo(COMPONENT_INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL);
+ assertThat(cmpInfo1.getTimeUs()).isEqualTo(20000);
+
+ ComponentInfo cmpInfo2 = latencyInfo.getComponentInfo(1);
+ assertThat(cmpInfo2.getComponentType())
+ .isEqualTo(COMPONENT_INPUT_EVENT_LATENCY_BEGIN_RWH);
+ assertThat(cmpInfo2.getTimeUs()).isEqualTo(40000);
+ }
+ }
+ }
+
+ collectInternedData(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasChromeLatencyInfo).isTrue();
+ assertThat(mCategoryNames).contains(FOO);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testActivateTrigger() throws Exception {
+ TraceConfig traceConfig = getTriggerTraceConfig(FOO, FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder().build();
+ PerfettoTrace.instant(FOO_CATEGORY, "event_trigger", extra);
+
+ PerfettoTrace.activateTrigger(FOO, 1000);
+
+ byte[] traceBytes = nativeStopTracing(ptr);
+
+ Trace trace = Trace.parseFrom(traceBytes);
+
+ boolean hasTrackEvent = false;
+ boolean hasChromeLatencyInfo = false;
+
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ }
+
+ collectInternedData(packet);
+ }
+
+ assertThat(mCategoryNames).contains(FOO);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testMultipleExtras() throws Exception {
+ boolean hasException = false;
+ try {
+ PerfettoTrackEventExtra.builder();
+
+ // Unclosed extra will throw an exception here
+ PerfettoTrackEventExtra.builder();
+ } catch (Exception e) {
+ hasException = true;
+ }
+
+ try {
+ PerfettoTrackEventExtra.builder().build();
+
+ // Closed extra but unused (reset hasn't been called internally) will throw an exception
+ // here.
+ PerfettoTrackEventExtra.builder();
+ } catch (Exception e) {
+ hasException &= true;
+ }
+
+ assertThat(hasException).isTrue();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testRegister() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(BAR);
+
+ Category barCategory = new Category(BAR);
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrackEventExtra beforeExtra = PerfettoTrackEventExtra.builder()
+ .addArg("before", 1)
+ .build();
+ PerfettoTrace.instant(barCategory, "event", beforeExtra);
+
+ barCategory.register();
+
+ PerfettoTrackEventExtra afterExtra = PerfettoTrackEventExtra.builder()
+ .addArg("after", 1)
+ .build();
+ PerfettoTrace.instant(barCategory, "event", afterExtra);
+
+ byte[] traceBytes = nativeStopTracing(ptr);
+
+ Trace trace = Trace.parseFrom(traceBytes);
+
+ boolean hasTrackEvent = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+ }
+
+ collectInternedData(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(mCategoryNames).contains(BAR);
+
+ assertThat(mDebugAnnotationNames).contains("after");
+ assertThat(mDebugAnnotationNames).doesNotContain("before");
+ }
+
+ private static native long nativeStartTracing(byte[] config);
+ private static native void nativeRegisterPerfetto();
+ private static native byte[] nativeStopTracing(long ptr);
+
+ private TrackEvent getTrackEvent(Trace trace, int idx) {
+ int curIdx = 0;
+ for (TracePacket packet: trace.getPacketList()) {
+ if (packet.hasTrackEvent()) {
+ if (curIdx++ == idx) {
+ return packet.getTrackEvent();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private TraceConfig getTraceConfig(String cat) {
+ BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build();
+ TrackEventConfig trackEventConfig = TrackEventConfig
+ .newBuilder()
+ .addEnabledCategories(cat)
+ .build();
+ DataSourceConfig dsConfig = DataSourceConfig
+ .newBuilder()
+ .setName("track_event")
+ .setTargetBuffer(0)
+ .setTrackEventConfig(trackEventConfig)
+ .build();
+ DataSource ds = DataSource.newBuilder().setConfig(dsConfig).build();
+ TraceConfig traceConfig = TraceConfig
+ .newBuilder()
+ .addBuffers(bufferConfig)
+ .addDataSources(ds)
+ .build();
+ return traceConfig;
+ }
+
+ private TraceConfig getTriggerTraceConfig(String cat, String triggerName) {
+ BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build();
+ TrackEventConfig trackEventConfig = TrackEventConfig
+ .newBuilder()
+ .addEnabledCategories(cat)
+ .build();
+ DataSourceConfig dsConfig = DataSourceConfig
+ .newBuilder()
+ .setName("track_event")
+ .setTargetBuffer(0)
+ .setTrackEventConfig(trackEventConfig)
+ .build();
+ DataSource ds = DataSource.newBuilder().setConfig(dsConfig).build();
+ Trigger trigger = Trigger.newBuilder().setName(triggerName).build();
+ TriggerConfig triggerConfig = TriggerConfig
+ .newBuilder()
+ .setTriggerMode(TriggerConfig.TriggerMode.STOP_TRACING)
+ .setTriggerTimeoutMs(1000)
+ .addTriggers(trigger)
+ .build();
+ TraceConfig traceConfig = TraceConfig
+ .newBuilder()
+ .addBuffers(bufferConfig)
+ .addDataSources(ds)
+ .setTriggerConfig(triggerConfig)
+ .build();
+ return traceConfig;
+ }
+
+ private void collectInternedData(TracePacket packet) {
+ if (!packet.hasInternedData()) {
+ return;
+ }
+
+ InternedData data = packet.getInternedData();
+
+ for (EventCategory cat : data.getEventCategoriesList()) {
+ mCategoryNames.add(cat.getName());
+ }
+ for (EventName ev : data.getEventNamesList()) {
+ mEventNames.add(ev.getName());
+ }
+ for (DebugAnnotationName dbg : data.getDebugAnnotationNamesList()) {
+ mDebugAnnotationNames.add(dbg.getName());
+ }
+ }
+
+ private void collectTrackNames(TracePacket packet) {
+ if (!packet.hasTrackDescriptor()) {
+ return;
+ }
+ TrackDescriptor desc = packet.getTrackDescriptor();
+ mTrackNames.add(desc.getName());
+ }
+}