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 17 package dev.perfetto.sdk; 18 19 import dalvik.annotation.optimization.CriticalNative; 20 import dalvik.annotation.optimization.FastNative; 21 22 import java.util.concurrent.atomic.AtomicInteger; 23 24 /** 25 * Writes trace events to the perfetto trace buffer. These trace events can be 26 * collected and visualized using the Perfetto UI. 27 * 28 * <p>This tracing mechanism is independent of the method tracing mechanism 29 * offered by {@link Debug#startMethodTracing} or {@link Trace}. 30 * 31 * @hide 32 */ 33 public final class PerfettoTrace { 34 private static final String TAG = "PerfettoTrace"; 35 36 // Keep in sync with C++ 37 private static final int PERFETTO_TE_TYPE_SLICE_BEGIN = 1; 38 private static final int PERFETTO_TE_TYPE_SLICE_END = 2; 39 private static final int PERFETTO_TE_TYPE_INSTANT = 3; 40 private static final int PERFETTO_TE_TYPE_COUNTER = 4; 41 42 static class NativeAllocationRegistry { createMalloced( ClassLoader classLoader, long freeFunction)43 public static NativeAllocationRegistry createMalloced( 44 ClassLoader classLoader, long freeFunction) { 45 // do nothing 46 return new NativeAllocationRegistry(); 47 } registerNativeAllocation(Object obj, long ptr)48 public void registerNativeAllocation(Object obj, long ptr) { 49 // do nothing 50 } 51 } 52 53 /** 54 * For fetching the next flow event id in a process. 55 */ 56 private static final AtomicInteger sFlowEventId = new AtomicInteger(); 57 58 /** 59 * Perfetto category a trace event belongs to. 60 * Registering a category is not sufficient to capture events within the category, it must 61 * also be enabled in the trace config. 62 */ 63 public static final class Category implements PerfettoTrackEventExtra.PerfettoPointer { 64 private static final NativeAllocationRegistry sRegistry = 65 NativeAllocationRegistry.createMalloced( 66 Category.class.getClassLoader(), native_delete()); 67 68 private final long mPtr; 69 private final long mExtraPtr; 70 private final String mName; 71 private final String mTag; 72 private final String mSeverity; 73 private boolean mIsRegistered; 74 75 /** 76 * Category ctor. 77 * 78 * @param name The category name. 79 */ Category(String name)80 public Category(String name) { 81 this(name, "", ""); 82 } 83 84 /** 85 * Category ctor. 86 * 87 * @param name The category name. 88 * @param tag An atrace tag name that this category maps to. 89 */ Category(String name, String tag)90 public Category(String name, String tag) { 91 this(name, tag, ""); 92 } 93 94 /** 95 * Category ctor. 96 * 97 * @param name The category name. 98 * @param tag An atrace tag name that this category maps to. 99 * @param severity A Log style severity string for the category. 100 */ Category(String name, String tag, String severity)101 public Category(String name, String tag, String severity) { 102 mName = name; 103 mTag = tag; 104 mSeverity = severity; 105 mPtr = native_init(name, tag, severity); 106 mExtraPtr = native_get_extra_ptr(mPtr); 107 } 108 109 @FastNative native_init(String name, String tag, String severity)110 private static native long native_init(String name, String tag, String severity); 111 @CriticalNative native_delete()112 private static native long native_delete(); 113 @CriticalNative native_register(long ptr)114 private static native void native_register(long ptr); 115 @CriticalNative native_unregister(long ptr)116 private static native void native_unregister(long ptr); 117 @CriticalNative native_is_enabled(long ptr)118 private static native boolean native_is_enabled(long ptr); 119 @CriticalNative native_get_extra_ptr(long ptr)120 private static native long native_get_extra_ptr(long ptr); 121 122 /** 123 * Register the category. 124 */ register()125 public Category register() { 126 native_register(mPtr); 127 mIsRegistered = true; 128 return this; 129 } 130 131 /** 132 * Unregister the category. 133 */ unregister()134 public Category unregister() { 135 native_unregister(mPtr); 136 mIsRegistered = false; 137 return this; 138 } 139 140 /** 141 * Whether the category is enabled or not. 142 */ isEnabled()143 public boolean isEnabled() { 144 return native_is_enabled(mPtr); 145 } 146 147 /** 148 * Whether the category is registered or not. 149 */ isRegistered()150 public boolean isRegistered() { 151 return mIsRegistered; 152 } 153 154 /** 155 * Returns the native pointer for the category. 156 */ 157 @Override getPtr()158 public long getPtr() { 159 return mExtraPtr; 160 } 161 } 162 163 /** 164 * Manages a perfetto tracing session. 165 * Constructing this object with a config automatically starts a tracing session. Each session 166 * must be closed after use and then the resulting trace bytes can be read. 167 * 168 * The session could be in process or system wide, depending on {@code isBackendInProcess}. 169 * This functionality is intended for testing. 170 */ 171 public static final class Session { 172 private final long mPtr; 173 174 /** 175 * Session ctor. 176 */ Session(boolean isBackendInProcess, byte[] config)177 public Session(boolean isBackendInProcess, byte[] config) { 178 mPtr = native_start_session(isBackendInProcess, config); 179 } 180 181 /** 182 * Closes the session and returns the trace. 183 */ close()184 public byte[] close() { 185 return native_stop_session(mPtr); 186 } 187 } 188 189 @CriticalNative native_get_process_track_uuid()190 private static native long native_get_process_track_uuid(); 191 @CriticalNative native_get_thread_track_uuid(long tid)192 private static native long native_get_thread_track_uuid(long tid); 193 194 @FastNative native_activate_trigger(String name, int ttlMs)195 private static native void native_activate_trigger(String name, int ttlMs); 196 @FastNative native_register(boolean isBackendInProcess)197 private static native void native_register(boolean isBackendInProcess); 198 native_start_session(boolean isBackendInProcess, byte[] config)199 private static native long native_start_session(boolean isBackendInProcess, byte[] config); native_stop_session(long ptr)200 private static native byte[] native_stop_session(long ptr); 201 202 /** 203 * Writes a trace message to indicate a given section of code was invoked. 204 * 205 * @param category The perfetto category. 206 * @param eventName The event name to appear in the trace. 207 */ instant(Category category, String eventName)208 public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) { 209 return PerfettoTrackEventExtra.builder(category.isEnabled()) 210 .init(PERFETTO_TE_TYPE_INSTANT, category) 211 .setEventName(eventName); 212 } 213 214 /** 215 * Writes a trace message to indicate the start of a given section of code. 216 * 217 * @param category The perfetto category. 218 * @param eventName The event name to appear in the trace. 219 */ begin(Category category, String eventName)220 public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) { 221 return PerfettoTrackEventExtra.builder(category.isEnabled()) 222 .init(PERFETTO_TE_TYPE_SLICE_BEGIN, category) 223 .setEventName(eventName); 224 } 225 226 /** 227 * Writes a trace message to indicate the end of a given section of code. 228 * 229 * @param category The perfetto category. 230 */ end(Category category)231 public static PerfettoTrackEventExtra.Builder end(Category category) { 232 return PerfettoTrackEventExtra.builder(category.isEnabled()) 233 .init(PERFETTO_TE_TYPE_SLICE_END, category); 234 } 235 236 /** 237 * Writes a trace message to indicate the value of a given section of code. 238 * 239 * @param category The perfetto category. 240 * @param value The value of the counter. 241 */ counter(Category category, long value)242 public static PerfettoTrackEventExtra.Builder counter(Category category, long value) { 243 return PerfettoTrackEventExtra.builder(category.isEnabled()) 244 .init(PERFETTO_TE_TYPE_COUNTER, category) 245 .setCounter(value); 246 } 247 248 /** 249 * Writes a trace message to indicate the value of a given section of code. 250 * 251 * @param category The perfetto category. 252 * @param value The value of the counter. 253 * @param trackName The trackName for the event. 254 */ counter( Category category, long value, String trackName)255 public static PerfettoTrackEventExtra.Builder counter( 256 Category category, long value, String trackName) { 257 return counter(category, value).usingProcessCounterTrack(trackName); 258 } 259 260 /** 261 * Writes a trace message to indicate the value of a given section of code. 262 * 263 * @param category The perfetto category. 264 * @param value The value of the counter. 265 */ counter(Category category, double value)266 public static PerfettoTrackEventExtra.Builder counter(Category category, double value) { 267 return PerfettoTrackEventExtra.builder(category.isEnabled()) 268 .init(PERFETTO_TE_TYPE_COUNTER, category) 269 .setCounter(value); 270 } 271 272 /** 273 * Writes a trace message to indicate the value of a given section of code. 274 * 275 * @param category The perfetto category. 276 * @param value The value of the counter. 277 * @param trackName The trackName for the event. 278 */ counter( Category category, double value, String trackName)279 public static PerfettoTrackEventExtra.Builder counter( 280 Category category, double value, String trackName) { 281 return counter(category, value).usingProcessCounterTrack(trackName); 282 } 283 284 /** 285 * Returns the next flow id to be used. 286 */ getFlowId()287 public static int getFlowId() { 288 return sFlowEventId.incrementAndGet(); 289 } 290 291 /** 292 * Returns the global track uuid that can be used as a parent track uuid. 293 */ getGlobalTrackUuid()294 public static long getGlobalTrackUuid() { 295 return 0; 296 } 297 298 /** 299 * Returns the process track uuid that can be used as a parent track uuid. 300 */ getProcessTrackUuid()301 public static long getProcessTrackUuid() { 302 return native_get_process_track_uuid(); 303 } 304 305 /** 306 * Given a thread tid, returns the thread track uuid that can be used as a parent track uuid. 307 */ getThreadTrackUuid(long tid)308 public static long getThreadTrackUuid(long tid) { 309 return native_get_thread_track_uuid(tid); 310 } 311 312 /** 313 * Activates a trigger by name {@code triggerName} with expiry in {@code ttlMs}. 314 */ activateTrigger(String triggerName, int ttlMs)315 public static void activateTrigger(String triggerName, int ttlMs) { 316 native_activate_trigger(triggerName, ttlMs); 317 } 318 319 /** 320 * Registers the process with Perfetto. 321 */ register(boolean isBackendInProcess)322 public static void register(boolean isBackendInProcess) { 323 native_register(isBackendInProcess); 324 } 325 } 326