Dqn/dqn_debug.h

133 lines
5.7 KiB
C
Raw Normal View History

2023-08-27 08:07:13 +00:00
#if !defined(DQN_ASAN_POISON)
#define DQN_ASAN_POISON 0
#endif
#if !defined(DQN_ASAN_VET_POISON)
#define DQN_ASAN_VET_POISON 0
#endif
#define DQN_ASAN_POISON_ALIGNMENT 8
#if !defined(DQN_ASAN_POISON_GUARD_SIZE)
#define DQN_ASAN_POISON_GUARD_SIZE 128
2023-08-27 08:07:13 +00:00
#endif
static_assert(Dqn_IsPowerOfTwoAligned(DQN_ASAN_POISON_GUARD_SIZE, DQN_ASAN_POISON_ALIGNMENT),
"ASAN poison guard size must be a power-of-two and aligned to ASAN's alignment"
"requirement (8 bytes)");
2023-08-27 08:07:13 +00:00
// NOTE: MSVC does not support the feature detection macro for instance so we
// compile it out
#if !defined(__has_feature)
#define __has_feature(x) 0
#endif
// NOTE: [$ASAN] Dqn_Asan ==========================================================================
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#include <sanitizer/asan_interface.h>
#endif
2023-08-27 08:07:13 +00:00
2023-08-30 15:03:48 +00:00
DQN_API void Dqn_ASAN_PoisonMemoryRegion(void const volatile *ptr, Dqn_usize size);
DQN_API void Dqn_ASAN_UnpoisonMemoryRegion(void const volatile *ptr, Dqn_usize size);
// NOTE: [$STKT] Dqn_StackTrace ====================================================================
2023-08-31 10:07:27 +00:00
// Create a stack trace at the calling site that these functions are invoked
// from.
//
// NOTE: API =======================================================================================
// @proc Dqn_StackTrace_Walk
// @desc This functions generates the stack trace as a series of U64's that
// represent the base address of the functions on the call-stack at the point
// of execution. These functions are stored in order from the current
// executing function first and the most ancestor function last in the walk.
// @proc Dqn_StackTrace_WalkResultIterate
// @desc Create an iterator to iterate the walk result and produce
// `Dqn_StackTraceRawFrame` from each level of the call-stack. These frames
// can then be converted into `Dqn_StackTraceFrame` which is a human readable
// representation of the frame.
#if 0
Dqn_ThreadScratch scratch = Dqn_Thread_GetScratch(nullptr);
Dqn_StackTraceWalkResult walk = Dqn_StackTrace_Walk(scratch.arena, 128 /*limit*/);
for (Dqn_StackTraceWalkResultIterator it = {}; Dqn_StackTrace_WalkResultIterate(&it, &walk); ) {
Dqn_StackTraceFrame frame = Dqn_StackTrace_RawFrameToFrame(scratch.arena, it.raw_frame);
Dqn_Print_LnF("%.*s(%I64u): %.*s", DQN_STRING_FMT(frame.file_name), frame.line_number, DQN_STRING_FMT(frame.function_name));
}
#endif
// @proc Dqn_StackTrace_GetFrames
// @desc Create a stack trace at the point of execution and return the frames
// converted into its human readable format.
// @proc Dqn_StackTrace_RawFrameToFrame
// @desc Convert a raw frame from a stack trace walk into the human readable
// format (e.g. with function names, line number and file name).
2023-08-30 15:03:48 +00:00
struct Dqn_StackTraceFrame
{
uint64_t address;
uint64_t line_number;
Dqn_String8 file_name;
Dqn_String8 function_name;
};
struct Dqn_StackTraceRawFrame
{
void *process;
uint64_t base_addr;
};
struct Dqn_StackTraceWalkResult
{
2023-08-31 10:07:27 +00:00
void *process; // [Internal] Windows handle to the process
uint64_t *base_addr; // The addresses of the functions in the stack trace
uint16_t size; // The number of `base_addr`'s stored from the walk
2023-08-30 15:03:48 +00:00
};
struct Dqn_StackTraceWalkResultIterator
{
Dqn_StackTraceRawFrame raw_frame;
uint16_t index;
};
2023-08-31 12:10:47 +00:00
DQN_API Dqn_StackTraceWalkResult Dqn_StackTrace_Walk (Dqn_Arena *arena, uint16_t limit);
DQN_API bool Dqn_StackTrace_WalkResultIterate(Dqn_StackTraceWalkResultIterator *it, Dqn_StackTraceWalkResult *walk);
DQN_API Dqn_Slice<Dqn_StackTraceFrame> Dqn_StackTrace_GetFrames (Dqn_Arena *arena, uint16_t limit);
DQN_API void Dqn_StackTrace_Print (uint16_t limit);
DQN_API Dqn_StackTraceFrame Dqn_StackTrace_RawFrameToFrame (Dqn_Arena *arena, Dqn_StackTraceRawFrame raw_frame);
2023-08-16 11:59:38 +00:00
// NOTE: [$DEBG] Dqn_Debug =========================================================================
enum Dqn_AllocRecordFlag
{
Dqn_AllocRecordFlag_Freed = 1 << 0,
Dqn_AllocRecordFlag_LeakPermitted = 1 << 1,
};
struct Dqn_AllocRecord
{
void *ptr; // Pointer to the allocation being tracked
Dqn_usize size; // Size of the allocation
Dqn_usize freed_size; // Store the size of the allocation when it is freed
char *stack_trace; // Stack trace at the point of allocation
char *freed_stack_trace; // Stack trace of where the allocation was freed
uint16_t stack_trace_size; // Size of the `stack_trace`
uint16_t freed_stack_trace_size; // Size of `freed_stack_trace`
uint16_t flags; // Bit flags from `Dqn_AllocRecordFlag`
char padding[2];
};
static_assert(sizeof(Dqn_AllocRecord) == 48,
"We aim to keep the allocation record as light as possible as "
"memory tracking can get expensive. Enforce that there is no "
"unexpected padding.");
#if defined(DQN_LEAK_TRACING)
#define Dqn_Debug_TrackAlloc(ptr, size, leak_permitted) Dqn_Debug_TrackAlloc_ (Dqn_String8_InitCString8(b_stacktrace_get_string()), ptr, size, leak_permitted)
#define Dqn_Debug_TrackDealloc(ptr) Dqn_Debug_TrackDealloc_(Dqn_String8_InitCString8(b_stacktrace_get_string()), ptr)
DQN_API void Dqn_Debug_TrackAlloc_(Dqn_String8 stack_trace, void *ptr, Dqn_usize size, bool leak_permitted);
DQN_API void Dqn_Debug_TrackDealloc_(Dqn_String8 stack_trace, void *ptr);
DQN_API void Dqn_Debug_DumpLeaks();
#else
#define Dqn_Debug_TrackAlloc(...)
#define Dqn_Debug_TrackDealloc(...)
#define Dqn_Debug_DumpLeaks(...)
#endif