1 /* This file is part of GEGL
2 *
3 * GEGL is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 3 of the License, or (at your option) any later version.
7 *
8 * GEGL is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15 *
16 * Copyright 2006 Øyvind Kolås
17 */
18
19 #include "config.h"
20 #include <glib.h>
21 #include <string.h>
22 #include "gegl-instrument.h"
23
24 long babl_ticks (void);
25
26 long
gegl_ticks(void)27 gegl_ticks (void)
28 {
29 return babl_ticks ();
30 }
31
32 typedef struct _Timing Timing;
33
34 struct _Timing
35 {
36 gchar *name;
37 long usecs;
38 Timing *parent;
39 Timing *children;
40 Timing *next;
41 };
42
43 gboolean gegl_instrument_enabled = FALSE;
44
45 static Timing *root = NULL;
46
47 static Timing *
iter_next(Timing * iter)48 iter_next (Timing *iter)
49 {
50 if (iter->children)
51 {
52 iter = iter->children;
53 }
54 else if (iter->next)
55 {
56 iter = iter->next;
57 }
58 else
59 {
60 while (iter && !iter->next)
61 iter = iter->parent;
62 if (iter && iter->next)
63 iter = iter->next;
64 else
65 return NULL;
66 }
67 return iter;
68 }
69
70 static gint
timing_depth(Timing * timing)71 timing_depth (Timing *timing)
72 {
73 Timing *iter = timing;
74 gint ret = 0;
75
76 while (iter &&
77 iter->parent)
78 {
79 ret++;
80 iter = iter->parent;
81 }
82
83 return ret;
84 }
85
86 static Timing *
timing_find(Timing * root,const gchar * name)87 timing_find (Timing *root,
88 const gchar *name)
89 {
90 Timing *iter = root;
91
92 if (!iter)
93 return NULL;
94
95 while (iter)
96 {
97 if (!strcmp (iter->name, name))
98 return iter;
99 if (timing_depth (iter_next (iter)) <= timing_depth (root))
100 return NULL;
101 iter = iter_next (iter);
102 }
103 return NULL;
104 }
105
106 void
gegl_instrument_enable(void)107 gegl_instrument_enable (void)
108 {
109 gegl_instrument_enabled = TRUE;
110 }
111
112 void
real_gegl_instrument(const gchar * parent_name,const gchar * name,long usecs)113 real_gegl_instrument (const gchar *parent_name,
114 const gchar *name,
115 long usecs)
116 {
117 Timing *iter;
118 Timing *parent;
119
120 if (root == NULL)
121 {
122 root = g_slice_new0 (Timing);
123 root->name = g_strdup (parent_name);
124 }
125 parent = timing_find (root, parent_name);
126 if (!parent)
127 {
128 real_gegl_instrument (root->name, parent_name, 0);
129 parent = timing_find (root, parent_name);
130 }
131 g_assert (parent);
132 iter = timing_find (parent, name);
133 if (!iter)
134 {
135 iter = g_slice_new0 (Timing);
136 iter->name = g_strdup (name);
137 iter->parent = parent;
138 iter->next = parent->children;
139 parent->children = iter;
140 }
141 iter->usecs += usecs;
142 }
143
144
145 static glong
timing_child_sum(Timing * timing)146 timing_child_sum (Timing *timing)
147 {
148 Timing *iter = timing->children;
149 glong sum = 0;
150
151 while (iter)
152 {
153 sum += iter->usecs;
154 iter = iter->next;
155 }
156
157 return sum;
158 }
159
160 static glong
timing_other(Timing * timing)161 timing_other (Timing *timing)
162 {
163 if (timing->children)
164 return timing->usecs - timing_child_sum (timing);
165 return 0;
166 }
167
seconds(glong usecs)168 static float seconds (glong usecs)
169 {
170 return usecs / 1000000.0;
171 }
172
normalized(glong usecs)173 static float normalized (glong usecs)
174 {
175 return 1.0 * usecs / root->usecs;
176 }
177
178 #include <string.h>
179
180 static GString *
tab_to(GString * string,gint position)181 tab_to (GString *string, gint position)
182 {
183 gchar *p;
184 gint curcount = 0;
185 gint i;
186
187 p = strrchr (string->str, '\n');
188 if (!p)
189 {
190 p = string->str;
191 curcount++;
192 }
193 while (p && *p != '\0')
194 {
195 curcount++;
196 p++;
197 }
198
199 if (curcount > position && position != 0)
200 {
201 g_warning ("%s tab overflow %i>%i", G_STRLOC, curcount, position);
202 }
203 else
204 {
205 for (i = 0; i <= position - curcount; i++)
206 string = g_string_append (string, " ");
207 }
208 return string;
209 }
210
211 static gchar *eight[] = {
212 " ",
213 "▏",
214 "▍",
215 "▌",
216 "▋",
217 "▊",
218 "▉",
219 "█"
220 };
221
222 static GString *
bar(GString * string,gint width,gfloat value)223 bar (GString *string,
224 gint width,
225 gfloat value)
226 {
227 gboolean utf8 = TRUE;
228 gint i;
229
230 if (value < 0)
231 return string;
232 if (utf8)
233 {
234 gint blocks = width * 8 * value;
235
236 for (i = 0; i < blocks / 8; i++)
237 {
238 string = g_string_append (string, "█");
239 }
240 string = g_string_append (string, eight[blocks % 8]);
241 }
242 else
243 {
244 for (i = 0; i < width; i++)
245 {
246 if (width * value > i)
247 string = g_string_append (string, "X");
248 }
249 }
250 return string;
251 }
252
253 #define INDENT_SPACES 2
254 #define SECONDS_COL 29
255 #define BAR_COL 36
256 #define BAR_WIDTH (78 - BAR_COL)
257
258 static void
sort_children(Timing * parent)259 sort_children (Timing *parent)
260 {
261 Timing *iter;
262 Timing *prev;
263 gboolean changed = FALSE;
264
265 do
266 {
267 iter = parent->children;
268 changed = FALSE;
269 prev = NULL;
270 while (iter && iter->next)
271 {
272 Timing *next = iter->next;
273
274 if (next->usecs > iter->usecs)
275 {
276 changed = TRUE;
277 if (prev)
278 {
279 prev->next = next;
280 iter->next = next->next;
281 next->next = iter;
282 }
283 else
284 {
285 iter->next = next->next;
286 next->next = iter;
287 parent->children = next;
288 }
289 }
290 prev = iter;
291 iter = iter->next;
292 }
293 } while (changed);
294
295
296 iter = parent->children;
297 while (iter && iter->next)
298 {
299 sort_children (iter);
300 iter = iter->next;
301 }
302 }
303
304 gchar *
gegl_instrument_utf8(void)305 gegl_instrument_utf8 (void)
306 {
307 GString *s = g_string_new ("");
308 gchar *ret;
309 Timing *iter = root;
310
311 sort_children (root);
312
313 while (iter)
314 {
315 gchar *buf;
316
317 if (!strcmp (iter->name, root->name))
318 {
319 buf = g_strdup_printf ("Total time: %.3fs\n", seconds (iter->usecs));
320 s = g_string_append (s, buf);
321 g_free (buf);
322 }
323
324 s = tab_to (s, timing_depth (iter) * INDENT_SPACES);
325 s = g_string_append (s, iter->name);
326
327 s = tab_to (s, SECONDS_COL);
328 buf = g_strdup_printf ("%5.1f%%", iter->parent ? 100.0 * iter->usecs / iter->parent->usecs : 100.0);
329 s = g_string_append (s, buf);
330 g_free (buf);
331 s = tab_to (s, BAR_COL);
332 s = bar (s, BAR_WIDTH, normalized (iter->usecs));
333 s = g_string_append (s, "\n");
334
335 if (timing_depth (iter_next (iter)) < timing_depth (iter))
336 {
337 if (timing_other (iter->parent) > 0)
338 {
339 s = tab_to (s, timing_depth (iter) * INDENT_SPACES);
340 s = g_string_append (s, "other");
341 s = tab_to (s, SECONDS_COL);
342 buf = g_strdup_printf ("%5.1f%%", 100 * normalized (timing_other (iter->parent)));
343 s = g_string_append (s, buf);
344 g_free (buf);
345 s = tab_to (s, BAR_COL);
346 s = bar (s, BAR_WIDTH, normalized (timing_other (iter->parent)));
347 s = g_string_append (s, "\n");
348 }
349 s = g_string_append (s, "\n");
350 }
351
352 iter = iter_next (iter);
353 }
354
355 ret = g_strdup (s->str);
356 g_string_free (s, TRUE);
357 return ret;
358 }
359