1 #include "config.h"
2 
3 #include "gskprofilerprivate.h"
4 
5 #define MAX_SAMPLES     32
6 
7 typedef struct {
8   GQuark id;
9   char *description;
10   gint64 value;
11   gint64 n_samples;
12   gboolean can_reset : 1;
13 } NamedCounter;
14 
15 typedef struct {
16   GQuark id;
17   char *description;
18   gint64 value;
19   gint64 start_time;
20   gint64 min_value;
21   gint64 max_value;
22   gint64 avg_value;
23   gint64 n_samples;
24   gboolean in_flight : 1;
25   gboolean can_reset : 1;
26   gboolean invert : 1;
27 } NamedTimer;
28 
29 typedef struct {
30   GQuark id;
31   gint64 value;
32 } Sample;
33 
34 struct _GskProfiler
35 {
36   GObject parent_instance;
37 
38   GHashTable *counters;
39   GHashTable *timers;
40 
41   Sample timer_samples[MAX_SAMPLES];
42   guint last_sample;
43 };
44 
G_DEFINE_TYPE(GskProfiler,gsk_profiler,G_TYPE_OBJECT)45 G_DEFINE_TYPE (GskProfiler, gsk_profiler, G_TYPE_OBJECT)
46 
47 static void
48 named_counter_free (gpointer data)
49 {
50   NamedCounter *counter = data;
51 
52   if (data == NULL)
53     return;
54 
55   g_free (counter->description);
56 
57   g_slice_free (NamedCounter, counter);
58 }
59 
60 static void
named_timer_free(gpointer data)61 named_timer_free (gpointer data)
62 {
63   NamedTimer *timer = data;
64 
65   if (data == NULL)
66     return;
67 
68   g_free (timer->description);
69 
70   g_slice_free (NamedTimer, timer);
71 }
72 
73 static void
gsk_profiler_finalize(GObject * gobject)74 gsk_profiler_finalize (GObject *gobject)
75 {
76   GskProfiler *self = GSK_PROFILER (gobject);
77 
78   g_clear_pointer (&self->counters, g_hash_table_unref);
79   g_clear_pointer (&self->timers, g_hash_table_unref);
80 
81   G_OBJECT_CLASS (gsk_profiler_parent_class)->finalize (gobject);
82 }
83 
84 static void
gsk_profiler_class_init(GskProfilerClass * klass)85 gsk_profiler_class_init (GskProfilerClass *klass)
86 {
87   G_OBJECT_CLASS (klass)->finalize = gsk_profiler_finalize;
88 }
89 
90 static void
gsk_profiler_init(GskProfiler * self)91 gsk_profiler_init (GskProfiler *self)
92 {
93   self->counters = g_hash_table_new_full (g_direct_hash, g_direct_equal,
94                                           NULL,
95                                           named_counter_free);
96   self->timers = g_hash_table_new_full (g_direct_hash, g_direct_equal,
97                                         NULL,
98                                         named_timer_free);
99 }
100 
101 GskProfiler *
gsk_profiler_new(void)102 gsk_profiler_new (void)
103 {
104   return g_object_new (GSK_TYPE_PROFILER, NULL);
105 }
106 
107 static NamedCounter *
named_counter_new(GQuark id,const char * description,gboolean can_reset)108 named_counter_new (GQuark      id,
109                    const char *description,
110                    gboolean    can_reset)
111 {
112   NamedCounter *res = g_slice_new0 (NamedCounter);
113 
114   res->id = id;
115   res->description = g_strdup (description);
116   res->can_reset = can_reset;
117 
118   return res;
119 }
120 
121 static NamedCounter *
gsk_profiler_get_counter(GskProfiler * profiler,GQuark id)122 gsk_profiler_get_counter (GskProfiler *profiler,
123                           GQuark       id)
124 {
125   return g_hash_table_lookup (profiler->counters, GINT_TO_POINTER (id));
126 }
127 
128 GQuark
gsk_profiler_add_counter(GskProfiler * profiler,const char * counter_name,const char * description,gboolean can_reset)129 gsk_profiler_add_counter (GskProfiler *profiler,
130                           const char  *counter_name,
131                           const char  *description,
132                           gboolean     can_reset)
133 {
134   NamedCounter *counter;
135   GQuark id;
136 
137   g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0);
138 
139   id = g_quark_from_string (counter_name);
140   counter = gsk_profiler_get_counter (profiler, id);
141   if (counter != NULL)
142     {
143       g_critical ("Cannot add a counter '%s' as one already exists.", counter_name);
144       return counter->id;
145     }
146 
147   counter = named_counter_new (id, description, can_reset);
148   g_hash_table_insert (profiler->counters, GINT_TO_POINTER (id), counter);
149 
150   return counter->id;
151 }
152 
153 static NamedTimer *
named_timer_new(GQuark id,const char * description,gboolean invert,gboolean can_reset)154 named_timer_new (GQuark      id,
155                  const char *description,
156                  gboolean    invert,
157                  gboolean    can_reset)
158 {
159   NamedTimer *res = g_slice_new0 (NamedTimer);
160 
161   res->id = id;
162   res->description = g_strdup (description);
163   res->invert = invert;
164   res->can_reset = can_reset;
165 
166   return res;
167 }
168 
169 static NamedTimer *
gsk_profiler_get_timer(GskProfiler * profiler,GQuark id)170 gsk_profiler_get_timer (GskProfiler *profiler,
171                         GQuark       id)
172 {
173   return g_hash_table_lookup (profiler->timers, GINT_TO_POINTER (id));
174 }
175 
176 GQuark
gsk_profiler_add_timer(GskProfiler * profiler,const char * timer_name,const char * description,gboolean invert,gboolean can_reset)177 gsk_profiler_add_timer (GskProfiler *profiler,
178                         const char  *timer_name,
179                         const char  *description,
180                         gboolean     invert,
181                         gboolean     can_reset)
182 {
183   NamedTimer *timer;
184   GQuark id;
185 
186   g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0);
187 
188   id = g_quark_from_string (timer_name);
189   timer = gsk_profiler_get_timer (profiler, id);
190   if (timer != NULL)
191     {
192       g_critical ("Cannot add a timer '%s' as one already exists.", timer_name);
193       return timer->id;
194     }
195 
196   timer = named_timer_new (id, description, invert, can_reset);
197   g_hash_table_insert (profiler->timers, GINT_TO_POINTER (id), timer);
198 
199   return timer->id;
200 }
201 
202 void
gsk_profiler_counter_inc(GskProfiler * profiler,GQuark counter_id)203 gsk_profiler_counter_inc (GskProfiler *profiler,
204                           GQuark       counter_id)
205 {
206   gsk_profiler_counter_add (profiler, counter_id, 1);
207 }
208 
209 void
gsk_profiler_counter_set(GskProfiler * profiler,GQuark counter_id,gint64 value)210 gsk_profiler_counter_set (GskProfiler *profiler,
211                           GQuark       counter_id,
212                           gint64       value)
213 {
214   NamedCounter *counter;
215 
216   g_return_if_fail (GSK_IS_PROFILER (profiler));
217 
218   counter = gsk_profiler_get_counter (profiler, counter_id);
219   if (counter == NULL)
220     {
221       g_critical ("No counter '%s' (id:%d) found; did you forget to call gsk_profiler_add_counter()?",
222                   g_quark_to_string (counter_id), counter_id);
223       return;
224     }
225 
226   counter->value = value;
227 }
228 
229 void
gsk_profiler_counter_add(GskProfiler * profiler,GQuark counter_id,gint64 increment)230 gsk_profiler_counter_add (GskProfiler *profiler,
231                           GQuark       counter_id,
232                           gint64       increment)
233 {
234   NamedCounter *counter;
235 
236   g_return_if_fail (GSK_IS_PROFILER (profiler));
237 
238   counter = gsk_profiler_get_counter (profiler, counter_id);
239   if (counter == NULL)
240     {
241       g_critical ("No counter '%s' (id:%d) found; did you forget to call gsk_profiler_add_counter()?",
242                   g_quark_to_string (counter_id), counter_id);
243       return;
244     }
245 
246   counter->value += increment;
247 }
248 
249 void
gsk_profiler_timer_begin(GskProfiler * profiler,GQuark timer_id)250 gsk_profiler_timer_begin (GskProfiler *profiler,
251                           GQuark       timer_id)
252 {
253   NamedTimer *timer;
254 
255   g_return_if_fail (GSK_IS_PROFILER (profiler));
256 
257   timer = gsk_profiler_get_timer (profiler, timer_id);
258   if (timer == NULL)
259     return;
260 
261   if (timer->in_flight)
262     return;
263 
264   timer->in_flight = TRUE;
265   timer->start_time = g_get_monotonic_time ();
266 }
267 
268 gint64
gsk_profiler_timer_end(GskProfiler * profiler,GQuark timer_id)269 gsk_profiler_timer_end (GskProfiler *profiler,
270                         GQuark       timer_id)
271 {
272   NamedTimer *timer;
273   gint64 diff;
274 
275   g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0);
276 
277   timer = gsk_profiler_get_timer (profiler, timer_id);
278   if (timer == NULL)
279     {
280       g_critical ("No timer '%s' (id:%d) found; did you forget to call gsk_profiler_add_timer()?",
281                   g_quark_to_string (timer_id), timer_id);
282       return 0;
283     }
284 
285   if (!timer->in_flight)
286     {
287       g_critical ("Timer '%s' (id:%d) is not running; did you forget to call gsk_profiler_timer_begin()?",
288                   g_quark_to_string (timer->id), timer->id);
289       return 0;
290     }
291 
292   diff = g_get_monotonic_time () - timer->start_time;
293 
294   timer->in_flight = FALSE;
295   timer->value += diff;
296 
297   return diff;
298 }
299 
300 void
gsk_profiler_timer_set(GskProfiler * profiler,GQuark timer_id,gint64 value)301 gsk_profiler_timer_set (GskProfiler *profiler,
302                         GQuark       timer_id,
303                         gint64       value)
304 {
305   NamedTimer *timer;
306 
307   g_return_if_fail (GSK_IS_PROFILER (profiler));
308 
309   timer = gsk_profiler_get_timer (profiler, timer_id);
310   if (timer == NULL)
311     {
312       g_critical ("No timer '%s' (id:%d) found; did you forget to call gsk_profiler_add_timer()?",
313                   g_quark_to_string (timer_id), timer_id);
314       return;
315     }
316 
317   if (timer->in_flight)
318     {
319       g_critical ("Timer '%s' (id:%d) is running; are you sure you don't want to call "
320                   "gsk_profiler_timer_end() instead of gsk_profiler_timer_set()?",
321                   g_quark_to_string (timer_id), timer_id);
322     }
323 
324   timer->value = value;
325 }
326 
327 gint64
gsk_profiler_counter_get(GskProfiler * profiler,GQuark counter_id)328 gsk_profiler_counter_get (GskProfiler *profiler,
329                           GQuark       counter_id)
330 {
331   NamedCounter *counter;
332 
333   g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0);
334 
335   counter = gsk_profiler_get_counter (profiler, counter_id);
336   if (counter == NULL)
337     {
338       g_critical ("No counter '%s' (id:%d) found; did you forget to call gsk_profiler_add_counter()?",
339                   g_quark_to_string (counter_id), counter_id);
340       return 0;
341     }
342 
343   return counter->value;
344 }
345 
346 gint64
gsk_profiler_timer_get(GskProfiler * profiler,GQuark timer_id)347 gsk_profiler_timer_get (GskProfiler *profiler,
348                         GQuark       timer_id)
349 {
350   NamedTimer *timer;
351 
352   g_return_val_if_fail (GSK_IS_PROFILER (profiler), 0);
353 
354   timer = gsk_profiler_get_timer (profiler, timer_id);
355   if (timer == NULL)
356     {
357       g_critical ("No timer '%s' (id:%d) found; did you forget to call gsk_profiler_add_timer()?",
358                   g_quark_to_string (timer_id), timer_id);
359       return 0;
360     }
361 
362   if (timer->invert)
363     return (gint64) (1000000.0 / (double) timer->value);
364 
365   return timer->value;
366 }
367 
368 gint64
gsk_profiler_timer_get_start(GskProfiler * profiler,GQuark timer_id)369 gsk_profiler_timer_get_start (GskProfiler *profiler,
370                               GQuark       timer_id)
371 {
372   NamedTimer *timer;
373 
374   timer = gsk_profiler_get_timer (profiler, timer_id);
375   if (timer == NULL)
376     return 0;
377 
378   return timer->start_time;
379 }
380 
381 void
gsk_profiler_reset(GskProfiler * profiler)382 gsk_profiler_reset (GskProfiler *profiler)
383 {
384   GHashTableIter iter;
385   gpointer value_p = NULL;
386 
387   g_return_if_fail (GSK_IS_PROFILER (profiler));
388 
389   g_hash_table_iter_init (&iter, profiler->counters);
390   while (g_hash_table_iter_next (&iter, NULL, &value_p))
391     {
392       NamedCounter *counter = value_p;
393 
394       if (counter->can_reset)
395         counter->value = 0;
396     }
397 
398   g_hash_table_iter_init (&iter, profiler->timers);
399   while (g_hash_table_iter_next (&iter, NULL, &value_p))
400     {
401       NamedTimer *timer = value_p;
402 
403       if (timer->can_reset)
404         {
405           timer->value = 0;
406           timer->min_value = 0;
407           timer->max_value = 0;
408           timer->avg_value = 0;
409           timer->n_samples = 0;
410         }
411     }
412 
413   profiler->last_sample = 0;
414 }
415 
416 void
gsk_profiler_push_samples(GskProfiler * profiler)417 gsk_profiler_push_samples (GskProfiler *profiler)
418 {
419   GHashTableIter iter;
420   gpointer value_p = NULL;
421   guint last_sample;
422 
423   g_return_if_fail (GSK_IS_PROFILER (profiler));
424 
425   g_hash_table_iter_init (&iter, profiler->timers);
426   while (g_hash_table_iter_next (&iter, NULL, &value_p))
427     {
428       NamedTimer *timer = value_p;
429       Sample *s;
430 
431       last_sample = profiler->last_sample;
432       profiler->last_sample += 1;
433       if (profiler->last_sample == MAX_SAMPLES)
434         profiler->last_sample = 0;
435 
436       s = &(profiler->timer_samples[last_sample]);
437       s->id = timer->id;
438 
439       if (timer->invert)
440         s->value = (gint64) (1000000.0 / (double) timer->value);
441       else
442         s->value = timer->value;
443     }
444 }
445 
446 void
gsk_profiler_append_counters(GskProfiler * profiler,GString * buffer)447 gsk_profiler_append_counters (GskProfiler *profiler,
448                               GString     *buffer)
449 {
450   GHashTableIter iter;
451   gpointer value_p = NULL;
452 
453   g_return_if_fail (GSK_IS_PROFILER (profiler));
454   g_return_if_fail (buffer != NULL);
455 
456   g_hash_table_iter_init (&iter, profiler->counters);
457   while (g_hash_table_iter_next (&iter, NULL, &value_p))
458     {
459       NamedCounter *counter = value_p;
460 
461       g_string_append_printf (buffer, "%s: %" G_GINT64_FORMAT "\n",
462                               counter->description,
463                               counter->value);
464     }
465 }
466 
467 void
gsk_profiler_append_timers(GskProfiler * profiler,GString * buffer)468 gsk_profiler_append_timers (GskProfiler *profiler,
469                             GString     *buffer)
470 {
471   GHashTableIter iter;
472   gpointer value_p = NULL;
473   int i;
474 
475   g_return_if_fail (GSK_IS_PROFILER (profiler));
476   g_return_if_fail (buffer != NULL);
477 
478   g_hash_table_iter_init (&iter, profiler->timers);
479   while (g_hash_table_iter_next (&iter, NULL, &value_p))
480     {
481       NamedTimer *timer = value_p;
482 
483       timer->min_value = G_MAXINT64;
484       timer->max_value = G_MININT64;
485       timer->avg_value = 0;
486       timer->n_samples = 0;
487     }
488 
489   for (i = 0; i < profiler->last_sample; i++)
490     {
491       Sample *s = &(profiler->timer_samples[i]);
492       NamedTimer *timer;
493 
494       if (s->id == 0)
495         continue;
496 
497       timer = gsk_profiler_get_timer (profiler, s->id);
498       timer->min_value = MIN (timer->min_value, s->value);
499       timer->max_value = MAX (timer->max_value, s->value);
500       timer->avg_value += s->value;
501       timer->n_samples += 1;
502     }
503 
504   g_hash_table_iter_init (&iter, profiler->timers);
505   while (g_hash_table_iter_next (&iter, NULL, &value_p))
506     {
507       NamedTimer *timer = value_p;
508       const char *unit = timer->invert ? "" : "usec";
509 
510       g_string_append_printf (buffer, "%s (%s): %.2f",
511                               timer->description,
512                               unit,
513                               (double) timer->value);
514 
515       if (timer->n_samples > 1)
516         {
517           timer->avg_value = timer->avg_value / timer->n_samples;
518           g_string_append_printf (buffer, " Min: %.2f Avg: %.2f Max: %.2f (%" G_GINT64_FORMAT " samples)",
519                                   (double) timer->min_value,
520                                   (double) timer->avg_value,
521                                   (double) timer->max_value,
522                                   timer->n_samples);
523         }
524 
525       g_string_append (buffer, "\n");
526     }
527 }
528