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