1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <android-base/logging.h>
17 #include <android-base/properties.h>
18 #include <cutils/compiler.h>
19 #include <cutils/trace.h>
20 #include <jni.h>
21 #include <log/log.h>
22 #include <nativehelper/JNIHelp.h>
23 #include <nativehelper/scoped_local_ref.h>
24 #include <nativehelper/scoped_primitive_array.h>
25 #include <nativehelper/scoped_utf_chars.h>
26 #include <nativehelper/utils.h>
27 #include <tracing_perfetto.h>
28 #include <tracing_sdk.h>
29
30 namespace android {
31 constexpr int kFlushTimeoutMs = 5000;
32
33 template <typename T>
toPointer(jlong ptr)34 inline static T* toPointer(jlong ptr) {
35 return reinterpret_cast<T*>(static_cast<uintptr_t>(ptr));
36 }
37
38 template <typename T>
toJLong(T * ptr)39 inline static jlong toJLong(T* ptr) {
40 return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
41 }
42
android_os_PerfettoTrace_get_process_track_uuid()43 static jlong android_os_PerfettoTrace_get_process_track_uuid() {
44 return tracing_perfetto::get_process_track_uuid();
45 }
46
android_os_PerfettoTrace_get_thread_track_uuid(jlong tid)47 static jlong android_os_PerfettoTrace_get_thread_track_uuid(jlong tid) {
48 return tracing_perfetto::get_thread_track_uuid(tid);
49 }
50
android_os_PerfettoTrace_activate_trigger(JNIEnv * env,jclass,jstring name,jint ttl_ms)51 static void android_os_PerfettoTrace_activate_trigger(JNIEnv* env, jclass, jstring name,
52 jint ttl_ms) {
53 ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name);
54 tracing_perfetto::activate_trigger(name_chars.c_str(), static_cast<uint32_t>(ttl_ms));
55 }
56
android_os_PerfettoTrace_register(bool is_backend_in_process)57 void android_os_PerfettoTrace_register(bool is_backend_in_process) {
58 tracing_perfetto::registerWithPerfetto(is_backend_in_process);
59 }
60
android_os_PerfettoTraceCategory_init(JNIEnv * env,jclass,jstring name,jstring tag,jstring severity)61 static jlong android_os_PerfettoTraceCategory_init(JNIEnv* env, jclass, jstring name, jstring tag,
62 jstring severity) {
63 ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
64 ScopedUtfChars tag_chars = GET_UTF_OR_RETURN(env, tag);
65 ScopedUtfChars severity_chars = GET_UTF_OR_RETURN(env, severity);
66
67 return toJLong(new tracing_perfetto::Category(name_chars.c_str(), tag_chars.c_str(),
68 severity_chars.c_str()));
69 }
70
android_os_PerfettoTraceCategory_delete()71 static jlong android_os_PerfettoTraceCategory_delete() {
72 return toJLong(&tracing_perfetto::Category::delete_category);
73 }
74
android_os_PerfettoTraceCategory_register(jlong ptr)75 static void android_os_PerfettoTraceCategory_register(jlong ptr) {
76 tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(ptr);
77 category->register_category();
78 }
79
android_os_PerfettoTraceCategory_unregister(jlong ptr)80 static void android_os_PerfettoTraceCategory_unregister(jlong ptr) {
81 tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(ptr);
82 category->unregister_category();
83 }
84
android_os_PerfettoTraceCategory_is_enabled(jlong ptr)85 static jboolean android_os_PerfettoTraceCategory_is_enabled(jlong ptr) {
86 tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(ptr);
87 return category->is_category_enabled();
88 }
89
android_os_PerfettoTraceCategory_get_extra_ptr(jlong ptr)90 static jlong android_os_PerfettoTraceCategory_get_extra_ptr(jlong ptr) {
91 tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(ptr);
92 return toJLong(category->get());
93 }
94
android_os_PerfettoTrace_start_session(JNIEnv * env,jclass,jboolean is_backend_in_process,jbyteArray config_bytes)95 static jlong android_os_PerfettoTrace_start_session(JNIEnv* env, jclass /* obj */,
96 jboolean is_backend_in_process,
97 jbyteArray config_bytes) {
98 jsize length = env->GetArrayLength(config_bytes);
99 std::vector<uint8_t> data;
100 data.reserve(length);
101 env->GetByteArrayRegion(config_bytes, 0, length, reinterpret_cast<jbyte*>(data.data()));
102
103 tracing_perfetto::Session* session =
104 new tracing_perfetto::Session(is_backend_in_process, data.data(), length);
105
106 return reinterpret_cast<long>(session);
107 }
108
android_os_PerfettoTrace_stop_session(JNIEnv * env,jclass,jlong ptr)109 static jbyteArray android_os_PerfettoTrace_stop_session([[maybe_unused]] JNIEnv* env,
110 jclass /* obj */, jlong ptr) {
111 tracing_perfetto::Session* session = reinterpret_cast<tracing_perfetto::Session*>(ptr);
112
113 session->FlushBlocking(kFlushTimeoutMs);
114 session->StopBlocking();
115
116 std::vector<uint8_t> data = session->ReadBlocking();
117
118 delete session;
119
120 jbyteArray bytes = env->NewByteArray(data.size());
121 env->SetByteArrayRegion(bytes, 0, data.size(), reinterpret_cast<jbyte*>(data.data()));
122 return bytes;
123 }
124
125 static const JNINativeMethod gCategoryMethods[] = {
native_init(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)126 {"native_init", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
127 (void*)android_os_PerfettoTraceCategory_init},
native_delete()128 {"native_delete", "()J", (void*)android_os_PerfettoTraceCategory_delete},
native_register(J)129 {"native_register", "(J)V", (void*)android_os_PerfettoTraceCategory_register},
native_unregister(J)130 {"native_unregister", "(J)V", (void*)android_os_PerfettoTraceCategory_unregister},
native_is_enabled(J)131 {"native_is_enabled", "(J)Z", (void*)android_os_PerfettoTraceCategory_is_enabled},
native_get_extra_ptr(J)132 {"native_get_extra_ptr", "(J)J", (void*)android_os_PerfettoTraceCategory_get_extra_ptr},
133 };
134
135 static const JNINativeMethod gTraceMethods[] =
native_get_process_track_uuid()136 {{"native_get_process_track_uuid", "()J",
137 (void*)android_os_PerfettoTrace_get_process_track_uuid},
138 {"native_get_thread_track_uuid", "(J)J",
139 (void*)android_os_PerfettoTrace_get_thread_track_uuid},
140 {"native_activate_trigger", "(Ljava/lang/String;I)V",
141 (void*)android_os_PerfettoTrace_activate_trigger},
142 {"native_register", "(Z)V", (void*)android_os_PerfettoTrace_register},
143 {"native_start_session", "(Z[B)J", (void*)android_os_PerfettoTrace_start_session},
144 {"native_stop_session", "(J)[B", (void*)android_os_PerfettoTrace_stop_session}};
145
register_android_os_PerfettoTrace(JNIEnv * env)146 int register_android_os_PerfettoTrace(JNIEnv* env) {
147 int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace", gTraceMethods,
148 NELEM(gTraceMethods));
149 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register perfetto native methods.");
150
151 res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace$Category", gCategoryMethods,
152 NELEM(gCategoryMethods));
153 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register category native methods.");
154
155 return 0;
156 }
157
158 } // namespace android
159