Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad0b0ed2 authored by Zimuzo Ezeozue's avatar Zimuzo Ezeozue
Browse files

Improve PerfettoTrace API

The old API was of the form:

PerfettoTrace.instant(category, name, extra).

This was not ideal for a few reasons:
1. Initializing the thread local extra was decoupled from the call
to emit an event and could be error prone if people initialize an
extra away from the point where it's emitted.

2. This call would ideally be wrapped around a check for isTraceEnabled
to avoid paying the cost of initializing the method parameters.

There was an alternative API that accepted a lambda, but that incurs
an object allocation on each call which is bad for the GC.

The new API is of the form:
PerfettoTrace.instant(category, name)
  .addArg(String val, Object... args)
  .emit().

This addresses both problems mentioned above. When tracing is disabled,
each intermediat call returns a NoOpBuilder that just returns 'this'.

Also fixed use-after-free with JNI string extraction.

Bug: 303199244
Flag: android.os.perfetto_sdk_tracing_v2
Test: atest PerfettoTraceTest

Change-Id: I6e732c58275d6ee00bc7c492a804ac32e5bcb47c
parent 7a78b07a
Loading
Loading
Loading
Loading
+44 −126
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ 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
@@ -72,7 +71,7 @@ public final class PerfettoTrace {
         * @param name The category name.
         */
        public Category(String name) {
            this(name, null, null);
            this(name, "", "");
        }

        /**
@@ -82,7 +81,7 @@ public final class PerfettoTrace {
         * @param tag An atrace tag name that this category maps to.
         */
        public Category(String name, String tag) {
            this(name, tag, null);
            this(name, tag, "");
        }

        /**
@@ -155,9 +154,6 @@ public final class PerfettoTrace {
        }
    }

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

@@ -167,179 +163,101 @@ public final class PerfettoTrace {
    @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) {
    public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) {
        if (!category.isEnabled()) {
            return;
        }

        native_event(PERFETTO_TE_TYPE_SLICE_BEGIN, category.getPtr(), eventName, extra.getPtr());
        extra.reset();
            return PerfettoTrackEventExtra.noOpBuilder();
        }

    /**
     * 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());
        return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_INSTANT, category)
            .setEventName(eventName);
    }

    /**
     * Writes a trace message to indicate the start of a given section of code.
     *
     * @param category The perfetto category pointer.
     * @param category The perfetto category.
     * @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) {
    public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) {
        if (!category.isEnabled()) {
            return;
            return PerfettoTrackEventExtra.noOpBuilder();
        }

        native_event(PERFETTO_TE_TYPE_SLICE_END, category.getPtr(), "", extra.getPtr());
        extra.reset();
        return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
            .setEventName(eventName);
    }

    /**
     * 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.
     * @param category The perfetto category.
     */
    public static void end(Category category,
            Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
        PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
        extraConfig.accept(extra);
        end(category, extra.build());
    public static PerfettoTrackEventExtra.Builder end(Category category) {
        if (!category.isEnabled()) {
            return PerfettoTrackEventExtra.noOpBuilder();
        }

    /**
     * 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());
        return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_END, category);
    }

    /**
     * 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.
     * @param category The perfetto category.
     * @param value The value of the counter.
     */
    public static void counter(Category category, PerfettoTrackEventExtra extra) {
    public static PerfettoTrackEventExtra.Builder counter(Category category, long value) {
        if (!category.isEnabled()) {
            return;
            return PerfettoTrackEventExtra.noOpBuilder();
        }

        native_event(PERFETTO_TE_TYPE_COUNTER, category.getPtr(), "", extra.getPtr());
        extra.reset();
        return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
            .setCounter(value);
    }

    /**
     * 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.
     * @param category The perfetto category.
     * @param value The value of the counter.
     * @param trackName The trackName for the event.
     */
    public static void counter(Category category,
            Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
        PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
        extraConfig.accept(extra);
        counter(category, extra.build());
    public static PerfettoTrackEventExtra.Builder counter(
            Category category, long value, String trackName) {
        return counter(category, value).usingProcessCounterTrack(trackName);
    }

    /**
     * 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 category The perfetto category.
     * @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);
    public static PerfettoTrackEventExtra.Builder counter(Category category, double value) {
        if (!category.isEnabled()) {
            return PerfettoTrackEventExtra.noOpBuilder();
        }

        return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
            .setCounter(value);
    }

    /**
     * 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 category The perfetto category.
     * @param value The value of the counter.
     * @param trackName The trackName for the event.
     */
    public static void counter(Category category, String trackName, double value) {
        PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
                .usingCounterTrack(trackName, PerfettoTrace.getProcessTrackUuid())
                .setCounter(value)
                .build();
        counter(category, extra);
    public static PerfettoTrackEventExtra.Builder counter(
            Category category, double value, String trackName) {
        return counter(category, value).usingProcessCounterTrack(trackName);
    }

    /**
@@ -360,7 +278,7 @@ public final class PerfettoTrace {
     * Returns the process track uuid that can be used as a parent track uuid.
     */
    public static long getProcessTrackUuid() {
        if (IS_FLAG_ENABLED) {
        if (!IS_FLAG_ENABLED) {
            return 0;
        }
        return native_get_process_track_uuid();
@@ -370,7 +288,7 @@ public final class PerfettoTrace {
     * 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) {
        if (!IS_FLAG_ENABLED) {
            return 0;
        }
        return native_get_thread_track_uuid(tid);
@@ -380,7 +298,7 @@ public final class PerfettoTrace {
     * Activates a trigger by name {@code triggerName} with expiry in {@code ttlMs}.
     */
    public static void activateTrigger(String triggerName, int ttlMs) {
        if (IS_FLAG_ENABLED) {
        if (!IS_FLAG_ENABLED) {
            return;
        }
        native_activate_trigger(triggerName, ttlMs);
+377 −104

File changed.

Preview size limit exceeded, changes collapsed.

+12 −37
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <nativehelper/scoped_local_ref.h>
#include <nativehelper/scoped_primitive_array.h>
#include <nativehelper/scoped_utf_chars.h>
#include <nativehelper/utils.h>
#include <tracing_sdk.h>

namespace android {
@@ -36,30 +37,6 @@ 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();
}
@@ -70,20 +47,18 @@ static jlong android_os_PerfettoTrace_get_thread_track_uuid(jlong 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));
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name);
    tracing_perfetto::activate_trigger(name_chars.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)));
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
    ScopedUtfChars tag_chars = GET_UTF_OR_RETURN(env, tag);
    ScopedUtfChars severity_chars = GET_UTF_OR_RETURN(env, severity);

    return toJLong(new tracing_perfetto::Category(name_chars.c_str(), tag_chars.c_str(),
                                                  severity_chars.c_str()));
}

static jlong android_os_PerfettoTraceCategory_delete() {
@@ -121,8 +96,7 @@ static const JNINativeMethod gCategoryMethods[] = {
};

static const JNINativeMethod gTraceMethods[] =
        {{"native_event", "(IJLjava/lang/String;J)V", (void*)android_os_PerfettoTrace_event},
         {"native_get_process_track_uuid", "()J",
        {{"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},
@@ -132,10 +106,11 @@ static const JNINativeMethod gTraceMethods[] =
int register_android_os_PerfettoTrace(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace", gTraceMethods,
                                       NELEM(gTraceMethods));
    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register perfetto native methods.");

    res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace$Category", gCategoryMethods,
                                   NELEM(gCategoryMethods));
    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register category native methods.");

    return 0;
}
+32 −22
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/scoped_utf_chars.h>
#include <nativehelper/utils.h>
#include <tracing_sdk.h>

static constexpr ssize_t kMaxStrLen = 4096;
@@ -34,32 +35,24 @@ 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)));
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
    return toJLong(new tracing_perfetto::DebugArg<int64_t>(name_chars.c_str()));
}

static jlong android_os_PerfettoTrackEventExtraArgBool_init(JNIEnv* env, jclass, jstring name) {
    return toJLong(new tracing_perfetto::DebugArg<bool>(fromJavaString(env, name)));
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
    return toJLong(new tracing_perfetto::DebugArg<bool>(name_chars.c_str()));
}

static jlong android_os_PerfettoTrackEventExtraArgDouble_init(JNIEnv* env, jclass, jstring name) {
    return toJLong(new tracing_perfetto::DebugArg<double>(fromJavaString(env, name)));
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
    return toJLong(new tracing_perfetto::DebugArg<double>(name_chars.c_str()));
}

static jlong android_os_PerfettoTrackEventExtraArgString_init(JNIEnv* env, jclass, jstring name) {
    return toJLong(new tracing_perfetto::DebugArg<const char*>(fromJavaString(env, name)));
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
    return toJLong(new tracing_perfetto::DebugArg<const char*>(name_chars.c_str()));
}

static jlong android_os_PerfettoTrackEventExtraArgInt64_delete() {
@@ -116,9 +109,11 @@ static void android_os_PerfettoTrackEventExtraArgDouble_set_value(jlong ptr, jdo

static void android_os_PerfettoTrackEventExtraArgString_set_value(JNIEnv* env, jclass, jlong ptr,
                                                                  jstring val) {
    ScopedUtfChars val_chars = GET_UTF_OR_RETURN_VOID(env, val);

    tracing_perfetto::DebugArg<const char*>* arg =
            toPointer<tracing_perfetto::DebugArg<const char*>>(ptr);
    arg->set_value(strdup(fromJavaString(env, val)));
    arg->set_value(strdup(val_chars.c_str()));
}

static jlong android_os_PerfettoTrackEventExtraFieldInt64_init() {
@@ -191,9 +186,11 @@ static void android_os_PerfettoTrackEventExtraFieldDouble_set_value(jlong ptr, j

static void android_os_PerfettoTrackEventExtraFieldString_set_value(JNIEnv* env, jclass, jlong ptr,
                                                                    jlong id, jstring val) {
    ScopedUtfChars val_chars = GET_UTF_OR_RETURN_VOID(env, val);

    tracing_perfetto::ProtoField<const char*>* field =
            toPointer<tracing_perfetto::ProtoField<const char*>>(ptr);
    field->set_value(id, strdup(fromJavaString(env, val)));
    field->set_value(id, strdup(val_chars.c_str()));
}

static void android_os_PerfettoTrackEventExtraFieldNested_add_field(jlong field_ptr,
@@ -234,7 +231,8 @@ static jlong android_os_PerfettoTrackEventExtraFlow_get_extra_ptr(jlong ptr) {

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)));
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
    return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, name_chars.c_str()));
}

static jlong android_os_PerfettoTrackEventExtraNamedTrack_delete() {
@@ -248,8 +246,9 @@ static jlong android_os_PerfettoTrackEventExtraNamedTrack_get_extra_ptr(jlong pt

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));
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);

    return toJLong(new tracing_perfetto::RegisteredTrack(1, parent_uuid, name_chars.c_str(), true));
}

static jlong android_os_PerfettoTrackEventExtraCounterTrack_delete() {
@@ -317,6 +316,15 @@ static void android_os_PerfettoTrackEventExtra_clear_args(jlong ptr) {
    extra->clear_extras();
}

static void android_os_PerfettoTrackEventExtra_emit(JNIEnv* env, jclass, jint type, jlong cat_ptr,
                                                    jstring name, jlong extra_ptr) {
    ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name);

    tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(cat_ptr);
    tracing_perfetto::trace_event(type, category->get(), name_chars.c_str(),
                                  toPointer<tracing_perfetto::Extra>(extra_ptr));
}

static jlong android_os_PerfettoTrackEventExtraProto_init() {
    return toJLong(new tracing_perfetto::Proto());
}
@@ -344,7 +352,9 @@ 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}};
         {"native_clear_args", "(J)V", (void*)android_os_PerfettoTrackEventExtra_clear_args},
         {"native_emit", "(IJLjava/lang/String;J)V",
          (void*)android_os_PerfettoTrackEventExtra_emit}};

static const JNINativeMethod gProtoMethods[] =
        {{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraProto_init},
+162 −64

File changed.

Preview size limit exceeded, changes collapsed.