summaryrefslogtreecommitdiffstats
path: root/src/ex_cmds2.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2005-02-26 23:04:13 +0000
committerBram Moolenaar <Bram@vim.org>2005-02-26 23:04:13 +0000
commit05159a0c6a27a030c8497c5cf836977090f9e75d (patch)
tree9ccc167cf3e830e5d01aff4555f99d854cbb623b /src/ex_cmds2.c
parent5313dcb75ac76501f23d21ac94efdbeeabc860bc (diff)
updated for version 7.0052v7.0052
Diffstat (limited to 'src/ex_cmds2.c')
-rw-r--r--src/ex_cmds2.c750
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(&regmatch, 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.