summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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());
+ }
+}