xref: /vim-8.2.3635/src/profiler.c (revision 8ee52aff)
1fa55cfc6SBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2fa55cfc6SBram Moolenaar  *
3fa55cfc6SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4fa55cfc6SBram Moolenaar  *
5fa55cfc6SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6fa55cfc6SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7fa55cfc6SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8fa55cfc6SBram Moolenaar  */
9fa55cfc6SBram Moolenaar 
10fa55cfc6SBram Moolenaar /*
11fa55cfc6SBram Moolenaar  * profiler.c: vim script profiler
12fa55cfc6SBram Moolenaar  */
13fa55cfc6SBram Moolenaar 
14fa55cfc6SBram Moolenaar #include "vim.h"
15fa55cfc6SBram Moolenaar 
16fa55cfc6SBram Moolenaar #if defined(FEAT_EVAL) || defined(PROTO)
17fa55cfc6SBram Moolenaar # if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO)
18fa55cfc6SBram Moolenaar /*
19fa55cfc6SBram Moolenaar  * Store the current time in "tm".
20fa55cfc6SBram Moolenaar  */
21fa55cfc6SBram Moolenaar     void
profile_start(proftime_T * tm)22fa55cfc6SBram Moolenaar profile_start(proftime_T *tm)
23fa55cfc6SBram Moolenaar {
24fa55cfc6SBram Moolenaar # ifdef MSWIN
25fa55cfc6SBram Moolenaar     QueryPerformanceCounter(tm);
26fa55cfc6SBram Moolenaar # else
27fa55cfc6SBram Moolenaar     gettimeofday(tm, NULL);
28fa55cfc6SBram Moolenaar # endif
29fa55cfc6SBram Moolenaar }
30fa55cfc6SBram Moolenaar 
31fa55cfc6SBram Moolenaar /*
32fa55cfc6SBram Moolenaar  * Compute the elapsed time from "tm" till now and store in "tm".
33fa55cfc6SBram Moolenaar  */
34fa55cfc6SBram Moolenaar     void
profile_end(proftime_T * tm)35fa55cfc6SBram Moolenaar profile_end(proftime_T *tm)
36fa55cfc6SBram Moolenaar {
37fa55cfc6SBram Moolenaar     proftime_T now;
38fa55cfc6SBram Moolenaar 
39fa55cfc6SBram Moolenaar # ifdef MSWIN
40fa55cfc6SBram Moolenaar     QueryPerformanceCounter(&now);
41fa55cfc6SBram Moolenaar     tm->QuadPart = now.QuadPart - tm->QuadPart;
42fa55cfc6SBram Moolenaar # else
43fa55cfc6SBram Moolenaar     gettimeofday(&now, NULL);
44fa55cfc6SBram Moolenaar     tm->tv_usec = now.tv_usec - tm->tv_usec;
45fa55cfc6SBram Moolenaar     tm->tv_sec = now.tv_sec - tm->tv_sec;
46fa55cfc6SBram Moolenaar     if (tm->tv_usec < 0)
47fa55cfc6SBram Moolenaar     {
48fa55cfc6SBram Moolenaar 	tm->tv_usec += 1000000;
49fa55cfc6SBram Moolenaar 	--tm->tv_sec;
50fa55cfc6SBram Moolenaar     }
51fa55cfc6SBram Moolenaar # endif
52fa55cfc6SBram Moolenaar }
53fa55cfc6SBram Moolenaar 
54fa55cfc6SBram Moolenaar /*
55fa55cfc6SBram Moolenaar  * Subtract the time "tm2" from "tm".
56fa55cfc6SBram Moolenaar  */
57fa55cfc6SBram Moolenaar     void
profile_sub(proftime_T * tm,proftime_T * tm2)58fa55cfc6SBram Moolenaar profile_sub(proftime_T *tm, proftime_T *tm2)
59fa55cfc6SBram Moolenaar {
60fa55cfc6SBram Moolenaar # ifdef MSWIN
61fa55cfc6SBram Moolenaar     tm->QuadPart -= tm2->QuadPart;
62fa55cfc6SBram Moolenaar # else
63fa55cfc6SBram Moolenaar     tm->tv_usec -= tm2->tv_usec;
64fa55cfc6SBram Moolenaar     tm->tv_sec -= tm2->tv_sec;
65fa55cfc6SBram Moolenaar     if (tm->tv_usec < 0)
66fa55cfc6SBram Moolenaar     {
67fa55cfc6SBram Moolenaar 	tm->tv_usec += 1000000;
68fa55cfc6SBram Moolenaar 	--tm->tv_sec;
69fa55cfc6SBram Moolenaar     }
70fa55cfc6SBram Moolenaar # endif
71fa55cfc6SBram Moolenaar }
72fa55cfc6SBram Moolenaar 
73fa55cfc6SBram Moolenaar /*
74fa55cfc6SBram Moolenaar  * Return a string that represents the time in "tm".
75fa55cfc6SBram Moolenaar  * Uses a static buffer!
76fa55cfc6SBram Moolenaar  */
77fa55cfc6SBram Moolenaar     char *
profile_msg(proftime_T * tm)78fa55cfc6SBram Moolenaar profile_msg(proftime_T *tm)
79fa55cfc6SBram Moolenaar {
80fa55cfc6SBram Moolenaar     static char buf[50];
81fa55cfc6SBram Moolenaar 
82fa55cfc6SBram Moolenaar # ifdef MSWIN
83fa55cfc6SBram Moolenaar     LARGE_INTEGER   fr;
84fa55cfc6SBram Moolenaar 
85fa55cfc6SBram Moolenaar     QueryPerformanceFrequency(&fr);
86fa55cfc6SBram Moolenaar     sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart);
87fa55cfc6SBram Moolenaar # else
88fa55cfc6SBram Moolenaar     sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec);
89fa55cfc6SBram Moolenaar # endif
90fa55cfc6SBram Moolenaar     return buf;
91fa55cfc6SBram Moolenaar }
92fa55cfc6SBram Moolenaar 
93fa55cfc6SBram Moolenaar # if defined(FEAT_FLOAT) || defined(PROTO)
94fa55cfc6SBram Moolenaar /*
95fa55cfc6SBram Moolenaar  * Return a float that represents the time in "tm".
96fa55cfc6SBram Moolenaar  */
97fa55cfc6SBram Moolenaar     float_T
profile_float(proftime_T * tm)98fa55cfc6SBram Moolenaar profile_float(proftime_T *tm)
99fa55cfc6SBram Moolenaar {
100fa55cfc6SBram Moolenaar #  ifdef MSWIN
101fa55cfc6SBram Moolenaar     LARGE_INTEGER   fr;
102fa55cfc6SBram Moolenaar 
103fa55cfc6SBram Moolenaar     QueryPerformanceFrequency(&fr);
104fa55cfc6SBram Moolenaar     return (float_T)tm->QuadPart / (float_T)fr.QuadPart;
105fa55cfc6SBram Moolenaar #  else
106fa55cfc6SBram Moolenaar     return (float_T)tm->tv_sec + (float_T)tm->tv_usec / 1000000.0;
107fa55cfc6SBram Moolenaar #  endif
108fa55cfc6SBram Moolenaar }
109fa55cfc6SBram Moolenaar # endif
110fa55cfc6SBram Moolenaar 
111fa55cfc6SBram Moolenaar /*
112fa55cfc6SBram Moolenaar  * Put the time "msec" past now in "tm".
113fa55cfc6SBram Moolenaar  */
114fa55cfc6SBram Moolenaar     void
profile_setlimit(long msec,proftime_T * tm)115fa55cfc6SBram Moolenaar profile_setlimit(long msec, proftime_T *tm)
116fa55cfc6SBram Moolenaar {
117660a10adSBram Moolenaar     if (msec <= 0)   // no limit
118fa55cfc6SBram Moolenaar 	profile_zero(tm);
119fa55cfc6SBram Moolenaar     else
120fa55cfc6SBram Moolenaar     {
121fa55cfc6SBram Moolenaar # ifdef MSWIN
122fa55cfc6SBram Moolenaar 	LARGE_INTEGER   fr;
123fa55cfc6SBram Moolenaar 
124fa55cfc6SBram Moolenaar 	QueryPerformanceCounter(tm);
125fa55cfc6SBram Moolenaar 	QueryPerformanceFrequency(&fr);
126fa55cfc6SBram Moolenaar 	tm->QuadPart += (LONGLONG)((double)msec / 1000.0 * (double)fr.QuadPart);
127fa55cfc6SBram Moolenaar # else
128fa55cfc6SBram Moolenaar 	long	    usec;
129fa55cfc6SBram Moolenaar 
130fa55cfc6SBram Moolenaar 	gettimeofday(tm, NULL);
131fa55cfc6SBram Moolenaar 	usec = (long)tm->tv_usec + (long)msec * 1000;
132fa55cfc6SBram Moolenaar 	tm->tv_usec = usec % 1000000L;
133fa55cfc6SBram Moolenaar 	tm->tv_sec += usec / 1000000L;
134fa55cfc6SBram Moolenaar # endif
135fa55cfc6SBram Moolenaar     }
136fa55cfc6SBram Moolenaar }
137fa55cfc6SBram Moolenaar 
138fa55cfc6SBram Moolenaar /*
139fa55cfc6SBram Moolenaar  * Return TRUE if the current time is past "tm".
140fa55cfc6SBram Moolenaar  */
141fa55cfc6SBram Moolenaar     int
profile_passed_limit(proftime_T * tm)142fa55cfc6SBram Moolenaar profile_passed_limit(proftime_T *tm)
143fa55cfc6SBram Moolenaar {
144fa55cfc6SBram Moolenaar     proftime_T	now;
145fa55cfc6SBram Moolenaar 
146fa55cfc6SBram Moolenaar # ifdef MSWIN
147660a10adSBram Moolenaar     if (tm->QuadPart == 0)  // timer was not set
148fa55cfc6SBram Moolenaar 	return FALSE;
149fa55cfc6SBram Moolenaar     QueryPerformanceCounter(&now);
150fa55cfc6SBram Moolenaar     return (now.QuadPart > tm->QuadPart);
151fa55cfc6SBram Moolenaar # else
152660a10adSBram Moolenaar     if (tm->tv_sec == 0)    // timer was not set
153fa55cfc6SBram Moolenaar 	return FALSE;
154fa55cfc6SBram Moolenaar     gettimeofday(&now, NULL);
155fa55cfc6SBram Moolenaar     return (now.tv_sec > tm->tv_sec
156fa55cfc6SBram Moolenaar 	    || (now.tv_sec == tm->tv_sec && now.tv_usec > tm->tv_usec));
157fa55cfc6SBram Moolenaar # endif
158fa55cfc6SBram Moolenaar }
159fa55cfc6SBram Moolenaar 
160fa55cfc6SBram Moolenaar /*
161fa55cfc6SBram Moolenaar  * Set the time in "tm" to zero.
162fa55cfc6SBram Moolenaar  */
163fa55cfc6SBram Moolenaar     void
profile_zero(proftime_T * tm)164fa55cfc6SBram Moolenaar profile_zero(proftime_T *tm)
165fa55cfc6SBram Moolenaar {
166fa55cfc6SBram Moolenaar # ifdef MSWIN
167fa55cfc6SBram Moolenaar     tm->QuadPart = 0;
168fa55cfc6SBram Moolenaar # else
169fa55cfc6SBram Moolenaar     tm->tv_usec = 0;
170fa55cfc6SBram Moolenaar     tm->tv_sec = 0;
171fa55cfc6SBram Moolenaar # endif
172fa55cfc6SBram Moolenaar }
173fa55cfc6SBram Moolenaar 
174660a10adSBram Moolenaar # endif  // FEAT_PROFILE || FEAT_RELTIME
175fa55cfc6SBram Moolenaar 
176fa55cfc6SBram Moolenaar #if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_FLOAT) && defined(FEAT_PROFILE)
177fa55cfc6SBram Moolenaar # if defined(HAVE_MATH_H)
178fa55cfc6SBram Moolenaar #  include <math.h>
179fa55cfc6SBram Moolenaar # endif
180fa55cfc6SBram Moolenaar 
181fa55cfc6SBram Moolenaar /*
182fa55cfc6SBram Moolenaar  * Divide the time "tm" by "count" and store in "tm2".
183fa55cfc6SBram Moolenaar  */
184fa55cfc6SBram Moolenaar     void
profile_divide(proftime_T * tm,int count,proftime_T * tm2)185fa55cfc6SBram Moolenaar profile_divide(proftime_T *tm, int count, proftime_T *tm2)
186fa55cfc6SBram Moolenaar {
187fa55cfc6SBram Moolenaar     if (count == 0)
188fa55cfc6SBram Moolenaar 	profile_zero(tm2);
189fa55cfc6SBram Moolenaar     else
190fa55cfc6SBram Moolenaar     {
191fa55cfc6SBram Moolenaar # ifdef MSWIN
192fa55cfc6SBram Moolenaar 	tm2->QuadPart = tm->QuadPart / count;
193fa55cfc6SBram Moolenaar # else
194fa55cfc6SBram Moolenaar 	double usec = (tm->tv_sec * 1000000.0 + tm->tv_usec) / count;
195fa55cfc6SBram Moolenaar 
196fa55cfc6SBram Moolenaar 	tm2->tv_sec = floor(usec / 1000000.0);
197fa55cfc6SBram Moolenaar 	tm2->tv_usec = vim_round(usec - (tm2->tv_sec * 1000000.0));
198fa55cfc6SBram Moolenaar # endif
199fa55cfc6SBram Moolenaar     }
200fa55cfc6SBram Moolenaar }
201fa55cfc6SBram Moolenaar #endif
202fa55cfc6SBram Moolenaar 
203fa55cfc6SBram Moolenaar # if defined(FEAT_PROFILE) || defined(PROTO)
204fa55cfc6SBram Moolenaar /*
205fa55cfc6SBram Moolenaar  * Functions for profiling.
206fa55cfc6SBram Moolenaar  */
207fa55cfc6SBram Moolenaar static proftime_T prof_wait_time;
208fa55cfc6SBram Moolenaar 
209fa55cfc6SBram Moolenaar /*
210fa55cfc6SBram Moolenaar  * Add the time "tm2" to "tm".
211fa55cfc6SBram Moolenaar  */
212fa55cfc6SBram Moolenaar     void
profile_add(proftime_T * tm,proftime_T * tm2)213fa55cfc6SBram Moolenaar profile_add(proftime_T *tm, proftime_T *tm2)
214fa55cfc6SBram Moolenaar {
215fa55cfc6SBram Moolenaar # ifdef MSWIN
216fa55cfc6SBram Moolenaar     tm->QuadPart += tm2->QuadPart;
217fa55cfc6SBram Moolenaar # else
218fa55cfc6SBram Moolenaar     tm->tv_usec += tm2->tv_usec;
219fa55cfc6SBram Moolenaar     tm->tv_sec += tm2->tv_sec;
220fa55cfc6SBram Moolenaar     if (tm->tv_usec >= 1000000)
221fa55cfc6SBram Moolenaar     {
222fa55cfc6SBram Moolenaar 	tm->tv_usec -= 1000000;
223fa55cfc6SBram Moolenaar 	++tm->tv_sec;
224fa55cfc6SBram Moolenaar     }
225fa55cfc6SBram Moolenaar # endif
226fa55cfc6SBram Moolenaar }
227fa55cfc6SBram Moolenaar 
228fa55cfc6SBram Moolenaar /*
229fa55cfc6SBram Moolenaar  * Add the "self" time from the total time and the children's time.
230fa55cfc6SBram Moolenaar  */
231fa55cfc6SBram Moolenaar     void
profile_self(proftime_T * self,proftime_T * total,proftime_T * children)232fa55cfc6SBram Moolenaar profile_self(proftime_T *self, proftime_T *total, proftime_T *children)
233fa55cfc6SBram Moolenaar {
234660a10adSBram Moolenaar     // Check that the result won't be negative.  Can happen with recursive
235660a10adSBram Moolenaar     // calls.
236fa55cfc6SBram Moolenaar #ifdef MSWIN
237fa55cfc6SBram Moolenaar     if (total->QuadPart <= children->QuadPart)
238fa55cfc6SBram Moolenaar 	return;
239fa55cfc6SBram Moolenaar #else
240fa55cfc6SBram Moolenaar     if (total->tv_sec < children->tv_sec
241fa55cfc6SBram Moolenaar 	    || (total->tv_sec == children->tv_sec
242fa55cfc6SBram Moolenaar 		&& total->tv_usec <= children->tv_usec))
243fa55cfc6SBram Moolenaar 	return;
244fa55cfc6SBram Moolenaar #endif
245fa55cfc6SBram Moolenaar     profile_add(self, total);
246fa55cfc6SBram Moolenaar     profile_sub(self, children);
247fa55cfc6SBram Moolenaar }
248fa55cfc6SBram Moolenaar 
249fa55cfc6SBram Moolenaar /*
250fa55cfc6SBram Moolenaar  * Get the current waittime.
251fa55cfc6SBram Moolenaar  */
2525843f5f3SBram Moolenaar     static void
profile_get_wait(proftime_T * tm)253fa55cfc6SBram Moolenaar profile_get_wait(proftime_T *tm)
254fa55cfc6SBram Moolenaar {
255fa55cfc6SBram Moolenaar     *tm = prof_wait_time;
256fa55cfc6SBram Moolenaar }
257fa55cfc6SBram Moolenaar 
258fa55cfc6SBram Moolenaar /*
259fa55cfc6SBram Moolenaar  * Subtract the passed waittime since "tm" from "tma".
260fa55cfc6SBram Moolenaar  */
261fa55cfc6SBram Moolenaar     void
profile_sub_wait(proftime_T * tm,proftime_T * tma)262fa55cfc6SBram Moolenaar profile_sub_wait(proftime_T *tm, proftime_T *tma)
263fa55cfc6SBram Moolenaar {
264fa55cfc6SBram Moolenaar     proftime_T tm3 = prof_wait_time;
265fa55cfc6SBram Moolenaar 
266fa55cfc6SBram Moolenaar     profile_sub(&tm3, tm);
267fa55cfc6SBram Moolenaar     profile_sub(tma, &tm3);
268fa55cfc6SBram Moolenaar }
269fa55cfc6SBram Moolenaar 
270fa55cfc6SBram Moolenaar /*
271fa55cfc6SBram Moolenaar  * Return TRUE if "tm1" and "tm2" are equal.
272fa55cfc6SBram Moolenaar  */
2735843f5f3SBram Moolenaar     static int
profile_equal(proftime_T * tm1,proftime_T * tm2)274fa55cfc6SBram Moolenaar profile_equal(proftime_T *tm1, proftime_T *tm2)
275fa55cfc6SBram Moolenaar {
276fa55cfc6SBram Moolenaar # ifdef MSWIN
277fa55cfc6SBram Moolenaar     return (tm1->QuadPart == tm2->QuadPart);
278fa55cfc6SBram Moolenaar # else
279fa55cfc6SBram Moolenaar     return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec);
280fa55cfc6SBram Moolenaar # endif
281fa55cfc6SBram Moolenaar }
282fa55cfc6SBram Moolenaar 
283fa55cfc6SBram Moolenaar /*
284fa55cfc6SBram Moolenaar  * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2"
285fa55cfc6SBram Moolenaar  */
286fa55cfc6SBram Moolenaar     int
profile_cmp(const proftime_T * tm1,const proftime_T * tm2)287fa55cfc6SBram Moolenaar profile_cmp(const proftime_T *tm1, const proftime_T *tm2)
288fa55cfc6SBram Moolenaar {
289fa55cfc6SBram Moolenaar # ifdef MSWIN
290fa55cfc6SBram Moolenaar     return (int)(tm2->QuadPart - tm1->QuadPart);
291fa55cfc6SBram Moolenaar # else
292fa55cfc6SBram Moolenaar     if (tm1->tv_sec == tm2->tv_sec)
293fa55cfc6SBram Moolenaar 	return tm2->tv_usec - tm1->tv_usec;
294fa55cfc6SBram Moolenaar     return tm2->tv_sec - tm1->tv_sec;
295fa55cfc6SBram Moolenaar # endif
296fa55cfc6SBram Moolenaar }
297fa55cfc6SBram Moolenaar 
298fa55cfc6SBram Moolenaar static char_u	*profile_fname = NULL;
299fa55cfc6SBram Moolenaar static proftime_T pause_time;
300fa55cfc6SBram Moolenaar 
301fa55cfc6SBram Moolenaar /*
302fa55cfc6SBram Moolenaar  * ":profile cmd args"
303fa55cfc6SBram Moolenaar  */
304fa55cfc6SBram Moolenaar     void
ex_profile(exarg_T * eap)305fa55cfc6SBram Moolenaar ex_profile(exarg_T *eap)
306fa55cfc6SBram Moolenaar {
307fa55cfc6SBram Moolenaar     char_u	*e;
308fa55cfc6SBram Moolenaar     int		len;
309fa55cfc6SBram Moolenaar 
310fa55cfc6SBram Moolenaar     e = skiptowhite(eap->arg);
311fa55cfc6SBram Moolenaar     len = (int)(e - eap->arg);
312fa55cfc6SBram Moolenaar     e = skipwhite(e);
313fa55cfc6SBram Moolenaar 
314fa55cfc6SBram Moolenaar     if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL)
315fa55cfc6SBram Moolenaar     {
316fa55cfc6SBram Moolenaar 	vim_free(profile_fname);
317fa55cfc6SBram Moolenaar 	profile_fname = expand_env_save_opt(e, TRUE);
318fa55cfc6SBram Moolenaar 	do_profiling = PROF_YES;
319fa55cfc6SBram Moolenaar 	profile_zero(&prof_wait_time);
320fa55cfc6SBram Moolenaar 	set_vim_var_nr(VV_PROFILING, 1L);
321fa55cfc6SBram Moolenaar     }
322fa55cfc6SBram Moolenaar     else if (do_profiling == PROF_NONE)
323fa55cfc6SBram Moolenaar 	emsg(_("E750: First use \":profile start {fname}\""));
324fa55cfc6SBram Moolenaar     else if (STRCMP(eap->arg, "pause") == 0)
325fa55cfc6SBram Moolenaar     {
326fa55cfc6SBram Moolenaar 	if (do_profiling == PROF_YES)
327fa55cfc6SBram Moolenaar 	    profile_start(&pause_time);
328fa55cfc6SBram Moolenaar 	do_profiling = PROF_PAUSED;
329fa55cfc6SBram Moolenaar     }
330fa55cfc6SBram Moolenaar     else if (STRCMP(eap->arg, "continue") == 0)
331fa55cfc6SBram Moolenaar     {
332fa55cfc6SBram Moolenaar 	if (do_profiling == PROF_PAUSED)
333fa55cfc6SBram Moolenaar 	{
334fa55cfc6SBram Moolenaar 	    profile_end(&pause_time);
335fa55cfc6SBram Moolenaar 	    profile_add(&prof_wait_time, &pause_time);
336fa55cfc6SBram Moolenaar 	}
337fa55cfc6SBram Moolenaar 	do_profiling = PROF_YES;
338fa55cfc6SBram Moolenaar     }
339fa55cfc6SBram Moolenaar     else
340fa55cfc6SBram Moolenaar     {
341660a10adSBram Moolenaar 	// The rest is similar to ":breakadd".
342fa55cfc6SBram Moolenaar 	ex_breakadd(eap);
343fa55cfc6SBram Moolenaar     }
344fa55cfc6SBram Moolenaar }
345fa55cfc6SBram Moolenaar 
346660a10adSBram Moolenaar // Command line expansion for :profile.
347fa55cfc6SBram Moolenaar static enum
348fa55cfc6SBram Moolenaar {
349660a10adSBram Moolenaar     PEXP_SUBCMD,	// expand :profile sub-commands
350660a10adSBram Moolenaar     PEXP_FUNC		// expand :profile func {funcname}
351fa55cfc6SBram Moolenaar } pexpand_what;
352fa55cfc6SBram Moolenaar 
353fa55cfc6SBram Moolenaar static char *pexpand_cmds[] = {
354fa55cfc6SBram Moolenaar 			"start",
355fa55cfc6SBram Moolenaar #define PROFCMD_START	0
356fa55cfc6SBram Moolenaar 			"pause",
357fa55cfc6SBram Moolenaar #define PROFCMD_PAUSE	1
358fa55cfc6SBram Moolenaar 			"continue",
359fa55cfc6SBram Moolenaar #define PROFCMD_CONTINUE 2
360fa55cfc6SBram Moolenaar 			"func",
361fa55cfc6SBram Moolenaar #define PROFCMD_FUNC	3
362fa55cfc6SBram Moolenaar 			"file",
363fa55cfc6SBram Moolenaar #define PROFCMD_FILE	4
364fa55cfc6SBram Moolenaar 			NULL
365fa55cfc6SBram Moolenaar #define PROFCMD_LAST	5
366fa55cfc6SBram Moolenaar };
367fa55cfc6SBram Moolenaar 
368fa55cfc6SBram Moolenaar /*
369fa55cfc6SBram Moolenaar  * Function given to ExpandGeneric() to obtain the profile command
370fa55cfc6SBram Moolenaar  * specific expansion.
371fa55cfc6SBram Moolenaar  */
372fa55cfc6SBram Moolenaar     char_u *
get_profile_name(expand_T * xp UNUSED,int idx)373fa55cfc6SBram Moolenaar get_profile_name(expand_T *xp UNUSED, int idx)
374fa55cfc6SBram Moolenaar {
375fa55cfc6SBram Moolenaar     switch (pexpand_what)
376fa55cfc6SBram Moolenaar     {
377fa55cfc6SBram Moolenaar     case PEXP_SUBCMD:
378fa55cfc6SBram Moolenaar 	return (char_u *)pexpand_cmds[idx];
379660a10adSBram Moolenaar     // case PEXP_FUNC: TODO
380fa55cfc6SBram Moolenaar     default:
381fa55cfc6SBram Moolenaar 	return NULL;
382fa55cfc6SBram Moolenaar     }
383fa55cfc6SBram Moolenaar }
384fa55cfc6SBram Moolenaar 
385fa55cfc6SBram Moolenaar /*
386fa55cfc6SBram Moolenaar  * Handle command line completion for :profile command.
387fa55cfc6SBram Moolenaar  */
388fa55cfc6SBram Moolenaar     void
set_context_in_profile_cmd(expand_T * xp,char_u * arg)389fa55cfc6SBram Moolenaar set_context_in_profile_cmd(expand_T *xp, char_u *arg)
390fa55cfc6SBram Moolenaar {
391fa55cfc6SBram Moolenaar     char_u	*end_subcmd;
392fa55cfc6SBram Moolenaar 
393660a10adSBram Moolenaar     // Default: expand subcommands.
394fa55cfc6SBram Moolenaar     xp->xp_context = EXPAND_PROFILE;
395fa55cfc6SBram Moolenaar     pexpand_what = PEXP_SUBCMD;
396fa55cfc6SBram Moolenaar     xp->xp_pattern = arg;
397fa55cfc6SBram Moolenaar 
398fa55cfc6SBram Moolenaar     end_subcmd = skiptowhite(arg);
399fa55cfc6SBram Moolenaar     if (*end_subcmd == NUL)
400fa55cfc6SBram Moolenaar 	return;
401fa55cfc6SBram Moolenaar 
402fa55cfc6SBram Moolenaar     if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0)
403fa55cfc6SBram Moolenaar     {
404fa55cfc6SBram Moolenaar 	xp->xp_context = EXPAND_FILES;
405fa55cfc6SBram Moolenaar 	xp->xp_pattern = skipwhite(end_subcmd);
406fa55cfc6SBram Moolenaar 	return;
407fa55cfc6SBram Moolenaar     }
408fa55cfc6SBram Moolenaar 
409660a10adSBram Moolenaar     // TODO: expand function names after "func"
410fa55cfc6SBram Moolenaar     xp->xp_context = EXPAND_NOTHING;
411fa55cfc6SBram Moolenaar }
412fa55cfc6SBram Moolenaar 
413fa55cfc6SBram Moolenaar static proftime_T inchar_time;
414fa55cfc6SBram Moolenaar 
415fa55cfc6SBram Moolenaar /*
416fa55cfc6SBram Moolenaar  * Called when starting to wait for the user to type a character.
417fa55cfc6SBram Moolenaar  */
418fa55cfc6SBram Moolenaar     void
prof_inchar_enter(void)419fa55cfc6SBram Moolenaar prof_inchar_enter(void)
420fa55cfc6SBram Moolenaar {
421fa55cfc6SBram Moolenaar     profile_start(&inchar_time);
422fa55cfc6SBram Moolenaar }
423fa55cfc6SBram Moolenaar 
424fa55cfc6SBram Moolenaar /*
425fa55cfc6SBram Moolenaar  * Called when finished waiting for the user to type a character.
426fa55cfc6SBram Moolenaar  */
427fa55cfc6SBram Moolenaar     void
prof_inchar_exit(void)428fa55cfc6SBram Moolenaar prof_inchar_exit(void)
429fa55cfc6SBram Moolenaar {
430fa55cfc6SBram Moolenaar     profile_end(&inchar_time);
431fa55cfc6SBram Moolenaar     profile_add(&prof_wait_time, &inchar_time);
432fa55cfc6SBram Moolenaar }
433fa55cfc6SBram Moolenaar 
434fa55cfc6SBram Moolenaar 
435fa55cfc6SBram Moolenaar /*
436fa55cfc6SBram Moolenaar  * Return TRUE when a function defined in the current script should be
437fa55cfc6SBram Moolenaar  * profiled.
438fa55cfc6SBram Moolenaar  */
439fa55cfc6SBram Moolenaar     int
prof_def_func(void)440fa55cfc6SBram Moolenaar prof_def_func(void)
441fa55cfc6SBram Moolenaar {
442fa55cfc6SBram Moolenaar     if (current_sctx.sc_sid > 0)
44321b9e977SBram Moolenaar 	return SCRIPT_ITEM(current_sctx.sc_sid)->sn_pr_force;
444fa55cfc6SBram Moolenaar     return FALSE;
445fa55cfc6SBram Moolenaar }
446fa55cfc6SBram Moolenaar 
447660a10adSBram Moolenaar /*
448660a10adSBram Moolenaar  * Print the count and times for one function or function line.
449660a10adSBram Moolenaar  */
450660a10adSBram Moolenaar     static void
prof_func_line(FILE * fd,int count,proftime_T * total,proftime_T * self,int prefer_self)451660a10adSBram Moolenaar prof_func_line(
452660a10adSBram Moolenaar     FILE	*fd,
453660a10adSBram Moolenaar     int		count,
454660a10adSBram Moolenaar     proftime_T	*total,
455660a10adSBram Moolenaar     proftime_T	*self,
456660a10adSBram Moolenaar     int		prefer_self)	// when equal print only self time
457660a10adSBram Moolenaar {
458660a10adSBram Moolenaar     if (count > 0)
459660a10adSBram Moolenaar     {
460660a10adSBram Moolenaar 	fprintf(fd, "%5d ", count);
461660a10adSBram Moolenaar 	if (prefer_self && profile_equal(total, self))
462660a10adSBram Moolenaar 	    fprintf(fd, "           ");
463660a10adSBram Moolenaar 	else
464660a10adSBram Moolenaar 	    fprintf(fd, "%s ", profile_msg(total));
465660a10adSBram Moolenaar 	if (!prefer_self && profile_equal(total, self))
466660a10adSBram Moolenaar 	    fprintf(fd, "           ");
467660a10adSBram Moolenaar 	else
468660a10adSBram Moolenaar 	    fprintf(fd, "%s ", profile_msg(self));
469660a10adSBram Moolenaar     }
470660a10adSBram Moolenaar     else
471660a10adSBram Moolenaar 	fprintf(fd, "                            ");
472660a10adSBram Moolenaar }
473660a10adSBram Moolenaar 
474660a10adSBram Moolenaar     static void
prof_sort_list(FILE * fd,ufunc_T ** sorttab,int st_len,char * title,int prefer_self)475fa55cfc6SBram Moolenaar prof_sort_list(
476fa55cfc6SBram Moolenaar     FILE	*fd,
477fa55cfc6SBram Moolenaar     ufunc_T	**sorttab,
478fa55cfc6SBram Moolenaar     int		st_len,
479fa55cfc6SBram Moolenaar     char	*title,
480660a10adSBram Moolenaar     int		prefer_self)	// when equal print only self time
481fa55cfc6SBram Moolenaar {
482fa55cfc6SBram Moolenaar     int		i;
483fa55cfc6SBram Moolenaar     ufunc_T	*fp;
484fa55cfc6SBram Moolenaar 
485fa55cfc6SBram Moolenaar     fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title);
486fa55cfc6SBram Moolenaar     fprintf(fd, "count  total (s)   self (s)  function\n");
487fa55cfc6SBram Moolenaar     for (i = 0; i < 20 && i < st_len; ++i)
488fa55cfc6SBram Moolenaar     {
489fa55cfc6SBram Moolenaar 	fp = sorttab[i];
490fa55cfc6SBram Moolenaar 	prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self,
491fa55cfc6SBram Moolenaar 								 prefer_self);
492fa55cfc6SBram Moolenaar 	if (fp->uf_name[0] == K_SPECIAL)
493fa55cfc6SBram Moolenaar 	    fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3);
494fa55cfc6SBram Moolenaar 	else
495fa55cfc6SBram Moolenaar 	    fprintf(fd, " %s()\n", fp->uf_name);
496fa55cfc6SBram Moolenaar     }
497fa55cfc6SBram Moolenaar     fprintf(fd, "\n");
498fa55cfc6SBram Moolenaar }
499fa55cfc6SBram Moolenaar 
500fa55cfc6SBram Moolenaar /*
501fa55cfc6SBram Moolenaar  * Compare function for total time sorting.
502fa55cfc6SBram Moolenaar  */
503660a10adSBram Moolenaar     static int
prof_total_cmp(const void * s1,const void * s2)504fa55cfc6SBram Moolenaar prof_total_cmp(const void *s1, const void *s2)
505fa55cfc6SBram Moolenaar {
506fa55cfc6SBram Moolenaar     ufunc_T	*p1, *p2;
507fa55cfc6SBram Moolenaar 
508fa55cfc6SBram Moolenaar     p1 = *(ufunc_T **)s1;
509fa55cfc6SBram Moolenaar     p2 = *(ufunc_T **)s2;
510fa55cfc6SBram Moolenaar     return profile_cmp(&p1->uf_tm_total, &p2->uf_tm_total);
511fa55cfc6SBram Moolenaar }
512fa55cfc6SBram Moolenaar 
513fa55cfc6SBram Moolenaar /*
514fa55cfc6SBram Moolenaar  * Compare function for self time sorting.
515fa55cfc6SBram Moolenaar  */
516660a10adSBram Moolenaar     static int
prof_self_cmp(const void * s1,const void * s2)517fa55cfc6SBram Moolenaar prof_self_cmp(const void *s1, const void *s2)
518fa55cfc6SBram Moolenaar {
519fa55cfc6SBram Moolenaar     ufunc_T	*p1, *p2;
520fa55cfc6SBram Moolenaar 
521fa55cfc6SBram Moolenaar     p1 = *(ufunc_T **)s1;
522fa55cfc6SBram Moolenaar     p2 = *(ufunc_T **)s2;
523fa55cfc6SBram Moolenaar     return profile_cmp(&p1->uf_tm_self, &p2->uf_tm_self);
524fa55cfc6SBram Moolenaar }
525fa55cfc6SBram Moolenaar 
526fa55cfc6SBram Moolenaar /*
527fa55cfc6SBram Moolenaar  * Start profiling function "fp".
528fa55cfc6SBram Moolenaar  */
529fa55cfc6SBram Moolenaar     void
func_do_profile(ufunc_T * fp)530fa55cfc6SBram Moolenaar func_do_profile(ufunc_T *fp)
531fa55cfc6SBram Moolenaar {
532fa55cfc6SBram Moolenaar     int		len = fp->uf_lines.ga_len;
533fa55cfc6SBram Moolenaar 
534fa55cfc6SBram Moolenaar     if (!fp->uf_prof_initialized)
535fa55cfc6SBram Moolenaar     {
536fa55cfc6SBram Moolenaar 	if (len == 0)
537660a10adSBram Moolenaar 	    len = 1;  // avoid getting error for allocating zero bytes
538fa55cfc6SBram Moolenaar 	fp->uf_tm_count = 0;
539fa55cfc6SBram Moolenaar 	profile_zero(&fp->uf_tm_self);
540fa55cfc6SBram Moolenaar 	profile_zero(&fp->uf_tm_total);
541fa55cfc6SBram Moolenaar 	if (fp->uf_tml_count == NULL)
542fa55cfc6SBram Moolenaar 	    fp->uf_tml_count = ALLOC_CLEAR_MULT(int, len);
543fa55cfc6SBram Moolenaar 	if (fp->uf_tml_total == NULL)
544fa55cfc6SBram Moolenaar 	    fp->uf_tml_total = ALLOC_CLEAR_MULT(proftime_T, len);
545fa55cfc6SBram Moolenaar 	if (fp->uf_tml_self == NULL)
546fa55cfc6SBram Moolenaar 	    fp->uf_tml_self = ALLOC_CLEAR_MULT(proftime_T, len);
547fa55cfc6SBram Moolenaar 	fp->uf_tml_idx = -1;
548fa55cfc6SBram Moolenaar 	if (fp->uf_tml_count == NULL || fp->uf_tml_total == NULL
549fa55cfc6SBram Moolenaar 						    || fp->uf_tml_self == NULL)
550660a10adSBram Moolenaar 	    return;	    // out of memory
551fa55cfc6SBram Moolenaar 	fp->uf_prof_initialized = TRUE;
552fa55cfc6SBram Moolenaar     }
553fa55cfc6SBram Moolenaar 
554fa55cfc6SBram Moolenaar     fp->uf_profiling = TRUE;
555fa55cfc6SBram Moolenaar }
556fa55cfc6SBram Moolenaar 
557fa55cfc6SBram Moolenaar /*
558*8ee52affSYegappan Lakshmanan  * Save time when starting to invoke another script or function.
559*8ee52affSYegappan Lakshmanan  */
560*8ee52affSYegappan Lakshmanan     static void
script_prof_save(proftime_T * tm)561*8ee52affSYegappan Lakshmanan script_prof_save(
562*8ee52affSYegappan Lakshmanan     proftime_T	*tm)	    // place to store wait time
563*8ee52affSYegappan Lakshmanan {
564*8ee52affSYegappan Lakshmanan     scriptitem_T    *si;
565*8ee52affSYegappan Lakshmanan 
566*8ee52affSYegappan Lakshmanan     if (SCRIPT_ID_VALID(current_sctx.sc_sid))
567*8ee52affSYegappan Lakshmanan     {
568*8ee52affSYegappan Lakshmanan 	si = SCRIPT_ITEM(current_sctx.sc_sid);
569*8ee52affSYegappan Lakshmanan 	if (si->sn_prof_on && si->sn_pr_nest++ == 0)
570*8ee52affSYegappan Lakshmanan 	    profile_start(&si->sn_pr_child);
571*8ee52affSYegappan Lakshmanan     }
572*8ee52affSYegappan Lakshmanan     profile_get_wait(tm);
573*8ee52affSYegappan Lakshmanan }
574*8ee52affSYegappan Lakshmanan 
575*8ee52affSYegappan Lakshmanan /*
576b2049903SBram Moolenaar  * When calling a function: may initialize for profiling.
577b2049903SBram Moolenaar  */
578b2049903SBram Moolenaar     void
profile_may_start_func(profinfo_T * info,ufunc_T * fp,ufunc_T * caller)57912d26531SBram Moolenaar profile_may_start_func(profinfo_T *info, ufunc_T *fp, ufunc_T *caller)
580b2049903SBram Moolenaar {
581b2049903SBram Moolenaar     if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL))
582b2049903SBram Moolenaar     {
583b2049903SBram Moolenaar 	info->pi_started_profiling = TRUE;
584b2049903SBram Moolenaar 	func_do_profile(fp);
585b2049903SBram Moolenaar     }
58612d26531SBram Moolenaar     if (fp->uf_profiling || (caller != NULL && caller->uf_profiling))
587b2049903SBram Moolenaar     {
588b2049903SBram Moolenaar 	++fp->uf_tm_count;
589b2049903SBram Moolenaar 	profile_start(&info->pi_call_start);
590b2049903SBram Moolenaar 	profile_zero(&fp->uf_tm_children);
591b2049903SBram Moolenaar     }
592b2049903SBram Moolenaar     script_prof_save(&info->pi_wait_start);
593b2049903SBram Moolenaar }
594b2049903SBram Moolenaar 
595b2049903SBram Moolenaar /*
596b2049903SBram Moolenaar  * After calling a function: may handle profiling.  profile_may_start_func()
597b2049903SBram Moolenaar  * must have been called previously.
598b2049903SBram Moolenaar  */
599b2049903SBram Moolenaar     void
profile_may_end_func(profinfo_T * info,ufunc_T * fp,ufunc_T * caller)60012d26531SBram Moolenaar profile_may_end_func(profinfo_T *info, ufunc_T *fp, ufunc_T *caller)
601b2049903SBram Moolenaar {
602b2049903SBram Moolenaar     profile_end(&info->pi_call_start);
603b2049903SBram Moolenaar     profile_sub_wait(&info->pi_wait_start, &info->pi_call_start);
604b2049903SBram Moolenaar     profile_add(&fp->uf_tm_total, &info->pi_call_start);
605b2049903SBram Moolenaar     profile_self(&fp->uf_tm_self, &info->pi_call_start, &fp->uf_tm_children);
60612d26531SBram Moolenaar     if (caller != NULL && caller->uf_profiling)
607b2049903SBram Moolenaar     {
60812d26531SBram Moolenaar 	profile_add(&caller->uf_tm_children, &info->pi_call_start);
60912d26531SBram Moolenaar 	profile_add(&caller->uf_tml_children, &info->pi_call_start);
610b2049903SBram Moolenaar     }
611b2049903SBram Moolenaar     if (info->pi_started_profiling)
612b2049903SBram Moolenaar 	// make a ":profdel func" stop profiling the function
613b2049903SBram Moolenaar 	fp->uf_profiling = FALSE;
614b2049903SBram Moolenaar }
615b2049903SBram Moolenaar 
616b2049903SBram Moolenaar /*
617fa55cfc6SBram Moolenaar  * Prepare profiling for entering a child or something else that is not
618fa55cfc6SBram Moolenaar  * counted for the script/function itself.
619fa55cfc6SBram Moolenaar  * Should always be called in pair with prof_child_exit().
620fa55cfc6SBram Moolenaar  */
621fa55cfc6SBram Moolenaar     void
prof_child_enter(proftime_T * tm)622fa55cfc6SBram Moolenaar prof_child_enter(
623660a10adSBram Moolenaar     proftime_T *tm)	// place to store waittime
624fa55cfc6SBram Moolenaar {
625fa55cfc6SBram Moolenaar     funccall_T *fc = get_current_funccal();
626fa55cfc6SBram Moolenaar 
627fa55cfc6SBram Moolenaar     if (fc != NULL && fc->func->uf_profiling)
628fa55cfc6SBram Moolenaar 	profile_start(&fc->prof_child);
629fa55cfc6SBram Moolenaar     script_prof_save(tm);
630fa55cfc6SBram Moolenaar }
631fa55cfc6SBram Moolenaar 
632fa55cfc6SBram Moolenaar /*
633fa55cfc6SBram Moolenaar  * Take care of time spent in a child.
634fa55cfc6SBram Moolenaar  * Should always be called after prof_child_enter().
635fa55cfc6SBram Moolenaar  */
636fa55cfc6SBram Moolenaar     void
prof_child_exit(proftime_T * tm)637fa55cfc6SBram Moolenaar prof_child_exit(
638660a10adSBram Moolenaar     proftime_T *tm)	// where waittime was stored
639fa55cfc6SBram Moolenaar {
640fa55cfc6SBram Moolenaar     funccall_T *fc = get_current_funccal();
641fa55cfc6SBram Moolenaar 
642fa55cfc6SBram Moolenaar     if (fc != NULL && fc->func->uf_profiling)
643fa55cfc6SBram Moolenaar     {
644fa55cfc6SBram Moolenaar 	profile_end(&fc->prof_child);
645660a10adSBram Moolenaar 	profile_sub_wait(tm, &fc->prof_child); // don't count waiting time
646fa55cfc6SBram Moolenaar 	profile_add(&fc->func->uf_tm_children, &fc->prof_child);
647fa55cfc6SBram Moolenaar 	profile_add(&fc->func->uf_tml_children, &fc->prof_child);
648fa55cfc6SBram Moolenaar     }
649fa55cfc6SBram Moolenaar     script_prof_restore(tm);
650fa55cfc6SBram Moolenaar }
651fa55cfc6SBram Moolenaar 
652fa55cfc6SBram Moolenaar /*
653fa55cfc6SBram Moolenaar  * Called when starting to read a function line.
654fa55cfc6SBram Moolenaar  * "sourcing_lnum" must be correct!
655fa55cfc6SBram Moolenaar  * When skipping lines it may not actually be executed, but we won't find out
656fa55cfc6SBram Moolenaar  * until later and we need to store the time now.
657fa55cfc6SBram Moolenaar  */
658fa55cfc6SBram Moolenaar     void
func_line_start(void * cookie,long lnum)659b2049903SBram Moolenaar func_line_start(void *cookie, long lnum)
660fa55cfc6SBram Moolenaar {
661fa55cfc6SBram Moolenaar     funccall_T	*fcp = (funccall_T *)cookie;
662fa55cfc6SBram Moolenaar     ufunc_T	*fp = fcp->func;
663fa55cfc6SBram Moolenaar 
664b2049903SBram Moolenaar     if (fp->uf_profiling && lnum >= 1 && lnum <= fp->uf_lines.ga_len)
665fa55cfc6SBram Moolenaar     {
666b2049903SBram Moolenaar 	fp->uf_tml_idx = lnum - 1;
667660a10adSBram Moolenaar 	// Skip continuation lines.
668fa55cfc6SBram Moolenaar 	while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL)
669fa55cfc6SBram Moolenaar 	    --fp->uf_tml_idx;
670fa55cfc6SBram Moolenaar 	fp->uf_tml_execed = FALSE;
671fa55cfc6SBram Moolenaar 	profile_start(&fp->uf_tml_start);
672fa55cfc6SBram Moolenaar 	profile_zero(&fp->uf_tml_children);
673fa55cfc6SBram Moolenaar 	profile_get_wait(&fp->uf_tml_wait);
674fa55cfc6SBram Moolenaar     }
675fa55cfc6SBram Moolenaar }
676fa55cfc6SBram Moolenaar 
677fa55cfc6SBram Moolenaar /*
678fa55cfc6SBram Moolenaar  * Called when actually executing a function line.
679fa55cfc6SBram Moolenaar  */
680fa55cfc6SBram Moolenaar     void
func_line_exec(void * cookie)681fa55cfc6SBram Moolenaar func_line_exec(void *cookie)
682fa55cfc6SBram Moolenaar {
683fa55cfc6SBram Moolenaar     funccall_T	*fcp = (funccall_T *)cookie;
684fa55cfc6SBram Moolenaar     ufunc_T	*fp = fcp->func;
685fa55cfc6SBram Moolenaar 
686fa55cfc6SBram Moolenaar     if (fp->uf_profiling && fp->uf_tml_idx >= 0)
687fa55cfc6SBram Moolenaar 	fp->uf_tml_execed = TRUE;
688fa55cfc6SBram Moolenaar }
689fa55cfc6SBram Moolenaar 
690fa55cfc6SBram Moolenaar /*
691fa55cfc6SBram Moolenaar  * Called when done with a function line.
692fa55cfc6SBram Moolenaar  */
693fa55cfc6SBram Moolenaar     void
func_line_end(void * cookie)694fa55cfc6SBram Moolenaar func_line_end(void *cookie)
695fa55cfc6SBram Moolenaar {
696fa55cfc6SBram Moolenaar     funccall_T	*fcp = (funccall_T *)cookie;
697fa55cfc6SBram Moolenaar     ufunc_T	*fp = fcp->func;
698fa55cfc6SBram Moolenaar 
699fa55cfc6SBram Moolenaar     if (fp->uf_profiling && fp->uf_tml_idx >= 0)
700fa55cfc6SBram Moolenaar     {
701fa55cfc6SBram Moolenaar 	if (fp->uf_tml_execed)
702fa55cfc6SBram Moolenaar 	{
703fa55cfc6SBram Moolenaar 	    ++fp->uf_tml_count[fp->uf_tml_idx];
704fa55cfc6SBram Moolenaar 	    profile_end(&fp->uf_tml_start);
705fa55cfc6SBram Moolenaar 	    profile_sub_wait(&fp->uf_tml_wait, &fp->uf_tml_start);
706fa55cfc6SBram Moolenaar 	    profile_add(&fp->uf_tml_total[fp->uf_tml_idx], &fp->uf_tml_start);
707fa55cfc6SBram Moolenaar 	    profile_self(&fp->uf_tml_self[fp->uf_tml_idx], &fp->uf_tml_start,
708fa55cfc6SBram Moolenaar 							&fp->uf_tml_children);
709fa55cfc6SBram Moolenaar 	}
710fa55cfc6SBram Moolenaar 	fp->uf_tml_idx = -1;
711fa55cfc6SBram Moolenaar     }
712fa55cfc6SBram Moolenaar }
713660a10adSBram Moolenaar 
714660a10adSBram Moolenaar /*
715660a10adSBram Moolenaar  * Dump the profiling results for all functions in file "fd".
716660a10adSBram Moolenaar  */
717660a10adSBram Moolenaar     static void
func_dump_profile(FILE * fd)718660a10adSBram Moolenaar func_dump_profile(FILE *fd)
719660a10adSBram Moolenaar {
720660a10adSBram Moolenaar     hashtab_T	*functbl;
721660a10adSBram Moolenaar     hashitem_T	*hi;
722660a10adSBram Moolenaar     int		todo;
723660a10adSBram Moolenaar     ufunc_T	*fp;
724660a10adSBram Moolenaar     int		i;
725660a10adSBram Moolenaar     ufunc_T	**sorttab;
726660a10adSBram Moolenaar     int		st_len = 0;
727660a10adSBram Moolenaar     char_u	*p;
728660a10adSBram Moolenaar 
729660a10adSBram Moolenaar     functbl = func_tbl_get();
730660a10adSBram Moolenaar     todo = (int)functbl->ht_used;
731660a10adSBram Moolenaar     if (todo == 0)
732660a10adSBram Moolenaar 	return;     // nothing to dump
733660a10adSBram Moolenaar 
734660a10adSBram Moolenaar     sorttab = ALLOC_MULT(ufunc_T *, todo);
735660a10adSBram Moolenaar 
736660a10adSBram Moolenaar     for (hi = functbl->ht_array; todo > 0; ++hi)
737660a10adSBram Moolenaar     {
738660a10adSBram Moolenaar 	if (!HASHITEM_EMPTY(hi))
739660a10adSBram Moolenaar 	{
740660a10adSBram Moolenaar 	    --todo;
741660a10adSBram Moolenaar 	    fp = HI2UF(hi);
742660a10adSBram Moolenaar 	    if (fp->uf_prof_initialized)
743660a10adSBram Moolenaar 	    {
744660a10adSBram Moolenaar 		if (sorttab != NULL)
745660a10adSBram Moolenaar 		    sorttab[st_len++] = fp;
746660a10adSBram Moolenaar 
747660a10adSBram Moolenaar 		if (fp->uf_name[0] == K_SPECIAL)
748660a10adSBram Moolenaar 		    fprintf(fd, "FUNCTION  <SNR>%s()\n", fp->uf_name + 3);
749660a10adSBram Moolenaar 		else
750660a10adSBram Moolenaar 		    fprintf(fd, "FUNCTION  %s()\n", fp->uf_name);
75116358800SBram Moolenaar 		if (fp->uf_script_ctx.sc_sid > 0)
75216358800SBram Moolenaar 		{
753660a10adSBram Moolenaar 		    p = home_replace_save(NULL,
754660a10adSBram Moolenaar 				     get_scriptname(fp->uf_script_ctx.sc_sid));
755660a10adSBram Moolenaar 		    if (p != NULL)
756660a10adSBram Moolenaar 		    {
757181d4f58SBram Moolenaar 			fprintf(fd, "    Defined: %s:%ld\n",
758660a10adSBram Moolenaar 					   p, (long)fp->uf_script_ctx.sc_lnum);
759660a10adSBram Moolenaar 			vim_free(p);
760660a10adSBram Moolenaar 		    }
76116358800SBram Moolenaar 		}
762660a10adSBram Moolenaar 		if (fp->uf_tm_count == 1)
763660a10adSBram Moolenaar 		    fprintf(fd, "Called 1 time\n");
764660a10adSBram Moolenaar 		else
765660a10adSBram Moolenaar 		    fprintf(fd, "Called %d times\n", fp->uf_tm_count);
766660a10adSBram Moolenaar 		fprintf(fd, "Total time: %s\n", profile_msg(&fp->uf_tm_total));
767660a10adSBram Moolenaar 		fprintf(fd, " Self time: %s\n", profile_msg(&fp->uf_tm_self));
768660a10adSBram Moolenaar 		fprintf(fd, "\n");
769660a10adSBram Moolenaar 		fprintf(fd, "count  total (s)   self (s)\n");
770660a10adSBram Moolenaar 
771660a10adSBram Moolenaar 		for (i = 0; i < fp->uf_lines.ga_len; ++i)
772660a10adSBram Moolenaar 		{
773660a10adSBram Moolenaar 		    if (FUNCLINE(fp, i) == NULL)
774660a10adSBram Moolenaar 			continue;
775660a10adSBram Moolenaar 		    prof_func_line(fd, fp->uf_tml_count[i],
776660a10adSBram Moolenaar 			     &fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE);
777660a10adSBram Moolenaar 		    fprintf(fd, "%s\n", FUNCLINE(fp, i));
778660a10adSBram Moolenaar 		}
779660a10adSBram Moolenaar 		fprintf(fd, "\n");
780660a10adSBram Moolenaar 	    }
781660a10adSBram Moolenaar 	}
782660a10adSBram Moolenaar     }
783660a10adSBram Moolenaar 
784660a10adSBram Moolenaar     if (sorttab != NULL && st_len > 0)
785660a10adSBram Moolenaar     {
786660a10adSBram Moolenaar 	qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
787660a10adSBram Moolenaar 							      prof_total_cmp);
788660a10adSBram Moolenaar 	prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE);
789660a10adSBram Moolenaar 	qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
790660a10adSBram Moolenaar 							      prof_self_cmp);
791660a10adSBram Moolenaar 	prof_sort_list(fd, sorttab, st_len, "SELF", TRUE);
792660a10adSBram Moolenaar     }
793660a10adSBram Moolenaar 
794660a10adSBram Moolenaar     vim_free(sorttab);
795660a10adSBram Moolenaar }
796660a10adSBram Moolenaar 
797660a10adSBram Moolenaar /*
798660a10adSBram Moolenaar  * Start profiling script "fp".
799660a10adSBram Moolenaar  */
800660a10adSBram Moolenaar     void
script_do_profile(scriptitem_T * si)801660a10adSBram Moolenaar script_do_profile(scriptitem_T *si)
802660a10adSBram Moolenaar {
803660a10adSBram Moolenaar     si->sn_pr_count = 0;
804660a10adSBram Moolenaar     profile_zero(&si->sn_pr_total);
805660a10adSBram Moolenaar     profile_zero(&si->sn_pr_self);
806660a10adSBram Moolenaar 
807660a10adSBram Moolenaar     ga_init2(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
808660a10adSBram Moolenaar     si->sn_prl_idx = -1;
809660a10adSBram Moolenaar     si->sn_prof_on = TRUE;
810660a10adSBram Moolenaar     si->sn_pr_nest = 0;
811660a10adSBram Moolenaar }
812660a10adSBram Moolenaar 
813660a10adSBram Moolenaar /*
814660a10adSBram Moolenaar  * Count time spent in children after invoking another script or function.
815660a10adSBram Moolenaar  */
816660a10adSBram Moolenaar     void
script_prof_restore(proftime_T * tm)817660a10adSBram Moolenaar script_prof_restore(proftime_T *tm)
818660a10adSBram Moolenaar {
819660a10adSBram Moolenaar     scriptitem_T    *si;
820660a10adSBram Moolenaar 
821e3d4685fSBram Moolenaar     if (SCRIPT_ID_VALID(current_sctx.sc_sid))
822660a10adSBram Moolenaar     {
82321b9e977SBram Moolenaar 	si = SCRIPT_ITEM(current_sctx.sc_sid);
824660a10adSBram Moolenaar 	if (si->sn_prof_on && --si->sn_pr_nest == 0)
825660a10adSBram Moolenaar 	{
826660a10adSBram Moolenaar 	    profile_end(&si->sn_pr_child);
827660a10adSBram Moolenaar 	    profile_sub_wait(tm, &si->sn_pr_child); // don't count wait time
828660a10adSBram Moolenaar 	    profile_add(&si->sn_pr_children, &si->sn_pr_child);
829660a10adSBram Moolenaar 	    profile_add(&si->sn_prl_children, &si->sn_pr_child);
830660a10adSBram Moolenaar 	}
831660a10adSBram Moolenaar     }
832660a10adSBram Moolenaar }
833660a10adSBram Moolenaar 
834660a10adSBram Moolenaar /*
835660a10adSBram Moolenaar  * Dump the profiling results for all scripts in file "fd".
836660a10adSBram Moolenaar  */
837660a10adSBram Moolenaar     static void
script_dump_profile(FILE * fd)838660a10adSBram Moolenaar script_dump_profile(FILE *fd)
839660a10adSBram Moolenaar {
840660a10adSBram Moolenaar     int		    id;
841660a10adSBram Moolenaar     scriptitem_T    *si;
842660a10adSBram Moolenaar     int		    i;
843660a10adSBram Moolenaar     FILE	    *sfd;
844660a10adSBram Moolenaar     sn_prl_T	    *pp;
845660a10adSBram Moolenaar 
846660a10adSBram Moolenaar     for (id = 1; id <= script_items.ga_len; ++id)
847660a10adSBram Moolenaar     {
84821b9e977SBram Moolenaar 	si = SCRIPT_ITEM(id);
849660a10adSBram Moolenaar 	if (si->sn_prof_on)
850660a10adSBram Moolenaar 	{
851660a10adSBram Moolenaar 	    fprintf(fd, "SCRIPT  %s\n", si->sn_name);
852660a10adSBram Moolenaar 	    if (si->sn_pr_count == 1)
853660a10adSBram Moolenaar 		fprintf(fd, "Sourced 1 time\n");
854660a10adSBram Moolenaar 	    else
855660a10adSBram Moolenaar 		fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
856660a10adSBram Moolenaar 	    fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total));
857660a10adSBram Moolenaar 	    fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self));
858660a10adSBram Moolenaar 	    fprintf(fd, "\n");
859660a10adSBram Moolenaar 	    fprintf(fd, "count  total (s)   self (s)\n");
860660a10adSBram Moolenaar 
861660a10adSBram Moolenaar 	    sfd = mch_fopen((char *)si->sn_name, "r");
862660a10adSBram Moolenaar 	    if (sfd == NULL)
863660a10adSBram Moolenaar 		fprintf(fd, "Cannot open file!\n");
864660a10adSBram Moolenaar 	    else
865660a10adSBram Moolenaar 	    {
866660a10adSBram Moolenaar 		// Keep going till the end of file, so that trailing
867660a10adSBram Moolenaar 		// continuation lines are listed.
868660a10adSBram Moolenaar 		for (i = 0; ; ++i)
869660a10adSBram Moolenaar 		{
870660a10adSBram Moolenaar 		    if (vim_fgets(IObuff, IOSIZE, sfd))
871660a10adSBram Moolenaar 			break;
872660a10adSBram Moolenaar 		    // When a line has been truncated, append NL, taking care
873660a10adSBram Moolenaar 		    // of multi-byte characters .
874660a10adSBram Moolenaar 		    if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL)
875660a10adSBram Moolenaar 		    {
876660a10adSBram Moolenaar 			int n = IOSIZE - 2;
877660a10adSBram Moolenaar 
878660a10adSBram Moolenaar 			if (enc_utf8)
879660a10adSBram Moolenaar 			{
880660a10adSBram Moolenaar 			    // Move to the first byte of this char.
881660a10adSBram Moolenaar 			    // utf_head_off() doesn't work, because it checks
882660a10adSBram Moolenaar 			    // for a truncated character.
883660a10adSBram Moolenaar 			    while (n > 0 && (IObuff[n] & 0xc0) == 0x80)
884660a10adSBram Moolenaar 				--n;
885660a10adSBram Moolenaar 			}
886660a10adSBram Moolenaar 			else if (has_mbyte)
887660a10adSBram Moolenaar 			    n -= mb_head_off(IObuff, IObuff + n);
888660a10adSBram Moolenaar 			IObuff[n] = NL;
889660a10adSBram Moolenaar 			IObuff[n + 1] = NUL;
890660a10adSBram Moolenaar 		    }
891660a10adSBram Moolenaar 		    if (i < si->sn_prl_ga.ga_len
892660a10adSBram Moolenaar 				     && (pp = &PRL_ITEM(si, i))->snp_count > 0)
893660a10adSBram Moolenaar 		    {
894660a10adSBram Moolenaar 			fprintf(fd, "%5d ", pp->snp_count);
895660a10adSBram Moolenaar 			if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self))
896660a10adSBram Moolenaar 			    fprintf(fd, "           ");
897660a10adSBram Moolenaar 			else
898660a10adSBram Moolenaar 			    fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total));
899660a10adSBram Moolenaar 			fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self));
900660a10adSBram Moolenaar 		    }
901660a10adSBram Moolenaar 		    else
902660a10adSBram Moolenaar 			fprintf(fd, "                            ");
903660a10adSBram Moolenaar 		    fprintf(fd, "%s", IObuff);
904660a10adSBram Moolenaar 		}
905660a10adSBram Moolenaar 		fclose(sfd);
906660a10adSBram Moolenaar 	    }
907660a10adSBram Moolenaar 	    fprintf(fd, "\n");
908660a10adSBram Moolenaar 	}
909660a10adSBram Moolenaar     }
910660a10adSBram Moolenaar }
911660a10adSBram Moolenaar 
912660a10adSBram Moolenaar /*
913660a10adSBram Moolenaar  * Dump the profiling info.
914660a10adSBram Moolenaar  */
915660a10adSBram Moolenaar     void
profile_dump(void)916660a10adSBram Moolenaar profile_dump(void)
917660a10adSBram Moolenaar {
918660a10adSBram Moolenaar     FILE	*fd;
919660a10adSBram Moolenaar 
920660a10adSBram Moolenaar     if (profile_fname != NULL)
921660a10adSBram Moolenaar     {
922660a10adSBram Moolenaar 	fd = mch_fopen((char *)profile_fname, "w");
923660a10adSBram Moolenaar 	if (fd == NULL)
924660a10adSBram Moolenaar 	    semsg(_(e_notopen), profile_fname);
925660a10adSBram Moolenaar 	else
926660a10adSBram Moolenaar 	{
927660a10adSBram Moolenaar 	    script_dump_profile(fd);
928660a10adSBram Moolenaar 	    func_dump_profile(fd);
929660a10adSBram Moolenaar 	    fclose(fd);
930660a10adSBram Moolenaar 	}
931660a10adSBram Moolenaar     }
932660a10adSBram Moolenaar }
933660a10adSBram Moolenaar 
934660a10adSBram Moolenaar /*
935660a10adSBram Moolenaar  * Called when starting to read a script line.
936660a10adSBram Moolenaar  * "sourcing_lnum" must be correct!
937660a10adSBram Moolenaar  * When skipping lines it may not actually be executed, but we won't find out
938660a10adSBram Moolenaar  * until later and we need to store the time now.
939660a10adSBram Moolenaar  */
940660a10adSBram Moolenaar     void
script_line_start(void)941660a10adSBram Moolenaar script_line_start(void)
942660a10adSBram Moolenaar {
943660a10adSBram Moolenaar     scriptitem_T    *si;
944660a10adSBram Moolenaar     sn_prl_T	    *pp;
945660a10adSBram Moolenaar 
946e3d4685fSBram Moolenaar     if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
947660a10adSBram Moolenaar 	return;
94821b9e977SBram Moolenaar     si = SCRIPT_ITEM(current_sctx.sc_sid);
9491a47ae32SBram Moolenaar     if (si->sn_prof_on && SOURCING_LNUM >= 1)
950660a10adSBram Moolenaar     {
951660a10adSBram Moolenaar 	// Grow the array before starting the timer, so that the time spent
952660a10adSBram Moolenaar 	// here isn't counted.
953660a10adSBram Moolenaar 	(void)ga_grow(&si->sn_prl_ga,
9541a47ae32SBram Moolenaar 				  (int)(SOURCING_LNUM - si->sn_prl_ga.ga_len));
9551a47ae32SBram Moolenaar 	si->sn_prl_idx = SOURCING_LNUM - 1;
956660a10adSBram Moolenaar 	while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
957660a10adSBram Moolenaar 		&& si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
958660a10adSBram Moolenaar 	{
959660a10adSBram Moolenaar 	    // Zero counters for a line that was not used before.
960660a10adSBram Moolenaar 	    pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
961660a10adSBram Moolenaar 	    pp->snp_count = 0;
962660a10adSBram Moolenaar 	    profile_zero(&pp->sn_prl_total);
963660a10adSBram Moolenaar 	    profile_zero(&pp->sn_prl_self);
964660a10adSBram Moolenaar 	    ++si->sn_prl_ga.ga_len;
965660a10adSBram Moolenaar 	}
966660a10adSBram Moolenaar 	si->sn_prl_execed = FALSE;
967660a10adSBram Moolenaar 	profile_start(&si->sn_prl_start);
968660a10adSBram Moolenaar 	profile_zero(&si->sn_prl_children);
969660a10adSBram Moolenaar 	profile_get_wait(&si->sn_prl_wait);
970660a10adSBram Moolenaar     }
971660a10adSBram Moolenaar }
972660a10adSBram Moolenaar 
973660a10adSBram Moolenaar /*
974660a10adSBram Moolenaar  * Called when actually executing a function line.
975660a10adSBram Moolenaar  */
976660a10adSBram Moolenaar     void
script_line_exec(void)977660a10adSBram Moolenaar script_line_exec(void)
978660a10adSBram Moolenaar {
979660a10adSBram Moolenaar     scriptitem_T    *si;
980660a10adSBram Moolenaar 
981e3d4685fSBram Moolenaar     if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
982660a10adSBram Moolenaar 	return;
98321b9e977SBram Moolenaar     si = SCRIPT_ITEM(current_sctx.sc_sid);
984660a10adSBram Moolenaar     if (si->sn_prof_on && si->sn_prl_idx >= 0)
985660a10adSBram Moolenaar 	si->sn_prl_execed = TRUE;
986660a10adSBram Moolenaar }
987660a10adSBram Moolenaar 
988660a10adSBram Moolenaar /*
989660a10adSBram Moolenaar  * Called when done with a script line.
990660a10adSBram Moolenaar  */
991660a10adSBram Moolenaar     void
script_line_end(void)992660a10adSBram Moolenaar script_line_end(void)
993660a10adSBram Moolenaar {
994660a10adSBram Moolenaar     scriptitem_T    *si;
995660a10adSBram Moolenaar     sn_prl_T	    *pp;
996660a10adSBram Moolenaar 
997e3d4685fSBram Moolenaar     if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
998660a10adSBram Moolenaar 	return;
99921b9e977SBram Moolenaar     si = SCRIPT_ITEM(current_sctx.sc_sid);
1000660a10adSBram Moolenaar     if (si->sn_prof_on && si->sn_prl_idx >= 0
1001660a10adSBram Moolenaar 				     && si->sn_prl_idx < si->sn_prl_ga.ga_len)
1002660a10adSBram Moolenaar     {
1003660a10adSBram Moolenaar 	if (si->sn_prl_execed)
1004660a10adSBram Moolenaar 	{
1005660a10adSBram Moolenaar 	    pp = &PRL_ITEM(si, si->sn_prl_idx);
1006660a10adSBram Moolenaar 	    ++pp->snp_count;
1007660a10adSBram Moolenaar 	    profile_end(&si->sn_prl_start);
1008660a10adSBram Moolenaar 	    profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
1009660a10adSBram Moolenaar 	    profile_add(&pp->sn_prl_total, &si->sn_prl_start);
1010660a10adSBram Moolenaar 	    profile_self(&pp->sn_prl_self, &si->sn_prl_start,
1011660a10adSBram Moolenaar 							&si->sn_prl_children);
1012660a10adSBram Moolenaar 	}
1013660a10adSBram Moolenaar 	si->sn_prl_idx = -1;
1014660a10adSBram Moolenaar     }
1015660a10adSBram Moolenaar }
1016660a10adSBram Moolenaar # endif // FEAT_PROFILE
1017fa55cfc6SBram Moolenaar 
1018fa55cfc6SBram Moolenaar #endif
1019