Mercurial > vim
comparison src/ex_cmds2.c @ 17370:ba06a1c42274 v8.1.1684
patch 8.1.1684: profiling functionality is spread out
commit https://github.com/vim/vim/commit/fa55cfc69d2b14761e2a8bd85bc1e0d82df770aa
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Jul 13 22:59:32 2019 +0200
patch 8.1.1684: profiling functionality is spread out
Problem: Profiling functionality is spread out.
Solution: Put profiling functionality in profiling.c. (Yegappan Lakshmanan,
closes #4666)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 13 Jul 2019 23:00:06 +0200 |
parents | 984eef966002 |
children | 8f44c630c366 |
comparison
equal
deleted
inserted
replaced
17369:4b97e0731720 | 17370:ba06a1c42274 |
---|---|
14 #include "vim.h" | 14 #include "vim.h" |
15 #include "version.h" | 15 #include "version.h" |
16 | 16 |
17 static void cmd_source(char_u *fname, exarg_T *eap); | 17 static void cmd_source(char_u *fname, exarg_T *eap); |
18 | 18 |
19 #ifdef FEAT_EVAL | |
20 /* Growarray to store info about already sourced scripts. | |
21 * For Unix also store the dev/ino, so that we don't have to stat() each | |
22 * script when going through the list. */ | |
23 typedef struct scriptitem_S | |
24 { | |
25 char_u *sn_name; | |
26 # ifdef UNIX | |
27 int sn_dev_valid; | |
28 dev_t sn_dev; | |
29 ino_t sn_ino; | |
30 # endif | |
31 # ifdef FEAT_PROFILE | |
32 int sn_prof_on; /* TRUE when script is/was profiled */ | |
33 int sn_pr_force; /* forceit: profile functions in this script */ | |
34 proftime_T sn_pr_child; /* time set when going into first child */ | |
35 int sn_pr_nest; /* nesting for sn_pr_child */ | |
36 /* profiling the script as a whole */ | |
37 int sn_pr_count; /* nr of times sourced */ | |
38 proftime_T sn_pr_total; /* time spent in script + children */ | |
39 proftime_T sn_pr_self; /* time spent in script itself */ | |
40 proftime_T sn_pr_start; /* time at script start */ | |
41 proftime_T sn_pr_children; /* time in children after script start */ | |
42 /* profiling the script per line */ | |
43 garray_T sn_prl_ga; /* things stored for every line */ | |
44 proftime_T sn_prl_start; /* start time for current line */ | |
45 proftime_T sn_prl_children; /* time spent in children for this line */ | |
46 proftime_T sn_prl_wait; /* wait start time for current line */ | |
47 int sn_prl_idx; /* index of line being timed; -1 if none */ | |
48 int sn_prl_execed; /* line being timed was executed */ | |
49 # endif | |
50 } scriptitem_T; | |
51 | |
52 static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; | |
53 #define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1]) | |
54 | |
55 # ifdef FEAT_PROFILE | |
56 /* Struct used in sn_prl_ga for every line of a script. */ | |
57 typedef struct sn_prl_S | |
58 { | |
59 int snp_count; /* nr of times line was executed */ | |
60 proftime_T sn_prl_total; /* time spent in a line + children */ | |
61 proftime_T sn_prl_self; /* time spent in a line itself */ | |
62 } sn_prl_T; | |
63 | |
64 # define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) | |
65 # endif | |
66 #endif | |
67 | |
68 #if defined(FEAT_EVAL) || defined(PROTO) | 19 #if defined(FEAT_EVAL) || defined(PROTO) |
69 # if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO) | |
70 /* | |
71 * Store the current time in "tm". | |
72 */ | |
73 void | |
74 profile_start(proftime_T *tm) | |
75 { | |
76 # ifdef MSWIN | |
77 QueryPerformanceCounter(tm); | |
78 # else | |
79 gettimeofday(tm, NULL); | |
80 # endif | |
81 } | |
82 | |
83 /* | |
84 * Compute the elapsed time from "tm" till now and store in "tm". | |
85 */ | |
86 void | |
87 profile_end(proftime_T *tm) | |
88 { | |
89 proftime_T now; | |
90 | |
91 # ifdef MSWIN | |
92 QueryPerformanceCounter(&now); | |
93 tm->QuadPart = now.QuadPart - tm->QuadPart; | |
94 # else | |
95 gettimeofday(&now, NULL); | |
96 tm->tv_usec = now.tv_usec - tm->tv_usec; | |
97 tm->tv_sec = now.tv_sec - tm->tv_sec; | |
98 if (tm->tv_usec < 0) | |
99 { | |
100 tm->tv_usec += 1000000; | |
101 --tm->tv_sec; | |
102 } | |
103 # endif | |
104 } | |
105 | |
106 /* | |
107 * Subtract the time "tm2" from "tm". | |
108 */ | |
109 void | |
110 profile_sub(proftime_T *tm, proftime_T *tm2) | |
111 { | |
112 # ifdef MSWIN | |
113 tm->QuadPart -= tm2->QuadPart; | |
114 # else | |
115 tm->tv_usec -= tm2->tv_usec; | |
116 tm->tv_sec -= tm2->tv_sec; | |
117 if (tm->tv_usec < 0) | |
118 { | |
119 tm->tv_usec += 1000000; | |
120 --tm->tv_sec; | |
121 } | |
122 # endif | |
123 } | |
124 | |
125 /* | |
126 * Return a string that represents the time in "tm". | |
127 * Uses a static buffer! | |
128 */ | |
129 char * | |
130 profile_msg(proftime_T *tm) | |
131 { | |
132 static char buf[50]; | |
133 | |
134 # ifdef MSWIN | |
135 LARGE_INTEGER fr; | |
136 | |
137 QueryPerformanceFrequency(&fr); | |
138 sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart); | |
139 # else | |
140 sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec); | |
141 # endif | |
142 return buf; | |
143 } | |
144 | |
145 # if defined(FEAT_FLOAT) || defined(PROTO) | |
146 /* | |
147 * Return a float that represents the time in "tm". | |
148 */ | |
149 float_T | |
150 profile_float(proftime_T *tm) | |
151 { | |
152 # ifdef MSWIN | |
153 LARGE_INTEGER fr; | |
154 | |
155 QueryPerformanceFrequency(&fr); | |
156 return (float_T)tm->QuadPart / (float_T)fr.QuadPart; | |
157 # else | |
158 return (float_T)tm->tv_sec + (float_T)tm->tv_usec / 1000000.0; | |
159 # endif | |
160 } | |
161 # endif | |
162 | |
163 /* | |
164 * Put the time "msec" past now in "tm". | |
165 */ | |
166 void | |
167 profile_setlimit(long msec, proftime_T *tm) | |
168 { | |
169 if (msec <= 0) /* no limit */ | |
170 profile_zero(tm); | |
171 else | |
172 { | |
173 # ifdef MSWIN | |
174 LARGE_INTEGER fr; | |
175 | |
176 QueryPerformanceCounter(tm); | |
177 QueryPerformanceFrequency(&fr); | |
178 tm->QuadPart += (LONGLONG)((double)msec / 1000.0 * (double)fr.QuadPart); | |
179 # else | |
180 long usec; | |
181 | |
182 gettimeofday(tm, NULL); | |
183 usec = (long)tm->tv_usec + (long)msec * 1000; | |
184 tm->tv_usec = usec % 1000000L; | |
185 tm->tv_sec += usec / 1000000L; | |
186 # endif | |
187 } | |
188 } | |
189 | |
190 /* | |
191 * Return TRUE if the current time is past "tm". | |
192 */ | |
193 int | |
194 profile_passed_limit(proftime_T *tm) | |
195 { | |
196 proftime_T now; | |
197 | |
198 # ifdef MSWIN | |
199 if (tm->QuadPart == 0) /* timer was not set */ | |
200 return FALSE; | |
201 QueryPerformanceCounter(&now); | |
202 return (now.QuadPart > tm->QuadPart); | |
203 # else | |
204 if (tm->tv_sec == 0) /* timer was not set */ | |
205 return FALSE; | |
206 gettimeofday(&now, NULL); | |
207 return (now.tv_sec > tm->tv_sec | |
208 || (now.tv_sec == tm->tv_sec && now.tv_usec > tm->tv_usec)); | |
209 # endif | |
210 } | |
211 | |
212 /* | |
213 * Set the time in "tm" to zero. | |
214 */ | |
215 void | |
216 profile_zero(proftime_T *tm) | |
217 { | |
218 # ifdef MSWIN | |
219 tm->QuadPart = 0; | |
220 # else | |
221 tm->tv_usec = 0; | |
222 tm->tv_sec = 0; | |
223 # endif | |
224 } | |
225 | |
226 # endif /* FEAT_PROFILE || FEAT_RELTIME */ | |
227 | |
228 # if defined(FEAT_TIMERS) || defined(PROTO) | 20 # if defined(FEAT_TIMERS) || defined(PROTO) |
229 static timer_T *first_timer = NULL; | 21 static timer_T *first_timer = NULL; |
230 static long last_timer_id = 0; | 22 static long last_timer_id = 0; |
231 | 23 |
232 /* | 24 /* |
601 } | 393 } |
602 } | 394 } |
603 # endif | 395 # endif |
604 # endif | 396 # endif |
605 | 397 |
606 #if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_FLOAT) && defined(FEAT_PROFILE) | |
607 # if defined(HAVE_MATH_H) | |
608 # include <math.h> | |
609 # endif | |
610 | |
611 /* | |
612 * Divide the time "tm" by "count" and store in "tm2". | |
613 */ | |
614 void | |
615 profile_divide(proftime_T *tm, int count, proftime_T *tm2) | |
616 { | |
617 if (count == 0) | |
618 profile_zero(tm2); | |
619 else | |
620 { | |
621 # ifdef MSWIN | |
622 tm2->QuadPart = tm->QuadPart / count; | |
623 # else | |
624 double usec = (tm->tv_sec * 1000000.0 + tm->tv_usec) / count; | |
625 | |
626 tm2->tv_sec = floor(usec / 1000000.0); | |
627 tm2->tv_usec = vim_round(usec - (tm2->tv_sec * 1000000.0)); | |
628 # endif | |
629 } | |
630 } | |
631 #endif | |
632 | |
633 # if defined(FEAT_PROFILE) || defined(PROTO) | 398 # if defined(FEAT_PROFILE) || defined(PROTO) |
634 /* | 399 /* |
635 * Functions for profiling. | |
636 */ | |
637 static void script_dump_profile(FILE *fd); | |
638 static proftime_T prof_wait_time; | |
639 | |
640 /* | |
641 * Add the time "tm2" to "tm". | |
642 */ | |
643 void | |
644 profile_add(proftime_T *tm, proftime_T *tm2) | |
645 { | |
646 # ifdef MSWIN | |
647 tm->QuadPart += tm2->QuadPart; | |
648 # else | |
649 tm->tv_usec += tm2->tv_usec; | |
650 tm->tv_sec += tm2->tv_sec; | |
651 if (tm->tv_usec >= 1000000) | |
652 { | |
653 tm->tv_usec -= 1000000; | |
654 ++tm->tv_sec; | |
655 } | |
656 # endif | |
657 } | |
658 | |
659 /* | |
660 * Add the "self" time from the total time and the children's time. | |
661 */ | |
662 void | |
663 profile_self(proftime_T *self, proftime_T *total, proftime_T *children) | |
664 { | |
665 /* Check that the result won't be negative. Can happen with recursive | |
666 * calls. */ | |
667 #ifdef MSWIN | |
668 if (total->QuadPart <= children->QuadPart) | |
669 return; | |
670 #else | |
671 if (total->tv_sec < children->tv_sec | |
672 || (total->tv_sec == children->tv_sec | |
673 && total->tv_usec <= children->tv_usec)) | |
674 return; | |
675 #endif | |
676 profile_add(self, total); | |
677 profile_sub(self, children); | |
678 } | |
679 | |
680 /* | |
681 * Get the current waittime. | |
682 */ | |
683 void | |
684 profile_get_wait(proftime_T *tm) | |
685 { | |
686 *tm = prof_wait_time; | |
687 } | |
688 | |
689 /* | |
690 * Subtract the passed waittime since "tm" from "tma". | |
691 */ | |
692 void | |
693 profile_sub_wait(proftime_T *tm, proftime_T *tma) | |
694 { | |
695 proftime_T tm3 = prof_wait_time; | |
696 | |
697 profile_sub(&tm3, tm); | |
698 profile_sub(tma, &tm3); | |
699 } | |
700 | |
701 /* | |
702 * Return TRUE if "tm1" and "tm2" are equal. | |
703 */ | |
704 int | |
705 profile_equal(proftime_T *tm1, proftime_T *tm2) | |
706 { | |
707 # ifdef MSWIN | |
708 return (tm1->QuadPart == tm2->QuadPart); | |
709 # else | |
710 return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec); | |
711 # endif | |
712 } | |
713 | |
714 /* | |
715 * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2" | |
716 */ | |
717 int | |
718 profile_cmp(const proftime_T *tm1, const proftime_T *tm2) | |
719 { | |
720 # ifdef MSWIN | |
721 return (int)(tm2->QuadPart - tm1->QuadPart); | |
722 # else | |
723 if (tm1->tv_sec == tm2->tv_sec) | |
724 return tm2->tv_usec - tm1->tv_usec; | |
725 return tm2->tv_sec - tm1->tv_sec; | |
726 # endif | |
727 } | |
728 | |
729 static char_u *profile_fname = NULL; | |
730 static proftime_T pause_time; | |
731 | |
732 /* | |
733 * ":profile cmd args" | |
734 */ | |
735 void | |
736 ex_profile(exarg_T *eap) | |
737 { | |
738 char_u *e; | |
739 int len; | |
740 | |
741 e = skiptowhite(eap->arg); | |
742 len = (int)(e - eap->arg); | |
743 e = skipwhite(e); | |
744 | |
745 if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) | |
746 { | |
747 vim_free(profile_fname); | |
748 profile_fname = expand_env_save_opt(e, TRUE); | |
749 do_profiling = PROF_YES; | |
750 profile_zero(&prof_wait_time); | |
751 set_vim_var_nr(VV_PROFILING, 1L); | |
752 } | |
753 else if (do_profiling == PROF_NONE) | |
754 emsg(_("E750: First use \":profile start {fname}\"")); | |
755 else if (STRCMP(eap->arg, "pause") == 0) | |
756 { | |
757 if (do_profiling == PROF_YES) | |
758 profile_start(&pause_time); | |
759 do_profiling = PROF_PAUSED; | |
760 } | |
761 else if (STRCMP(eap->arg, "continue") == 0) | |
762 { | |
763 if (do_profiling == PROF_PAUSED) | |
764 { | |
765 profile_end(&pause_time); | |
766 profile_add(&prof_wait_time, &pause_time); | |
767 } | |
768 do_profiling = PROF_YES; | |
769 } | |
770 else | |
771 { | |
772 /* The rest is similar to ":breakadd". */ | |
773 ex_breakadd(eap); | |
774 } | |
775 } | |
776 | |
777 /* Command line expansion for :profile. */ | |
778 static enum | |
779 { | |
780 PEXP_SUBCMD, /* expand :profile sub-commands */ | |
781 PEXP_FUNC /* expand :profile func {funcname} */ | |
782 } pexpand_what; | |
783 | |
784 static char *pexpand_cmds[] = { | |
785 "start", | |
786 #define PROFCMD_START 0 | |
787 "pause", | |
788 #define PROFCMD_PAUSE 1 | |
789 "continue", | |
790 #define PROFCMD_CONTINUE 2 | |
791 "func", | |
792 #define PROFCMD_FUNC 3 | |
793 "file", | |
794 #define PROFCMD_FILE 4 | |
795 NULL | |
796 #define PROFCMD_LAST 5 | |
797 }; | |
798 | |
799 /* | |
800 * Function given to ExpandGeneric() to obtain the profile command | |
801 * specific expansion. | |
802 */ | |
803 char_u * | |
804 get_profile_name(expand_T *xp UNUSED, int idx) | |
805 { | |
806 switch (pexpand_what) | |
807 { | |
808 case PEXP_SUBCMD: | |
809 return (char_u *)pexpand_cmds[idx]; | |
810 /* case PEXP_FUNC: TODO */ | |
811 default: | |
812 return NULL; | |
813 } | |
814 } | |
815 | |
816 /* | |
817 * Handle command line completion for :profile command. | |
818 */ | |
819 void | |
820 set_context_in_profile_cmd(expand_T *xp, char_u *arg) | |
821 { | |
822 char_u *end_subcmd; | |
823 | |
824 /* Default: expand subcommands. */ | |
825 xp->xp_context = EXPAND_PROFILE; | |
826 pexpand_what = PEXP_SUBCMD; | |
827 xp->xp_pattern = arg; | |
828 | |
829 end_subcmd = skiptowhite(arg); | |
830 if (*end_subcmd == NUL) | |
831 return; | |
832 | |
833 if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0) | |
834 { | |
835 xp->xp_context = EXPAND_FILES; | |
836 xp->xp_pattern = skipwhite(end_subcmd); | |
837 return; | |
838 } | |
839 | |
840 /* TODO: expand function names after "func" */ | |
841 xp->xp_context = EXPAND_NOTHING; | |
842 } | |
843 | |
844 /* | |
845 * Dump the profiling info. | |
846 */ | |
847 void | |
848 profile_dump(void) | |
849 { | |
850 FILE *fd; | |
851 | |
852 if (profile_fname != NULL) | |
853 { | |
854 fd = mch_fopen((char *)profile_fname, "w"); | |
855 if (fd == NULL) | |
856 semsg(_(e_notopen), profile_fname); | |
857 else | |
858 { | |
859 script_dump_profile(fd); | |
860 func_dump_profile(fd); | |
861 fclose(fd); | |
862 } | |
863 } | |
864 } | |
865 | |
866 /* | |
867 * Start profiling script "fp". | 400 * Start profiling script "fp". |
868 */ | 401 */ |
869 static void | 402 void |
870 script_do_profile(scriptitem_T *si) | 403 script_do_profile(scriptitem_T *si) |
871 { | 404 { |
872 si->sn_pr_count = 0; | 405 si->sn_pr_count = 0; |
873 profile_zero(&si->sn_pr_total); | 406 profile_zero(&si->sn_pr_total); |
874 profile_zero(&si->sn_pr_self); | 407 profile_zero(&si->sn_pr_self); |
916 profile_add(&si->sn_prl_children, &si->sn_pr_child); | 449 profile_add(&si->sn_prl_children, &si->sn_pr_child); |
917 } | 450 } |
918 } | 451 } |
919 } | 452 } |
920 | 453 |
921 static proftime_T inchar_time; | |
922 | |
923 /* | |
924 * Called when starting to wait for the user to type a character. | |
925 */ | |
926 void | |
927 prof_inchar_enter(void) | |
928 { | |
929 profile_start(&inchar_time); | |
930 } | |
931 | |
932 /* | |
933 * Called when finished waiting for the user to type a character. | |
934 */ | |
935 void | |
936 prof_inchar_exit(void) | |
937 { | |
938 profile_end(&inchar_time); | |
939 profile_add(&prof_wait_time, &inchar_time); | |
940 } | |
941 | |
942 /* | 454 /* |
943 * Dump the profiling results for all scripts in file "fd". | 455 * Dump the profiling results for all scripts in file "fd". |
944 */ | 456 */ |
945 static void | 457 void |
946 script_dump_profile(FILE *fd) | 458 script_dump_profile(FILE *fd) |
947 { | 459 { |
948 int id; | 460 int id; |
949 scriptitem_T *si; | 461 scriptitem_T *si; |
950 int i; | 462 int i; |
1014 } | 526 } |
1015 fprintf(fd, "\n"); | 527 fprintf(fd, "\n"); |
1016 } | 528 } |
1017 } | 529 } |
1018 } | 530 } |
1019 | |
1020 /* | |
1021 * Return TRUE when a function defined in the current script should be | |
1022 * profiled. | |
1023 */ | |
1024 int | |
1025 prof_def_func(void) | |
1026 { | |
1027 if (current_sctx.sc_sid > 0) | |
1028 return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force; | |
1029 return FALSE; | |
1030 } | |
1031 | |
1032 # endif | 531 # endif |
1033 #endif | 532 #endif |
1034 | 533 |
1035 /* | 534 /* |
1036 * If 'autowrite' option set, try to write the file. | 535 * If 'autowrite' option set, try to write the file. |