Loading core/java/android/os/PerfettoTrackEventExtra.java +17 −4 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; Loading Loading @@ -50,6 +52,7 @@ public final class PerfettoTrackEventExtra { private static final Supplier<FieldString> sFieldStringSupplier = FieldString::new; private static final Supplier<FieldNested> sFieldNestedSupplier = FieldNested::new; private final List<PerfettoPointer> mPendingPointers = new ArrayList<>(); private CounterInt64 mCounterInt64; private CounterDouble mCounterDouble; private Proto mProto; Loading Loading @@ -592,7 +595,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldInt64 field = mFieldInt64Cache.get(sFieldInt64Supplier); field.setValue(id, val); mCurrentContainer.addField(field); mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } Loading @@ -601,7 +604,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldDouble field = mFieldDoubleCache.get(sFieldDoubleSupplier); field.setValue(id, val); mCurrentContainer.addField(field); mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } Loading @@ -610,7 +613,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldString field = mFieldStringCache.get(sFieldStringSupplier); field.setValue(id, val); mCurrentContainer.addField(field); mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } Loading @@ -635,7 +638,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldNested field = mFieldNestedCache.get(sFieldNestedSupplier); field.setId(id); mCurrentContainer.addField(field); mExtra.addPerfettoPointer(mCurrentContainer, field); return mBuilderCache.get(sBuilderSupplier).initInternal(this, field); } Loading Loading @@ -735,6 +738,15 @@ public final class PerfettoTrackEventExtra { */ public void addPerfettoPointer(PerfettoPointer extra) { native_add_arg(mPtr, extra.getPtr()); mPendingPointers.add(extra); } /** * Adds a pointer representing a track event parameter to the {@code container}. */ public void addPerfettoPointer(FieldContainer container, PerfettoPointer extra) { container.addField(extra); mPendingPointers.add(extra); } /** Loading @@ -742,6 +754,7 @@ public final class PerfettoTrackEventExtra { */ public void reset() { native_clear_args(mPtr); mPendingPointers.clear(); } private CounterInt64 getCounterInt64() { Loading core/jni/android_os_PerfettoTrackEventExtra.cpp +143 −23 Original line number Diff line number Diff line Loading @@ -23,7 +23,8 @@ #include <nativehelper/utils.h> #include <tracing_sdk.h> static constexpr ssize_t kMaxStrLen = 4096; #include <list> namespace android { template <typename T> inline static T* toPointer(jlong ptr) { Loading @@ -35,24 +36,145 @@ inline static jlong toJLong(T* ptr) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr)); } /** * @brief A thread-safe utility class for converting Java UTF-16 strings to ASCII in JNI * environment. * * StringBuffer provides efficient conversion of Java strings to ASCII with optimized memory * handling. * It uses a two-tiered buffering strategy: * 1. A fast path using pre-allocated thread-local buffers for strings up to 128 characters * 2. A fallback path using dynamic allocation for longer strings * * Non-ASCII characters (>255) are replaced with '?' during conversion. The class maintains * thread safety through thread-local storage and provides zero-copy string views for optimal * performance. * * Memory Management: * - Uses fixed-size thread-local buffers for both UTF-16 and ASCII characters * - Overflow strings are stored in a thread-local list to maintain valid string views * - Avoids unnecessary allocations in the common case of small strings * * Usage example: * @code * JNIEnv* env = ...; * jstring java_string = ...; * std::string_view ascii = StringBuffer::utf16_to_ascii(env, java_string); * // Use the ASCII string... * StringBuffer::reset(); // Clean up when done * @endcode * * Thread Safety: All methods are thread-safe due to thread-local storage. */ class StringBuffer { private: static constexpr size_t BASE_SIZE = 128; // Temporarily stores the UTF-16 characters retrieved from the Java // string before they are converted to ASCII. static thread_local inline char char_buffer[BASE_SIZE]; // For fast-path conversions when the resulting ASCII string fits within // the pre-allocated space. All ascii strings in a trace event will be stored // here until emitted. static thread_local inline jchar jchar_buffer[BASE_SIZE]; // When the fast-path conversion is not possible (because char_buffer // doesn't have enough space), the converted ASCII string is stored // in this list. We use list here to avoid moving the strings on resize // with vector. This way, we can give out string_views from the stored strings. // The additional overhead from list node allocations is fine cos we are already // in an extremely unlikely path here and there are other bigger problems if here. static thread_local inline std::list<std::string> overflow_strings; // current offset into the char_buffer. static thread_local inline size_t current_offset{0}; // This allows us avoid touching the overflow_strings directly in the fast path. // Touching it causes some thread local init routine to run which shows up in profiles. static thread_local inline bool is_overflow_strings_empty = true; static void copy_utf16_to_ascii(const jchar* src, size_t len, char* dst, JNIEnv* env, jstring str) { std::transform(src, src + len, dst, [](jchar c) { return (c <= 0xFF) ? static_cast<char>(c) : '?'; }); if (src != jchar_buffer) { // We hit the slow path to populate src, so we have to release. env->ReleaseStringCritical(str, src); } } public: static void reset() { if (!is_overflow_strings_empty) { overflow_strings.clear(); is_overflow_strings_empty = true; } current_offset = 0; } // Converts a Java string (jstring) to an ASCII string_view. Characters // outside the ASCII range (0-255) are replaced with '?'. // // @param env The JNI environment. // @param val The Java string to convert. // @return A string_view representing the ASCII version of the string. // Returns an empty string_view if the input is null or empty. static std::string_view utf16_to_ascii(JNIEnv* env, jstring val) { if (!val) return ""; const jsize len = env->GetStringLength(val); if (len == 0) return ""; const jchar* temp_buffer; // Fast path: Enough space in jchar_buffer if (static_cast<size_t>(len) <= BASE_SIZE) { env->GetStringRegion(val, 0, len, jchar_buffer); temp_buffer = jchar_buffer; } else { // Slow path: Fallback to asking ART for the string which will likely // allocate and return a copy. temp_buffer = env->GetStringCritical(val, nullptr); } const size_t next_offset = current_offset + len + 1; // Fast path: Enough space in char_buffer if (BASE_SIZE > next_offset) { const size_t start_offset = current_offset; copy_utf16_to_ascii(temp_buffer, len, char_buffer + current_offset, env, val); char_buffer[current_offset + len] = '\0'; auto res = std::string_view(char_buffer + current_offset, len); current_offset = next_offset; return res; } else { // Slow path: Not enough space in char_buffer. Use overflow_strings. // This will cause a string alloc but should be very unlikely to hit. std::string& str = overflow_strings.emplace_back(len + 1, '\0'); copy_utf16_to_ascii(temp_buffer, len, str.data(), env, val); is_overflow_strings_empty = false; return std::string_view(str); } } }; static jlong android_os_PerfettoTrackEventExtraArgInt64_init(JNIEnv* env, jclass, jstring name) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::DebugArg<int64_t>(name_chars.c_str())); return toJLong(new tracing_perfetto::DebugArg<int64_t>( StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgBool_init(JNIEnv* env, jclass, jstring name) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::DebugArg<bool>(name_chars.c_str())); return toJLong( new tracing_perfetto::DebugArg<bool>(StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgDouble_init(JNIEnv* env, jclass, jstring name) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::DebugArg<double>(name_chars.c_str())); return toJLong( new tracing_perfetto::DebugArg<double>(StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgString_init(JNIEnv* env, jclass, jstring name) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::DebugArg<const char*>(name_chars.c_str())); return toJLong(new tracing_perfetto::DebugArg<const char*>( StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgInt64_delete() { Loading Loading @@ -109,11 +231,9 @@ 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(val_chars.c_str())); arg->set_value(StringBuffer::utf16_to_ascii(env, val).data()); } static jlong android_os_PerfettoTrackEventExtraFieldInt64_init() { Loading Loading @@ -186,11 +306,9 @@ 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(val_chars.c_str())); field->set_value(id, StringBuffer::utf16_to_ascii(env, val).data()); } static void android_os_PerfettoTrackEventExtraFieldNested_add_field(jlong field_ptr, Loading Loading @@ -231,8 +349,9 @@ 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) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, name_chars.c_str())); return toJLong( new tracing_perfetto::NamedTrack(id, parent_uuid, StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraNamedTrack_delete() { Loading @@ -246,9 +365,10 @@ static jlong android_os_PerfettoTrackEventExtraNamedTrack_get_extra_ptr(jlong pt static jlong android_os_PerfettoTrackEventExtraCounterTrack_init(JNIEnv* env, jclass, jstring name, jlong parent_uuid) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::RegisteredTrack(1, parent_uuid, name_chars.c_str(), true)); return toJLong( new tracing_perfetto::RegisteredTrack(1, parent_uuid, StringBuffer::utf16_to_ascii(env, name).data(), true)); } static jlong android_os_PerfettoTrackEventExtraCounterTrack_delete() { Loading Loading @@ -318,11 +438,11 @@ static void android_os_PerfettoTrackEventExtra_clear_args(jlong ptr) { 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(), tracing_perfetto::trace_event(type, category->get(), StringBuffer::utf16_to_ascii(env, name).data(), toPointer<tracing_perfetto::Extra>(extra_ptr)); StringBuffer::reset(); } static jlong android_os_PerfettoTrackEventExtraProto_init() { Loading core/tests/coretests/src/android/os/PerfettoTraceTest.java +49 −36 Original line number Diff line number Diff line Loading @@ -77,6 +77,8 @@ public class PerfettoTraceTest { private static final String TAG = "PerfettoTraceTest"; private static final String FOO = "foo"; private static final String BAR = "bar"; private static final String TEXT_ABOVE_4K_SIZE = new String(new char[8192]).replace('\0', 'a'); private static final Category FOO_CATEGORY = new Category(FOO); private static final int MESSAGE = 1234567; Loading Loading @@ -153,41 +155,6 @@ public class PerfettoTraceTest { assertThat(mDebugAnnotationNames).contains("string_val"); } @Test @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) public void testDebugAnnotationsWithLambda() throws Exception { TraceConfig traceConfig = getTraceConfig(FOO); PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); PerfettoTrace.instant(FOO_CATEGORY, "event").addArg("long_val", 123L).emit(); byte[] traceBytes = session.close(); 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 { Loading Loading @@ -440,7 +407,6 @@ public class PerfettoTraceTest { boolean hasTrackEvent = false; boolean hasSourceLocation = false; for (TracePacket packet: trace.getPacketList()) { TrackEvent event; if (packet.hasTrackEvent()) { Loading @@ -465,6 +431,53 @@ public class PerfettoTraceTest { assertThat(mCategoryNames).contains(FOO); } @Test @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) public void testProtoWithSlowPath() throws Exception { TraceConfig traceConfig = getTraceConfig(FOO); PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); PerfettoTrace.instant(FOO_CATEGORY, "event_proto") .beginProto() .beginNested(33L) .addField(4L, 2L) .addField(3, TEXT_ABOVE_4K_SIZE) .endNested() .addField(2001, "AIDL::IActivityManager") .endProto() .emit(); byte[] traceBytes = session.close(); 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 (TEXT_ABOVE_4K_SIZE.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 { Loading Loading
core/java/android/os/PerfettoTrackEventExtra.java +17 −4 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; Loading Loading @@ -50,6 +52,7 @@ public final class PerfettoTrackEventExtra { private static final Supplier<FieldString> sFieldStringSupplier = FieldString::new; private static final Supplier<FieldNested> sFieldNestedSupplier = FieldNested::new; private final List<PerfettoPointer> mPendingPointers = new ArrayList<>(); private CounterInt64 mCounterInt64; private CounterDouble mCounterDouble; private Proto mProto; Loading Loading @@ -592,7 +595,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldInt64 field = mFieldInt64Cache.get(sFieldInt64Supplier); field.setValue(id, val); mCurrentContainer.addField(field); mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } Loading @@ -601,7 +604,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldDouble field = mFieldDoubleCache.get(sFieldDoubleSupplier); field.setValue(id, val); mCurrentContainer.addField(field); mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } Loading @@ -610,7 +613,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldString field = mFieldStringCache.get(sFieldStringSupplier); field.setValue(id, val); mCurrentContainer.addField(field); mExtra.addPerfettoPointer(mCurrentContainer, field); return this; } Loading @@ -635,7 +638,7 @@ public final class PerfettoTrackEventExtra { checkContainer(); FieldNested field = mFieldNestedCache.get(sFieldNestedSupplier); field.setId(id); mCurrentContainer.addField(field); mExtra.addPerfettoPointer(mCurrentContainer, field); return mBuilderCache.get(sBuilderSupplier).initInternal(this, field); } Loading Loading @@ -735,6 +738,15 @@ public final class PerfettoTrackEventExtra { */ public void addPerfettoPointer(PerfettoPointer extra) { native_add_arg(mPtr, extra.getPtr()); mPendingPointers.add(extra); } /** * Adds a pointer representing a track event parameter to the {@code container}. */ public void addPerfettoPointer(FieldContainer container, PerfettoPointer extra) { container.addField(extra); mPendingPointers.add(extra); } /** Loading @@ -742,6 +754,7 @@ public final class PerfettoTrackEventExtra { */ public void reset() { native_clear_args(mPtr); mPendingPointers.clear(); } private CounterInt64 getCounterInt64() { Loading
core/jni/android_os_PerfettoTrackEventExtra.cpp +143 −23 Original line number Diff line number Diff line Loading @@ -23,7 +23,8 @@ #include <nativehelper/utils.h> #include <tracing_sdk.h> static constexpr ssize_t kMaxStrLen = 4096; #include <list> namespace android { template <typename T> inline static T* toPointer(jlong ptr) { Loading @@ -35,24 +36,145 @@ inline static jlong toJLong(T* ptr) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr)); } /** * @brief A thread-safe utility class for converting Java UTF-16 strings to ASCII in JNI * environment. * * StringBuffer provides efficient conversion of Java strings to ASCII with optimized memory * handling. * It uses a two-tiered buffering strategy: * 1. A fast path using pre-allocated thread-local buffers for strings up to 128 characters * 2. A fallback path using dynamic allocation for longer strings * * Non-ASCII characters (>255) are replaced with '?' during conversion. The class maintains * thread safety through thread-local storage and provides zero-copy string views for optimal * performance. * * Memory Management: * - Uses fixed-size thread-local buffers for both UTF-16 and ASCII characters * - Overflow strings are stored in a thread-local list to maintain valid string views * - Avoids unnecessary allocations in the common case of small strings * * Usage example: * @code * JNIEnv* env = ...; * jstring java_string = ...; * std::string_view ascii = StringBuffer::utf16_to_ascii(env, java_string); * // Use the ASCII string... * StringBuffer::reset(); // Clean up when done * @endcode * * Thread Safety: All methods are thread-safe due to thread-local storage. */ class StringBuffer { private: static constexpr size_t BASE_SIZE = 128; // Temporarily stores the UTF-16 characters retrieved from the Java // string before they are converted to ASCII. static thread_local inline char char_buffer[BASE_SIZE]; // For fast-path conversions when the resulting ASCII string fits within // the pre-allocated space. All ascii strings in a trace event will be stored // here until emitted. static thread_local inline jchar jchar_buffer[BASE_SIZE]; // When the fast-path conversion is not possible (because char_buffer // doesn't have enough space), the converted ASCII string is stored // in this list. We use list here to avoid moving the strings on resize // with vector. This way, we can give out string_views from the stored strings. // The additional overhead from list node allocations is fine cos we are already // in an extremely unlikely path here and there are other bigger problems if here. static thread_local inline std::list<std::string> overflow_strings; // current offset into the char_buffer. static thread_local inline size_t current_offset{0}; // This allows us avoid touching the overflow_strings directly in the fast path. // Touching it causes some thread local init routine to run which shows up in profiles. static thread_local inline bool is_overflow_strings_empty = true; static void copy_utf16_to_ascii(const jchar* src, size_t len, char* dst, JNIEnv* env, jstring str) { std::transform(src, src + len, dst, [](jchar c) { return (c <= 0xFF) ? static_cast<char>(c) : '?'; }); if (src != jchar_buffer) { // We hit the slow path to populate src, so we have to release. env->ReleaseStringCritical(str, src); } } public: static void reset() { if (!is_overflow_strings_empty) { overflow_strings.clear(); is_overflow_strings_empty = true; } current_offset = 0; } // Converts a Java string (jstring) to an ASCII string_view. Characters // outside the ASCII range (0-255) are replaced with '?'. // // @param env The JNI environment. // @param val The Java string to convert. // @return A string_view representing the ASCII version of the string. // Returns an empty string_view if the input is null or empty. static std::string_view utf16_to_ascii(JNIEnv* env, jstring val) { if (!val) return ""; const jsize len = env->GetStringLength(val); if (len == 0) return ""; const jchar* temp_buffer; // Fast path: Enough space in jchar_buffer if (static_cast<size_t>(len) <= BASE_SIZE) { env->GetStringRegion(val, 0, len, jchar_buffer); temp_buffer = jchar_buffer; } else { // Slow path: Fallback to asking ART for the string which will likely // allocate and return a copy. temp_buffer = env->GetStringCritical(val, nullptr); } const size_t next_offset = current_offset + len + 1; // Fast path: Enough space in char_buffer if (BASE_SIZE > next_offset) { const size_t start_offset = current_offset; copy_utf16_to_ascii(temp_buffer, len, char_buffer + current_offset, env, val); char_buffer[current_offset + len] = '\0'; auto res = std::string_view(char_buffer + current_offset, len); current_offset = next_offset; return res; } else { // Slow path: Not enough space in char_buffer. Use overflow_strings. // This will cause a string alloc but should be very unlikely to hit. std::string& str = overflow_strings.emplace_back(len + 1, '\0'); copy_utf16_to_ascii(temp_buffer, len, str.data(), env, val); is_overflow_strings_empty = false; return std::string_view(str); } } }; static jlong android_os_PerfettoTrackEventExtraArgInt64_init(JNIEnv* env, jclass, jstring name) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::DebugArg<int64_t>(name_chars.c_str())); return toJLong(new tracing_perfetto::DebugArg<int64_t>( StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgBool_init(JNIEnv* env, jclass, jstring name) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::DebugArg<bool>(name_chars.c_str())); return toJLong( new tracing_perfetto::DebugArg<bool>(StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgDouble_init(JNIEnv* env, jclass, jstring name) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::DebugArg<double>(name_chars.c_str())); return toJLong( new tracing_perfetto::DebugArg<double>(StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgString_init(JNIEnv* env, jclass, jstring name) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::DebugArg<const char*>(name_chars.c_str())); return toJLong(new tracing_perfetto::DebugArg<const char*>( StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraArgInt64_delete() { Loading Loading @@ -109,11 +231,9 @@ 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(val_chars.c_str())); arg->set_value(StringBuffer::utf16_to_ascii(env, val).data()); } static jlong android_os_PerfettoTrackEventExtraFieldInt64_init() { Loading Loading @@ -186,11 +306,9 @@ 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(val_chars.c_str())); field->set_value(id, StringBuffer::utf16_to_ascii(env, val).data()); } static void android_os_PerfettoTrackEventExtraFieldNested_add_field(jlong field_ptr, Loading Loading @@ -231,8 +349,9 @@ 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) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, name_chars.c_str())); return toJLong( new tracing_perfetto::NamedTrack(id, parent_uuid, StringBuffer::utf16_to_ascii(env, name).data())); } static jlong android_os_PerfettoTrackEventExtraNamedTrack_delete() { Loading @@ -246,9 +365,10 @@ static jlong android_os_PerfettoTrackEventExtraNamedTrack_get_extra_ptr(jlong pt static jlong android_os_PerfettoTrackEventExtraCounterTrack_init(JNIEnv* env, jclass, jstring name, jlong parent_uuid) { ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name); return toJLong(new tracing_perfetto::RegisteredTrack(1, parent_uuid, name_chars.c_str(), true)); return toJLong( new tracing_perfetto::RegisteredTrack(1, parent_uuid, StringBuffer::utf16_to_ascii(env, name).data(), true)); } static jlong android_os_PerfettoTrackEventExtraCounterTrack_delete() { Loading Loading @@ -318,11 +438,11 @@ static void android_os_PerfettoTrackEventExtra_clear_args(jlong ptr) { 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(), tracing_perfetto::trace_event(type, category->get(), StringBuffer::utf16_to_ascii(env, name).data(), toPointer<tracing_perfetto::Extra>(extra_ptr)); StringBuffer::reset(); } static jlong android_os_PerfettoTrackEventExtraProto_init() { Loading
core/tests/coretests/src/android/os/PerfettoTraceTest.java +49 −36 Original line number Diff line number Diff line Loading @@ -77,6 +77,8 @@ public class PerfettoTraceTest { private static final String TAG = "PerfettoTraceTest"; private static final String FOO = "foo"; private static final String BAR = "bar"; private static final String TEXT_ABOVE_4K_SIZE = new String(new char[8192]).replace('\0', 'a'); private static final Category FOO_CATEGORY = new Category(FOO); private static final int MESSAGE = 1234567; Loading Loading @@ -153,41 +155,6 @@ public class PerfettoTraceTest { assertThat(mDebugAnnotationNames).contains("string_val"); } @Test @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) public void testDebugAnnotationsWithLambda() throws Exception { TraceConfig traceConfig = getTraceConfig(FOO); PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); PerfettoTrace.instant(FOO_CATEGORY, "event").addArg("long_val", 123L).emit(); byte[] traceBytes = session.close(); 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 { Loading Loading @@ -440,7 +407,6 @@ public class PerfettoTraceTest { boolean hasTrackEvent = false; boolean hasSourceLocation = false; for (TracePacket packet: trace.getPacketList()) { TrackEvent event; if (packet.hasTrackEvent()) { Loading @@ -465,6 +431,53 @@ public class PerfettoTraceTest { assertThat(mCategoryNames).contains(FOO); } @Test @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2) public void testProtoWithSlowPath() throws Exception { TraceConfig traceConfig = getTraceConfig(FOO); PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray()); PerfettoTrace.instant(FOO_CATEGORY, "event_proto") .beginProto() .beginNested(33L) .addField(4L, 2L) .addField(3, TEXT_ABOVE_4K_SIZE) .endNested() .addField(2001, "AIDL::IActivityManager") .endProto() .emit(); byte[] traceBytes = session.close(); 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 (TEXT_ABOVE_4K_SIZE.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 { Loading