1#include "config.h"
2
3#include "gskprofilerprivate.h"
4
5#define MAX_SAMPLES 32
6
7typedef struct {
8 GQuark id;
9 char *description;
10 gint64 value;
11 gint64 n_samples;
12 gboolean can_reset : 1;
13} NamedCounter;
14
15typedef 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
29typedef struct {
30 GQuark id;
31 gint64 value;
32} Sample;
33
34struct _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
45G_DEFINE_TYPE (GskProfiler, gsk_profiler, G_TYPE_OBJECT)
46
47static void
48named_counter_free (gpointer data)
49{
50 NamedCounter *counter = data;
51
52 if (data == NULL)
53 return;
54
55 g_free (mem: counter->description);
56
57 g_slice_free (NamedCounter, counter);
58}
59
60static void
61named_timer_free (gpointer data)
62{
63 NamedTimer *timer = data;
64
65 if (data == NULL)
66 return;
67
68 g_free (mem: timer->description);
69
70 g_slice_free (NamedTimer, timer);
71}
72
73static void
74gsk_profiler_finalize (GObject *gobject)
75{
76 GskProfiler *self = GSK_PROFILER (ptr: 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
84static void
85gsk_profiler_class_init (GskProfilerClass *klass)
86{
87 G_OBJECT_CLASS (klass)->finalize = gsk_profiler_finalize;
88}
89
90static void
91gsk_profiler_init (GskProfiler *self)
92{
93 self->counters = g_hash_table_new_full (hash_func: g_direct_hash, key_equal_func: g_direct_equal,
94 NULL,
95 value_destroy_func: named_counter_free);
96 self->timers = g_hash_table_new_full (hash_func: g_direct_hash, key_equal_func: g_direct_equal,
97 NULL,
98 value_destroy_func: named_timer_free);
99}
100
101GskProfiler *
102gsk_profiler_new (void)
103{
104 return g_object_new (GSK_TYPE_PROFILER, NULL);
105}
106
107static NamedCounter *
108named_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 (str: description);
116 res->can_reset = can_reset;
117
118 return res;
119}
120
121static NamedCounter *
122gsk_profiler_get_counter (GskProfiler *profiler,
123 GQuark id)
124{
125 return g_hash_table_lookup (hash_table: profiler->counters, GINT_TO_POINTER (id));
126}
127
128GQuark
129gsk_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 (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 (hash_table: profiler->counters, GINT_TO_POINTER (id), value: counter);
149
150 return counter->id;
151}
152
153static NamedTimer *
154named_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 (str: description);
163 res->invert = invert;
164 res->can_reset = can_reset;
165
166 return res;
167}
168
169static NamedTimer *
170gsk_profiler_get_timer (GskProfiler *profiler,
171 GQuark id)
172{
173 return g_hash_table_lookup (hash_table: profiler->timers, GINT_TO_POINTER (id));
174}
175
176GQuark
177gsk_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 (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 (hash_table: profiler->timers, GINT_TO_POINTER (id), value: timer);
198
199 return timer->id;
200}
201
202void
203gsk_profiler_counter_inc (GskProfiler *profiler,
204 GQuark counter_id)
205{
206 gsk_profiler_counter_add (profiler, counter_id, increment: 1);
207}
208
209void
210gsk_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, id: 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
229void
230gsk_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, id: 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
249void
250gsk_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, id: 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
268gint64
269gsk_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, id: 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
300void
301gsk_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, id: 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
327gint64
328gsk_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, id: 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
346gint64
347gsk_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, id: 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
368gint64
369gsk_profiler_timer_get_start (GskProfiler *profiler,
370 GQuark timer_id)
371{
372 NamedTimer *timer;
373
374 timer = gsk_profiler_get_timer (profiler, id: timer_id);
375 if (timer == NULL)
376 return 0;
377
378 return timer->start_time;
379}
380
381void
382gsk_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: &iter, hash_table: profiler->counters);
390 while (g_hash_table_iter_next (iter: &iter, NULL, value: &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: &iter, hash_table: profiler->timers);
399 while (g_hash_table_iter_next (iter: &iter, NULL, value: &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
416void
417gsk_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: &iter, hash_table: profiler->timers);
426 while (g_hash_table_iter_next (iter: &iter, NULL, value: &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
446void
447gsk_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: &iter, hash_table: profiler->counters);
457 while (g_hash_table_iter_next (iter: &iter, NULL, value: &value_p))
458 {
459 NamedCounter *counter = value_p;
460
461 g_string_append_printf (string: buffer, format: "%s: %" G_GINT64_FORMAT "\n",
462 counter->description,
463 counter->value);
464 }
465}
466
467void
468gsk_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: &iter, hash_table: profiler->timers);
479 while (g_hash_table_iter_next (iter: &iter, NULL, value: &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, id: 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: &iter, hash_table: profiler->timers);
505 while (g_hash_table_iter_next (iter: &iter, NULL, value: &value_p))
506 {
507 NamedTimer *timer = value_p;
508 const char *unit = timer->invert ? "" : "usec";
509
510 g_string_append_printf (string: buffer, format: "%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 (string: buffer, format: " 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 (string: buffer, val: "\n");
526 }
527}
528

source code of gtk/gsk/gskprofiler.c