1 /*
2 * Copyright (C) 2025 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/log.h>
17 #include <jni.h>
18
19 #include "src/android_sdk/jni/dev_perfetto_sdk_PerfettoTrackEventExtra.h"
20 #include "src/android_sdk/nativehelper/JNIHelp.h"
21 #include "src/android_sdk/nativehelper/scoped_utf_chars.h"
22 #include "src/android_sdk/nativehelper/utils.h"
23 #include "src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.h"
24
25 namespace perfetto {
26 namespace jni {
27 constexpr int kFlushTimeoutMs = 5000;
28
29 template <typename T>
toPointer(jlong ptr)30 inline static T* toPointer(jlong ptr) {
31 return reinterpret_cast<T*>(static_cast<uintptr_t>(ptr));
32 }
33
34 template <typename T>
toJLong(T * ptr)35 inline static jlong toJLong(T* ptr) {
36 return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
37 }
38
android_os_PerfettoTrace_get_process_track_uuid()39 static jlong android_os_PerfettoTrace_get_process_track_uuid() {
40 return sdk_for_jni::get_process_track_uuid();
41 }
42
android_os_PerfettoTrace_get_thread_track_uuid(jlong tid)43 static jlong android_os_PerfettoTrace_get_thread_track_uuid(jlong tid) {
44 return sdk_for_jni::get_thread_track_uuid(tid);
45 }
46
android_os_PerfettoTrace_activate_trigger(JNIEnv * env,jclass,jstring name,jint ttl_ms)47 static void android_os_PerfettoTrace_activate_trigger(JNIEnv* env,
48 jclass,
49 jstring name,
50 jint ttl_ms) {
51 ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name);
52 sdk_for_jni::activate_trigger(name_chars.c_str(),
53 static_cast<uint32_t>(ttl_ms));
54 }
55
android_os_PerfettoTrace_register(JNIEnv *,jclass,bool is_backend_in_process)56 void android_os_PerfettoTrace_register(JNIEnv*,
57 jclass,
58 bool is_backend_in_process) {
59 sdk_for_jni::register_perfetto(is_backend_in_process);
60 }
61
android_os_PerfettoTraceCategory_init(JNIEnv * env,jclass,jstring name,jstring tag,jstring severity)62 static jlong android_os_PerfettoTraceCategory_init(JNIEnv* env,
63 jclass,
64 jstring name,
65 jstring tag,
66 jstring severity) {
67 ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
68 ScopedUtfChars tag_chars = GET_UTF_OR_RETURN(env, tag);
69 ScopedUtfChars severity_chars = GET_UTF_OR_RETURN(env, severity);
70
71 return toJLong(new sdk_for_jni::Category(
72 name_chars.c_str(), tag_chars.c_str(), severity_chars.c_str()));
73 }
74
android_os_PerfettoTraceCategory_delete()75 static jlong android_os_PerfettoTraceCategory_delete() {
76 return toJLong(&sdk_for_jni::Category::delete_category);
77 }
78
android_os_PerfettoTraceCategory_register(jlong ptr)79 static void android_os_PerfettoTraceCategory_register(jlong ptr) {
80 auto* category = toPointer<sdk_for_jni::Category>(ptr);
81 category->register_category();
82 }
83
android_os_PerfettoTraceCategory_unregister(jlong ptr)84 static void android_os_PerfettoTraceCategory_unregister(jlong ptr) {
85 auto* category = toPointer<sdk_for_jni::Category>(ptr);
86 category->unregister_category();
87 }
88
android_os_PerfettoTraceCategory_is_enabled(jlong ptr)89 static jboolean android_os_PerfettoTraceCategory_is_enabled(jlong ptr) {
90 auto* category = toPointer<sdk_for_jni::Category>(ptr);
91 return category->is_category_enabled();
92 }
93
android_os_PerfettoTraceCategory_get_extra_ptr(jlong ptr)94 static jlong android_os_PerfettoTraceCategory_get_extra_ptr(jlong ptr) {
95 auto* category = toPointer<sdk_for_jni::Category>(ptr);
96 return toJLong(category->get());
97 }
98
android_os_PerfettoTrace_start_session(JNIEnv * env,jclass,jboolean is_backend_in_process,jbyteArray config_bytes)99 static jlong android_os_PerfettoTrace_start_session(
100 JNIEnv* env,
101 jclass /* obj */,
102 jboolean is_backend_in_process,
103 jbyteArray config_bytes) {
104 jsize length = env->GetArrayLength(config_bytes);
105 std::vector<uint8_t> data;
106 data.reserve(length);
107 env->GetByteArrayRegion(config_bytes, 0, length,
108 reinterpret_cast<jbyte*>(data.data()));
109
110 auto session =
111 new sdk_for_jni::Session(is_backend_in_process, data.data(), length);
112
113 return reinterpret_cast<long>(session);
114 }
115
android_os_PerfettoTrace_stop_session(JNIEnv * env,jclass,jlong ptr)116 static jbyteArray android_os_PerfettoTrace_stop_session(
117 [[maybe_unused]] JNIEnv* env,
118 jclass /* obj */,
119 jlong ptr) {
120 auto* session = reinterpret_cast<sdk_for_jni::Session*>(ptr);
121
122 session->FlushBlocking(kFlushTimeoutMs);
123 session->StopBlocking();
124
125 std::vector<uint8_t> data = session->ReadBlocking();
126
127 delete session;
128
129 jbyteArray bytes = env->NewByteArray(data.size());
130 env->SetByteArrayRegion(bytes, 0, data.size(),
131 reinterpret_cast<jbyte*>(data.data()));
132 return bytes;
133 }
134
135 static const JNINativeMethod gCategoryMethods[] = {
native_init(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)136 {"native_init", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
137 (void*)android_os_PerfettoTraceCategory_init},
native_delete()138 {"native_delete", "()J", (void*)android_os_PerfettoTraceCategory_delete},
native_register(J)139 {"native_register", "(J)V",
140 (void*)android_os_PerfettoTraceCategory_register},
native_unregister(J)141 {"native_unregister", "(J)V",
142 (void*)android_os_PerfettoTraceCategory_unregister},
native_is_enabled(J)143 {"native_is_enabled", "(J)Z",
144 (void*)android_os_PerfettoTraceCategory_is_enabled},
native_get_extra_ptr(J)145 {"native_get_extra_ptr", "(J)J",
146 (void*)android_os_PerfettoTraceCategory_get_extra_ptr},
147 };
148
149 static const JNINativeMethod gTraceMethods[] = {
native_get_process_track_uuid()150 {"native_get_process_track_uuid", "()J",
151 (void*)android_os_PerfettoTrace_get_process_track_uuid},
native_get_thread_track_uuid(J)152 {"native_get_thread_track_uuid", "(J)J",
153 (void*)android_os_PerfettoTrace_get_thread_track_uuid},
native_activate_trigger(Ljava/lang/String;I)154 {"native_activate_trigger", "(Ljava/lang/String;I)V",
155 (void*)android_os_PerfettoTrace_activate_trigger},
native_register(Z)156 {"native_register", "(Z)V", (void*)android_os_PerfettoTrace_register},
native_start_session(Z[B)157 {"native_start_session", "(Z[B)J",
158 (void*)android_os_PerfettoTrace_start_session},
native_stop_session(J)159 {"native_stop_session", "(J)[B",
160 (void*)android_os_PerfettoTrace_stop_session}};
161
162 #define LOG_ALWAYS_FATAL_IF(cond, fmt) \
163 if (cond) \
164 __android_log_assert(nullptr, "PerfettoJNI", fmt);
165
register_android_os_PerfettoTrace(JNIEnv * env)166 int register_android_os_PerfettoTrace(JNIEnv* env) {
167 int res = jniRegisterNativeMethods(env, "dev/perfetto/sdk/PerfettoTrace",
168 gTraceMethods, NELEM(gTraceMethods));
169 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register perfetto native methods.");
170
171 res = jniRegisterNativeMethods(env, "dev/perfetto/sdk/PerfettoTrace$Category",
172 gCategoryMethods, NELEM(gCategoryMethods));
173 LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register category native methods.");
174
175 return 0;
176 }
177
178 #undef LOG_ALWAYS_FATAL_IF
179
180 } // namespace jni
181 } // namespace perfetto
182
JNI_OnLoad(JavaVM * vm,void *)183 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
184 JNIEnv* env;
185 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
186 return JNI_ERR;
187 }
188
189 perfetto::jni::register_android_os_PerfettoTrace(env);
190 perfetto::jni::register_android_os_PerfettoTrackEventExtra(env);
191
192 return JNI_VERSION_1_6;
193 }
194