Mercurial > vim
diff src/eval.c @ 170:8c60f65311fa v7.0052
updated for version 7.0052
author | vimboss |
---|---|
date | Sat, 26 Feb 2005 23:04:13 +0000 |
parents | 0e902b8f511f |
children | c12f39141bbc |
line wrap: on
line diff
--- a/src/eval.c +++ b/src/eval.c @@ -106,6 +106,7 @@ static char *e_funcdict = N_("E717: Dict static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_letwrong = N_("E734: Wrong variable type for %s="); +static char *e_nofunc = N_("E130: Unknown function: %s"); /* * All user-defined global variables are stored in dictionary "globvardict". @@ -153,6 +154,24 @@ struct ufunc int uf_calls; /* nr of active calls */ garray_T uf_args; /* arguments */ garray_T uf_lines; /* function lines */ +#ifdef FEAT_PROFILE + int uf_profiling; /* TRUE when func is being profiled */ + /* profiling the function as a whole */ + int uf_tm_count; /* nr of calls */ + proftime_T uf_tm_total; /* time spend in function + children */ + proftime_T uf_tm_self; /* time spend in function itself */ + proftime_T uf_tm_start; /* time at function call */ + proftime_T uf_tm_children; /* time spent in children this call */ + /* profiling the function per line */ + int *uf_tml_count; /* nr of times line was executed */ + proftime_T *uf_tml_total; /* time spend in a line + children */ + proftime_T *uf_tml_self; /* time spend in a line itself */ + proftime_T uf_tml_start; /* start time for current line */ + proftime_T uf_tml_children; /* time spent in children for this line */ + proftime_T uf_tml_wait; /* start wait time for current line */ + int uf_tml_idx; /* index of line being timed; -1 if none */ + int uf_tml_execed; /* line being timed was executed */ +#endif scid_T uf_script_ID; /* ID of script where function was defined, used for s: variables */ int uf_refcount; /* for numbered function: reference count */ @@ -205,6 +224,9 @@ typedef struct funccall_S linenr_T breakpoint; /* next line with breakpoint or zero */ int dbg_tick; /* debug_tick when breakpoint was set */ int level; /* top nesting level of executed function */ +#ifdef FEAT_PROFILE + proftime_T prof_child; /* time spent in a child */ +#endif } funccall_T; /* @@ -293,6 +315,7 @@ static struct vimvar {VV_NAME("insertmode", VAR_STRING), VV_RO}, {VV_NAME("val", VAR_UNKNOWN), VV_RO}, {VV_NAME("key", VAR_UNKNOWN), VV_RO}, + {VV_NAME("profiling", VAR_NUMBER), VV_RO}, }; /* shorthand */ @@ -345,7 +368,6 @@ static void list_remove __ARGS((list_T * static char_u *list2string __ARGS((typval_T *tv)); static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo)); -static dict_T *dict_alloc __ARGS((void)); static void dict_unref __ARGS((dict_T *d)); static void dict_free __ARGS((dict_T *d)); static dictitem_T *dictitem_alloc __ARGS((char_u *key)); @@ -399,6 +421,7 @@ static void f_did_filetype __ARGS((typva static void f_diff_filler __ARGS((typval_T *argvars, typval_T *rettv)); static void f_diff_hlID __ARGS((typval_T *argvars, typval_T *rettv)); static void f_empty __ARGS((typval_T *argvars, typval_T *rettv)); +static void f_errorlist __ARGS((typval_T *argvars, typval_T *rettv)); static void f_escape __ARGS((typval_T *argvars, typval_T *rettv)); static void f_eval __ARGS((typval_T *argvars, typval_T *rettv)); static void f_eventhandler __ARGS((typval_T *argvars, typval_T *rettv)); @@ -582,6 +605,9 @@ static void cat_func_name __ARGS((char_u static ufunc_T *find_func __ARGS((char_u *name)); static int function_exists __ARGS((char_u *name)); static int builtin_function __ARGS((char_u *name)); +#ifdef FEAT_PROFILE +static void func_do_profile __ARGS((ufunc_T *fp)); +#endif static int script_autoload __ARGS((char_u *name)); static char_u *autoload_name __ARGS((char_u *name)); static void func_free __ARGS((ufunc_T *fp)); @@ -1170,19 +1196,59 @@ call_vim_function(func, argc, argv, safe void * save_funccal() { - funccall_T *fc; - - fc = current_funccal; + funccall_T *fc = current_funccal; + current_funccal = NULL; return (void *)fc; } void -restore_funccal(fc) - void *fc; -{ - current_funccal = (funccall_T *)fc; -} +restore_funccal(vfc) + void *vfc; +{ + funccall_T *fc = (funccall_T *)vfc; + + current_funccal = fc; +} + +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Prepare profiling for entering a child or something else that is not + * counted for the script/function itself. + * Should always be called in pair with prof_child_exit(). + */ + void +prof_child_enter(tm) + proftime_T *tm; /* place to store waittime */ +{ + funccall_T *fc = current_funccal; + + if (fc != NULL && fc->func->uf_profiling) + profile_start(&fc->prof_child); + script_prof_save(tm); +} + +/* + * Take care of time spent in a child. + * Should always be called after prof_child_enter(). + */ + void +prof_child_exit(tm) + proftime_T *tm; /* where waittime was stored */ +{ + funccall_T *fc = current_funccal; + + if (fc != NULL && fc->func->uf_profiling) + { + profile_end(&fc->prof_child); + profile_sub_wait(tm, &fc->prof_child); /* don't count waiting time */ + profile_add(&fc->func->uf_tm_children, &fc->prof_child); + profile_add(&fc->func->uf_tml_children, &fc->prof_child); + } + script_prof_restore(tm); +} +#endif + #ifdef FEAT_FOLDING /* @@ -5020,12 +5086,33 @@ list_append_tv(l, tv) list_T *l; typval_T *tv; { - listitem_T *ni = listitem_alloc(); - - if (ni == NULL) + listitem_T *li = listitem_alloc(); + + if (li == NULL) return FAIL; - copy_tv(tv, &ni->li_tv); - list_append(l, ni); + copy_tv(tv, &li->li_tv); + list_append(l, li); + return OK; +} + +/* + * Add a dictionary to a list. Used by errorlist(). + * Return FAIL when out of memory. + */ + int +list_append_dict(list, dict) + list_T *list; + dict_T *dict; +{ + listitem_T *li = listitem_alloc(); + + if (li == NULL) + return FAIL; + li->li_tv.v_type = VAR_DICT; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_dict = dict; + list_append(list, li); + ++dict->dv_refcount; return OK; } @@ -5266,7 +5353,7 @@ list_join(gap, l, sep, echo) /* * Allocate an empty header for a dictionary. */ - static dict_T * + dict_T * dict_alloc() { dict_T *d; @@ -5470,6 +5557,42 @@ dict_add(d, item) } /* + * Add a number or string entry to dictionary "d". + * When "str" is NULL use number "nr", otherwise use "str". + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add_nr_str(d, key, nr, str) + dict_T *d; + char *key; + long nr; + char_u *str; +{ + dictitem_T *item; + + item = dictitem_alloc((char_u *)key); + if (item == NULL) + return FAIL; + item->di_tv.v_lock = 0; + if (str == NULL) + { + item->di_tv.v_type = VAR_NUMBER; + item->di_tv.vval.v_number = nr; + } + else + { + item->di_tv.v_type = VAR_STRING; + item->di_tv.vval.v_string = vim_strsave(str); + } + if (dict_add(d, item) == FAIL) + { + dictitem_free(item); + return FAIL; + } + return OK; +} + +/* * Get the number of items in a Dictionary. */ static long @@ -5844,6 +5967,7 @@ get_env_tv(arg, rettv, evaluate) int len; int cc; char_u *name; + int mustfree = FALSE; ++*arg; name = *arg; @@ -5854,12 +5978,18 @@ get_env_tv(arg, rettv, evaluate) { cc = name[len]; name[len] = NUL; - /* first try mch_getenv(), fast for normal environment vars */ - string = mch_getenv(name); + /* first try vim_getenv(), fast for normal environment vars */ + string = vim_getenv(name, &mustfree); if (string != NULL && *string != NUL) - string = vim_strsave(string); - else - { + { + if (!mustfree) + string = vim_strsave(string); + } + else + { + if (mustfree) + vim_free(string); + /* next try expanding things like $VIM and ${HOME} */ string = expand_env_save(name - 1); if (string != NULL && *string == '$') @@ -5923,6 +6053,7 @@ static struct fst {"diff_filler", 1, 1, f_diff_filler}, {"diff_hlID", 2, 2, f_diff_hlID}, {"empty", 1, 1, f_empty}, + {"errorlist", 0, 0, f_errorlist}, {"escape", 2, 2, f_escape}, {"eval", 1, 1, f_eval}, {"eventhandler", 0, 0, f_eventhandler}, @@ -7421,6 +7552,36 @@ f_empty(argvars, rettv) } /* + * "errorlist()" function + */ +/*ARGSUSED*/ + static void +f_errorlist(argvars, rettv) + typval_T *argvars; + typval_T *rettv; +{ +#ifdef FEAT_QUICKFIX + list_T *l; +#endif + + rettv->vval.v_number = FALSE; +#ifdef FEAT_QUICKFIX + l = list_alloc(); + if (l != NULL) + { + if (get_errorlist(l) != FAIL) + { + rettv->vval.v_list = l; + rettv->v_type = VAR_LIST; + ++l->lv_refcount; + } + else + list_free(l); + } +#endif +} + +/* * "escape({string}, {chars})" function */ static void @@ -9212,6 +9373,9 @@ f_has(argvars, rettv) #ifdef FEAT_PRINTER "printer", #endif +#ifdef FEAT_PROFILE + "profile", +#endif #ifdef FEAT_QUICKFIX "quickfix", #endif @@ -12651,25 +12815,27 @@ f_strridx(argvars, rettv) needle = get_tv_string(&argvars[1]); haystack = get_tv_string_buf(&argvars[0], buf); haystack_len = STRLEN(haystack); + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* Third argument: upper limit for index */ + end_idx = get_tv_number(&argvars[2]); + if (end_idx < 0) + { + /* can never find a match */ + rettv->vval.v_number = -1; + return; + } + } + else + end_idx = haystack_len; + if (*needle == NUL) + { /* Empty string matches past the end. */ - lastmatch = haystack + haystack_len; - else - { - if (argvars[2].v_type != VAR_UNKNOWN) - { - /* Third argument: upper limit for index */ - end_idx = get_tv_number(&argvars[2]); - if (end_idx < 0) - { - /* can never find a match */ - rettv->vval.v_number = -1; - return; - } - } - else - end_idx = haystack_len; - + lastmatch = haystack + end_idx; + } + else + { for (rest = haystack; *rest != '\0'; ++rest) { rest = (char_u *)strstr((char *)rest, (char *)needle); @@ -15625,6 +15791,14 @@ ex_function(eap) } fp->uf_args = newargs; fp->uf_lines = newlines; +#ifdef FEAT_PROFILE + fp->uf_tml_count = NULL; + fp->uf_tml_total = NULL; + fp->uf_tml_self = NULL; + fp->uf_profiling = FALSE; + if (prof_def_func()) + func_do_profile(fp); +#endif fp->uf_varargs = varargs; fp->uf_flags = flags; fp->uf_calls = 0; @@ -15912,6 +16086,92 @@ builtin_function(name) return ASCII_ISLOWER(name[0]) && vim_strchr(name, ':') == NULL; } +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Start profiling function "fp". + */ + static void +func_do_profile(fp) + ufunc_T *fp; +{ + fp->uf_tm_count = 0; + profile_zero(&fp->uf_tm_self); + profile_zero(&fp->uf_tm_total); + if (fp->uf_tml_count == NULL) + fp->uf_tml_count = (int *)alloc_clear((unsigned) + (sizeof(int) * fp->uf_lines.ga_len)); + if (fp->uf_tml_total == NULL) + fp->uf_tml_total = (proftime_T *)alloc_clear((unsigned) + (sizeof(proftime_T) * fp->uf_lines.ga_len)); + if (fp->uf_tml_self == NULL) + fp->uf_tml_self = (proftime_T *)alloc_clear((unsigned) + (sizeof(proftime_T) * fp->uf_lines.ga_len)); + fp->uf_tml_idx = -1; + if (fp->uf_tml_count == NULL || fp->uf_tml_total == NULL + || fp->uf_tml_self == NULL) + return; /* out of memory */ + + fp->uf_profiling = TRUE; +} + +/* + * Dump the profiling results for all functions in file "fd". + */ + void +func_dump_profile(fd) + FILE *fd; +{ + hashitem_T *hi; + int todo; + ufunc_T *fp; + int i; + + todo = func_hashtab.ht_used; + for (hi = func_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + fp = HI2UF(hi); + if (fp->uf_profiling) + { + if (fp->uf_name[0] == K_SPECIAL) + fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3); + else + fprintf(fd, "FUNCTION %s()\n", fp->uf_name); + if (fp->uf_tm_count == 1) + fprintf(fd, "Called 1 time\n"); + else + fprintf(fd, "Called %d times\n", fp->uf_tm_count); + fprintf(fd, "Total time: %s\n", profile_msg(&fp->uf_tm_total)); + fprintf(fd, " Self time: %s\n", profile_msg(&fp->uf_tm_self)); + fprintf(fd, "\n"); + fprintf(fd, "count total (s) self (s)\n"); + + for (i = 0; i < fp->uf_lines.ga_len; ++i) + { + if (fp->uf_tml_count[i] > 0) + { + fprintf(fd, "%5d ", fp->uf_tml_count[i]); + if (profile_equal(&fp->uf_tml_total[i], + &fp->uf_tml_self[i])) + fprintf(fd, " "); + else + fprintf(fd, "%s ", + profile_msg(&fp->uf_tml_total[i])); + fprintf(fd, "%s ", profile_msg(&fp->uf_tml_self[i])); + } + else + fprintf(fd, " "); + fprintf(fd, "%s\n", FUNCLINE(fp, i)); + } + fprintf(fd, "\n"); + } + } + } +} +#endif + /* * If "name" has a package name try autoloading the script for it. * Return TRUE if a package was loaded. @@ -16065,7 +16325,7 @@ ex_delfunction(eap) { if (fp == NULL) { - EMSG2(_("E130: Undefined function: %s"), eap->arg); + EMSG2(_(e_nofunc), eap->arg); return; } if (fp->uf_calls > 0) @@ -16097,6 +16357,11 @@ func_free(fp) /* clear this function */ ga_clear_strings(&(fp->uf_args)); ga_clear_strings(&(fp->uf_lines)); +#ifdef FEAT_PROFILE + vim_free(fp->uf_tml_count); + vim_free(fp->uf_tml_total); + vim_free(fp->uf_tml_self); +#endif /* remove the function from the function hashtable */ hi = hash_find(&func_hashtab, UF2HIKEY(fp)); @@ -16178,6 +16443,9 @@ call_user_func(fp, argcount, argvars, re int ai; char_u numbuf[NUMBUFLEN]; char_u *name; +#ifdef FEAT_PROFILE + proftime_T wait_start; +#endif /* If depth of calling is getting too high, don't execute the function */ if (depth >= p_mfd) @@ -16341,6 +16609,22 @@ call_user_func(fp, argcount, argvars, re --no_wait_return; } } +#ifdef FEAT_PROFILE + if (do_profiling) + { + if (!fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL)) + func_do_profile(fp); + if (fp->uf_profiling + || (save_fcp != NULL && &save_fcp->func->uf_profiling)) + { + ++fp->uf_tm_count; + profile_start(&fp->uf_tm_start); + profile_zero(&fp->uf_tm_children); + } + script_prof_save(&wait_start); + } +#endif + save_current_SID = current_SID; current_SID = fp->uf_script_ID; save_did_emsg = did_emsg; @@ -16360,6 +16644,22 @@ call_user_func(fp, argcount, argvars, re rettv->vval.v_number = -1; } +#ifdef FEAT_PROFILE + if (fp->uf_profiling || (save_fcp != NULL && &save_fcp->func->uf_profiling)) + { + profile_end(&fp->uf_tm_start); + profile_sub_wait(&wait_start, &fp->uf_tm_start); + profile_add(&fp->uf_tm_total, &fp->uf_tm_start); + profile_add(&fp->uf_tm_self, &fp->uf_tm_start); + profile_sub(&fp->uf_tm_self, &fp->uf_tm_children); + if (save_fcp != NULL && &save_fcp->func->uf_profiling) + { + profile_add(&save_fcp->func->uf_tm_children, &fp->uf_tm_start); + profile_add(&save_fcp->func->uf_tml_children, &fp->uf_tm_start); + } + } +#endif + /* when being verbose, mention the return value */ if (p_verbose >= 12) { @@ -16398,6 +16698,10 @@ call_user_func(fp, argcount, argvars, re sourcing_name = save_sourcing_name; sourcing_lnum = save_sourcing_lnum; current_SID = save_current_SID; +#ifdef FEAT_PROFILE + if (do_profiling) + script_prof_restore(&wait_start); +#endif if (p_verbose >= 12 && sourcing_name != NULL) { @@ -16624,19 +16928,24 @@ get_func_line(c, cookie, indent) int indent; /* not used */ { funccall_T *fcp = (funccall_T *)cookie; - char_u *retval; - garray_T *gap; /* growarray with function lines */ + ufunc_T *fp = fcp->func; + char_u *retval; + garray_T *gap; /* growarray with function lines */ /* If breakpoints have been added/deleted need to check for it. */ if (fcp->dbg_tick != debug_tick) { - fcp->breakpoint = dbg_find_breakpoint(FALSE, fcp->func->uf_name, + fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, sourcing_lnum); fcp->dbg_tick = debug_tick; } - - gap = &fcp->func->uf_lines; - if ((fcp->func->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try()) +#ifdef FEAT_PROFILE + if (do_profiling) + func_line_end(cookie); +#endif + + gap = &fp->uf_lines; + if ((fp->uf_flags & FC_ABORT) && did_emsg && !aborted_in_try()) retval = NULL; else if (fcp->returned || fcp->linenr >= gap->ga_len) retval = NULL; @@ -16644,14 +16953,18 @@ get_func_line(c, cookie, indent) { retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]); sourcing_lnum = fcp->linenr; +#ifdef FEAT_PROFILE + if (do_profiling) + func_line_start(cookie); +#endif } /* Did we encounter a breakpoint? */ if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum) { - dbg_breakpoint(fcp->func->uf_name, sourcing_lnum); + dbg_breakpoint(fp->uf_name, sourcing_lnum); /* Find next breakpoint. */ - fcp->breakpoint = dbg_find_breakpoint(FALSE, fcp->func->uf_name, + fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, sourcing_lnum); fcp->dbg_tick = debug_tick; } @@ -16659,6 +16972,71 @@ get_func_line(c, cookie, indent) return retval; } +#if defined(FEAT_PROFILE) || defined(PROTO) +/* + * Called when starting to read a function 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 +func_line_start(cookie) + void *cookie; +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && sourcing_lnum >= 1 + && sourcing_lnum <= fp->uf_lines.ga_len) + { + fp->uf_tml_idx = sourcing_lnum - 1; + fp->uf_tml_execed = FALSE; + profile_start(&fp->uf_tml_start); + profile_zero(&fp->uf_tml_children); + profile_get_wait(&fp->uf_tml_wait); + } +} + +/* + * Called when actually executing a function line. + */ + void +func_line_exec(cookie) + void *cookie; +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && fp->uf_tml_idx >= 0) + fp->uf_tml_execed = TRUE; +} + +/* + * Called when done with a function line. + */ + void +func_line_end(cookie) + void *cookie; +{ + funccall_T *fcp = (funccall_T *)cookie; + ufunc_T *fp = fcp->func; + + if (fp->uf_profiling && fp->uf_tml_idx >= 0) + { + if (fp->uf_tml_execed) + { + ++fp->uf_tml_count[fp->uf_tml_idx]; + profile_end(&fp->uf_tml_start); + profile_sub_wait(&fp->uf_tml_wait, &fp->uf_tml_start); + profile_add(&fp->uf_tml_self[fp->uf_tml_idx], &fp->uf_tml_start); + profile_add(&fp->uf_tml_total[fp->uf_tml_idx], &fp->uf_tml_start); + profile_sub(&fp->uf_tml_self[fp->uf_tml_idx], &fp->uf_tml_children); + } + fp->uf_tml_idx = -1; + } +} +#endif + /* * Return TRUE if the currently active function should be ended, because a * return was encountered or an error occured. Used inside a ":while".