summaryrefslogtreecommitdiff
path: root/src/util/profiler.h
blob: c8eea246370b6442faa2791817142db6c44ea5bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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