Adding experimental Tracy API for TLS-less event recording. (#19625)
This allows for events from remote threads (remove devices, sandboxes,
other tracing APIs, etc) to be plumbed into Tracy during execution. It
has a lot of rough edges and there's a list of TODOs required to make
Tracy work better with this style of recording.
diff --git a/runtime/src/iree/base/tracing/tracy.cc b/runtime/src/iree/base/tracing/tracy.cc
index 6272bb0..129fb75 100644
--- a/runtime/src/iree/base/tracing/tracy.cc
+++ b/runtime/src/iree/base/tracing/tracy.cc
@@ -13,6 +13,13 @@
#include "TracyClient.cpp"
#endif // IREE_TRACING_FEATURES
+#if defined(TRACY_ENABLE) && IREE_TRACING_EXPERIMENTAL_CONTEXT_API
+// HACK: tracy doesn't let us at this but we need it in order to create new
+// queue contexts. It's an implementation detail we have to take a dependency on
+// because tracy does not have an API for what we're doing (yet).
+extern tracy::moodycamel::ConcurrentQueue<tracy::QueueItem> tracy::s_queue;
+#endif // TRACY_ENABLE && IREE_TRACING_EXPERIMENTAL_CONTEXT_API
+
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
@@ -438,6 +445,236 @@
void* iree_tracing_obscure_ptr(void* ptr) { return ptr; }
#endif // IREE_TRACING_FEATURE_ALLOCATION_TRACKING
+//===----------------------------------------------------------------------===//
+// Experimental Tracing Interop API
+//===----------------------------------------------------------------------===//
+
+#if IREE_TRACING_EXPERIMENTAL_CONTEXT_API
+
+struct iree_tracing_context_t {
+ static std::atomic<uint32_t> next_tracing_thread_id;
+ tracy::moodycamel::ProducerToken token_detail;
+ tracy::ProducerWrapper token;
+ uint32_t thread_id = 0;
+ iree_tracing_context_t()
+ : token_detail(tracy::s_queue),
+ token({tracy::s_queue.get_explicit_producer(token_detail)}),
+ thread_id(iree_tracing_context_t::next_tracing_thread_id++) {
+ token.ptr->threadId = thread_id;
+ }
+};
+
+// static
+std::atomic<uint32_t> iree_tracing_context_t::next_tracing_thread_id{
+ 0x80000000u};
+
+#define IREE_TRACING_CONTEXT_BEGIN_WRITE(context, queue_type) \
+ tracy::moodycamel::ConcurrentQueueDefaultTraits::index_t __magic; \
+ tracy::moodycamel::ConcurrentQueue<tracy::QueueItem>::ExplicitProducer* \
+ __token = (context)->token.ptr; \
+ auto& __tail = __token->get_tail_index(); \
+ auto item = __token->enqueue_begin(__magic); \
+ tracy::MemWrite(&item->hdr.type, (queue_type));
+
+#define IREE_TRACING_CONTEXT_END_WRITE(context) \
+ __tail.store(__magic + 1, std::memory_order_release);
+
+iree_tracing_context_t* iree_tracing_context_allocate(
+ const char* name, iree_host_size_t name_length) {
+ iree_tracing_context_t* context = new iree_tracing_context_t();
+
+ // TODO(benvanik): upstream a tracy::Profiler::SetThreadNameWithHint that
+ // only updates the GetThreadNameData() linked list with a new entry. Today
+ // there's no way to set the thread name explicitly.
+
+ return context;
+}
+
+void iree_tracing_context_free(iree_tracing_context_t* context) {
+ if (context) delete context;
+}
+
+void iree_tracing_context_calibrate_executor(
+ iree_tracing_context_t* context, iree_tracing_executor_id_t executor_id,
+ int64_t cpu_delta, uint64_t host_timestamp, uint64_t executor_timestamp) {
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::GpuCalibration);
+ tracy::MemWrite(&item->gpuCalibration.gpuTime, executor_timestamp);
+ tracy::MemWrite(&item->gpuCalibration.cpuTime, host_timestamp);
+ tracy::MemWrite(&item->gpuCalibration.cpuDelta, cpu_delta);
+ tracy::MemWrite(&item->gpuCalibration.context, executor_id);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+void iree_tracing_context_zone_begin(iree_tracing_context_t* context,
+ uint64_t timestamp,
+ const iree_tracing_location_t* src_loc) {
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::ZoneBegin);
+ tracy::MemWrite(&item->zoneBegin.time, timestamp);
+ tracy::MemWrite(&item->zoneBegin.srcloc, reinterpret_cast<uint64_t>(src_loc));
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+void iree_tracing_context_zone_end(iree_tracing_context_t* context,
+ uint64_t timestamp) {
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::ZoneEnd);
+ tracy::MemWrite(&item->zoneEnd.time, timestamp);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+void iree_tracing_context_zone_value_i64(iree_tracing_context_t* context,
+ uint64_t value) {
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::ZoneValue);
+ tracy::MemWrite(&item->zoneValue.value, value);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+void iree_tracing_context_zone_value_text_literal(
+ iree_tracing_context_t* context, const char* value) {
+ // NOTE: no literal tracing support, have to use the slow path.
+ iree_tracing_context_zone_value_text_dynamic(context, value, strlen(value));
+}
+
+void iree_tracing_context_zone_value_text_dynamic(
+ iree_tracing_context_t* context, const char* value,
+ iree_host_size_t value_length) {
+ auto ptr = (char*)tracy::tracy_malloc(value_length);
+ memcpy(ptr, value, value_length);
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::ZoneText);
+ tracy::MemWrite(&item->zoneTextFat.text, (uint64_t)ptr);
+ tracy::MemWrite(&item->zoneTextFat.size, (uint16_t)value_length);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+// TODO(benvanik): figure out why serial recording works with GPU zones and
+// thread-local recording doesn't (sometimes?). May be timing related.
+#define IREE_TRACING_CONTEXT_SERIAL_FALLBACK 1
+
+void iree_tracing_context_execution_zone_begin(
+ iree_tracing_context_t* context, uint64_t timestamp,
+ const iree_tracing_location_t* src_loc,
+ iree_tracing_executor_id_t executor_id, iree_tracing_query_id_t query_id) {
+#if IREE_TRACING_CONTEXT_SERIAL_FALLBACK
+ auto* item = tracy::Profiler::QueueSerial();
+ tracy::MemWrite(&item->hdr.type, tracy::QueueType::GpuZoneBeginSerial);
+ tracy::MemWrite(&item->gpuZoneBegin.cpuTime, timestamp);
+ tracy::MemWrite(&item->gpuZoneBegin.srcloc, src_loc);
+ tracy::MemWrite(&item->gpuZoneBegin.thread, context->thread_id);
+ tracy::MemWrite(&item->gpuZoneBegin.queryId, query_id);
+ tracy::MemWrite(&item->gpuZoneBegin.context, executor_id);
+ tracy::Profiler::QueueSerialFinish();
+#else
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::GpuZoneBegin);
+ tracy::MemWrite(&item->gpuZoneBegin.cpuTime, timestamp);
+ tracy::MemWrite(&item->gpuZoneBegin.thread, context->thread_id);
+ tracy::MemWrite(&item->gpuZoneBegin.queryId, query_id);
+ tracy::MemWrite(&item->gpuZoneBegin.context, executor_id);
+ tracy::MemWrite(&item->gpuZoneBegin.srcloc, src_loc);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+#endif // IREE_TRACING_CONTEXT_SERIAL_FALLBACK
+}
+
+void iree_tracing_context_execution_zone_end(
+ iree_tracing_context_t* context, uint64_t timestamp,
+ iree_tracing_executor_id_t executor_id, iree_tracing_query_id_t query_id) {
+#if IREE_TRACING_CONTEXT_SERIAL_FALLBACK
+ auto* item = tracy::Profiler::QueueSerial();
+ tracy::MemWrite(&item->hdr.type, tracy::QueueType::GpuZoneEndSerial);
+ tracy::MemWrite(&item->gpuZoneEnd.cpuTime, timestamp);
+ tracy::MemWrite(&item->gpuZoneEnd.thread, context->thread_id);
+ tracy::MemWrite(&item->gpuZoneEnd.queryId, query_id);
+ tracy::MemWrite(&item->gpuZoneEnd.context, executor_id);
+ tracy::Profiler::QueueSerialFinish();
+#else
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::GpuZoneEnd);
+ tracy::MemWrite(&item->gpuZoneEnd.cpuTime, timestamp);
+ tracy::MemWrite(&item->gpuZoneEnd.thread, context->thread_id);
+ tracy::MemWrite(&item->gpuZoneEnd.queryId, query_id);
+ tracy::MemWrite(&item->gpuZoneEnd.context, executor_id);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+#endif // IREE_TRACING_CONTEXT_SERIAL_FALLBACK
+}
+
+void iree_tracing_context_execution_zone_notify(
+ iree_tracing_context_t* context, iree_tracing_executor_id_t executor_id,
+ iree_tracing_query_id_t query_id, uint64_t query_timestamp) {
+#if IREE_TRACING_CONTEXT_SERIAL_FALLBACK
+ iree_tracing_gpu_zone_notify(executor_id, query_id, query_timestamp);
+ auto* item = tracy::Profiler::QueueSerial();
+ tracy::MemWrite(&item->hdr.type, tracy::QueueType::GpuTime);
+ tracy::MemWrite(&item->gpuTime.gpuTime, query_timestamp);
+ tracy::MemWrite(&item->gpuTime.queryId, query_id);
+ tracy::MemWrite(&item->gpuTime.context, executor_id);
+ tracy::Profiler::QueueSerialFinish();
+#else
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::GpuTime);
+ tracy::MemWrite(&item->gpuTime.gpuTime, query_timestamp);
+ tracy::MemWrite(&item->gpuTime.queryId, query_id);
+ tracy::MemWrite(&item->gpuTime.context, executor_id);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+#endif // IREE_TRACING_CONTEXT_SERIAL_FALLBACK
+}
+
+void iree_tracing_context_memory_alloc(iree_tracing_context_t* context,
+ uint64_t timestamp, const char* pool,
+ uint64_t ptr, uint64_t size) {
+ // TODO(benvanik): add a thread override to MemAllocNamed - it does shady
+ // things with m_memNamePayload that we can't easily replicate outside of the
+ // tracy implementation.
+}
+
+void iree_tracing_context_memory_free(iree_tracing_context_t* context,
+ uint64_t timestamp, const char* pool,
+ uint64_t ptr) {
+ // TODO(benvanik): add a thread override to MemFreeNamed- it does shady
+ // things with m_memNamePayload that we can't easily replicate outside of the
+ // tracy implementation.
+}
+
+void iree_tracing_context_message_literal(iree_tracing_context_t* context,
+ uint64_t timestamp,
+ const char* value) {
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::MessageLiteral);
+ tracy::MemWrite(&item->messageLiteral.time, timestamp);
+ tracy::MemWrite(&item->messageLiteral.text, (uint64_t)value);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+void iree_tracing_context_message_dynamic(iree_tracing_context_t* context,
+ uint64_t timestamp, const char* value,
+ iree_host_size_t value_length) {
+ auto ptr = (char*)tracy::tracy_malloc(value_length);
+ memcpy(ptr, value, value_length);
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::Message);
+ tracy::MemWrite(&item->messageFat.time, timestamp);
+ tracy::MemWrite(&item->messageFat.text, (uint64_t)ptr);
+ tracy::MemWrite(&item->messageFat.size, (uint16_t)value_length);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+void iree_tracing_context_plot_config(iree_tracing_context_t* context,
+ const char* name_literal, uint8_t type,
+ bool step, bool fill, uint32_t color) {
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::PlotConfig);
+ tracy::MemWrite(&item->plotConfig.name, (uint64_t)name_literal);
+ tracy::MemWrite(&item->plotConfig.type, (uint8_t)type);
+ tracy::MemWrite(&item->plotConfig.step, (uint8_t)step);
+ tracy::MemWrite(&item->plotConfig.fill, (uint8_t)fill);
+ tracy::MemWrite(&item->plotConfig.color, color);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+void iree_tracing_context_plot_value_i64(iree_tracing_context_t* context,
+ uint64_t timestamp,
+ const char* plot_name, int64_t value) {
+ IREE_TRACING_CONTEXT_BEGIN_WRITE(context, tracy::QueueType::PlotDataInt);
+ tracy::MemWrite(&item->plotDataInt.name, (uint64_t)plot_name);
+ tracy::MemWrite(&item->plotDataInt.time, timestamp);
+ tracy::MemWrite(&item->plotDataInt.val, value);
+ IREE_TRACING_CONTEXT_END_WRITE(context);
+}
+
+#endif // IREE_TRACING_EXPERIMENTAL_CONTEXT_API
+
#endif // IREE_TRACING_FEATURES
#ifdef __cplusplus
diff --git a/runtime/src/iree/base/tracing/tracy.h b/runtime/src/iree/base/tracing/tracy.h
index 5edbf79..bc6d4ab 100644
--- a/runtime/src/iree/base/tracing/tracy.h
+++ b/runtime/src/iree/base/tracing/tracy.h
@@ -88,14 +88,29 @@
// Local zone ID used for the C IREE_TRACE_ZONE_* macros.
typedef uint32_t iree_zone_id_t;
+typedef struct ___tracy_source_location_data iree_tracing_location_t;
+
+// Matches GpuContextType.
+// TODO(benvanik): upstream a few more enum values for CUDA/Metal/etc.
+// The only real behavior that changes in tracy is around whether multi-threaded
+// recording is assumed and IREE_TRACING_GPU_CONTEXT_TYPE_VULKAN is a safe
+// default choice - the context name provided during creation should be
+// descriptive enough for the user.
+typedef enum iree_tracing_gpu_context_type_e {
+ IREE_TRACING_GPU_CONTEXT_TYPE_INVALID = 0,
+ IREE_TRACING_GPU_CONTEXT_TYPE_OPENGL,
+ IREE_TRACING_GPU_CONTEXT_TYPE_VULKAN,
+ IREE_TRACING_GPU_CONTEXT_TYPE_OPENCL,
+ IREE_TRACING_GPU_CONTEXT_TYPE_DIRECT3D12,
+ IREE_TRACING_GPU_CONTEXT_TYPE_DIRECT3D11,
+} iree_tracing_gpu_context_type_t;
+
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if IREE_TRACING_FEATURES
-typedef struct ___tracy_source_location_data iree_tracing_location_t;
-
#ifdef __cplusplus
#define iree_tracing_make_zone_ctx(zone_id) \
TracyCZoneCtx { zone_id, 1 }
@@ -141,21 +156,6 @@
int64_t iree_tracing_time(void);
int64_t iree_tracing_frequency(void);
-// Matches GpuContextType.
-// TODO(benvanik): upstream a few more enum values for CUDA/Metal/etc.
-// The only real behavior that changes in tracy is around whether multi-threaded
-// recording is assumed and IREE_TRACING_GPU_CONTEXT_TYPE_VULKAN is a safe
-// default choice - the context name provided during creation should be
-// descriptive enough for the user.
-typedef enum iree_tracing_gpu_context_type_e {
- IREE_TRACING_GPU_CONTEXT_TYPE_INVALID = 0,
- IREE_TRACING_GPU_CONTEXT_TYPE_OPENGL,
- IREE_TRACING_GPU_CONTEXT_TYPE_VULKAN,
- IREE_TRACING_GPU_CONTEXT_TYPE_OPENCL,
- IREE_TRACING_GPU_CONTEXT_TYPE_DIRECT3D12,
- IREE_TRACING_GPU_CONTEXT_TYPE_DIRECT3D11,
-} iree_tracing_gpu_context_type_t;
-
uint8_t iree_tracing_gpu_context_allocate(iree_tracing_gpu_context_type_t type,
const char* name, size_t name_length,
bool is_calibrated,
@@ -329,6 +329,162 @@
#endif // IREE_TRACING_FEATURE_ALLOCATION_TRACKING
//===----------------------------------------------------------------------===//
+// Experimental Tracing Interop API
+//===----------------------------------------------------------------------===//
+
+// Enables a mechanism for recording trace events without relying on
+// thread local state managed by the underlying tracing implementation to allow
+// for a single thread to publish events for any number of threads. This is
+// required when interoping with non-thread-based event sources (remote devices,
+// virtualized processes, threads running in sandboxes, etc). Since support both
+// in IREE and in Tracy for this style of event generation is experimental the
+// normal TLS-based tracing API should be used in most cases.
+//
+// TODO:
+// - Add a tracy::Profiler::SetThreadNameWithHint that updates the
+// GetThreadNameData() linked list and allow us to set thread names.
+// - Add a tracy::QueueType::ZoneTextLiteral that takes its payload as a ptr.
+// - Add a thread override to MemAllocNamed and MemFreeNamed.
+// - Fix GPU zones to not require serial recording.
+//
+// The implementation relies on details of Tracy internals as it doesn't have
+// an API for what we're doing. We could propose one and try to get it landed
+// such that we at least did not need to manually write out structs and manage
+// the concurrent queues.
+#define IREE_TRACING_EXPERIMENTAL_CONTEXT_API 1
+
+typedef struct iree_tracing_context_t iree_tracing_context_t;
+
+typedef uint8_t iree_tracing_executor_id_t;
+typedef uint16_t iree_tracing_query_id_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+#if IREE_TRACING_FEATURES && IREE_TRACING_EXPERIMENTAL_CONTEXT_API
+
+iree_tracing_context_t* iree_tracing_context_allocate(
+ const char* name, iree_host_size_t name_length);
+
+void iree_tracing_context_free(iree_tracing_context_t* context);
+
+void iree_tracing_context_calibrate_executor(
+ iree_tracing_context_t* context, iree_tracing_executor_id_t executor_id,
+ int64_t cpu_delta, uint64_t host_timestamp, uint64_t executor_timestamp);
+
+void iree_tracing_context_zone_begin(iree_tracing_context_t* context,
+ uint64_t timestamp,
+ const iree_tracing_location_t* src_loc);
+
+void iree_tracing_context_zone_end(iree_tracing_context_t* context,
+ uint64_t timestamp);
+
+void iree_tracing_context_zone_value_i64(iree_tracing_context_t* context,
+ uint64_t value);
+
+void iree_tracing_context_zone_value_text_literal(
+ iree_tracing_context_t* context, const char* value);
+
+void iree_tracing_context_zone_value_text_dynamic(
+ iree_tracing_context_t* context, const char* value,
+ iree_host_size_t value_length);
+
+void iree_tracing_context_execution_zone_begin(
+ iree_tracing_context_t* context, uint64_t timestamp,
+ const iree_tracing_location_t* src_loc,
+ iree_tracing_executor_id_t executor_id, iree_tracing_query_id_t query_id);
+
+void iree_tracing_context_execution_zone_end(
+ iree_tracing_context_t* context, uint64_t timestamp,
+ iree_tracing_executor_id_t executor_id, iree_tracing_query_id_t query_id);
+
+void iree_tracing_context_execution_zone_notify(
+ iree_tracing_context_t* context, iree_tracing_executor_id_t executor_id,
+ iree_tracing_query_id_t query_id, uint64_t query_timestamp);
+
+void iree_tracing_context_memory_alloc(iree_tracing_context_t* context,
+ uint64_t timestamp, const char* pool,
+ uint64_t ptr, uint64_t size);
+
+void iree_tracing_context_memory_free(iree_tracing_context_t* context,
+ uint64_t timestamp, const char* pool,
+ uint64_t ptr);
+
+void iree_tracing_context_message_literal(iree_tracing_context_t* context,
+ uint64_t timestamp,
+ const char* value);
+
+void iree_tracing_context_message_dynamic(iree_tracing_context_t* context,
+ uint64_t timestamp, const char* value,
+ iree_host_size_t value_length);
+
+void iree_tracing_context_plot_config(iree_tracing_context_t* context,
+ const char* name_literal, uint8_t type,
+ bool step, bool fill, uint32_t color);
+
+void iree_tracing_context_plot_value_i64(iree_tracing_context_t* context,
+ uint64_t timestamp,
+ const char* plot_name, int64_t value);
+
+#else
+
+static inline iree_tracing_context_t* iree_tracing_context_allocate(
+ const char* name, iree_host_size_t name_length) {
+ return NULL;
+}
+static inline void iree_tracing_context_free(iree_tracing_context_t* context) {}
+void iree_tracing_context_calibrate_executor(
+ iree_tracing_context_t* context, iree_tracing_executor_id_t executor_id,
+ int64_t cpu_delta, uint64_t host_timestamp, uint64_t executor_timestamp) {}
+static inline void iree_tracing_context_zone_begin(
+ iree_tracing_context_t* context, uint64_t timestamp,
+ const iree_tracing_location_t* src_loc) {}
+static inline void iree_tracing_context_zone_end(
+ iree_tracing_context_t* context, uint64_t timestamp) {}
+static inline void iree_tracing_context_zone_value_i64(
+ iree_tracing_context_t* context, uint64_t value) {}
+static inline void iree_tracing_context_zone_value_text_literal(
+ iree_tracing_context_t* context, const char* value) {}
+static inline void iree_tracing_context_zone_value_text_dynamic(
+ iree_tracing_context_t* context, const char* value,
+ iree_host_size_t value_length) {}
+static inline void iree_tracing_context_execution_zone_begin(
+ iree_tracing_context_t* context, uint64_t timestamp,
+ const iree_tracing_location_t* src_loc,
+ iree_tracing_executor_id_t executor_id, iree_tracing_query_id_t query_id) {}
+static inline void iree_tracing_context_execution_zone_end(
+ iree_tracing_context_t* context, uint64_t timestamp,
+ iree_tracing_executor_id_t executor_id, iree_tracing_query_id_t query_id) {}
+static inline void iree_tracing_context_execution_zone_notify(
+ iree_tracing_context_t* context, iree_tracing_executor_id_t executor_id,
+ iree_tracing_query_id_t query_id, uint64_t query_timestamp) {}
+static inline void iree_tracing_context_memory_alloc(
+ iree_tracing_context_t* context, uint64_t timestamp, const char* pool,
+ uint64_t ptr, uint64_t size) {}
+static inline void iree_tracing_context_memory_free(
+ iree_tracing_context_t* context, uint64_t timestamp, const char* pool,
+ uint64_t ptr) {}
+static inline void iree_tracing_context_message_literal(
+ iree_tracing_context_t* context, uint64_t timestamp, const char* value) {}
+static inline void iree_tracing_context_message_dynamic(
+ iree_tracing_context_t* context, uint64_t timestamp, const char* value,
+ iree_host_size_t value_length) {}
+static void iree_tracing_context_plot_config(iree_tracing_context_t* context,
+ const char* name_literal,
+ uint8_t type, bool step, bool fill,
+ uint32_t color) {}
+static inline void iree_tracing_context_plot_value_i64(
+ iree_tracing_context_t* context, uint64_t timestamp, const char* plot_name,
+ int64_t value) {}
+
+#endif // IREE_TRACING_FEATURES && IREE_TRACING_EXPERIMENTAL_CONTEXT_API
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+//===----------------------------------------------------------------------===//
// Instrumentation C++ RAII types, wrappers, and macros
//===----------------------------------------------------------------------===//