diff src/ex_cmds2.c @ 170:8c60f65311fa v7.0052

updated for version 7.0052
author vimboss
date Sat, 26 Feb 2005 23:04:13 +0000
parents 6df0106fc595
children 84c21eb4fc40
line wrap: on
line diff
--- 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;
-
-    if (dbg_parsearg(eap->arg) == OK)
-    {
-	bp = &BREAKP(dbg_breakp.ga_len);
+    garray_T	*gap;
+
+    gap = &dbg_breakp;
+#ifdef FEAT_PROFILE
+    if (eap->cmdidx == CMD_profile)
+	gap = &prof_ga;
+#endif
+
+    if (dbg_parsearg(eap->arg, gap) == OK)
+    {
+	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)
-    {
-	/* 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))
+    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 = &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;
+	    ++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
+	}
+	si = &SCRIPT_ITEM(current_SID);
+	si->sn_name = fname_exp;
+	fname_exp = NULL;
 # 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;
+	if (stat_ok)
+	{
+	    si->sn_dev = st.st_dev;
+	    si->sn_ino = st.st_ino;
+	}
+	else
+	    si->sn_dev = -1;
 # endif
-	    fname_exp = NULL;
-	}
+
 	/* 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.