android_sdk: add nested tracks (PerfettoTrack + usingTrack)
Immutable PerfettoTrack handle (process/thread/global root + child())
plus usingTrack(), carried by one native NestedTracks extra over the
existing HL NESTED_TRACKS ABI.
Example usage:
static final PerfettoTrack RENDER = PerfettoTrack.process("Render");
static final PerfettoTrack GPU = RENDER.child("GPU");
PerfettoTrace.instant(CAT, "frame").usingTrack(GPU).emit();
Change-Id: Ie2c9248ae7bd2fcd3be256ae3974559837b4b6c1
diff --git a/Android.bp b/Android.bp
index b67784e..941254b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -22233,6 +22233,7 @@
srcs: [
"src/android_sdk/java/main/dev/perfetto/sdk/PerfettoNativeMemoryCleaner.java",
"src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrace.java",
+ "src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrack.java",
"src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventBuilder.java",
"src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventExtra.java",
],
diff --git a/BUILD b/BUILD
index df3c8af..24c7d2f 100644
--- a/BUILD
+++ b/BUILD
@@ -6548,6 +6548,7 @@
srcs = [
"src/android_sdk/java/main/dev/perfetto/sdk/PerfettoNativeMemoryCleaner.java",
"src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrace.java",
+ "src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrack.java",
"src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventBuilder.java",
"src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventExtra.java",
],
diff --git a/src/android_sdk/java/main/BUILD.gn b/src/android_sdk/java/main/BUILD.gn
index 6f219f5..be304c2 100644
--- a/src/android_sdk/java/main/BUILD.gn
+++ b/src/android_sdk/java/main/BUILD.gn
@@ -7,6 +7,7 @@
sources = [
"dev/perfetto/sdk/PerfettoNativeMemoryCleaner.java",
"dev/perfetto/sdk/PerfettoTrace.java",
+ "dev/perfetto/sdk/PerfettoTrack.java",
"dev/perfetto/sdk/PerfettoTrackEventBuilder.java",
"dev/perfetto/sdk/PerfettoTrackEventExtra.java",
]
diff --git a/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrack.java b/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrack.java
new file mode 100644
index 0000000..97d73da
--- /dev/null
+++ b/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrack.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.perfetto.sdk;
+
+import com.google.errorprone.annotations.CompileTimeConstant;
+
+import dev.perfetto.sdk.PerfettoTrackEventExtra.NestedTracks;
+
+/**
+ * An immutable, reusable handle to a (possibly nested) named track.
+ *
+ * <p>Build a track once — typically a {@code static final} — and pass it to
+ * {@link PerfettoTrackEventBuilder#usingTrack}. A track is rooted at the process,
+ * the current thread, or the global scope, and can be nested arbitrarily deep with
+ * {@link #child}:
+ *
+ * <pre>
+ * static final PerfettoTrack RENDER = PerfettoTrack.process("Render");
+ * static final PerfettoTrack GPU = RENDER.child("GPU");
+ * ...
+ * PerfettoTrace.instant(CAT, "frame").usingTrack(GPU).emit();
+ * </pre>
+ *
+ * <p>Emitting on {@code GPU} emits the {@code TrackDescriptor}s for the whole
+ * chain (Render under the process, GPU under Render) once per sequence, matching
+ * the C SDK's nested-track behaviour. The track uuid is derived natively exactly
+ * as the C SDK derives it.
+ *
+ * <p>This is the nesting-only shape supported by the high-level ABI; sibling
+ * ordering, counter units and similar are intentionally not exposed here.
+ */
+public final class PerfettoTrack {
+ // Root scope of the chain. Mirrors RootType in tracing_sdk.h.
+ static final int ROOT_GLOBAL = 0;
+ static final int ROOT_PROCESS = 1;
+ static final int ROOT_THREAD = 2;
+
+ // Default per-level id: no disambiguation between same-named sibling tracks.
+ private static final long DEFAULT_ID = 0;
+
+ final int mRootType;
+ // Names and ids of the chain, outermost (closest to the root) first.
+ final String[] mNames;
+ final long[] mIds;
+
+ // The handle's native nested-tracks extra, built lazily on first use and held
+ // for the handle's lifetime. Freed by the cleaner when the handle is collected.
+ private volatile NestedTracks mNested;
+
+ // Frees a handle's native track when the handle is collected. SystemCleaner
+ // needs no native lib, so it is safe to hold statically.
+ private static final PerfettoNativeMemoryCleaner sCleaner =
+ new PerfettoNativeMemoryCleaner();
+
+ private PerfettoTrack(int rootType, String[] names, long[] ids) {
+ mRootType = rootType;
+ mNames = names;
+ mIds = ids;
+ }
+
+ /**
+ * The handle's native nested-tracks extra, built once and reused.
+ * Package-private; used by {@link PerfettoTrackEventBuilder#usingTrack}.
+ *
+ * <p>Lock-free lazy init: concurrent first callers may each build an equivalent
+ * {@code NestedTracks}, but they are read-only and content-identical (same uuid
+ * and descriptor, deduped per-sequence natively), so only one need survive. The
+ * {@code volatile} field publishes the winner; the rest are freed by the cleaner.
+ */
+ NestedTracks nestedTracks() {
+ NestedTracks n = mNested;
+ if (n == null) {
+ n = new NestedTracks(this, sCleaner);
+ mNested = n;
+ }
+ return n;
+ }
+
+ /** A track named {@code name} rooted at the process track. */
+ public static PerfettoTrack process(@CompileTimeConstant String name) {
+ return new PerfettoTrack(ROOT_PROCESS, new String[] {name}, new long[] {DEFAULT_ID});
+ }
+
+ /** A track named {@code name} rooted at the calling thread's track. */
+ public static PerfettoTrack thread(@CompileTimeConstant String name) {
+ return new PerfettoTrack(ROOT_THREAD, new String[] {name}, new long[] {DEFAULT_ID});
+ }
+
+ /** A track named {@code name} rooted at the global scope. */
+ public static PerfettoTrack global(@CompileTimeConstant String name) {
+ return new PerfettoTrack(ROOT_GLOBAL, new String[] {name}, new long[] {DEFAULT_ID});
+ }
+
+ /** A child track named {@code name} nested under this one. */
+ public PerfettoTrack child(@CompileTimeConstant String name) {
+ return child(DEFAULT_ID, name);
+ }
+
+ /**
+ * A child track named {@code name} nested under this one. {@code id} further
+ * disambiguates the track from same-named siblings.
+ */
+ public PerfettoTrack child(long id, @CompileTimeConstant String name) {
+ int n = mNames.length;
+ String[] names = new String[n + 1];
+ long[] ids = new long[n + 1];
+ System.arraycopy(mNames, 0, names, 0, n);
+ System.arraycopy(mIds, 0, ids, 0, n);
+ names[n] = name;
+ ids[n] = id;
+ return new PerfettoTrack(mRootType, names, ids);
+ }
+}
diff --git a/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventBuilder.java b/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventBuilder.java
index c02b948..3170b3c 100644
--- a/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventBuilder.java
+++ b/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventBuilder.java
@@ -358,7 +358,27 @@
return this;
}
- /** Adds the events to a named track instead of the thread track where the event occurred. */
+ /**
+ * Emits this event on {@code track}, a (possibly nested) named track. The
+ * descriptor for each level of the chain is emitted once per sequence; the
+ * native side derives the per-level uuids. The handle owns its native track
+ * (built once on first use, reused for its lifetime), so a reused {@code track}
+ * -- the intended {@code static final} usage -- is allocation-free.
+ */
+ public PerfettoTrackEventBuilder usingTrack(PerfettoTrack track) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (mIsDebug) {
+ checkNotBuildingProto();
+ }
+
+ // The handle owns its native track (built once, reused for its lifetime), so
+ // there is no builder-side cache to consult -- just hand it over.
+ addPerfettoPointerToExtra(track.nestedTracks());
+ return this;
+ }
+
public PerfettoTrackEventBuilder usingNamedTrack(
long id, @CompileTimeConstant String name, long parentUuid) {
return usingNamedTrack(id, name, parentUuid, /* isNameStatic = */ true);
diff --git a/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventExtra.java b/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventExtra.java
index 15d75bb..c9faaca 100644
--- a/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventExtra.java
+++ b/src/android_sdk/java/main/dev/perfetto/sdk/PerfettoTrackEventExtra.java
@@ -158,6 +158,37 @@
private static native long native_get_extra_ptr(long ptr);
}
+ /**
+ * A nested chain of named tracks emitted via the HL {@code NESTED_TRACKS} extra.
+ * Built once per {@link PerfettoTrack} (cached by the builder) so the emit path
+ * stays allocation-free; the native side derives the per-level uuids and emits a
+ * {@code TrackDescriptor} for each level once per sequence.
+ */
+ static final class NestedTracks implements PerfettoPointer {
+ private final long mPtr;
+ private final long mExtraPtr;
+
+ NestedTracks(PerfettoTrack track, PerfettoNativeMemoryCleaner memoryCleaner) {
+ mPtr = native_init(track.mRootType, track.mNames, track.mIds);
+ mExtraPtr = native_get_extra_ptr(mPtr);
+ memoryCleaner.registerNativeAllocation(this, mPtr, native_delete());
+ }
+
+ @Override
+ public long getPtr() {
+ return mExtraPtr;
+ }
+
+ @FastNative
+ private static native long native_init(int rootType, String[] names, long[] ids);
+
+ @CriticalNative
+ private static native long native_delete();
+
+ @CriticalNative
+ private static native long native_get_extra_ptr(long ptr);
+ }
+
static final class CounterTrack implements PerfettoPointer {
private final long mPtr;
private final long mExtraPtr;
diff --git a/src/android_sdk/java/test/dev/perfetto/sdk/test/PerfettoTraceTest.java b/src/android_sdk/java/test/dev/perfetto/sdk/test/PerfettoTraceTest.java
index dfcf63b..46399dd 100644
--- a/src/android_sdk/java/test/dev/perfetto/sdk/test/PerfettoTraceTest.java
+++ b/src/android_sdk/java/test/dev/perfetto/sdk/test/PerfettoTraceTest.java
@@ -27,9 +27,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import dev.perfetto.sdk.PerfettoNativeMemoryCleaner.AllocationStats;
import dev.perfetto.sdk.PerfettoTrace;
+import dev.perfetto.sdk.PerfettoTrack;
import dev.perfetto.sdk.PerfettoTrackEventBuilder;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
@@ -259,6 +262,89 @@
}
@Test
+ public void testNestedTrack() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
+
+ PerfettoTrack parent = PerfettoTrack.process("parent_track");
+ PerfettoTrack child = parent.child("child_track");
+ PerfettoTrace.instant(FOO_CATEGORY, "event").usingTrack(child).emit();
+
+ Trace trace = Trace.parseFrom(session.close());
+
+ // Index every track descriptor by its uuid and capture the event's track.
+ Map<Long, TrackDescriptor> descriptorsByUuid = new HashMap<>();
+ long eventTrackUuid = 0;
+ for (TracePacket packet : trace.getPacketList()) {
+ if (packet.hasTrackDescriptor()) {
+ TrackDescriptor td = packet.getTrackDescriptor();
+ descriptorsByUuid.put(td.getUuid(), td);
+ }
+ if (packet.hasTrackEvent()
+ && TrackEvent.Type.TYPE_INSTANT.equals(packet.getTrackEvent().getType())
+ && packet.getTrackEvent().hasTrackUuid()) {
+ eventTrackUuid = packet.getTrackEvent().getTrackUuid();
+ }
+ }
+
+ // The event is on the leaf (child) track.
+ TrackDescriptor childTd = descriptorsByUuid.get(eventTrackUuid);
+ assertThat(childTd).isNotNull();
+ assertThat(childTd.getName()).isEqualTo("child_track");
+
+ // The child is nested under the parent track.
+ TrackDescriptor parentTd = descriptorsByUuid.get(childTd.getParentUuid());
+ assertThat(parentTd).isNotNull();
+ assertThat(parentTd.getName()).isEqualTo("parent_track");
+
+ // The parent track is rooted at the process track.
+ assertThat(parentTd.getParentUuid()).isEqualTo(PerfettoTrace.getProcessTrackUuid());
+ }
+
+ @Test
+ public void testGlobalNestedTrack() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
+
+ PerfettoTrack parent = PerfettoTrack.global("global_parent");
+ PerfettoTrack child = parent.child("global_child");
+ PerfettoTrace.instant(FOO_CATEGORY, "event").usingTrack(child).emit();
+
+ Trace trace = Trace.parseFrom(session.close());
+
+ // Index every track descriptor by its uuid and capture the event's track.
+ Map<Long, TrackDescriptor> descriptorsByUuid = new HashMap<>();
+ long eventTrackUuid = 0;
+ for (TracePacket packet : trace.getPacketList()) {
+ if (packet.hasTrackDescriptor()) {
+ TrackDescriptor td = packet.getTrackDescriptor();
+ descriptorsByUuid.put(td.getUuid(), td);
+ }
+ if (packet.hasTrackEvent()
+ && TrackEvent.Type.TYPE_INSTANT.equals(packet.getTrackEvent().getType())
+ && packet.getTrackEvent().hasTrackUuid()) {
+ eventTrackUuid = packet.getTrackEvent().getTrackUuid();
+ }
+ }
+
+ // The event is on the leaf (child) track.
+ TrackDescriptor childTd = descriptorsByUuid.get(eventTrackUuid);
+ assertThat(childTd).isNotNull();
+ assertThat(childTd.getName()).isEqualTo("global_child");
+
+ // The child is nested under the global parent track.
+ TrackDescriptor parentTd = descriptorsByUuid.get(childTd.getParentUuid());
+ assertThat(parentTd).isNotNull();
+ assertThat(parentTd.getName()).isEqualTo("global_parent");
+
+ // A global root has no process/thread anchor: the outermost named level
+ // hangs off uuid 0.
+ assertThat(parentTd.getParentUuid()).isEqualTo(0);
+ }
+
+ @Test
public void testProcessThreadNamedTrack() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
diff --git a/src/android_sdk/jni/dev_perfetto_sdk_PerfettoTrackEventExtra.cc b/src/android_sdk/jni/dev_perfetto_sdk_PerfettoTrackEventExtra.cc
index 20920e0..d57199e 100644
--- a/src/android_sdk/jni/dev_perfetto_sdk_PerfettoTrackEventExtra.cc
+++ b/src/android_sdk/jni/dev_perfetto_sdk_PerfettoTrackEventExtra.cc
@@ -380,6 +380,39 @@
return toJLong(track->get());
}
+static jlong dev_perfetto_sdk_PerfettoTrackEventExtraNestedTracks_init(
+ JNIEnv* env,
+ jclass,
+ jint root_type,
+ jobjectArray names,
+ jlongArray ids) {
+ const jsize n = env->GetArrayLength(names);
+ std::vector<std::string> names_vec;
+ names_vec.reserve(static_cast<size_t>(n));
+ for (jsize i = 0; i < n; i++) {
+ jstring s = static_cast<jstring>(env->GetObjectArrayElement(names, i));
+ names_vec.emplace_back(StringBuffer::utf16_to_ascii(env, s));
+ env->DeleteLocalRef(s);
+ }
+ jlong* id_ptr = env->GetLongArrayElements(ids, nullptr);
+ std::vector<uint64_t> ids_vec(reinterpret_cast<const uint64_t*>(id_ptr),
+ reinterpret_cast<const uint64_t*>(id_ptr) + n);
+ env->ReleaseLongArrayElements(ids, id_ptr, JNI_ABORT);
+ return toJLong(new sdk_for_jni::NestedTracks(
+ static_cast<sdk_for_jni::RootType>(root_type), names_vec, ids_vec));
+}
+
+static jlong dev_perfetto_sdk_PerfettoTrackEventExtraNestedTracks_delete(
+ PERFETTO_JNI_HOST_PARAMS) {
+ return toJLong(&sdk_for_jni::NestedTracks::delete_track);
+}
+
+static jlong dev_perfetto_sdk_PerfettoTrackEventExtraNestedTracks_get_extra_ptr(
+ PERFETTO_JNI_HOST_PARAMS_COMMA jlong ptr) {
+ sdk_for_jni::NestedTracks* track = toPointer<sdk_for_jni::NestedTracks>(ptr);
+ return toJLong(track->get());
+}
+
static jlong dev_perfetto_sdk_PerfettoTrackEventExtraCounterTrack_init(
JNIEnv* env,
jclass,
@@ -596,6 +629,15 @@
(void*)dev_perfetto_sdk_PerfettoTrackEventExtraNamedTrack_get_extra_ptr},
};
+static const JNINativeMethod gNestedTracksMethods[] = {
+ {"native_init", "(I[Ljava/lang/String;[J)J",
+ (void*)dev_perfetto_sdk_PerfettoTrackEventExtraNestedTracks_init},
+ {"native_delete", "()J",
+ (void*)dev_perfetto_sdk_PerfettoTrackEventExtraNestedTracks_delete},
+ {"native_get_extra_ptr", "(J)J",
+ (void*)dev_perfetto_sdk_PerfettoTrackEventExtraNestedTracks_get_extra_ptr},
+};
+
static const JNINativeMethod gCounterTrackMethods[] = {
{"native_init", "(Ljava/lang/String;JZ)J",
(void*)dev_perfetto_sdk_PerfettoTrackEventExtraCounterTrack_init},
@@ -671,6 +713,14 @@
res = jniRegisterNativeMethods(
env,
TO_MAYBE_JAR_JAR_CLASS_NAME(
+ "dev/perfetto/sdk/PerfettoTrackEventExtra$NestedTracks"),
+ gNestedTracksMethods, NELEM(gNestedTracksMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0,
+ "Unable to register nested tracks native methods.");
+
+ res = jniRegisterNativeMethods(
+ env,
+ TO_MAYBE_JAR_JAR_CLASS_NAME(
"dev/perfetto/sdk/PerfettoTrackEventExtra$CounterTrack"),
gCounterTrackMethods, NELEM(gCounterTrackMethods));
LOG_ALWAYS_FATAL_IF(res < 0,
diff --git a/src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.cc b/src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.cc
index cc85f57..4b4b3aa 100644
--- a/src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.cc
+++ b/src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.cc
@@ -157,6 +157,49 @@
delete ptr;
}
+NestedTracks::NestedTracks(RootType root_type,
+ const std::vector<std::string>& names,
+ const std::vector<uint64_t>& ids)
+ : names_(names), root_{} {
+ const size_t count = names_.size();
+ named_.reserve(count);
+ ptrs_.reserve(count + 2);
+
+ // Outermost entry: the root scope. A process or thread root prepends one
+ // entry; a global root has none -- its first named level hangs off uuid 0.
+ switch (root_type) {
+ case RootType::kProcess:
+ root_.type = PERFETTO_TE_HL_NESTED_TRACK_TYPE_PROCESS;
+ ptrs_.push_back(&root_);
+ break;
+ case RootType::kThread:
+ root_.type = PERFETTO_TE_HL_NESTED_TRACK_TYPE_THREAD;
+ ptrs_.push_back(&root_);
+ break;
+ case RootType::kGlobal:
+ break; // No root entry; the chain hangs off uuid 0.
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ PerfettoTeHlNestedTrackNamed entry{};
+ entry.header.type = PERFETTO_TE_HL_NESTED_TRACK_TYPE_NAMED;
+ entry.name = names_[i].c_str();
+ entry.id = ids[i];
+ named_.push_back(entry);
+ }
+ for (size_t i = 0; i < count; i++) {
+ ptrs_.push_back(reinterpret_cast<PerfettoTeHlNestedTrack*>(&named_[i]));
+ }
+ ptrs_.push_back(nullptr);
+
+ extra_.header.type = PERFETTO_TE_HL_EXTRA_TYPE_NESTED_TRACKS;
+ extra_.tracks = ptrs_.data();
+}
+
+void NestedTracks::delete_track(NestedTracks* ptr) {
+ delete ptr;
+}
+
RegisteredTrack::RegisteredTrack(uint64_t id,
uint64_t parent_uuid,
const std::string& name,
diff --git a/src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.h b/src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.h
index 5d06d9e..34547cd 100644
--- a/src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.h
+++ b/src/android_sdk/perfetto_sdk_for_jni/tracing_sdk.h
@@ -179,6 +179,42 @@
PerfettoTeHlExtraNamedTrack track_;
};
+// Root scope of a nested-track chain. Values match PerfettoTrack (Java) and are
+// passed verbatim over JNI.
+enum class RootType : int {
+ kGlobal = 0,
+ kProcess = 1,
+ kThread = 2,
+};
+
+/**
+ * @brief A nested chain of named tracks (the HL NESTED_TRACKS extra).
+ *
+ * The chain is, outermost first: an optional root (process or thread; global
+ * roots have none) followed by one named level per name. The native HL path
+ * derives the per-level uuids and emits a descriptor for each level.
+ */
+class NestedTracks {
+ public:
+ NestedTracks(RootType root_type,
+ const std::vector<std::string>& names,
+ const std::vector<uint64_t>& ids);
+
+ static void delete_track(NestedTracks* track);
+
+ const PerfettoTeHlExtraNestedTracks* get() const { return &extra_; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NestedTracks);
+ // ptrs_ point into named_/root_ and named_[i].name into names_; nothing is
+ // resized after construction, so the pointers stay valid.
+ std::vector<std::string> names_;
+ PerfettoTeHlNestedTrack root_;
+ std::vector<PerfettoTeHlNestedTrackNamed> named_;
+ std::vector<PerfettoTeHlNestedTrack*> ptrs_;
+ PerfettoTeHlExtraNestedTracks extra_;
+};
+
/**
* @brief Represents a registered track.
*/