vim-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 vim/vim#4666)
fa55cfc69d

Move proftime_T to types.h for now to avoid recursive #include.
This commit is contained in:
zeertzjq 2022-08-12 12:58:52 +08:00
parent 0d9e09bf67
commit c7ca94ba7f
27 changed files with 449 additions and 422 deletions

View File

@ -241,6 +241,10 @@ preprocess_patch() {
LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/map\(\.[ch]\)/\1\/mapping\2/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
# Rename profiler.c to profile.c
LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/profiler\(\.[ch]\)/\1\/profile\2/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"
# Rename session.c to ex_session.c
LC_ALL=C sed -e 's/\( [ab]\/src\/nvim\)\/session\(\.[ch]\)/\1\/ex_session\2/g' \
"$file" > "$file".tmp && mv "$file".tmp "$file"

View File

@ -24,6 +24,7 @@
#include "nvim/map.h"
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/search.h"
#include "nvim/state.h"

View File

@ -36,8 +36,6 @@ typedef struct {
#include "nvim/hashtab.h"
// for dict_T
#include "nvim/eval/typval.h"
// for proftime_T
#include "nvim/profile.h"
// for String
#include "nvim/api/private/defs.h"
// for Map(K, V)

View File

@ -44,6 +44,7 @@
#include "nvim/os/input.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
@ -1192,42 +1193,6 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv)
return rettv.vval.v_list;
}
/// 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().
///
/// @param tm place to store waittime
void prof_child_enter(proftime_T *tm)
{
funccall_T *fc = get_current_funccal();
if (fc != NULL && fc->func->uf_profiling) {
fc->prof_child = profile_start();
}
script_prof_save(tm);
}
/// Take care of time spent in a child.
/// Should always be called after prof_child_enter().
///
/// @param tm where waittime was stored
void prof_child_exit(proftime_T *tm)
{
funccall_T *fc = get_current_funccal();
if (fc != NULL && fc->func->uf_profiling) {
fc->prof_child = profile_end(fc->prof_child);
// don't count waiting time
fc->prof_child = profile_sub_wait(*tm, fc->prof_child);
fc->func->uf_tm_children =
profile_add(fc->func->uf_tm_children, fc->prof_child);
fc->func->uf_tml_children =
profile_add(fc->func->uf_tml_children, fc->prof_child);
}
script_prof_restore(tm);
}
/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
/// it in "*cp". Doesn't give error messages.
int eval_foldexpr(char *arg, int *cp)
@ -7941,38 +7906,6 @@ const char *find_option_end(const char **const arg, int *const opt_flags)
return p;
}
/// Start profiling function "fp".
void func_do_profile(ufunc_T *fp)
{
int len = fp->uf_lines.ga_len;
if (!fp->uf_prof_initialized) {
if (len == 0) {
len = 1; // avoid getting error for allocating zero bytes
}
fp->uf_tm_count = 0;
fp->uf_tm_self = profile_zero();
fp->uf_tm_total = profile_zero();
if (fp->uf_tml_count == NULL) {
fp->uf_tml_count = xcalloc((size_t)len, sizeof(int));
}
if (fp->uf_tml_total == NULL) {
fp->uf_tml_total = xcalloc((size_t)len, sizeof(proftime_T));
}
if (fp->uf_tml_self == NULL) {
fp->uf_tml_self = xcalloc((size_t)len, sizeof(proftime_T));
}
fp->uf_tml_idx = -1;
fp->uf_prof_initialized = true;
}
fp->uf_profiling = TRUE;
}
/// Dump the profiling results for all functions in file "fd".
void func_dump_profile(FILE *fd)
{
@ -8049,66 +7982,6 @@ void func_dump_profile(FILE *fd)
xfree(sorttab);
}
/// @param prefer_self when equal print only self time
static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, int prefer_self)
{
int i;
ufunc_T *fp;
fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title);
fprintf(fd, "count total (s) self (s) function\n");
for (i = 0; i < 20 && i < st_len; ++i) {
fp = sorttab[i];
prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self,
prefer_self);
if (fp->uf_name[0] == K_SPECIAL) {
fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3);
} else {
fprintf(fd, " %s()\n", fp->uf_name);
}
}
fprintf(fd, "\n");
}
/// Print the count and times for one function or function line.
///
/// @param prefer_self when equal print only self time
static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self,
int prefer_self)
{
if (count > 0) {
fprintf(fd, "%5d ", count);
if (prefer_self && profile_equal(*total, *self)) {
fprintf(fd, " ");
} else {
fprintf(fd, "%s ", profile_msg(*total));
}
if (!prefer_self && profile_equal(*total, *self)) {
fprintf(fd, " ");
} else {
fprintf(fd, "%s ", profile_msg(*self));
}
} else {
fprintf(fd, " ");
}
}
/// Compare function for total time sorting.
static int prof_total_cmp(const void *s1, const void *s2)
{
ufunc_T *p1 = *(ufunc_T **)s1;
ufunc_T *p2 = *(ufunc_T **)s2;
return profile_cmp(p1->uf_tm_total, p2->uf_tm_total);
}
/// Compare function for self time sorting.
static int prof_self_cmp(const void *s1, const void *s2)
{
ufunc_T *p1 = *(ufunc_T **)s1;
ufunc_T *p2 = *(ufunc_T **)s2;
return profile_cmp(p1->uf_tm_self, p2->uf_tm_self);
}
/// Return the autoload script name for a function or variable name
/// Caller must make sure that "name" contains AUTOLOAD_CHAR.
///
@ -8183,61 +8056,6 @@ bool script_autoload(const char *const name, const size_t name_len, const bool r
return ret;
}
/// 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(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;
// Skip continuation lines.
while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) {
fp->uf_tml_idx--;
}
fp->uf_tml_execed = false;
fp->uf_tml_start = profile_start();
fp->uf_tml_children = profile_zero();
fp->uf_tml_wait = profile_get_wait();
}
}
/// Called when actually executing a function line.
void func_line_exec(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(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];
fp->uf_tml_start = profile_end(fp->uf_tml_start);
fp->uf_tml_start = profile_sub_wait(fp->uf_tml_wait, fp->uf_tml_start);
fp->uf_tml_total[fp->uf_tml_idx] =
profile_add(fp->uf_tml_total[fp->uf_tml_idx], fp->uf_tml_start);
fp->uf_tml_self[fp->uf_tml_idx] =
profile_self(fp->uf_tml_self[fp->uf_tml_idx], fp->uf_tml_start,
fp->uf_tml_children);
}
fp->uf_tml_idx = -1;
}
}
static var_flavour_T var_flavour(char *varname)
FUNC_ATTR_PURE
{

View File

@ -58,6 +58,7 @@
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/popupmnu.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"

View File

@ -16,7 +16,6 @@
#include "nvim/mbyte_defs.h"
#include "nvim/message.h"
#include "nvim/pos.h" // for linenr_T
#include "nvim/profile.h" // for proftime_T
#include "nvim/types.h"
#ifdef LOG_LIST_ACTIONS
# include "nvim/memory.h"

View File

@ -21,6 +21,7 @@
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
#include "nvim/os/input.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/search.h"
#include "nvim/ui.h"

View File

@ -60,6 +60,7 @@
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"

View File

@ -54,17 +54,6 @@
#include "nvim/version.h"
#include "nvim/window.h"
/// Growarray to store info about already sourced scripts.
static garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
// 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 spent in a line + children
proftime_T sn_prl_self; ///< time spent in a line itself
} sn_prl_T;
/// Structure used to store info for each sourced file.
/// It is shared between do_source() and getsourceline().
/// This is required, because it needs to be handed to do_cmdline() and
@ -91,52 +80,6 @@ struct source_cookie {
# include "ex_cmds2.c.generated.h"
#endif
static char *profile_fname = NULL;
/// ":profile cmd args"
void ex_profile(exarg_T *eap)
{
static proftime_T pause_time;
char *e;
int len;
e = (char *)skiptowhite((char_u *)eap->arg);
len = (int)(e - eap->arg);
e = skipwhite(e);
if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) {
xfree(profile_fname);
profile_fname = (char *)expand_env_save_opt((char_u *)e, true);
do_profiling = PROF_YES;
profile_set_wait(profile_zero());
set_vim_var_nr(VV_PROFILING, 1L);
} else if (do_profiling == PROF_NONE) {
emsg(_("E750: First use \":profile start {fname}\""));
} else if (STRCMP(eap->arg, "stop") == 0) {
profile_dump();
do_profiling = PROF_NONE;
set_vim_var_nr(VV_PROFILING, 0L);
profile_reset();
} else if (STRCMP(eap->arg, "pause") == 0) {
if (do_profiling == PROF_YES) {
pause_time = profile_start();
}
do_profiling = PROF_PAUSED;
} else if (STRCMP(eap->arg, "continue") == 0) {
if (do_profiling == PROF_PAUSED) {
pause_time = profile_end(pause_time);
profile_set_wait(profile_add(profile_get_wait(), pause_time));
}
do_profiling = PROF_YES;
} else if (STRCMP(eap->arg, "dump") == 0) {
profile_dump();
} else {
// The rest is similar to ":breakadd".
ex_breakadd(eap);
}
}
void ex_ruby(exarg_T *eap)
{
script_host_execute("ruby", eap);
@ -182,134 +125,6 @@ void ex_perldo(exarg_T *eap)
script_host_do_range("perl", eap);
}
// Command line expansion for :profile.
static enum {
PEXP_SUBCMD, ///< expand :profile sub-commands
PEXP_FUNC, ///< expand :profile func {funcname}
} pexpand_what;
static char *pexpand_cmds[] = {
"continue",
"dump",
"file",
"func",
"pause",
"start",
"stop",
NULL
};
/// Function given to ExpandGeneric() to obtain the profile command
/// specific expansion.
char *get_profile_name(expand_T *xp, int idx)
FUNC_ATTR_PURE
{
switch (pexpand_what) {
case PEXP_SUBCMD:
return pexpand_cmds[idx];
// case PEXP_FUNC: TODO
default:
return NULL;
}
}
/// Handle command line completion for :profile command.
void set_context_in_profile_cmd(expand_T *xp, const char *arg)
{
// Default: expand subcommands.
xp->xp_context = EXPAND_PROFILE;
pexpand_what = PEXP_SUBCMD;
xp->xp_pattern = (char *)arg;
char_u *const end_subcmd = skiptowhite((const char_u *)arg);
if (*end_subcmd == NUL) {
return;
}
if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) {
xp->xp_context = EXPAND_FILES;
xp->xp_pattern = skipwhite((char *)end_subcmd);
return;
}
// TODO(tarruda): expand function names after "func"
xp->xp_context = EXPAND_NOTHING;
}
/// Dump the profiling info.
void profile_dump(void)
{
FILE *fd;
if (profile_fname != NULL) {
fd = os_fopen(profile_fname, "w");
if (fd == NULL) {
semsg(_(e_notopen), profile_fname);
} else {
script_dump_profile(fd);
func_dump_profile(fd);
fclose(fd);
}
}
}
/// Reset all profiling information.
static void profile_reset(void)
{
// Reset sourced files.
for (int id = 1; id <= script_items.ga_len; id++) {
scriptitem_T *si = &SCRIPT_ITEM(id);
if (si->sn_prof_on) {
si->sn_prof_on = false;
si->sn_pr_force = false;
si->sn_pr_child = profile_zero();
si->sn_pr_nest = 0;
si->sn_pr_count = 0;
si->sn_pr_total = profile_zero();
si->sn_pr_self = profile_zero();
si->sn_pr_start = profile_zero();
si->sn_pr_children = profile_zero();
ga_clear(&si->sn_prl_ga);
si->sn_prl_start = profile_zero();
si->sn_prl_children = profile_zero();
si->sn_prl_wait = profile_zero();
si->sn_prl_idx = -1;
si->sn_prl_execed = 0;
}
}
// Reset functions.
size_t n = func_hashtab.ht_used;
hashitem_T *hi = func_hashtab.ht_array;
for (; n > (size_t)0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
n--;
ufunc_T *uf = HI2UF(hi);
if (uf->uf_prof_initialized) {
uf->uf_profiling = 0;
uf->uf_tm_count = 0;
uf->uf_tm_total = profile_zero();
uf->uf_tm_self = profile_zero();
uf->uf_tm_children = profile_zero();
for (int i = 0; i < uf->uf_lines.ga_len; i++) {
uf->uf_tml_count[i] = 0;
uf->uf_tml_total[i] = uf->uf_tml_self[i] = 0;
}
uf->uf_tml_start = profile_zero();
uf->uf_tml_children = profile_zero();
uf->uf_tml_wait = profile_zero();
uf->uf_tml_idx = -1;
uf->uf_tml_execed = 0;
}
}
}
XFREE_CLEAR(profile_fname);
}
/// Start profiling a script.
static void profile_init(scriptitem_T *si)
{
@ -356,23 +171,8 @@ void script_prof_restore(proftime_T *tm)
}
}
static proftime_T inchar_time;
/// Called when starting to wait for the user to type a character.
void prof_inchar_enter(void)
{
inchar_time = profile_start();
}
/// Called when finished waiting for the user to type a character.
void prof_inchar_exit(void)
{
inchar_time = profile_end(inchar_time);
profile_set_wait(profile_add(profile_get_wait(), inchar_time));
}
/// Dump the profiling results for all scripts in file "fd".
static void script_dump_profile(FILE *fd)
void script_dump_profile(FILE *fd)
{
scriptitem_T *si;
FILE *sfd;
@ -438,17 +238,6 @@ static void script_dump_profile(FILE *fd)
}
}
/// @return true when a function defined in the current script should be
/// profiled.
bool prof_def_func(void)
FUNC_ATTR_PURE
{
if (current_sctx.sc_sid > 0) {
return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
}
return false;
}
/// If 'autowrite' option set, try to write the file.
/// Careful: autocommands may make "buf" invalid!
///

View File

@ -15,27 +15,6 @@
#define CCGD_ALLBUF 8 // may write all buffers
#define CCGD_EXCMD 16 // may suggest using !
typedef struct scriptitem_S {
char_u *sn_name;
bool sn_prof_on; ///< true when script is/was profiled
bool sn_pr_force; ///< forceit: profile functions in this script
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 spent in script + children
proftime_T sn_pr_self; ///< time spent 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
linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
int sn_prl_execed; ///< line being timed was executed
} scriptitem_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_cmds2.h.generated.h"
#endif

View File

@ -64,6 +64,7 @@
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/popupmnu.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"

View File

@ -65,6 +65,7 @@
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/popupmnu.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"

View File

@ -12,6 +12,7 @@
#include "nvim/highlight_group.h"
#include "nvim/match.h"
#include "nvim/memline.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/screen.h"

View File

@ -51,6 +51,7 @@
#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/plines.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/screen.h"
#include "nvim/search.h"

View File

@ -19,6 +19,7 @@
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/input.h"
#include "nvim/profile.h"
#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/ui.h"

View File

@ -25,6 +25,7 @@
#include "nvim/os/shell.h"
#include "nvim/os/signal.h"
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/tag.h"

View File

@ -6,11 +6,18 @@
#include <stdio.h>
#include "nvim/assert.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds2.h"
#include "nvim/func_attr.h"
#include "nvim/globals.h" // for the global `time_fd` (startuptime)
#include "nvim/os/os_defs.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/profile.h"
#include "nvim/runtime.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "profile.c.generated.h"
@ -195,6 +202,388 @@ int profile_cmp(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST
return profile_signed(tm2 - tm1) < 0 ? -1 : 1;
}
static char *profile_fname = NULL;
/// Reset all profiling information.
void profile_reset(void)
{
// Reset sourced files.
for (int id = 1; id <= script_items.ga_len; id++) {
scriptitem_T *si = &SCRIPT_ITEM(id);
if (si->sn_prof_on) {
si->sn_prof_on = false;
si->sn_pr_force = false;
si->sn_pr_child = profile_zero();
si->sn_pr_nest = 0;
si->sn_pr_count = 0;
si->sn_pr_total = profile_zero();
si->sn_pr_self = profile_zero();
si->sn_pr_start = profile_zero();
si->sn_pr_children = profile_zero();
ga_clear(&si->sn_prl_ga);
si->sn_prl_start = profile_zero();
si->sn_prl_children = profile_zero();
si->sn_prl_wait = profile_zero();
si->sn_prl_idx = -1;
si->sn_prl_execed = 0;
}
}
// Reset functions.
size_t n = func_hashtab.ht_used;
hashitem_T *hi = func_hashtab.ht_array;
for (; n > (size_t)0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
n--;
ufunc_T *uf = HI2UF(hi);
if (uf->uf_prof_initialized) {
uf->uf_profiling = 0;
uf->uf_tm_count = 0;
uf->uf_tm_total = profile_zero();
uf->uf_tm_self = profile_zero();
uf->uf_tm_children = profile_zero();
for (int i = 0; i < uf->uf_lines.ga_len; i++) {
uf->uf_tml_count[i] = 0;
uf->uf_tml_total[i] = uf->uf_tml_self[i] = 0;
}
uf->uf_tml_start = profile_zero();
uf->uf_tml_children = profile_zero();
uf->uf_tml_wait = profile_zero();
uf->uf_tml_idx = -1;
uf->uf_tml_execed = 0;
}
}
}
XFREE_CLEAR(profile_fname);
}
/// ":profile cmd args"
void ex_profile(exarg_T *eap)
{
static proftime_T pause_time;
char *e;
int len;
e = (char *)skiptowhite((char_u *)eap->arg);
len = (int)(e - eap->arg);
e = skipwhite(e);
if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) {
xfree(profile_fname);
profile_fname = (char *)expand_env_save_opt((char_u *)e, true);
do_profiling = PROF_YES;
profile_set_wait(profile_zero());
set_vim_var_nr(VV_PROFILING, 1L);
} else if (do_profiling == PROF_NONE) {
emsg(_("E750: First use \":profile start {fname}\""));
} else if (STRCMP(eap->arg, "stop") == 0) {
profile_dump();
do_profiling = PROF_NONE;
set_vim_var_nr(VV_PROFILING, 0L);
profile_reset();
} else if (STRCMP(eap->arg, "pause") == 0) {
if (do_profiling == PROF_YES) {
pause_time = profile_start();
}
do_profiling = PROF_PAUSED;
} else if (STRCMP(eap->arg, "continue") == 0) {
if (do_profiling == PROF_PAUSED) {
pause_time = profile_end(pause_time);
profile_set_wait(profile_add(profile_get_wait(), pause_time));
}
do_profiling = PROF_YES;
} else if (STRCMP(eap->arg, "dump") == 0) {
profile_dump();
} else {
// The rest is similar to ":breakadd".
ex_breakadd(eap);
}
}
/// Command line expansion for :profile.
static enum {
PEXP_SUBCMD, ///< expand :profile sub-commands
PEXP_FUNC, ///< expand :profile func {funcname}
} pexpand_what;
static char *pexpand_cmds[] = {
"continue",
"dump",
"file",
"func",
"pause",
"start",
"stop",
NULL
};
/// Function given to ExpandGeneric() to obtain the profile command
/// specific expansion.
char *get_profile_name(expand_T *xp, int idx)
FUNC_ATTR_PURE
{
switch (pexpand_what) {
case PEXP_SUBCMD:
return pexpand_cmds[idx];
// case PEXP_FUNC: TODO
default:
return NULL;
}
}
/// Handle command line completion for :profile command.
void set_context_in_profile_cmd(expand_T *xp, const char *arg)
{
// Default: expand subcommands.
xp->xp_context = EXPAND_PROFILE;
pexpand_what = PEXP_SUBCMD;
xp->xp_pattern = (char *)arg;
char_u *const end_subcmd = skiptowhite((const char_u *)arg);
if (*end_subcmd == NUL) {
return;
}
if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) {
xp->xp_context = EXPAND_FILES;
xp->xp_pattern = skipwhite((char *)end_subcmd);
return;
}
// TODO(tarruda): expand function names after "func"
xp->xp_context = EXPAND_NOTHING;
}
/// Dump the profiling info.
void profile_dump(void)
{
FILE *fd;
if (profile_fname != NULL) {
fd = os_fopen(profile_fname, "w");
if (fd == NULL) {
semsg(_(e_notopen), profile_fname);
} else {
script_dump_profile(fd);
func_dump_profile(fd);
fclose(fd);
}
}
}
static proftime_T inchar_time;
/// Called when starting to wait for the user to type a character.
void prof_inchar_enter(void)
{
inchar_time = profile_start();
}
/// Called when finished waiting for the user to type a character.
void prof_inchar_exit(void)
{
inchar_time = profile_end(inchar_time);
profile_set_wait(profile_add(profile_get_wait(), inchar_time));
}
/// @return true when a function defined in the current script should be
/// profiled.
bool prof_def_func(void)
FUNC_ATTR_PURE
{
if (current_sctx.sc_sid > 0) {
return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
}
return false;
}
/// @param prefer_self when equal print only self time
void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, int prefer_self)
{
int i;
ufunc_T *fp;
fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title);
fprintf(fd, "count total (s) self (s) function\n");
for (i = 0; i < 20 && i < st_len; i++) {
fp = sorttab[i];
prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self,
prefer_self);
if (fp->uf_name[0] == K_SPECIAL) {
fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3);
} else {
fprintf(fd, " %s()\n", fp->uf_name);
}
}
fprintf(fd, "\n");
}
/// Print the count and times for one function or function line.
///
/// @param prefer_self when equal print only self time
void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self, int prefer_self)
{
if (count > 0) {
fprintf(fd, "%5d ", count);
if (prefer_self && profile_equal(*total, *self)) {
fprintf(fd, " ");
} else {
fprintf(fd, "%s ", profile_msg(*total));
}
if (!prefer_self && profile_equal(*total, *self)) {
fprintf(fd, " ");
} else {
fprintf(fd, "%s ", profile_msg(*self));
}
} else {
fprintf(fd, " ");
}
}
/// Compare function for total time sorting.
int prof_total_cmp(const void *s1, const void *s2)
{
ufunc_T *p1 = *(ufunc_T **)s1;
ufunc_T *p2 = *(ufunc_T **)s2;
return profile_cmp(p1->uf_tm_total, p2->uf_tm_total);
}
/// Compare function for self time sorting.
int prof_self_cmp(const void *s1, const void *s2)
{
ufunc_T *p1 = *(ufunc_T **)s1;
ufunc_T *p2 = *(ufunc_T **)s2;
return profile_cmp(p1->uf_tm_self, p2->uf_tm_self);
}
/// Start profiling function "fp".
void func_do_profile(ufunc_T *fp)
{
int len = fp->uf_lines.ga_len;
if (!fp->uf_prof_initialized) {
if (len == 0) {
len = 1; // avoid getting error for allocating zero bytes
}
fp->uf_tm_count = 0;
fp->uf_tm_self = profile_zero();
fp->uf_tm_total = profile_zero();
if (fp->uf_tml_count == NULL) {
fp->uf_tml_count = xcalloc((size_t)len, sizeof(int));
}
if (fp->uf_tml_total == NULL) {
fp->uf_tml_total = xcalloc((size_t)len, sizeof(proftime_T));
}
if (fp->uf_tml_self == NULL) {
fp->uf_tml_self = xcalloc((size_t)len, sizeof(proftime_T));
}
fp->uf_tml_idx = -1;
fp->uf_prof_initialized = true;
}
fp->uf_profiling = true;
}
/// 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().
///
/// @param tm place to store waittime
void prof_child_enter(proftime_T *tm)
{
funccall_T *fc = get_current_funccal();
if (fc != NULL && fc->func->uf_profiling) {
fc->prof_child = profile_start();
}
script_prof_save(tm);
}
/// Take care of time spent in a child.
/// Should always be called after prof_child_enter().
///
/// @param tm where waittime was stored
void prof_child_exit(proftime_T *tm)
{
funccall_T *fc = get_current_funccal();
if (fc != NULL && fc->func->uf_profiling) {
fc->prof_child = profile_end(fc->prof_child);
// don't count waiting time
fc->prof_child = profile_sub_wait(*tm, fc->prof_child);
fc->func->uf_tm_children =
profile_add(fc->func->uf_tm_children, fc->prof_child);
fc->func->uf_tml_children =
profile_add(fc->func->uf_tml_children, fc->prof_child);
}
script_prof_restore(tm);
}
/// 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(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;
// Skip continuation lines.
while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) {
fp->uf_tml_idx--;
}
fp->uf_tml_execed = false;
fp->uf_tml_start = profile_start();
fp->uf_tml_children = profile_zero();
fp->uf_tml_wait = profile_get_wait();
}
}
/// Called when actually executing a function line.
void func_line_exec(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(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]++;
fp->uf_tml_start = profile_end(fp->uf_tml_start);
fp->uf_tml_start = profile_sub_wait(fp->uf_tml_wait, fp->uf_tml_start);
fp->uf_tml_total[fp->uf_tml_idx] =
profile_add(fp->uf_tml_total[fp->uf_tml_idx], fp->uf_tml_start);
fp->uf_tml_self[fp->uf_tml_idx] =
profile_self(fp->uf_tml_self[fp->uf_tml_idx], fp->uf_tml_start,
fp->uf_tml_children);
}
fp->uf_tml_idx = -1;
}
}
/// globals for use in the startuptime related functionality (time_*).
static proftime_T g_start_time;
static proftime_T g_prev_time;

View File

@ -4,7 +4,7 @@
#include <stdint.h>
#include <time.h>
typedef uint64_t proftime_T;
#include "nvim/ex_cmds_defs.h"
#define TIME_MSG(s) do { \
if (time_fd != NULL) time_msg(s, NULL); \

View File

@ -28,6 +28,7 @@
#include "nvim/message.h"
#include "nvim/os/input.h"
#include "nvim/plines.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/strings.h"
#include "nvim/vim.h"

View File

@ -15,7 +15,6 @@
#include <stdbool.h>
#include "nvim/pos.h"
#include "nvim/profile.h"
#include "nvim/types.h"
/*

View File

@ -14,6 +14,7 @@
#include "nvim/lua/executor.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/profile.h"
#include "nvim/runtime.h"
#include "nvim/vim.h"
@ -21,6 +22,8 @@
# include "runtime.c.generated.h"
#endif
garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
static bool runtime_search_path_valid = false;
static int *runtime_search_path_ref = NULL;
static RuntimeSearchPath runtime_search_path;

View File

@ -5,6 +5,38 @@
#include "nvim/ex_docmd.h"
typedef struct scriptitem_S {
char_u *sn_name;
bool sn_prof_on; ///< true when script is/was profiled
bool sn_pr_force; ///< forceit: profile functions in this script
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 spent in script + children
proftime_T sn_pr_self; ///< time spent 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
linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
int sn_prl_execed; ///< line being timed was executed
} scriptitem_T;
/// Growarray to store info about already sourced scripts.
extern garray_T script_items;
#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
/// 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 spent in a line + children
proftime_T sn_prl_self; ///< time spent in a line itself
} sn_prl_T;
typedef void (*DoInRuntimepathCB)(char *, void *);
typedef struct {

View File

@ -112,6 +112,7 @@
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/popupmnu.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"

View File

@ -43,6 +43,7 @@
#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"

View File

@ -107,6 +107,7 @@
#include "nvim/os/os.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"

View File

@ -41,6 +41,7 @@
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/sign.h"

View File

@ -28,6 +28,8 @@ typedef handle_T NS;
typedef struct expand expand_T;
typedef uint64_t proftime_T;
typedef enum {
kNone = -1,
kFalse = 0,