blob: a3226ad48e4298a760e7f207bdfbe8cbbdaa0705 [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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
//
// https://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.
// Utilities for runtime tracing support.
// These allow the various runtime subsystems to insert trace events, attach
// metadata to events or allocations, and control tracing verbosity.
//
// Tracing features can be enabled with either an IREE_TRACING_MODE define that
// allows predefined tracing modes or individual IREE_TRACING_FEATURE_* flags
// set on IREE_TRACING_FEATURES when a more custom set of features is
// required. Exact feature support may vary on platform and toolchain.
//
// The tracing infrastructure is currently designed to target the Tracy
// profiler: https://github.com/wolfpld/tracy
// Tracy's profiler UI allowing for streaming captures and analysis can be
// downloaded from: https://github.com/wolfpld/tracy/releases
// The manual provided on the releases page contains more information about how
// Tracy works, its limitations, and how to operate the UI.
//
// NOTE: this header is used both from C and C++ code and only conditionally
// enables the C++ when in a valid context. Do not use C++ features or include
// other files that are not C-compatible.
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "absl/base/attributes.h"
#ifndef IREE_BASE_TRACING_H_
#define IREE_BASE_TRACING_H_
//===----------------------------------------------------------------------===//
// IREE_TRACING_FEATURE_* flags and options
//===----------------------------------------------------------------------===//
// Enables IREE_TRACE_* macros for instrumented tracing.
#define IREE_TRACING_FEATURE_INSTRUMENTATION (1 << 0)
// Captures callstacks up to IREE_TRACING_MAX_CALLSTACK_DEPTH at all
// IREE_TRACE_* events. This has a significant performance impact and should
// only be enabled when tracking down missing instrumentation.
#define IREE_TRACING_FEATURE_INSTRUMENTATION_CALLSTACKS (1 << 1)
// Tracks all allocations (we know about) via new/delete/malloc/free.
// This allows fine-grained allocation and usage tracking down to the code that
// performed the allocations. Allocations or frees that are performed outside of
// the IREE API or runtime library will not be tracked and unbalanced usage
// (allocating with IREE's API then freeing with stdlib free, for example) will
// cause Tracy to become very unhappy.
#define IREE_TRACING_FEATURE_ALLOCATION_TRACKING (1 << 2)
// Captures callstacks up to IREE_TRACING_MAX_CALLSTACK_DEPTH at all allocation
// events when allocation tracking is enabled.
#define IREE_TRACING_FEATURE_ALLOCATION_CALLSTACKS (1 << 3)
// Tracks fast locks in all cases (both contended and uncontended).
// This may introduce contention where there would otherwise be none as what
// would be a handful of instructions and little memory access may become
// hundreds. To see only locks under contention use
// IREE_TRACING_FEATURE_SLOW_LOCKS.
#define IREE_TRACING_FEATURE_FAST_LOCKS (1 << 4)
// Tracks slow locks that end up going to the OS for waits/wakes in futexes.
// Uncontended locks will not be displayed and only waits will be visible in the
// Tracy UI.
#define IREE_TRACING_FEATURE_SLOW_LOCKS (1 << 5)
#if !defined(IREE_TRACING_MAX_CALLSTACK_DEPTH)
// Tracing functions that capture stack traces will only capture up to N frames.
// The overhead for stack walking scales linearly with the number of frames
// captured and can increase the cost of an event capture by orders of
// magnitude.
// Minimum: 0 (disable)
// Maximum: 62
#define IREE_TRACING_MAX_CALLSTACK_DEPTH 16
#endif // IREE_TRACING_MAX_CALLSTACK_DEPTH
//===----------------------------------------------------------------------===//
// IREE_TRACING_MODE simple setting
//===----------------------------------------------------------------------===//
// Set IREE_TRACING_FEATURES based on IREE_TRACING_MODE if the user hasn't
// overridden it with more specific settings.
//
// IREE_TRACING_MODE = 0: tracing disabled
// IREE_TRACING_MODE = 1: instrumentation and basic statistics
// IREE_TRACING_MODE = 2: same as 1 with added allocation tracking
// IREE_TRACING_MODE = 3: same as 2 with callstacks for allocations
// IREE_TRACING_MODE = 4: same as 3 with callstacks for all instrumentation
#if !defined(IREE_TRACING_FEATURES)
#if defined(IREE_TRACING_MODE) && IREE_TRACING_MODE == 1
#define IREE_TRACING_FEATURES (IREE_TRACING_FEATURE_INSTRUMENTATION)
#undef IREE_TRACING_MAX_CALLSTACK_DEPTH
#define IREE_TRACING_MAX_CALLSTACK_DEPTH 0
#elif defined(IREE_TRACING_MODE) && IREE_TRACING_MODE == 2
#define IREE_TRACING_FEATURES \
(IREE_TRACING_FEATURE_INSTRUMENTATION | \
IREE_TRACING_FEATURE_ALLOCATION_TRACKING)
#elif defined(IREE_TRACING_MODE) && IREE_TRACING_MODE == 3
#define IREE_TRACING_FEATURES \
(IREE_TRACING_FEATURE_INSTRUMENTATION | \
IREE_TRACING_FEATURE_ALLOCATION_TRACKING | \
IREE_TRACING_FEATURE_ALLOCATION_CALLSTACKS)
#elif defined(IREE_TRACING_MODE) && IREE_TRACING_MODE >= 4
#define IREE_TRACING_FEATURES \
(IREE_TRACING_FEATURE_INSTRUMENTATION | \
IREE_TRACING_FEATURE_INSTRUMENTATION_CALLSTACKS | \
IREE_TRACING_FEATURE_ALLOCATION_TRACKING | \
IREE_TRACING_FEATURE_ALLOCATION_CALLSTACKS)
#else
#define IREE_TRACING_FEATURES 0
#endif // IREE_TRACING_MODE
#endif // !IREE_TRACING_FEATURES
//===----------------------------------------------------------------------===//
// Tracy configuration
//===----------------------------------------------------------------------===//
// NOTE: order matters here as we are including files that require/define.
// Enable Tracy only when we are using tracing features.
#if IREE_TRACING_FEATURES != 0
#define TRACY_ENABLE 1
#endif // IREE_TRACING_FEATURES
// Disable zone nesting verification in release builds.
// The verification makes it easy to find unbalanced zones but doubles the cost
// (at least) of each zone recorded. Run in debug builds to verify new
// instrumentation is correct before capturing traces in release builds.
#if defined(NDEBUG)
#define TRACY_NO_VERIFY 1
#endif // NDEBUG
// Force callstack capture on all zones (even those without the C suffix).
#if (IREE_TRACING_FEATURES & \
IREE_TRACING_FEATURE_INSTRUMENTATION_CALLSTACKS) || \
(IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_ALLOCATION_CALLSTACKS)
#define TRACY_CALLSTACK 1
#endif // IREE_TRACING_FEATURE_INSTRUMENTATION_CALLSTACKS
// TODO(#1926): upstream a TRACY_NO_FRAME_IMAGE flag to remove the frame
// compression thread and dxt1 compression code.
// Flush the settings we have so far; settings after this point will be
// overriding values set by Tracy itself.
#if defined(TRACY_ENABLE)
#include "third_party/tracy/TracyC.h" // IWYU pragma: export
#endif
// Disable callstack capture if our depth is 0; this allows us to avoid any
// expensive capture (and all the associated dependencies) if we aren't going to
// use it. Note that this means that unless code is instrumented we won't be
// able to tell what's happening in the Tracy UI.
#if IREE_TRACING_MAX_CALLSTACK_DEPTH == 0
#undef TRACY_CALLSTACK
#endif // IREE_TRACING_MAX_CALLSTACK_DEPTH
//===----------------------------------------------------------------------===//
// C API used for Tracy control
//===----------------------------------------------------------------------===//
// These functions are implementation details and should not be called directly.
// Always use the macros (or C++ RAII types).
// Local zone ID used for the C IREE_TRACE_ZONE_* macros.
typedef uint32_t iree_zone_id_t;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if IREE_TRACING_FEATURES
void iree_tracing_set_thread_name_impl(const char* name);
typedef struct ___tracy_source_location_data iree_tracing_location_t;
ABSL_MUST_USE_RESULT iree_zone_id_t
iree_tracing_zone_begin_impl(const iree_tracing_location_t* src_loc,
const char* name, size_t name_length);
ABSL_MUST_USE_RESULT iree_zone_id_t iree_tracing_zone_begin_external_impl(
const char* file_name, size_t file_name_length, uint32_t line,
const char* function_name, size_t function_name_length, const char* name,
size_t name_length);
void iree_tracing_set_plot_type_impl(const char* name_literal,
uint8_t plot_type);
void iree_tracing_plot_value_i64_impl(const char* name_literal, int64_t value);
void iree_tracing_plot_value_f32_impl(const char* name_literal, float value);
void iree_tracing_plot_value_f64_impl(const char* name_literal, double value);
void iree_tracing_mutex_announce(const iree_tracing_location_t* src_loc,
uint32_t* out_lock_id);
void iree_tracing_mutex_terminate(uint32_t lock_id);
void iree_tracing_mutex_before_lock(uint32_t lock_id);
void iree_tracing_mutex_after_lock(uint32_t lock_id);
void iree_tracing_mutex_after_unlock(uint32_t lock_id);
#endif // IREE_TRACING_FEATURES
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
//===----------------------------------------------------------------------===//
// Instrumentation macros (C)
//===----------------------------------------------------------------------===//
// Matches Tracy's PlotFormatType enum.
enum {
// Values will be displayed as plain numbers.
IREE_TRACING_PLOT_TYPE_NUMBER = 0,
// Treats the values as memory sizes. Will display kilobytes, megabytes, etc.
IREE_TRACING_PLOT_TYPE_MEMORY = 1,
// Values will be displayed as percentage with value 100 being equal to 100%.
IREE_TRACING_PLOT_TYPE_PERCENTAGE = 2,
};
// Colors used for messages based on the level provided to the macro.
enum {
IREE_TRACING_MESSAGE_LEVEL_ERROR = 0xFF0000u,
IREE_TRACING_MESSAGE_LEVEL_WARNING = 0xFFFF00u,
IREE_TRACING_MESSAGE_LEVEL_INFO = 0xFFFFFFu,
IREE_TRACING_MESSAGE_LEVEL_VERBOSE = 0xC0C0C0u,
IREE_TRACING_MESSAGE_LEVEL_DEBUG = 0x00FF00u,
};
#if IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_INSTRUMENTATION
// Sets an application-specific payload that will be stored in the trace.
// This can be used to fingerprint traces to particular versions and denote
// compilation options or configuration. The given string value will be copied.
#define IREE_TRACE_SET_APP_INFO(value, value_length) \
___tracy_emit_message_appinfo(value, value_length)
// Sets the current thread name to the given string value.
// This will only set the thread name as it appears in the tracing backend and
// not set the OS thread name as it would appear in a debugger.
// The C-string |name| will be copied and does not need to be a literal.
#define IREE_TRACE_SET_THREAD_NAME(name) iree_tracing_set_thread_name_impl(name)
// Begins a new zone with the parent function name.
#define IREE_TRACE_ZONE_BEGIN(zone_id) \
IREE_TRACE_ZONE_BEGIN_NAMED(zone_id, NULL)
// Begins a new zone with the given compile-time literal name.
#define IREE_TRACE_ZONE_BEGIN_NAMED(zone_id, name_literal) \
static const iree_tracing_location_t TracyConcat( \
__tracy_source_location, __LINE__) = {name_literal, __FUNCTION__, \
__FILE__, (uint32_t)__LINE__, 0}; \
iree_zone_id_t zone_id = iree_tracing_zone_begin_impl( \
&TracyConcat(__tracy_source_location, __LINE__), NULL, 0);
// Begins a new zone with the given runtime dynamic string name.
// The |value| string will be copied into the trace buffer.
#define IREE_TRACE_ZONE_BEGIN_NAMED_DYNAMIC(zone_id, name, name_length) \
static const iree_tracing_location_t TracyConcat( \
__tracy_source_location, __LINE__) = {0, __FUNCTION__, __FILE__, \
(uint32_t)__LINE__, 0}; \
iree_zone_id_t zone_id = iree_tracing_zone_begin_impl( \
&TracyConcat(__tracy_source_location, __LINE__), (name), (name_length));
// Begins an externally defined zone with a dynamic source location.
// The |file_name|, |function_name|, and optional |name| strings will be copied
// into the trace buffer and do not need to persist.
#define IREE_TRACE_ZONE_BEGIN_EXTERNAL( \
zone_id, file_name, file_name_length, line, function_name, \
function_name_length, name, name_length) \
iree_zone_id_t zone_id = iree_tracing_zone_begin_external_impl( \
file_name, file_name_length, line, function_name, function_name_length, \
name, name_length)
// Appends an integer value to the parent zone. May be called multiple times.
#define IREE_TRACE_ZONE_APPEND_VALUE(zone_id, value) \
___tracy_emit_zone_value((struct ___tracy_c_zone_context){zone_id, 1}, value);
// Appends a string value to the parent zone. May be called multiple times.
// The |value| string will be copied into the trace buffer.
#define IREE_TRACE_ZONE_APPEND_TEXT(...) \
IREE_TRACE_IMPL_GET_VARIADIC_((__VA_ARGS__, \
IREE_TRACE_ZONE_APPEND_TEXT_STRING_VIEW, \
IREE_TRACE_ZONE_APPEND_TEXT_CSTRING)) \
(__VA_ARGS__)
#define IREE_TRACE_ZONE_APPEND_TEXT_CSTRING(zone_id, value) \
IREE_TRACE_ZONE_APPEND_TEXT_STRING_VIEW(zone_id, value, strlen(value))
#define IREE_TRACE_ZONE_APPEND_TEXT_STRING_VIEW(zone_id, value, value_length) \
___tracy_emit_zone_text((struct ___tracy_c_zone_context){zone_id, 1}, value, \
value_length)
// Ends the current zone. Must be passed the |zone_id| from the _BEGIN.
#define IREE_TRACE_ZONE_END(zone_id) \
___tracy_emit_zone_end((struct ___tracy_c_zone_context){zone_id, 1})
// Ends the current zone before returning on a failure.
// Sugar for IREE_TRACE_ZONE_END+IREE_RETURN_IF_ERROR.
#define IREE_RETURN_AND_END_ZONE_IF_ERROR(zone_id, ...) \
IREE_RETURN_AND_EVAL_IF_ERROR(IREE_TRACE_ZONE_END(zone_id), __VA_ARGS__)
// Configures the named plot with an IREE_TRACING_PLOT_TYPE_* representation.
#define IREE_TRACE_SET_PLOT_TYPE(name_literal, plot_type) \
iree_tracing_set_plot_type_impl(name_literal, plot_type)
// Plots a value in the named plot group as an integer.
#define IREE_TRACE_PLOT_VALUE_I64(name_literal, value) \
iree_tracing_plot_value_i64_impl(name_literal, value)
// Plots a value in the named plot group as a single-precision float.
#define IREE_TRACE_PLOT_VALUE_F32(name_literal, value) \
iree_tracing_plot_value_f32_impl(name_literal, value)
// Plots a value in the named plot group as a double-precision float.
#define IREE_TRACE_PLOT_VALUE_F64(name_literal, value) \
iree_tracing_plot_value_f64_impl(name_literal, value)
// Demarcates an advancement of the top-level unnamed frame group.
#define IREE_TRACE_FRAME_MARK() ___tracy_emit_frame_mark(NULL)
// Demarcates an advancement of a named frame group.
#define IREE_TRACE_FRAME_MARK_NAMED(name_literal) \
___tracy_emit_frame_mark(name_literal)
// Begins a discontinuous frame in a named frame group.
// Must be properly matched with a IREE_TRACE_FRAME_MARK_NAMED_END.
#define IREE_TRACE_FRAME_MARK_BEGIN_NAMED(name_literal) \
___tracy_emit_frame_mark_start(name_literal)
// Ends a discontinuous frame in a named frame group.
#define IREE_TRACE_FRAME_MARK_END_NAMED(name_literal) \
___tracy_emit_frame_mark_end(name_literal)
// Logs a message at the given logging level to the trace.
// The message text must be a compile-time string literal.
#define IREE_TRACE_MESSAGE(level, value_literal) \
___tracy_emit_messageLC(value_literal, IREE_TRACING_MESSAGE_LEVEL_##level, 0)
// Logs a dynamically-allocated message at the given logging level to the trace.
// The string |value| will be copied into the trace buffer.
#define IREE_TRACE_MESSAGE_DYNAMIC(level, value, value_length) \
___tracy_emit_messageC(value, value_length, \
IREE_TRACING_MESSAGE_LEVEL_##level, 0)
// Utilities:
#define IREE_TRACE_IMPL_GET_VARIADIC_HELPER_(_1, _2, _3, NAME, ...) NAME
#define IREE_TRACE_IMPL_GET_VARIADIC_(args) \
IREE_TRACE_IMPL_GET_VARIADIC_HELPER_ args
#else
#define IREE_TRACE_SET_APP_INFO(value, value_length)
#define IREE_TRACE_SET_THREAD_NAME(name)
#define IREE_TRACE_ZONE_BEGIN(zone_id)
#define IREE_TRACE_ZONE_BEGIN_NAMED(zone_id, name_literal)
#define IREE_TRACE_ZONE_BEGIN_NAMED_DYNAMIC(zone_id, name, name_length)
#define IREE_TRACE_ZONE_BEGIN_EXTERNAL( \
zone_id, file_name, file_name_length, line, function_name, \
function_name_length, name, name_length)
#define IREE_TRACE_ZONE_APPEND_VALUE(zone_id, value)
#define IREE_TRACE_ZONE_APPEND_TEXT(zone_id, value, value_length)
#define IREE_TRACE_ZONE_END(zone_id)
#define IREE_RETURN_AND_END_ZONE_IF_ERROR(zone_id, ...) \
IREE_RETURN_IF_ERROR(__VA_ARGS__)
#define IREE_TRACE_SET_PLOT_TYPE(name_literal, plot_type)
#define IREE_TRACE_PLOT_VALUE_I64(name_literal, value)
#define IREE_TRACE_PLOT_VALUE_F32(name_literal, value)
#define IREE_TRACE_PLOT_VALUE_F64(name_literal, value)
#define IREE_TRACE_FRAME_MARK()
#define IREE_TRACE_FRAME_MARK_NAMED(name_literal)
#define IREE_TRACE_FRAME_MARK_BEGIN_NAMED(name_literal)
#define IREE_TRACE_FRAME_MARK_END_NAMED(name_literal)
#define IREE_TRACE_MESSAGE(level, value_literal)
#define IREE_TRACE_MESSAGE_DYNAMIC(level, value, value_length)
#endif // IREE_TRACING_FEATURE_INSTRUMENTATION
//===----------------------------------------------------------------------===//
// Allocation tracking macros (C/C++)
//===----------------------------------------------------------------------===//
//
// IREE_TRACE_ALLOC: records an malloc.
// IREE_TRACE_FREE: records a free.
//
// NOTE: realloc must be recorded as a FREE/ALLOC pair.
#if IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_ALLOCATION_TRACKING
#if IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_ALLOCATION_CALLSTACKS
#define IREE_TRACE_ALLOC(ptr, size) \
___tracy_emit_memory_alloc_callstack(ptr, size, \
IREE_TRACING_MAX_CALLSTACK_DEPTH, 0)
#define IREE_TRACE_FREE(ptr) \
___tracy_emit_memory_free_callstack(ptr, IREE_TRACING_MAX_CALLSTACK_DEPTH, 0)
#else
#define IREE_TRACE_ALLOC(ptr, size) ___tracy_emit_memory_alloc(ptr, size, 0)
#define IREE_TRACE_FREE(ptr) ___tracy_emit_memory_free(ptr, 0)
#endif // IREE_TRACING_FEATURE_ALLOCATION_CALLSTACKS
#else
#define IREE_TRACE_ALLOC(ptr, size)
#define IREE_TRACE_FREE(ptr)
#endif // IREE_TRACING_FEATURE_ALLOCATION_TRACKING
#if defined(__cplusplus) && \
(IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_ALLOCATION_TRACKING)
void* operator new(size_t count) noexcept;
void operator delete(void* ptr) noexcept;
#endif // __cplusplus && IREE_TRACING_FEATURE_ALLOCATION_TRACKING
//===----------------------------------------------------------------------===//
// Instrumentation C++ RAII types, wrappers, and macros
//===----------------------------------------------------------------------===//
#ifdef __cplusplus
#if defined(TRACY_ENABLE)
#include "third_party/tracy/Tracy.hpp" // IWYU pragma: export
#endif
#if IREE_TRACING_FEATURES & IREE_TRACING_FEATURE_INSTRUMENTATION
// TODO(#1886): update these to tracy and drop the 0.
#define IREE_TRACE_SCOPE() ZoneScoped
#define IREE_TRACE_SCOPE_DYNAMIC(name_cstr) \
ZoneTransientN(___tracy_scoped_zone, name_cstr, true)
#define IREE_TRACE_SCOPE0(name_literal) ZoneScopedN(name_literal)
#define IREE_TRACE_EVENT
#define IREE_TRACE_EVENT0
#else
#define IREE_TRACE_THREAD_ENABLE(name)
#define IREE_TRACE_SCOPE()
#define IREE_TRACE_SCOPE_DYNAMIC(name_string_view)
#define IREE_TRACE_SCOPE0(name_literal)
#define IREE_TRACE_EVENT(void)
#define IREE_TRACE_EVENT0
#endif // IREE_TRACING_FEATURE_INSTRUMENTATION
// TODO(benvanik): macros for LockableCtx / Lockable mutex tracking.
#endif // __cplusplus
#endif // IREE_BASE_TRACING_H_