diff options
Diffstat (limited to 'src/ex_cmds2.c')
-rw-r--r-- | src/ex_cmds2.c | 750 |
1 files changed, 668 insertions, 82 deletions
diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index 6c8ece95b1..f1b8d2a874 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -25,6 +25,54 @@ static void cmd_source __ARGS((char_u *fname, exarg_T *eap)); +#ifdef FEAT_EVAL +/* Growarray to store the names of sourced scripts. + * For Unix also store the dev/ino, so that we don't have to stat() each + * script when going through the list. */ +typedef struct scriptitem_S +{ + char_u *sn_name; +# ifdef UNIX + int sn_dev; + ino_t sn_ino; +# endif +# ifdef FEAT_PROFILE + int sn_prof_on; /* TRUE when script is/was profiled */ + int sn_pr_force; /* forceit: profile defined functions */ + proftime_T sn_pr_child; /* time set when going into first child */ + int sn_pr_nest; /* nesting for sn_pr_child */ + /* profiling the script as a whole */ + int sn_pr_count; /* nr of times sourced */ + proftime_T sn_pr_total; /* time spend in script + children */ + proftime_T sn_pr_self; /* time spend in script itself */ + proftime_T sn_pr_start; /* time at script start */ + proftime_T sn_pr_children; /* time in children after script start */ + /* profiling the script per line */ + garray_T sn_prl_ga; /* things stored for every line */ + proftime_T sn_prl_start; /* start time for current line */ + proftime_T sn_prl_children; /* time spent in children for this line */ + proftime_T sn_prl_wait; /* wait start time for current line */ + int sn_prl_idx; /* index of line being timed; -1 if none */ + int sn_prl_execed; /* line being timed was executed */ +# endif +} scriptitem_T; + +static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL}; +#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1]) + +# ifdef FEAT_PROFILE +/* Struct used in sn_prl_ga for every line of a script. */ +typedef struct sn_prl_S +{ + int snp_count; /* nr of times line was executed */ + proftime_T sn_prl_total; /* time spend in a line + children */ + proftime_T sn_prl_self; /* time spend in a line itself */ +} sn_prl_T; + +# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) +# endif +#endif + #if defined(FEAT_EVAL) || defined(PROTO) static int debug_greedy = FALSE; /* batch mode debugging: don't save and restore typeahead. */ @@ -352,41 +400,54 @@ struct debuggy char_u *dbg_name; /* function or file name */ regprog_T *dbg_prog; /* regexp program */ linenr_T dbg_lnum; /* line number in function or file */ + int dbg_forceit; /* ! used */ }; static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; -#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx]) +#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx]) +#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx]) static int last_breakp = 0; /* nr of last defined breakpoint */ +#ifdef FEAT_PROFILE +/* Profiling uses file and func names similar to breakpoints. */ +static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL}; +#endif #define DBG_FUNC 1 #define DBG_FILE 2 -static int dbg_parsearg __ARGS((char_u *arg)); +static int dbg_parsearg __ARGS((char_u *arg, garray_T *gap)); +static linenr_T debuggy_find __ARGS((int file,char_u *fname, linenr_T after, garray_T *gap, int *fp)); /* - * Parse the arguments of ":breakadd" or ":breakdel" and put them in the entry - * just after the last one in dbg_breakp. Note that "dbg_name" is allocated. + * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them + * in the entry just after the last one in dbg_breakp. Note that "dbg_name" + * is allocated. * Returns FAIL for failure. */ static int -dbg_parsearg(arg) +dbg_parsearg(arg, gap) char_u *arg; + garray_T *gap; /* either &dbg_breakp or &prof_ga */ { char_u *p = arg; char_u *q; struct debuggy *bp; int here = FALSE; - if (ga_grow(&dbg_breakp, 1) == FAIL) + if (ga_grow(gap, 1) == FAIL) return FAIL; - bp = &BREAKP(dbg_breakp.ga_len); + bp = &DEBUGGY(gap, gap->ga_len); /* Find "func" or "file". */ if (STRNCMP(p, "func", 4) == 0) bp->dbg_type = DBG_FUNC; else if (STRNCMP(p, "file", 4) == 0) bp->dbg_type = DBG_FILE; - else if (STRNCMP(p, "here", 4) == 0) + else if ( +#ifdef FEAT_PROFILE + gap != &prof_ga && +#endif + STRNCMP(p, "here", 4) == 0) { if (curbuf->b_ffname == NULL) { @@ -406,7 +467,11 @@ dbg_parsearg(arg) /* Find optional line number. */ if (here) bp->dbg_lnum = curwin->w_cursor.lnum; - else if (VIM_ISDIGIT(*p)) + else if ( +#ifdef FEAT_PROFILE + gap != &prof_ga && +#endif + VIM_ISDIGIT(*p)) { bp->dbg_lnum = getdigits(&p); p = skipwhite(p); @@ -474,10 +539,19 @@ ex_breakadd(eap) { struct debuggy *bp; char_u *pat; + garray_T *gap; - if (dbg_parsearg(eap->arg) == OK) + gap = &dbg_breakp; +#ifdef FEAT_PROFILE + if (eap->cmdidx == CMD_profile) + gap = &prof_ga; +#endif + + if (dbg_parsearg(eap->arg, gap) == OK) { - bp = &BREAKP(dbg_breakp.ga_len); + bp = &DEBUGGY(gap, gap->ga_len); + bp->dbg_forceit = eap->forceit; + pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE); if (pat != NULL) { @@ -490,8 +564,14 @@ ex_breakadd(eap) { if (bp->dbg_lnum == 0) /* default line number is 1 */ bp->dbg_lnum = 1; - BREAKP(dbg_breakp.ga_len++).dbg_nr = ++last_breakp; - ++debug_tick; +#ifdef FEAT_PROFILE + if (eap->cmdidx != CMD_profile) +#endif + { + DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp; + ++debug_tick; + } + ++gap->ga_len; } } } @@ -536,7 +616,7 @@ ex_breakdel(eap) else { /* ":breakdel {func|file} [lnum] {name}" */ - if (dbg_parsearg(eap->arg) == FAIL) + if (dbg_parsearg(eap->arg, &dbg_breakp) == FAIL) return; bp = &BREAKP(dbg_breakp.ga_len); for (i = 0; i < dbg_breakp.ga_len; ++i) @@ -605,6 +685,35 @@ dbg_find_breakpoint(file, fname, after) char_u *fname; /* file or function name */ linenr_T after; /* after this line number */ { + return debuggy_find(file, fname, after, &dbg_breakp, NULL); +} + +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Return TRUE if profiling is on for a function or sourced file. + */ + int +has_profiling(file, fname, fp) + int file; /* TRUE for a file, FALSE for a function */ + char_u *fname; /* file or function name */ + int *fp; /* return: forceit */ +{ + return (debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp) + != (linenr_T)0); +} +#endif + +/* + * Common code for dbg_find_breakpoint() and has_profiling(). + */ + static linenr_T +debuggy_find(file, fname, after, gap, fp) + int file; /* TRUE for a file, FALSE for a function */ + char_u *fname; /* file or function name */ + linenr_T after; /* after this line number */ + garray_T *gap; /* either &dbg_breakp or &prof_ga */ + int *fp; /* if not NULL: return forceit */ +{ struct debuggy *bp; int i; linenr_T lnum = 0; @@ -612,6 +721,10 @@ dbg_find_breakpoint(file, fname, after) char_u *name = fname; int prev_got_int; + /* Return quickly when there are no breakpoints. */ + if (gap->ga_len == 0) + return (linenr_T)0; + /* Replace K_SNR in function name with "<SNR>". */ if (!file && fname[0] == K_SPECIAL) { @@ -625,26 +738,32 @@ dbg_find_breakpoint(file, fname, after) } } - for (i = 0; i < dbg_breakp.ga_len; ++i) + for (i = 0; i < gap->ga_len; ++i) { - /* skip entries that are not useful or are for a line that is beyond - * an already found breakpoint */ - bp = &BREAKP(i); - if ((bp->dbg_type == DBG_FILE) == file - && bp->dbg_lnum > after - && (lnum == 0 || bp->dbg_lnum < lnum)) + /* Skip entries that are not useful or are for a line that is beyond + * an already found breakpoint. */ + bp = &DEBUGGY(gap, i); + if (((bp->dbg_type == DBG_FILE) == file && ( +#ifdef FEAT_PROFILE + gap == &prof_ga || +#endif + (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum))))) { regmatch.regprog = bp->dbg_prog; regmatch.rm_ic = FALSE; /* - * Save the value of got_int and reset it. We don't want a previous - * interruption cancel matching, only hitting CTRL-C while matching - * should abort it. + * Save the value of got_int and reset it. We don't want a + * previous interruption cancel matching, only hitting CTRL-C + * while matching should abort it. */ prev_got_int = got_int; got_int = FALSE; if (vim_regexec(®match, name, (colnr_T)0)) + { lnum = bp->dbg_lnum; + if (fp != NULL) + *fp = bp->dbg_forceit; + } got_int |= prev_got_int; } } @@ -666,6 +785,339 @@ dbg_breakpoint(name, lnum) debug_breakpoint_name = name; debug_breakpoint_lnum = lnum; } + + +# if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Functions for profiling. + */ +static void script_do_profile __ARGS((scriptitem_T *si)); +static void script_dump_profile __ARGS((FILE *fd)); +static proftime_T prof_wait_time; + +/* + * Set the time in "tm" to zero. + */ + void +profile_zero(tm) + proftime_T *tm; +{ + tm->tv_usec = 0; + tm->tv_sec = 0; +} + +/* + * Store the current time in "tm". + */ + void +profile_start(tm) + proftime_T *tm; +{ + gettimeofday(tm, NULL); +} + +/* + * Compute the elapsed time from "tm" till now and store in "tm". + */ + void +profile_end(tm) + proftime_T *tm; +{ + proftime_T now; + + gettimeofday(&now, NULL); + tm->tv_usec = now.tv_usec - tm->tv_usec; + tm->tv_sec = now.tv_sec - tm->tv_sec; + if (tm->tv_usec < 0) + { + tm->tv_usec += 1000000; + --tm->tv_sec; + } +} + +/* + * Subtract the time "tm2" from "tm". + */ + void +profile_sub(tm, tm2) + proftime_T *tm, *tm2; +{ + tm->tv_usec -= tm2->tv_usec; + tm->tv_sec -= tm2->tv_sec; + if (tm->tv_usec < 0) + { + tm->tv_usec += 1000000; + --tm->tv_sec; + } +} + +/* + * Add the time "tm2" to "tm". + */ + void +profile_add(tm, tm2) + proftime_T *tm, *tm2; +{ + tm->tv_usec += tm2->tv_usec; + tm->tv_sec += tm2->tv_sec; + if (tm->tv_usec >= 1000000) + { + tm->tv_usec -= 1000000; + ++tm->tv_sec; + } +} + +/* + * Get the current waittime. + */ + void +profile_get_wait(tm) + proftime_T *tm; +{ + *tm = prof_wait_time; +} + +/* + * Subtract the passed waittime since "tm" from "tma". + */ + void +profile_sub_wait(tm, tma) + proftime_T *tm, *tma; +{ + proftime_T tm3 = prof_wait_time; + + profile_sub(&tm3, tm); + profile_sub(tma, &tm3); +} + +/* + * Return TRUE if "tm1" and "tm2" are equal. + */ + int +profile_equal(tm1, tm2) + proftime_T *tm1, *tm2; +{ + return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec); +} + +/* + * Return a string that represents a time. + * Uses a static buffer! + */ + char * +profile_msg(tm) + proftime_T *tm; +{ + static char buf[50]; + + sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec); + return buf; +} + +static char_u *profile_fname = NULL; + +/* + * ":profile cmd args" + */ + void +ex_profile(eap) + exarg_T *eap; +{ + char_u *e; + int len; + + e = skiptowhite(eap->arg); + len = e - eap->arg; + e = skipwhite(e); + + if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) + { + vim_free(profile_fname); + profile_fname = vim_strsave(e); + do_profiling = TRUE; + profile_zero(&prof_wait_time); + set_vim_var_nr(VV_PROFILING, 1L); + } + else if (!do_profiling) + EMSG(_("E750: First use :profile start <fname>")); + else + { + /* The rest is similar to ":breakadd". */ + ex_breakadd(eap); + } +} + +/* + * Dump the profiling info. + */ + void +profile_dump() +{ + FILE *fd; + + if (profile_fname != NULL) + { + fd = fopen((char *)profile_fname, "w"); + if (fd == NULL) + EMSG2(_(e_notopen), profile_fname); + else + { + func_dump_profile(fd); + script_dump_profile(fd); + fclose(fd); + } + } +} + +/* + * Start profiling script "fp". + */ + static void +script_do_profile(si) + scriptitem_T *si; +{ + si->sn_pr_count = 0; + profile_zero(&si->sn_pr_total); + profile_zero(&si->sn_pr_self); + + ga_init2(&si->sn_prl_ga, sizeof(sn_prl_T), 100); + si->sn_prl_idx = -1; + si->sn_prof_on = TRUE; + si->sn_pr_nest = 0; +} + +/* + * save time when starting to invoke another script or function. + */ + void +script_prof_save(tm) + proftime_T *tm; /* place to store wait time */ +{ + scriptitem_T *si; + + if (current_SID > 0 && current_SID <= script_items.ga_len) + { + si = &SCRIPT_ITEM(current_SID); + if (si->sn_prof_on && si->sn_pr_nest++ == 0) + profile_start(&si->sn_pr_child); + } + profile_get_wait(tm); +} + +/* + * Count time spent in children after invoking another script or function. + */ + void +script_prof_restore(tm) + proftime_T *tm; +{ + scriptitem_T *si; + + if (current_SID > 0 && current_SID <= script_items.ga_len) + { + si = &SCRIPT_ITEM(current_SID); + if (si->sn_prof_on && --si->sn_pr_nest == 0) + { + profile_end(&si->sn_pr_child); + profile_sub_wait(tm, &si->sn_pr_child); /* don't count wait time */ + profile_add(&si->sn_pr_children, &si->sn_pr_child); + profile_add(&si->sn_prl_children, &si->sn_pr_child); + } + } +} + +static proftime_T inchar_time; + +/* + * Called when starting to wait for the user to type a character. + */ + void +prof_inchar_enter() +{ + profile_start(&inchar_time); +} + +/* + * Called when finished waiting for the user to type a character. + */ + void +prof_inchar_exit() +{ + profile_end(&inchar_time); + profile_add(&prof_wait_time, &inchar_time); +} + +/* + * Dump the profiling results for all scripts in file "fd". + */ + static void +script_dump_profile(fd) + FILE *fd; +{ + int id; + scriptitem_T *si; + int i; + FILE *sfd; + sn_prl_T *pp; + + for (id = 1; id <= script_items.ga_len; ++id) + { + si = &SCRIPT_ITEM(id); + if (si->sn_prof_on) + { + fprintf(fd, "SCRIPT %s\n", si->sn_name); + if (si->sn_pr_count == 1) + fprintf(fd, "Sourced 1 time\n"); + else + fprintf(fd, "Sourced %d times\n", si->sn_pr_count); + fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total)); + fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self)); + fprintf(fd, "\n"); + fprintf(fd, "count total (s) self (s)\n"); + + sfd = fopen((char *)si->sn_name, "r"); + if (sfd == NULL) + fprintf(fd, "Cannot open file!\n"); + else + { + for (i = 0; i < si->sn_prl_ga.ga_len; ++i) + { + if (vim_fgets(IObuff, IOSIZE, sfd)) + break; + pp = &PRL_ITEM(si, i); + if (pp->snp_count > 0) + { + fprintf(fd, "%5d ", pp->snp_count); + if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self)) + fprintf(fd, " "); + else + fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total)); + fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self)); + } + else + fprintf(fd, " "); + fprintf(fd, "%s", IObuff); + } + fclose(sfd); + } + fprintf(fd, "\n"); + } + } +} + +/* + * Return TRUE when a function defined in the current script should be + * profiled. + */ + int +prof_def_func() +{ + scriptitem_T *si = &SCRIPT_ITEM(current_SID); + + return si->sn_pr_force; +} + +# endif #endif /* @@ -2116,24 +2568,6 @@ source_level(cookie) static char_u *get_one_sourceline __ARGS((struct source_cookie *sp)); -#ifdef FEAT_EVAL -/* Growarray to store the names of sourced scripts. - * For Unix also store the dev/ino, so that we don't have to stat() each - * script when going through the list. */ -struct scriptstuff -{ - char_u *name; -# ifdef UNIX - int dev; - ino_t ino; -# endif -}; -static garray_T script_names = {0, 0, sizeof(struct scriptstuff), 4, NULL}; -#define SCRIPT_NAME(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].name) -#define SCRIPT_DEV(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].dev) -#define SCRIPT_INO(id) (((struct scriptstuff *)script_names.ga_data)[(id) - 1].ino) -#endif - #if defined(WIN32) && defined(FEAT_CSCOPE) static FILE *fopen_noinh_readbin __ARGS((char *filename)); @@ -2177,6 +2611,7 @@ do_source(fname, check_other, is_vimrc) static scid_T last_current_SID = 0; void *save_funccalp; int save_debug_break_level = debug_break_level; + scriptitem_T *si = NULL; # ifdef UNIX struct stat st; int stat_ok; @@ -2186,6 +2621,9 @@ do_source(fname, check_other, is_vimrc) struct timeval tv_rel; struct timeval tv_start; #endif +#ifdef FEAT_PROFILE + proftime_T wait_start; +#endif #ifdef RISCOS p = mch_munge_fname(fname); @@ -2327,6 +2765,15 @@ do_source(fname, check_other, is_vimrc) #endif #ifdef FEAT_EVAL +# ifdef FEAT_PROFILE + if (do_profiling) + prof_child_enter(&wait_start); /* entering a child now */ +# endif + + /* Don't use local function variables, if called from a function. + * Also starts profiling timer for nested script. */ + save_funccalp = save_funccal(); + /* * Check if this script was sourced before to finds its SID. * If it's new, generate a new SID. @@ -2335,48 +2782,72 @@ do_source(fname, check_other, is_vimrc) # ifdef UNIX stat_ok = (mch_stat((char *)fname_exp, &st) >= 0); # endif - for (current_SID = script_names.ga_len; current_SID > 0; --current_SID) - if (SCRIPT_NAME(current_SID) != NULL + for (current_SID = script_items.ga_len; current_SID > 0; --current_SID) + { + si = &SCRIPT_ITEM(current_SID); + if (si->sn_name != NULL && ( # ifdef UNIX /* Compare dev/ino when possible, it catches symbolic * links. Also compare file names, the inode may change * when the file was edited. */ - ((stat_ok && SCRIPT_DEV(current_SID) != -1) - && (SCRIPT_DEV(current_SID) == st.st_dev - && SCRIPT_INO(current_SID) == st.st_ino)) || + ((stat_ok && si->sn_dev != -1) + && (si->sn_dev == st.st_dev + && si->sn_ino == st.st_ino)) || # endif - fnamecmp(SCRIPT_NAME(current_SID), fname_exp) == 0)) + fnamecmp(si->sn_name, fname_exp) == 0)) break; + } if (current_SID == 0) { current_SID = ++last_current_SID; - if (ga_grow(&script_names, (int)(current_SID - script_names.ga_len)) - == OK) + if (ga_grow(&script_items, (int)(current_SID - script_items.ga_len)) + == FAIL) + goto almosttheend; + while (script_items.ga_len < current_SID) { - while (script_names.ga_len < current_SID) - { - SCRIPT_NAME(script_names.ga_len + 1) = NULL; - ++script_names.ga_len; - } - SCRIPT_NAME(current_SID) = fname_exp; -# ifdef UNIX - if (stat_ok) - { - SCRIPT_DEV(current_SID) = st.st_dev; - SCRIPT_INO(current_SID) = st.st_ino; - } - else - SCRIPT_DEV(current_SID) = -1; + ++script_items.ga_len; + SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; +# ifdef FEAT_PROFILE + SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE; # endif - fname_exp = NULL; } + si = &SCRIPT_ITEM(current_SID); + si->sn_name = fname_exp; + fname_exp = NULL; +# ifdef UNIX + if (stat_ok) + { + si->sn_dev = st.st_dev; + si->sn_ino = st.st_ino; + } + else + si->sn_dev = -1; +# endif + /* Allocate the local script variables to use for this script. */ new_script_vars(current_SID); } - /* Don't use local function variables, if called from a function */ - save_funccalp = save_funccal(); +# ifdef FEAT_PROFILE + if (do_profiling) + { + int forceit; + + /* Check if we do profiling for this script. */ + if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit)) + { + script_do_profile(si); + si->sn_pr_force = forceit; + } + if (si->sn_prof_on) + { + ++si->sn_pr_count; + profile_start(&si->sn_pr_start); + profile_zero(&si->sn_pr_children); + } + } +# endif #endif /* @@ -2386,20 +2857,27 @@ do_source(fname, check_other, is_vimrc) DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); retval = OK; - fclose(cookie.fp); - vim_free(cookie.nextline); -#ifdef FEAT_MBYTE - convert_setup(&cookie.conv, NULL, NULL); + +#ifdef FEAT_PROFILE + if (do_profiling) + { + /* Get "si" again, "script_items" may have been reallocated. */ + si = &SCRIPT_ITEM(current_SID); + if (si->sn_prof_on) + { + profile_end(&si->sn_pr_start); + profile_sub_wait(&wait_start, &si->sn_pr_start); + profile_add(&si->sn_pr_total, &si->sn_pr_start); + profile_add(&si->sn_pr_self, &si->sn_pr_start); + profile_sub(&si->sn_pr_self, &si->sn_pr_children); + } + } #endif if (got_int) EMSG(_(e_interr)); sourcing_name = save_sourcing_name; sourcing_lnum = save_sourcing_lnum; -#ifdef FEAT_EVAL - current_SID = save_current_SID; - restore_funccal(save_funccalp); -#endif if (p_verbose > 1) { msg_str((char_u *)_("finished sourcing %s"), fname); @@ -2426,6 +2904,21 @@ do_source(fname, check_other, is_vimrc) ++debug_break_level; #endif +#ifdef FEAT_EVAL +almosttheend: + current_SID = save_current_SID; + restore_funccal(save_funccalp); +# ifdef FEAT_PROFILE + if (do_profiling) + prof_child_exit(&wait_start); /* leaving a child now */ +# endif +#endif + fclose(cookie.fp); + vim_free(cookie.nextline); +#ifdef FEAT_MBYTE + convert_setup(&cookie.conv, NULL, NULL); +#endif + theend: vim_free(fname_exp); return retval; @@ -2442,9 +2935,9 @@ ex_scriptnames(eap) { int i; - for (i = 1; i <= script_names.ga_len && !got_int; ++i) - if (SCRIPT_NAME(i) != NULL) - smsg((char_u *)"%3d: %s", i, SCRIPT_NAME(i)); + for (i = 1; i <= script_items.ga_len && !got_int; ++i) + if (SCRIPT_ITEM(i).sn_name != NULL) + smsg((char_u *)"%3d: %s", i, SCRIPT_ITEM(i).sn_name); } # if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) @@ -2456,9 +2949,9 @@ scriptnames_slash_adjust() { int i; - for (i = 1; i <= script_names.ga_len; ++i) - if (SCRIPT_NAME(i) != NULL) - slash_adjust(SCRIPT_NAME(i)); + for (i = 1; i <= script_items.ga_len; ++i) + if (SCRIPT_ITEM(i).sn_name != NULL) + slash_adjust(SCRIPT_ITEM(i).sn_name); } # endif @@ -2477,8 +2970,9 @@ get_scriptname(id) return (char_u *)"-c argument"; if (id == SID_ENV) return (char_u *)"environment variable"; - return SCRIPT_NAME(id); + return SCRIPT_ITEM(id).sn_name; } + #endif #if defined(USE_CR) || defined(PROTO) @@ -2565,6 +3059,10 @@ getsourceline(c, cookie, indent) sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum); sp->dbg_tick = debug_tick; } +# ifdef FEAT_PROFILE + if (do_profiling) + script_line_end(); +# endif #endif /* * Get current line. If there is a read-ahead line, use it, otherwise get @@ -2579,6 +3077,10 @@ getsourceline(c, cookie, indent) line = sp->nextline; sp->nextline = NULL; ++sourcing_lnum; +#ifdef FEAT_PROFILE + if (do_profiling) + script_line_start(); +#endif } /* Only concatenate lines starting with a \ when 'cpoptions' doesn't @@ -2783,6 +3285,90 @@ get_one_sourceline(sp) return NULL; } +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Called when starting to read a script line. + * "sourcing_lnum" must be correct! + * When skipping lines it may not actually be executed, but we won't find out + * until later and we need to store the time now. + */ + void +script_line_start() +{ + scriptitem_T *si; + sn_prl_T *pp; + + if (current_SID <= 0 || current_SID > script_items.ga_len) + return; + si = &SCRIPT_ITEM(current_SID); + if (si->sn_prof_on && sourcing_lnum >= 1) + { + /* Grow the array before starting the timer, so that the time spend + * here isn't counted. */ + ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len)); + si->sn_prl_idx = sourcing_lnum - 1; + while (si->sn_prl_ga.ga_len <= si->sn_prl_idx + && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) + { + /* Zero counters for a line that was not used before. */ + pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len); + pp->snp_count = 0; + profile_zero(&pp->sn_prl_total); + profile_zero(&pp->sn_prl_self); + ++si->sn_prl_ga.ga_len; + } + si->sn_prl_execed = FALSE; + profile_start(&si->sn_prl_start); + profile_zero(&si->sn_prl_children); + profile_get_wait(&si->sn_prl_wait); + } +} + +/* + * Called when actually executing a function line. + */ + void +script_line_exec() +{ + scriptitem_T *si; + + if (current_SID <= 0 || current_SID > script_items.ga_len) + return; + si = &SCRIPT_ITEM(current_SID); + if (si->sn_prof_on && si->sn_prl_idx >= 0) + si->sn_prl_execed = TRUE; +} + +/* + * Called when done with a function line. + */ + void +script_line_end() +{ + scriptitem_T *si; + sn_prl_T *pp; + + if (current_SID <= 0 || current_SID > script_items.ga_len) + return; + si = &SCRIPT_ITEM(current_SID); + if (si->sn_prof_on && si->sn_prl_idx >= 0 + && si->sn_prl_idx < si->sn_prl_ga.ga_len) + { + if (si->sn_prl_execed) + { + pp = &PRL_ITEM(si, si->sn_prl_idx); + ++pp->snp_count; + profile_end(&si->sn_prl_start); + profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start); + profile_add(&pp->sn_prl_self, &si->sn_prl_start); + profile_add(&pp->sn_prl_total, &si->sn_prl_start); + profile_sub(&pp->sn_prl_self, &si->sn_prl_children); + } + si->sn_prl_idx = -1; + } +} +#endif + /* * ":scriptencoding": Set encoding conversion for a sourced script. * Without the multi-byte feature it's simply ignored. |