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
36typedef struct pfunc *Pfunc;
37
38struct pfunc {
39    Pfunc next;
40    char *name;
41    long calls;
42    double time;
43    double self;
44    long num;
45};
46
47typedef struct sfunc *Sfunc;
48
49struct sfunc {
50    Pfunc p;
51    Sfunc prev;
52    double beg;
53};
54
55typedef struct parc *Parc;
56
57struct parc {
58    Parc next;
59    Pfunc from;
60    Pfunc to;
61    long calls;
62    double time;
63    double self;
64};
65
66static Pfunc calls;
67static int ncalls;
68static Parc arcs;
69static int narcs;
70static Sfunc stack;
71static Module zprof_module;
72
73static void
74freepfuncs(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
85static void
86freeparcs(Parc a)
87{
88    Parc n;
89
90    for (; a; a = n) {
91	n = a->next;
92	zfree(a, sizeof(*a));
93    }
94}
95
96static Pfunc
97findpfunc(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
108static Parc
109findparc(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
120static int
121cmpsfuncs(Pfunc *a, Pfunc *b)
122{
123    return ((*a)->self > (*b)->self ? -1 : ((*a)->self != (*b)->self));
124}
125
126static int
127cmptfuncs(Pfunc *a, Pfunc *b)
128{
129    return ((*a)->time > (*b)->time ? -1 : ((*a)->time != (*b)->time));
130}
131
132static int
133cmpparcs(Parc *a, Parc *b)
134{
135    return ((*a)->time > (*b)->time ? -1 : ((*a)->time != (*b)->time));
136}
137
138static int
139bin_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/**/
217static int
218zprof_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
290static struct builtin bintab[] = {
291    BUILTIN("zprof", 0, bin_zprof, 0, 0, 0, "c", NULL),
292};
293
294static struct funcwrap wrapper[] = {
295    WRAPDEF(zprof_wrapper),
296};
297
298static struct features module_features = {
299    bintab, sizeof(bintab)/sizeof(*bintab),
300    NULL, 0,
301    NULL, 0,
302    NULL, 0,
303    0
304};
305
306/**/
307int
308setup_(Module m)
309{
310    zprof_module = m;
311    return 0;
312}
313
314/**/
315int
316features_(Module m, char ***features)
317{
318    *features = featuresarray(m, &module_features);
319    return 0;
320}
321
322/**/
323int
324enables_(Module m, int **enables)
325{
326    return handlefeatures(m, &module_features, enables);
327}
328
329/**/
330int
331boot_(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/**/
342int
343cleanup_(Module m)
344{
345    freepfuncs(calls);
346    freeparcs(arcs);
347    deletewrapper(m, wrapper);
348    return setfeatureenables(m, &module_features, NULL);
349}
350
351/**/
352int
353finish_(UNUSED(Module m))
354{
355    return 0;
356}
357