summaryrefslogtreecommitdiff
path: root/src/util/profiler.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/profiler.h')
-rw-r--r--src/util/profiler.h139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/util/profiler.h b/src/util/profiler.h
new file mode 100644
index 0000000..c8eea24
--- /dev/null
+++ b/src/util/profiler.h
@@ -0,0 +1,139 @@
+#pragma once
+
+#if defined(DEBUG) || defined(PROFILER)
+#include "allocator.h"
+#include "time.h"
+#include "thread.h"
+#include "fnv.h"
+
+static THREAD_MUTEX __profiler_intern_mutex;
+
+#ifndef __func__
+ #define __func__ __FUNCTION__
+#endif
+
+#define PROFILE( x ) \
+ PROFILER_LIST_ENTRY* __profiler_intern_id = __profiler_intern_start( x ); \
+ defer( { __profiler_intern_end( __profiler_intern_id ); } )
+
+#define _profiled PROFILE( __func__ );
+
+
+struct PROFILER_LIST_ENTRY {
+ LIST<PROFILER_LIST_ENTRY> children;
+ const char* name;
+ U64 duration;
+ U64 start;
+ FNV1A hash;
+
+ PROFILER_LIST_ENTRY* parent;
+};
+
+struct PROFILER_GLOBAL {
+ LIST<PROFILER_LIST_ENTRY> frames;
+ LIST<PROFILER_LIST_ENTRY> stack;
+ PROFILER_LIST_ENTRY* current = 0;
+};
+
+extern struct CVAR* prof_overlay;
+extern PROFILER_GLOBAL gprof;
+
+inline PROFILER_LIST_ENTRY* __profiler_intern_is_root( FNV1A hash ) {
+ for( auto& it : gprof.stack ) {
+ if( it.hash == hash ) {
+ return &it;
+ }
+ }
+ return 0;
+}
+
+
+inline void __profiler_intern_clear_frame( PROFILER_LIST_ENTRY* entry ) {
+ entry->children.each( __profiler_intern_clear_frame );
+ entry->children.clear();
+}
+
+inline void __profiler_intern_new_frame_child( PROFILER_LIST_ENTRY* entry ) {
+ for( auto& it : entry->children ) {
+ __profiler_intern_new_frame_child( &it );
+ it.parent = entry;
+ }
+}
+
+inline void __profiler_intern_new_frame( PROFILER_LIST_ENTRY* entry ) {
+ PROFILER_LIST_ENTRY ne = *entry;
+ U32 i = gprof.frames.idx_where( fn( PROFILER_LIST_ENTRY* pe ) {
+ return pe->hash == entry->hash;
+ } );
+
+ if( i != -1 ) {
+ __profiler_intern_clear_frame( &gprof.frames.data[i] );
+ gprof.frames.erase( i );
+ }
+
+ PROFILER_LIST_ENTRY* pne = gprof.frames.push( ne );
+ for( auto& it : pne->children ) {
+ it.parent = pne;
+ __profiler_intern_new_frame_child( &it );
+ }
+}
+
+inline PROFILER_LIST_ENTRY* __profiler_intern_start( const char* name ) {
+ PROFILER_LIST_ENTRY e;
+ FNV1A fnv = fnv1a( name );
+ U64 tick = u_tick();
+ PROFILER_LIST_ENTRY* ep;
+
+ thread_mutex_lock( &__profiler_intern_mutex );
+ defer( thread_mutex_unlock( &__profiler_intern_mutex ) );
+ if( (ep = __profiler_intern_is_root( fnv )) != 0 ) {
+ __profiler_intern_new_frame( ep );
+ ep->children.clear();
+ ep->start = tick;
+ gprof.current = ep;
+ return ep;
+ }
+
+ e.name = name;
+ e.children = LIST<PROFILER_LIST_ENTRY>();
+ memset( e.children.data, 0, sizeof(PROFILER_LIST_ENTRY) );
+ e.duration = 0;
+ e.start = tick;
+ e.children = {};
+ e.parent = gprof.current;
+ e.hash = fnv;
+
+ if( gprof.current )
+ ep = gprof.current->children.push( e );
+ else
+ ep = gprof.stack.push( e );
+
+ gprof.current = ep;
+ return ep;
+}
+
+inline void __profiler_intern_end( PROFILER_LIST_ENTRY* entry ) {
+ U64 tick = u_tick();
+
+ thread_mutex_lock( &__profiler_intern_mutex );
+ entry->duration = tick - entry->start;
+ if( gprof.current )
+ gprof.current = gprof.current->parent;
+ thread_mutex_unlock( &__profiler_intern_mutex );
+}
+
+inline void __profiler_intern_init() {
+ thread_mutex_init( &__profiler_intern_mutex );
+}
+
+extern void __profiler_intern_draw_overlay( struct GL_FONT* font );
+
+#define profiler_init() __profiler_intern_init()
+#define profiler_draw_tree( font ) __profiler_intern_draw_overlay( font )
+
+#else
+#define PROFILE( x )
+#define _profiled
+#define profiler_init()
+#define profiler_draw_tree( x )
+#endif