diff options
| -rw-r--r-- | core/java/android/os/PerfettoTrace.java | 395 | ||||
| -rw-r--r-- | core/java/android/os/PerfettoTrackEventExtra.java | 1081 | ||||
| -rw-r--r-- | core/java/android/os/flags.aconfig | 7 | ||||
| -rw-r--r-- | core/jni/Android.bp | 2 | ||||
| -rw-r--r-- | core/jni/AndroidRuntime.cpp | 4 | ||||
| -rw-r--r-- | core/jni/android_os_PerfettoTrace.cpp | 143 | ||||
| -rw-r--r-- | core/jni/android_os_PerfettoTrackEventExtra.cpp | 536 | ||||
| -rw-r--r-- | core/tests/coretests/Android.bp | 2 | ||||
| -rw-r--r-- | core/tests/coretests/jni/Android.bp | 24 | ||||
| -rw-r--r-- | core/tests/coretests/jni/PerfettoTraceTest.cpp | 117 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/PerfettoTraceTest.java | 600 |
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()); + } +} |