Remove core dn layer
This commit is contained in:
parent
3aba851aef
commit
a8c32301a9
File diff suppressed because it is too large
Load Diff
@ -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)
|
#if !defined(DN_BASE_INC_H)
|
||||||
#define DN_BASE_INC_H
|
#define DN_BASE_INC_H
|
||||||
@ -983,6 +983,50 @@ struct DN_FmtAppendResult
|
|||||||
bool truncated;
|
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)
|
#if !defined(DN_STB_SPRINTF_HEADER_ONLY)
|
||||||
#define STB_SPRINTF_IMPLEMENTATION
|
#define STB_SPRINTF_IMPLEMENTATION
|
||||||
#define STB_SPRINTF_STATIC
|
#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_GCC_WARNING_POP
|
||||||
DN_MSVC_WARNING_POP
|
DN_MSVC_WARNING_POP
|
||||||
|
|
||||||
|
DN_API void DN_BeginFrame ();
|
||||||
|
|
||||||
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
|
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
|
||||||
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
|
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
|
||||||
#define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__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);
|
DN_API DN_Str8x32 DN_ByteCountStr8x32FromType (DN_U64 bytes, DN_ByteCountType type);
|
||||||
#define DN_ByteCountStr8x32(bytes) DN_ByteCountStr8x32FromType(bytes, DN_ByteCountType_Auto)
|
#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)
|
#endif // !defined(DN_BASE_H)
|
||||||
// DN: Single header generator inlined this file => #include "Base/dn_base_os.h"
|
// DN: Single header generator inlined this file => #include "Base/dn_base_os.h"
|
||||||
#if !defined(DN_BASE_OS_H)
|
#if !defined(DN_BASE_OS_H)
|
||||||
@ -3253,26 +3317,26 @@ struct DN_StackTraceWalkResultIterator
|
|||||||
|
|
||||||
|
|
||||||
#if defined(DN_FREESTANDING)
|
#if defined(DN_FREESTANDING)
|
||||||
#define DN_StackTrace_WalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
||||||
#define DN_StackTrace_Walk(...)
|
#define DN_StackTraceWalk(...)
|
||||||
#define DN_StackTrace_WalkResultIterate(...)
|
#define DN_StackTraceWalkResultIterate(...)
|
||||||
#define DN_StackTrace_WalkResultToStr8(...) DN_Str8Lit("N/A")
|
#define DN_StackTraceWalkResultToStr8(...) DN_Str8Lit("N/A")
|
||||||
#define DN_StackTrace_WalkStr8(...) DN_Str8Lit("N/A")
|
#define DN_StackTraceWalkStr8(...) DN_Str8Lit("N/A")
|
||||||
#define DN_StackTrace_WalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
||||||
#define DN_StackTrace_GetFrames(...)
|
#define DN_StackTraceGetFrames(...)
|
||||||
#define DN_StackTrace_RawFrameToFrame(...)
|
#define DN_StackTraceRawFrameToFrame(...)
|
||||||
#define DN_StackTrace_Print(...)
|
#define DN_StackTracePrint(...)
|
||||||
#define DN_StackTrace_ReloadSymbols(...)
|
#define DN_StackTraceReloadSymbols(...)
|
||||||
#else
|
#else
|
||||||
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk (struct DN_Arena *arena, DN_U16 limit);
|
DN_API DN_StackTraceWalkResult DN_StackTraceWalk (struct DN_Arena *arena, DN_U16 limit);
|
||||||
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
|
DN_API bool DN_StackTraceWalkResultIterate(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_StackTraceWalkResultToStr8 (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_StackTraceWalkStr8 (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_Str8 DN_StackTraceWalkStr8FromHeap (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_Slice<DN_StackTraceFrame> DN_StackTraceGetFrames (struct DN_Arena *arena, DN_U16 limit);
|
||||||
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
|
DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
|
||||||
DN_API void DN_StackTrace_Print (DN_U16 limit);
|
DN_API void DN_StackTracePrint (DN_U16 limit);
|
||||||
DN_API void DN_StackTrace_ReloadSymbols ();
|
DN_API void DN_StackTraceReloadSymbols ();
|
||||||
#endif
|
#endif
|
||||||
#endif // !defined(DN_BASE_OS_H)
|
#endif // !defined(DN_BASE_OS_H)
|
||||||
// DN: Single header generator inlined this file => #include "Base/dn_base_assert.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, ...) \
|
#define DN_HardAssertF(expr, fmt, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (!(expr)) { \
|
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_LOG_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||||
DN_Str8PrintFmt(stack_trace_), \
|
DN_Str8PrintFmt(stack_trace_), \
|
||||||
##__VA_ARGS__); \
|
##__VA_ARGS__); \
|
||||||
@ -3300,7 +3364,7 @@ DN_API void DN_StackTrace_ReloadSymbols ();
|
|||||||
#define DN_AssertF(expr, fmt, ...) \
|
#define DN_AssertF(expr, fmt, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (!(expr)) { \
|
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_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||||
DN_Str8PrintFmt(stack_trace_), \
|
DN_Str8PrintFmt(stack_trace_), \
|
||||||
##__VA_ARGS__); \
|
##__VA_ARGS__); \
|
||||||
@ -3313,7 +3377,7 @@ DN_API void DN_StackTrace_ReloadSymbols ();
|
|||||||
static bool once = true; \
|
static bool once = true; \
|
||||||
if (!(expr) && once) { \
|
if (!(expr) && once) { \
|
||||||
once = false; \
|
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_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||||
DN_Str8PrintFmt(stack_trace_), \
|
DN_Str8PrintFmt(stack_trace_), \
|
||||||
##__VA_ARGS__); \
|
##__VA_ARGS__); \
|
||||||
@ -3339,7 +3403,7 @@ DN_API void DN_StackTrace_ReloadSymbols ();
|
|||||||
((expr) ? true : (DN_LOG_WarningF(fmt, ##__VA_ARGS__), false))
|
((expr) ? true : (DN_LOG_WarningF(fmt, ##__VA_ARGS__), false))
|
||||||
#else
|
#else
|
||||||
#define DN_CheckF(expr, fmt, ...) \
|
#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
|
||||||
|
|
||||||
#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);
|
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_NO_LIST)
|
||||||
#endif // !defined(DN_CONTAINER_H)
|
#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)
|
#endif // !defined(DN_BASE_INC_H)
|
||||||
#if !defined(DN_OS_INC_H)
|
#if !defined(DN_OS_INC_H)
|
||||||
@ -5816,14 +5925,6 @@ struct DN_OSHttpResponse
|
|||||||
#endif
|
#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
|
struct DN_OSCore
|
||||||
{
|
{
|
||||||
DN_CPUReport cpu_report;
|
DN_CPUReport cpu_report;
|
||||||
@ -5862,7 +5963,6 @@ struct DN_OSDiskSpace
|
|||||||
DN_U64 size;
|
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_EmitLogsWithOSPrintFunctions (DN_OSCore *os);
|
||||||
DN_API void DN_OS_DumpThreadContextArenaStat (DN_Str8 file_path);
|
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 // !defined(DN_OS_STRING_H)
|
||||||
|
|
||||||
#endif // DN_OS_INC_H
|
#endif // DN_OS_INC_H
|
||||||
#if !defined(DN_CORE_INC_H)
|
#if !defined(DN_INC_H)
|
||||||
#define DN_CORE_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
|
struct DN_Core
|
||||||
{
|
{
|
||||||
// NOTE: Leak Tracing
|
DN_USize mem_allocs_frame;
|
||||||
#if defined(DN_LEAK_TRACKING)
|
DN_LeakTracker leak;
|
||||||
DN_DSMap<DN_DebugAlloc> alloc_table;
|
#if defined(DN_OS_H)
|
||||||
DN_TicketMutex alloc_table_mutex;
|
DN_OSCore os;
|
||||||
DN_Arena alloc_table_arena;
|
|
||||||
#endif
|
#endif
|
||||||
DN_U64 alloc_table_bytes_allocated_for_stack_traces;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DN_CoreOnInit
|
struct DN_InitArgs
|
||||||
{
|
{
|
||||||
DN_CoreOnInit_Nil = 0,
|
DN_U64 os_tls_reserve;
|
||||||
DN_CoreOnInit_LogLibFeatures = 1 << 0,
|
DN_U64 os_tls_commit;
|
||||||
DN_CoreOnInit_LogCPUFeatures = 1 << 1,
|
DN_U64 os_tls_err_sink_reserve;
|
||||||
DN_CoreOnInit_LogAllFeatures = DN_CoreOnInit_LogLibFeatures | DN_CoreOnInit_LogCPUFeatures,
|
DN_U64 os_tls_err_sink_commit;
|
||||||
};
|
};
|
||||||
|
|
||||||
DN_API void DN_Core_Init (DN_Core *core, DN_CoreOnInit on_init);
|
typedef DN_USize DN_InitFlags;
|
||||||
DN_API void DN_Core_BeginFrame();
|
enum DN_InitFlags_
|
||||||
#endif // !defined(DN_CORE_H)
|
{
|
||||||
|
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)
|
#if !defined(DN_MATH_H)
|
||||||
#define DN_MATH_H
|
#define DN_MATH_H
|
||||||
|
|
||||||
|
|||||||
@ -776,7 +776,6 @@ DN_API DN_F32 DN_EpsilonClampF32(DN_F32 value, DN_F32 target, DN_F32 epsilon)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static DN_ArenaBlock *DN_ArenaBlockFromMemFuncs_(DN_U64 reserve, DN_U64 commit, bool track_alloc, bool alloc_can_leak, DN_ArenaMemFuncs mem_funcs)
|
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;
|
DN_ArenaBlock *result = nullptr;
|
||||||
@ -823,7 +822,7 @@ static DN_ArenaBlock *DN_ArenaBlockFromMemFuncs_(DN_U64 reserve, DN_U64 commit,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (track_alloc && result)
|
if (track_alloc && result)
|
||||||
DN_DBGTrackAlloc(result, result->reserve, alloc_can_leak);
|
DN_LeakTrackAlloc(dn->leak, result, result->reserve, alloc_can_leak);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -890,7 +889,7 @@ static void DN_ArenaBlockDeinit_(DN_Arena const *arena, DN_ArenaBlock *block)
|
|||||||
{
|
{
|
||||||
DN_USize release_size = block->reserve;
|
DN_USize release_size = block->reserve;
|
||||||
if (DN_BitIsNotSet(arena->flags, DN_ArenaFlags_NoAllocTrack))
|
if (DN_BitIsNotSet(arena->flags, DN_ArenaFlags_NoAllocTrack))
|
||||||
DN_DBGTrackDealloc(block);
|
DN_LeakTrackDealloc(&g_dn_->leak, block);
|
||||||
DN_ASanUnpoisonMemoryRegion(block, block->commit);
|
DN_ASanUnpoisonMemoryRegion(block, block->commit);
|
||||||
if (arena->flags & DN_ArenaFlags_MemFuncs) {
|
if (arena->flags & DN_ArenaFlags_MemFuncs) {
|
||||||
if (arena->mem_funcs.type == DN_ArenaMemFuncType_Basic)
|
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));
|
DN_Str8x32 result = DN_Str8x32FromFmt("%.2f%.*s", byte_count.bytes, DN_Str8PrintFmt(byte_count.suffix));
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -765,6 +765,50 @@ struct DN_FmtAppendResult
|
|||||||
bool truncated;
|
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)
|
#if !defined(DN_STB_SPRINTF_HEADER_ONLY)
|
||||||
#define STB_SPRINTF_IMPLEMENTATION
|
#define STB_SPRINTF_IMPLEMENTATION
|
||||||
#define STB_SPRINTF_STATIC
|
#define STB_SPRINTF_STATIC
|
||||||
@ -778,6 +822,8 @@ DN_GCC_WARNING_DISABLE(-Wunused-function)
|
|||||||
DN_GCC_WARNING_POP
|
DN_GCC_WARNING_POP
|
||||||
DN_MSVC_WARNING_POP
|
DN_MSVC_WARNING_POP
|
||||||
|
|
||||||
|
DN_API void DN_BeginFrame ();
|
||||||
|
|
||||||
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
|
#define DN_SPrintF(...) STB_SPRINTF_DECORATE(sprintf)(__VA_ARGS__)
|
||||||
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
|
#define DN_SNPrintF(...) STB_SPRINTF_DECORATE(snprintf)(__VA_ARGS__)
|
||||||
#define DN_VSPrintF(...) STB_SPRINTF_DECORATE(vsprintf)(__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);
|
DN_API DN_Str8x32 DN_ByteCountStr8x32FromType (DN_U64 bytes, DN_ByteCountType type);
|
||||||
#define DN_ByteCountStr8x32(bytes) DN_ByteCountStr8x32FromType(bytes, DN_ByteCountType_Auto)
|
#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)
|
#endif // !defined(DN_BASE_H)
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
#define DN_HardAssertF(expr, fmt, ...) \
|
#define DN_HardAssertF(expr, fmt, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (!(expr)) { \
|
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_LOG_ErrorF("Hard assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||||
DN_Str8PrintFmt(stack_trace_), \
|
DN_Str8PrintFmt(stack_trace_), \
|
||||||
##__VA_ARGS__); \
|
##__VA_ARGS__); \
|
||||||
@ -22,7 +22,7 @@
|
|||||||
#define DN_AssertF(expr, fmt, ...) \
|
#define DN_AssertF(expr, fmt, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (!(expr)) { \
|
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_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||||
DN_Str8PrintFmt(stack_trace_), \
|
DN_Str8PrintFmt(stack_trace_), \
|
||||||
##__VA_ARGS__); \
|
##__VA_ARGS__); \
|
||||||
@ -35,7 +35,7 @@
|
|||||||
static bool once = true; \
|
static bool once = true; \
|
||||||
if (!(expr) && once) { \
|
if (!(expr) && once) { \
|
||||||
once = false; \
|
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_LOG_ErrorF("Assertion [" #expr "], stack trace was:\n\n%.*s\n\n" fmt, \
|
||||||
DN_Str8PrintFmt(stack_trace_), \
|
DN_Str8PrintFmt(stack_trace_), \
|
||||||
##__VA_ARGS__); \
|
##__VA_ARGS__); \
|
||||||
@ -61,7 +61,7 @@
|
|||||||
((expr) ? true : (DN_LOG_WarningF(fmt, ##__VA_ARGS__), false))
|
((expr) ? true : (DN_LOG_WarningF(fmt, ##__VA_ARGS__), false))
|
||||||
#else
|
#else
|
||||||
#define DN_CheckF(expr, fmt, ...) \
|
#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
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
142
Source/Base/dn_base_leak.cpp
Normal file
142
Source/Base/dn_base_leak.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Source/Base/dn_base_leak.h
Normal file
44
Source/Base/dn_base_leak.h
Normal 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
|
||||||
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#define DN_BASE_MEM_CPP
|
|
||||||
|
|
||||||
#include "../dn_base_inc.h"
|
|
||||||
|
|
||||||
@ -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)
|
|
||||||
@ -34,25 +34,25 @@ struct DN_StackTraceWalkResultIterator
|
|||||||
|
|
||||||
|
|
||||||
#if defined(DN_FREESTANDING)
|
#if defined(DN_FREESTANDING)
|
||||||
#define DN_StackTrace_WalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
||||||
#define DN_StackTrace_Walk(...)
|
#define DN_StackTraceWalk(...)
|
||||||
#define DN_StackTrace_WalkResultIterate(...)
|
#define DN_StackTraceWalkResultIterate(...)
|
||||||
#define DN_StackTrace_WalkResultToStr8(...) DN_Str8Lit("N/A")
|
#define DN_StackTraceWalkResultToStr8(...) DN_Str8Lit("N/A")
|
||||||
#define DN_StackTrace_WalkStr8(...) DN_Str8Lit("N/A")
|
#define DN_StackTraceWalkStr8(...) DN_Str8Lit("N/A")
|
||||||
#define DN_StackTrace_WalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
#define DN_StackTraceWalkStr8FromHeap(...) DN_Str8Lit("N/A")
|
||||||
#define DN_StackTrace_GetFrames(...)
|
#define DN_StackTraceGetFrames(...)
|
||||||
#define DN_StackTrace_RawFrameToFrame(...)
|
#define DN_StackTraceRawFrameToFrame(...)
|
||||||
#define DN_StackTrace_Print(...)
|
#define DN_StackTracePrint(...)
|
||||||
#define DN_StackTrace_ReloadSymbols(...)
|
#define DN_StackTraceReloadSymbols(...)
|
||||||
#else
|
#else
|
||||||
DN_API DN_StackTraceWalkResult DN_StackTrace_Walk (struct DN_Arena *arena, DN_U16 limit);
|
DN_API DN_StackTraceWalkResult DN_StackTraceWalk (struct DN_Arena *arena, DN_U16 limit);
|
||||||
DN_API bool DN_StackTrace_WalkResultIterate(DN_StackTraceWalkResultIterator *it, DN_StackTraceWalkResult const *walk);
|
DN_API bool DN_StackTraceWalkResultIterate(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_StackTraceWalkResultToStr8 (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_StackTraceWalkStr8 (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_Str8 DN_StackTraceWalkStr8FromHeap (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_Slice<DN_StackTraceFrame> DN_StackTraceGetFrames (struct DN_Arena *arena, DN_U16 limit);
|
||||||
DN_API DN_StackTraceFrame DN_StackTrace_RawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
|
DN_API DN_StackTraceFrame DN_StackTraceRawFrameToFrame (struct DN_Arena *arena, DN_StackTraceRawFrame raw_frame);
|
||||||
DN_API void DN_StackTrace_Print (DN_U16 limit);
|
DN_API void DN_StackTracePrint (DN_U16 limit);
|
||||||
DN_API void DN_StackTrace_ReloadSymbols ();
|
DN_API void DN_StackTraceReloadSymbols ();
|
||||||
#endif
|
#endif
|
||||||
#endif // !defined(DN_BASE_OS_H)
|
#endif // !defined(DN_BASE_OS_H)
|
||||||
|
|||||||
@ -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);
|
|
||||||
}
|
|
||||||
@ -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)
|
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
@ -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_PUSH
|
||||||
DN_MSVC_WARNING_DISABLE(4702) // unreachable code
|
DN_MSVC_WARNING_DISABLE(4702) // unreachable code
|
||||||
|
void DN_Demo()
|
||||||
void DN_Docs_Demo()
|
|
||||||
{
|
{
|
||||||
// NOTE: Before using anything in the library, DN_Core_Init() must be
|
// NOTE: Before using anything in the library, DN_Core_Init() must be
|
||||||
// called, for example:
|
// called, for example:
|
||||||
@ -556,75 +526,42 @@ void DN_Docs_Demo()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 0
|
|
||||||
#if !defined(DN_NO_PROFILER)
|
|
||||||
// NOTE: DN_Profiler
|
// NOTE: DN_Profiler
|
||||||
//
|
//
|
||||||
// A profiler based off Casey Muratori's Computer Enhance course, Performance
|
// A profiler based off Casey Muratori's Computer Enhance course, Performance
|
||||||
// Aware Programming. This profiler measures function elapsed time using the
|
// Aware Programming. This profiler measures function elapsed time using the
|
||||||
// CPU's time stamp counter (e.g. rdtsc) providing a rough cycle count
|
// CPU's time stamp counter (e.g. rdtsc) providing a rough cycle count
|
||||||
// that can be converted into a duration.
|
// that can be converted into a duration.
|
||||||
//
|
#if defined(DN_OS_CPP)
|
||||||
// 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.
|
|
||||||
{
|
{
|
||||||
enum Zone
|
enum DemoZone
|
||||||
{
|
{
|
||||||
Zone_MainLoop,
|
DemoZone_MainLoop,
|
||||||
Zone_Count
|
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
|
DN_ProfilerAnchor anchors[4] = {};
|
||||||
//
|
DN_USize anchors_count = DN_ArrayCountU(anchors);
|
||||||
// Retrieve the requested buffer from the profiler for
|
DN_USize anchors_per_frame = anchors_count / 2;
|
||||||
// writing/reading profiling metrics. Pass in the enum to specify
|
DN_Profiler profiler = DN_ProfilerInit(anchors, anchors_count, anchors_per_frame, tsc_now, tsc_frequency);
|
||||||
// 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.
|
|
||||||
|
|
||||||
// NOTE: DN_Profiler_ReadBuffer
|
for (DN_USize it = 0; it < 1; it++) {
|
||||||
//
|
DN_ProfilerNewFrame(&profiler);
|
||||||
// Retrieve the buffer of anchors of which there are
|
DN_ProfilerZone zone = DN_ProfilerBeginZone(&profiler, DN_Str8Lit("Main Loop"), DemoZone_MainLoop);
|
||||||
// `DN_PROFILER_ANCHOR_BUFFER_SIZE` anchors from the most recent run
|
DN_OS_SleepMs(100);
|
||||||
// of the profiler after you have called `SwapAnchorBuffer` to trigger
|
DN_ProfilerEndZone(&profiler, zone);
|
||||||
// the double buffer
|
DN_ProfilerDump(&profiler);
|
||||||
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) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
#endif
|
|
||||||
|
|
||||||
// NOTE: DN_Raycast_LineIntersectV2
|
// NOTE: DN_Raycast_LineIntersectV2
|
||||||
// Calculate the intersection point of 2 rays returning a `t` value
|
// Calculate the intersection point of 2 rays returning a `t` value
|
||||||
@ -1199,5 +1136,4 @@ void DN_Docs_Demo()
|
|||||||
// queried using 'DN_W32_LastError'
|
// queried using 'DN_W32_LastError'
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
DN_MSVC_WARNING_POP
|
DN_MSVC_WARNING_POP
|
||||||
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
#include "../dn_base_inc.h"
|
#include "../dn_base_inc.h"
|
||||||
#include "../dn_os_inc.h"
|
#include "../dn_os_inc.h"
|
||||||
#include "../dn_core_inc.h"
|
#include "../dn_inc.h"
|
||||||
|
|
||||||
#include "../dn_base_inc.cpp"
|
#include "../dn_base_inc.cpp"
|
||||||
#include "../dn_os_inc.cpp"
|
#include "../dn_os_inc.cpp"
|
||||||
#include "../dn_core_inc.cpp"
|
#include "../dn_inc.cpp"
|
||||||
|
|
||||||
#include "../Extra/dn_math.h"
|
#include "../Extra/dn_math.h"
|
||||||
#include "../Extra/dn_helpers.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**)
|
int main(int, char**)
|
||||||
{
|
{
|
||||||
DN_Core core = {};
|
DN_Core core = {};
|
||||||
DN_OSCore os = {};
|
DN_Init(&core, DN_InitFlags_LogAllFeatures, nullptr);
|
||||||
|
|
||||||
DN_OS_Init(&os, nullptr);
|
|
||||||
DN_Core_Init(&core, DN_CoreOnInit_LogAllFeatures);
|
|
||||||
DN_Tests_RunSuite(DN_TestsPrint_Yes);
|
DN_Tests_RunSuite(DN_TestsPrint_Yes);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,6 @@
|
|||||||
#include <unistd.h> // getpagesize
|
#include <unistd.h> // getpagesize
|
||||||
#endif
|
#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)
|
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);
|
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_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_API void DN_OS_EmitLogsWithOSPrintFunctions(DN_OSCore *os)
|
||||||
{
|
{
|
||||||
DN_Assert(os);
|
DN_Assert(os);
|
||||||
@ -321,7 +233,7 @@ DN_API bool DN_OS_DateIsValid(DN_OSDateTime date)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Other /////////////////////////////////////////////////////////////////////////////////////
|
// NOTE: Other
|
||||||
DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena)
|
DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena)
|
||||||
{
|
{
|
||||||
DN_Str8 result = {};
|
DN_Str8 result = {};
|
||||||
@ -335,7 +247,7 @@ DN_API DN_Str8 DN_OS_EXEDir(DN_Arena *arena)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Counters //////////////////////////////////////////////////////////////////////////////////
|
// NOTE: Counters
|
||||||
DN_API DN_F64 DN_OS_PerfCounterS(uint64_t begin, uint64_t end)
|
DN_API DN_F64 DN_OS_PerfCounterS(uint64_t begin, uint64_t end)
|
||||||
{
|
{
|
||||||
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
uint64_t frequency = DN_OS_PerfCounterFrequency();
|
||||||
|
|||||||
@ -259,14 +259,6 @@ struct DN_OSHttpResponse
|
|||||||
#endif
|
#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
|
struct DN_OSCore
|
||||||
{
|
{
|
||||||
DN_CPUReport cpu_report;
|
DN_CPUReport cpu_report;
|
||||||
@ -305,7 +297,6 @@ struct DN_OSDiskSpace
|
|||||||
DN_U64 size;
|
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_EmitLogsWithOSPrintFunctions (DN_OSCore *os);
|
||||||
DN_API void DN_OS_DumpThreadContextArenaStat (DN_Str8 file_path);
|
DN_API void DN_OS_DumpThreadContextArenaStat (DN_Str8 file_path);
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ DN_API DN_Arena DN_ArenaFromVMem(DN_U64 reserve, DN_U64 commit, DN_ArenaFlags fl
|
|||||||
{
|
{
|
||||||
DN_ArenaMemFuncs mem_funcs = {};
|
DN_ArenaMemFuncs mem_funcs = {};
|
||||||
mem_funcs.type = DN_ArenaMemFuncType_VMem;
|
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_reserve = DN_OS_MemReserve;
|
||||||
mem_funcs.vmem_commit = DN_OS_MemCommit;
|
mem_funcs.vmem_commit = DN_OS_MemCommit;
|
||||||
mem_funcs.vmem_release = DN_OS_MemRelease;
|
mem_funcs.vmem_release = DN_OS_MemRelease;
|
||||||
|
|||||||
@ -193,7 +193,7 @@ bool DN_VArray_Reserve(DN_VArray<T> *array, DN_USize count)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
DN_USize real_commit = (array->size + count) * sizeof(T);
|
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)
|
if (array->commit >= aligned_commit)
|
||||||
return true;
|
return true;
|
||||||
bool result = DN_OS_MemCommit(array->data, aligned_commit, DN_MemPage_ReadWrite);
|
bool result = DN_OS_MemCommit(array->data, aligned_commit, DN_MemPage_ReadWrite);
|
||||||
|
|||||||
@ -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);
|
os_page_flags |= (PROT_READ | PROT_WRITE);
|
||||||
|
|
||||||
void *result = mmap(nullptr, size, os_page_flags, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
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.mem_allocs_total, 1);
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_frame, 1);
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
|
||||||
if (result == MAP_FAILED)
|
if (result == MAP_FAILED)
|
||||||
result = nullptr;
|
result = nullptr;
|
||||||
return result;
|
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);
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
||||||
result = mprotect(ptr, size, os_page_flags) == 0;
|
result = mprotect(ptr, size, os_page_flags) == 0;
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_total, 1);
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_total, 1);
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_frame, 1);
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
|
||||||
return result;
|
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(
|
static DN_Str8 const ALIGNMENT_ERROR_MSG = DN_Str8Lit(
|
||||||
"Page protection requires pointers to be page aligned because we "
|
"Page protection requires pointers to be page aligned because we "
|
||||||
"can only guard memory at a multiple of the page boundary.");
|
"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",
|
"%s",
|
||||||
ALIGNMENT_ERROR_MSG.data);
|
ALIGNMENT_ERROR_MSG.data);
|
||||||
DN_AssertF(
|
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);
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
||||||
int result = mprotect(ptr, size, os_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_()
|
static DN_POSIXCore *DN_OS_GetPOSIXCore_()
|
||||||
{
|
{
|
||||||
DN_Assert(g_dn_os_core_ && 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_core_->platform_context;
|
DN_POSIXCore *result = DN_Cast(DN_POSIXCore *)g_dn_->os.platform_context;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1015,7 +1015,7 @@ static DN_POSIXSyncPrimitive *DN_POSIX_AllocSyncPrimitive_()
|
|||||||
posix->sync_primitive_free_list = posix->sync_primitive_free_list->next;
|
posix->sync_primitive_free_list = posix->sync_primitive_free_list->next;
|
||||||
result->next = nullptr;
|
result->next = nullptr;
|
||||||
} else {
|
} else {
|
||||||
DN_OSCore *os = g_dn_os_core_;
|
DN_OSCore *os = &g_dn_->os;
|
||||||
result = DN_ArenaNew(&os->arena, DN_POSIXSyncPrimitive, DN_ZMem_Yes);
|
result = DN_ArenaNew(&os->arena, DN_POSIXSyncPrimitive, DN_ZMem_Yes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
209
Source/OS/dn_os_stacktrace.cpp
Normal file
209
Source/OS/dn_os_stacktrace.cpp
Normal 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
|
||||||
|
}
|
||||||
@ -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);
|
void *result = VirtualAlloc(nullptr, size, flags, os_page_flags);
|
||||||
if (flags & MEM_COMMIT) {
|
if (flags & MEM_COMMIT) {
|
||||||
DN_Assert(g_dn_os_core_);
|
DN_Assert(g_dn_);
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->vmem_allocs_total, 1);
|
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_total, 1);
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->vmem_allocs_frame, 1);
|
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_frame, 1);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -65,9 +65,9 @@ DN_API bool DN_OS_MemCommit(void *ptr, DN_USize size, DN_U32 page_flags)
|
|||||||
return false;
|
return false;
|
||||||
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
unsigned long os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
||||||
result = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
|
result = VirtualAlloc(ptr, size, MEM_COMMIT, os_page_flags) != nullptr;
|
||||||
DN_Assert(g_dn_os_core_);
|
DN_Assert(g_dn_);
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->vmem_allocs_total, 1);
|
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_total, 1);
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->vmem_allocs_frame, 1);
|
DN_AtomicAddU64(&g_dn_->os.vmem_allocs_frame, 1);
|
||||||
return result;
|
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 =
|
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_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(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_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 os_page_flags = DN_OS_MemConvertPageToOSFlags_(page_flags);
|
||||||
unsigned long prev_flags = 0;
|
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_U32 flags = z_mem == DN_ZMem_Yes ? HEAP_ZERO_MEMORY : 0;
|
||||||
DN_Assert(size <= DN_Cast(DWORD)(-1));
|
DN_Assert(size <= DN_Cast(DWORD)(-1));
|
||||||
void *result = HeapAlloc(GetProcessHeap(), flags, DN_Cast(DWORD) size);
|
void *result = HeapAlloc(GetProcessHeap(), flags, DN_Cast(DWORD) size);
|
||||||
DN_Assert(g_dn_os_core_);
|
DN_Assert(g_dn_);
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_total, 1);
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_total, 1);
|
||||||
DN_AtomicAddU64(&g_dn_os_core_->mem_allocs_frame, 1);
|
DN_AtomicAddU64(&g_dn_->os.mem_allocs_frame, 1);
|
||||||
return result;
|
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_API void DN_OS_GenBytesSecure(void *buffer, DN_U32 size)
|
||||||
{
|
{
|
||||||
DN_Assert(g_dn_os_core_);
|
DN_Assert(g_dn_);
|
||||||
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_os_core_->platform_context;
|
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_->os.platform_context;
|
||||||
DN_Assert(w32->bcrypt_init_success);
|
DN_Assert(w32->bcrypt_init_success);
|
||||||
|
|
||||||
long gen_status = BCryptGenRandom(w32->bcrypt_rng_handle, DN_Cast(unsigned char *) buffer, size, 0 /*flags*/);
|
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_API DN_U64 DN_OS_PerfCounterFrequency()
|
||||||
{
|
{
|
||||||
DN_Assert(g_dn_os_core_);
|
DN_Assert(g_dn_);
|
||||||
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_os_core_->platform_context;
|
DN_W32Core *w32 = DN_Cast(DN_W32Core *) g_dn_->os.platform_context;
|
||||||
DN_Assert(w32->qpc_frequency.QuadPart);
|
DN_Assert(w32->qpc_frequency.QuadPart);
|
||||||
DN_U64 result = w32->qpc_frequency.QuadPart;
|
DN_U64 result = w32->qpc_frequency.QuadPart;
|
||||||
return result;
|
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_()
|
static DN_W32Core *DN_OS_GetW32Core_()
|
||||||
{
|
{
|
||||||
DN_Assert(g_dn_os_core_ && 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_core_->platform_context;
|
DN_W32Core *result = DN_Cast(DN_W32Core *)g_dn_->os.platform_context;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1033,7 +1033,7 @@ static DN_W32SyncPrimitive *DN_W32_AllocSyncPrimitive_()
|
|||||||
w32->sync_primitive_free_list = w32->sync_primitive_free_list->next;
|
w32->sync_primitive_free_list = w32->sync_primitive_free_list->next;
|
||||||
result->next = nullptr;
|
result->next = nullptr;
|
||||||
} else {
|
} else {
|
||||||
DN_OSCore *os = g_dn_os_core_;
|
DN_OSCore *os = &g_dn_->os;
|
||||||
result = DN_ArenaNew(&os->arena, DN_W32SyncPrimitive, DN_ZMem_Yes);
|
result = DN_ArenaNew(&os->arena, DN_W32SyncPrimitive, DN_ZMem_Yes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,3 +3,4 @@
|
|||||||
#include "Base/dn_base.cpp"
|
#include "Base/dn_base.cpp"
|
||||||
#include "Base/dn_base_containers.cpp"
|
#include "Base/dn_base_containers.cpp"
|
||||||
#include "Base/dn_base_log.cpp"
|
#include "Base/dn_base_log.cpp"
|
||||||
|
#include "Base/dn_base_leak.cpp"
|
||||||
|
|||||||
@ -58,5 +58,6 @@
|
|||||||
#include "Base/dn_base_assert.h"
|
#include "Base/dn_base_assert.h"
|
||||||
#include "Base/dn_base_log.h"
|
#include "Base/dn_base_log.h"
|
||||||
#include "Base/dn_base_containers.h"
|
#include "Base/dn_base_containers.h"
|
||||||
|
#include "Base/dn_base_leak.h"
|
||||||
|
|
||||||
#endif // !defined(DN_BASE_INC_H)
|
#endif // !defined(DN_BASE_INC_H)
|
||||||
|
|||||||
@ -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"
|
|
||||||
@ -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
180
Source/dn_inc.cpp
Normal 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
36
Source/dn_inc.h
Normal 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)
|
||||||
@ -14,3 +14,5 @@
|
|||||||
#else
|
#else
|
||||||
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
#error Please define a platform e.g. 'DN_PLATFORM_WIN32' to enable the correct implementation for platform APIs
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "OS/dn_os_stacktrace.cpp"
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
#else
|
#else
|
||||||
#include "Source/dn_base_inc.h"
|
#include "Source/dn_base_inc.h"
|
||||||
#include "Source/dn_os_inc.h"
|
#include "Source/dn_os_inc.h"
|
||||||
#include "Source/dn_core_inc.h"
|
#include "Source/dn_inc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if USE_SINGLE_HEADER
|
#if USE_SINGLE_HEADER
|
||||||
@ -13,7 +13,7 @@
|
|||||||
#else
|
#else
|
||||||
#include "Source/dn_base_inc.cpp"
|
#include "Source/dn_base_inc.cpp"
|
||||||
#include "Source/dn_os_inc.cpp"
|
#include "Source/dn_os_inc.cpp"
|
||||||
#include "Source/dn_core_inc.cpp"
|
#include "Source/dn_inc.cpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum FileType
|
enum FileType
|
||||||
@ -98,10 +98,10 @@ int main(int argc, char **argv)
|
|||||||
File const FILES[] = {
|
File const FILES[] = {
|
||||||
{FileType_Header, DN_Str8Lit("dn_base_inc.h")},
|
{FileType_Header, DN_Str8Lit("dn_base_inc.h")},
|
||||||
{FileType_Header, DN_Str8Lit("dn_os_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_base_inc.cpp")},
|
||||||
{FileType_Impl, DN_Str8Lit("dn_os_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)) {
|
for (DN_ForIndexU(type, FileType_Count)) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user