blob: 03348dff33b5a75dfc4c7e19d9c8f9eccdef9dd0 [file] [log] [blame]
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * profiler.c: vim script profiler
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17# if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO)
18/*
19 * Store the current time in "tm".
20 */
21 void
22profile_start(proftime_T *tm)
23{
24# ifdef MSWIN
25 QueryPerformanceCounter(tm);
26# else
27 gettimeofday(tm, NULL);
28# endif
29}
30
31/*
32 * Compute the elapsed time from "tm" till now and store in "tm".
33 */
34 void
35profile_end(proftime_T *tm)
36{
37 proftime_T now;
38
39# ifdef MSWIN
40 QueryPerformanceCounter(&now);
41 tm->QuadPart = now.QuadPart - tm->QuadPart;
42# else
43 gettimeofday(&now, NULL);
44 tm->tv_usec = now.tv_usec - tm->tv_usec;
45 tm->tv_sec = now.tv_sec - tm->tv_sec;
46 if (tm->tv_usec < 0)
47 {
48 tm->tv_usec += 1000000;
49 --tm->tv_sec;
50 }
51# endif
52}
53
54/*
55 * Subtract the time "tm2" from "tm".
56 */
57 void
58profile_sub(proftime_T *tm, proftime_T *tm2)
59{
60# ifdef MSWIN
61 tm->QuadPart -= tm2->QuadPart;
62# else
63 tm->tv_usec -= tm2->tv_usec;
64 tm->tv_sec -= tm2->tv_sec;
65 if (tm->tv_usec < 0)
66 {
67 tm->tv_usec += 1000000;
68 --tm->tv_sec;
69 }
70# endif
71}
72
73/*
74 * Return a string that represents the time in "tm".
75 * Uses a static buffer!
76 */
77 char *
78profile_msg(proftime_T *tm)
79{
80 static char buf[50];
81
82# ifdef MSWIN
83 LARGE_INTEGER fr;
84
85 QueryPerformanceFrequency(&fr);
86 sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart);
87# else
88 sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec);
89# endif
90 return buf;
91}
92
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +020093/*
94 * Return a float that represents the time in "tm".
95 */
96 float_T
97profile_float(proftime_T *tm)
98{
Bram Moolenaar73e28dc2022-09-17 21:08:33 +010099# ifdef MSWIN
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200100 LARGE_INTEGER fr;
101
102 QueryPerformanceFrequency(&fr);
103 return (float_T)tm->QuadPart / (float_T)fr.QuadPart;
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100104# else
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200105 return (float_T)tm->tv_sec + (float_T)tm->tv_usec / 1000000.0;
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200106# endif
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100107}
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200108
109/*
110 * Put the time "msec" past now in "tm".
111 */
112 void
113profile_setlimit(long msec, proftime_T *tm)
114{
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200115 if (msec <= 0) // no limit
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200116 profile_zero(tm);
117 else
118 {
119# ifdef MSWIN
120 LARGE_INTEGER fr;
121
122 QueryPerformanceCounter(tm);
123 QueryPerformanceFrequency(&fr);
124 tm->QuadPart += (LONGLONG)((double)msec / 1000.0 * (double)fr.QuadPart);
125# else
126 long usec;
127
128 gettimeofday(tm, NULL);
129 usec = (long)tm->tv_usec + (long)msec * 1000;
130 tm->tv_usec = usec % 1000000L;
131 tm->tv_sec += usec / 1000000L;
132# endif
133 }
134}
135
136/*
137 * Return TRUE if the current time is past "tm".
138 */
139 int
140profile_passed_limit(proftime_T *tm)
141{
142 proftime_T now;
143
144# ifdef MSWIN
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200145 if (tm->QuadPart == 0) // timer was not set
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200146 return FALSE;
147 QueryPerformanceCounter(&now);
148 return (now.QuadPart > tm->QuadPart);
149# else
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200150 if (tm->tv_sec == 0) // timer was not set
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200151 return FALSE;
152 gettimeofday(&now, NULL);
153 return (now.tv_sec > tm->tv_sec
154 || (now.tv_sec == tm->tv_sec && now.tv_usec > tm->tv_usec));
155# endif
156}
157
158/*
159 * Set the time in "tm" to zero.
160 */
161 void
162profile_zero(proftime_T *tm)
163{
164# ifdef MSWIN
165 tm->QuadPart = 0;
166# else
167 tm->tv_usec = 0;
168 tm->tv_sec = 0;
169# endif
170}
171
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200172# endif // FEAT_PROFILE || FEAT_RELTIME
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200173
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100174#if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_PROFILE)
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200175# if defined(HAVE_MATH_H)
176# include <math.h>
177# endif
178
179/*
180 * Divide the time "tm" by "count" and store in "tm2".
181 */
182 void
183profile_divide(proftime_T *tm, int count, proftime_T *tm2)
184{
185 if (count == 0)
186 profile_zero(tm2);
187 else
188 {
189# ifdef MSWIN
190 tm2->QuadPart = tm->QuadPart / count;
191# else
192 double usec = (tm->tv_sec * 1000000.0 + tm->tv_usec) / count;
193
194 tm2->tv_sec = floor(usec / 1000000.0);
195 tm2->tv_usec = vim_round(usec - (tm2->tv_sec * 1000000.0));
196# endif
197 }
198}
199#endif
200
201# if defined(FEAT_PROFILE) || defined(PROTO)
202/*
203 * Functions for profiling.
204 */
205static proftime_T prof_wait_time;
206
207/*
208 * Add the time "tm2" to "tm".
209 */
210 void
211profile_add(proftime_T *tm, proftime_T *tm2)
212{
213# ifdef MSWIN
214 tm->QuadPart += tm2->QuadPart;
215# else
216 tm->tv_usec += tm2->tv_usec;
217 tm->tv_sec += tm2->tv_sec;
218 if (tm->tv_usec >= 1000000)
219 {
220 tm->tv_usec -= 1000000;
221 ++tm->tv_sec;
222 }
223# endif
224}
225
226/*
227 * Add the "self" time from the total time and the children's time.
228 */
229 void
230profile_self(proftime_T *self, proftime_T *total, proftime_T *children)
231{
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200232 // Check that the result won't be negative. Can happen with recursive
233 // calls.
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200234#ifdef MSWIN
235 if (total->QuadPart <= children->QuadPart)
236 return;
237#else
238 if (total->tv_sec < children->tv_sec
239 || (total->tv_sec == children->tv_sec
240 && total->tv_usec <= children->tv_usec))
241 return;
242#endif
243 profile_add(self, total);
244 profile_sub(self, children);
245}
246
247/*
248 * Get the current waittime.
249 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200250 static void
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200251profile_get_wait(proftime_T *tm)
252{
253 *tm = prof_wait_time;
254}
255
256/*
257 * Subtract the passed waittime since "tm" from "tma".
258 */
259 void
260profile_sub_wait(proftime_T *tm, proftime_T *tma)
261{
262 proftime_T tm3 = prof_wait_time;
263
264 profile_sub(&tm3, tm);
265 profile_sub(tma, &tm3);
266}
267
268/*
269 * Return TRUE if "tm1" and "tm2" are equal.
270 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200271 static int
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200272profile_equal(proftime_T *tm1, proftime_T *tm2)
273{
274# ifdef MSWIN
275 return (tm1->QuadPart == tm2->QuadPart);
276# else
277 return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec);
278# endif
279}
280
281/*
282 * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2"
283 */
284 int
285profile_cmp(const proftime_T *tm1, const proftime_T *tm2)
286{
287# ifdef MSWIN
288 return (int)(tm2->QuadPart - tm1->QuadPart);
289# else
290 if (tm1->tv_sec == tm2->tv_sec)
291 return tm2->tv_usec - tm1->tv_usec;
292 return tm2->tv_sec - tm1->tv_sec;
293# endif
294}
295
296static char_u *profile_fname = NULL;
297static proftime_T pause_time;
298
299/*
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100300 * Reset all profiling information.
301 */
302 static void
303profile_reset(void)
304{
305 int id;
306 int todo;
307 hashtab_T *functbl;
308 hashitem_T *hi;
309
310 // Reset sourced files.
311 for (id = 1; id <= script_items.ga_len; id++)
312 {
313 scriptitem_T *si = SCRIPT_ITEM(id);
314 if (si->sn_prof_on)
315 {
316 si->sn_prof_on = FALSE;
317 si->sn_pr_force = FALSE;
318 profile_zero(&si->sn_pr_child);
319 si->sn_pr_nest = 0;
320 si->sn_pr_count = 0;
321 profile_zero(&si->sn_pr_total);
322 profile_zero(&si->sn_pr_self);
323 profile_zero(&si->sn_pr_start);
324 profile_zero(&si->sn_pr_children);
325 ga_clear(&si->sn_prl_ga);
326 profile_zero(&si->sn_prl_start);
327 profile_zero(&si->sn_prl_children);
328 profile_zero(&si->sn_prl_wait);
329 si->sn_prl_idx = -1;
330 si->sn_prl_execed = 0;
331 }
332 }
333
334 // Reset functions.
335 functbl = func_tbl_get();
336 todo = (int)functbl->ht_used;
337
338 for (hi = functbl->ht_array; todo > 0; ++hi)
339 {
340 ufunc_T *fp;
341 int i;
342
343 if (HASHITEM_EMPTY(hi))
344 continue;
345
346 todo--;
347 fp = HI2UF(hi);
348 if (fp->uf_prof_initialized)
349 {
350 fp->uf_profiling = 0;
351 fp->uf_prof_initialized = FALSE;
352 fp->uf_tm_count = 0;
353 profile_zero(&fp->uf_tm_total);
354 profile_zero(&fp->uf_tm_self);
355 profile_zero(&fp->uf_tm_children);
356
357 for (i = 0; i < fp->uf_lines.ga_len; i++)
358 {
359 fp->uf_tml_count[i] = 0;
360 profile_zero(&fp->uf_tml_total[i]);
361 profile_zero(&fp->uf_tml_self[i]);
362 }
363
364 profile_zero(&fp->uf_tml_start);
365 profile_zero(&fp->uf_tml_children);
366 profile_zero(&fp->uf_tml_wait);
367 fp->uf_tml_idx = -1;
368 fp->uf_tml_execed = 0;
369 }
370 }
371
372 VIM_CLEAR(profile_fname);
373}
374
375/*
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200376 * ":profile cmd args"
377 */
378 void
379ex_profile(exarg_T *eap)
380{
381 char_u *e;
382 int len;
383
384 e = skiptowhite(eap->arg);
385 len = (int)(e - eap->arg);
386 e = skipwhite(e);
387
388 if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL)
389 {
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100390 VIM_CLEAR(profile_fname);
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200391 profile_fname = expand_env_save_opt(e, TRUE);
392 do_profiling = PROF_YES;
393 profile_zero(&prof_wait_time);
394 set_vim_var_nr(VV_PROFILING, 1L);
395 }
396 else if (do_profiling == PROF_NONE)
Bram Moolenaar677658a2022-01-05 16:09:06 +0000397 emsg(_(e_first_use_profile_start_fname));
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100398 else if (STRCMP(eap->arg, "stop") == 0)
399 {
400 profile_dump();
401 do_profiling = PROF_NONE;
402 set_vim_var_nr(VV_PROFILING, 0L);
403 profile_reset();
404 }
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200405 else if (STRCMP(eap->arg, "pause") == 0)
406 {
407 if (do_profiling == PROF_YES)
408 profile_start(&pause_time);
409 do_profiling = PROF_PAUSED;
410 }
411 else if (STRCMP(eap->arg, "continue") == 0)
412 {
413 if (do_profiling == PROF_PAUSED)
414 {
415 profile_end(&pause_time);
416 profile_add(&prof_wait_time, &pause_time);
417 }
418 do_profiling = PROF_YES;
419 }
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100420 else if (STRCMP(eap->arg, "dump") == 0)
421 profile_dump();
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200422 else
423 {
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200424 // The rest is similar to ":breakadd".
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200425 ex_breakadd(eap);
426 }
427}
428
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200429// Command line expansion for :profile.
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200430static enum
431{
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200432 PEXP_SUBCMD, // expand :profile sub-commands
433 PEXP_FUNC // expand :profile func {funcname}
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200434} pexpand_what;
435
436static char *pexpand_cmds[] = {
437 "start",
438#define PROFCMD_START 0
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100439 "stop",
440#define PROFCMD_STOP 1
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200441 "pause",
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100442#define PROFCMD_PAUSE 2
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200443 "continue",
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100444#define PROFCMD_CONTINUE 3
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200445 "func",
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100446#define PROFCMD_FUNC 4
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200447 "file",
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100448#define PROFCMD_DUMP 5
449 "dump",
450#define PROFCMD_FILE 6
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200451 NULL
Yegappan Lakshmanan18ee0f62022-04-08 13:23:19 +0100452#define PROFCMD_LAST 7
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200453};
454
455/*
456 * Function given to ExpandGeneric() to obtain the profile command
457 * specific expansion.
458 */
459 char_u *
460get_profile_name(expand_T *xp UNUSED, int idx)
461{
462 switch (pexpand_what)
463 {
464 case PEXP_SUBCMD:
465 return (char_u *)pexpand_cmds[idx];
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200466 default:
467 return NULL;
468 }
469}
470
471/*
472 * Handle command line completion for :profile command.
473 */
474 void
475set_context_in_profile_cmd(expand_T *xp, char_u *arg)
476{
477 char_u *end_subcmd;
478
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200479 // Default: expand subcommands.
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200480 xp->xp_context = EXPAND_PROFILE;
481 pexpand_what = PEXP_SUBCMD;
482 xp->xp_pattern = arg;
483
484 end_subcmd = skiptowhite(arg);
485 if (*end_subcmd == NUL)
486 return;
487
Yegappan Lakshmanan1fdf84e2022-03-15 10:53:09 +0000488 if ((end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0)
489 || (end_subcmd - arg == 4 && STRNCMP(arg, "file", 4) == 0))
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200490 {
491 xp->xp_context = EXPAND_FILES;
492 xp->xp_pattern = skipwhite(end_subcmd);
493 return;
494 }
Yegappan Lakshmanan1fdf84e2022-03-15 10:53:09 +0000495 else if (end_subcmd - arg == 4 && STRNCMP(arg, "func", 4) == 0)
496 {
497 xp->xp_context = EXPAND_USER_FUNC;
498 xp->xp_pattern = skipwhite(end_subcmd);
499 return;
500 }
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200501
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200502 xp->xp_context = EXPAND_NOTHING;
503}
504
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200505static proftime_T inchar_time;
506
507/*
508 * Called when starting to wait for the user to type a character.
509 */
510 void
511prof_inchar_enter(void)
512{
513 profile_start(&inchar_time);
514}
515
516/*
517 * Called when finished waiting for the user to type a character.
518 */
519 void
520prof_inchar_exit(void)
521{
522 profile_end(&inchar_time);
523 profile_add(&prof_wait_time, &inchar_time);
524}
525
526
527/*
528 * Return TRUE when a function defined in the current script should be
529 * profiled.
530 */
531 int
532prof_def_func(void)
533{
534 if (current_sctx.sc_sid > 0)
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100535 return SCRIPT_ITEM(current_sctx.sc_sid)->sn_pr_force;
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200536 return FALSE;
537}
538
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200539/*
540 * Print the count and times for one function or function line.
541 */
542 static void
543prof_func_line(
544 FILE *fd,
545 int count,
546 proftime_T *total,
547 proftime_T *self,
548 int prefer_self) // when equal print only self time
549{
550 if (count > 0)
551 {
552 fprintf(fd, "%5d ", count);
553 if (prefer_self && profile_equal(total, self))
554 fprintf(fd, " ");
555 else
556 fprintf(fd, "%s ", profile_msg(total));
557 if (!prefer_self && profile_equal(total, self))
558 fprintf(fd, " ");
559 else
560 fprintf(fd, "%s ", profile_msg(self));
561 }
562 else
563 fprintf(fd, " ");
564}
565
566 static void
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200567prof_sort_list(
568 FILE *fd,
569 ufunc_T **sorttab,
570 int st_len,
571 char *title,
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200572 int prefer_self) // when equal print only self time
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200573{
574 int i;
575 ufunc_T *fp;
576
577 fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title);
578 fprintf(fd, "count total (s) self (s) function\n");
579 for (i = 0; i < 20 && i < st_len; ++i)
580 {
581 fp = sorttab[i];
582 prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self,
583 prefer_self);
584 if (fp->uf_name[0] == K_SPECIAL)
585 fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3);
586 else
587 fprintf(fd, " %s()\n", fp->uf_name);
588 }
589 fprintf(fd, "\n");
590}
591
592/*
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200593 * Compare function for total time sorting.
594 */
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200595 static int
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200596prof_total_cmp(const void *s1, const void *s2)
597{
598 ufunc_T *p1, *p2;
599
600 p1 = *(ufunc_T **)s1;
601 p2 = *(ufunc_T **)s2;
602 return profile_cmp(&p1->uf_tm_total, &p2->uf_tm_total);
603}
604
605/*
606 * Compare function for self time sorting.
607 */
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200608 static int
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200609prof_self_cmp(const void *s1, const void *s2)
610{
611 ufunc_T *p1, *p2;
612
613 p1 = *(ufunc_T **)s1;
614 p2 = *(ufunc_T **)s2;
615 return profile_cmp(&p1->uf_tm_self, &p2->uf_tm_self);
616}
617
618/*
619 * Start profiling function "fp".
620 */
621 void
622func_do_profile(ufunc_T *fp)
623{
624 int len = fp->uf_lines.ga_len;
625
626 if (!fp->uf_prof_initialized)
627 {
628 if (len == 0)
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200629 len = 1; // avoid getting error for allocating zero bytes
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200630 fp->uf_tm_count = 0;
631 profile_zero(&fp->uf_tm_self);
632 profile_zero(&fp->uf_tm_total);
633 if (fp->uf_tml_count == NULL)
634 fp->uf_tml_count = ALLOC_CLEAR_MULT(int, len);
635 if (fp->uf_tml_total == NULL)
636 fp->uf_tml_total = ALLOC_CLEAR_MULT(proftime_T, len);
637 if (fp->uf_tml_self == NULL)
638 fp->uf_tml_self = ALLOC_CLEAR_MULT(proftime_T, len);
639 fp->uf_tml_idx = -1;
640 if (fp->uf_tml_count == NULL || fp->uf_tml_total == NULL
641 || fp->uf_tml_self == NULL)
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200642 return; // out of memory
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200643 fp->uf_prof_initialized = TRUE;
644 }
645
646 fp->uf_profiling = TRUE;
647}
648
649/*
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +0200650 * Save time when starting to invoke another script or function.
651 */
652 static void
653script_prof_save(
654 proftime_T *tm) // place to store wait time
655{
656 scriptitem_T *si;
657
658 if (SCRIPT_ID_VALID(current_sctx.sc_sid))
659 {
660 si = SCRIPT_ITEM(current_sctx.sc_sid);
661 if (si->sn_prof_on && si->sn_pr_nest++ == 0)
662 profile_start(&si->sn_pr_child);
663 }
664 profile_get_wait(tm);
665}
666
667/*
Bram Moolenaarb2049902021-01-24 12:53:53 +0100668 * When calling a function: may initialize for profiling.
669 */
670 void
Bram Moolenaar12d26532021-02-19 19:13:21 +0100671profile_may_start_func(profinfo_T *info, ufunc_T *fp, ufunc_T *caller)
Bram Moolenaarb2049902021-01-24 12:53:53 +0100672{
Bram Moolenaar12d26532021-02-19 19:13:21 +0100673 if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL))
Bram Moolenaarb2049902021-01-24 12:53:53 +0100674 {
Bram Moolenaar12d26532021-02-19 19:13:21 +0100675 info->pi_started_profiling = TRUE;
676 func_do_profile(fp);
Bram Moolenaarb2049902021-01-24 12:53:53 +0100677 }
Bram Moolenaar12d26532021-02-19 19:13:21 +0100678 if (fp->uf_profiling || (caller != NULL && caller->uf_profiling))
679 {
680 ++fp->uf_tm_count;
681 profile_start(&info->pi_call_start);
682 profile_zero(&fp->uf_tm_children);
683 }
684 script_prof_save(&info->pi_wait_start);
Bram Moolenaarb2049902021-01-24 12:53:53 +0100685}
686
687/*
688 * After calling a function: may handle profiling. profile_may_start_func()
689 * must have been called previously.
690 */
691 void
Bram Moolenaar12d26532021-02-19 19:13:21 +0100692profile_may_end_func(profinfo_T *info, ufunc_T *fp, ufunc_T *caller)
Bram Moolenaarb2049902021-01-24 12:53:53 +0100693{
694 profile_end(&info->pi_call_start);
695 profile_sub_wait(&info->pi_wait_start, &info->pi_call_start);
696 profile_add(&fp->uf_tm_total, &info->pi_call_start);
697 profile_self(&fp->uf_tm_self, &info->pi_call_start, &fp->uf_tm_children);
Bram Moolenaar12d26532021-02-19 19:13:21 +0100698 if (caller != NULL && caller->uf_profiling)
Bram Moolenaarb2049902021-01-24 12:53:53 +0100699 {
Bram Moolenaar12d26532021-02-19 19:13:21 +0100700 profile_add(&caller->uf_tm_children, &info->pi_call_start);
701 profile_add(&caller->uf_tml_children, &info->pi_call_start);
Bram Moolenaarb2049902021-01-24 12:53:53 +0100702 }
703 if (info->pi_started_profiling)
704 // make a ":profdel func" stop profiling the function
705 fp->uf_profiling = FALSE;
706}
707
708/*
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200709 * Prepare profiling for entering a child or something else that is not
710 * counted for the script/function itself.
711 * Should always be called in pair with prof_child_exit().
712 */
713 void
714prof_child_enter(
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200715 proftime_T *tm) // place to store waittime
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200716{
717 funccall_T *fc = get_current_funccal();
718
Bram Moolenaarca16c602022-09-06 18:57:08 +0100719 if (fc != NULL && fc->fc_func->uf_profiling)
720 profile_start(&fc->fc_prof_child);
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200721 script_prof_save(tm);
722}
723
724/*
725 * Take care of time spent in a child.
726 * Should always be called after prof_child_enter().
727 */
728 void
729prof_child_exit(
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200730 proftime_T *tm) // where waittime was stored
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200731{
732 funccall_T *fc = get_current_funccal();
733
Bram Moolenaarca16c602022-09-06 18:57:08 +0100734 if (fc != NULL && fc->fc_func->uf_profiling)
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200735 {
Bram Moolenaarca16c602022-09-06 18:57:08 +0100736 profile_end(&fc->fc_prof_child);
737 profile_sub_wait(tm, &fc->fc_prof_child); // don't count waiting time
738 profile_add(&fc->fc_func->uf_tm_children, &fc->fc_prof_child);
739 profile_add(&fc->fc_func->uf_tml_children, &fc->fc_prof_child);
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200740 }
741 script_prof_restore(tm);
742}
743
744/*
745 * Called when starting to read a function line.
746 * "sourcing_lnum" must be correct!
747 * When skipping lines it may not actually be executed, but we won't find out
748 * until later and we need to store the time now.
749 */
750 void
Bram Moolenaarb2049902021-01-24 12:53:53 +0100751func_line_start(void *cookie, long lnum)
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200752{
753 funccall_T *fcp = (funccall_T *)cookie;
Bram Moolenaarca16c602022-09-06 18:57:08 +0100754 ufunc_T *fp = fcp->fc_func;
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200755
Bram Moolenaarb2049902021-01-24 12:53:53 +0100756 if (fp->uf_profiling && lnum >= 1 && lnum <= fp->uf_lines.ga_len)
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200757 {
Bram Moolenaarb2049902021-01-24 12:53:53 +0100758 fp->uf_tml_idx = lnum - 1;
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200759 // Skip continuation lines.
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200760 while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL)
761 --fp->uf_tml_idx;
762 fp->uf_tml_execed = FALSE;
763 profile_start(&fp->uf_tml_start);
764 profile_zero(&fp->uf_tml_children);
765 profile_get_wait(&fp->uf_tml_wait);
766 }
767}
768
769/*
770 * Called when actually executing a function line.
771 */
772 void
773func_line_exec(void *cookie)
774{
775 funccall_T *fcp = (funccall_T *)cookie;
Bram Moolenaarca16c602022-09-06 18:57:08 +0100776 ufunc_T *fp = fcp->fc_func;
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200777
778 if (fp->uf_profiling && fp->uf_tml_idx >= 0)
779 fp->uf_tml_execed = TRUE;
780}
781
782/*
783 * Called when done with a function line.
784 */
785 void
786func_line_end(void *cookie)
787{
788 funccall_T *fcp = (funccall_T *)cookie;
Bram Moolenaarca16c602022-09-06 18:57:08 +0100789 ufunc_T *fp = fcp->fc_func;
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +0200790
791 if (fp->uf_profiling && fp->uf_tml_idx >= 0)
792 {
793 if (fp->uf_tml_execed)
794 {
795 ++fp->uf_tml_count[fp->uf_tml_idx];
796 profile_end(&fp->uf_tml_start);
797 profile_sub_wait(&fp->uf_tml_wait, &fp->uf_tml_start);
798 profile_add(&fp->uf_tml_total[fp->uf_tml_idx], &fp->uf_tml_start);
799 profile_self(&fp->uf_tml_self[fp->uf_tml_idx], &fp->uf_tml_start,
800 &fp->uf_tml_children);
801 }
802 fp->uf_tml_idx = -1;
803 }
804}
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200805
806/*
807 * Dump the profiling results for all functions in file "fd".
808 */
809 static void
810func_dump_profile(FILE *fd)
811{
812 hashtab_T *functbl;
813 hashitem_T *hi;
814 int todo;
815 ufunc_T *fp;
816 int i;
817 ufunc_T **sorttab;
818 int st_len = 0;
819 char_u *p;
820
821 functbl = func_tbl_get();
822 todo = (int)functbl->ht_used;
823 if (todo == 0)
824 return; // nothing to dump
825
826 sorttab = ALLOC_MULT(ufunc_T *, todo);
827
828 for (hi = functbl->ht_array; todo > 0; ++hi)
829 {
830 if (!HASHITEM_EMPTY(hi))
831 {
832 --todo;
833 fp = HI2UF(hi);
834 if (fp->uf_prof_initialized)
835 {
836 if (sorttab != NULL)
837 sorttab[st_len++] = fp;
838
839 if (fp->uf_name[0] == K_SPECIAL)
840 fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3);
841 else
842 fprintf(fd, "FUNCTION %s()\n", fp->uf_name);
Bram Moolenaar16358802019-08-30 18:37:26 +0200843 if (fp->uf_script_ctx.sc_sid > 0)
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200844 {
Bram Moolenaar16358802019-08-30 18:37:26 +0200845 p = home_replace_save(NULL,
846 get_scriptname(fp->uf_script_ctx.sc_sid));
847 if (p != NULL)
848 {
Bram Moolenaar181d4f52019-09-18 22:04:56 +0200849 fprintf(fd, " Defined: %s:%ld\n",
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200850 p, (long)fp->uf_script_ctx.sc_lnum);
Bram Moolenaar16358802019-08-30 18:37:26 +0200851 vim_free(p);
852 }
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200853 }
854 if (fp->uf_tm_count == 1)
855 fprintf(fd, "Called 1 time\n");
856 else
857 fprintf(fd, "Called %d times\n", fp->uf_tm_count);
858 fprintf(fd, "Total time: %s\n", profile_msg(&fp->uf_tm_total));
859 fprintf(fd, " Self time: %s\n", profile_msg(&fp->uf_tm_self));
860 fprintf(fd, "\n");
861 fprintf(fd, "count total (s) self (s)\n");
862
863 for (i = 0; i < fp->uf_lines.ga_len; ++i)
864 {
865 if (FUNCLINE(fp, i) == NULL)
866 continue;
867 prof_func_line(fd, fp->uf_tml_count[i],
868 &fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE);
869 fprintf(fd, "%s\n", FUNCLINE(fp, i));
870 }
871 fprintf(fd, "\n");
872 }
873 }
874 }
875
876 if (sorttab != NULL && st_len > 0)
877 {
878 qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
879 prof_total_cmp);
880 prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE);
881 qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
882 prof_self_cmp);
883 prof_sort_list(fd, sorttab, st_len, "SELF", TRUE);
884 }
885
886 vim_free(sorttab);
887}
888
889/*
890 * Start profiling script "fp".
891 */
892 void
893script_do_profile(scriptitem_T *si)
894{
895 si->sn_pr_count = 0;
896 profile_zero(&si->sn_pr_total);
897 profile_zero(&si->sn_pr_self);
898
899 ga_init2(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
900 si->sn_prl_idx = -1;
901 si->sn_prof_on = TRUE;
902 si->sn_pr_nest = 0;
903}
904
905/*
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200906 * Count time spent in children after invoking another script or function.
907 */
908 void
909script_prof_restore(proftime_T *tm)
910{
911 scriptitem_T *si;
912
Bram Moolenaare3d46852020-08-29 13:39:17 +0200913 if (SCRIPT_ID_VALID(current_sctx.sc_sid))
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200914 {
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100915 si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200916 if (si->sn_prof_on && --si->sn_pr_nest == 0)
917 {
918 profile_end(&si->sn_pr_child);
919 profile_sub_wait(tm, &si->sn_pr_child); // don't count wait time
920 profile_add(&si->sn_pr_children, &si->sn_pr_child);
921 profile_add(&si->sn_prl_children, &si->sn_pr_child);
922 }
923 }
924}
925
926/*
927 * Dump the profiling results for all scripts in file "fd".
928 */
929 static void
930script_dump_profile(FILE *fd)
931{
932 int id;
933 scriptitem_T *si;
934 int i;
935 FILE *sfd;
936 sn_prl_T *pp;
937
938 for (id = 1; id <= script_items.ga_len; ++id)
939 {
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100940 si = SCRIPT_ITEM(id);
Bram Moolenaar660a10a2019-07-14 15:48:38 +0200941 if (si->sn_prof_on)
942 {
943 fprintf(fd, "SCRIPT %s\n", si->sn_name);
944 if (si->sn_pr_count == 1)
945 fprintf(fd, "Sourced 1 time\n");
946 else
947 fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
948 fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total));
949 fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self));
950 fprintf(fd, "\n");
951 fprintf(fd, "count total (s) self (s)\n");
952
953 sfd = mch_fopen((char *)si->sn_name, "r");
954 if (sfd == NULL)
955 fprintf(fd, "Cannot open file!\n");
956 else
957 {
958 // Keep going till the end of file, so that trailing
959 // continuation lines are listed.
960 for (i = 0; ; ++i)
961 {
962 if (vim_fgets(IObuff, IOSIZE, sfd))
963 break;
964 // When a line has been truncated, append NL, taking care
965 // of multi-byte characters .
966 if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL)
967 {
968 int n = IOSIZE - 2;
969
970 if (enc_utf8)
971 {
972 // Move to the first byte of this char.
973 // utf_head_off() doesn't work, because it checks
974 // for a truncated character.
975 while (n > 0 && (IObuff[n] & 0xc0) == 0x80)
976 --n;
977 }
978 else if (has_mbyte)
979 n -= mb_head_off(IObuff, IObuff + n);
980 IObuff[n] = NL;
981 IObuff[n + 1] = NUL;
982 }
983 if (i < si->sn_prl_ga.ga_len
984 && (pp = &PRL_ITEM(si, i))->snp_count > 0)
985 {
986 fprintf(fd, "%5d ", pp->snp_count);
987 if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self))
988 fprintf(fd, " ");
989 else
990 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total));
991 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self));
992 }
993 else
994 fprintf(fd, " ");
995 fprintf(fd, "%s", IObuff);
996 }
997 fclose(sfd);
998 }
999 fprintf(fd, "\n");
1000 }
1001 }
1002}
1003
1004/*
1005 * Dump the profiling info.
1006 */
1007 void
1008profile_dump(void)
1009{
1010 FILE *fd;
1011
1012 if (profile_fname != NULL)
1013 {
1014 fd = mch_fopen((char *)profile_fname, "w");
1015 if (fd == NULL)
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00001016 semsg(_(e_cant_open_file_str), profile_fname);
Bram Moolenaar660a10a2019-07-14 15:48:38 +02001017 else
1018 {
1019 script_dump_profile(fd);
1020 func_dump_profile(fd);
1021 fclose(fd);
1022 }
1023 }
1024}
1025
1026/*
1027 * Called when starting to read a script line.
1028 * "sourcing_lnum" must be correct!
1029 * When skipping lines it may not actually be executed, but we won't find out
1030 * until later and we need to store the time now.
1031 */
1032 void
1033script_line_start(void)
1034{
1035 scriptitem_T *si;
1036 sn_prl_T *pp;
1037
Bram Moolenaare3d46852020-08-29 13:39:17 +02001038 if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
Bram Moolenaar660a10a2019-07-14 15:48:38 +02001039 return;
Bram Moolenaar21b9e972020-01-26 19:26:46 +01001040 si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001041 if (si->sn_prof_on && SOURCING_LNUM >= 1)
Bram Moolenaar660a10a2019-07-14 15:48:38 +02001042 {
1043 // Grow the array before starting the timer, so that the time spent
1044 // here isn't counted.
1045 (void)ga_grow(&si->sn_prl_ga,
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001046 (int)(SOURCING_LNUM - si->sn_prl_ga.ga_len));
1047 si->sn_prl_idx = SOURCING_LNUM - 1;
Bram Moolenaar660a10a2019-07-14 15:48:38 +02001048 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
1049 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
1050 {
1051 // Zero counters for a line that was not used before.
1052 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
1053 pp->snp_count = 0;
1054 profile_zero(&pp->sn_prl_total);
1055 profile_zero(&pp->sn_prl_self);
1056 ++si->sn_prl_ga.ga_len;
1057 }
1058 si->sn_prl_execed = FALSE;
1059 profile_start(&si->sn_prl_start);
1060 profile_zero(&si->sn_prl_children);
1061 profile_get_wait(&si->sn_prl_wait);
1062 }
1063}
1064
1065/*
1066 * Called when actually executing a function line.
1067 */
1068 void
1069script_line_exec(void)
1070{
1071 scriptitem_T *si;
1072
Bram Moolenaare3d46852020-08-29 13:39:17 +02001073 if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
Bram Moolenaar660a10a2019-07-14 15:48:38 +02001074 return;
Bram Moolenaar21b9e972020-01-26 19:26:46 +01001075 si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar660a10a2019-07-14 15:48:38 +02001076 if (si->sn_prof_on && si->sn_prl_idx >= 0)
1077 si->sn_prl_execed = TRUE;
1078}
1079
1080/*
1081 * Called when done with a script line.
1082 */
1083 void
1084script_line_end(void)
1085{
1086 scriptitem_T *si;
1087 sn_prl_T *pp;
1088
Bram Moolenaare3d46852020-08-29 13:39:17 +02001089 if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
Bram Moolenaar660a10a2019-07-14 15:48:38 +02001090 return;
Bram Moolenaar21b9e972020-01-26 19:26:46 +01001091 si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar660a10a2019-07-14 15:48:38 +02001092 if (si->sn_prof_on && si->sn_prl_idx >= 0
1093 && si->sn_prl_idx < si->sn_prl_ga.ga_len)
1094 {
1095 if (si->sn_prl_execed)
1096 {
1097 pp = &PRL_ITEM(si, si->sn_prl_idx);
1098 ++pp->snp_count;
1099 profile_end(&si->sn_prl_start);
1100 profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
1101 profile_add(&pp->sn_prl_total, &si->sn_prl_start);
1102 profile_self(&pp->sn_prl_self, &si->sn_prl_start,
1103 &si->sn_prl_children);
1104 }
1105 si->sn_prl_idx = -1;
1106 }
1107}
1108# endif // FEAT_PROFILE
Bram Moolenaarfa55cfc2019-07-13 22:59:32 +02001109
1110#endif