Remove core dn layer

This commit is contained in:
doylet 2025-11-09 13:19:50 +11:00
parent 3aba851aef
commit a8c32301a9
31 changed files with 1719 additions and 2923 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Generated by the DN single header generator 2025-11-08 18:54:58
// Generated by the DN single header generator 2025-11-09 13:19:26
#if !defined(DN_BASE_INC_H)
#define DN_BASE_INC_H
@ -983,6 +983,50 @@ struct DN_FmtAppendResult
bool truncated;
};
struct DN_ProfilerAnchor
{
// Inclusive refers to the time spent to complete the function call
// including all children functions.
//
// Exclusive refers to the time spent in the function, not including any
// time spent in children functions that we call that are also being
// profiled. If we recursively call into ourselves, the time we spent in
// our function is accumulated.
DN_U64 tsc_inclusive;
DN_U64 tsc_exclusive;
DN_U16 hit_count;
DN_Str8 name;
};
struct DN_ProfilerZone
{
DN_U16 anchor_index;
DN_U64 begin_tsc;
DN_U16 parent_zone;
DN_U64 elapsed_tsc_at_zone_start;
};
struct DN_ProfilerAnchorArray
{
DN_ProfilerAnchor *data;
DN_USize count;
};
typedef DN_U64 (DN_ProfilerTSCNowFunc)();
struct DN_Profiler
{
DN_USize frame_index;
DN_ProfilerAnchor *anchors;
DN_USize anchors_count;
DN_USize anchors_per_frame;
DN_U16 parent_zone;
bool paused;
DN_ProfilerTSCNowFunc *tsc_now;
DN_U64 tsc_frequency;
DN_ProfilerZone frame_zone;
DN_F64 frame_avg_tsc;
};
#if !defined(DN_STB_SPRINTF_HEADER_ONLY)
#define STB_SPRINTF_IMPLEMENTATION
#define STB_SPRINTF_STATIC
@ -2924,6 +2968,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
DN_GCC_WARNING_POP
DN_MSVC_WARNING_POP
DN_API void DN_BeginFrame ();
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
#define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__)
@ -3215,6 +3261,24 @@ DN_API DN_ByteCountResult DN_ByteCountFromType (DN_U64 bytes, DN_By
DN_API DN_Str8x32 DN_ByteCountStr8x32FromType (DN_U64 bytes, DN_ByteCountType type);
#define DN_ByteCountStr8x32(bytes) DN_ByteCountStr8x32FromType(bytes, DN_ByteCountType_Auto)
#define DN_ProfilerZoneLoop(prof, name, index) \
DN_ProfilerZone DN_UniqueName(zone_) = DN_ProfilerBeginZone(prof, DN_Str8Lit(name), index), DN_UniqueName(dummy_) = {}; \
DN_UniqueName(dummy_).begin_tsc == 0; \
DN_ProfilerEndZone(prof, DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1
#define DN_ProfilerZoneLoopAuto(prof, name) DN_ProfilerZoneLoop(prof, name, __COUNTER__ + 1)
DN_API DN_Profiler DN_ProfilerInit (DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSCNowFunc *tsc_now, DN_U64 tsc_frequency);
DN_API DN_ProfilerZone DN_ProfilerBeginZone (DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index);
#define DN_ProfilerBeginZoneAuto(prof, name) DN_ProfilerBeginZone(prof, DN_Str8Lit(name), __COUNTER__ + 1)
DN_API void DN_ProfilerEndZone (DN_Profiler *profiler, DN_ProfilerZone zone);
DN_API DN_USize DN_ProfilerFrameCount (DN_Profiler const *profiler);
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchorsFromIndex (DN_Profiler *profiler, DN_USize frame_index);
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchors (DN_Profiler *profiler);
DN_API void DN_ProfilerNewFrame (DN_Profiler *profiler);
DN_API void DN_ProfilerDump (DN_Profiler *profiler);
DN_API DN_F64 DN_ProfilerSecFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
DN_API DN_F64 DN_ProfilerMsFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
#endif // !defined(DN_BASE_H)
// DN: Single header generator inlined this file => #include "Base/dn_base_os.h"
#if !defined(DN_BASE_OS_H)
@ -3253,26 +3317,26 @@ struct DN_StackTraceWalkResultIterator
#if defined(DN_FREESTANDING)
#define DN_StackTrace_WalkStr8FromHeap(...) DN_Str8Lit("N/A")
#define DN_StackTrace_Walk(...)
#define DN_StackTrace_WalkResultIterate(...)
#define DN_StackTrace_WalkResultToStr8(...) DN_Str8Lit("N/A")
#define DN_StackTrace_WalkStr8(...) DN_Str8Lit("N/A")
#define DN_StackTrace_WalkStr8FromHeap(...) DN_Str8Lit("N/A")
#define DN_StackTrace_GetFrames(...)
#define DN_StackTrace_RawFrameToFrame(...)
#define DN_StackTrace_Print(...)
#define DN_StackTrace_ReloadSymbols(...)
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
#define DN_StackTraceWalk(...)
#define DN_StackTraceWalkResultIterate(...)
#define DN_StackTraceWalkResultToStr8(...) DN_Str8Lit("N/A")
#define DN_StackTraceWalkStr8(...) DN_Str8Lit("N/A")
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
#define DN_StackTraceGetFrames(...)
#define DN_StackTraceRawFrameToFrame(...)
#define DN_StackTracePrint(...)
#define DN_StackTraceReloadSymbols(...)
#else
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk (struct DN_Arena *arena, DN_U16 limit);
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
DN_API DN_Str8 DN_StackTrace_WalkResultToStr8 (struct DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip);
DN_API DN_Str8 DN_StackTrace_WalkStr8 (struct DN_Arena *arena, DN_U16 limit, DN_U16 skip);
DN_API DN_Str8 DN_StackTrace_WalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTrace_GetFrames (struct DN_Arena *arena, DN_U16 limit);
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
DN_API void DN_StackTrace_Print (DN_U16 limit);
DN_API void DN_StackTrace_ReloadSymbols ();
DN_API DN_StackTraceWalkResult DN_StackTraceWalk (struct DN_Arena *arena, DN_U16 limit);
DN_API bool DN_StackTraceWalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
DN_API DN_Str8 DN_StackTraceWalkResultToStr8 (struct DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip);
DN_API DN_Str8 DN_StackTraceWalkStr8 (struct DN_Arena *arena, DN_U16 limit, DN_U16 skip);
DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTraceGetFrames (struct DN_Arena *arena, DN_U16 limit);
DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
DN_API void DN_StackTracePrint (DN_U16 limit);
DN_API void DN_StackTraceReloadSymbols ();
#endif
#endif // !defined(DN_BASE_OS_H)
// DN: Single header generator inlined this file => #include "Base/dn_base_assert.h"
@ -3282,7 +3346,7 @@ DN_API void DN_StackTrace_ReloadSymbols ();
#define DN_HardAssertF(expr, fmt, ...) \
do { \
if (!(expr)) { \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_Str8PrintFmt(stack_trace_), \
##__VA_ARGS__); \
@ -3300,7 +3364,7 @@ DN_API void DN_StackTrace_ReloadSymbols ();
#define DN_AssertF(expr, fmt, ...) \
do { \
if (!(expr)) { \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_Str8PrintFmt(stack_trace_), \
##__VA_ARGS__); \
@ -3313,7 +3377,7 @@ DN_API void DN_StackTrace_ReloadSymbols ();
static bool once = true; \
if (!(expr) && once) { \
once = false; \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_Str8PrintFmt(stack_trace_), \
##__VA_ARGS__); \
@ -3339,7 +3403,7 @@ DN_API void DN_StackTrace_ReloadSymbols ();
((expr) ? true : (DN_LOG_WarningF(fmt, ##__VA_ARGS__), false))
#else
#define DN_CheckF(expr, fmt, ...) \
((expr) ? true : (DN_LOG_ErrorF(fmt, ##__VA_ARGS__), DN_StackTrace_Print(128 /*limit*/), DN_DebugBreak, false))
((expr) ? true : (DN_LOG_ErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false))
#endif
#endif
@ -3913,6 +3977,51 @@ template <typename T> void DN_List_Ad
template <typename T> DN_Slice<T> DN_List_ToSliceCopy (DN_List<T> const *list, DN_Arena* arena);
#endif // !defined(DN_NO_LIST)
#endif // !defined(DN_CONTAINER_H)
// DN: Single header generator inlined this file => #include "Base/dn_base_leak.h"
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
enum DN_LeakAllocFlag
{
DN_LeakAllocFlag_Freed = 1 << 0,
DN_LeakAllocFlag_LeakPermitted = 1 << 1,
};
struct DN_LeakAlloc
{
void *ptr; // 8 Pointer to the allocation being tracked
DN_USize size; // 16 Size of the allocation
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag`
};
// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get
// expensive. Enforce that there is no unexpected padding.
DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively
struct DN_LeakTracker
{
DN_DSMap<DN_LeakAlloc> alloc_table;
DN_TicketMutex alloc_table_mutex;
DN_Arena alloc_table_arena;
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
};
DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak);
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr);
DN_API void DN_LeakDump_ (DN_LeakTracker *leak);
#if defined(DN_LEAK_TRACKING)
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak)
#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr)
#define DN_LeakDump(leak) DN_LeakDump(leak);
#else
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0)
#define DN_LeakDump(leak) do { } while (0)
#endif
#endif // !defined(DN_BASE_INC_H)
#if !defined(DN_OS_INC_H)
@ -5816,14 +5925,6 @@ struct DN_OSHttpResponse
#endif
};
struct DN_OSInitArgs
{
DN_U64 tls_reserve;
DN_U64 tls_commit;
DN_U64 tls_err_sink_reserve;
DN_U64 tls_err_sink_commit;
};
struct DN_OSCore
{
DN_CPUReport cpu_report;
@ -5862,7 +5963,6 @@ struct DN_OSDiskSpace
DN_U64 size;
};
DN_API void DN_OS_Init (DN_OSCore *os, DN_OSInitArgs *args);
DN_API void DN_OS_EmitLogsWithOSPrintFunctions (DN_OSCore *os);
DN_API void DN_OS_DumpThreadContextArenaStat (DN_Str8 file_path);
@ -6205,146 +6305,42 @@ DN_API DN_Slice<DN_Str8> DN_Str8BuilderBuildSliceFromTLS (DN_Str8Bui
#endif // !defined(DN_OS_STRING_H)
#endif // DN_OS_INC_H
#if !defined(DN_CORE_INC_H)
#define DN_CORE_INC_H
#if !defined(DN_INC_H)
#define DN_INC_H
// DN: Single header generator inlined this file => #include "Core/dn_core_debug.h"
#if !defined(DN_CORE_DEBUG_H)
#define DN_CORE_DEBUG_H
// DN: Single header generator commented out this header => #include "../dn_base_inc.h"
// NOTE: DN_Debug
enum DN_DebugAllocFlag
{
DN_DebugAllocFlag_Freed = 1 << 0,
DN_DebugAllocFlag_LeakPermitted = 1 << 1,
};
struct DN_DebugAlloc
{
void *ptr; // 8 Pointer to the allocation being tracked
DN_USize size; // 16 Size of the allocation
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
DN_U16 flags; // 72 Bit flags from `DN_DebugAllocFlag`
};
static_assert(sizeof(DN_DebugAlloc) == 64 || sizeof(DN_DebugAlloc) == 32, // NOTE: 64 bit vs 32 bit pointers respectively
"We aim to keep the allocation record as light as possible as "
"memory tracking can get expensive. Enforce that there is no "
"unexpected padding.");
// NOTE: DN_Profiler
struct DN_ProfilerAnchor
{
// Inclusive refers to the time spent to complete the function call
// including all children functions.
//
// Exclusive refers to the time spent in the function, not including any
// time spent in children functions that we call that are also being
// profiled. If we recursively call into ourselves, the time we spent in
// our function is accumulated.
DN_U64 tsc_inclusive;
DN_U64 tsc_exclusive;
DN_U16 hit_count;
DN_Str8 name;
};
struct DN_ProfilerZone
{
DN_U16 anchor_index;
DN_U64 begin_tsc;
DN_U16 parent_zone;
DN_U64 elapsed_tsc_at_zone_start;
};
struct DN_ProfilerAnchorArray
{
DN_ProfilerAnchor *data;
DN_USize count;
};
enum DN_ProfilerTSC
{
DN_ProfilerTSC_RDTSC,
DN_ProfilerTSC_OSPerformanceCounter,
};
struct DN_Profiler
{
DN_USize frame_index;
DN_ProfilerAnchor *anchors;
DN_USize anchors_count;
DN_USize anchors_per_frame;
DN_U16 parent_zone;
bool paused;
DN_ProfilerTSC tsc;
DN_U64 tsc_frequency;
DN_ProfilerZone frame_zone;
DN_F64 frame_avg_tsc;
};
#define DN_Profiler_ZoneLoop(prof, name, index) \
DN_ProfilerZone DN_UniqueName(zone_) = DN_Profiler_BeginZone(prof, DN_Str8Lit(name), index), DN_UniqueName(dummy_) = {}; \
DN_UniqueName(dummy_).begin_tsc == 0; \
DN_Profiler_EndZone(prof, DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1
#define DN_Profiler_ZoneLoopAuto(prof, name) DN_Profiler_ZoneLoop(prof, name, __COUNTER__ + 1)
DN_API DN_Profiler DN_Profiler_Init (DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSC tsc, DN_U64 tsc_frequency);
DN_API DN_ProfilerZone DN_Profiler_BeginZone (DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index);
#define DN_Profiler_BeginZoneAuto(prof, name) DN_Profiler_BeginZone(prof, DN_Str8Lit(name), __COUNTER__ + 1)
DN_API void DN_Profiler_EndZone (DN_Profiler *profiler, DN_ProfilerZone zone);
DN_API DN_USize DN_Profiler_FrameCount (DN_Profiler const *profiler);
DN_API DN_ProfilerAnchorArray DN_Profiler_FrameAnchorsFromIndex (DN_Profiler *profiler, DN_USize frame_index);
DN_API DN_ProfilerAnchorArray DN_Profiler_FrameAnchors (DN_Profiler *profiler);
DN_API void DN_Profiler_NewFrame (DN_Profiler *profiler);
DN_API void DN_Profiler_Dump (DN_Profiler *profiler);
DN_API DN_F64 DN_Profiler_SecFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
DN_API DN_F64 DN_Profiler_MsFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
#if defined(DN_LEAK_TRACKING)
DN_API void DN_DBGTrackAlloc (void *ptr, DN_USize size, bool alloc_can_leak);
DN_API void DN_DBGTrackDealloc (void *ptr);
DN_API void DN_DBGDumpLeaks ();
#else
#define DN_DBGTrackAlloc(ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
#define DN_DBGTrackDealloc(ptr) do { (void)ptr; } while (0)
#define DN_DBGDumpLeaks() do { } while (0)
#endif
#endif // DN_CORE_DEBUG_H
// DN: Single header generator inlined this file => #include "Core/dn_core.h"
#if !defined(DN_CORE_H)
#define DN_CORE_H
// NOTE: DN_Core
struct DN_Core
{
// NOTE: Leak Tracing
#if defined(DN_LEAK_TRACKING)
DN_DSMap<DN_DebugAlloc> alloc_table;
DN_TicketMutex alloc_table_mutex;
DN_Arena alloc_table_arena;
DN_USize mem_allocs_frame;
DN_LeakTracker leak;
#if defined(DN_OS_H)
DN_OSCore os;
#endif
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
};
enum DN_CoreOnInit
struct DN_InitArgs
{
DN_CoreOnInit_Nil = 0,
DN_CoreOnInit_LogLibFeatures = 1 << 0,
DN_CoreOnInit_LogCPUFeatures = 1 << 1,
DN_CoreOnInit_LogAllFeatures = DN_CoreOnInit_LogLibFeatures | DN_CoreOnInit_LogCPUFeatures,
DN_U64 os_tls_reserve;
DN_U64 os_tls_commit;
DN_U64 os_tls_err_sink_reserve;
DN_U64 os_tls_err_sink_commit;
};
DN_API void DN_Core_Init (DN_Core *core, DN_CoreOnInit on_init);
DN_API void DN_Core_BeginFrame();
#endif // !defined(DN_CORE_H)
typedef DN_USize DN_InitFlags;
enum DN_InitFlags_
{
DN_InitFlags_Nil = 0,
DN_InitFlags_OS = 1 << 0,
DN_InitFlags_LogLibFeatures = 1 << 1,
DN_InitFlags_LogCPUFeatures = 1 << 2,
DN_InitFlags_LogAllFeatures = DN_InitFlags_LogLibFeatures | DN_InitFlags_LogCPUFeatures,
};
#endif // !defined(DN_CORE_INC_H)
extern DN_Core *g_dn_;
DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args);
DN_API void DN_BeginFrame();
#endif // !defined(DN_INC_H)
#if !defined(DN_MATH_H)
#define DN_MATH_H

View File

@ -776,7 +776,6 @@ DN_API DN_F32 DN_EpsilonClampF32(DN_F32 value, DN_F32 target, DN_F32 epsilon)
return result;
}
static DN_ArenaBlock *DN_ArenaBlockFromMemFuncs_(DN_U64 reserve, DN_U64 commit, bool track_alloc, bool alloc_can_leak, DN_ArenaMemFuncs mem_funcs)
{
DN_ArenaBlock *result = nullptr;
@ -823,7 +822,7 @@ static DN_ArenaBlock *DN_ArenaBlockFromMemFuncs_(DN_U64 reserve, DN_U64 commit,
}
if (track_alloc && result)
DN_DBGTrackAlloc(result, result->reserve, alloc_can_leak);
DN_LeakTrackAlloc(dn->leak, result, result->reserve, alloc_can_leak);
return result;
}
@ -890,7 +889,7 @@ static void DN_ArenaBlockDeinit_(DN_Arena const *arena, DN_ArenaBlock *block)
{
DN_USize release_size = block->reserve;
if (DN_BitIsNotSet(arena->flags, DN_ArenaFlags_NoAllocTrack))
DN_DBGTrackDealloc(block);
DN_LeakTrackDealloc(&g_dn_->leak, block);
DN_ASanUnpoisonMemoryRegion(block, block->commit);
if (arena->flags & DN_ArenaFlags_MemFuncs) {
if (arena->mem_funcs.type == DN_ArenaMemFuncType_Basic)
@ -2901,3 +2900,170 @@ DN_API DN_Str8x32 DN_ByteCountStr8x32FromType(DN_U64 bytes, DN_ByteCountType typ
DN_Str8x32 result = DN_Str8x32FromFmt("%.2f%.*s", byte_count.bytes, DN_Str8PrintFmt(byte_count.suffix));
return result;
}
DN_API DN_Profiler DN_ProfilerInit(DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSCNowFunc *tsc_now, DN_U64 tsc_frequency)
{
DN_Profiler result = {};
result.anchors = anchors;
result.anchors_count = count;
result.anchors_per_frame = anchors_per_frame;
result.tsc_now = tsc_now;
result.tsc_frequency = tsc_frequency;
DN_AssertF(result.tsc_frequency != 0,
"You must set this to the frequency of the timestamp counter function (TSC) (e.g. how "
"many ticks occur between timestamps). We use this to determine the duration between "
"each zone's recorded TSC. For example if the 'tsc_now' was set to Window's "
"QueryPerformanceCounter then 'tsc_frequency' would be set to the value of "
"QueryPerformanceFrequency which is typically 10mhz (e.g. The duration between two "
"consecutive TSC's is 10mhz)."
""
"Hence frequency can't be zero otherwise it's a divide by 0. If you don't have a TSC "
"function and pass in null, the profiler defaults to rdtsc() and you must measure the "
"frequency of rdtsc yourself. The reason for this is that measuring rdtsc requires "
"having some alternate timing mechanism to measure the duration between the TSCs "
"provided by rdtsc and this profiler makes no assumption about what timing primitives "
"are available other than rdtsc which is a CPU builtin available on basically all "
"platforms or have an equivalent (e.g. __builtin_readcyclecounter)"
""
"This codebase provides DN_OS_EstimateTSCPerSecond() as an example of how to that for "
"convenience and is available if compiling with the OS layer. Some platforms like "
"Emscripten don't support rdtsc() so you should use an alternative method like "
"emscripten_get_now() or clock_gettime with CLOCK_MONOTONIC.");
return result;
}
DN_API DN_USize DN_ProfilerFrameCount(DN_Profiler const *profiler)
{
DN_USize result = profiler->anchors_count / profiler->anchors_per_frame;
return result;
}
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchorsFromIndex(DN_Profiler *profiler, DN_USize frame_index)
{
DN_ProfilerAnchorArray result = {};
DN_USize anchor_offset = frame_index * profiler->anchors_per_frame;
result.data = profiler->anchors + anchor_offset;
result.count = profiler->anchors_per_frame;
return result;
}
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchors(DN_Profiler *profiler)
{
DN_ProfilerAnchorArray result = DN_ProfilerFrameAnchorsFromIndex(profiler, profiler->frame_index);
return result;
}
DN_API DN_ProfilerZone DN_ProfilerBeginZone(DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index)
{
DN_ProfilerZone result = {};
if (profiler->paused)
return result;
DN_Assert(anchor_index < profiler->anchors_per_frame);
DN_ProfilerAnchor *anchor = DN_ProfilerFrameAnchors(profiler).data + anchor_index;
anchor->name = name;
// TODO: We need per-thread-local-storage profiler so that we can use these apis
// across threads. For now, we let them overwrite each other but this is not tenable.
#if 0
if (anchor->name.size && anchor->name != name)
DN_AssertF(name == anchor->name, "Potentially overwriting a zone by accident? Anchor is '%.*s', name is '%.*s'", DN_Str8PrintFmt(anchor->name), DN_Str8PrintFmt(name));
#endif
result.begin_tsc = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTSC();
result.anchor_index = anchor_index;
result.parent_zone = profiler->parent_zone;
result.elapsed_tsc_at_zone_start = anchor->tsc_inclusive;
profiler->parent_zone = anchor_index;
return result;
}
DN_API void DN_ProfilerEndZone(DN_Profiler *profiler, DN_ProfilerZone zone)
{
if (profiler->paused)
return;
DN_Assert(zone.anchor_index < profiler->anchors_per_frame);
DN_Assert(zone.parent_zone < profiler->anchors_per_frame);
DN_ProfilerAnchorArray array = DN_ProfilerFrameAnchors(profiler);
DN_ProfilerAnchor *anchor = array.data + zone.anchor_index;
DN_U64 tsc_now = profiler->tsc_now ? profiler->tsc_now() : DN_CPUGetTSC();
DN_U64 elapsed_tsc = tsc_now - zone.begin_tsc;
anchor->hit_count++;
anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc;
anchor->tsc_exclusive += elapsed_tsc;
if (zone.parent_zone != zone.anchor_index) {
DN_ProfilerAnchor *parent_anchor = array.data + zone.parent_zone;
parent_anchor->tsc_exclusive -= elapsed_tsc;
}
profiler->parent_zone = zone.parent_zone;
}
DN_API void DN_ProfilerNewFrame(DN_Profiler *profiler)
{
if (profiler->paused)
return;
// NOTE: End the frame's zone
DN_ProfilerEndZone(profiler, profiler->frame_zone);
DN_ProfilerAnchorArray old_frame_anchors = DN_ProfilerFrameAnchors(profiler);
DN_ProfilerAnchor old_frame_anchor = old_frame_anchors.data[0];
profiler->frame_avg_tsc = (profiler->frame_avg_tsc + old_frame_anchor.tsc_inclusive) / 2.f;
// NOTE: Bump to the next frame
DN_USize frame_count = profiler->anchors_count / profiler->anchors_per_frame;
profiler->frame_index = (profiler->frame_index + 1) % frame_count;
// NOTE: Zero out the anchors
DN_ProfilerAnchorArray next_anchors = DN_ProfilerFrameAnchors(profiler);
DN_Memset(next_anchors.data, 0, sizeof(*profiler->anchors) * next_anchors.count);
// NOTE: Start the frame's zone
profiler->frame_zone = DN_ProfilerBeginZone(profiler, DN_Str8Lit("Profiler Frame"), 0);
}
DN_API void DN_ProfilerDump(DN_Profiler *profiler)
{
if (profiler->frame_index == 0)
return;
DN_USize frame_index = profiler->frame_index - 1;
DN_Assert(profiler->frame_index < profiler->anchors_per_frame);
DN_ProfilerAnchor *anchors = profiler->anchors + (frame_index * profiler->anchors_per_frame);
for (DN_USize index = 1; index < profiler->anchors_per_frame; index++) {
DN_ProfilerAnchor const *anchor = anchors + index;
if (!anchor->hit_count)
continue;
DN_U64 tsc_exclusive = anchor->tsc_exclusive;
DN_U64 tsc_inclusive = anchor->tsc_inclusive;
DN_F64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency;
if (tsc_exclusive == tsc_inclusive) {
DN_OS_PrintOutLnF("%.*s[%u]: %.1fms", DN_Str8PrintFmt(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds);
} else {
DN_F64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency;
DN_OS_PrintOutLnF("%.*s[%u]: %.1f/%.1fms",
DN_Str8PrintFmt(anchor->name),
anchor->hit_count,
tsc_exclusive_milliseconds,
tsc_inclusive_milliseconds);
}
}
}
DN_API DN_F64 DN_ProfilerSecFromTSC(DN_Profiler *profiler, DN_U64 duration_tsc)
{
DN_F64 result = DN_Cast(DN_F64)duration_tsc / profiler->tsc_frequency;
return result;
}
DN_API DN_F64 DN_ProfilerMsFromTSC(DN_Profiler *profiler, DN_U64 duration_tsc)
{
DN_F64 result = DN_Cast(DN_F64)duration_tsc / profiler->tsc_frequency * 1000.0;
return result;
}

View File

@ -765,6 +765,50 @@ struct DN_FmtAppendResult
bool truncated;
};
struct DN_ProfilerAnchor
{
// Inclusive refers to the time spent to complete the function call
// including all children functions.
//
// Exclusive refers to the time spent in the function, not including any
// time spent in children functions that we call that are also being
// profiled. If we recursively call into ourselves, the time we spent in
// our function is accumulated.
DN_U64 tsc_inclusive;
DN_U64 tsc_exclusive;
DN_U16 hit_count;
DN_Str8 name;
};
struct DN_ProfilerZone
{
DN_U16 anchor_index;
DN_U64 begin_tsc;
DN_U16 parent_zone;
DN_U64 elapsed_tsc_at_zone_start;
};
struct DN_ProfilerAnchorArray
{
DN_ProfilerAnchor *data;
DN_USize count;
};
typedef DN_U64 (DN_ProfilerTSCNowFunc)();
struct DN_Profiler
{
DN_USize frame_index;
DN_ProfilerAnchor *anchors;
DN_USize anchors_count;
DN_USize anchors_per_frame;
DN_U16 parent_zone;
bool paused;
DN_ProfilerTSCNowFunc *tsc_now;
DN_U64 tsc_frequency;
DN_ProfilerZone frame_zone;
DN_F64 frame_avg_tsc;
};
#if !defined(DN_STB_SPRINTF_HEADER_ONLY)
#define STB_SPRINTF_IMPLEMENTATION
#define STB_SPRINTF_STATIC
@ -778,6 +822,8 @@ DN_GCC_WARNING_DISABLE(-Wunused-function)
DN_GCC_WARNING_POP
DN_MSVC_WARNING_POP
DN_API void DN_BeginFrame ();
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
#define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__VA_ARGS__)
@ -1069,4 +1115,22 @@ DN_API DN_ByteCountResult DN_ByteCountFromType (DN_U64 bytes, DN_By
DN_API DN_Str8x32 DN_ByteCountStr8x32FromType (DN_U64 bytes, DN_ByteCountType type);
#define DN_ByteCountStr8x32(bytes) DN_ByteCountStr8x32FromType(bytes, DN_ByteCountType_Auto)
#define DN_ProfilerZoneLoop(prof, name, index) \
DN_ProfilerZone DN_UniqueName(zone_) = DN_ProfilerBeginZone(prof, DN_Str8Lit(name), index), DN_UniqueName(dummy_) = {}; \
DN_UniqueName(dummy_).begin_tsc == 0; \
DN_ProfilerEndZone(prof, DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1
#define DN_ProfilerZoneLoopAuto(prof, name) DN_ProfilerZoneLoop(prof, name, __COUNTER__ + 1)
DN_API DN_Profiler DN_ProfilerInit (DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSCNowFunc *tsc_now, DN_U64 tsc_frequency);
DN_API DN_ProfilerZone DN_ProfilerBeginZone (DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index);
#define DN_ProfilerBeginZoneAuto(prof, name) DN_ProfilerBeginZone(prof, DN_Str8Lit(name), __COUNTER__ + 1)
DN_API void DN_ProfilerEndZone (DN_Profiler *profiler, DN_ProfilerZone zone);
DN_API DN_USize DN_ProfilerFrameCount (DN_Profiler const *profiler);
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchorsFromIndex (DN_Profiler *profiler, DN_USize frame_index);
DN_API DN_ProfilerAnchorArray DN_ProfilerFrameAnchors (DN_Profiler *profiler);
DN_API void DN_ProfilerNewFrame (DN_Profiler *profiler);
DN_API void DN_ProfilerDump (DN_Profiler *profiler);
DN_API DN_F64 DN_ProfilerSecFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
DN_API DN_F64 DN_ProfilerMsFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
#endif // !defined(DN_BASE_H)

View File

@ -4,7 +4,7 @@
#define DN_HardAssertF(expr, fmt, ...) \
do { \
if (!(expr)) { \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_Str8PrintFmt(stack_trace_), \
##__VA_ARGS__); \
@ -22,7 +22,7 @@
#define DN_AssertF(expr, fmt, ...) \
do { \
if (!(expr)) { \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_Str8PrintFmt(stack_trace_), \
##__VA_ARGS__); \
@ -35,7 +35,7 @@
static bool once = true; \
if (!(expr) && once) { \
once = false; \
DN_Str8 stack_trace_ = DN_StackTrace_WalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_Str8 stack_trace_ = DN_StackTraceWalkStr8FromHeap(128 /*limit*/, 2 /*skip*/); \
DN_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
DN_Str8PrintFmt(stack_trace_), \
##__VA_ARGS__); \
@ -61,7 +61,7 @@
((expr) ? true : (DN_LOG_WarningF(fmt, ##__VA_ARGS__), false))
#else
#define DN_CheckF(expr, fmt, ...) \
((expr) ? true : (DN_LOG_ErrorF(fmt, ##__VA_ARGS__), DN_StackTrace_Print(128 /*limit*/), DN_DebugBreak, false))
((expr) ? true : (DN_LOG_ErrorF(fmt, ##__VA_ARGS__), DN_StackTracePrint(128 /*limit*/), DN_DebugBreak, false))
#endif
#endif

View File

@ -0,0 +1,142 @@
#define DN_BASE_LEAK_CPP
#include "../dn_base_inc.h"
DN_API void DN_LeakTrackAlloc_(DN_LeakTracker *leak, void *ptr, DN_USize size, bool leak_permitted)
{
if (!ptr)
return;
DN_TicketMutex_Begin(&leak->alloc_table_mutex);
DN_DEFER
{
DN_TicketMutex_End(&leak->alloc_table_mutex);
};
// NOTE: If the entry was not added, we are reusing a pointer that has been freed.
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it
// already existed.
DN_Str8 stack_trace = DN_StackTraceWalkStr8FromHeap(128, 3 /*skip*/);
DN_DSMap<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
DN_DSMapResult<DN_LeakAlloc> alloc_entry = DN_DSMap_MakeKeyU64(alloc_table, DN_Cast(DN_U64) ptr);
DN_LeakAlloc *alloc = alloc_entry.value;
if (alloc_entry.found) {
if ((alloc->flags & DN_LeakAllocFlag_Freed) == 0) {
DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size);
DN_Str8x32 new_alloc_size = DN_ByteCountStr8x32(size);
DN_HardAssertF(
alloc->flags & DN_LeakAllocFlag_Freed,
"This pointer is already in the leak tracker, however it has not been freed yet. This "
"same pointer is being ask to be tracked twice in the allocation table, e.g. one if its "
"previous free calls has not being marked freed with an equivalent call to "
"DN_LeakTrackDealloc()\n"
"\n"
"The pointer (0x%p) originally allocated %.*s at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer is allocating %.*s again at:\n"
"\n"
"%.*s\n",
ptr,
DN_Str8PrintFmt(alloc_size),
DN_Str8PrintFmt(alloc->stack_trace),
DN_Str8PrintFmt(new_alloc_size),
DN_Str8PrintFmt(stack_trace));
}
// NOTE: Pointer was reused, clean up the prior entry
leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.size;
leak->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.size;
DN_OS_MemDealloc(alloc->stack_trace.data);
DN_OS_MemDealloc(alloc->freed_stack_trace.data);
*alloc = {};
}
alloc->ptr = ptr;
alloc->size = size;
alloc->stack_trace = stack_trace;
alloc->flags |= leak_permitted ? DN_LeakAllocFlag_LeakPermitted : 0;
leak->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size;
}
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr)
{
if (!ptr)
return;
DN_TicketMutex_Begin(&leak->alloc_table_mutex);
DN_DEFER
{
DN_TicketMutex_End(&leak->alloc_table_mutex);
};
DN_Str8 stack_trace = DN_StackTraceWalkStr8FromHeap(128, 3 /*skip*/);
DN_DSMap<DN_LeakAlloc> *alloc_table = &leak->alloc_table;
DN_DSMapResult<DN_LeakAlloc> alloc_entry = DN_DSMap_FindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr);
DN_HardAssertF(alloc_entry.found,
"Allocated pointer can not be removed as it does not exist in the "
"allocation table. When this memory was allocated, the pointer was "
"not added to the allocation table [ptr=%p]",
ptr);
DN_LeakAlloc *alloc = alloc_entry.value;
if (alloc->flags & DN_LeakAllocFlag_Freed) {
DN_Str8x32 freed_size = DN_ByteCountStr8x32(alloc->freed_size);
DN_HardAssertF((alloc->flags & DN_LeakAllocFlag_Freed) == 0,
"Double free detected, pointer to free was already marked "
"as freed. Either the pointer was reallocated but not "
"traced, or, the pointer was freed twice.\n"
"\n"
"The pointer (0x%p) originally allocated %.*s at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer was freed at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer is being freed again at:\n"
"\n"
"%.*s\n",
ptr,
DN_Str8PrintFmt(freed_size),
DN_Str8PrintFmt(alloc->stack_trace),
DN_Str8PrintFmt(alloc->freed_stack_trace),
DN_Str8PrintFmt(stack_trace));
}
DN_Assert(alloc->freed_stack_trace.size == 0);
alloc->flags |= DN_LeakAllocFlag_Freed;
alloc->freed_stack_trace = stack_trace;
leak->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size;
}
DN_API void DN_LeakDump_(DN_LeakTracker *leak)
{
DN_U64 leak_count = 0;
DN_U64 leaked_bytes = 0;
for (DN_USize index = 1; index < leak->alloc_table.occupied; index++) {
DN_DSMapSlot<DN_LeakAlloc> *slot = leak->alloc_table.slots + index;
DN_LeakAlloc *alloc = &slot->value;
bool alloc_leaked = (alloc->flags & DN_LeakAllocFlag_Freed) == 0;
bool leak_permitted = (alloc->flags & DN_LeakAllocFlag_LeakPermitted);
if (alloc_leaked && !leak_permitted) {
leaked_bytes += alloc->size;
leak_count++;
DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size);
DN_LOG_WarningF(
"Pointer (0x%p) leaked %.*s at:\n"
"%.*s",
alloc->ptr,
DN_Str8PrintFmt(alloc_size),
DN_Str8PrintFmt(alloc->stack_trace));
}
}
if (leak_count) {
DN_Str8x32 leak_size = DN_ByteCountStr8x32(leaked_bytes);
DN_LOG_WarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size));
}
}

View File

@ -0,0 +1,44 @@
#include "../dn_base_inc.h"
enum DN_LeakAllocFlag
{
DN_LeakAllocFlag_Freed = 1 << 0,
DN_LeakAllocFlag_LeakPermitted = 1 << 1,
};
struct DN_LeakAlloc
{
void *ptr; // 8 Pointer to the allocation being tracked
DN_USize size; // 16 Size of the allocation
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
DN_U16 flags; // 72 Bit flags from `DN_LeakAllocFlag`
};
// NOTE: We aim to keep the allocation record as light as possible as memory tracking can get
// expensive. Enforce that there is no unexpected padding.
DN_StaticAssert(sizeof(DN_LeakAlloc) == 64 || sizeof(DN_LeakAlloc) == 32); // NOTE: 64 bit vs 32 bit pointers respectively
struct DN_LeakTracker
{
DN_DSMap<DN_LeakAlloc> alloc_table;
DN_TicketMutex alloc_table_mutex;
DN_Arena alloc_table_arena;
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
};
DN_API void DN_LeakTrackAlloc_ (DN_LeakTracker *leak, void *ptr, DN_USize size, bool alloc_can_leak);
DN_API void DN_LeakTrackDealloc_(DN_LeakTracker *leak, void *ptr);
DN_API void DN_LeakDump_ (DN_LeakTracker *leak);
#if defined(DN_LEAK_TRACKING)
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) DN_LeakTrackAlloc_(leak, ptr, size, alloc_can_leak)
#define DN_LeakTrackDealloc(leak, ptr) DN_LeakTrackDealloc_(leak, ptr)
#define DN_LeakDump(leak) DN_LeakDump(leak);
#else
#define DN_LeakTrackAlloc(leak, ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
#define DN_LeakTrackDealloc(leak, ptr) do { (void)ptr; } while (0)
#define DN_LeakDump(leak) do { } while (0)
#endif

View File

@ -1,4 +0,0 @@
#define DN_BASE_MEM_CPP
#include "../dn_base_inc.h"

View File

@ -1,6 +0,0 @@
#if !defined(DN_BASE_MEM_H)
#define DN_BASE_MEM_H
#include "../dn_base_inc.h"
#endif // !defined(DN_BASE_MEM_H)

View File

@ -34,25 +34,25 @@ struct DN_StackTraceWalkResultIterator
#if defined(DN_FREESTANDING)
#define DN_StackTrace_WalkStr8FromHeap(...) DN_Str8Lit("N/A")
#define DN_StackTrace_Walk(...)
#define DN_StackTrace_WalkResultIterate(...)
#define DN_StackTrace_WalkResultToStr8(...) DN_Str8Lit("N/A")
#define DN_StackTrace_WalkStr8(...) DN_Str8Lit("N/A")
#define DN_StackTrace_WalkStr8FromHeap(...) DN_Str8Lit("N/A")
#define DN_StackTrace_GetFrames(...)
#define DN_StackTrace_RawFrameToFrame(...)
#define DN_StackTrace_Print(...)
#define DN_StackTrace_ReloadSymbols(...)
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
#define DN_StackTraceWalk(...)
#define DN_StackTraceWalkResultIterate(...)
#define DN_StackTraceWalkResultToStr8(...) DN_Str8Lit("N/A")
#define DN_StackTraceWalkStr8(...) DN_Str8Lit("N/A")
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
#define DN_StackTraceGetFrames(...)
#define DN_StackTraceRawFrameToFrame(...)
#define DN_StackTracePrint(...)
#define DN_StackTraceReloadSymbols(...)
#else
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk (struct DN_Arena *arena, DN_U16 limit);
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
DN_API DN_Str8 DN_StackTrace_WalkResultToStr8 (struct DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip);
DN_API DN_Str8 DN_StackTrace_WalkStr8 (struct DN_Arena *arena, DN_U16 limit, DN_U16 skip);
DN_API DN_Str8 DN_StackTrace_WalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTrace_GetFrames (struct DN_Arena *arena, DN_U16 limit);
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
DN_API void DN_StackTrace_Print (DN_U16 limit);
DN_API void DN_StackTrace_ReloadSymbols ();
DN_API DN_StackTraceWalkResult DN_StackTraceWalk (struct DN_Arena *arena, DN_U16 limit);
DN_API bool DN_StackTraceWalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
DN_API DN_Str8 DN_StackTraceWalkResultToStr8 (struct DN_Arena *arena, DN_StackTraceWalkResult const *walk, DN_U16 skip);
DN_API DN_Str8 DN_StackTraceWalkStr8 (struct DN_Arena *arena, DN_U16 limit, DN_U16 skip);
DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap (DN_U16 limit, DN_U16 skip);
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTraceGetFrames (struct DN_Arena *arena, DN_U16 limit);
DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
DN_API void DN_StackTracePrint (DN_U16 limit);
DN_API void DN_StackTraceReloadSymbols ();
#endif
#endif // !defined(DN_BASE_OS_H)

View File

@ -1,84 +0,0 @@
static DN_Core *g_dn_core;
DN_API void DN_Core_Init(DN_Core *core, DN_CoreOnInit on_init)
{
DN_Assert(g_dn_os_core_);
g_dn_core = core;
// NOTE Initialise fields
#if defined(DN_LEAK_TRACKING)
// NOTE: Setup the allocation table with allocation tracking turned off on
// the arena we're using to initialise the table.
core->alloc_table_arena = DN_ArenaFromVMem(DN_Megabytes(1), DN_Kilobytes(512), DN_ArenaFlags_NoAllocTrack | DN_ArenaFlags_AllocCanLeak);
core->alloc_table = DN_DSMap_Init<DN_DebugAlloc>(&core->alloc_table_arena, 4096, DN_DSMapFlags_Nil);
#endif
// NOTE: Print out init features ///////////////////////////////////////////////////////////////
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
DN_Str8Builder builder = DN_Str8BuilderFromArena(tmem.arena);
if (on_init & DN_CoreOnInit_LogLibFeatures) {
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit("DN initialised:\n"));
DN_F32 page_size_kib = g_dn_os_core_->page_size / 1024.0f;
DN_F32 alloc_granularity_kib = g_dn_os_core_->alloc_granularity / 1024.0f;
DN_Str8BuilderAppendF(&builder,
" OS Page Size/Alloc Granularity: %.1f/%.1fKiB\n"
" Logical Processor Count: %u\n",
page_size_kib,
alloc_granularity_kib,
g_dn_os_core_->logical_processor_count);
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
if (DN_ASAN_POISON) {
DN_Str8BuilderAppendF(
&builder, " ASAN manual poisoning%s\n", DN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
DN_Str8BuilderAppendF(&builder, " ASAN poison guard size: %u\n", DN_ASAN_POISON_GUARD_SIZE);
}
#endif
#if defined(DN_LEAK_TRACKING)
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit(" Allocation leak tracing\n"));
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN) || defined(DN_PLATFORM_POSIX)
DN_POSIXCore *posix = DN_Cast(DN_POSIXCore *)g_dn_os_core_->platform_context;
DN_Str8BuilderAppendF(&builder, " Clock GetTime: %S\n", posix->clock_monotonic_raw ? DN_Str8Lit("CLOCK_MONOTONIC_RAW") : DN_Str8Lit("CLOCK_MONOTONIC"));
#endif
// TODO(doyle): Add stacktrace feature log
}
if (on_init & DN_CoreOnInit_LogCPUFeatures) {
DN_CPUReport const *report = &g_dn_os_core_->cpu_report;
DN_Str8 brand = DN_Str8TrimWhitespaceAround(DN_Str8FromPtr(report->brand, sizeof(report->brand) - 1));
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(3) when a string is required in call to 'DN_Str8BuilderAppendF' Actual type: 'struct DN_Str8'.
DN_Str8BuilderAppendF(&builder, " CPU '%S' from '%s' detected:\n", brand, report->vendor);
DN_MSVC_WARNING_POP
DN_USize longest_feature_name = 0;
for (DN_ForIndexU(feature_index, DN_CPUFeature_Count)) {
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
longest_feature_name = DN_Max(longest_feature_name, feature_decl.label.size);
}
for (DN_ForIndexU(feature_index, DN_CPUFeature_Count)) {
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
bool has_feature = DN_CPUHasFeature(report, feature_decl.value);
DN_Str8BuilderAppendF(&builder,
" %.*s:%*s%s\n",
DN_Str8PrintFmt(feature_decl.label),
DN_Cast(int)(longest_feature_name - feature_decl.label.size),
"",
has_feature ? "available" : "not available");
}
}
DN_Str8 info_log = DN_Str8BuilderBuild(&builder, tmem.arena);
if (info_log.size)
DN_LOG_DebugF("%.*s", DN_Str8PrintFmt(info_log));
}
DN_API void DN_Core_BeginFrame()
{
DN_AtomicSetValue64(&g_dn_os_core_->mem_allocs_frame, 0);
}

View File

@ -1,26 +0,0 @@
#if !defined(DN_CORE_H)
#define DN_CORE_H
// NOTE: DN_Core
struct DN_Core
{
// NOTE: Leak Tracing
#if defined(DN_LEAK_TRACKING)
DN_DSMap<DN_DebugAlloc> alloc_table;
DN_TicketMutex alloc_table_mutex;
DN_Arena alloc_table_arena;
#endif
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
};
enum DN_CoreOnInit
{
DN_CoreOnInit_Nil = 0,
DN_CoreOnInit_LogLibFeatures = 1 << 0,
DN_CoreOnInit_LogCPUFeatures = 1 << 1,
DN_CoreOnInit_LogAllFeatures = DN_CoreOnInit_LogLibFeatures | DN_CoreOnInit_LogCPUFeatures,
};
DN_API void DN_Core_Init (DN_Core *core, DN_CoreOnInit on_init);
DN_API void DN_Core_BeginFrame();
#endif // !defined(DN_CORE_H)

View File

@ -1,499 +0,0 @@
#define DN_CORE_DEBUG_CPP
#include "../dn_base_inc.h"
#include "../dn_os_inc.h"
#include "../dn_core_inc.h"
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk(DN_Arena *arena, uint16_t limit)
{
DN_StackTraceWalkResult result = {};
#if defined(DN_OS_WIN32)
if (!arena)
return result;
static DN_TicketMutex mutex = {};
DN_TicketMutex_Begin(&mutex);
HANDLE thread = GetCurrentThread();
result.process = GetCurrentProcess();
DN_W32Core *w32 = DN_OS_GetW32Core_();
if (!w32->sym_initialised) {
w32->sym_initialised = true;
SymSetOptions(SYMOPT_LOAD_LINES);
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
DN_W32Error error = DN_W32_LastError(tmem.arena);
DN_LOG_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_Str8PrintFmt(error.msg));
}
}
CONTEXT context;
RtlCaptureContext(&context);
STACKFRAME64 frame = {};
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
DN_FArray<uint64_t, 256> raw_frames = {};
while (raw_frames.size < limit) {
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
result.process,
thread,
&frame,
&context,
nullptr /*ReadMemoryRoutine*/,
SymFunctionTableAccess64,
SymGetModuleBase64,
nullptr /*TranslateAddress*/))
break;
// NOTE: It might be useful one day to use frame.AddrReturn.Offset.
// If AddrPC.Offset == AddrReturn.Offset then we can detect recursion.
DN_FArray_Add(&raw_frames, frame.AddrPC.Offset);
}
DN_TicketMutex_End(&mutex);
result.base_addr = DN_ArenaNewArray(arena, uint64_t, raw_frames.size, DN_ZMem_No);
result.size = DN_Cast(uint16_t) raw_frames.size;
DN_Memcpy(result.base_addr, raw_frames.data, raw_frames.size * sizeof(raw_frames.data[0]));
#else
(void)limit;
(void)arena;
#endif
return result;
}
static void DN_StackTrace_AddWalkToStr8Builder(DN_StackTraceWalkResult const *walk, DN_Str8Builder *builder, DN_USize skip)
{
DN_StackTraceRawFrame raw_frame = {};
raw_frame.process = walk->process;
for (DN_USize index = skip; index < walk->size; index++) {
raw_frame.base_addr = walk->base_addr[index];
DN_StackTraceFrame frame = DN_StackTrace_RawFrameToFrame(builder->arena, raw_frame);
DN_Str8BuilderAppendF(builder, "%.*s(%zu): %.*s%s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name), (DN_Cast(int) index == walk->size - 1) ? "" : "\n");
}
}
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk)
{
bool result = false;
if (!it || !walk || !walk->base_addr || !walk->process)
return result;
if (it->index >= walk->size)
return false;
result = true;
it->raw_frame.process = walk->process;
it->raw_frame.base_addr = walk->base_addr[it->index++];
return result;
}
DN_API DN_Str8 DN_StackTrace_WalkResultToStr8(DN_Arena *arena, DN_StackTraceWalkResult const *walk, uint16_t skip)
{
DN_Str8 result{};
if (!walk || !arena)
return result;
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
DN_Str8Builder builder = DN_Str8BuilderFromArena(tmem.arena);
DN_StackTrace_AddWalkToStr8Builder(walk, &builder, skip);
result = DN_Str8BuilderBuild(&builder, arena);
return result;
}
DN_API DN_Str8 DN_StackTrace_WalkStr8(DN_Arena *arena, uint16_t limit, uint16_t skip)
{
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(arena);
DN_StackTraceWalkResult walk = DN_StackTrace_Walk(tmem.arena, limit);
DN_Str8 result = DN_StackTrace_WalkResultToStr8(arena, &walk, skip);
return result;
}
DN_API DN_Str8 DN_StackTrace_WalkStr8FromHeap(uint16_t limit, uint16_t skip)
{
// NOTE: We don't use WalkResultToStr8 because that uses the TLS arenas which
// does not use the OS heap.
DN_Arena arena = DN_ArenaFromHeap(DN_Kilobytes(64), DN_ArenaFlags_NoAllocTrack);
DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena);
DN_StackTraceWalkResult walk = DN_StackTrace_Walk(&arena, limit);
DN_StackTrace_AddWalkToStr8Builder(&walk, &builder, skip);
DN_Str8 result = DN_Str8BuilderBuildFromOSHeap(&builder);
DN_ArenaDeinit(&arena);
return result;
}
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTrace_GetFrames(DN_Arena *arena, uint16_t limit)
{
DN_Slice<DN_StackTraceFrame> result = {};
if (!arena)
return result;
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
DN_StackTraceWalkResult walk = DN_StackTrace_Walk(tmem.arena, limit);
if (!walk.size)
return result;
DN_USize slice_index = 0;
result = DN_Slice_Alloc<DN_StackTraceFrame>(arena, walk.size, DN_ZMem_No);
for (DN_StackTraceWalkResultIterator it = {}; DN_StackTrace_WalkResultIterate(&it, &walk); ) {
result.data[slice_index++] = DN_StackTrace_RawFrameToFrame(arena, it.raw_frame);
}
return result;
}
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame(DN_Arena *arena, DN_StackTraceRawFrame raw_frame)
{
#if defined(DN_OS_WIN32)
// NOTE: Get line+filename /////////////////////////////////////////////////////////////////////
// TODO: Why does zero-initialising this with `line = {};` cause
// SymGetLineFromAddr64 function to fail once we are at
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The
// line and file number are still valid in the result which we use, so,
// we silently ignore this error.
IMAGEHLP_LINEW64 line;
line.SizeOfStruct = sizeof(line);
DWORD line_displacement = 0;
if (!SymGetLineFromAddrW64(raw_frame.process, raw_frame.base_addr, &line_displacement, &line)) {
line = {};
}
// NOTE: Get function name /////////////////////////////////////////////////////////////////////
alignas(SYMBOL_INFOW) char buffer[sizeof(SYMBOL_INFOW) + (MAX_SYM_NAME * sizeof(wchar_t))] = {};
SYMBOL_INFOW *symbol = DN_Cast(SYMBOL_INFOW *)buffer;
symbol->SizeOfStruct = sizeof(*symbol);
symbol->MaxNameLen = sizeof(buffer) - sizeof(*symbol);
uint64_t symbol_displacement = 0; // Offset to the beginning of the symbol to the address
SymFromAddrW(raw_frame.process, raw_frame.base_addr, &symbol_displacement, symbol);
// NOTE: Construct result //////////////////////////////////////////////////////////////////////
DN_Str16 file_name16 = DN_Str16{line.FileName, DN_CStr16Size(line.FileName)};
DN_Str16 function_name16 = DN_Str16{symbol->Name, symbol->NameLen};
DN_StackTraceFrame result = {};
result.address = raw_frame.base_addr;
result.line_number = line.LineNumber;
result.file_name = DN_W32_Str16ToStr8(arena, file_name16);
result.function_name = DN_W32_Str16ToStr8(arena, function_name16);
if (result.function_name.size == 0)
result.function_name = DN_Str8Lit("<unknown function>");
if (result.file_name.size == 0)
result.file_name = DN_Str8Lit("<unknown file>");
#else
DN_StackTraceFrame result = {};
#endif
return result;
}
DN_API void DN_StackTrace_Print(uint16_t limit)
{
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
DN_Slice<DN_StackTraceFrame> stack_trace = DN_StackTrace_GetFrames(tmem.arena, limit);
for (DN_StackTraceFrame &frame : stack_trace)
DN_OS_PrintErrLnF("%.*s(%I64u): %.*s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name));
}
DN_API void DN_StackTrace_ReloadSymbols()
{
#if defined(DN_OS_WIN32)
HANDLE process = GetCurrentProcess();
SymRefreshModuleList(process);
#endif
}
// NOTE: DN_Debug //////////////////////////////////////////////////////////////////////////////////
#if defined(DN_LEAK_TRACKING)
DN_API void DN_DBGTrackAlloc(void *ptr, DN_USize size, bool leak_permitted)
{
if (!ptr)
return;
DN_TicketMutex_Begin(&g_dn_core->alloc_table_mutex);
DN_DEFER
{
DN_TicketMutex_End(&g_dn_core->alloc_table_mutex);
};
// NOTE: If the entry was not added, we are reusing a pointer that has been freed.
// TODO: Add API for always making the item but exposing a var to indicate if the item was newly created or it
// already existed.
DN_Str8 stack_trace = DN_StackTrace_WalkStr8FromHeap(128, 3 /*skip*/);
DN_DSMap<DN_DebugAlloc> *alloc_table = &g_dn_core->alloc_table;
DN_DSMapResult<DN_DebugAlloc> alloc_entry = DN_DSMap_MakeKeyU64(alloc_table, DN_Cast(uint64_t) ptr);
DN_DebugAlloc *alloc = alloc_entry.value;
if (alloc_entry.found) {
if ((alloc->flags & DN_DebugAllocFlag_Freed) == 0) {
DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size);
DN_Str8x32 new_alloc_size = DN_ByteCountStr8x32(size);
DN_HardAssertF(
alloc->flags & DN_DebugAllocFlag_Freed,
"This pointer is already in the leak tracker, however it has not been freed yet. This "
"same pointer is being ask to be tracked twice in the allocation table, e.g. one if its "
"previous free calls has not being marked freed with an equivalent call to "
"DN_DBGTrackDealloc()\n"
"\n"
"The pointer (0x%p) originally allocated %.*s at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer is allocating %.*s again at:\n"
"\n"
"%.*s\n",
ptr,
DN_Str8PrintFmt(alloc_size),
DN_Str8PrintFmt(alloc->stack_trace),
DN_Str8PrintFmt(new_alloc_size),
DN_Str8PrintFmt(stack_trace));
}
// NOTE: Pointer was reused, clean up the prior entry
g_dn_core->alloc_table_bytes_allocated_for_stack_traces -= alloc->stack_trace.size;
g_dn_core->alloc_table_bytes_allocated_for_stack_traces -= alloc->freed_stack_trace.size;
DN_OS_MemDealloc(alloc->stack_trace.data);
DN_OS_MemDealloc(alloc->freed_stack_trace.data);
*alloc = {};
}
alloc->ptr = ptr;
alloc->size = size;
alloc->stack_trace = stack_trace;
alloc->flags |= leak_permitted ? DN_DebugAllocFlag_LeakPermitted : 0;
g_dn_core->alloc_table_bytes_allocated_for_stack_traces += alloc->stack_trace.size;
}
DN_API void DN_DBGTrackDealloc(void *ptr)
{
if (!ptr)
return;
DN_TicketMutex_Begin(&g_dn_core->alloc_table_mutex);
DN_DEFER { DN_TicketMutex_End(&g_dn_core->alloc_table_mutex); };
DN_Str8 stack_trace = DN_StackTrace_WalkStr8FromHeap(128, 3 /*skip*/);
DN_DSMap<DN_DebugAlloc> *alloc_table = &g_dn_core->alloc_table;
DN_DSMapResult<DN_DebugAlloc> alloc_entry = DN_DSMap_FindKeyU64(alloc_table, DN_Cast(uintptr_t) ptr);
DN_HardAssertF(alloc_entry.found,
"Allocated pointer can not be removed as it does not exist in the "
"allocation table. When this memory was allocated, the pointer was "
"not added to the allocation table [ptr=%p]",
ptr);
DN_DebugAlloc *alloc = alloc_entry.value;
if (alloc->flags & DN_DebugAllocFlag_Freed) {
DN_Str8x32 freed_size = DN_ByteCountStr8x32(alloc->freed_size);
DN_HardAssertF((alloc->flags & DN_DebugAllocFlag_Freed) == 0,
"Double free detected, pointer to free was already marked "
"as freed. Either the pointer was reallocated but not "
"traced, or, the pointer was freed twice.\n"
"\n"
"The pointer (0x%p) originally allocated %.*s at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer was freed at:\n"
"\n"
"%.*s\n"
"\n"
"The pointer is being freed again at:\n"
"\n"
"%.*s\n"
,
ptr, DN_Str8PrintFmt(freed_size),
DN_Str8PrintFmt(alloc->stack_trace),
DN_Str8PrintFmt(alloc->freed_stack_trace),
DN_Str8PrintFmt(stack_trace));
}
DN_Assert(alloc->freed_stack_trace.size == 0);
alloc->flags |= DN_DebugAllocFlag_Freed;
alloc->freed_stack_trace = stack_trace;
g_dn_core->alloc_table_bytes_allocated_for_stack_traces += alloc->freed_stack_trace.size;
}
DN_API void DN_DBGDumpLeaks()
{
uint64_t leak_count = 0;
uint64_t leaked_bytes = 0;
for (DN_USize index = 1; index < g_dn_core->alloc_table.occupied; index++) {
DN_DSMapSlot<DN_DebugAlloc> *slot = g_dn_core->alloc_table.slots + index;
DN_DebugAlloc *alloc = &slot->value;
bool alloc_leaked = (alloc->flags & DN_DebugAllocFlag_Freed) == 0;
bool leak_permitted = (alloc->flags & DN_DebugAllocFlag_LeakPermitted);
if (alloc_leaked && !leak_permitted) {
leaked_bytes += alloc->size;
leak_count++;
DN_Str8x32 alloc_size = DN_ByteCountStr8x32(alloc->size);
DN_LOG_WarningF("Pointer (0x%p) leaked %.*s at:\n"
"%.*s",
alloc->ptr, DN_Str8PrintFmt(alloc_size),
DN_Str8PrintFmt(alloc->stack_trace));
}
}
if (leak_count) {
DN_Str8x32 leak_size = DN_ByteCountStr8x32(leaked_bytes);
DN_LOG_WarningF("There were %I64u leaked allocations totalling %.*s", leak_count, DN_Str8PrintFmt(leak_size));
}
}
#endif // DN_LEAK_TRACKING
// NOTE: DN_Profiler
DN_API DN_Profiler DN_Profiler_Init(DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSC tsc, DN_U64 tsc_frequency)
{
DN_Profiler result = {};
result.anchors = anchors;
result.anchors_count = count;
result.anchors_per_frame = anchors_per_frame;
result.tsc = tsc;
result.tsc_frequency = tsc_frequency;
return result;
}
DN_API DN_USize DN_Profiler_FrameCount(DN_Profiler const *profiler)
{
DN_USize result = profiler->anchors_count / profiler->anchors_per_frame;
return result;
}
DN_API DN_ProfilerAnchorArray DN_Profiler_FrameAnchorsFromIndex(DN_Profiler *profiler, DN_USize frame_index)
{
DN_ProfilerAnchorArray result = {};
DN_USize anchor_offset = frame_index * profiler->anchors_per_frame;
result.data = profiler->anchors + anchor_offset;
result.count = profiler->anchors_per_frame;
return result;
}
DN_API DN_ProfilerAnchorArray DN_Profiler_FrameAnchors(DN_Profiler *profiler)
{
DN_ProfilerAnchorArray result = DN_Profiler_FrameAnchorsFromIndex(profiler, profiler->frame_index);
return result;
}
DN_API DN_ProfilerZone DN_Profiler_BeginZone(DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index)
{
DN_ProfilerZone result = {};
if (profiler->paused)
return result;
DN_Assert(anchor_index < profiler->anchors_per_frame);
DN_ProfilerAnchor *anchor = DN_Profiler_FrameAnchors(profiler).data + anchor_index;
anchor->name = name;
// TODO: We need per-thread-local-storage profiler so that we can use these apis
// across threads. For now, we let them overwrite each other but this is not tenable.
#if 0
if (anchor->name.size && anchor->name != name)
DN_AssertF(name == anchor->name, "Potentially overwriting a zone by accident? Anchor is '%.*s', name is '%.*s'", DN_Str8PrintFmt(anchor->name), DN_Str8PrintFmt(name));
#endif
if (profiler->tsc == DN_ProfilerTSC_RDTSC)
result.begin_tsc = DN_CPUGetTSC();
else
result.begin_tsc = DN_OS_PerfCounterNow();
result.anchor_index = anchor_index;
result.parent_zone = profiler->parent_zone;
result.elapsed_tsc_at_zone_start = anchor->tsc_inclusive;
profiler->parent_zone = anchor_index;
return result;
}
DN_API void DN_Profiler_EndZone(DN_Profiler *profiler, DN_ProfilerZone zone)
{
if (profiler->paused)
return;
DN_Assert(zone.anchor_index < profiler->anchors_per_frame);
DN_Assert(zone.parent_zone < profiler->anchors_per_frame);
DN_ProfilerAnchorArray array = DN_Profiler_FrameAnchors(profiler);
DN_ProfilerAnchor *anchor = array.data + zone.anchor_index;
DN_U64 tsc_now = profiler->tsc == DN_ProfilerTSC_RDTSC ? DN_CPUGetTSC() : DN_OS_PerfCounterNow();
DN_U64 elapsed_tsc = tsc_now - zone.begin_tsc;
anchor->hit_count++;
anchor->tsc_inclusive = zone.elapsed_tsc_at_zone_start + elapsed_tsc;
anchor->tsc_exclusive += elapsed_tsc;
if (zone.parent_zone != zone.anchor_index) {
DN_ProfilerAnchor *parent_anchor = array.data + zone.parent_zone;
parent_anchor->tsc_exclusive -= elapsed_tsc;
}
profiler->parent_zone = zone.parent_zone;
}
DN_API void DN_Profiler_NewFrame(DN_Profiler *profiler)
{
if (profiler->paused)
return;
// NOTE: End the frame's zone
DN_Profiler_EndZone(profiler, profiler->frame_zone);
DN_ProfilerAnchorArray old_frame_anchors = DN_Profiler_FrameAnchors(profiler);
DN_ProfilerAnchor old_frame_anchor = old_frame_anchors.data[0];
profiler->frame_avg_tsc = (profiler->frame_avg_tsc + old_frame_anchor.tsc_inclusive) / 2.f;
// NOTE: Bump to the next frame
DN_USize frame_count = profiler->anchors_count / profiler->anchors_per_frame;
profiler->frame_index = (profiler->frame_index + 1) % frame_count;
// NOTE: Zero out the anchors
DN_ProfilerAnchorArray next_anchors = DN_Profiler_FrameAnchors(profiler);
DN_Memset(next_anchors.data, 0, sizeof(*profiler->anchors) * next_anchors.count);
// NOTE: Start the frame's zone
profiler->frame_zone = DN_Profiler_BeginZone(profiler, DN_Str8Lit("Profiler Frame"), 0);
}
DN_API void DN_Profiler_Dump(DN_Profiler *profiler)
{
if (profiler->frame_index == 0)
return;
DN_USize frame_index = profiler->frame_index - 1;
DN_Assert(profiler->frame_index < profiler->anchors_per_frame);
DN_ProfilerAnchor *anchors = profiler->anchors + (frame_index * profiler->anchors_per_frame);
for (DN_USize index = 1; index < profiler->anchors_per_frame; index++) {
DN_ProfilerAnchor const *anchor = anchors + index;
if (!anchor->hit_count)
continue;
DN_U64 tsc_exclusive = anchor->tsc_exclusive;
DN_U64 tsc_inclusive = anchor->tsc_inclusive;
DN_F64 tsc_exclusive_milliseconds = tsc_exclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency;
if (tsc_exclusive == tsc_inclusive) {
DN_OS_PrintOutLnF("%.*s[%u]: %.1fms", DN_Str8PrintFmt(anchor->name), anchor->hit_count, tsc_exclusive_milliseconds);
} else {
DN_F64 tsc_inclusive_milliseconds = tsc_inclusive * 1000 / DN_Cast(DN_F64) profiler->tsc_frequency;
DN_OS_PrintOutLnF("%.*s[%u]: %.1f/%.1fms",
DN_Str8PrintFmt(anchor->name),
anchor->hit_count,
tsc_exclusive_milliseconds,
tsc_inclusive_milliseconds);
}
}
}
DN_API DN_F64 DN_Profiler_SecFromTSC(DN_Profiler *profiler, DN_U64 duration_tsc)
{
DN_F64 result = DN_Cast(DN_F64)duration_tsc / profiler->tsc_frequency;
return result;
}
DN_API DN_F64 DN_Profiler_MsFromTSC(DN_Profiler *profiler, DN_U64 duration_tsc)
{
DN_F64 result = DN_Cast(DN_F64)duration_tsc / profiler->tsc_frequency * 1000.0;
return result;
}

View File

@ -1,107 +0,0 @@
#if !defined(DN_CORE_DEBUG_H)
#define DN_CORE_DEBUG_H
#include "../dn_base_inc.h"
// NOTE: DN_Debug
enum DN_DebugAllocFlag
{
DN_DebugAllocFlag_Freed = 1 << 0,
DN_DebugAllocFlag_LeakPermitted = 1 << 1,
};
struct DN_DebugAlloc
{
void *ptr; // 8 Pointer to the allocation being tracked
DN_USize size; // 16 Size of the allocation
DN_USize freed_size; // 24 Store the size of the allocation when it is freed
DN_Str8 stack_trace; // 40 Stack trace at the point of allocation
DN_Str8 freed_stack_trace; // 56 Stack trace of where the allocation was freed
DN_U16 flags; // 72 Bit flags from `DN_DebugAllocFlag`
};
static_assert(sizeof(DN_DebugAlloc) == 64 || sizeof(DN_DebugAlloc) == 32, // NOTE: 64 bit vs 32 bit pointers respectively
"We aim to keep the allocation record as light as possible as "
"memory tracking can get expensive. Enforce that there is no "
"unexpected padding.");
// NOTE: DN_Profiler
struct DN_ProfilerAnchor
{
// Inclusive refers to the time spent to complete the function call
// including all children functions.
//
// Exclusive refers to the time spent in the function, not including any
// time spent in children functions that we call that are also being
// profiled. If we recursively call into ourselves, the time we spent in
// our function is accumulated.
DN_U64 tsc_inclusive;
DN_U64 tsc_exclusive;
DN_U16 hit_count;
DN_Str8 name;
};
struct DN_ProfilerZone
{
DN_U16 anchor_index;
DN_U64 begin_tsc;
DN_U16 parent_zone;
DN_U64 elapsed_tsc_at_zone_start;
};
struct DN_ProfilerAnchorArray
{
DN_ProfilerAnchor *data;
DN_USize count;
};
enum DN_ProfilerTSC
{
DN_ProfilerTSC_RDTSC,
DN_ProfilerTSC_OSPerformanceCounter,
};
struct DN_Profiler
{
DN_USize frame_index;
DN_ProfilerAnchor *anchors;
DN_USize anchors_count;
DN_USize anchors_per_frame;
DN_U16 parent_zone;
bool paused;
DN_ProfilerTSC tsc;
DN_U64 tsc_frequency;
DN_ProfilerZone frame_zone;
DN_F64 frame_avg_tsc;
};
#define DN_Profiler_ZoneLoop(prof, name, index) \
DN_ProfilerZone DN_UniqueName(zone_) = DN_Profiler_BeginZone(prof, DN_Str8Lit(name), index), DN_UniqueName(dummy_) = {}; \
DN_UniqueName(dummy_).begin_tsc == 0; \
DN_Profiler_EndZone(prof, DN_UniqueName(zone_)), DN_UniqueName(dummy_).begin_tsc = 1
#define DN_Profiler_ZoneLoopAuto(prof, name) DN_Profiler_ZoneLoop(prof, name, __COUNTER__ + 1)
DN_API DN_Profiler DN_Profiler_Init (DN_ProfilerAnchor *anchors, DN_USize count, DN_USize anchors_per_frame, DN_ProfilerTSC tsc, DN_U64 tsc_frequency);
DN_API DN_ProfilerZone DN_Profiler_BeginZone (DN_Profiler *profiler, DN_Str8 name, DN_U16 anchor_index);
#define DN_Profiler_BeginZoneAuto(prof, name) DN_Profiler_BeginZone(prof, DN_Str8Lit(name), __COUNTER__ + 1)
DN_API void DN_Profiler_EndZone (DN_Profiler *profiler, DN_ProfilerZone zone);
DN_API DN_USize DN_Profiler_FrameCount (DN_Profiler const *profiler);
DN_API DN_ProfilerAnchorArray DN_Profiler_FrameAnchorsFromIndex (DN_Profiler *profiler, DN_USize frame_index);
DN_API DN_ProfilerAnchorArray DN_Profiler_FrameAnchors (DN_Profiler *profiler);
DN_API void DN_Profiler_NewFrame (DN_Profiler *profiler);
DN_API void DN_Profiler_Dump (DN_Profiler *profiler);
DN_API DN_F64 DN_Profiler_SecFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
DN_API DN_F64 DN_Profiler_MsFromTSC (DN_Profiler *profiler, DN_U64 duration_tsc);
#if defined(DN_LEAK_TRACKING)
DN_API void DN_DBGTrackAlloc (void *ptr, DN_USize size, bool alloc_can_leak);
DN_API void DN_DBGTrackDealloc (void *ptr);
DN_API void DN_DBGDumpLeaks ();
#else
#define DN_DBGTrackAlloc(ptr, size, alloc_can_leak) do { (void)ptr; (void)size; (void)alloc_can_leak; } while (0)
#define DN_DBGTrackDealloc(ptr) do { (void)ptr; } while (0)
#define DN_DBGDumpLeaks() do { } while (0)
#endif
#endif // DN_CORE_DEBUG_H

View File

@ -1,36 +1,6 @@
/*
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// $$$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\
// $$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\
// $$ | $$ |$$ / $$ |$$ / \__|$$ / \__|
// $$ | $$ |$$ | $$ |$$ | \$$$$$$\
// $$ | $$ |$$ | $$ |$$ | \____$$\
// $$ | $$ |$$ | $$ |$$ | $$\ $$\ $$ |
// $$$$$$$ | $$$$$$ |\$$$$$$ |\$$$$$$ |
// \_______/ \______/ \______/ \______/
//
// dn_docs.cpp -- Library documentation via real code examples
//
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Use this file for documentation and examples of the various APIs in this
// library. Normally docs are written as inline comments in header files,
// however, these quickly go out of date as APIs change. Instead, I provide
// some example code that compiles here that serves to also document the API.
//
// The library header files then become a very minimal reference of exactly the
// function prototypes and definitions instead of massive reams of inline
// comments that visually space out the functions and hinders discoverability
// and/or conciseness of being able to learn the breadth of the APIs.
//
////////////////////////////////////////////////////////////////////////////////////////////////////
*/
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(4702) // unreachable code
void DN_Docs_Demo()
void DN_Demo()
{
// NOTE: Before using anything in the library, DN_Core_Init() must be
// called, for example:
@ -556,74 +526,41 @@ void DN_Docs_Demo()
}
#endif
#if 0
#if !defined(DN_NO_PROFILER)
// NOTE: DN_Profiler
//
// A profiler based off Casey Muratori's Computer Enhance course, Performance
// Aware Programming. This profiler measures function elapsed time using the
// CPU's time stamp counter (e.g. rdtsc) providing a rough cycle count
// that can be converted into a duration.
//
// This profiler uses a double buffer scheme for storing profiling markers.
// After an application's typical update/frame cycle you can swap the
// profiler's buffer whereby the front buffer contains the previous frames
// profiling metrics and the back buffer will be populated with the new
// frame's profiling metrics.
#if defined(DN_OS_CPP)
{
enum Zone
enum DemoZone
{
Zone_MainLoop,
Zone_Count
DemoZone_MainLoop,
DemoZone_Count
};
DN_ProfilerZone profiler_zone_main_update = DN_Profiler_BeginZone(Zone_MainLoop);
#if defined(DN_PLATFORM_EMSCRIPTEN)
DN_ProfilerTSCNowFunc *tsc_now = DN_OS_PerfCounterNow;
DN_U64 tsc_frequency = DN_OS_PerfCounterFrequency();
#else
DN_ProfilerTSCNowFunc *tsc_now = __rdtsc;
DN_U64 tsc_frequency = DN_OS_EstimateTSCPerSecond(100);
#endif
// NOTE: DN_Profiler_AnchorBuffer
//
// Retrieve the requested buffer from the profiler for
// writing/reading profiling metrics. Pass in the enum to specify
// which buffer to grab from the profiler.
//
// The front buffer contains the previous frame's profiling metrics
// and the back buffer is where the profiler is currently writing
// to.
//
// For end user intents and purposes, you likely only need to read
// the front buffer which contain the metrics that you can visualise
// regarding the most profiling metrics recorded.
DN_ProfilerAnchor anchors[4] = {};
DN_USize anchors_count = DN_ArrayCountU(anchors);
DN_USize anchors_per_frame = anchors_count / 2;
DN_Profiler profiler = DN_ProfilerInit(anchors, anchors_count, anchors_per_frame, tsc_now, tsc_frequency);
// NOTE: DN_Profiler_ReadBuffer
//
// Retrieve the buffer of anchors of which there are
// `DN_PROFILER_ANCHOR_BUFFER_SIZE` anchors from the most recent run
// of the profiler after you have called `SwapAnchorBuffer` to trigger
// the double buffer
DN_ProfilerAnchor *read_anchors = DN_Profiler_ReadBuffer();
for (DN_USize index = 0; index < DN_PROFILER_ANCHOR_BUFFER_SIZE; index++) {
DN_ProfilerAnchor *anchor = read_anchors + index;
if (anchor->name.size) {
// ...
for (DN_USize it = 0; it < 1; it++) {
DN_ProfilerNewFrame(&profiler);
DN_ProfilerZone zone = DN_ProfilerBeginZone(&profiler, DN_Str8Lit("Main Loop"), DemoZone_MainLoop);
DN_OS_SleepMs(100);
DN_ProfilerEndZone(&profiler, zone);
DN_ProfilerDump(&profiler);
}
}
// NOTE: DN_Profiler_WriteBuffer
//
// Same as `ReadBuffer` however we return the buffer that the profiler
// is currently writing anchors into.
DN_ProfilerAnchor *write_anchors = DN_Profiler_WriteBuffer();
for (DN_USize index = 0; index < DN_PROFILER_ANCHOR_BUFFER_SIZE; index++) {
DN_ProfilerAnchor *anchor = write_anchors + index;
if (anchor->name.size) {
// ...
}
}
DN_Profiler_EndZone(profiler_zone_main_update);
DN_Profiler_SwapAnchorBuffer(); // Should occur after all profiling zones are ended!
DN_Memset(g_dn_core->profiler, 0, sizeof(*g_dn_core->profiler));
}
#endif // !defined(DN_NO_PROFILER)
#endif
// NOTE: DN_Raycast_LineIntersectV2
@ -1199,5 +1136,4 @@ void DN_Docs_Demo()
// queried using 'DN_W32_LastError'
#endif
}
DN_MSVC_WARNING_POP

View File

@ -4,11 +4,11 @@
#include "../dn_base_inc.h"
#include "../dn_os_inc.h"
#include "../dn_core_inc.h"
#include "../dn_inc.h"
#include "../dn_base_inc.cpp"
#include "../dn_os_inc.cpp"
#include "../dn_core_inc.cpp"
#include "../dn_inc.cpp"
#include "../Extra/dn_math.h"
#include "../Extra/dn_helpers.h"
@ -42,10 +42,7 @@ DN_MSVC_WARNING_DISABLE(6262) // Function uses '29804' bytes of stack. Consider
int main(int, char**)
{
DN_Core core = {};
DN_OSCore os = {};
DN_OS_Init(&os, nullptr);
DN_Core_Init(&core, DN_CoreOnInit_LogAllFeatures);
DN_Init(&core, DN_InitFlags_LogAllFeatures, nullptr);
DN_Tests_RunSuite(DN_TestsPrint_Yes);
return 0;
}

View File

@ -8,8 +8,6 @@
#include <unistd.h> // getpagesize
#endif
static DN_OSCore *g_dn_os_core_;
static void DN_OS_LOGEmitFromTypeTypeFV_(DN_LOGTypeParam type, void *user_data, DN_CallSite call_site, DN_FMT_ATTRIB char const *fmt, va_list args)
{
DN_Assert(user_data);
@ -85,92 +83,6 @@ static void DN_OS_LOGEmitFromTypeTypeFV_(DN_LOGTypeParam type, void *user_data,
DN_OS_PrintLnFV(dest, fmt, args);
}
DN_API void DN_OS_Init(DN_OSCore *os, DN_OSInitArgs *args)
{
g_dn_os_core_ = os;
// NOTE: OS
{
#if defined(DN_PLATFORM_WIN32)
SYSTEM_INFO system_info = {};
GetSystemInfo(&system_info);
os->logical_processor_count = system_info.dwNumberOfProcessors;
os->page_size = system_info.dwPageSize;
os->alloc_granularity = system_info.dwAllocationGranularity;
#else
#if defined(DN_PLATFORM_EMSCRIPTEN)
os->logical_processor_count = 1;
#else
os->logical_processor_count = get_nprocs();
#endif
os->page_size = getpagesize();
os->alloc_granularity = os->page_size;
#endif
}
// NOTE: Setup logging
DN_OS_EmitLogsWithOSPrintFunctions(os);
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
os->arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_ArenaFlags_NoAllocTrack);
#else
os->arena = DN_ArenaFromVMem(DN_Megabytes(1), DN_Kilobytes(4), DN_ArenaFlags_NoAllocTrack);
#endif
#if defined(DN_PLATFORM_WIN32)
os->platform_context = DN_ArenaNew(&os->arena, DN_W32Core, DN_ZMem_Yes);
#elif defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
os->platform_context = DN_ArenaNew(&os->arena, DN_POSIXCore, DN_ZMem_Yes);
#endif
#if defined(DN_PLATFORM_WIN32)
DN_W32Core *w32 = DN_Cast(DN_W32Core *) os->platform_context;
InitializeCriticalSection(&w32->sync_primitive_free_list_mutex);
QueryPerformanceFrequency(&w32->qpc_frequency);
HMODULE module = LoadLibraryA("kernel32.dll");
if (module) {
w32->set_thread_description = DN_Cast(DN_W32SetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription");
FreeLibrary(module);
}
// NOTE: win32 bcrypt
wchar_t const BCRYPT_ALGORITHM[] = L"RNG";
long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/);
if (w32->bcrypt_rng_handle && init_status == 0)
w32->bcrypt_init_success = true;
else
DN_LOG_ErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status);
#else
DN_Posix_Init(DN_Cast(DN_POSIXCore *)os->platform_context);
#endif
}
// NOTE: Initialise tmem arenas which allocate memory and will be
// recorded to the now initialised allocation table. The initialisation
// of tmem memory may request tmem memory itself in leak tracing mode.
// This is supported as the tmem arenas defer allocation tracking until
// initialisation is done.
DN_OSTLSInitArgs tls_init_args = {};
if (args) {
tls_init_args.commit = args->tls_commit;
tls_init_args.reserve = args->tls_reserve;
tls_init_args.err_sink_reserve = args->tls_err_sink_reserve;
tls_init_args.err_sink_commit = args->tls_err_sink_commit;
}
DN_OS_TLSInit(&os->tls, tls_init_args);
DN_OS_TLSSetCurrentThreadTLS(&os->tls);
os->cpu_report = DN_CPUGetReport();
#define DN_CPU_FEAT_XENTRY(label) g_dn_cpu_feature_decl[DN_CPUFeature_##label] = {DN_CPUFeature_##label, DN_Str8Lit(#label)};
DN_CPU_FEAT_XMACRO
#undef DN_CPU_FEAT_XENTRY
DN_Assert(g_dn_os_core_);
}
DN_API void DN_OS_EmitLogsWithOSPrintFunctions(DN_OSCore *os)
{
DN_Assert(os);
@ -321,7 +233,7 @@ DN_API bool DN_OS_DateIsValid(DN_OSDateTime date)
return true;
}
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
// NOTE: Other
DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena)
{
DN_Str8 result = {};
@ -335,7 +247,7 @@ DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena)
return result;
}
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
// NOTE: Counters
DN_API DN_F64 DN_OS_PerfCounterS(uint64_t begin, uint64_t end)
{
uint64_t frequency = DN_OS_PerfCounterFrequency();

View File

@ -259,14 +259,6 @@ struct DN_OSHttpResponse
#endif
};
struct DN_OSInitArgs
{
DN_U64 tls_reserve;
DN_U64 tls_commit;
DN_U64 tls_err_sink_reserve;
DN_U64 tls_err_sink_commit;
};
struct DN_OSCore
{
DN_CPUReport cpu_report;
@ -305,7 +297,6 @@ struct DN_OSDiskSpace
DN_U64 size;
};
DN_API void DN_OS_Init (DN_OSCore *os, DN_OSInitArgs *args);
DN_API void DN_OS_EmitLogsWithOSPrintFunctions (DN_OSCore *os);
DN_API void DN_OS_DumpThreadContextArenaStat (DN_Str8 file_path);

View File

@ -23,7 +23,7 @@ DN_API DN_Arena DN_ArenaFromVMem(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags fl
{
DN_ArenaMemFuncs mem_funcs = {};
mem_funcs.type = DN_ArenaMemFuncType_VMem;
mem_funcs.vmem_page_size = g_dn_os_core_->page_size;
mem_funcs.vmem_page_size = g_dn_->os.page_size;
mem_funcs.vmem_reserve = DN_OS_MemReserve;
mem_funcs.vmem_commit = DN_OS_MemCommit;
mem_funcs.vmem_release = DN_OS_MemRelease;

View File

@ -193,7 +193,7 @@ bool DN_VArray_Reserve(DN_VArray<T> *array, DN_USize count)
return false;
DN_USize real_commit = (array->size + count) * sizeof(T);
DN_USize aligned_commit = DN_AlignUpPowerOfTwo(real_commit, g_dn_os_core_->page_size);
DN_USize aligned_commit = DN_AlignUpPowerOfTwo(real_commit, g_dn_->os.page_size);
if (array->commit >= aligned_commit)
return true;
bool result = DN_OS_MemCommit(array->data, aligned_commit, DN_MemPage_ReadWrite);

View File

@ -29,8 +29,8 @@ DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_fl
os_page_flags |= (PROT_READ | PROT_WRITE);
void *result = mmap(nullptr, size, os_page_flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_frame, 1);
DN_AtomicAddU64(&g_dn_->os.mem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
if (result == MAP_FAILED)
result = nullptr;
return result;
@ -44,8 +44,8 @@ DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
result = mprotect(ptr, size, os_page_flags) == 0;
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_frame, 1);
DN_AtomicAddU64(&g_dn_->os.mem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
return result;
}
@ -68,11 +68,11 @@ DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
static DN_Str8 const ALIGNMENT_ERROR_MSG = DN_Str8Lit(
"Page protection requires pointers to be page aligned because we "
"can only guard memory at a multiple of the page boundary.");
DN_AssertF(DN_IsPowerOfTwoAligned(DN_Cast(uintptr_t) ptr, g_dn_os_core_->page_size),
DN_AssertF(DN_IsPowerOfTwoAligned(DN_Cast(uintptr_t) ptr, g_dn_->os.page_size),
"%s",
ALIGNMENT_ERROR_MSG.data);
DN_AssertF(
DN_IsPowerOfTwoAligned(size, g_dn_os_core_->page_size), "%s", ALIGNMENT_ERROR_MSG.data);
DN_IsPowerOfTwoAligned(size, g_dn_->os.page_size), "%s", ALIGNMENT_ERROR_MSG.data);
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
int result = mprotect(ptr, size, os_page_flags);
@ -283,8 +283,8 @@ DN_API DN_U64 DN_OS_PerfCounterFrequency()
static DN_POSIXCore *DN_OS_GetPOSIXCore_()
{
DN_Assert(g_dn_os_core_ && g_dn_os_core_->platform_context);
DN_POSIXCore *result = DN_Cast(DN_POSIXCore *)g_dn_os_core_->platform_context;
DN_Assert(g_dn_ && g_dn_->os.platform_context);
DN_POSIXCore *result = DN_Cast(DN_POSIXCore *)g_dn_->os.platform_context;
return result;
}
@ -1015,7 +1015,7 @@ static DN_POSIXSyncPrimitive *DN_POSIX_AllocSyncPrimitive_()
posix->sync_primitive_free_list = posix->sync_primitive_free_list->next;
result->next = nullptr;
} else {
DN_OSCore *os = g_dn_os_core_;
DN_OSCore *os = &g_dn_->os;
result = DN_ArenaNew(&os->arena, DN_POSIXSyncPrimitive, DN_ZMem_Yes);
}
}

View File

@ -0,0 +1,209 @@
#define DN_CORE_DEBUG_CPP
#include "../dn_base_inc.h"
#include "../dn_os_inc.h"
DN_API DN_StackTraceWalkResult DN_StackTraceWalk(DN_Arena *arena, uint16_t limit)
{
DN_StackTraceWalkResult result = {};
#if defined(DN_OS_WIN32)
if (!arena)
return result;
static DN_TicketMutex mutex = {};
DN_TicketMutex_Begin(&mutex);
HANDLE thread = GetCurrentThread();
result.process = GetCurrentProcess();
DN_W32Core *w32 = DN_OS_GetW32Core_();
if (!w32->sym_initialised) {
w32->sym_initialised = true;
SymSetOptions(SYMOPT_LOAD_LINES);
if (!SymInitialize(result.process, nullptr /*UserSearchPath*/, true /*fInvadeProcess*/)) {
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
DN_W32Error error = DN_W32_LastError(tmem.arena);
DN_LOG_ErrorF("SymInitialize failed, stack trace can not be generated (%lu): %.*s\n", error.code, DN_Str8PrintFmt(error.msg));
}
}
CONTEXT context;
RtlCaptureContext(&context);
STACKFRAME64 frame = {};
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
DN_FArray<uint64_t, 256> raw_frames = {};
while (raw_frames.size < limit) {
if (!StackWalk64(IMAGE_FILE_MACHINE_AMD64,
result.process,
thread,
&frame,
&context,
nullptr /*ReadMemoryRoutine*/,
SymFunctionTableAccess64,
SymGetModuleBase64,
nullptr /*TranslateAddress*/))
break;
// NOTE: It might be useful one day to use frame.AddrReturn.Offset.
// If AddrPC.Offset == AddrReturn.Offset then we can detect recursion.
DN_FArray_Add(&raw_frames, frame.AddrPC.Offset);
}
DN_TicketMutex_End(&mutex);
result.base_addr = DN_ArenaNewArray(arena, uint64_t, raw_frames.size, DN_ZMem_No);
result.size = DN_Cast(uint16_t) raw_frames.size;
DN_Memcpy(result.base_addr, raw_frames.data, raw_frames.size * sizeof(raw_frames.data[0]));
#else
(void)limit;
(void)arena;
#endif
return result;
}
static void DN_StackTraceAddWalkToStr8Builder(DN_StackTraceWalkResult const *walk, DN_Str8Builder *builder, DN_USize skip)
{
DN_StackTraceRawFrame raw_frame = {};
raw_frame.process = walk->process;
for (DN_USize index = skip; index < walk->size; index++) {
raw_frame.base_addr = walk->base_addr[index];
DN_StackTraceFrame frame = DN_StackTraceRawFrameToFrame(builder->arena, raw_frame);
DN_Str8BuilderAppendF(builder, "%.*s(%zu): %.*s%s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name), (DN_Cast(int) index == walk->size - 1) ? "" : "\n");
}
}
DN_API bool DN_StackTraceWalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk)
{
bool result = false;
if (!it || !walk || !walk->base_addr || !walk->process)
return result;
if (it->index >= walk->size)
return false;
result = true;
it->raw_frame.process = walk->process;
it->raw_frame.base_addr = walk->base_addr[it->index++];
return result;
}
DN_API DN_Str8 DN_StackTraceWalkResultToStr8(DN_Arena *arena, DN_StackTraceWalkResult const *walk, uint16_t skip)
{
DN_Str8 result{};
if (!walk || !arena)
return result;
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
DN_Str8Builder builder = DN_Str8BuilderFromArena(tmem.arena);
DN_StackTraceAddWalkToStr8Builder(walk, &builder, skip);
result = DN_Str8BuilderBuild(&builder, arena);
return result;
}
DN_API DN_Str8 DN_StackTraceWalkStr8(DN_Arena *arena, uint16_t limit, uint16_t skip)
{
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(arena);
DN_StackTraceWalkResult walk = DN_StackTraceWalk(tmem.arena, limit);
DN_Str8 result = DN_StackTraceWalkResultToStr8(arena, &walk, skip);
return result;
}
DN_API DN_Str8 DN_StackTraceWalkStr8FromHeap(uint16_t limit, uint16_t skip)
{
// NOTE: We don't use WalkResultToStr8 because that uses the TLS arenas which
// does not use the OS heap.
DN_Arena arena = DN_ArenaFromHeap(DN_Kilobytes(64), DN_ArenaFlags_NoAllocTrack);
DN_Str8Builder builder = DN_Str8BuilderFromArena(&arena);
DN_StackTraceWalkResult walk = DN_StackTraceWalk(&arena, limit);
DN_StackTraceAddWalkToStr8Builder(&walk, &builder, skip);
DN_Str8 result = DN_Str8BuilderBuildFromOSHeap(&builder);
DN_ArenaDeinit(&arena);
return result;
}
DN_API DN_Slice<DN_StackTraceFrame> DN_StackTraceGetFrames(DN_Arena *arena, uint16_t limit)
{
DN_Slice<DN_StackTraceFrame> result = {};
if (!arena)
return result;
DN_OSTLSTMem tmem = DN_OS_TLSTMem(arena);
DN_StackTraceWalkResult walk = DN_StackTraceWalk(tmem.arena, limit);
if (!walk.size)
return result;
DN_USize slice_index = 0;
result = DN_Slice_Alloc<DN_StackTraceFrame>(arena, walk.size, DN_ZMem_No);
for (DN_StackTraceWalkResultIterator it = {}; DN_StackTraceWalkResultIterate(&it, &walk);)
result.data[slice_index++] = DN_StackTraceRawFrameToFrame(arena, it.raw_frame);
return result;
}
DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame(DN_Arena *arena, DN_StackTraceRawFrame raw_frame)
{
#if defined(DN_OS_WIN32)
// NOTE: Get line+filename
// TODO: Why does zero-initialising this with `line = {};` cause
// SymGetLineFromAddr64 function to fail once we are at
// __scrt_commain_main_seh and hit BaseThreadInitThunk frame? The
// line and file number are still valid in the result which we use, so,
// we silently ignore this error.
IMAGEHLP_LINEW64 line;
line.SizeOfStruct = sizeof(line);
DWORD line_displacement = 0;
if (!SymGetLineFromAddrW64(raw_frame.process, raw_frame.base_addr, &line_displacement, &line))
line = {};
// NOTE: Get function name
alignas(SYMBOL_INFOW) char buffer[sizeof(SYMBOL_INFOW) + (MAX_SYM_NAME * sizeof(wchar_t))] = {};
SYMBOL_INFOW *symbol = DN_Cast(SYMBOL_INFOW *) buffer;
symbol->SizeOfStruct = sizeof(*symbol);
symbol->MaxNameLen = sizeof(buffer) - sizeof(*symbol);
uint64_t symbol_displacement = 0; // Offset to the beginning of the symbol to the address
SymFromAddrW(raw_frame.process, raw_frame.base_addr, &symbol_displacement, symbol);
// NOTE: Construct result
DN_Str16 file_name16 = DN_Str16{line.FileName, DN_CStr16Size(line.FileName)};
DN_Str16 function_name16 = DN_Str16{symbol->Name, symbol->NameLen};
DN_StackTraceFrame result = {};
result.address = raw_frame.base_addr;
result.line_number = line.LineNumber;
result.file_name = DN_W32_Str16ToStr8(arena, file_name16);
result.function_name = DN_W32_Str16ToStr8(arena, function_name16);
if (result.function_name.size == 0)
result.function_name = DN_Str8Lit("<unknown function>");
if (result.file_name.size == 0)
result.file_name = DN_Str8Lit("<unknown file>");
#else
DN_StackTraceFrame result = {};
#endif
return result;
}
DN_API void DN_StackTracePrint(uint16_t limit)
{
DN_OSTLSTMem tmem = DN_OS_TLSTMem(nullptr);
DN_Slice<DN_StackTraceFrame> stack_trace = DN_StackTraceGetFrames(tmem.arena, limit);
for (DN_StackTraceFrame &frame : stack_trace)
DN_OS_PrintErrLnF("%.*s(%I64u): %.*s", DN_Str8PrintFmt(frame.file_name), frame.line_number, DN_Str8PrintFmt(frame.function_name));
}
DN_API void DN_StackTraceReloadSymbols()
{
#if defined(DN_OS_WIN32)
HANDLE process = GetCurrentProcess();
SymRefreshModuleList(process);
#endif
}

View File

@ -51,9 +51,9 @@ DN_API void *DN_OS_MemReserve(DN_USize size, DN_MemCommit commit, DN_U32 page_fl
void *result = VirtualAlloc(nullptr, size, flags, os_page_flags);
if (flags & MEM_COMMIT) {
DN_Assert(g_dn_os_core_);
DN_AtomicAddU64(&g_dn_os_core_->vmem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_os_core_->vmem_allocs_frame, 1);
DN_Assert(g_dn_);
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_frame, 1);
}
return result;
}
@ -65,9 +65,9 @@ DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
return false;
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
result = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
DN_Assert(g_dn_os_core_);
DN_AtomicAddU64(&g_dn_os_core_->vmem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_os_core_->vmem_allocs_frame, 1);
DN_Assert(g_dn_);
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_frame, 1);
return result;
}
@ -95,8 +95,8 @@ DN_API int DN_OS_MemProtect(void *ptr, DN_USize size, DN_U32 page_flags)
static DN_Str8 const ALIGNMENT_ERROR_MSG =
DN_Str8Lit("Page protection requires pointers to be page aligned because we can only guard memory at a multiple of the page boundary.");
DN_AssertF(DN_IsPowerOfTwoAligned(DN_Cast(uintptr_t) ptr, g_dn_os_core_->page_size), "%s", ALIGNMENT_ERROR_MSG.data);
DN_AssertF(DN_IsPowerOfTwoAligned(size, g_dn_os_core_->page_size), "%s", ALIGNMENT_ERROR_MSG.data);
DN_AssertF(DN_IsPowerOfTwoAligned(DN_Cast(uintptr_t) ptr, g_dn_->os.page_size), "%s", ALIGNMENT_ERROR_MSG.data);
DN_AssertF(DN_IsPowerOfTwoAligned(size, g_dn_->os.page_size), "%s", ALIGNMENT_ERROR_MSG.data);
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
unsigned long prev_flags = 0;
@ -113,9 +113,9 @@ DN_API void *DN_OS_MemAlloc(DN_USize size, DN_ZMem z_mem)
DN_U32 flags = z_mem == DN_ZMem_Yes ? HEAP_ZERO_MEMORY : 0;
DN_Assert(size <= DN_Cast(DWORD)(-1));
void *result = HeapAlloc(GetProcessHeap(), flags, DN_Cast(DWORD) size);
DN_Assert(g_dn_os_core_);
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_frame, 1);
DN_Assert(g_dn_);
DN_AtomicAddU64(&g_dn_->os.mem_allocs_total, 1);
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
return result;
}
@ -225,8 +225,8 @@ DN_API DN_OSDateTime DN_OS_DateUnixTimeSToDate(DN_U64 time)
DN_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size)
{
DN_Assert(g_dn_os_core_);
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_os_core_->platform_context;
DN_Assert(g_dn_);
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_->os.platform_context;
DN_Assert(w32->bcrypt_init_success);
long gen_status = BCryptGenRandom(w32->bcrypt_rng_handle, DN_Cast(unsigned char *) buffer, size, 0 /*flags*/);
@ -283,8 +283,8 @@ DN_API void DN_OS_SleepMs(DN_UInt milliseconds)
DN_API DN_U64 DN_OS_PerfCounterFrequency()
{
DN_Assert(g_dn_os_core_);
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_os_core_->platform_context;
DN_Assert(g_dn_);
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_->os.platform_context;
DN_Assert(w32->qpc_frequency.QuadPart);
DN_U64 result = w32->qpc_frequency.QuadPart;
return result;
@ -1002,8 +1002,8 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Slice<DN_Str8> cmd_line, DN_OSExe
static DN_W32Core *DN_OS_GetW32Core_()
{
DN_Assert(g_dn_os_core_ && g_dn_os_core_->platform_context);
DN_W32Core *result = DN_Cast(DN_W32Core *)g_dn_os_core_->platform_context;
DN_Assert(g_dn_ && g_dn_->os.platform_context);
DN_W32Core *result = DN_Cast(DN_W32Core *)g_dn_->os.platform_context;
return result;
}
@ -1033,7 +1033,7 @@ static DN_W32SyncPrimitive *DN_W32_AllocSyncPrimitive_()
w32->sync_primitive_free_list = w32->sync_primitive_free_list->next;
result->next = nullptr;
} else {
DN_OSCore *os = g_dn_os_core_;
DN_OSCore *os = &g_dn_->os;
result = DN_ArenaNew(&os->arena, DN_W32SyncPrimitive, DN_ZMem_Yes);
}
}

View File

@ -3,3 +3,4 @@
#include "Base/dn_base.cpp"
#include "Base/dn_base_containers.cpp"
#include "Base/dn_base_log.cpp"
#include "Base/dn_base_leak.cpp"

View File

@ -58,5 +58,6 @@
#include "Base/dn_base_assert.h"
#include "Base/dn_base_log.h"
#include "Base/dn_base_containers.h"
#include "Base/dn_base_leak.h"
#endif // !defined(DN_BASE_INC_H)

View File

@ -1,5 +0,0 @@
#define DN_CORE_INC_CPP
#include "Core/dn_core.cpp"
#include "Core/dn_core_debug.cpp"
#include "Core/dn_core_demo.cpp"

View File

@ -1,7 +0,0 @@
#if !defined(DN_CORE_INC_H)
#define DN_CORE_INC_H
#include "Core/dn_core_debug.h"
#include "Core/dn_core.h"
#endif // !defined(DN_CORE_INC_H)

180
Source/dn_inc.cpp Normal file
View File

@ -0,0 +1,180 @@
#define DN_INC_CPP
#include "dn_inc.h"
DN_Core *g_dn_;
static void DN_InitOS_(DN_OSCore *os, DN_InitArgs *args)
{
#if defined(DN_OS_H) && defined(DN_OS_CPP)
// NOTE: OS
{
#if defined(DN_PLATFORM_WIN32)
SYSTEM_INFO system_info = {};
GetSystemInfo(&system_info);
os->logical_processor_count = system_info.dwNumberOfProcessors;
os->page_size = system_info.dwPageSize;
os->alloc_granularity = system_info.dwAllocationGranularity;
#else
#if defined(DN_PLATFORM_EMSCRIPTEN)
os->logical_processor_count = 1;
#else
os->logical_processor_count = get_nprocs();
#endif
os->page_size = getpagesize();
os->alloc_granularity = os->page_size;
#endif
}
// NOTE: Setup logging
DN_OS_EmitLogsWithOSPrintFunctions(os);
{
#if defined(DN_PLATFORM_EMSCRIPTEN)
os->arena = DN_ArenaFromHeap(DN_Megabytes(1), DN_ArenaFlags_NoAllocTrack);
#else
os->arena = DN_ArenaFromVMem(DN_Megabytes(1), DN_Kilobytes(4), DN_ArenaFlags_NoAllocTrack);
#endif
#if defined(DN_PLATFORM_WIN32)
os->platform_context = DN_ArenaNew(&os->arena, DN_W32Core, DN_ZMem_Yes);
#elif defined(DN_PLATFORM_POSIX) || defined(DN_PLATFORM_EMSCRIPTEN)
os->platform_context = DN_ArenaNew(&os->arena, DN_POSIXCore, DN_ZMem_Yes);
#endif
#if defined(DN_PLATFORM_WIN32)
DN_W32Core *w32 = DN_Cast(DN_W32Core *) os->platform_context;
InitializeCriticalSection(&w32->sync_primitive_free_list_mutex);
QueryPerformanceFrequency(&w32->qpc_frequency);
HMODULE module = LoadLibraryA("kernel32.dll");
if (module) {
w32->set_thread_description = DN_Cast(DN_W32SetThreadDescriptionFunc *) GetProcAddress(module, "SetThreadDescription");
FreeLibrary(module);
}
// NOTE: win32 bcrypt
wchar_t const BCRYPT_ALGORITHM[] = L"RNG";
long /*NTSTATUS*/ init_status = BCryptOpenAlgorithmProvider(&w32->bcrypt_rng_handle, BCRYPT_ALGORITHM, nullptr /*implementation*/, 0 /*flags*/);
if (w32->bcrypt_rng_handle && init_status == 0)
w32->bcrypt_init_success = true;
else
DN_LOG_ErrorF("Failed to initialise Windows secure random number generator, error: %d", init_status);
#else
DN_Posix_Init(DN_Cast(DN_POSIXCore *)os->platform_context);
#endif
}
// NOTE: Initialise tmem arenas which allocate memory and will be
// recorded to the now initialised allocation table. The initialisation
// of tmem memory may request tmem memory itself in leak tracing mode.
// This is supported as the tmem arenas defer allocation tracking until
// initialisation is done.
DN_OSTLSInitArgs tls_init_args = {};
if (args) {
tls_init_args.commit = args->os_tls_commit;
tls_init_args.reserve = args->os_tls_reserve;
tls_init_args.err_sink_reserve = args->os_tls_err_sink_reserve;
tls_init_args.err_sink_commit = args->os_tls_err_sink_commit;
}
DN_OS_TLSInit(&os->tls, tls_init_args);
DN_OS_TLSSetCurrentThreadTLS(&os->tls);
os->cpu_report = DN_CPUGetReport();
#define DN_CPU_FEAT_XENTRY(label) g_dn_cpu_feature_decl[DN_CPUFeature_##label] = {DN_CPUFeature_##label, DN_Str8Lit(#label)};
DN_CPU_FEAT_XMACRO
#undef DN_CPU_FEAT_XENTRY
DN_Assert(g_dn_);
#endif // defined(DN_OS_H) && defined(DN_OS_CPP)
}
DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args)
{
g_dn_ = dn;
#if defined(DN_OS_H) && defined(DN_OS_CPP)
DN_InitOS_(&dn->os, args);
#endif
// NOTE Initialise fields
#if defined(DN_LEAK_TRACKING)
// NOTE: Setup the allocation table with allocation tracking turned off on
// the arena we're using to initialise the table.
core->alloc_table_arena = DN_ArenaFromVMem(DN_Megabytes(1), DN_Kilobytes(512), DN_ArenaFlags_NoAllocTrack | DN_ArenaFlags_AllocCanLeak);
core->alloc_table = DN_DSMap_Init<DN_DebugAlloc>(&core->alloc_table_arena, 4096, DN_DSMapFlags_Nil);
#endif
// NOTE: Print out init features
DN_OSTLSTMem tmem = DN_OS_TLSPushTMem(nullptr);
DN_Str8Builder builder = DN_Str8BuilderFromArena(tmem.arena);
if (flags & DN_InitFlags_LogLibFeatures) {
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit("DN initialised:\n"));
#if defined(DN_OS_CPP)
DN_F32 page_size_kib = dn->os.page_size / 1024.0f;
DN_F32 alloc_granularity_kib = dn->os.alloc_granularity / 1024.0f;
DN_Str8BuilderAppendF(&builder,
" OS Page Size/Alloc Granularity: %.1f/%.1fKiB\n"
" Logical Processor Count: %u\n",
page_size_kib,
alloc_granularity_kib,
dn->os.logical_processor_count);
#endif
#if DN_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
if (DN_ASAN_POISON) {
DN_Str8BuilderAppendF(
&builder, " ASAN manual poisoning%s\n", DN_ASAN_VET_POISON ? " (+vet sanity checks)" : "");
DN_Str8BuilderAppendF(&builder, " ASAN poison guard size: %u\n", DN_ASAN_POISON_GUARD_SIZE);
}
#endif
#if defined(DN_LEAK_TRACKING)
DN_Str8BuilderAppendRef(&builder, DN_Str8Lit(" Allocation leak tracing\n"));
#endif
#if defined(DN_PLATFORM_EMSCRIPTEN) || defined(DN_PLATFORM_POSIX)
DN_POSIXCore *posix = DN_Cast(DN_POSIXCore *)g_dn_->os.platform_context;
DN_Str8BuilderAppendF(&builder, " Clock GetTime: %S\n", posix->clock_monotonic_raw ? DN_Str8Lit("CLOCK_MONOTONIC_RAW") : DN_Str8Lit("CLOCK_MONOTONIC"));
#endif
// TODO(doyle): Add stacktrace feature log
}
if (flags & DN_InitFlags_LogCPUFeatures) {
DN_CPUReport const *report = &dn->os.cpu_report;
DN_Str8 brand = DN_Str8TrimWhitespaceAround(DN_Str8FromPtr(report->brand, sizeof(report->brand) - 1));
DN_MSVC_WARNING_PUSH
DN_MSVC_WARNING_DISABLE(6284) // Object passed as _Param_(3) when a string is required in call to 'DN_Str8BuilderAppendF' Actual type: 'struct DN_Str8'.
DN_Str8BuilderAppendF(&builder, " CPU '%S' from '%s' detected:\n", brand, report->vendor);
DN_MSVC_WARNING_POP
DN_USize longest_feature_name = 0;
for (DN_ForIndexU(feature_index, DN_CPUFeature_Count)) {
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
longest_feature_name = DN_Max(longest_feature_name, feature_decl.label.size);
}
for (DN_ForIndexU(feature_index, DN_CPUFeature_Count)) {
DN_CPUFeatureDecl feature_decl = g_dn_cpu_feature_decl[feature_index];
bool has_feature = DN_CPUHasFeature(report, feature_decl.value);
DN_Str8BuilderAppendF(&builder,
" %.*s:%*s%s\n",
DN_Str8PrintFmt(feature_decl.label),
DN_Cast(int)(longest_feature_name - feature_decl.label.size),
"",
has_feature ? "available" : "not available");
}
}
DN_Str8 info_log = DN_Str8BuilderBuild(&builder, tmem.arena);
if (info_log.size)
DN_LOG_DebugF("%.*s", DN_Str8PrintFmt(info_log));
}
DN_API void DN_BeginFrame()
{
DN_AtomicSetValue64(&g_dn_->os.mem_allocs_frame, 0);
}

36
Source/dn_inc.h Normal file
View File

@ -0,0 +1,36 @@
#if !defined(DN_INC_H)
#define DN_INC_H
struct DN_Core
{
DN_USize mem_allocs_frame;
DN_LeakTracker leak;
#if defined(DN_OS_H)
DN_OSCore os;
#endif
};
struct DN_InitArgs
{
DN_U64 os_tls_reserve;
DN_U64 os_tls_commit;
DN_U64 os_tls_err_sink_reserve;
DN_U64 os_tls_err_sink_commit;
};
typedef DN_USize DN_InitFlags;
enum DN_InitFlags_
{
DN_InitFlags_Nil = 0,
DN_InitFlags_OS = 1 << 0,
DN_InitFlags_LogLibFeatures = 1 << 1,
DN_InitFlags_LogCPUFeatures = 1 << 2,
DN_InitFlags_LogAllFeatures = DN_InitFlags_LogLibFeatures | DN_InitFlags_LogCPUFeatures,
};
extern DN_Core *g_dn_;
DN_API void DN_Init(DN_Core *dn, DN_InitFlags flags, DN_InitArgs *args);
DN_API void DN_BeginFrame();
#endif // !defined(DN_INC_H)

View File

@ -14,3 +14,5 @@
#else
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
#endif
#include "OS/dn_os_stacktrace.cpp"

View File

@ -5,7 +5,7 @@
#else
#include "Source/dn_base_inc.h"
#include "Source/dn_os_inc.h"
#include "Source/dn_core_inc.h"
#include "Source/dn_inc.h"
#endif
#if USE_SINGLE_HEADER
@ -13,7 +13,7 @@
#else
#include "Source/dn_base_inc.cpp"
#include "Source/dn_os_inc.cpp"
#include "Source/dn_core_inc.cpp"
#include "Source/dn_inc.cpp"
#endif
enum FileType
@ -98,10 +98,10 @@ int main(int argc, char **argv)
File const FILES[] = {
{FileType_Header, DN_Str8Lit("dn_base_inc.h")},
{FileType_Header, DN_Str8Lit("dn_os_inc.h")},
{FileType_Header, DN_Str8Lit("dn_core_inc.h")},
{FileType_Header, DN_Str8Lit("dn_inc.h")},
{FileType_Impl, DN_Str8Lit("dn_base_inc.cpp")},
{FileType_Impl, DN_Str8Lit("dn_os_inc.cpp")},
{FileType_Impl, DN_Str8Lit("dn_core_inc.cpp")},
{FileType_Impl, DN_Str8Lit("dn_inc.cpp")},
};
for (DN_ForIndexU(type, FileType_Count)) {