mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:8.1.1689: profiling code is spread out
Problem: Profiling code is spread out.
Solution: Move more profiling code to profiler.c. (Yegappan Lakshmanan,
closes vim/vim#4668)
660a10ad41
This commit is contained in:
parent
c7ca94ba7f
commit
a93d29589a
@ -9,6 +9,7 @@
|
|||||||
#include "nvim/api/vimscript.h"
|
#include "nvim/api/vimscript.h"
|
||||||
#include "nvim/context.h"
|
#include "nvim/context.h"
|
||||||
#include "nvim/eval/encode.h"
|
#include "nvim/eval/encode.h"
|
||||||
|
#include "nvim/eval/userfunc.h"
|
||||||
#include "nvim/ex_docmd.h"
|
#include "nvim/ex_docmd.h"
|
||||||
#include "nvim/option.h"
|
#include "nvim/option.h"
|
||||||
#include "nvim/shada.h"
|
#include "nvim/shada.h"
|
||||||
@ -249,7 +250,7 @@ static inline void ctx_save_funcs(Context *ctx, bool scriptonly)
|
|||||||
ctx->funcs = (Array)ARRAY_DICT_INIT;
|
ctx->funcs = (Array)ARRAY_DICT_INIT;
|
||||||
Error err = ERROR_INIT;
|
Error err = ERROR_INIT;
|
||||||
|
|
||||||
HASHTAB_ITER(&func_hashtab, hi, {
|
HASHTAB_ITER(func_tbl_get(), hi, {
|
||||||
const char_u *const name = hi->hi_key;
|
const char_u *const name = hi->hi_key;
|
||||||
bool islambda = (STRNCMP(name, "<lambda>", 8) == 0);
|
bool islambda = (STRNCMP(name, "<lambda>", 8) == 0);
|
||||||
bool isscript = (name[0] == K_SPECIAL);
|
bool isscript = (name[0] == K_SPECIAL);
|
||||||
|
@ -7906,82 +7906,6 @@ const char *find_option_end(const char **const arg, int *const opt_flags)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dump the profiling results for all functions in file "fd".
|
|
||||||
void func_dump_profile(FILE *fd)
|
|
||||||
{
|
|
||||||
hashitem_T *hi;
|
|
||||||
int todo;
|
|
||||||
ufunc_T *fp;
|
|
||||||
ufunc_T **sorttab;
|
|
||||||
int st_len = 0;
|
|
||||||
|
|
||||||
todo = (int)func_hashtab.ht_used;
|
|
||||||
if (todo == 0) {
|
|
||||||
return; // nothing to dump
|
|
||||||
}
|
|
||||||
|
|
||||||
sorttab = xmalloc(sizeof(ufunc_T *) * (size_t)todo);
|
|
||||||
|
|
||||||
for (hi = func_hashtab.ht_array; todo > 0; ++hi) {
|
|
||||||
if (!HASHITEM_EMPTY(hi)) {
|
|
||||||
--todo;
|
|
||||||
fp = HI2UF(hi);
|
|
||||||
if (fp->uf_prof_initialized) {
|
|
||||||
sorttab[st_len++] = fp;
|
|
||||||
|
|
||||||
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_script_ctx.sc_sid != 0) {
|
|
||||||
bool should_free;
|
|
||||||
const LastSet last_set = (LastSet){
|
|
||||||
.script_ctx = fp->uf_script_ctx,
|
|
||||||
.channel_id = 0,
|
|
||||||
};
|
|
||||||
char *p = (char *)get_scriptname(last_set, &should_free);
|
|
||||||
fprintf(fd, " Defined: %s:%" PRIdLINENR "\n",
|
|
||||||
p, fp->uf_script_ctx.sc_lnum);
|
|
||||||
if (should_free) {
|
|
||||||
xfree(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 (int i = 0; i < fp->uf_lines.ga_len; ++i) {
|
|
||||||
if (FUNCLINE(fp, i) == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
prof_func_line(fd, fp->uf_tml_count[i],
|
|
||||||
&fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE);
|
|
||||||
fprintf(fd, "%s\n", FUNCLINE(fp, i));
|
|
||||||
}
|
|
||||||
fprintf(fd, "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (st_len > 0) {
|
|
||||||
qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
|
|
||||||
prof_total_cmp);
|
|
||||||
prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE);
|
|
||||||
qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
|
|
||||||
prof_self_cmp);
|
|
||||||
prof_sort_list(fd, sorttab, st_len, "SELF", TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
xfree(sorttab);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the autoload script name for a function or variable name
|
/// Return the autoload script name for a function or variable name
|
||||||
/// Caller must make sure that "name" contains AUTOLOAD_CHAR.
|
/// Caller must make sure that "name" contains AUTOLOAD_CHAR.
|
||||||
///
|
///
|
||||||
|
@ -11,15 +11,6 @@
|
|||||||
#define COPYID_INC 2
|
#define COPYID_INC 2
|
||||||
#define COPYID_MASK (~0x1)
|
#define COPYID_MASK (~0x1)
|
||||||
|
|
||||||
// All user-defined functions are found in this hashtable.
|
|
||||||
extern hashtab_T func_hashtab;
|
|
||||||
|
|
||||||
// From user function to hashitem and back.
|
|
||||||
EXTERN ufunc_T dumuf;
|
|
||||||
#define UF2HIKEY(fp) ((fp)->uf_name)
|
|
||||||
#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name)))
|
|
||||||
#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Structure returned by get_lval() and used by set_var_lval().
|
* Structure returned by get_lval() and used by set_var_lval().
|
||||||
* For a plain name:
|
* For a plain name:
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
# include "eval/userfunc.c.generated.h"
|
# include "eval/userfunc.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
hashtab_T func_hashtab;
|
static hashtab_T func_hashtab;
|
||||||
|
|
||||||
// Used by get_func_tv()
|
// Used by get_func_tv()
|
||||||
static garray_T funcargs = GA_EMPTY_INIT_VALUE;
|
static garray_T funcargs = GA_EMPTY_INIT_VALUE;
|
||||||
@ -67,6 +67,12 @@ void func_init(void)
|
|||||||
hash_init(&func_hashtab);
|
hash_init(&func_hashtab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the function hash table
|
||||||
|
hashtab_T *func_tbl_get(void)
|
||||||
|
{
|
||||||
|
return &func_hashtab;
|
||||||
|
}
|
||||||
|
|
||||||
/// Get function arguments.
|
/// Get function arguments.
|
||||||
static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, int *varargs,
|
static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, int *varargs,
|
||||||
garray_T *default_args, bool skip)
|
garray_T *default_args, bool skip)
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
#include "nvim/eval/typval.h"
|
#include "nvim/eval/typval.h"
|
||||||
#include "nvim/ex_cmds_defs.h"
|
#include "nvim/ex_cmds_defs.h"
|
||||||
|
|
||||||
|
// From user function to hashitem and back.
|
||||||
|
#define UF2HIKEY(fp) ((fp)->uf_name)
|
||||||
|
#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name)))
|
||||||
|
#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
|
||||||
|
|
||||||
///< Structure used by trans_function_name()
|
///< Structure used by trans_function_name()
|
||||||
typedef struct {
|
typedef struct {
|
||||||
dict_T *fd_dict; ///< Dictionary used.
|
dict_T *fd_dict; ///< Dictionary used.
|
||||||
|
@ -74,8 +74,6 @@ struct source_cookie {
|
|||||||
vimconv_T conv; ///< type of conversion
|
vimconv_T conv; ///< type of conversion
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
|
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ex_cmds2.c.generated.h"
|
# include "ex_cmds2.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
@ -125,119 +123,6 @@ void ex_perldo(exarg_T *eap)
|
|||||||
script_host_do_range("perl", eap);
|
script_host_do_range("perl", eap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start profiling a script.
|
|
||||||
static void profile_init(scriptitem_T *si)
|
|
||||||
{
|
|
||||||
si->sn_pr_count = 0;
|
|
||||||
si->sn_pr_total = profile_zero();
|
|
||||||
si->sn_pr_self = profile_zero();
|
|
||||||
|
|
||||||
ga_init(&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.
|
|
||||||
///
|
|
||||||
/// @param tm place to store wait time
|
|
||||||
void script_prof_save(proftime_T *tm)
|
|
||||||
{
|
|
||||||
scriptitem_T *si;
|
|
||||||
|
|
||||||
if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
|
|
||||||
si = &SCRIPT_ITEM(current_sctx.sc_sid);
|
|
||||||
if (si->sn_prof_on && si->sn_pr_nest++ == 0) {
|
|
||||||
si->sn_pr_child = profile_start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*tm = profile_get_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Count time spent in children after invoking another script or function.
|
|
||||||
void script_prof_restore(proftime_T *tm)
|
|
||||||
{
|
|
||||||
scriptitem_T *si;
|
|
||||||
|
|
||||||
if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
|
|
||||||
si = &SCRIPT_ITEM(current_sctx.sc_sid);
|
|
||||||
if (si->sn_prof_on && --si->sn_pr_nest == 0) {
|
|
||||||
si->sn_pr_child = profile_end(si->sn_pr_child);
|
|
||||||
// don't count wait time
|
|
||||||
si->sn_pr_child = profile_sub_wait(*tm, si->sn_pr_child);
|
|
||||||
si->sn_pr_children = profile_add(si->sn_pr_children, si->sn_pr_child);
|
|
||||||
si->sn_prl_children = profile_add(si->sn_prl_children, si->sn_pr_child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dump the profiling results for all scripts in file "fd".
|
|
||||||
void script_dump_profile(FILE *fd)
|
|
||||||
{
|
|
||||||
scriptitem_T *si;
|
|
||||||
FILE *sfd;
|
|
||||||
sn_prl_T *pp;
|
|
||||||
|
|
||||||
for (int 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 = os_fopen((char *)si->sn_name, "r");
|
|
||||||
if (sfd == NULL) {
|
|
||||||
fprintf(fd, "Cannot open file!\n");
|
|
||||||
} else {
|
|
||||||
// Keep going till the end of file, so that trailing
|
|
||||||
// continuation lines are listed.
|
|
||||||
for (int i = 0;; i++) {
|
|
||||||
if (vim_fgets(IObuff, IOSIZE, sfd)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// When a line has been truncated, append NL, taking care
|
|
||||||
// of multi-byte characters .
|
|
||||||
if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) {
|
|
||||||
int n = IOSIZE - 2;
|
|
||||||
|
|
||||||
// Move to the first byte of this char.
|
|
||||||
// utf_head_off() doesn't work, because it checks
|
|
||||||
// for a truncated character.
|
|
||||||
while (n > 0 && (IObuff[n] & 0xc0) == 0x80) {
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
|
|
||||||
IObuff[n] = NL;
|
|
||||||
IObuff[n + 1] = NUL;
|
|
||||||
}
|
|
||||||
if (i < si->sn_prl_ga.ga_len
|
|
||||||
&& (pp = &PRL_ITEM(si, i))->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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If 'autowrite' option set, try to write the file.
|
/// If 'autowrite' option set, try to write the file.
|
||||||
/// Careful: autocommands may make "buf" invalid!
|
/// Careful: autocommands may make "buf" invalid!
|
||||||
///
|
///
|
||||||
@ -2257,79 +2142,6 @@ retry:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(void)
|
|
||||||
{
|
|
||||||
scriptitem_T *si;
|
|
||||||
sn_prl_T *pp;
|
|
||||||
|
|
||||||
if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
si = &SCRIPT_ITEM(current_sctx.sc_sid);
|
|
||||||
if (si->sn_prof_on && sourcing_lnum >= 1) {
|
|
||||||
// Grow the array before starting the timer, so that the time spent
|
|
||||||
// here isn't counted.
|
|
||||||
(void)ga_grow(&si->sn_prl_ga, 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;
|
|
||||||
pp->sn_prl_total = profile_zero();
|
|
||||||
pp->sn_prl_self = profile_zero();
|
|
||||||
si->sn_prl_ga.ga_len++;
|
|
||||||
}
|
|
||||||
si->sn_prl_execed = false;
|
|
||||||
si->sn_prl_start = profile_start();
|
|
||||||
si->sn_prl_children = profile_zero();
|
|
||||||
si->sn_prl_wait = profile_get_wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when actually executing a function line.
|
|
||||||
void script_line_exec(void)
|
|
||||||
{
|
|
||||||
scriptitem_T *si;
|
|
||||||
|
|
||||||
if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
si = &SCRIPT_ITEM(current_sctx.sc_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(void)
|
|
||||||
{
|
|
||||||
scriptitem_T *si;
|
|
||||||
sn_prl_T *pp;
|
|
||||||
|
|
||||||
if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
si = &SCRIPT_ITEM(current_sctx.sc_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++;
|
|
||||||
si->sn_prl_start = profile_end(si->sn_prl_start);
|
|
||||||
si->sn_prl_start = profile_sub_wait(si->sn_prl_wait, si->sn_prl_start);
|
|
||||||
pp->sn_prl_total = profile_add(pp->sn_prl_total, si->sn_prl_start);
|
|
||||||
pp->sn_prl_self = profile_self(pp->sn_prl_self, si->sn_prl_start,
|
|
||||||
si->sn_prl_children);
|
|
||||||
}
|
|
||||||
si->sn_prl_idx = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ":scriptencoding": Set encoding conversion for a sourced script.
|
/// ":scriptencoding": Set encoding conversion for a sourced script.
|
||||||
/// Without the multi-byte feature it's simply ignored.
|
/// Without the multi-byte feature it's simply ignored.
|
||||||
void ex_scriptencoding(exarg_T *eap)
|
void ex_scriptencoding(exarg_T *eap)
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "nvim/eval.h"
|
#include "nvim/eval.h"
|
||||||
#include "nvim/eval/userfunc.h"
|
#include "nvim/eval/userfunc.h"
|
||||||
#include "nvim/ex_cmds2.h"
|
#include "nvim/ex_cmds2.h"
|
||||||
|
#include "nvim/fileio.h"
|
||||||
#include "nvim/func_attr.h"
|
#include "nvim/func_attr.h"
|
||||||
#include "nvim/globals.h" // for the global `time_fd` (startuptime)
|
#include "nvim/globals.h" // for the global `time_fd` (startuptime)
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
@ -23,6 +24,15 @@
|
|||||||
# include "profile.c.generated.h"
|
# include "profile.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
#define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
|
||||||
|
|
||||||
static proftime_T prof_wait_time;
|
static proftime_T prof_wait_time;
|
||||||
|
|
||||||
/// Gets the current time.
|
/// Gets the current time.
|
||||||
@ -230,12 +240,13 @@ void profile_reset(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset functions.
|
// Reset functions.
|
||||||
size_t n = func_hashtab.ht_used;
|
hashtab_T *const functbl = func_tbl_get();
|
||||||
hashitem_T *hi = func_hashtab.ht_array;
|
size_t todo = functbl->ht_used;
|
||||||
|
hashitem_T *hi = functbl->ht_array;
|
||||||
|
|
||||||
for (; n > (size_t)0; hi++) {
|
for (; todo > (size_t)0; hi++) {
|
||||||
if (!HASHITEM_EMPTY(hi)) {
|
if (!HASHITEM_EMPTY(hi)) {
|
||||||
n--;
|
todo--;
|
||||||
ufunc_T *uf = HI2UF(hi);
|
ufunc_T *uf = HI2UF(hi);
|
||||||
if (uf->uf_prof_initialized) {
|
if (uf->uf_prof_initialized) {
|
||||||
uf->uf_profiling = 0;
|
uf->uf_profiling = 0;
|
||||||
@ -359,23 +370,6 @@ void set_context_in_profile_cmd(expand_T *xp, const char *arg)
|
|||||||
xp->xp_context = EXPAND_NOTHING;
|
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;
|
static proftime_T inchar_time;
|
||||||
|
|
||||||
/// Called when starting to wait for the user to type a character.
|
/// Called when starting to wait for the user to type a character.
|
||||||
@ -402,8 +396,31 @@ bool prof_def_func(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print the count and times for one function or function line.
|
||||||
|
///
|
||||||
/// @param prefer_self when equal print only self time
|
/// @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)
|
static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self,
|
||||||
|
bool 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, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @param prefer_self when equal print only self time
|
||||||
|
static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, bool prefer_self)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
ufunc_T *fp;
|
ufunc_T *fp;
|
||||||
@ -423,30 +440,8 @@ void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, int pr
|
|||||||
fprintf(fd, "\n");
|
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.
|
/// Compare function for total time sorting.
|
||||||
int prof_total_cmp(const void *s1, const void *s2)
|
static int prof_total_cmp(const void *s1, const void *s2)
|
||||||
{
|
{
|
||||||
ufunc_T *p1 = *(ufunc_T **)s1;
|
ufunc_T *p1 = *(ufunc_T **)s1;
|
||||||
ufunc_T *p2 = *(ufunc_T **)s2;
|
ufunc_T *p2 = *(ufunc_T **)s2;
|
||||||
@ -454,7 +449,7 @@ int prof_total_cmp(const void *s1, const void *s2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compare function for self time sorting.
|
/// Compare function for self time sorting.
|
||||||
int prof_self_cmp(const void *s1, const void *s2)
|
static int prof_self_cmp(const void *s1, const void *s2)
|
||||||
{
|
{
|
||||||
ufunc_T *p1 = *(ufunc_T **)s1;
|
ufunc_T *p1 = *(ufunc_T **)s1;
|
||||||
ufunc_T *p2 = *(ufunc_T **)s2;
|
ufunc_T *p2 = *(ufunc_T **)s2;
|
||||||
@ -584,6 +579,286 @@ void func_line_end(void *cookie)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dump the profiling results for all functions in file "fd".
|
||||||
|
static void func_dump_profile(FILE *fd)
|
||||||
|
{
|
||||||
|
hashtab_T *const functbl = func_tbl_get();
|
||||||
|
hashitem_T *hi;
|
||||||
|
int todo;
|
||||||
|
ufunc_T *fp;
|
||||||
|
ufunc_T **sorttab;
|
||||||
|
int st_len = 0;
|
||||||
|
|
||||||
|
todo = (int)functbl->ht_used;
|
||||||
|
if (todo == 0) {
|
||||||
|
return; // nothing to dump
|
||||||
|
}
|
||||||
|
|
||||||
|
sorttab = xmalloc(sizeof(ufunc_T *) * (size_t)todo);
|
||||||
|
|
||||||
|
for (hi = functbl->ht_array; todo > 0; hi++) {
|
||||||
|
if (!HASHITEM_EMPTY(hi)) {
|
||||||
|
todo--;
|
||||||
|
fp = HI2UF(hi);
|
||||||
|
if (fp->uf_prof_initialized) {
|
||||||
|
sorttab[st_len++] = fp;
|
||||||
|
|
||||||
|
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_script_ctx.sc_sid != 0) {
|
||||||
|
bool should_free;
|
||||||
|
const LastSet last_set = (LastSet){
|
||||||
|
.script_ctx = fp->uf_script_ctx,
|
||||||
|
.channel_id = 0,
|
||||||
|
};
|
||||||
|
char *p = (char *)get_scriptname(last_set, &should_free);
|
||||||
|
fprintf(fd, " Defined: %s:%" PRIdLINENR "\n",
|
||||||
|
p, fp->uf_script_ctx.sc_lnum);
|
||||||
|
if (should_free) {
|
||||||
|
xfree(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 (int i = 0; i < fp->uf_lines.ga_len; i++) {
|
||||||
|
if (FUNCLINE(fp, i) == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
prof_func_line(fd, fp->uf_tml_count[i],
|
||||||
|
&fp->uf_tml_total[i], &fp->uf_tml_self[i], true);
|
||||||
|
fprintf(fd, "%s\n", FUNCLINE(fp, i));
|
||||||
|
}
|
||||||
|
fprintf(fd, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (st_len > 0) {
|
||||||
|
qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
|
||||||
|
prof_total_cmp);
|
||||||
|
prof_sort_list(fd, sorttab, st_len, "TOTAL", false);
|
||||||
|
qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
|
||||||
|
prof_self_cmp);
|
||||||
|
prof_sort_list(fd, sorttab, st_len, "SELF", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
xfree(sorttab);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start profiling a script.
|
||||||
|
void profile_init(scriptitem_T *si)
|
||||||
|
{
|
||||||
|
si->sn_pr_count = 0;
|
||||||
|
si->sn_pr_total = profile_zero();
|
||||||
|
si->sn_pr_self = profile_zero();
|
||||||
|
|
||||||
|
ga_init(&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.
|
||||||
|
///
|
||||||
|
/// @param tm place to store wait time
|
||||||
|
void script_prof_save(proftime_T *tm)
|
||||||
|
{
|
||||||
|
scriptitem_T *si;
|
||||||
|
|
||||||
|
if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
|
||||||
|
si = &SCRIPT_ITEM(current_sctx.sc_sid);
|
||||||
|
if (si->sn_prof_on && si->sn_pr_nest++ == 0) {
|
||||||
|
si->sn_pr_child = profile_start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*tm = profile_get_wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Count time spent in children after invoking another script or function.
|
||||||
|
void script_prof_restore(proftime_T *tm)
|
||||||
|
{
|
||||||
|
scriptitem_T *si;
|
||||||
|
|
||||||
|
if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
|
||||||
|
si = &SCRIPT_ITEM(current_sctx.sc_sid);
|
||||||
|
if (si->sn_prof_on && --si->sn_pr_nest == 0) {
|
||||||
|
si->sn_pr_child = profile_end(si->sn_pr_child);
|
||||||
|
// don't count wait time
|
||||||
|
si->sn_pr_child = profile_sub_wait(*tm, si->sn_pr_child);
|
||||||
|
si->sn_pr_children = profile_add(si->sn_pr_children, si->sn_pr_child);
|
||||||
|
si->sn_prl_children = profile_add(si->sn_prl_children, si->sn_pr_child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dump the profiling results for all scripts in file "fd".
|
||||||
|
static void script_dump_profile(FILE *fd)
|
||||||
|
{
|
||||||
|
scriptitem_T *si;
|
||||||
|
FILE *sfd;
|
||||||
|
sn_prl_T *pp;
|
||||||
|
|
||||||
|
for (int 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 = os_fopen((char *)si->sn_name, "r");
|
||||||
|
if (sfd == NULL) {
|
||||||
|
fprintf(fd, "Cannot open file!\n");
|
||||||
|
} else {
|
||||||
|
// Keep going till the end of file, so that trailing
|
||||||
|
// continuation lines are listed.
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
if (vim_fgets(IObuff, IOSIZE, sfd)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// When a line has been truncated, append NL, taking care
|
||||||
|
// of multi-byte characters .
|
||||||
|
if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) {
|
||||||
|
int n = IOSIZE - 2;
|
||||||
|
|
||||||
|
// Move to the first byte of this char.
|
||||||
|
// utf_head_off() doesn't work, because it checks
|
||||||
|
// for a truncated character.
|
||||||
|
while (n > 0 && (IObuff[n] & 0xc0) == 0x80) {
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
|
||||||
|
IObuff[n] = NL;
|
||||||
|
IObuff[n + 1] = NUL;
|
||||||
|
}
|
||||||
|
if (i < si->sn_prl_ga.ga_len
|
||||||
|
&& (pp = &PRL_ITEM(si, i))->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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(void)
|
||||||
|
{
|
||||||
|
scriptitem_T *si;
|
||||||
|
sn_prl_T *pp;
|
||||||
|
|
||||||
|
if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
si = &SCRIPT_ITEM(current_sctx.sc_sid);
|
||||||
|
if (si->sn_prof_on && sourcing_lnum >= 1) {
|
||||||
|
// Grow the array before starting the timer, so that the time spent
|
||||||
|
// here isn't counted.
|
||||||
|
(void)ga_grow(&si->sn_prl_ga, 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;
|
||||||
|
pp->sn_prl_total = profile_zero();
|
||||||
|
pp->sn_prl_self = profile_zero();
|
||||||
|
si->sn_prl_ga.ga_len++;
|
||||||
|
}
|
||||||
|
si->sn_prl_execed = false;
|
||||||
|
si->sn_prl_start = profile_start();
|
||||||
|
si->sn_prl_children = profile_zero();
|
||||||
|
si->sn_prl_wait = profile_get_wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when actually executing a function line.
|
||||||
|
void script_line_exec(void)
|
||||||
|
{
|
||||||
|
scriptitem_T *si;
|
||||||
|
|
||||||
|
if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
si = &SCRIPT_ITEM(current_sctx.sc_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(void)
|
||||||
|
{
|
||||||
|
scriptitem_T *si;
|
||||||
|
sn_prl_T *pp;
|
||||||
|
|
||||||
|
if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
si = &SCRIPT_ITEM(current_sctx.sc_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++;
|
||||||
|
si->sn_prl_start = profile_end(si->sn_prl_start);
|
||||||
|
si->sn_prl_start = profile_sub_wait(si->sn_prl_wait, si->sn_prl_start);
|
||||||
|
pp->sn_prl_total = profile_add(pp->sn_prl_total, si->sn_prl_start);
|
||||||
|
pp->sn_prl_self = profile_self(pp->sn_prl_self, si->sn_prl_start,
|
||||||
|
si->sn_prl_children);
|
||||||
|
}
|
||||||
|
si->sn_prl_idx = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// globals for use in the startuptime related functionality (time_*).
|
/// globals for use in the startuptime related functionality (time_*).
|
||||||
static proftime_T g_start_time;
|
static proftime_T g_start_time;
|
||||||
static proftime_T g_prev_time;
|
static proftime_T g_prev_time;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "nvim/ex_cmds_defs.h"
|
#include "nvim/ex_cmds_defs.h"
|
||||||
|
#include "nvim/runtime.h"
|
||||||
|
|
||||||
#define TIME_MSG(s) do { \
|
#define TIME_MSG(s) do { \
|
||||||
if (time_fd != NULL) time_msg(s, NULL); \
|
if (time_fd != NULL) time_msg(s, NULL); \
|
||||||
|
@ -30,13 +30,6 @@ typedef struct scriptitem_S {
|
|||||||
extern garray_T script_items;
|
extern garray_T script_items;
|
||||||
#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
|
#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 void (*DoInRuntimepathCB)(char *, void *);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user