1 /*
2  * zprof.c - a shell function profiling module for zsh
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1996-1997 Sven Wischnowsky
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Sven Wischnowsky or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Sven Wischnowsky and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Sven Wischnowsky and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Sven Wischnowsky and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "zprof.mdh"
31 #include "zprof.pro"
32 
33 #include <sys/time.h>
34 #include <unistd.h>
35 
36 typedef struct pfunc *Pfunc;
37 
38 struct pfunc {
39     Pfunc next;
40     char *name;
41     long calls;
42     double time;
43     double self;
44     long num;
45 };
46 
47 typedef struct sfunc *Sfunc;
48 
49 struct sfunc {
50     Pfunc p;
51     Sfunc prev;
52     double beg;
53 };
54 
55 typedef struct parc *Parc;
56 
57 struct parc {
58     Parc next;
59     Pfunc from;
60     Pfunc to;
61     long calls;
62     double time;
63     double self;
64 };
65 
66 static Pfunc calls;
67 static int ncalls;
68 static Parc arcs;
69 static int narcs;
70 static Sfunc stack;
71 static Module zprof_module;
72 
73 static void
freepfuncs(Pfunc f)74 freepfuncs(Pfunc f)
75 {
76     Pfunc n;
77 
78     for (; f; f = n) {
79 	n = f->next;
80 	zsfree(f->name);
81 	zfree(f, sizeof(*f));
82     }
83 }
84 
85 static void
freeparcs(Parc a)86 freeparcs(Parc a)
87 {
88     Parc n;
89 
90     for (; a; a = n) {
91 	n = a->next;
92 	zfree(a, sizeof(*a));
93     }
94 }
95 
96 static Pfunc
findpfunc(char * name)97 findpfunc(char *name)
98 {
99     Pfunc f;
100 
101     for (f = calls; f; f = f->next)
102 	if (!strcmp(name, f->name))
103 	    return f;
104 
105     return NULL;
106 }
107 
108 static Parc
findparc(Pfunc f,Pfunc t)109 findparc(Pfunc f, Pfunc t)
110 {
111     Parc a;
112 
113     for (a = arcs; a; a = a->next)
114 	if (a->from == f && a->to == t)
115 	    return a;
116 
117     return NULL;
118 }
119 
120 static int
cmpsfuncs(Pfunc * a,Pfunc * b)121 cmpsfuncs(Pfunc *a, Pfunc *b)
122 {
123     return ((*a)->self > (*b)->self ? -1 : ((*a)->self != (*b)->self));
124 }
125 
126 static int
cmptfuncs(Pfunc * a,Pfunc * b)127 cmptfuncs(Pfunc *a, Pfunc *b)
128 {
129     return ((*a)->time > (*b)->time ? -1 : ((*a)->time != (*b)->time));
130 }
131 
132 static int
cmpparcs(Parc * a,Parc * b)133 cmpparcs(Parc *a, Parc *b)
134 {
135     return ((*a)->time > (*b)->time ? -1 : ((*a)->time != (*b)->time));
136 }
137 
138 static int
bin_zprof(UNUSED (char * nam),UNUSED (char ** args),Options ops,UNUSED (int func))139 bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func))
140 {
141     if (OPT_ISSET(ops,'c')) {
142 	freepfuncs(calls);
143 	calls = NULL;
144 	ncalls = 0;
145 	freeparcs(arcs);
146 	arcs = NULL;
147 	narcs = 0;
148     } else {
149 	VARARR(Pfunc, fs, (ncalls + 1));
150 	Pfunc f, *fp;
151 	VARARR(Parc, as, (narcs + 1));
152 	Parc a, *ap;
153 	long i;
154 	double total;
155 
156 	for (total = 0.0, f = calls, fp = fs; f; f = f->next, fp++) {
157 	    *fp = f;
158 	    total += f->self;
159 	}
160 	*fp = NULL;
161 	for (a = arcs, ap = as; a; a = a->next, ap++)
162 	    *ap = a;
163 	*ap = NULL;
164 
165 	qsort(fs, ncalls, sizeof(f),
166 	      (int (*) _((const void *, const void *))) cmpsfuncs);
167 	qsort(as, narcs, sizeof(a),
168 	      (int (*) _((const void *, const void *))) cmpparcs);
169 
170 	printf("num  calls                time                       self            name\n-----------------------------------------------------------------------------------\n");
171 	for (fp = fs, i = 1; *fp; fp++, i++) {
172 	    printf("%2ld) %4ld       %8.2f %8.2f  %6.2f%%  %8.2f %8.2f  %6.2f%%  %s\n",
173 		   ((*fp)->num = i),
174 		   (*fp)->calls,
175 		   (*fp)->time, (*fp)->time / ((double) (*fp)->calls),
176 		   ((*fp)->time / total) * 100.0,
177 		   (*fp)->self, (*fp)->self / ((double) (*fp)->calls),
178 		   ((*fp)->self / total) * 100.0,
179 		   (*fp)->name);
180 	}
181 	qsort(fs, ncalls, sizeof(f),
182 	      (int (*) _((const void *, const void *))) cmptfuncs);
183 
184 	for (fp = fs; *fp; fp++) {
185 	    printf("\n-----------------------------------------------------------------------------------\n\n");
186 	    for (ap = as; *ap; ap++)
187 		if ((*ap)->to == *fp) {
188 		    printf("    %4ld/%-4ld  %8.2f %8.2f  %6.2f%%  %8.2f %8.2f             %s [%ld]\n",
189 			   (*ap)->calls, (*fp)->calls,
190 			   (*ap)->time, (*ap)->time / ((double) (*ap)->calls),
191 			   ((*ap)->time / total) * 100.0,
192 			   (*ap)->self, (*ap)->self / ((double) (*ap)->calls),
193 			   (*ap)->from->name, (*ap)->from->num);
194 		}
195 	    printf("%2ld) %4ld       %8.2f %8.2f  %6.2f%%  %8.2f %8.2f  %6.2f%%  %s\n",
196 		   (*fp)->num, (*fp)->calls,
197 		   (*fp)->time, (*fp)->time / ((double) (*fp)->calls),
198 		   ((*fp)->time / total) * 100.0,
199 		   (*fp)->self, (*fp)->self / ((double) (*fp)->calls),
200 		   ((*fp)->self / total) * 100.0,
201 		   (*fp)->name);
202 	    for (ap = as + narcs - 1; ap >= as; ap--)
203 		if ((*ap)->from == *fp) {
204 		    printf("    %4ld/%-4ld  %8.2f %8.2f  %6.2f%%  %8.2f %8.2f             %s [%ld]\n",
205 			   (*ap)->calls, (*ap)->to->calls,
206 			   (*ap)->time, (*ap)->time / ((double) (*ap)->calls),
207 			   ((*ap)->time / total) * 100.0,
208 			   (*ap)->self, (*ap)->self / ((double) (*ap)->calls),
209 			   (*ap)->to->name, (*ap)->to->num);
210 		}
211 	}
212     }
213     return 0;
214 }
215 
216 /**/
217 static int
zprof_wrapper(Eprog prog,FuncWrap w,char * name)218 zprof_wrapper(Eprog prog, FuncWrap w, char *name)
219 {
220     int active = 0;
221     struct sfunc sf, *sp;
222     Pfunc f = NULL;
223     Parc a = NULL;
224     struct timeval tv;
225     struct timezone dummy;
226     double prev = 0, now;
227 
228     if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) {
229         active = 1;
230         if (!(f = findpfunc(name))) {
231             f = (Pfunc) zalloc(sizeof(*f));
232             f->name = ztrdup(name);
233             f->calls = 0;
234             f->time = f->self = 0.0;
235             f->next = calls;
236             calls = f;
237             ncalls++;
238         }
239         if (stack) {
240             if (!(a = findparc(stack->p, f))) {
241                 a = (Parc) zalloc(sizeof(*a));
242                 a->from = stack->p;
243                 a->to = f;
244                 a->calls = 0;
245                 a->time = a->self = 0.0;
246                 a->next = arcs;
247                 arcs = a;
248                 narcs++;
249             }
250         }
251         sf.prev = stack;
252         sf.p = f;
253         stack = &sf;
254 
255         f->calls++;
256         tv.tv_sec = tv.tv_usec = 0;
257         gettimeofday(&tv, &dummy);
258         sf.beg = prev = ((((double) tv.tv_sec) * 1000.0) +
259                          (((double) tv.tv_usec) / 1000.0));
260     }
261     runshfunc(prog, w, name);
262     if (active) {
263         if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) {
264             tv.tv_sec = tv.tv_usec = 0;
265             gettimeofday(&tv, &dummy);
266 
267             now = ((((double) tv.tv_sec) * 1000.0) +
268                    (((double) tv.tv_usec) / 1000.0));
269             f->self += now - sf.beg;
270             for (sp = sf.prev; sp && sp->p != f; sp = sp->prev);
271             if (!sp)
272                 f->time += now - prev;
273             if (a) {
274                 a->calls++;
275                 a->self += now - sf.beg;
276             }
277             stack = sf.prev;
278 
279             if (stack) {
280                 stack->beg += now - prev;
281                 if (a)
282                     a->time += now - prev;
283             }
284         } else
285             stack = sf.prev;
286     }
287     return 0;
288 }
289 
290 static struct builtin bintab[] = {
291     BUILTIN("zprof", 0, bin_zprof, 0, 0, 0, "c", NULL),
292 };
293 
294 static struct funcwrap wrapper[] = {
295     WRAPDEF(zprof_wrapper),
296 };
297 
298 static struct features module_features = {
299     bintab, sizeof(bintab)/sizeof(*bintab),
300     NULL, 0,
301     NULL, 0,
302     NULL, 0,
303     0
304 };
305 
306 /**/
307 int
setup_(Module m)308 setup_(Module m)
309 {
310     zprof_module = m;
311     return 0;
312 }
313 
314 /**/
315 int
features_(Module m,char *** features)316 features_(Module m, char ***features)
317 {
318     *features = featuresarray(m, &module_features);
319     return 0;
320 }
321 
322 /**/
323 int
enables_(Module m,int ** enables)324 enables_(Module m, int **enables)
325 {
326     return handlefeatures(m, &module_features, enables);
327 }
328 
329 /**/
330 int
boot_(Module m)331 boot_(Module m)
332 {
333     calls = NULL;
334     ncalls = 0;
335     arcs = NULL;
336     narcs = 0;
337     stack = NULL;
338     return addwrapper(m, wrapper);
339 }
340 
341 /**/
342 int
cleanup_(Module m)343 cleanup_(Module m)
344 {
345     freepfuncs(calls);
346     freeparcs(arcs);
347     deletewrapper(m, wrapper);
348     return setfeatureenables(m, &module_features, NULL);
349 }
350 
351 /**/
352 int
finish_(UNUSED (Module m))353 finish_(UNUSED(Module m))
354 {
355     return 0;
356 }
357