Trace2 tracing facility
Subsystems (3)
cover Trace2 tracing facility
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
A new design doc in Documentation/technical/api-trace2.txt (in the first commit) explains the relationship of Trace2 to the current tracing facility. Calls to the current tracing facility have not been changed, rather new trace2 calls have been added so that both continue to work in parallel for the time being.
[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (1):
pack-objects: add trace2 regions
Jeff Hostetler (13):
trace2: Documentation/technical/api-trace2.txt
trace2: create new combined trace facility
trace2: collect platform-specific process information trace2:data: add trace2 regions to wt-status trace2:data: add editor/pager child classification trace2:data: add trace2 sub-process classification trace2:data: add trace2 transport child classification trace2:data: add trace2 hook classification trace2:data: add trace2 instrumentation to index read/write trace2:data: add subverb to checkout command trace2:data: add subverb to reset command trace2:data: add subverb for rebase
trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
Documentation/technical/api-trace2.txt | 1158 ++++++++++++++++++++++++ Makefile | 11 + builtin/am.c | 1 + builtin/checkout.c | 7 + builtin/pack-objects.c | 12 +- builtin/rebase.c | 19 + builtin/receive-pack.c | 4 + builtin/reset.c | 6 + builtin/submodule--helper.c | 11 +- builtin/worktree.c | 1 + cache.h | 1 + common-main.c | 13 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- compat/win32/ancestry.c | 102 +++ config.c | 2 + config.mak.uname | 2 + connect.c | 3 + editor.c | 1 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 ++ pager.c | 1 + read-cache.c | 47 +- remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 63 +- run-command.h | 17 +- sequencer.c | 2 + sh-i18n--envsubst.c | 3 + sub-process.c | 1 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 4 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 273 ++++++ t/t0001-init.sh | 1 + t/t0210-trace2-normal.sh | 135 +++ t/t0210/scrub_normal.perl | 48 + t/t0211-trace2-perf.sh | 153 ++++ t/t0211/scrub_perf.perl | 76 ++ t/t0212-trace2-event.sh | 237 +++++ t/t0212/parse_events.perl | 251 +++++ trace2.c | 809 +++++++++++++++++ trace2.h | 403 +++++++++ trace2/tr2_cfg.c | 92 ++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 90 ++ trace2/tr2_dst.h | 34 + trace2/tr2_sid.c | 67 ++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 + trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 126 +++ trace2/tr2_tgt_event.c | 606 +++++++++++++ trace2/tr2_tgt_normal.c | 331 +++++++ trace2/tr2_tgt_perf.c | 573 ++++++++++++ trace2/tr2_tls.c | 164 ++++ trace2/tr2_tls.h | 95 ++ trace2/tr2_verb.c | 30 + trace2/tr2_verb.h | 24 + transport-helper.c | 2 + transport.c | 1 + usage.c | 31 + wt-status.c | 23 +- 66 files changed, 6353 insertions(+), 21 deletions(-) create mode 100644 Documentation/technical/api-trace2.txt create mode 100644 compat/win32/ancestry.c create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
base-commit: 77556354bb7ac50450e3b28999e3576969869068
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-108%2Fjeffhostetler%2Fcore-trace2-2019-v0-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-108/jeffhostetler/core-trace2-2019-v0-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/108
Signature · 1 line
gitgitgadget
PATCH 4/14 trace2:data: add trace2 regions to wt-status
From: Jeff Hostetler <[email protected]>
Add trace2_region_enter() and trace2_region_leave() calls around the various phases of a status scan. This gives elapsed time for each phase in the GIT_TR2_PERF and GIT_TR2_EVENT trace target.
Also, these Trace2 calls now use s->repo rather than the_repository.
--- wt-status.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
wt-status.c +21 -2
@@ -748,12 +748,23 @@ static int has_unmerged(struct wt_status *s)void wt_status_collect (struct wt_status *s) { +trace2_region_enter ("status" ,"worktrees" , s->repo);wt_status_collect_changes_worktree (s); -if (s->is_initial) +trace2_region_leave ("status" ,"worktrees" , s->repo); + +if (s->is_initial) { +trace2_region_enter ("status" ,"initial" , s->repo);wt_status_collect_changes_initial (s); -else +trace2_region_leave ("status" ,"initial" , s->repo); + }else { +trace2_region_enter ("status" ,"index" , s->repo);wt_status_collect_changes_index (s); +trace2_region_leave ("status" ,"index" , s->repo); + } + +trace2_region_enter ("status" ,"untracked" , s->repo);wt_status_collect_untracked (s); +trace2_region_leave ("status" ,"untracked" , s->repo);wt_status_get_state (s->repo, &s->state, s->branch && !strcmp (s->branch,"HEAD" ));if (s->state.merge_in_progress && !has_unmerged (s))
@@ -2291,6 +2302,12 @@ static void wt_porcelain_v2_print(struct wt_status *s)void wt_status_print (struct wt_status *s) { +trace2_data_intmax ("status" , s->repo,"count/changed" , s->change.nr); +trace2_data_intmax ("status" , s->repo,"count/untracked" , s->untracked.nr); +trace2_data_intmax ("status" , s->repo,"count/ignored" , s->ignored.nr); + +trace2_region_enter ("status" ,"print" , s->repo); +switch (s->status_format) {case STATUS_FORMAT_SHORT :wt_shortstatus_print (s);
@@ -2309,6 +2326,8 @@ void wt_status_print(struct wt_status *s)wt_longstatus_print (s);break ; } + +trace2_region_leave ("status" ,"print" , s->repo); }/**
Signature · 1 line
gitgitgadget
PATCH 3/14 trace2: collect platform-specific process information
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add optional platform-specific code to log information about the current process.
On Windows, this includes whether git.exe is running under a debugger and information about the ancestors of the process.
The purpose of this information is to help indicate if the process was launched interactively or in the background by an IDE, for example.
--- common-main.c | 1 + compat/win32/ancestry.c | 102 ++++++++++++++++++++++++++++++++++++++++ config.mak.uname | 2 + trace2.h | 13 +++++ 4 files changed, 118 insertions(+) create mode 100644 compat/win32/ancestry.c
common-main.c +1 -0
@@ -37,6 +37,7 @@ int main(int argc, const char **argv)trace2_initialize ();trace2_cmd_start (argv); +trace2_collect_process_info ();git_resolve_executable_dir (argv[0 ]);
compat/win32/ancestry.c new file +102 -0
102 lines not on this page · raw message
config.mak.uname +2 -0
@@ -391,6 +391,7 @@ ifeq ($(uname_S),Windows) BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE COMPAT_OBJS = compat/msvc.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ + compat/win32/ancestry.o \ compat/win32/dirent.o COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
@@ -528,6 +529,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ + compat/win32/ancestry.o \ compat/win32/path-utils.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o
trace2.h +13 -0
@@ -387,4 +387,17 @@ __attribute__((format (printf, 1, 2)))void trace2_printf (const char *fmt, ...);#endif +/* +* Optional platform-specific code to dump information about the +* current and any parent process(es). This is intended to allow +* post-processors to know who spawned this git instance and anything +* else the platform may be able to tell us about the current process. +*/ +#if defined(GIT_WINDOWS_NATIVE ) +void trace2_collect_process_info (void ); +#else +#define trace2_collect_process_info () \ + do {} while (0) +#endif +#endif /* TRACE2_H */
Signature · 1 line
gitgitgadget
PATCH 7/14 trace2:data: add trace2 transport child classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 child classification for transport processes.
--- connect.c | 3 +++ transport-helper.c | 2 ++ 2 files changed, 5 insertions(+)
connect.c +3 -0
@@ -1251,6 +1251,7 @@ struct child_process *git_connect(int fd[2], const char *url, conn =NULL ; }else if (protocol ==PROTO_GIT ) { conn =git_connect_git (fd, hostandport, path, prog, version, flags); + conn->trace2_child_class ="transport/git" ; }else {struct strbuf cmd =STRBUF_INIT ;const char *const *var;
@@ -1293,9 +1294,11 @@ struct child_process *git_connect(int fd[2], const char *url,strbuf_release (&cmd);return NULL ; } + conn->trace2_child_class ="transport/ssh" ;fill_ssh_args (conn, ssh_host, port, version, flags); }else {transport_check_allowed ("file" ); + conn->trace2_child_class ="transport/file" ;if (version >0 ) {argv_array_pushf (&conn->env_array,GIT_PROTOCOL_ENVIRONMENT "=version=%d" , version);
transport-helper.c +2 -0
@@ -127,6 +127,8 @@ static struct child_process *get_helper(struct transport *transport)argv_array_pushf (&helper->env_array,"%s=%s" ,GIT_DIR_ENVIRONMENT ,get_git_dir ()); + helper->trace2_child_class = helper->args.argv[0 ];/* "remote-<name>" */ + code =start_command (helper);if (code <0 && errno ==ENOENT )die (_ ("unable to find remote helper for '%s'" ), data->name);
Signature · 1 line
gitgitgadget
PATCH 5/14 trace2:data: add editor/pager child classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 process classification for editor and pager child processes.
--- editor.c | 1 + pager.c | 1 + 2 files changed, 2 insertions(+)
editor.c +1 -0
@@ -78,6 +78,7 @@ static int launch_specified_editor(const char *editor, const char *path, p.argv = args; p.env = env; p.use_shell =1 ; + p.trace2_child_class ="editor" ;if (start_command (&p) <0 )return error ("unable to start editor '%s'" , editor);
pager.c +1 -0
@@ -100,6 +100,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)argv_array_push (&pager_process->args, pager); pager_process->use_shell =1 ;setup_pager_env (&pager_process->env_array); + pager_process->trace2_child_class ="pager" ; }void setup_pager (void )
Signature · 1 line
gitgitgadget
PATCH 8/14 trace2:data: add trace2 hook classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Classify certain child processes as hooks.
--- builtin/am.c | 1 + builtin/receive-pack.c | 4 ++++ builtin/worktree.c | 1 + sequencer.c | 2 ++ transport.c | 1 + 5 files changed, 9 insertions(+)
builtin/am.c +1 -0
@@ -468,6 +468,7 @@ static int run_post_rewrite_hook(const struct am_state *state) cp.in =xopen (am_path (state,"rewritten" ),O_RDONLY ); cp.stdout_to_stderr =1 ; + cp.trace2_hook_name ="post-rewrite" ; ret =run_command (&cp);
builtin/receive-pack.c +4 -0
@@ -694,6 +694,8 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, proc.argv = argv; proc.in =-1 ; proc.stdout_to_stderr =1 ; + proc.trace2_hook_name = hook_name; +if (feed_state->push_options) {int i;for (i =0 ; i < feed_state->push_options->nr; i++)
@@ -807,6 +809,7 @@ static int run_update_hook(struct command *cmd) proc.stdout_to_stderr =1 ; proc.err = use_sideband ?-1 :0 ; proc.argv = argv; + proc.trace2_hook_name ="update" ; code =start_command (&proc);if (code)
@@ -1190,6 +1193,7 @@ static void run_update_post_hook(struct command *commands) proc.no_stdin =1 ; proc.stdout_to_stderr =1 ; proc.err = use_sideband ?-1 :0 ; + proc.trace2_hook_name ="post-update" ;if (!start_command (&proc)) {if (use_sideband)
builtin/worktree.c +1 -0
@@ -401,6 +401,7 @@ static int add_worktree(const char *path, const char *refname, cp.dir = path; cp.env = env; cp.argv =NULL ; + cp.trace2_hook_name ="post-checkout" ; argv_array_pushl(&cp.args, absolute_path(hook), oid_to_hex(&null_oid), oid_to_hex(&commit->object.oid),
sequencer.c +2 -0
@@ -1102,6 +1102,7 @@ static int run_rewrite_hook(const struct object_id *oldoid, proc.argv = argv; proc.in =-1 ; proc.stdout_to_stderr =1 ; + proc.trace2_hook_name ="post-rewrite" ; code =start_command (&proc);if (code)
@@ -3771,6 +3772,7 @@ static int pick_commits(struct repository *r, hook.in =open (rebase_path_rewritten_list (),O_RDONLY ); hook.stdout_to_stderr =1 ; + hook.trace2_hook_name ="post-rewrite" ;argv_array_push (&hook.args, post_rewrite_hook);argv_array_push (&hook.args,"rebase" );/* we don't care if this hook failed */
transport.c +1 -0
@@ -1061,6 +1061,7 @@ static int run_pre_push_hook(struct transport *transport, proc.argv = argv; proc.in =-1 ; + proc.trace2_hook_name ="pre-push" ;if (start_command (&proc)) {finish_command (&proc);
Signature · 1 line
gitgitgadget
PATCH 2/14 trace2: create new combined trace facility
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Create a new unified tracing facility for git. The eventual intent is to replace the current trace_printf* and trace_performance* routines with a unified set of git_trace2* routines.
In addition to the usual printf-style API, trace2 provides higer-level event verbs with fixed-fields allowing structured data to be written. This makes post-processing and analysis easier for external tools.
Trace2 defines 3 output targets. These are set using the environment variables "GIT_TR2", "GIT_TR2_PERF", and "GIT_TR2_EVENT". These may be set to "1" or to an absolute pathname (just like the current GIT_TRACE).
* GIT_TR2 is intended to be a replacement for GIT_TRACE and logs command summary data.
* GIT_TR2_PERF is intended as a replacement for GIT_TRACE_PERFORMANCE. It extends the output with columns for the command process, thread, repo, absolute and relative elapsed times. It reports events for child process start/stop, thread start/stop, and per-thread function nesting.
* GIT_TR2_EVENT is a new structured format. It writes event data as a series of JSON records.
Calls to trace2 functions log to any of the 3 output targets enabled without the need to call different trace_printf* or trace_performance* routines.
--- Makefile | 10 + builtin/submodule--helper.c | 11 +- cache.h | 1 + common-main.c | 12 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- config.c | 2 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 +++ remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 63 ++- run-command.h | 17 +- sh-i18n--envsubst.c | 3 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 3 + t/t0001-init.sh | 1 + trace2.c | 809 ++++++++++++++++++++++++++++++++++ trace2.h | 390 ++++++++++++++++ trace2/tr2_cfg.c | 92 ++++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 90 ++++ trace2/tr2_dst.h | 34 ++ trace2/tr2_sid.c | 67 +++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 ++ trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 126 ++++++ trace2/tr2_tgt_event.c | 606 +++++++++++++++++++++++++ trace2/tr2_tgt_normal.c | 331 ++++++++++++++ trace2/tr2_tgt_perf.c | 573 ++++++++++++++++++++++++ trace2/tr2_tls.c | 164 +++++++ trace2/tr2_tls.h | 95 ++++ trace2/tr2_verb.c | 30 ++ trace2/tr2_verb.h | 24 + usage.c | 31 ++ 39 files changed, 3774 insertions(+), 17 deletions(-) create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
Makefile +10 -0
@@ -996,6 +996,16 @@ LIB_OBJS += tempfile.o LIB_OBJS += thread-utils.o LIB_OBJS += tmp-objdir.o LIB_OBJS += trace.o +LIB_OBJS += trace2.o +LIB_OBJS += trace2/tr2_cfg.o +LIB_OBJS += trace2/tr2_dst.o +LIB_OBJS += trace2/tr2_sid.o +LIB_OBJS += trace2/tr2_tbuf.o +LIB_OBJS += trace2/tr2_tgt_event.o +LIB_OBJS += trace2/tr2_tgt_normal.o +LIB_OBJS += trace2/tr2_tgt_perf.o +LIB_OBJS += trace2/tr2_tls.o +LIB_OBJS += trace2/tr2_verb.o LIB_OBJS += trailer.o LIB_OBJS += transport.o LIB_OBJS += transport-helper.o
builtin/submodule--helper.c +6 -5
@@ -1813,11 +1813,12 @@ static int update_submodules(struct submodule_update_clone *suc) {int i; -run_processes_parallel (suc->max_jobs, - update_clone_get_next_task, - update_clone_start_failure, - update_clone_task_finished, - suc); +run_processes_parallel_tr2 (suc->max_jobs, + update_clone_get_next_task, + update_clone_start_failure, + update_clone_task_finished, + suc, +"submodule" ,"parallel/update" );/* * We saved the output and put it out all at once now.
cache.h +1 -0
@@ -9,6 +9,7 @@#include "gettext.h" #include "convert.h" #include "trace.h" +#include "trace2.h" #include "string-list.h" #include "pack-revindex.h" #include "hash.h"
common-main.c +10 -2
@@ -25,12 +25,18 @@ static void restore_sigpipe_to_default(void)int main (int argc,const char **argv) { +int result; +/* * Always open file descriptors 0/1/2 to avoid clobbering files * in die(). It also avoids messing up when the pipes are dup'ed * onto stdin/stdout/stderr in the child processes we spawn. */ sanitize_stdfds (); +restore_sigpipe_to_default (); + +trace2_initialize (); +trace2_cmd_start (argv);git_resolve_executable_dir (argv[0 ]);
@@ -40,7 +46,9 @@ int main(int argc, const char **argv)attr_start (); -restore_sigpipe_to_default (); + result =cmd_main (argc, argv); + +trace2_cmd_exit (result); -return cmd_main (argc, argv); +return result; }
compat/mingw.c +10 -1
@@ -1476,19 +1476,23 @@ static int try_shell_exec(const char *cmd, char *const *argv)return 0 ; prog =path_lookup (interpr,1 );if (prog) { +int exec_id;int argc =0 ;const char **argv2;while (argv[argc]) argc++;ALLOC_ARRAY (argv2, argc +1 ); argv2[0 ] = (char *)cmd;/* full path to the script file */ memcpy (&argv2[1 ], &argv[1 ],sizeof (*argv) * argc); + exec_id =trace2_exec (prog, argv2); pid =mingw_spawnv (prog, argv2,1 );if (pid >=0 ) {int status;if (waitpid (pid, &status,0 ) <0 ) status =255 ; +trace2_exec_result (exec_id, status);exit (status); } +trace2_exec_result (exec_id,-1 ); pid =1 ;/* indicate that we tried but failed */ free (prog);free (argv2);
@@ -1501,12 +1505,17 @@ int mingw_execv(const char *cmd, char *const *argv)/* check if git_command is a shell script */ if (!try_shell_exec (cmd, argv)) {int pid, status; +int exec_id; + exec_id =trace2_exec (cmd, (const char **)argv); pid =mingw_spawnv (cmd, (const char **)argv,0 ); -if (pid <0 ) +if (pid <0 ) { +trace2_exec_result (exec_id,-1 );return -1 ; + }if (waitpid (pid, &status,0 ) <0 ) status =255 ; +trace2_exec_result (exec_id, status);exit (status); }return -1 ;
compat/mingw.h +1 -2
@@ -147,8 +147,7 @@ static inline int fcntl(int fd, int cmd, ...) errno =EINVAL ;return -1 ; } -/* bash cannot reliably detect negative return codes as failure */ -#define exit (code) exit((code) & 0xff) +#define sigemptyset (x) (void)0static inline int sigaddset (sigset_t *set,int signum) {return 0 ; }
config.c +2 -0
@@ -2656,6 +2656,8 @@ int git_config_set_gently(const char *key, const char *value)void git_config_set (const char *key,const char *value) {git_config_set_multivar (key, value,NULL ,0 ); + +trace2_cmd_set_config (key, value); }/*
exec-cmd.c +2 -0
@@ -209,6 +209,8 @@ static int git_get_exec_path(struct strbuf *buf, const char *argv0)return -1 ; } +trace2_cmd_path (buf->buf); +return 0 ; }
git-compat-util.h +7 -0
@@ -1238,6 +1238,13 @@ static inline int is_missing_file_error(int errno_)extern int cmd_main (int ,const char **); +/* +* Intercept all calls to exit() and route them to trace2 to +* optionally emit a message before calling the real exit(). +*/ +int trace2_cmd_exit_fl (const char *file,int line,int code); +#define exit (code) exit(trace2_cmd_exit_fl(__FILE__, __LINE__, (code))) +/* * You can mark a stack variable with UNLEAK(var) to avoid it being * reported as a leak by tools like LSAN or valgrind. The argument
git.c +65 -0
127 lines not on this page · raw message
remote-curl.c +7 -0
@@ -1340,6 +1340,13 @@ int cmd_main(int argc, const char **argv)string_list_init (&options.deepen_not,1 );string_list_init (&options.push_options,1 ); +/* +* Just report "remote-curl" here (folding all the various aliases +* ("git-remote-http", "git-remote-https", and etc.) here since they +* are all just copies of the same actual executable. +*/ +trace2_cmd_verb ("remote-curl" ); + remote =remote_get (argv[1 ]);if (argc >2 ) {
repository.c +2 -0
@@ -119,6 +119,8 @@ static int repo_init_gitdir(struct repository *repo, const char *gitdir)void repo_set_worktree (struct repository *repo,const char *path) { repo->worktree =real_pathdup (path,1 ); + +trace2_def_repo (repo); } static int read_and_verify_repository_format(struct repository_format *format,
repository.h +3 -0
@@ -90,6 +90,9 @@ struct repository {/* Repository's current hash algorithm, as serialized on disk. */ const struct git_hash_algo *hash_algo; +/* A unique-id for tracing purposes. */ +int trace2_repo_id; +/* Configurations */ /* Indicate if a repository has a different 'commondir' from 'gitdir' */
run-command.c +62 -1
118 lines not on this page · raw message
run-command.h +16 -1
@@ -10,6 +10,12 @@ struct child_process {struct argv_array args;struct argv_array env_array;pid_t pid; + +int trace2_child_id; +uint64_t trace2_child_us_start; +const char *trace2_child_class; +const char *trace2_hook_name; +/* * Using .in, .out, .err: * - Specify 0 for no redirections (child inherits stdin, stdout,
@@ -73,12 +79,14 @@ extern int run_hook_ve(const char *const *env, const char *name, va_list args);#define RUN_USING_SHELL 16#define RUN_CLEAN_ON_EXIT 32int run_command_v_opt (const char **argv,int opt); - +int run_command_v_opt_tr2 (const char **argv,int opt,const char *tr2_class);/* * env (the environment) is to be formatted like environ: "VAR=VALUE". * To unset an environment variable use just "VAR". */ int run_command_v_opt_cd_env (const char **argv,int opt,const char *dir,const char *const *env); +int run_command_v_opt_cd_env_tr2 (const char **argv,int opt,const char *dir,const char *const *env, +const char *tr2_class);/** * Execute the given command, sending "in" to its stdin, and capturing its
@@ -220,5 +228,12 @@ int run_processes_parallel(int n, start_failure_fn, task_finished_fn, void *pp_cb); +int run_processes_parallel_tr2 (int n, +get_next_task_fn , +start_failure_fn , +task_finished_fn , +void *pp_cb, +const char *tr2_category, +const char *tr2_label);#endif
sh-i18n--envsubst.c +3 -0
@@ -14,6 +14,7 @@*/ #include "git-compat-util.h" +#include "trace2.h" /* Substitution of environment variables in shell format strings. Copyright (C) 2003-2007 Free Software Foundation, Inc.
@@ -67,6 +68,8 @@ cmd_main (int argc, const char *argv[])/* Default values for command line options. */ /* unsigned short int show_variables = 0; */ +trace2_cmd_verb ("sh-i18n--envsubst" ); +switch (argc) {case 1 :
submodule.c +6 -5
@@ -1374,11 +1374,12 @@ int fetch_populated_submodules(struct repository *r,/* default value, "--submodule-prefix" and its value are added later */ calculate_changed_submodule_paths (r); -run_processes_parallel (max_parallel_jobs, - get_next_submodule, - fetch_start_failure, - fetch_finish, - &spf); +run_processes_parallel_tr2 (max_parallel_jobs, + get_next_submodule, + fetch_start_failure, + fetch_finish, + &spf, +"submodule" ,"parallel/fetch" );argv_array_clear (&spf.args); out:
t/helper/test-parse-options.c +3 -0
@@ -2,6 +2,7 @@#include "cache.h" #include "parse-options.h" #include "string-list.h" +#include "trace2.h" static int boolean =0 ;static int integer =0 ;
@@ -153,6 +154,8 @@ int cmd__parse_options(int argc, const char **argv)int i;int ret =0 ; +trace2_cmd_verb ("_parse_" ); + argc =parse_options (argc, (const char **)argv, prefix, options, usage,0 );if (length_cb.called) {
t/helper/test-tool.c +3 -0
@@ -1,5 +1,6 @@#include "git-compat-util.h" #include "test-tool.h" +#include "trace2.h" struct test_cmd {const char *name;
@@ -78,6 +79,8 @@ int cmd_main(int argc, const char **argv)if (!strcmp (cmds[i].name, argv[1 ])) { argv++; argc--; +trace2_cmd_verb (cmds[i].name); +trace2_cmd_list_config ();return cmds[i].fn (argc, argv); } }
t/t0001-init.sh +1 -0
@@ -93,6 +93,7 @@ test_expect_success 'No extra GIT_* on alias scripts' ' sed -n \ -e "/^GIT_PREFIX=/d" \ -e "/^GIT_TEXTDOMAINDIR=/d" \ + -e "/^GIT_TR2_PARENT/d" \ -e "/^GIT_/s/=.*//p" | sort EOF
trace2.c new file +809 -0
809 lines not on this page · raw message
trace2.h new file +390 -0
390 lines not on this page · raw message
trace2/tr2_cfg.c new file +92 -0
92 lines not on this page · raw message
trace2/tr2_cfg.h new file +19 -0
@@ -0,0 +1,19 @@ +#ifndef TR2_CFG_H +#define TR2_CFG_H + +/* +* Iterate over all config settings and emit 'def_param' events for the +* "interesting" ones to TRACE2. +*/ +void tr2_cfg_list_config_fl (const char *file,int line); + +/* +* Emit a "def_param" event for the given key/value pair IF we consider +* the key to be "interesting". +*/ +void tr2_cfg_set_fl (const char *file,int line, +const char *key,const char *value); + +void tr2_cfg_free_patterns (void ); + +#endif /* TR2_CFG_H */
trace2/tr2_dst.c new file +90 -0
90 lines not on this page · raw message
trace2/tr2_dst.h new file +34 -0
@@ -0,0 +1,34 @@ +#ifndef TR2_DST_H +#define TR2_DST_H + +struct tr2_dst { +const char *const env_var_name; +int fd; +unsigned int initialized :1 ; +unsigned int need_close :1 ; +}; + +/* +* Disable TRACE2 on the destination. In TRACE2 a destination (DST) +* wraps a file descriptor; it is associated with a TARGET which +* defines the formatting. +*/ +void tr2_dst_trace_disable (struct tr2_dst *dst); + +/* +* Return the file descriptor for the DST. +* If 0, the dst is closed or disabled. +*/ +int tr2_dst_get_trace_fd (struct tr2_dst *dst); + +/* +* Return true if the DST is opened for writing. +*/ +int tr2_dst_trace_want (struct tr2_dst *dst); + +/* +* Write a single line/message to the trace file. +*/ +void tr2_dst_write_line (struct tr2_dst *dst,struct strbuf *buf_line); + +#endif /* TR2_DST_H */
trace2/tr2_sid.c new file +67 -0
67 lines not on this page · raw message
trace2/tr2_sid.h new file +18 -0
@@ -0,0 +1,18 @@ +#ifndef TR2_SID_H +#define TR2_SID_H + +/* +* Get our session id. Compute if necessary. +*/ +const char *tr2_sid_get (void ); + +/* +* Get our process depth. A top-level git process invoked from the +* command line will have depth=0. A child git process will have +* depth=1 and so on. +*/ +int tr2_sid_depth (void ); + +void tr2_sid_release (void ); + +#endif /* TR2_SID_H */
trace2/tr2_tbuf.c new file +32 -0
@@ -0,0 +1,32 @@ +#include "cache.h" +#include "tr2_tbuf.h" + +void tr2_tbuf_local_time (struct tr2_tbuf *tb) +{ +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +localtime_r (&secs, &tm); + +xsnprintf (tb->buf,sizeof (tb->buf),"%02d:%02d:%02d.%06ld" , + tm.tm_hour, tm.tm_min, tm.tm_sec, (long )tv.tv_usec); +} + +void tr2_tbuf_utc_time (struct tr2_tbuf *tb) +{ +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +gmtime_r (&secs, &tm); + +xsnprintf (tb->buf,sizeof (tb->buf),"%4d-%02d-%02d %02d:%02d:%02d.%06ld" , + tm.tm_year+1900 , tm.tm_mon+1 , tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, (long )tv.tv_usec); +} +
trace2/tr2_tbuf.h new file +23 -0
@@ -0,0 +1,23 @@ +#ifndef TR2_TBUF_H +#define TR2_TBUF_H + +/* +* A simple wrapper around a fixed buffer to avoid C syntax +* quirks and the need to pass around an additional size_t +* argument. +*/ +struct tr2_tbuf { +char buf[32 ]; +}; + +/* +* Fill buffer with formatted local time string. +*/ +void tr2_tbuf_local_time (struct tr2_tbuf *tb); + +/* +* Fill buffer with formatted UTC time string. +*/ +void tr2_tbuf_utc_time (struct tr2_tbuf *tb); + +#endif /* TR2_TBUF_H */
trace2/tr2_tgt.h new file +126 -0
126 lines not on this page · raw message
trace2/tr2_tgt_event.c new file +606 -0
606 lines not on this page · raw message
trace2/tr2_tgt_normal.c new file +331 -0
164 lines not on this page · raw message
trace2/tr2_tgt_perf.c new file +573 -0
573 lines not on this page · raw message
trace2/tr2_tls.c new file +164 -0
164 lines not on this page · raw message
trace2/tr2_tls.h new file +95 -0
95 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
PATCH 11/14 trace2:data: add subverb to checkout command
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/checkout.c | 7 +++++++ 1 file changed, 7 insertions(+)
builtin/checkout.c +7 -0
@@ -262,6 +262,8 @@ static int checkout_paths(const struct checkout_opts *opts,struct lock_file lock_file =LOCK_INIT ;int nr_checkouts =0 ; +trace2_cmd_subverb (opts->patch_mode ?"patch" :"path" ); +if (opts->track !=BRANCH_TRACK_UNSPECIFIED )die (_ ("'%s' cannot be used with updating paths" ),"--track" );
@@ -952,6 +954,9 @@ static int switch_branches(const struct checkout_opts *opts,void *path_to_free;struct object_id rev;int flag, writeout_error =0 ; + +trace2_cmd_subverb ("branch" ); +memset (&old_branch_info,0 ,sizeof (old_branch_info)); old_branch_info.path = path_to_free =resolve_refdup ("HEAD" ,0 , &rev, &flag);if (old_branch_info.path)
@@ -1189,6 +1194,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)int status;struct strbuf branch_ref =STRBUF_INIT ; +trace2_cmd_subverb ("unborn" ); +if (!opts->new_branch)die (_ ("You are on a branch yet to be born" ));strbuf_addf (&branch_ref,"refs/heads/%s" , opts->new_branch);
Signature · 1 line
gitgitgadget
PATCH 12/14 trace2:data: add subverb to reset command
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/reset.c | 6 ++++++ 1 file changed, 6 insertions(+)
builtin/reset.c +6 -0
@@ -340,6 +340,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (patch_mode) {if (reset_type !=NONE )die (_ ("--patch is incompatible with --{hard,mixed,soft}" )); +trace2_cmd_subverb ("patch-interactive" );return run_add_interactive (rev,"--patch=reset" , &pathspec); }
@@ -356,6 +357,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==NONE ) reset_type =MIXED ;/* by default */ +if (pathspec.nr) +trace2_cmd_subverb ("path" ); +else +trace2_cmd_subverb (reset_type_names[reset_type]); +if (reset_type !=SOFT && (reset_type !=MIXED ||get_git_work_tree ()))setup_work_tree ();
Signature · 1 line
gitgitgadget
PATCH 14/14 trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Create unit tests for Trace2.
--- Makefile | 1 + t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 273 ++++++++++++++++++++++++++++++++++++++ t/t0210-trace2-normal.sh | 135 +++++++++++++++++++ t/t0210/scrub_normal.perl | 48 +++++++ t/t0211-trace2-perf.sh | 153 +++++++++++++++++++++ t/t0211/scrub_perf.perl | 76 +++++++++++ t/t0212-trace2-event.sh | 237 +++++++++++++++++++++++++++++++++ t/t0212/parse_events.perl | 251 +++++++++++++++++++++++++++++++++++ 10 files changed, 1176 insertions(+) create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl
Makefile +1 -0
@@ -753,6 +753,7 @@ TEST_BUILTINS_OBJS += test-string-list.o TEST_BUILTINS_OBJS += test-submodule-config.o TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o +TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o TEST_BUILTINS_OBJS += test-wildmatch.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o
t/helper/test-tool.c +1 -0
@@ -49,6 +49,7 @@ static struct test_cmd cmds[] = { { "submodule-config", cmd__submodule_config }, { "submodule-nested-repo-config", cmd__submodule_nested_repo_config }, { "subprocess", cmd__subprocess }, + { "trace2", cmd__trace2 }, { "urlmatch-normalization", cmd__urlmatch_normalization }, { "wildmatch", cmd__wildmatch }, #ifdef GIT_WINDOWS_NATIVE
t/helper/test-tool.h +1 -0
@@ -44,6 +44,7 @@ int cmd__string_list(int argc, const char **argv);int cmd__submodule_config (int argc,const char **argv);int cmd__submodule_nested_repo_config (int argc,const char **argv);int cmd__subprocess (int argc,const char **argv); +int cmd__trace2 (int argc,const char **argv);int cmd__urlmatch_normalization (int argc,const char **argv);int cmd__wildmatch (int argc,const char **argv);#ifdef GIT_WINDOWS_NATIVE
t/helper/test-trace2.c new file +273 -0
273 lines not on this page · raw message
t/t0210-trace2-normal.sh new file +135 -0
135 lines not on this page · raw message
t/t0210/scrub_normal.perl new file +48 -0
48 lines not on this page · raw message
t/t0211-trace2-perf.sh new file +153 -0
153 lines not on this page · raw message
t/t0211/scrub_perf.perl new file +76 -0
76 lines not on this page · raw message
t/t0212-trace2-event.sh new file +237 -0
237 lines not on this page · raw message
t/t0212/parse_events.perl new file +251 -0
251 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 10/14 pack-objects: add trace2 regions
In reply to Jeff Hostetler via GitGitGadgetFrom: Derrick Stolee <[email protected]>
When studying the performance of 'git push' we would like to know how much time is spent at various parts of the command. One area that could cause performance trouble is 'git pack-objects'.
Add trace2 regions around the three main actions taken in this command:
1. Enumerate objects.
2. Prepare pack.
3. Write pack-file.
--- builtin/pack-objects.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
builtin/pack-objects.c +11 -1
@@ -33,6 +33,7 @@#include "object-store.h" #include "dir.h" #include "midx.h" +#include "trace2.h" #define IN_PACK (obj) oe_in_pack(&to_pack, obj)#define SIZE (obj) oe_size(&to_pack, obj)
@@ -3472,6 +3473,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } } +trace2_region_enter ("pack-objects" ,"enumerate-objects" , the_repository);prepare_packing_data (the_repository, &to_pack);if (progress)
@@ -3486,12 +3488,20 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)if (include_tag && nr_result)for_each_ref (add_ref_tag,NULL );stop_progress (&progress_state); +trace2_region_leave ("pack-objects" ,"enumerate-objects" , the_repository);if (non_empty && !nr_result)return 0 ; -if (nr_result) +if (nr_result) { +trace2_region_enter ("pack-objects" ,"prepare-pack" , the_repository);prepare_pack (window, depth); +trace2_region_leave ("pack-objects" ,"prepare-pack" , the_repository); + } + +trace2_region_enter ("pack-objects" ,"write-pack-file" , the_repository);write_pack_file (); +trace2_region_leave ("pack-objects" ,"write-pack-file" , the_repository); +if (progress) fprintf_ln(stderr, _("Total %"PRIu32" (delta %"PRIu32"),"
Signature · 1 line
gitgitgadget
PATCH 9/14 trace2:data: add trace2 instrumentation to index read/write
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 events to measure reading and writing the index.
--- read-cache.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-)
read-cache.c +46 -1
@@ -2232,6 +2232,14 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)load_index_extensions (&p); }munmap ((void *)mmap, mmap_size); + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_data_intmax ("index" , the_repository,"read/version" , istate->version); +trace2_data_intmax ("index" , the_repository,"read/cache_nr" , istate->cache_nr); +return istate->cache_nr; unmap:
@@ -2263,9 +2271,17 @@ int read_index_from(struct index_state *istate, const char *path,if (istate->initialized)return istate->cache_nr; +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_region_enter_printf ("index" ,"do_read_index" , the_repository, +"%s" , path);trace_performance_enter (); ret =do_read_index (istate, path,0 );trace_performance_leave ("read cache %s" , path); +trace2_region_leave_printf ("index" ,"do_read_index" , the_repository, +"%s" , path); split_index = istate->split_index;if (!split_index ||is_null_oid (&split_index->base_oid)) {
@@ -2281,7 +2297,11 @@ int read_index_from(struct index_state *istate, const char *path, base_oid_hex =oid_to_hex (&split_index->base_oid); base_path =xstrfmt ("%s/sharedindex.%s" , gitdir, base_oid_hex); +trace2_region_enter_printf ("index" ,"shared/do_read_index" , the_repository, +"%s" , base_path); ret =do_read_index (split_index->base, base_path,1 ); +trace2_region_leave_printf ("index" ,"shared/do_read_index" , the_repository, +"%s" , base_path);if (!oideq (&split_index->base_oid, &split_index->base->oid)) die(_("broken index, expect %s in %s, got %s"), base_oid_hex, base_path,
@@ -2988,6 +3008,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, istate->timestamp.sec = (unsigned int )st.st_mtime; istate->timestamp.nsec =ST_MTIME_NSEC (st);trace_performance_since (start,"write index, changed mask = %x" , istate->cache_changed); + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_data_intmax ("index" , the_repository,"write/version" , istate->version); +trace2_data_intmax ("index" , the_repository,"write/cache_nr" , istate->cache_nr); +return 0 ; }
@@ -3007,7 +3035,18 @@ static int commit_locked_index(struct lock_file *lk)static int do_write_locked_index (struct index_state *istate,struct lock_file *lock,unsigned flags) { -int ret =do_write_index (istate, lock->tempfile,0 ); +int ret; + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_region_enter_printf ("index" ,"do_write_index" , the_repository, +"%s" , lock->tempfile->filename.buf); + ret =do_write_index (istate, lock->tempfile,0 ); +trace2_region_leave_printf ("index" ,"do_write_index" , the_repository, +"%s" , lock->tempfile->filename.buf); +if (ret)return ret;if (flags &COMMIT_LOCK )
@@ -3092,7 +3131,13 @@ static int write_shared_index(struct index_state *istate,int ret;move_cache_to_base_index (istate); + +trace2_region_enter_printf ("index" ,"shared/do_write_index" , + the_repository,"%s" , (*temp)->filename.buf); ret =do_write_index (si->base, *temp,1 ); +trace2_region_enter_printf ("index" ,"shared/do_write_index" , + the_repository,"%s" , (*temp)->filename.buf); +if (ret)return ret; ret =adjust_shared_perm (get_tempfile_path (*temp));
Signature · 1 line
gitgitgadget
PATCH 13/14 trace2:data: add subverb for rebase
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/rebase.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
builtin/rebase.c +19 -0
@@ -819,6 +819,16 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)ACTION_EDIT_TODO ,ACTION_SHOW_CURRENT_PATCH , } action = NO_ACTION; +static const char *action_names[] = { +N_ ("undefined" ), +N_ ("continue" ), +N_ ("skip" ), +N_ ("abort" ), +N_ ("quit" ), +N_ ("edit_todo" ), +N_ ("show_current_patch" ), +NULL + };const char *gpg_sign =NULL ;struct string_list exec =STRING_LIST_INIT_NODUP ;const char *rebase_merges =NULL ;
@@ -1001,6 +1011,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("The --edit-todo action can only be used during " "interactive rebase." )); +if (trace2_is_enabled ()) { +if (is_interactive (&options)) +trace2_cmd_subverb ("interactive" ); +else if (exec.nr) +trace2_cmd_subverb ("interactive-exec" ); +else +trace2_cmd_subverb (action_names[action]); + } +switch (action) {case ACTION_CONTINUE : {struct object_id head;
Signature · 1 line
gitgitgadget
PATCH 1/14 trace2: Documentation/technical/api-trace2.txt
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Created design document for Trace2 feature.
--- Documentation/technical/api-trace2.txt | 1158 ++++++++++++++++++++++++ 1 file changed, 1158 insertions(+) create mode 100644 Documentation/technical/api-trace2.txt
Documentation/technical/api-trace2.txt new file +1158 -0
@@ -0,0 +1,1158 @@ +Trace2 API +========== + +The Trace2 API can be used to print debug, performance, and telemetry +information to stderr or a file. The Trace2 feature is inactive unless +explicitly enabled by setting one or more of the `GIT_TR2`, `GIT_TR2_PERF`, +or `GIT_TR2_EVENT` environment variables. + +The Trace2 API is intended to replace the existing (Trace1) +printf-style tracing provided by the existing `GIT_TRACE` and +`GIT_TRACE_PERFORMANCE` facilities. During initial implementation, +Trace2 and Trace1 may operate in parallel. + +The Trace2 API defines a set of high-level events with known fields, +such as (`start`: `argv`) and (`exit`: {`exit-code`, `elapsed-time`}). + +These high-level events are written to one or more Trace2 Targets +in a target-specific format. Each Trace2 Target defines a different +purpose-specific view onto the event data stream. In this mannor, +a single set of Trace2 API event calls in the Git source can drive +different types of analysis. + +Trace2 Targets +-------------- + +Trace2 defines the following set of Trace2 Targets. These can be +independently enabled and disabled. Trace2 API events are sent to +each enabled target. Each enabled target writes an event message +in a target-specific format (described later). + +`GIT_TR2` (NORMAL):: + + a simple printf format like GIT_TRACE. ++ +------------ +$ export GIT_TR2=~/log.normal +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.normal +12:28:42.620009 common-main.c:38 version 2.20.1.155.g426c96fcdb +12:28:42.620989 common-main.c:39 start git version +12:28:42.621101 git.c:432 cmd_verb version (version) +12:28:42.621215 git.c:662 exit elapsed:0.001227 code:0 +12:28:42.621250 trace2/tr2_tgt_normal.c:124 atexit elapsed:0.001265 code:0 +------------ + +`GIT_TR2_PERF` (PERF):: + + a column-based format to replace GIT_TRACE_PERFORMANCE suitable for + development and testing, possibly to complement tools like gprof. ++ +------------ +$ export GIT_TR2_PERF=~/log.perf +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.perf +12:28:42.620675 common-main.c:38 | d0 | main | version | | | | | 2.20.1.155.g426c96fcdb +12:28:42.621001 common-main.c:39 | d0 | main | start | | | | | git version +12:28:42.621111 git.c:432 | d0 | main | cmd_verb | | | | | version (version) +12:28:42.621225 git.c:662 | d0 | main | exit | | 0.001227 | | | code:0 +12:28:42.621259 trace2/tr2_tgt_perf.c:211 | d0 | main | atexit | | 0.001265 | | | code:0 +------------ + +`GIT_TR2_EVENT` (EVENT):: + + a JSON-based format of event data suitable for telemetry analysis. ++ +------------ +$ export GIT_TR2_EVENT=~/log.event +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.event +{"event":"version","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.620713","file":"common-main.c","line":38,"evt":"1","exe":"2.20.1.155.g426c96fcdb"} +{"event":"start","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621027","file":"common-main.c","line":39,"argv":["git","version"]} +{"event":"cmd_verb","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621122","file":"git.c","line":432,"name":"version","hierarchy":"version"} +{"event":"exit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621236","file":"git.c","line":662,"t_abs":0.001227,"code":0} +{"event":"atexit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621268","file":"trace2/tr2_tgt_event.c","line":163,"t_abs":0.001265,"code":0} +------------ + +Trace2 API Definitions +---------------------- + +All public Trace2 functions and macros are defined in `trace2.h` and +`trace2.c`. These are documented in detail in `trace2.h`. All +public symbols are prefixed with `trace2_`. + +There are no public Trace2 data structures. + +The Trace2 code also defines a set of private functions and data types +in the `trace2/` directory. These symbols are prefixed with `tr2_` +and should only be used by functions in `trace2.c`. + +The functions defined by the Trace2 API can be grouped into the +following groups. + +Basic Command Events:: + + These are concerned with the lifetime of the overall git process. ++ +---------------- +trace2_initialize(...) + +trace2_cmd_start(...) +trace2_cmd_exit(...) +trace2_cmd_error(...) +trace2_cmd_path(...) +---------------- + +Git Command Detail Events:: + + These are concerned with describing the specific Git command + after the command line, config, and environment are inspected. ++ +---------------- +trace2_cmd_verb(...) +trace2_cmd_subverb(...) +trace2_cmd_alias(...) + +trace2_cmd_list_config(...) +trace2_cmd_set_config(...) + +trace2_def_param(...) +trace2_def_repo(...) +---------------- + +Git Child Process Events:: + + These are concerned with the various spawned child processes, + including sub-git processes and hooks, ++ +---------------- +trace2_child_start(...) +trace2_child_exit(...) + +trace2_exec(...) +trace2_exec_result(...) +---------------- + +Git Thread Events:: + + These are concerned with Git thread usage. ++ +---------------- +trace2_thread_start(...) +trace2_thread_exit(...) +---------------- + +Region and Data Events:: + + These are concerned with recording performance data + over regions or spans of code. ++ +---------------- +trace2_region_enter(...) +trace2_region_enter_printf(...) +trace2_region_leave(...) +trace2_region_leave_printf(...) + +trace2_data_string(...) +trace2_data_intmax(...) +trace2_data_json(...) + +trace2_printf(...) +---------------- + +Trace2 API Usage +---------------- + +Here is a hypothetical usage of the Trace2 API showing the intended +usage (without worrying about the actual Git details). + +Initialization:: + + Initialization happens in `main()`. The initialization code + determines which, if any, Trace2 Targets should be enabled and + emits the `version`, `start`, and `exit` events. It causes an + `atexit` function and `signal` handler to be registered that + will emit `atexit` and `signal` events. ++ +---------------- +int main(int argc, const char **argv) +{ + int exit_code; + + trace2_initialize(); + trace2_cmd_start(argv); + + exit_code = cmd_main(argc, argv); + + trace2_cmd_exit(exit_code); + + return exit_code; +} +---------------- + +Command Details:: + + After the basics are established, additional process + information can be sent to Trace2 as it is discovered, such as + the command verb, alias expansion, interesting config + settings, the repository worktree, error messages, and etc. ++ +---------------- +int cmd_checkout(int argc, const char **argv) +{ + // emit a single "def_param" event + trace2_def_param("core.editor", "emacs"); + + // emit def_params for any config setting matching a pattern + // in GIT_TR2_CONFIG_PARAMS. + trace2_cmd_list_config(); + + trace2_cmd_verb("checkout"); + trace2_cmd_subverb("branch"); + trace2_def_repo(the_repository); + + if (do_something(...)) + trace2_cmd_error("Path '%s': cannot do something", path); + + return 0; +} +---------------- + +Child Processes:: + + Git typically spawns many sub-processes, such as sub-git + commands, shell scripts, editors, pagers, and hooks. + Trace2 child events record time spent waiting for child + processes and help track performance problems across + multiple layers of child processes. ++ +---------------- +void run_child(...) +{ + int child_exit_code; + struct child_process cmd = CHILD_PROCESS_INIT; + ... + + trace2_child_start(&cmd); + child_exit_code = ...(); + trace2_child_exit(&cmd, child_exit_code); +} +---------------- ++ +For example, the following fetch command spawned ssh, index-pack, +rev-list, and gc. This example also shows that fetch took +5.199 seconds and of that 4.932 was in ssh. ++ +---------------- +$ export GIT_TR2_BRIEF=1 +$ export GIT_TR2=~/log.normal +$ git fetch origin +... + +$ cat ~/log.normal +version 2.20.1.vfs.1.1.47.g534dbe1ad1 +start git fetch origin +worktree /Users/jeffhost/work/gfw +cmd_verb fetch (fetch) +child_start[0] ssh [email protected] ... +child_start[1] git index-pack ... +... (Trace2 events from child processes omitted) +child_exit[1] pid:14707 code:0 elapsed:0.076353 +child_exit[0] pid:14706 code:0 elapsed:4.931869 +child_start[2] git rev-list ... +... (Trace2 events from child process omitted) +child_exit[2] pid:14708 code:0 elapsed:0.110605 +child_start[3] git gc --auto +... (Trace2 events from child process omitted) +child_exit[3] pid:14709 code:0 elapsed:0.006240 +exit elapsed:5.198503 code:0 +atexit elapsed:5.198541 code:0 +---------------- ++ +When a git process is a (direct or indirect) child of another +git process, it inherits Trace2 context information. This +allows the child to print a verb hierarchy. This example +shows gc as child[3] of fetch. When the gc process reports +its verb as "gc", it also reports the hierarchy as "fetch/gc". +(In this example, trace2 messages from the child process is +indented for clarity.) ++ +---------------- +$ export GIT_TR2_BRIEF=1 +$ export GIT_TR2=~/log.normal +$ git fetch origin +... + +$ cat ~/log.normal +version 2.20.1.160.g5676107ecd.dirty +start git fetch official +worktree /Users/jeffhost/work/gfw +cmd_verb fetch (fetch) +... +child_start[3] git gc --auto + version 2.20.1.160.g5676107ecd.dirty + start /Users/jeffhost/work/gfw/git gc --auto + worktree /Users/jeffhost/work/gfw + cmd_verb gc (fetch/gc) + exit elapsed:0.001959 code:0 + atexit elapsed:0.001997 code:0 +child_exit[3] pid:20303 code:0 elapsed:0.007564 +exit elapsed:3.868938 code:0 +atexit elapsed:3.868970 code:0 +---------------- + +Region Events:: + + Trace2 defines a region as a timed span of code. ++ +---------------- +void wt_status_collect(struct wt_status *s) +{ + trace2_region_enter("status", "worktrees", s->repo); + wt_status_collect_changes_worktree(s); + trace2_region_leave("status", "worktrees", s->repo); + + trace2_region_enter("status", "index", s->repo); + wt_status_collect_changes_index(s); + trace2_region_leave("status", "index", s->repo); + + trace2_region_enter("status", "untracked", s->repo); + wt_status_collect_untracked(s); + trace2_region_leave("status", "untracked", s->repo); +} + +void wt_status_print(struct wt_status *s) +{ + trace2_region_enter("status", "print", s->repo); + switch (s->status_format) { + ... + } + trace2_region_leave("status", "print", s->repo); +} +---------------- ++ +In this example, scanning for untracked files ran from +0.012568 to ++0.027149 (since the process started) and took 0.014581 seconds. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... + +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.160.g5676107ecd.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.010988 | | status | label:worktrees +d0 | main | region_leave | r1 | 0.011236 | 0.000248 | status | label:worktrees +d0 | main | region_enter | r1 | 0.011260 | | status | label:index +d0 | main | region_leave | r1 | 0.012542 | 0.001282 | status | label:index +d0 | main | region_enter | r1 | 0.012568 | | status | label:untracked +d0 | main | region_leave | r1 | 0.027149 | 0.014581 | status | label:untracked +d0 | main | region_enter | r1 | 0.027411 | | status | label:print +d0 | main | region_leave | r1 | 0.028741 | 0.001330 | status | label:print +d0 | main | exit | | 0.028778 | | | code:0 +d0 | main | atexit | | 0.028809 | | | code:0 +---------------- ++ +Regions may be nested. This causes event messages to be indented. +Elapsed times are relative to the start of the correpsonding nesting +level as expected. For example, if we add region events to: ++ +---------------- +static enum path_treatment read_directory_recursive(struct dir_struct *dir, + struct index_state *istate, const char *base, int baselen, + struct untracked_cache_dir *untracked, int check_only, + int stop_at_first_file, const struct pathspec *pathspec) +{ + enum path_treatment state, subdir_state, dir_state = path_none; + + trace2_region_enter_printf("dir", "read_recursive", NULL, "%.*s", baselen, base); + ... + trace2_region_leave_printf("dir", "read_recursive", NULL, "%.*s", baselen, base); + return dir_state; +} +---------------- ++ +We can further investigate the time spent scanning for untracked files. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.162.gb4ccea44db.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.015047 | | status | label:untracked +d0 | main | region_enter | | 0.015132 | | dir | ..label:read_recursive +d0 | main | region_enter | | 0.016341 | | dir | ....label:read_recursive vcs-svn/ +d0 | main | region_leave | | 0.016422 | 0.000081 | dir | ....label:read_recursive vcs-svn/ +d0 | main | region_enter | | 0.016446 | | dir | ....label:read_recursive xdiff/ +d0 | main | region_leave | | 0.016522 | 0.000076 | dir | ....label:read_recursive xdiff/ +d0 | main | region_enter | | 0.016612 | | dir | ....label:read_recursive git-gui/ +d0 | main | region_enter | | 0.016698 | | dir | ......label:read_recursive git-gui/po/ +d0 | main | region_enter | | 0.016810 | | dir | ........label:read_recursive git-gui/po/glossary/ +d0 | main | region_leave | | 0.016863 | 0.000053 | dir | ........label:read_recursive git-gui/po/glossary/ +... +d0 | main | region_enter | | 0.031876 | | dir | ....label:read_recursive builtin/ +d0 | main | region_leave | | 0.032270 | 0.000394 | dir | ....label:read_recursive builtin/ +d0 | main | region_leave | | 0.032414 | 0.017282 | dir | ..label:read_recursive +d0 | main | region_leave | r1 | 0.032454 | 0.017407 | status | label:untracked +... +d0 | main | exit | | 0.034279 | | | code:0 +d0 | main | atexit | | 0.034322 | | | code:0 +---------------- ++ +Trace2 regions are similar to the existing trace_performance_enter() +and trace_performance_leave() routines, but are thread safe and +maintain per-thread stacks of timers. + +Data Events:: + + Trace2 defines a data event to report important variables. + These events contain the active thread and are indented + relative to the current thread's open region stack. ++ +---------------- +int read_index_from(struct index_state *istate, const char *path, + const char *gitdir) +{ + trace2_region_enter_printf("index", "do_read_index", the_repository, "%s", path); + + ... + + trace2_data_intmax("index", the_repository, "read/version", istate->version); + trace2_data_intmax("index", the_repository, "read/cache_nr", istate->cache_nr); + + trace2_region_leave_printf("index", "do_read_index", the_repository, "%s", path); +} +---------------- ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.156.gf9916ae094.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +d0 | main | region_enter | r1 | 0.001791 | | index | label:do_read_index .git/index +d0 | main | data | r1 | 0.002494 | 0.000703 | index | ..read/version:2 +d0 | main | data | r1 | 0.002520 | 0.000729 | index | ..read/cache_nr:3552 +d0 | main | region_leave | r1 | 0.002539 | 0.000748 | index | label:do_read_index .git/index +... +---------------- + +Thread Events:: + + Trace2 defines events to mark the start and end of a thread. + A thread name constructed during the start event. This name is + added to subsequent thread-specific events. The thread exit + event reports the elapsed time in the thread. ++ +For example, the multithreaded preload-index code can be +instrumented with a region around the thread pool and then +per-thread start and exit events within the threadproc. ++ +---------------- +static void *preload_thread(void *_data) +{ + trace2_thread_start("preload_thread"); + + trace2_data_intmax("index", the_repository, "offset", p->offset); + trace2_data_intmax("index", the_repository, "count", nr); + + do { + ... + } while (--nr > 0); + ... + + trace2_thread_exit(); + return NULL; +} + +void preload_index(struct index_state *index, + const struct pathspec *pathspec, + unsigned int refresh_flags) +{ + trace2_region_enter("index", "preload", the_repository); + + for (i = 0; i < threads; i++) { + ... /* create thread */ + } + + for (i = 0; i < threads; i++) { + ... /* join thread */ + } + + trace2_region_leave("index", "preload", the_repository); +} +---------------- ++ +In this example preload_index() was executed by the `main` thread +and started the `preload` region. Seven threads, named +`th01:preload_thread` through `th07:preload_thread`, were started. +Events from each thread are atomically appended to the shared target +stream as they occur so they may appear in random order with respect +other threads. Finally, the main thread waits for the threads to +finish and leaves the region. ++ +Data events are tagged with the active thread name. They are used +to report the per-thread parameters. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.156.gf9916ae094.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.002595 | | index | label:preload +d0 | th01:preload_thread | thread_start | | 0.002699 | | | +d0 | th02:preload_thread | thread_start | | 0.002721 | | | +d0 | th01:preload_thread | data | r1 | 0.002736 | 0.000037 | index | offset:0 +d0 | th02:preload_thread | data | r1 | 0.002751 | 0.000030 | index | offset:2032 +d0 | th03:preload_thread | thread_start | | 0.002711 | | | +d0 | th06:preload_thread | thread_start | | 0.002739 | | | +d0 | th01:preload_thread | data | r1 | 0.002766 | 0.000067 | index | count:508 +d0 | th06:preload_thread | data | r1 | 0.002856 | 0.000117 | index | offset:2540 +d0 | th03:preload_thread | data | r1 | 0.002824 | 0.000113 | index | offset:1016 +d0 | th04:preload_thread | thread_start | | 0.002710 | | | +d0 | th02:preload_thread | data | r1 | 0.002779 | 0.000058 | index | count:508 +d0 | th06:preload_thread | data | r1 | 0.002966 | 0.000227 | index | count:508 +d0 | th07:preload_thread | thread_start | | 0.002741 | | | +d0 | th07:preload_thread | data | r1 | 0.003017 | 0.000276 | index | offset:3048 +d0 | th05:preload_thread | thread_start | | 0.002712 | | | +d0 | th05:preload_thread | data | r1 | 0.003067 | 0.000355 | index | offset:1524 +d0 | th05:preload_thread | data | r1 | 0.003090 | 0.000378 | index | count:508 +d0 | th07:preload_thread | data | r1 | 0.003037 | 0.000296 | index | count:504 +d0 | th03:preload_thread | data | r1 | 0.002971 | 0.000260 | index | count:508 +d0 | th04:preload_thread | data | r1 | 0.002983 | 0.000273 | index | offset:508 +d0 | th04:preload_thread | data | r1 | 0.007311 | 0.004601 | index | count:508 +d0 | th05:preload_thread | thread_exit | | 0.008781 | 0.006069 | | +d0 | th01:preload_thread | thread_exit | | 0.009561 | 0.006862 | | +d0 | th03:preload_thread | thread_exit | | 0.009742 | 0.007031 | | +d0 | th06:preload_thread | thread_exit | | 0.009820 | 0.007081 | | +d0 | th02:preload_thread | thread_exit | | 0.010274 | 0.007553 | | +d0 | th07:preload_thread | thread_exit | | 0.010477 | 0.007736 | | +d0 | th04:preload_thread | thread_exit | | 0.011657 | 0.008947 | | +d0 | main | region_leave | r1 | 0.011717 | 0.009122 | index | label:preload +... +d0 | main | exit | | 0.029996 | | | code:0 +d0 | main | atexit | | 0.030027 | | | code:0 +---------------- ++ +In this example, the preload region took 0.009122 seconds. The 7 threads +took between 0.006069 and 0.008947 seconds to work on their portion of +the index. Thread "th01" worked on 508 items at offset 0. Thread "th02" +worked on 508 items at offset 2032. Thread "th04" worked on 508 itemts +at offset 508. ++ +This example also shows that thread names are assigned in a racy manner +as each thread starts and allocates TLS storage. + +Printf Events:: + + Trace2 defines a printf interface to write arbitrary messages + to the targets. These are convenient for development, but + may more difficult to post-process. + +Trace2 Target Formats +--------------------- + +NORMAL Target Format +~~~~~~~~~~~~~~~~~~~~ + +NORMAL format is enabled when the `GIT_TR2` environment variable +is set to "1" or an absolute pathname. + +Events are written as lines of the form: + +------------ +[<time> SP <filename>:<line> SP+] <event-name> [[SP] <event-message>] LF +------------ + +`<event-name>`:: + + is the event name. +`<event-message>`:: + is a free-form printf message intended for human consumption. ++ +Note that this may contain embedded LF or CRLF characters that are +not escaped, so the event may spill across multiple lines. + +If `GIT_TR2_BRIEF` is true, the time, filename, and line number fields +are omitted. + +This target is intended to be more of a summary (like GIT_TRACE) and +less detailed than the other targets, and therefore does not emit +messages for some of the Trace2 API verbs. For example, thread, +region, and data events are omitted. + +PERF Target Format +~~~~~~~~~~~~~~~~~~ + +PERF format is enabled when the `GIT_TR2_PERF` environment variable +is set to "1" or an absolute pathname. + +Events are written as lines of the form: + +------------ +[<time> SP <filename>:<line> SP+ + BAR SP] d<depth> SP + BAR SP <thread-name> SP+ + BAR SP <event-name> SP+ + BAR SP [r<repo-id>] SP+ + BAR SP [<t_abs>] SP+ + BAR SP [<t_rel>] SP+ + BAR SP [<category>] SP+ + BAR SP DOTS* <perf-event-message> + LF +------------ + +`<depth>`:: + is the git process depth. This is the number of parent + git processes. A top-level git command has depth value 0. + A child of it has depth value 1. + +`<thread-name>`:: + is a unique name for the thread. The primary thread + is called "main". Other thread names are of the form "th%d:%s" + and include a unique number and the name of the thread-proc. + +`<event-name>`:: + is the event name. + +`<repo-id>`:: + when present, is a number indicating the repository + in use. A `def_repo` event is emitted when a repository is + opened. This defines the repo-id and associated worktree. + Subsequent repo-specific events will reference this repo-id. ++ +Currently, this is always "r1" for the main repository. +This field is in anticipation of in-proc submodules in the future. + +`<t_abs>`:: + when present, is the absolute time in seconds since the + program started. + +`<t_rel>`:: + when present, is time in seconds relative to the start of + the current region. For a thread-exit event, it is the elapsed + time of the thread. + +`<category>`:: + is present on region and data events and is used to + indicate a broad category, such as "index" or "status". ++ +Currently, this is advisory. This field is in anticipation +of category-based filtering, similar to the `GIT_TRACE_<key>` +facility in Trace1. + +`<perf-event-message>`:: + is a free-form printf message intended for human consumption. + +If `GIT_TR2_PERF_BRIEF` is true, the time, filename, and line number +fields are omitted. + +This target is intended for interactive performance analysis +during development and is quite noisy. + +EVENT Target Format +~~~~~~~~~~~~~~~~~~~ + +EVENT format is enabled when the `GIT_TR2_EVENT` environment +variable is set to "1" or an absolute pathname. + +Each event is a JSON-object containing multiple key/value pairs +written as a single line and followed by a LF. + +------------ +'{' <key> ':' <value> [',' <key> ':' <value>]* '}' LF +------------ + +Some key/value pairs are common to all events and some are +event-specific. + +Common Key/Value Pairs +^^^^^^^^^^^^^^^^^^^^^^ + +The following key/value pairs are common to all events: + +------------ +{ + "event":"version", + "sid":"1547659722619736-11614", + "thread":"main", + "time":"2019-01-16 17:28:42.620713", + "file":"common-main.c", + "line":38, + ... +} +------------ + +`"event":<event>`:: + is the event name. + +`"sid":<sid>`:: + is the session-id. This is a unique string to identify the + process instance to allow all events emitted by a process to + be identified. A session-id is used instead of a PID because + PIDs are recycled by the OS. For child git processes, the + session-id is prepended with the session-id of the parent git + process to allow parent-child relationships to be identified + during post-processing. + +`"thread":<thread>`:: + is the thread name. + +`"time":<time>`:: + is the UTC time of the event. + +`"file":<filename>`:: + is source file generating the event. + +`"line":<line-number>`:: + is the integer source line number generating the event. + +`"repo":<repo-id>`:: + when present, is the integer repo-id as described previously. + +If `GIT_TR2_EVENT_BRIEF` is true, the file and line fields are omitted +from all events and the time field is only present on the `start` and +`atexit` events. + +Event-Specific Key/Value Pairs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`version`:: + This event gives the version of the executable and the EVENT format. ++ +------------ +{ + "event":"version", + ... + "evt":"1", # EVENT format version + "exe":"2.20.1.155.g426c96fcdb" # git version +} +------------ + +`start`:: + This event contains the complete argv received by main(). ++ +------------ +{ + "event":"start", + ... + "argv":["git","version"] +} +------------ + +`exit`:: + This event is emitted when git calls `exit()`. ++ +------------ +{ + "event":"exit", + ... + "t_abs":0.001227, # elapsed time in seconds + "code":0 # exit code +} +------------ + +`atexit`:: + This event is emitted by the Trace2 `atexit` routine during + final shutdown. It should be the last event emitted by the + process. (The elapsed time reported here is greater than the + time reported in the `exit` event because it runs after all + other atexit tasks have completed.) ++ +------------ +{ + "event":"atexit", + ... + "t_abs":0.001227, + "code":0 +} +------------ + +`signal`:: + This event is emitted when the program is terminated by a user + signal. Depending on the platform, the signal event may + prevent the `atexit` event from being generated. ++ +------------ +{ + "event":"signal", + ... + "t_abs":0.001227, + "signal":13 +} +------------ + +`error`:: + This event is emitted when one of the `error()`, `die()`, + or `usage()` functions are called. ++ +------------ +{ + "event":"error", + ... + "msg":"invalid option: --cahced", # formatted error message + "fmt":"invalid option: %s" # error format string +} +------------ ++ +The error event may be emitted more than once. The format string +allows post-processors to group errors by type without worrying +about specific error arguments. + +`cmd_path`:: + This event contains the discovered full path of the git + executable (on platforms that are configured to resolve it). ++ +------------ +{ + "event":"cmd_path", + ... + "path":"C:/work/gfw/git.exe" +} +------------ + +`cmd_verb`:: + This event contains the primary verb for this command + and the hierarchy of command verbs. ++ +------------ +{ + "event":"cmd_verb", + ... + "name":"pack-objects", + "hierarchy":"push/pack-objects" +} +------------ ++ +Normally, the `name` field contains the canonical name of the +command. When a canonical command verb is not available, one of +these special values are used: ++ +------------ +"_query_" # "git --html-path" +"_run_dashed_" # when "git foo" tries to run "git-foo" +"_run_shell_alias_" # alias expansion to a shell command +"_run_git_alias_" # alias expansion to a git command +"_usage_" # usage error +------------ + +`cmd_subverb`:: + This event, when present, describes the variant of the main + command verb. This event may be emitted more than once. ++ +------------ +{ + "event":"cmd_subverb", + ... + "name":"branch" +} +------------ ++ +The `name` field is an arbitrary string that makes sense in the +context of the primary verb. For example, checkout can checkout a +branch or an individual file. And these variations typically have +different performance characteristics that are not comparable. + +`alias`:: + This event is present when an alias is expanded. ++ +------------ +{ + "event":"alias", + ... + "alias":"l", # registered alias + "argv":["log","--graph"] # alias expansion +} +------------ + +`child_start`:: + This event describes a child process that is about to be + spawned. ++ +------------ +{ + "event":"child_start", + ... + "child_id":2, + "child_class":"?", + "use_shell":false, + "argv":["git","rev-list","--objects","--stdin","--not","--all","--quiet"] + + "hook_name":"<hook_name>" # present when child_class is "hook" + "cd":"<path>" # present when cd is required +} +------------ ++ +The `child_id` field can be used to match this child_start with the +corresponding child_exit event. ++ +The `child_class` field is a rough classification, such as "editor", +"pager", "transport/*", and "hook". Unclassified children are classified +with "?". + +`child_exit`:: + This event is generated after the git process has returned + from the waitpid() and collected the exit information from the + child. ++ +------------ +{ + "event":"child_exit", + ... + "child_id":2, + "pid":14708, # child PID + "code":0, # child exit-code + "t_rel":0.110605 # observed run-time of child process +} +------------ ++ +Note that the session-id of the child process is not available to +the current/spawning process, so the child's PID is reported here as +a hint for post-processing. (But it is only a hint because the child +proces may be a shell script which doesn't have a session-id.) ++ +Note that the `t_rel` field contains the observed run time in seconds +for the child process (starting before the fork/exec/spawn and +stopping after the waitpid() and includes OS process creation overhead). +So this time will be slightly larger than the atexit time reported by +the child process itself. + +`exec`:: + This event is generated before git attempts to `exec()` + another command rather than starting a child process. ++ +------------ +{ + "event":"exec", + ... + "exec_id":0, + "exe":"git", + "argv":["foo", "bar"] +} +------------ ++ +Since `exec()` does not return if successful, this will be the last +event generated for the current git command before the new git commit +starts. ++ +The `exec_id` field is a command-unique id and is only useful if the +`exec()` fails and a corresponding exec_result event is generated. + +`exec_result`:: + This event is generated if the `exec()` fails and control + returns to the current git command. ++ +------------ +{ + "event":"exec_result", + ... + "exec_id":0, + "code":1 # error code (errno) from exec() +} +------------ + +`thread_start`:: + This event is generated when a thread is started. It is + generated from *within* the new thread's thread-proc (for TLS + reasons). ++ +------------ +{ + "event":"thread_start", + ... + "thread":"th02:preload_thread" +} +------------ + +`thread_exit`:: + This event is generated when a thread exits. It is generated + from *within* the thread's thread-proc (for TLS reasons). ++ +------------ +{ + "event":"thread_exit", + ... + "thread":"th02:preload_thread", + "t_rel":0.007328 # thread elapsed time +} +------------ + +`def_param`:: + This event is generated to log a global key/value pair for the + current command. ++ +------------ +{ + "event":"def_param", + ... + "param":"core.editor", + "value":"emacs" +} +------------ ++ +This is intended to log "values of interest" for post-processing. For +example, this might be configuration settings known to affect +performance or the remote URL so that different repos can be grouped +differently. For thread- or region-specific key/values pairs, see the +`data` and `data_json` events. + +`def_repo`:: + This event defines a repo-id and associates it with the root + of the worktree. Subsequent repo-relative events will refer + to this repo-id. Repo-id number 1 refers to the main + repository. ++ +------------ +{ + "event":"def_repo", + ... + "repo":1, + "worktree":"/Users/jeffhost/work/gfw" +} +------------ ++ +As stated earlier, the repo-id is currently always 1, so there will +only be one def_repo event. Later, if in-proc submodules are +supported, a def_repo event should be emitted for each submodule +visited. + +`region_enter`:: + This event is generated when entering a region. + It serves as a marker in the event stream. It starts an activity timer + for the region. And it pushes a frame on the region stack for the + current thread. ++ +------------ +{ + "event":"region_enter", + ... + "repo":1, + "nesting":1, # current region stack depth + "category":"index", # optional + "label":"do_read_index", # optional + "msg":".git/index" # optional +} +------------ ++ +The `category` field may be used in a future enhancement to +do category-based filtering. ++ +The `GIT_TR2_EVENT_NESTING` environment variable can be used to +filter deeply nested regions and data events. It defaults to "2". + +`region_leave`:: + This event is generated when leaving a region. It serves as a + marker in the event stream and reports the elapsed time spent + in the region. It pops a frame off of the region stack for + the current thread. ++ +------------ +{ + "event":"region_leave", + ... + "repo":1, + "t_rel":0.002876, # time spent in region in seconds + "nesting":1, # region stack depth + "category":"index", # optional + "label":"do_read_index", # optional + "msg":".git/index" # optional +} +------------ ++ +The examples in this document have generally shown the region_enter +and region_leave events with the same category and label fields. This +is not a requirement, but makes it easier to understand the logs. + +`data`:: + This event is generated to log a thread- and region-local + key/value pair. For example, during a recursive treewalk, + logging the relative path and the number of entries in the + tree. ++ +------------ +{ + "event":"data", + ... + "repo":1, # optional + "t_abs":0.024107, # absolute elapsed time + "t_rel":0.001031, # elapsed time in region/thread + "nesting":2, + "category":"index", + "key":"read/cache_nr", + "value":"3552"} +} +------------ ++ +The `value` field may be an integer or a string. + +`data-json`:: + This event is generated to log a pre-formatted JSON string + containing structured data. ++ +------------ +{ + "event":"data_json", + ... + "repo":1, # optional + "t_abs":0.015905, + "t_rel":0.015905, + "nesting":1, + "category":"process", + "key":"windows/ancestry", + "value":["bash.exe","bash.exe"] +} +------------ + +Appendix: Relationship to the Existing Trace Api (api-trace.txt) +---------------------------------------------------------------- + +Eventually, I would like Trace2 to replace the existing Trace1 API. + +Trace2 is a superset of Trace1 and the existing Trace1 API calls could +be converted to Trace2, but that work has not yet been attempted. + +There are a few issues here: + +* Updating existing tests that assume GIT_TRACE format messages. + +* How to best handle custom GIT_TRACE_<key> messages? + +** The GIT_TRACE_<key> mechanism allows each <key> to write to a +different file (in addition to just stderr). + +** Do we want to maintain that ability or simply write to the existing +Trace2 targets (and convert <key> to a "category"). +
Signature · 1 line
gitgitgadget
PATCH 6/14 trace2:data: add trace2 sub-process classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 classification for long-running processes started in sub-process.c
--- sub-process.c | 1 + 1 file changed, 1 insertion(+)
sub-process.c +1 -0
@@ -88,6 +88,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co process->out =-1 ; process->clean_on_exit =1 ; process->clean_on_exit_handler = subprocess_exit_handler; + process->trace2_child_class ="subprocess" ; err =start_command (process);if (err) {
Signature · 1 line
gitgitgadget
Quoted 2 lines · Jeff Hostetler via GitGitGadget
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
Great to see this series revived. Looking forward to reading it thru.
Thanks.
Quoted 5 lines
A new design doc in
Documentation/technical/api-trace2.txt(in the first commit) explains the relationship of Trace2 to the current tracing facility. Calls to the current tracing facility have not been changed, rather new trace2 calls have been added so that both continue to work in parallel for the time being.
Re: [PATCH 10/14] pack-objects: add trace2 regions
In reply to Derrick Stolee via GitGitGadgetQuoted 6 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
When studying the performance of 'git push' we would like to know how much time is spent at various parts of the command. One area that could cause performance trouble is 'git pack-objects'.
Quoted 8 lines
Add trace2 regions around the three main actions taken in this command:
1. Enumerate objects.
2. Prepare pack.
3. Write pack-file.Signed-off-by Derrick Stolee <[email protected]>
We've had this patch in our private branch for a while, and is how we discovered the need for the sparse push algorithm [1]. We will use it to measure it's effectiveness as new Microsoft users onboard to the new algorithm.
Thanks,
-Stolee
Re: [PATCH 01/14] trace2: Documentation/technical/api-trace2.txt
In reply to Jeff Hostetler via GitGitGadgetDocumentation/technical/api-trace2.txt line 19
Quoted 3 lines · Jeff Hostetler via GitGitGadget
Documentation/technical/api-trace2.txt new file +3
-0+These high-level events are written to one or more Trace2 Targets +in a target-specific format. Each Trace2 Target defines a different +purpose-specific view onto the event data stream. In this mannor,
"In this manner"
Quoted 4 lines
Documentation/technical/api-trace2.txt new file +3
-0+a single set of Trace2 API event calls in the Git source can drive +different types of analysis. +
Documentation/technical/api-trace2.txt line 131
Quoted 10 lines
Documentation/technical/api-trace2.txt new file +9
-0+$ cat ~/log.perf +12:28:42.620675 common-main.c:38 | d0 | main | version | | | | | 2.20.1.155.g426c96fcdb +12:28:42.621001 common-main.c:39 | d0 | main | start | | | | | git version +12:28:42.621111 git.c:432 | d0 | main | cmd_verb | | | | | version (version) +12:28:42.621225 git.c:662 | d0 | main | exit | | 0.001227 | | | code:0 +12:28:42.621259 trace2/tr2_tgt_perf.c:211 | d0 | main | atexit | | 0.001265 | | | code:0 +------------ + +trace2_def_param(...)
Not limited to this single one, but please either
- omit "..." in parens, unless all of these functions take varargs of unspecified type (which I do not think is the case), or
- write a proper prototype for these functions, explain what the function itself and what the parameters are for.
I'll complain about lack of info around here later X-<.
Quoted 15 lines
Documentation/technical/api-trace2.txt new file +14
-0+trace2_def_repo(...) +---------------- + +Git Child Process Events:: + + These are concerned with the various spawned child processes, + including sub-git processes and hooks, ++ +---------------- +trace2_child_start(...) +trace2_child_exit(...) + +trace2_exec(...) +trace2_exec_result(...)
Documentation/technical/api-trace2.txt line 155
Quoted 10 lines
Documentation/technical/api-trace2.txt new file +10
-0+---------------- + +Git Thread Events:: + + These are concerned with Git thread usage. ++ +---------------- +trace2_thread_start(...) +trace2_thread_exit(...) +----------------
Lack of _wait()/_join() feels a bit curious, but _exit() from what is being waited would suffice as a substitute.
Quoted 5 lines
Documentation/technical/api-trace2.txt new file +5
-0+Initialization:: + + Initialization happens in `main()`. The initialization code + determines which, if any, Trace2 Targets should be enabled and + emits the `version`, `start`, and `exit` events. It causes an
Documentation/technical/api-trace2.txt line 195
Quoted 10 lines
Documentation/technical/api-trace2.txt new file +10
-0+ `atexit` function and `signal` handler to be registered that + will emit `atexit` and `signal` events. ++ +---------------- +int main(int argc, const char **argv) +{ + int exit_code; + + trace2_initialize(); + trace2_cmd_start(argv);
Nobody other than trace2 integration would make a call to this helper, so it may not matter, but sending av alone without ac, even though ac is almost always redundant, feels somewhat unexpected.
Documentation/technical/api-trace2.txt line 216
Quoted 12 lines
Documentation/technical/api-trace2.txt new file +12
-0+Command Details:: + + After the basics are established, additional process + information can be sent to Trace2 as it is discovered, such as + the command verb, alias expansion, interesting config + settings, the repository worktree, error messages, and etc. ++ +---------------- +int cmd_checkout(int argc, const char **argv) +{ + // emit a single "def_param" event + trace2_def_param("core.editor", "emacs");
Without knowing what "def_param event" is, this example is hard to fathom. At this point in the doc, the reader does not even know what "def" stands for. Is this call to define a param called core.editor? Is it reporting that the default value for core.editor is emacs?
Documentation/technical/api-trace2.txt line 220
Quoted 3 lines
Documentation/technical/api-trace2.txt new file +3
-0+ // emit def_params for any config setting matching a pattern + // in GIT_TR2_CONFIG_PARAMS. + trace2_cmd_list_config();
As the reader does not know what def_param is, this is also hard to follow.
Documentation/technical/api-trace2.txt line 223
Quoted 2 lines
Documentation/technical/api-trace2.txt new file +2
-0+ trace2_cmd_verb("checkout"); + trace2_cmd_subverb("branch");
These are guessable. It probably reports what the codepath is doing.
Documentation/technical/api-trace2.txt line 224
Quoted 1 line
Documentation/technical/api-trace2.txt new file +1
-0+ trace2_def_repo(the_repository);
Again, this is not easy to guess. Is it reporting what the default repository is?
Documentation/technical/api-trace2.txt line 227
Quoted 2 lines
Documentation/technical/api-trace2.txt new file +2
-0+ if (do_something(...)) + trace2_cmd_error("Path '%s': cannot do something", path);
This is guessable, which is good.
Documentation/technical/api-trace2.txt line 250
Quoted 9 lines
Documentation/technical/api-trace2.txt new file +9
-0+void run_child(...) +{ + int child_exit_code; + struct child_process cmd = CHILD_PROCESS_INIT; + ... + + trace2_child_start(&cmd); + child_exit_code = ...(); + trace2_child_exit(&cmd, child_exit_code);
Even though child_exit() has not been explained just like def_param(), this is far more guessable. I wonder why it is. The name of the variable passed to it in the example certainly helps, as it is totally unclear where the string constant "emacs" in the earlier example came from (e.g. is it hardcoded in the program? is it merely illustrating "here is how you report the value you have decided to use for 'core.editor' variable"?).
Documentation/technical/api-trace2.txt line 361
Quoted 8 lines
...
+$ cat ~/log.perfDocumentation/technical/api-trace2.txt new file +6
-0+d0 | main | version | | | | | 2.20.1.160.g5676107ecd.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.010988 | | status | label:worktrees
It is hard to guess what "d0" and "r1" stand for here.
In an earlier example we also saw an unexplained "d0" there, which I think was OK because the illustration was merely to give the "feel" of the format up there. But now we are giving a bit more detailed explanation, we probably would want to at least briefly mention what each column means.
I'd stop here for now, as I am more interested in reading the code ;-)
Re: [PATCH 02/14] trace2: create new combined trace facility
In reply to Jeff Hostetler via GitGitGadgetQuoted 56 lines · Jeff Hostetler via GitGitGadget
trace2/tr2_tgt_perf.c new file +48
-0@@ -0,0 +1,573 @@ +static struct strbuf dots =STRBUF_INIT ; +static void perf_fmt_prepare ( +const char *event_name,struct tr2tls_thread_ctx *ctx, +const char *file,int line,const struct repository *repo, +uint64_t *p_us_elapsed_absolute,uint64_t *p_us_elapsed_relative, +const char *category,struct strbuf *buf) +{ +int len; + +strbuf_setlen (buf,0 ); + +if (!tr2env_perf_brief) { + +struct tr2_tbuf tb_now; + +tr2_tbuf_local_time (&tb_now); +strbuf_addstr (buf, tb_now.buf); +strbuf_addch (buf,' ' ); + +if (file && *file) +strbuf_addf (buf,"%s:%d " , file, line); +while (buf->len <TR2FMT_PERF_FL_WIDTH ) +strbuf_addch (buf,' ' ); + +strbuf_addstr (buf,"| " ); + } + +strbuf_addf (buf,"d%d | " ,tr2_sid_depth ()); +strbuf_addf (buf,"%-*s | %-*s | " , +TR2_MAX_THREAD_NAME , ctx->thread_name.buf, +TR2FMT_PERF_MAX_EVENT_NAME , event_name); + + len = buf->len +TR2FMT_PERF_REPO_WIDTH ; +if (repo) +strbuf_addf (buf,"r%d " , repo->trace2_repo_id); +while (buf->len < len) +strbuf_addch (buf,' ' ); +strbuf_addstr (buf,"| " ); + +if (p_us_elapsed_absolute) +strbuf_addf (buf,"%9.6f | " , + ((double )(*p_us_elapsed_absolute)) /1000000.0 ); +else +strbuf_addf (buf,"%9s | " ," " ); + +if (p_us_elapsed_relative) +strbuf_addf (buf,"%9.6f | " , + ((double )(*p_us_elapsed_relative)) /1000000.0 );
trace2/tr2_tgt_perf.c line 125
Quoted 10 lines
trace2/tr2_tgt_perf.c new file +10
-0+else +strbuf_addf (buf,"%9s | " ," " ); + +strbuf_addf (buf,"%-*s | " ,TR2FMT_PERF_CATEGORY_WIDTH , + (category ? category :"" )); + +if (ctx->nr_open_regions >0 ) { +int len_indent =TR2_INDENT_LENGTH (ctx); +while (len_indent > dots.len) { +strbuf_addf (buf,"%s" , dots.buf);
Please use the much simpler are more idiomatic strbuf_addbuf() function instead.
trace2/tr2_tgt_perf.c line 157
Quoted 12 lines
trace2/tr2_tgt_perf.c new file +11
-0+ len_indent -= dots.len; + } +strbuf_addf (buf,"%.*s" , len_indent, dots.buf); + } +} +static void fn_version_fl (const char *file,int line) +{ +const char *event_name ="version" ; +struct strbuf buf_payload =STRBUF_INIT ; + +strbuf_addf (&buf_payload,"%s" , git_version_string);
strbuf_addstr()
Quoted 6 lines
trace2/tr2_tgt_perf.c new file +6
-0+ +perf_io_write_fl (file, line, event_name,NULL , +NULL ,NULL ,NULL , + &buf_payload); +strbuf_release (&buf_payload); +}
Re: [PATCH 01/14] trace2: Documentation/technical/api-trace2.txt
In reply to Jeff Hostetler via GitGitGadgetQuoted 18 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Created design document for Trace2 feature.
Signed-off-by Jeff Hostetler <[email protected]>--- Documentation/technical/api-trace2.txt | 1158 ++++++++++++++++++++++++ 1 file changed, 1158 insertions(+) create mode 100644 Documentation/technical/api-trace2.txtDocumentation/technical/api-trace2.txt new file +2
-0@@ -0,0 +1,1158 @@ +Trace2 API +==========
Documentation/technical/api-trace2.txt line 12
Quoted 10 lines
Documentation/technical/api-trace2.txt new file +10
-0+ +The Trace2 API can be used to print debug, performance, and telemetry +information to stderr or a file. The Trace2 feature is inactive unless +explicitly enabled by setting one or more of the `GIT_TR2`, `GIT_TR2_PERF`, +or `GIT_TR2_EVENT` environment variables. + +The Trace2 API is intended to replace the existing (Trace1) +printf-style tracing provided by the existing `GIT_TRACE` and +`GIT_TRACE_PERFORMANCE` facilities. During initial implementation, +Trace2 and Trace1 may operate in parallel.
Speaking of replacing Trace1, I couldn't find (or managed to overlook) the Trace2 equivalent of the good old "plain"
trace_printf("Uh-oh!");which is my go-to tool when chasing elusive heisenbugs and attempting to understand racy situations and flaky tests.
Documentation/technical/api-trace2.txt line 124
Quoted 7 lines
Documentation/technical/api-trace2.txt new file +7
-0+Git Command Detail Events:: + + These are concerned with describing the specific Git command + after the command line, config, and environment are inspected. ++ +---------------- +trace2_cmd_verb(...)
What is a "verb"?
If it means "command", then just call it so. Please stick to established Git terminology instead of introducing unnecessary new terms.
Documentation/technical/api-trace2.txt line 125
Quoted 1 line
Documentation/technical/api-trace2.txt new file +1
-0+trace2_cmd_subverb(...)
What is a "subverb"?
Looking at the strings passed to this function in later patches, I am only sure in one thing: that it is not, in fact, a verb :)
I think that in general we are better off without the word "verb", e.g.:
Documentation/technical/api-trace2.txt line 209
Quoted 3 lines
Documentation/technical/api-trace2.txt new file +3
-0+ After the basics are established, additional process + information can be sent to Trace2 as it is discovered, such as + the command verb, alias expansion, interesting config
The word 'verb' doesn't add any value to the sentence, but s/ verb// does make it clearer.
Documentation/technical/api-trace2.txt
Quoted 3 lines
Documentation/technical/api-trace2.txt new file +3
-0+When a git process is a (direct or indirect) child of another +git process, it inherits Trace2 context information. This +allows the child to print a verb hierarchy.
Without s/verb/command/ this sentence doesn't make much sense to begin with.
Re: [PATCH 01/14] trace2: Documentation/technical/api-trace2.txt
Quoted 33 lines · SZEDER Gábor
Quoted 18 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
Created design document for Trace2 feature.
Signed-off-by Jeff Hostetler <[email protected]>--- Documentation/technical/api-trace2.txt | 1158 ++++++++++++++++++++++++ 1 file changed, 1158 insertions(+) create mode 100644 Documentation/technical/api-trace2.txtDocumentation/technical/api-trace2.txt new file +2
-0@@ -0,0 +1,1158 @@ +Trace2 API +==========Documentation/technical/api-trace2.txt line 12
Quoted 10 lines
Documentation/technical/api-trace2.txt new file +10
-0+ +The Trace2 API can be used to print debug, performance, and telemetry +information to stderr or a file. The Trace2 feature is inactive unless +explicitly enabled by setting one or more of the `GIT_TR2`, `GIT_TR2_PERF`, +or `GIT_TR2_EVENT` environment variables. + +The Trace2 API is intended to replace the existing (Trace1) +printf-style tracing provided by the existing `GIT_TRACE` and +`GIT_TRACE_PERFORMANCE` facilities. During initial implementation, +Trace2 and Trace1 may operate in parallel.Speaking of replacing Trace1, I couldn't find (or managed to overlook) the Trace2 equivalent of the good old "plain"
Quoted 4 lines
trace_printf("Uh-oh!");which is my go-to tool when chasing elusive heisenbugs and attempting to understand racy situations and flaky tests.
trace2_printf() is declared in trace2.h (line 387) and defined in trace2.c (line 801).
Re: [PATCH 02/14] trace2: create new combined trace facility
In reply to SZEDER GáborQuoted 7 lines · SZEDER Gábor
trace2/tr2_tgt_perf.c line 125
Quoted 4 lines
trace2/tr2_tgt_perf.c new file +4
-0+if (ctx->nr_open_regions >0 ) { +int len_indent =TR2_INDENT_LENGTH (ctx); +while (len_indent > dots.len) { +strbuf_addf (buf,"%s" , dots.buf);Please use the much simpler are more idiomatic
strbuf_addbuf()function instead.
Ahh, contrib/coccinelle/strbuf.cocci would help ;-)
Quoted 127 lines · Jeff Hostetler via GitGitGadget
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
A new design doc in
Documentation/technical/api-trace2.txt(in the first commit) explains the relationship of Trace2 to the current tracing facility. Calls to the current tracing facility have not been changed, rather new trace2 calls have been added so that both continue to work in parallel for the time being.[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (1):
pack-objects: add trace2 regionsJeff Hostetler (13):
trace2:Documentation/technical/api-trace2.txt
trace2: create new combined trace facility
trace2: collect platform-specific process information trace2:data: add trace2 regions to wt-status trace2:data: add editor/pager child classification trace2:data: add trace2 sub-process classification trace2:data: add trace2 transport child classification trace2:data: add trace2 hook classification trace2:data: add trace2 instrumentation to index read/write trace2:data: add subverb to checkout command trace2:data: add subverb to reset command trace2:data: add subverb for rebase
trace2: t/helper/test-trace2,t0210.sh,t0211.sh,t0212.shDocumentation/technical/api-trace2.txt | 1158 ++++++++++++++++++++++++ Makefile | 11 + builtin/am.c | 1 + builtin/checkout.c | 7 + builtin/pack-objects.c | 12 +- builtin/rebase.c | 19 + builtin/receive-pack.c | 4 + builtin/reset.c | 6 + builtin/submodule--helper.c | 11 +- builtin/worktree.c | 1 + cache.h | 1 + common-main.c | 13 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- compat/win32/ancestry.c | 102 +++ config.c | 2 + config.mak.uname | 2 + connect.c | 3 + editor.c | 1 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 ++ pager.c | 1 + read-cache.c | 47 +- remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 63 +- run-command.h | 17 +- sequencer.c | 2 + sh-i18n--envsubst.c | 3 + sub-process.c | 1 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 4 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 273 ++++++ t/t0001-init.sh | 1 + t/t0210-trace2-normal.sh | 135 +++ t/t0210/scrub_normal.perl | 48 + t/t0211-trace2-perf.sh | 153 ++++ t/t0211/scrub_perf.perl | 76 ++ t/t0212-trace2-event.sh | 237 +++++ t/t0212/parse_events.perl | 251 +++++ trace2.c | 809 +++++++++++++++++ trace2.h | 403 +++++++++ trace2/tr2_cfg.c | 92 ++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 90 ++ trace2/tr2_dst.h | 34 + trace2/tr2_sid.c | 67 ++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 + trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 126 +++ trace2/tr2_tgt_event.c | 606 +++++++++++++ trace2/tr2_tgt_normal.c | 331 +++++++ trace2/tr2_tgt_perf.c | 573 ++++++++++++ trace2/tr2_tls.c | 164 ++++ trace2/tr2_tls.h | 95 ++ trace2/tr2_verb.c | 30 + trace2/tr2_verb.h | 24 + transport-helper.c | 2 + transport.c | 1 + usage.c | 31 + wt-status.c | 23 +- 66 files changed, 6353 insertions(+), 21 deletions(-) create mode 100644 Documentation/technical/api-trace2.txt create mode 100644 compat/win32/ancestry.c create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
Quoted 6 lines
base-commit:
77556354bb7ac50450e3b28999e3576969869068
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-108%2Fjeffhostetler%2Fcore-trace2-2019-v0-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-108/jeffhostetler/core-trace2-2019-v0-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/108Signature · 1 line
gitgitgadget
Several patches in this series have many style diffs as reported by clang-format. Not all the diffs actually improve readability, but many do. If you have clang-format installed, you can run:
git clang-format --style file --diff --extensions c,h ${commit}^ ${commit}
for each commit in the series to see what it thinks needs to be changed.
Other than that, I don't have any comments apart from what the other reviewers have already mentioned.
Thanks for the series!
Re: [PATCH 01/14] trace2: Documentation/technical/api-trace2.txt
In reply to Junio C HamanoQuoted 25 lines · Junio C Hamano
Documentation/technical/api-trace2.txt line 19
Quoted 3 lines · Jeff Hostetler via GitGitGadget
Documentation/technical/api-trace2.txt new file +3
-0+These high-level events are written to one or more Trace2 Targets +in a target-specific format. Each Trace2 Target defines a different +purpose-specific view onto the event data stream. In this mannor,"In this manner"
Quoted 4 lines
Documentation/technical/api-trace2.txt new file +3
-0+a single set of Trace2 API event calls in the Git source can drive +different types of analysis. +Documentation/technical/api-trace2.txt line 131
Quoted 10 lines
Documentation/technical/api-trace2.txt new file +9
-0+$ cat ~/log.perf +12:28:42.620675 common-main.c:38 | d0 | main | version | | | | | 2.20.1.155.g426c96fcdb +12:28:42.621001 common-main.c:39 | d0 | main | start | | | | | git version +12:28:42.621111 git.c:432 | d0 | main | cmd_verb | | | | | version (version) +12:28:42.621225 git.c:662 | d0 | main | exit | | 0.001227 | | | code:0 +12:28:42.621259 trace2/tr2_tgt_perf.c:211 | d0 | main | atexit | | 0.001265 | | | code:0 +------------ + +trace2_def_param(...)Not limited to this single one, but please either
Quoted 7 lines
- omit "..." in parens, unless all of these functions take varargs of unspecified type (which I do not think is the case), or
- write a proper prototype for these functions, explain what the function itself and what the parameters are for.
I'll complain about lack of info around here later X-<.
I documented the prototypes in trace2.h and was hoping to avoid duplicating all that text here in this document. The list of functions here in this document was more of an overview of the groups of concepts covered. I'll revisit this.
Quoted 21 lines
Quoted 21 lines
Documentation/technical/api-trace2.txt new file +19
-0+trace2_def_repo(...) +---------------- + +Git Child Process Events:: + + These are concerned with the various spawned child processes, + including sub-git processes and hooks, ++ +---------------- +trace2_child_start(...) +trace2_child_exit(...) + +trace2_exec(...) +trace2_exec_result(...) +---------------- + +Git Thread Events:: + + These are concerned with Git thread usage.
Quoted 8 lines
Documentation/technical/api-trace2.txt line 155
Quoted 5 lines
++
+----------------Documentation/technical/api-trace2.txt new file +3
-0+trace2_thread_start(...) +trace2_thread_exit(...) +----------------Lack of _wait()/_join() feels a bit curious, but
_exit()from what is being waited would suffice as a substitute.
The 2 trace2_thread_ functions have to be run by the thread-proc itself rather than by the code calling the pthread functions. So to me it made more sense inside the thread-proc for them to be named _start and _exit. This gives us an event with the elapsed time of the thread in isolation using TLS data within each thread.
If I understand your question about _wait/_join, adding trace2 calls near the pthread_create and pthread_join, would be a bit of a mess because of the usual create loop and then later the join loop. And the exit times would be less accurate, since we typically _join them in array order rather in the order they finish.
And having the trace2_thread_ calls in the pthread caller doesn't let me access the thread's private TLS data.
Wrapping a region (like I show later in the preload_index() example) around both of the pthread loops gives us the overall time for the threading and insight into the thread overhead. (One could even have a region around the pthread_create loop and another around the pthread_join loop, but that might not be worth the trouble.
Quoted 12 lines
Quoted 12 lines
Documentation/technical/api-trace2.txt new file +11
-0+Initialization:: + + Initialization happens in `main()`. The initialization code + determines which, if any, Trace2 Targets should be enabled and + emits the `version`, `start`, and `exit` events. It causes an + `atexit` function and `signal` handler to be registered that + will emit `atexit` and `signal` events. ++ +---------------- +int main(int argc, const char **argv) +{
Quoted 8 lines
Documentation/technical/api-trace2.txt line 195
Quoted 4 lines
Documentation/technical/api-trace2.txt new file +4
-0+ int exit_code; + + trace2_initialize(); + trace2_cmd_start(argv);Nobody other than trace2 integration would make a call to this helper, so it may not matter, but sending av alone without ac, even though ac is almost always redundant, feels somewhat unexpected.
Agreed. There were other places that took an argv that didn't have an argc on hand in the calling code. So rather than fake up one for them, I just omitted it from all the calls in my API.
Quoted 20 lines
Documentation/technical/api-trace2.txt line 216
Quoted 13 lines
Documentation/technical/api-trace2.txt new file +12
-0+Command Details:: + + After the basics are established, additional process + information can be sent to Trace2 as it is discovered, such as + the command verb, alias expansion, interesting config + settings, the repository worktree, error messages, and etc. ++ +---------------- +int cmd_checkout(int argc, const char **argv) +{ + // emit a single "def_param" event + trace2_def_param("core.editor", "emacs");Without knowing what "
def_paramevent" is, this example is hard to fathom. At this point in the doc, the reader does not even know what "def" stands for. Is this call to define a param called core.editor? Is it reporting that the default value for core.editor is emacs?
Quoted 6 lines
Documentation/technical/api-trace2.txt line 220
Quoted 3 lines
Documentation/technical/api-trace2.txt new file +3
-0+ // emit def_params for any config setting matching a pattern + // in GIT_TR2_CONFIG_PARAMS. + trace2_cmd_list_config();As the reader does not know what
def_paramis, this is also hard to follow.
I'll rewrite that section. The idea is to define a parameter that a perf-tester might consider important. For example, "core.abbrev" used to cause massive perf problems on our mega repo where it might take minutes to compute 7 or 8 digit abbreviations for a log or branch command (completely dominating the time to actually compute the log or branch list).
Quoted 6 lines
Documentation/technical/api-trace2.txt line 223
Quoted 3 lines
Documentation/technical/api-trace2.txt new file +2
-0+ trace2_cmd_verb("checkout"); + trace2_cmd_subverb("branch");These are guessable. It probably reports what the codepath is doing.
These are to help post-processing. The git command line is parsed
in 2 or 3 different phases and it takes quite a bit of work to get
to cmd_*() function (such as skipping over the -c, -C, --exec-path,
and other such args, and handling the commands where there isn't
a cmd_*() function (such as "git --exec-path")). Then within the
cmd_*() function, a lot of work to see which variant of the command
is (such as is this checkout checking out a branch or a single
file). And there are short and long forms of many arguments. And then there is the alias expansion which wants to rewrite the command line (possibly recursively). And then there is the code to try to run the
non-builtin dashed form.
For post-processing, you want to be able to do something like this:
select elapsed_time where verb='checkout' and subverb='branch' select elapsed_time where verb='checkout' and subverb='path'
and report averages over those 2 sets independently without having to recreate all that argv parsing inside the database query (or perl script on the trace log).
Quoted 5 lines
Documentation/technical/api-trace2.txt line 224
Quoted 2 lines
Documentation/technical/api-trace2.txt new file +1
-0+ trace2_def_repo(the_repository);Again, this is not easy to guess. Is it reporting what the default repository is?
I'll reword this.
Quoted 16 lines
Documentation/technical/api-trace2.txt line 227
Quoted 3 lines
Documentation/technical/api-trace2.txt new file +2
-0+ if (do_something(...)) + trace2_cmd_error("Path '%s': cannot do something", path);This is guessable, which is good.
Documentation/technical/api-trace2.txt line 250
Quoted 9 lines
Documentation/technical/api-trace2.txt new file +9
-0+void run_child(...) +{ + int child_exit_code; + struct child_process cmd = CHILD_PROCESS_INIT; + ... + + trace2_child_start(&cmd); + child_exit_code = ...(); + trace2_child_exit(&cmd, child_exit_code);
Quoted 7 lines
Even though
child_exit()has not been explained just likedef_param(), this is far more guessable. I wonder why it is. The name of the variable passed to it in the example certainly helps, as it is totally unclear where the string constant "emacs" in the earlier example came from (e.g. is it hardcoded in the program? is it merely illustrating "here is how you report the value you have decided to use for 'core.editor' variable"?).
yes, some of these are synthetic uses of the API to illustrate the trace2 concepts. I just picked 'core.editor' at random. I'll revisit the examples.
Quoted 12 lines
Documentation/technical/api-trace2.txt line 361
Quoted 9 lines
...
+$ cat ~/log.perfDocumentation/technical/api-trace2.txt new file +6
-0+d0 | main | version | | | | | 2.20.1.160.g5676107ecd.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.010988 | | status | label:worktreesIt is hard to guess what "d0" and "r1" stand for here.
Quoted 5 lines
In an earlier example we also saw an unexplained "d0" there, which I think was OK because the illustration was merely to give the "feel" of the format up there. But now we are giving a bit more detailed explanation, we probably would want to at least briefly mention what each column means.
yes, the example at the top was just to preview the formats. I'll add more info here.
Quoted 3 lines
I'd stop here for now, as I am more interested in reading the code ;-)
Thanks
Jeff
Quoted 2 lines · Jeff Hostetler via GitGitGadget
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
My Trace2 series "jh/trace2" has a bad interaction with "js/vsts-ci" causing some unit tests to fail in "pu". I'll post a new version shortly.
Jeff
Quoted 8 lines · Jeff Hostetler
Quoted 2 lines · Jeff Hostetler via GitGitGadget
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
My Trace2 series "jh/trace2" has a bad interaction with "js/vsts-ci" causing some unit tests to fail in "pu". I'll post a new version shortly.
Thanks for a heads-up.
v2 cover Trace2 tracing facility
In reply to Jeff Hostetler via GitGitGadgetV2 addresses: [] "jh/trace2" bad interaction with "js/vsts-ci" in "pu". [] coccinelle warnings in trace2/tr2_tgt_perf.c reported during CI testing.
----------------------------------------------------------------------------
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
A new design doc in Documentation/technical/api-trace2.txt (in the first commit) explains the relationship of Trace2 to the current tracing facility. Calls to the current tracing facility have not been changed, rather new trace2 calls have been added so that both continue to work in parallel for the time being.
[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (1):
pack-objects: add trace2 regions
Jeff Hostetler (13):
trace2: Documentation/technical/api-trace2.txt
trace2: create new combined trace facility
trace2: collect platform-specific process information trace2:data: add trace2 regions to wt-status trace2:data: add editor/pager child classification trace2:data: add trace2 sub-process classification trace2:data: add trace2 transport child classification trace2:data: add trace2 hook classification trace2:data: add trace2 instrumentation to index read/write trace2:data: add subverb to checkout command trace2:data: add subverb to reset command trace2:data: add subverb for rebase
trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
Documentation/technical/api-trace2.txt | 1158 ++++++++++++++++++++++++ Makefile | 11 + builtin/am.c | 1 + builtin/checkout.c | 7 + builtin/pack-objects.c | 12 +- builtin/rebase.c | 19 + builtin/receive-pack.c | 4 + builtin/reset.c | 6 + builtin/submodule--helper.c | 11 +- builtin/worktree.c | 1 + cache.h | 1 + common-main.c | 13 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- compat/win32/ancestry.c | 102 +++ config.c | 2 + config.mak.uname | 2 + connect.c | 3 + editor.c | 1 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 ++ pager.c | 1 + read-cache.c | 47 +- remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 63 +- run-command.h | 17 +- sequencer.c | 2 + sh-i18n--envsubst.c | 3 + sub-process.c | 1 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 4 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 273 ++++++ t/t0001-init.sh | 1 + t/t0210-trace2-normal.sh | 147 +++ t/t0210/scrub_normal.perl | 48 + t/t0211-trace2-perf.sh | 163 ++++ t/t0211/scrub_perf.perl | 76 ++ t/t0212-trace2-event.sh | 246 +++++ t/t0212/parse_events.perl | 251 +++++ trace2.c | 809 +++++++++++++++++ trace2.h | 403 +++++++++ trace2/tr2_cfg.c | 92 ++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 90 ++ trace2/tr2_dst.h | 34 + trace2/tr2_sid.c | 67 ++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 + trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 126 +++ trace2/tr2_tgt_event.c | 606 +++++++++++++ trace2/tr2_tgt_normal.c | 331 +++++++ trace2/tr2_tgt_perf.c | 573 ++++++++++++ trace2/tr2_tls.c | 164 ++++ trace2/tr2_tls.h | 95 ++ trace2/tr2_verb.c | 30 + trace2/tr2_verb.h | 24 + transport-helper.c | 2 + transport.c | 1 + usage.c | 31 + wt-status.c | 23 +- 66 files changed, 6384 insertions(+), 21 deletions(-) create mode 100644 Documentation/technical/api-trace2.txt create mode 100644 compat/win32/ancestry.c create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
base-commit: 77556354bb7ac50450e3b28999e3576969869068
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-108%2Fjeffhostetler%2Fcore-trace2-2019-v0-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-108/jeffhostetler/core-trace2-2019-v0-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/108
Range-diff vs v1:
1: 1a90de9dab = 1: 1a90de9dab trace2: Documentation/technical/api-trace2.txt
2: 4aaf4834bf ! 2: ea39b76d31 trace2: create new combined trace facility
@@ -3527,7 +3527,7 @@
+ if (ctx->nr_open_regions > 0) {
+ int len_indent = TR2_INDENT_LENGTH(ctx);
+ while (len_indent > dots.len) {
-+ strbuf_addf(buf, "%s", dots.buf);
++ strbuf_addbuf(buf, &dots);
+ len_indent -= dots.len;
+ }
+ strbuf_addf(buf, "%.*s", len_indent, dots.buf);
@@ -3559,7 +3559,7 @@
+ const char *event_name = "version";
+ struct strbuf buf_payload = STRBUF_INIT;
+
-+ strbuf_addf(&buf_payload, "%s", git_version_string);
++ strbuf_addstr(&buf_payload, git_version_string);
+
+ perf_io_write_fl(file, line, event_name, NULL,
+ NULL, NULL, NULL,
3: 5baee8295e = 3: 5ac061e14a trace2: collect platform-specific process information
4: 978b9ebf0d = 4: f9d689a54b trace2:data: add trace2 regions to wt-status
5: 3941647097 = 5: 6be101d520 trace2:data: add editor/pager child classification
6: a20cf139ee = 6: b590f19100 trace2:data: add trace2 sub-process classification
7: 49b6b9bd24 = 7: 68192b8dfb trace2:data: add trace2 transport child classification
8: 0f8f366bbe = 8: b373ab640b trace2:data: add trace2 hook classification
9: 705c2ece6d = 9: 548ea52742 trace2:data: add trace2 instrumentation to index read/write
10: 1aa79cb126 = 10: 3458917811 pack-objects: add trace2 regions
11: 8446f69b57 = 11: 86feec03e2 trace2:data: add subverb to checkout command
12: 7eea9027f9 = 12: 9abbdf9ccd trace2:data: add subverb to reset command
13: 618e5ccb0b = 13: 06ccce9632 trace2:data: add subverb for rebase
14: 33a1ca7222 ! 14: 851aa8f34d trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
@@ -356,9 +356,11 @@
+# Trace2 events will/can be written to each active target (subject
+# to whatever filtering that target decides to do).
+# This script tests the normal target in isolation.
++#
++# Defer setting GIT_TR2 until the actual command line we want to test
++# because hidden git and test-tool commands run by the test harness
++# can contaminate our output.
+
-+# Enable "normal" trace2 target.
-+GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2
+# Enable "brief" feature which turns off "<clock> <file>:<line> " prefix.
+GIT_TR2_BRIEF=1 && export GIT_TR2_BRIEF
+
@@ -382,7 +384,9 @@
+
+test_expect_success 'normal stream, return code 0' '
+ test_when_finished "rm trace.normal actual expect" &&
++ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
+ $TT trace2 001return 0 &&
++ unset GIT_TR2 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -396,7 +400,9 @@
+
+test_expect_success 'normal stream, return code 1' '
+ test_when_finished "rm trace.normal actual expect" &&
++ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
+ test_must_fail $TT trace2 001return 1 &&
++ unset GIT_TR2 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -414,7 +420,9 @@
+
+test_expect_success 'normal stream, exit code 0' '
+ test_when_finished "rm trace.normal actual expect" &&
++ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
+ $TT trace2 002exit 0 &&
++ unset GIT_TR2 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -428,7 +436,9 @@
+
+test_expect_success 'normal stream, exit code 1' '
+ test_when_finished "rm trace.normal actual expect" &&
++ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
+ test_must_fail $TT trace2 002exit 1 &&
++ unset GIT_TR2 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -446,7 +456,9 @@
+
+test_expect_success 'normal stream, error event' '
+ test_when_finished "rm trace.normal actual expect" &&
++ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
+ $TT trace2 003error "hello world" "this is a test" &&
++ unset GIT_TR2 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -551,9 +563,11 @@
+# Trace2 events will/can be written to each active target (subject
+# to whatever filtering that target decides to do).
+# Test each target independently.
++#
++# Defer setting GIT_TR2_PERF until the actual command we want to
++# test because hidden git and test-tool commands in the test
++# harness can contaminate our output.
+
-+# Enable "perf" trace2 target.
-+GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF
+# Enable "brief" feature which turns off the prefix:
+# "<clock> <file>:<line> | <nr_parents> | "
+GIT_TR2_PERF_BRIEF=1 && export GIT_TR2_PERF_BRIEF
@@ -569,7 +583,9 @@
+
+test_expect_success 'perf stream, return code 0' '
+ test_when_finished "rm trace.perf actual expect" &&
++ GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF &&
+ $TT trace2 001return 0 &&
++ unset GIT_TR2_PERF &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
@@ -583,7 +599,9 @@
+
+test_expect_success 'perf stream, return code 1' '
+ test_when_finished "rm trace.perf actual expect" &&
++ GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF &&
+ test_must_fail $TT trace2 001return 1 &&
++ unset GIT_TR2_PERF &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
@@ -601,7 +619,9 @@
+
+test_expect_success 'perf stream, error event' '
+ test_when_finished "rm trace.perf actual expect" &&
++ GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF &&
+ $TT trace2 003error "hello world" "this is a test" &&
++ unset GIT_TR2_PERF &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
@@ -647,7 +667,9 @@
+
+test_expect_success 'perf stream, child processes' '
+ test_when_finished "rm trace.perf actual expect" &&
++ GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF &&
+ $TT trace2 004child $TT trace2 004child $TT trace2 001return 0 &&
++ unset GIT_TR2_PERF &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
@@ -792,9 +814,10 @@
+# Trace2 events will/can be written to each active target (subject
+# to whatever filtering that target decides to do).
+# Test each target independently.
-+
-+# Enable "event" trace2 target.
-+GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT
++#
++# Defer setting GIT_TR2_PERF until the actual command we want to
++# test because hidden git and test-tool commands in the test
++# harness can contaminate our output.
+
+# We don't bother repeating the 001return and 002exit tests, since they
+# have coverage in the normal and perf targets.
@@ -805,7 +828,9 @@
+
+test_expect_success 'event stream, error event' '
+ test_when_finished "rm trace.event actual expect" &&
++ GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT &&
+ $TT trace2 003error "hello world" "this is a test" &&
++ unset GIT_TR2_EVENT &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
@@ -842,7 +867,9 @@
+
+test_expect_success 'event stream, return code 0' '
+# test_when_finished "rm trace.event actual expect" &&
++ GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT &&
+ $TT trace2 004child $TT trace2 004child $TT trace2 001return 0 &&
++ unset GIT_TR2_EVENT &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
@@ -933,7 +960,9 @@
+ git config --local t0212.def "hello world" &&
+ # delete events generated by the above config commands
+ rm trace.event &&
++ GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT &&
+ GIT_TR2_CONFIG_PARAMS="t0212.*" $TT trace2 001return 0 &&
++ unset GIT_TR2_EVENT &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
@@ -966,7 +995,9 @@
+
+test_expect_success 'basic trace2_data' '
+ test_when_finished "rm trace.event actual expect" &&
++ GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT &&
+ $TT trace2 006data test_category k1 v1 test_category k2 v2 &&
++ unset GIT_TR2_EVENT &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {Signature · 1 line
gitgitgadget
PATCH v2 1/14 trace2: Documentation/technical/api-trace2.txt
From: Jeff Hostetler <[email protected]>
Created design document for Trace2 feature.
--- Documentation/technical/api-trace2.txt | 1158 ++++++++++++++++++++++++ 1 file changed, 1158 insertions(+) create mode 100644 Documentation/technical/api-trace2.txt
Documentation/technical/api-trace2.txt new file +1158 -0
@@ -0,0 +1,1158 @@ +Trace2 API +========== + +The Trace2 API can be used to print debug, performance, and telemetry +information to stderr or a file. The Trace2 feature is inactive unless +explicitly enabled by setting one or more of the `GIT_TR2`, `GIT_TR2_PERF`, +or `GIT_TR2_EVENT` environment variables. + +The Trace2 API is intended to replace the existing (Trace1) +printf-style tracing provided by the existing `GIT_TRACE` and +`GIT_TRACE_PERFORMANCE` facilities. During initial implementation, +Trace2 and Trace1 may operate in parallel. + +The Trace2 API defines a set of high-level events with known fields, +such as (`start`: `argv`) and (`exit`: {`exit-code`, `elapsed-time`}). + +These high-level events are written to one or more Trace2 Targets +in a target-specific format. Each Trace2 Target defines a different +purpose-specific view onto the event data stream. In this mannor, +a single set of Trace2 API event calls in the Git source can drive +different types of analysis. + +Trace2 Targets +-------------- + +Trace2 defines the following set of Trace2 Targets. These can be +independently enabled and disabled. Trace2 API events are sent to +each enabled target. Each enabled target writes an event message +in a target-specific format (described later). + +`GIT_TR2` (NORMAL):: + + a simple printf format like GIT_TRACE. ++ +------------ +$ export GIT_TR2=~/log.normal +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.normal +12:28:42.620009 common-main.c:38 version 2.20.1.155.g426c96fcdb +12:28:42.620989 common-main.c:39 start git version +12:28:42.621101 git.c:432 cmd_verb version (version) +12:28:42.621215 git.c:662 exit elapsed:0.001227 code:0 +12:28:42.621250 trace2/tr2_tgt_normal.c:124 atexit elapsed:0.001265 code:0 +------------ + +`GIT_TR2_PERF` (PERF):: + + a column-based format to replace GIT_TRACE_PERFORMANCE suitable for + development and testing, possibly to complement tools like gprof. ++ +------------ +$ export GIT_TR2_PERF=~/log.perf +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.perf +12:28:42.620675 common-main.c:38 | d0 | main | version | | | | | 2.20.1.155.g426c96fcdb +12:28:42.621001 common-main.c:39 | d0 | main | start | | | | | git version +12:28:42.621111 git.c:432 | d0 | main | cmd_verb | | | | | version (version) +12:28:42.621225 git.c:662 | d0 | main | exit | | 0.001227 | | | code:0 +12:28:42.621259 trace2/tr2_tgt_perf.c:211 | d0 | main | atexit | | 0.001265 | | | code:0 +------------ + +`GIT_TR2_EVENT` (EVENT):: + + a JSON-based format of event data suitable for telemetry analysis. ++ +------------ +$ export GIT_TR2_EVENT=~/log.event +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.event +{"event":"version","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.620713","file":"common-main.c","line":38,"evt":"1","exe":"2.20.1.155.g426c96fcdb"} +{"event":"start","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621027","file":"common-main.c","line":39,"argv":["git","version"]} +{"event":"cmd_verb","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621122","file":"git.c","line":432,"name":"version","hierarchy":"version"} +{"event":"exit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621236","file":"git.c","line":662,"t_abs":0.001227,"code":0} +{"event":"atexit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621268","file":"trace2/tr2_tgt_event.c","line":163,"t_abs":0.001265,"code":0} +------------ + +Trace2 API Definitions +---------------------- + +All public Trace2 functions and macros are defined in `trace2.h` and +`trace2.c`. These are documented in detail in `trace2.h`. All +public symbols are prefixed with `trace2_`. + +There are no public Trace2 data structures. + +The Trace2 code also defines a set of private functions and data types +in the `trace2/` directory. These symbols are prefixed with `tr2_` +and should only be used by functions in `trace2.c`. + +The functions defined by the Trace2 API can be grouped into the +following groups. + +Basic Command Events:: + + These are concerned with the lifetime of the overall git process. ++ +---------------- +trace2_initialize(...) + +trace2_cmd_start(...) +trace2_cmd_exit(...) +trace2_cmd_error(...) +trace2_cmd_path(...) +---------------- + +Git Command Detail Events:: + + These are concerned with describing the specific Git command + after the command line, config, and environment are inspected. ++ +---------------- +trace2_cmd_verb(...) +trace2_cmd_subverb(...) +trace2_cmd_alias(...) + +trace2_cmd_list_config(...) +trace2_cmd_set_config(...) + +trace2_def_param(...) +trace2_def_repo(...) +---------------- + +Git Child Process Events:: + + These are concerned with the various spawned child processes, + including sub-git processes and hooks, ++ +---------------- +trace2_child_start(...) +trace2_child_exit(...) + +trace2_exec(...) +trace2_exec_result(...) +---------------- + +Git Thread Events:: + + These are concerned with Git thread usage. ++ +---------------- +trace2_thread_start(...) +trace2_thread_exit(...) +---------------- + +Region and Data Events:: + + These are concerned with recording performance data + over regions or spans of code. ++ +---------------- +trace2_region_enter(...) +trace2_region_enter_printf(...) +trace2_region_leave(...) +trace2_region_leave_printf(...) + +trace2_data_string(...) +trace2_data_intmax(...) +trace2_data_json(...) + +trace2_printf(...) +---------------- + +Trace2 API Usage +---------------- + +Here is a hypothetical usage of the Trace2 API showing the intended +usage (without worrying about the actual Git details). + +Initialization:: + + Initialization happens in `main()`. The initialization code + determines which, if any, Trace2 Targets should be enabled and + emits the `version`, `start`, and `exit` events. It causes an + `atexit` function and `signal` handler to be registered that + will emit `atexit` and `signal` events. ++ +---------------- +int main(int argc, const char **argv) +{ + int exit_code; + + trace2_initialize(); + trace2_cmd_start(argv); + + exit_code = cmd_main(argc, argv); + + trace2_cmd_exit(exit_code); + + return exit_code; +} +---------------- + +Command Details:: + + After the basics are established, additional process + information can be sent to Trace2 as it is discovered, such as + the command verb, alias expansion, interesting config + settings, the repository worktree, error messages, and etc. ++ +---------------- +int cmd_checkout(int argc, const char **argv) +{ + // emit a single "def_param" event + trace2_def_param("core.editor", "emacs"); + + // emit def_params for any config setting matching a pattern + // in GIT_TR2_CONFIG_PARAMS. + trace2_cmd_list_config(); + + trace2_cmd_verb("checkout"); + trace2_cmd_subverb("branch"); + trace2_def_repo(the_repository); + + if (do_something(...)) + trace2_cmd_error("Path '%s': cannot do something", path); + + return 0; +} +---------------- + +Child Processes:: + + Git typically spawns many sub-processes, such as sub-git + commands, shell scripts, editors, pagers, and hooks. + Trace2 child events record time spent waiting for child + processes and help track performance problems across + multiple layers of child processes. ++ +---------------- +void run_child(...) +{ + int child_exit_code; + struct child_process cmd = CHILD_PROCESS_INIT; + ... + + trace2_child_start(&cmd); + child_exit_code = ...(); + trace2_child_exit(&cmd, child_exit_code); +} +---------------- ++ +For example, the following fetch command spawned ssh, index-pack, +rev-list, and gc. This example also shows that fetch took +5.199 seconds and of that 4.932 was in ssh. ++ +---------------- +$ export GIT_TR2_BRIEF=1 +$ export GIT_TR2=~/log.normal +$ git fetch origin +... + +$ cat ~/log.normal +version 2.20.1.vfs.1.1.47.g534dbe1ad1 +start git fetch origin +worktree /Users/jeffhost/work/gfw +cmd_verb fetch (fetch) +child_start[0] ssh [email protected] ... +child_start[1] git index-pack ... +... (Trace2 events from child processes omitted) +child_exit[1] pid:14707 code:0 elapsed:0.076353 +child_exit[0] pid:14706 code:0 elapsed:4.931869 +child_start[2] git rev-list ... +... (Trace2 events from child process omitted) +child_exit[2] pid:14708 code:0 elapsed:0.110605 +child_start[3] git gc --auto +... (Trace2 events from child process omitted) +child_exit[3] pid:14709 code:0 elapsed:0.006240 +exit elapsed:5.198503 code:0 +atexit elapsed:5.198541 code:0 +---------------- ++ +When a git process is a (direct or indirect) child of another +git process, it inherits Trace2 context information. This +allows the child to print a verb hierarchy. This example +shows gc as child[3] of fetch. When the gc process reports +its verb as "gc", it also reports the hierarchy as "fetch/gc". +(In this example, trace2 messages from the child process is +indented for clarity.) ++ +---------------- +$ export GIT_TR2_BRIEF=1 +$ export GIT_TR2=~/log.normal +$ git fetch origin +... + +$ cat ~/log.normal +version 2.20.1.160.g5676107ecd.dirty +start git fetch official +worktree /Users/jeffhost/work/gfw +cmd_verb fetch (fetch) +... +child_start[3] git gc --auto + version 2.20.1.160.g5676107ecd.dirty + start /Users/jeffhost/work/gfw/git gc --auto + worktree /Users/jeffhost/work/gfw + cmd_verb gc (fetch/gc) + exit elapsed:0.001959 code:0 + atexit elapsed:0.001997 code:0 +child_exit[3] pid:20303 code:0 elapsed:0.007564 +exit elapsed:3.868938 code:0 +atexit elapsed:3.868970 code:0 +---------------- + +Region Events:: + + Trace2 defines a region as a timed span of code. ++ +---------------- +void wt_status_collect(struct wt_status *s) +{ + trace2_region_enter("status", "worktrees", s->repo); + wt_status_collect_changes_worktree(s); + trace2_region_leave("status", "worktrees", s->repo); + + trace2_region_enter("status", "index", s->repo); + wt_status_collect_changes_index(s); + trace2_region_leave("status", "index", s->repo); + + trace2_region_enter("status", "untracked", s->repo); + wt_status_collect_untracked(s); + trace2_region_leave("status", "untracked", s->repo); +} + +void wt_status_print(struct wt_status *s) +{ + trace2_region_enter("status", "print", s->repo); + switch (s->status_format) { + ... + } + trace2_region_leave("status", "print", s->repo); +} +---------------- ++ +In this example, scanning for untracked files ran from +0.012568 to ++0.027149 (since the process started) and took 0.014581 seconds. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... + +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.160.g5676107ecd.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.010988 | | status | label:worktrees +d0 | main | region_leave | r1 | 0.011236 | 0.000248 | status | label:worktrees +d0 | main | region_enter | r1 | 0.011260 | | status | label:index +d0 | main | region_leave | r1 | 0.012542 | 0.001282 | status | label:index +d0 | main | region_enter | r1 | 0.012568 | | status | label:untracked +d0 | main | region_leave | r1 | 0.027149 | 0.014581 | status | label:untracked +d0 | main | region_enter | r1 | 0.027411 | | status | label:print +d0 | main | region_leave | r1 | 0.028741 | 0.001330 | status | label:print +d0 | main | exit | | 0.028778 | | | code:0 +d0 | main | atexit | | 0.028809 | | | code:0 +---------------- ++ +Regions may be nested. This causes event messages to be indented. +Elapsed times are relative to the start of the correpsonding nesting +level as expected. For example, if we add region events to: ++ +---------------- +static enum path_treatment read_directory_recursive(struct dir_struct *dir, + struct index_state *istate, const char *base, int baselen, + struct untracked_cache_dir *untracked, int check_only, + int stop_at_first_file, const struct pathspec *pathspec) +{ + enum path_treatment state, subdir_state, dir_state = path_none; + + trace2_region_enter_printf("dir", "read_recursive", NULL, "%.*s", baselen, base); + ... + trace2_region_leave_printf("dir", "read_recursive", NULL, "%.*s", baselen, base); + return dir_state; +} +---------------- ++ +We can further investigate the time spent scanning for untracked files. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.162.gb4ccea44db.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.015047 | | status | label:untracked +d0 | main | region_enter | | 0.015132 | | dir | ..label:read_recursive +d0 | main | region_enter | | 0.016341 | | dir | ....label:read_recursive vcs-svn/ +d0 | main | region_leave | | 0.016422 | 0.000081 | dir | ....label:read_recursive vcs-svn/ +d0 | main | region_enter | | 0.016446 | | dir | ....label:read_recursive xdiff/ +d0 | main | region_leave | | 0.016522 | 0.000076 | dir | ....label:read_recursive xdiff/ +d0 | main | region_enter | | 0.016612 | | dir | ....label:read_recursive git-gui/ +d0 | main | region_enter | | 0.016698 | | dir | ......label:read_recursive git-gui/po/ +d0 | main | region_enter | | 0.016810 | | dir | ........label:read_recursive git-gui/po/glossary/ +d0 | main | region_leave | | 0.016863 | 0.000053 | dir | ........label:read_recursive git-gui/po/glossary/ +... +d0 | main | region_enter | | 0.031876 | | dir | ....label:read_recursive builtin/ +d0 | main | region_leave | | 0.032270 | 0.000394 | dir | ....label:read_recursive builtin/ +d0 | main | region_leave | | 0.032414 | 0.017282 | dir | ..label:read_recursive +d0 | main | region_leave | r1 | 0.032454 | 0.017407 | status | label:untracked +... +d0 | main | exit | | 0.034279 | | | code:0 +d0 | main | atexit | | 0.034322 | | | code:0 +---------------- ++ +Trace2 regions are similar to the existing trace_performance_enter() +and trace_performance_leave() routines, but are thread safe and +maintain per-thread stacks of timers. + +Data Events:: + + Trace2 defines a data event to report important variables. + These events contain the active thread and are indented + relative to the current thread's open region stack. ++ +---------------- +int read_index_from(struct index_state *istate, const char *path, + const char *gitdir) +{ + trace2_region_enter_printf("index", "do_read_index", the_repository, "%s", path); + + ... + + trace2_data_intmax("index", the_repository, "read/version", istate->version); + trace2_data_intmax("index", the_repository, "read/cache_nr", istate->cache_nr); + + trace2_region_leave_printf("index", "do_read_index", the_repository, "%s", path); +} +---------------- ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.156.gf9916ae094.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +d0 | main | region_enter | r1 | 0.001791 | | index | label:do_read_index .git/index +d0 | main | data | r1 | 0.002494 | 0.000703 | index | ..read/version:2 +d0 | main | data | r1 | 0.002520 | 0.000729 | index | ..read/cache_nr:3552 +d0 | main | region_leave | r1 | 0.002539 | 0.000748 | index | label:do_read_index .git/index +... +---------------- + +Thread Events:: + + Trace2 defines events to mark the start and end of a thread. + A thread name constructed during the start event. This name is + added to subsequent thread-specific events. The thread exit + event reports the elapsed time in the thread. ++ +For example, the multithreaded preload-index code can be +instrumented with a region around the thread pool and then +per-thread start and exit events within the threadproc. ++ +---------------- +static void *preload_thread(void *_data) +{ + trace2_thread_start("preload_thread"); + + trace2_data_intmax("index", the_repository, "offset", p->offset); + trace2_data_intmax("index", the_repository, "count", nr); + + do { + ... + } while (--nr > 0); + ... + + trace2_thread_exit(); + return NULL; +} + +void preload_index(struct index_state *index, + const struct pathspec *pathspec, + unsigned int refresh_flags) +{ + trace2_region_enter("index", "preload", the_repository); + + for (i = 0; i < threads; i++) { + ... /* create thread */ + } + + for (i = 0; i < threads; i++) { + ... /* join thread */ + } + + trace2_region_leave("index", "preload", the_repository); +} +---------------- ++ +In this example preload_index() was executed by the `main` thread +and started the `preload` region. Seven threads, named +`th01:preload_thread` through `th07:preload_thread`, were started. +Events from each thread are atomically appended to the shared target +stream as they occur so they may appear in random order with respect +other threads. Finally, the main thread waits for the threads to +finish and leaves the region. ++ +Data events are tagged with the active thread name. They are used +to report the per-thread parameters. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.156.gf9916ae094.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.002595 | | index | label:preload +d0 | th01:preload_thread | thread_start | | 0.002699 | | | +d0 | th02:preload_thread | thread_start | | 0.002721 | | | +d0 | th01:preload_thread | data | r1 | 0.002736 | 0.000037 | index | offset:0 +d0 | th02:preload_thread | data | r1 | 0.002751 | 0.000030 | index | offset:2032 +d0 | th03:preload_thread | thread_start | | 0.002711 | | | +d0 | th06:preload_thread | thread_start | | 0.002739 | | | +d0 | th01:preload_thread | data | r1 | 0.002766 | 0.000067 | index | count:508 +d0 | th06:preload_thread | data | r1 | 0.002856 | 0.000117 | index | offset:2540 +d0 | th03:preload_thread | data | r1 | 0.002824 | 0.000113 | index | offset:1016 +d0 | th04:preload_thread | thread_start | | 0.002710 | | | +d0 | th02:preload_thread | data | r1 | 0.002779 | 0.000058 | index | count:508 +d0 | th06:preload_thread | data | r1 | 0.002966 | 0.000227 | index | count:508 +d0 | th07:preload_thread | thread_start | | 0.002741 | | | +d0 | th07:preload_thread | data | r1 | 0.003017 | 0.000276 | index | offset:3048 +d0 | th05:preload_thread | thread_start | | 0.002712 | | | +d0 | th05:preload_thread | data | r1 | 0.003067 | 0.000355 | index | offset:1524 +d0 | th05:preload_thread | data | r1 | 0.003090 | 0.000378 | index | count:508 +d0 | th07:preload_thread | data | r1 | 0.003037 | 0.000296 | index | count:504 +d0 | th03:preload_thread | data | r1 | 0.002971 | 0.000260 | index | count:508 +d0 | th04:preload_thread | data | r1 | 0.002983 | 0.000273 | index | offset:508 +d0 | th04:preload_thread | data | r1 | 0.007311 | 0.004601 | index | count:508 +d0 | th05:preload_thread | thread_exit | | 0.008781 | 0.006069 | | +d0 | th01:preload_thread | thread_exit | | 0.009561 | 0.006862 | | +d0 | th03:preload_thread | thread_exit | | 0.009742 | 0.007031 | | +d0 | th06:preload_thread | thread_exit | | 0.009820 | 0.007081 | | +d0 | th02:preload_thread | thread_exit | | 0.010274 | 0.007553 | | +d0 | th07:preload_thread | thread_exit | | 0.010477 | 0.007736 | | +d0 | th04:preload_thread | thread_exit | | 0.011657 | 0.008947 | | +d0 | main | region_leave | r1 | 0.011717 | 0.009122 | index | label:preload +... +d0 | main | exit | | 0.029996 | | | code:0 +d0 | main | atexit | | 0.030027 | | | code:0 +---------------- ++ +In this example, the preload region took 0.009122 seconds. The 7 threads +took between 0.006069 and 0.008947 seconds to work on their portion of +the index. Thread "th01" worked on 508 items at offset 0. Thread "th02" +worked on 508 items at offset 2032. Thread "th04" worked on 508 itemts +at offset 508. ++ +This example also shows that thread names are assigned in a racy manner +as each thread starts and allocates TLS storage. + +Printf Events:: + + Trace2 defines a printf interface to write arbitrary messages + to the targets. These are convenient for development, but + may more difficult to post-process. + +Trace2 Target Formats +--------------------- + +NORMAL Target Format +~~~~~~~~~~~~~~~~~~~~ + +NORMAL format is enabled when the `GIT_TR2` environment variable +is set to "1" or an absolute pathname. + +Events are written as lines of the form: + +------------ +[<time> SP <filename>:<line> SP+] <event-name> [[SP] <event-message>] LF +------------ + +`<event-name>`:: + + is the event name. +`<event-message>`:: + is a free-form printf message intended for human consumption. ++ +Note that this may contain embedded LF or CRLF characters that are +not escaped, so the event may spill across multiple lines. + +If `GIT_TR2_BRIEF` is true, the time, filename, and line number fields +are omitted. + +This target is intended to be more of a summary (like GIT_TRACE) and +less detailed than the other targets, and therefore does not emit +messages for some of the Trace2 API verbs. For example, thread, +region, and data events are omitted. + +PERF Target Format +~~~~~~~~~~~~~~~~~~ + +PERF format is enabled when the `GIT_TR2_PERF` environment variable +is set to "1" or an absolute pathname. + +Events are written as lines of the form: + +------------ +[<time> SP <filename>:<line> SP+ + BAR SP] d<depth> SP + BAR SP <thread-name> SP+ + BAR SP <event-name> SP+ + BAR SP [r<repo-id>] SP+ + BAR SP [<t_abs>] SP+ + BAR SP [<t_rel>] SP+ + BAR SP [<category>] SP+ + BAR SP DOTS* <perf-event-message> + LF +------------ + +`<depth>`:: + is the git process depth. This is the number of parent + git processes. A top-level git command has depth value 0. + A child of it has depth value 1. + +`<thread-name>`:: + is a unique name for the thread. The primary thread + is called "main". Other thread names are of the form "th%d:%s" + and include a unique number and the name of the thread-proc. + +`<event-name>`:: + is the event name. + +`<repo-id>`:: + when present, is a number indicating the repository + in use. A `def_repo` event is emitted when a repository is + opened. This defines the repo-id and associated worktree. + Subsequent repo-specific events will reference this repo-id. ++ +Currently, this is always "r1" for the main repository. +This field is in anticipation of in-proc submodules in the future. + +`<t_abs>`:: + when present, is the absolute time in seconds since the + program started. + +`<t_rel>`:: + when present, is time in seconds relative to the start of + the current region. For a thread-exit event, it is the elapsed + time of the thread. + +`<category>`:: + is present on region and data events and is used to + indicate a broad category, such as "index" or "status". ++ +Currently, this is advisory. This field is in anticipation +of category-based filtering, similar to the `GIT_TRACE_<key>` +facility in Trace1. + +`<perf-event-message>`:: + is a free-form printf message intended for human consumption. + +If `GIT_TR2_PERF_BRIEF` is true, the time, filename, and line number +fields are omitted. + +This target is intended for interactive performance analysis +during development and is quite noisy. + +EVENT Target Format +~~~~~~~~~~~~~~~~~~~ + +EVENT format is enabled when the `GIT_TR2_EVENT` environment +variable is set to "1" or an absolute pathname. + +Each event is a JSON-object containing multiple key/value pairs +written as a single line and followed by a LF. + +------------ +'{' <key> ':' <value> [',' <key> ':' <value>]* '}' LF +------------ + +Some key/value pairs are common to all events and some are +event-specific. + +Common Key/Value Pairs +^^^^^^^^^^^^^^^^^^^^^^ + +The following key/value pairs are common to all events: + +------------ +{ + "event":"version", + "sid":"1547659722619736-11614", + "thread":"main", + "time":"2019-01-16 17:28:42.620713", + "file":"common-main.c", + "line":38, + ... +} +------------ + +`"event":<event>`:: + is the event name. + +`"sid":<sid>`:: + is the session-id. This is a unique string to identify the + process instance to allow all events emitted by a process to + be identified. A session-id is used instead of a PID because + PIDs are recycled by the OS. For child git processes, the + session-id is prepended with the session-id of the parent git + process to allow parent-child relationships to be identified + during post-processing. + +`"thread":<thread>`:: + is the thread name. + +`"time":<time>`:: + is the UTC time of the event. + +`"file":<filename>`:: + is source file generating the event. + +`"line":<line-number>`:: + is the integer source line number generating the event. + +`"repo":<repo-id>`:: + when present, is the integer repo-id as described previously. + +If `GIT_TR2_EVENT_BRIEF` is true, the file and line fields are omitted +from all events and the time field is only present on the `start` and +`atexit` events. + +Event-Specific Key/Value Pairs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`version`:: + This event gives the version of the executable and the EVENT format. ++ +------------ +{ + "event":"version", + ... + "evt":"1", # EVENT format version + "exe":"2.20.1.155.g426c96fcdb" # git version +} +------------ + +`start`:: + This event contains the complete argv received by main(). ++ +------------ +{ + "event":"start", + ... + "argv":["git","version"] +} +------------ + +`exit`:: + This event is emitted when git calls `exit()`. ++ +------------ +{ + "event":"exit", + ... + "t_abs":0.001227, # elapsed time in seconds + "code":0 # exit code +} +------------ + +`atexit`:: + This event is emitted by the Trace2 `atexit` routine during + final shutdown. It should be the last event emitted by the + process. (The elapsed time reported here is greater than the + time reported in the `exit` event because it runs after all + other atexit tasks have completed.) ++ +------------ +{ + "event":"atexit", + ... + "t_abs":0.001227, + "code":0 +} +------------ + +`signal`:: + This event is emitted when the program is terminated by a user + signal. Depending on the platform, the signal event may + prevent the `atexit` event from being generated. ++ +------------ +{ + "event":"signal", + ... + "t_abs":0.001227, + "signal":13 +} +------------ + +`error`:: + This event is emitted when one of the `error()`, `die()`, + or `usage()` functions are called. ++ +------------ +{ + "event":"error", + ... + "msg":"invalid option: --cahced", # formatted error message + "fmt":"invalid option: %s" # error format string +} +------------ ++ +The error event may be emitted more than once. The format string +allows post-processors to group errors by type without worrying +about specific error arguments. + +`cmd_path`:: + This event contains the discovered full path of the git + executable (on platforms that are configured to resolve it). ++ +------------ +{ + "event":"cmd_path", + ... + "path":"C:/work/gfw/git.exe" +} +------------ + +`cmd_verb`:: + This event contains the primary verb for this command + and the hierarchy of command verbs. ++ +------------ +{ + "event":"cmd_verb", + ... + "name":"pack-objects", + "hierarchy":"push/pack-objects" +} +------------ ++ +Normally, the `name` field contains the canonical name of the +command. When a canonical command verb is not available, one of +these special values are used: ++ +------------ +"_query_" # "git --html-path" +"_run_dashed_" # when "git foo" tries to run "git-foo" +"_run_shell_alias_" # alias expansion to a shell command +"_run_git_alias_" # alias expansion to a git command +"_usage_" # usage error +------------ + +`cmd_subverb`:: + This event, when present, describes the variant of the main + command verb. This event may be emitted more than once. ++ +------------ +{ + "event":"cmd_subverb", + ... + "name":"branch" +} +------------ ++ +The `name` field is an arbitrary string that makes sense in the +context of the primary verb. For example, checkout can checkout a +branch or an individual file. And these variations typically have +different performance characteristics that are not comparable. + +`alias`:: + This event is present when an alias is expanded. ++ +------------ +{ + "event":"alias", + ... + "alias":"l", # registered alias + "argv":["log","--graph"] # alias expansion +} +------------ + +`child_start`:: + This event describes a child process that is about to be + spawned. ++ +------------ +{ + "event":"child_start", + ... + "child_id":2, + "child_class":"?", + "use_shell":false, + "argv":["git","rev-list","--objects","--stdin","--not","--all","--quiet"] + + "hook_name":"<hook_name>" # present when child_class is "hook" + "cd":"<path>" # present when cd is required +} +------------ ++ +The `child_id` field can be used to match this child_start with the +corresponding child_exit event. ++ +The `child_class` field is a rough classification, such as "editor", +"pager", "transport/*", and "hook". Unclassified children are classified +with "?". + +`child_exit`:: + This event is generated after the git process has returned + from the waitpid() and collected the exit information from the + child. ++ +------------ +{ + "event":"child_exit", + ... + "child_id":2, + "pid":14708, # child PID + "code":0, # child exit-code + "t_rel":0.110605 # observed run-time of child process +} +------------ ++ +Note that the session-id of the child process is not available to +the current/spawning process, so the child's PID is reported here as +a hint for post-processing. (But it is only a hint because the child +proces may be a shell script which doesn't have a session-id.) ++ +Note that the `t_rel` field contains the observed run time in seconds +for the child process (starting before the fork/exec/spawn and +stopping after the waitpid() and includes OS process creation overhead). +So this time will be slightly larger than the atexit time reported by +the child process itself. + +`exec`:: + This event is generated before git attempts to `exec()` + another command rather than starting a child process. ++ +------------ +{ + "event":"exec", + ... + "exec_id":0, + "exe":"git", + "argv":["foo", "bar"] +} +------------ ++ +Since `exec()` does not return if successful, this will be the last +event generated for the current git command before the new git commit +starts. ++ +The `exec_id` field is a command-unique id and is only useful if the +`exec()` fails and a corresponding exec_result event is generated. + +`exec_result`:: + This event is generated if the `exec()` fails and control + returns to the current git command. ++ +------------ +{ + "event":"exec_result", + ... + "exec_id":0, + "code":1 # error code (errno) from exec() +} +------------ + +`thread_start`:: + This event is generated when a thread is started. It is + generated from *within* the new thread's thread-proc (for TLS + reasons). ++ +------------ +{ + "event":"thread_start", + ... + "thread":"th02:preload_thread" +} +------------ + +`thread_exit`:: + This event is generated when a thread exits. It is generated + from *within* the thread's thread-proc (for TLS reasons). ++ +------------ +{ + "event":"thread_exit", + ... + "thread":"th02:preload_thread", + "t_rel":0.007328 # thread elapsed time +} +------------ + +`def_param`:: + This event is generated to log a global key/value pair for the + current command. ++ +------------ +{ + "event":"def_param", + ... + "param":"core.editor", + "value":"emacs" +} +------------ ++ +This is intended to log "values of interest" for post-processing. For +example, this might be configuration settings known to affect +performance or the remote URL so that different repos can be grouped +differently. For thread- or region-specific key/values pairs, see the +`data` and `data_json` events. + +`def_repo`:: + This event defines a repo-id and associates it with the root + of the worktree. Subsequent repo-relative events will refer + to this repo-id. Repo-id number 1 refers to the main + repository. ++ +------------ +{ + "event":"def_repo", + ... + "repo":1, + "worktree":"/Users/jeffhost/work/gfw" +} +------------ ++ +As stated earlier, the repo-id is currently always 1, so there will +only be one def_repo event. Later, if in-proc submodules are +supported, a def_repo event should be emitted for each submodule +visited. + +`region_enter`:: + This event is generated when entering a region. + It serves as a marker in the event stream. It starts an activity timer + for the region. And it pushes a frame on the region stack for the + current thread. ++ +------------ +{ + "event":"region_enter", + ... + "repo":1, + "nesting":1, # current region stack depth + "category":"index", # optional + "label":"do_read_index", # optional + "msg":".git/index" # optional +} +------------ ++ +The `category` field may be used in a future enhancement to +do category-based filtering. ++ +The `GIT_TR2_EVENT_NESTING` environment variable can be used to +filter deeply nested regions and data events. It defaults to "2". + +`region_leave`:: + This event is generated when leaving a region. It serves as a + marker in the event stream and reports the elapsed time spent + in the region. It pops a frame off of the region stack for + the current thread. ++ +------------ +{ + "event":"region_leave", + ... + "repo":1, + "t_rel":0.002876, # time spent in region in seconds + "nesting":1, # region stack depth + "category":"index", # optional + "label":"do_read_index", # optional + "msg":".git/index" # optional +} +------------ ++ +The examples in this document have generally shown the region_enter +and region_leave events with the same category and label fields. This +is not a requirement, but makes it easier to understand the logs. + +`data`:: + This event is generated to log a thread- and region-local + key/value pair. For example, during a recursive treewalk, + logging the relative path and the number of entries in the + tree. ++ +------------ +{ + "event":"data", + ... + "repo":1, # optional + "t_abs":0.024107, # absolute elapsed time + "t_rel":0.001031, # elapsed time in region/thread + "nesting":2, + "category":"index", + "key":"read/cache_nr", + "value":"3552"} +} +------------ ++ +The `value` field may be an integer or a string. + +`data-json`:: + This event is generated to log a pre-formatted JSON string + containing structured data. ++ +------------ +{ + "event":"data_json", + ... + "repo":1, # optional + "t_abs":0.015905, + "t_rel":0.015905, + "nesting":1, + "category":"process", + "key":"windows/ancestry", + "value":["bash.exe","bash.exe"] +} +------------ + +Appendix: Relationship to the Existing Trace Api (api-trace.txt) +---------------------------------------------------------------- + +Eventually, I would like Trace2 to replace the existing Trace1 API. + +Trace2 is a superset of Trace1 and the existing Trace1 API calls could +be converted to Trace2, but that work has not yet been attempted. + +There are a few issues here: + +* Updating existing tests that assume GIT_TRACE format messages. + +* How to best handle custom GIT_TRACE_<key> messages? + +** The GIT_TRACE_<key> mechanism allows each <key> to write to a +different file (in addition to just stderr). + +** Do we want to maintain that ability or simply write to the existing +Trace2 targets (and convert <key> to a "category"). +
Signature · 1 line
gitgitgadget
PATCH v2 3/14 trace2: collect platform-specific process information
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add optional platform-specific code to log information about the current process.
On Windows, this includes whether git.exe is running under a debugger and information about the ancestors of the process.
The purpose of this information is to help indicate if the process was launched interactively or in the background by an IDE, for example.
--- common-main.c | 1 + compat/win32/ancestry.c | 102 ++++++++++++++++++++++++++++++++++++++++ config.mak.uname | 2 + trace2.h | 13 +++++ 4 files changed, 118 insertions(+) create mode 100644 compat/win32/ancestry.c
common-main.c +1 -0
@@ -37,6 +37,7 @@ int main(int argc, const char **argv)trace2_initialize ();trace2_cmd_start (argv); +trace2_collect_process_info ();git_resolve_executable_dir (argv[0 ]);
compat/win32/ancestry.c new file +102 -0
102 lines not on this page · raw message
config.mak.uname +2 -0
@@ -391,6 +391,7 @@ ifeq ($(uname_S),Windows) BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE COMPAT_OBJS = compat/msvc.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ + compat/win32/ancestry.o \ compat/win32/dirent.o COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
@@ -528,6 +529,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ + compat/win32/ancestry.o \ compat/win32/path-utils.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o
trace2.h +13 -0
@@ -387,4 +387,17 @@ __attribute__((format (printf, 1, 2)))void trace2_printf (const char *fmt, ...);#endif +/* +* Optional platform-specific code to dump information about the +* current and any parent process(es). This is intended to allow +* post-processors to know who spawned this git instance and anything +* else the platform may be able to tell us about the current process. +*/ +#if defined(GIT_WINDOWS_NATIVE ) +void trace2_collect_process_info (void ); +#else +#define trace2_collect_process_info () \ + do {} while (0) +#endif +#endif /* TRACE2_H */
Signature · 1 line
gitgitgadget
PATCH v2 5/14 trace2:data: add editor/pager child classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 process classification for editor and pager child processes.
--- editor.c | 1 + pager.c | 1 + 2 files changed, 2 insertions(+)
editor.c +1 -0
@@ -78,6 +78,7 @@ static int launch_specified_editor(const char *editor, const char *path, p.argv = args; p.env = env; p.use_shell =1 ; + p.trace2_child_class ="editor" ;if (start_command (&p) <0 )return error ("unable to start editor '%s'" , editor);
pager.c +1 -0
@@ -100,6 +100,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)argv_array_push (&pager_process->args, pager); pager_process->use_shell =1 ;setup_pager_env (&pager_process->env_array); + pager_process->trace2_child_class ="pager" ; }void setup_pager (void )
Signature · 1 line
gitgitgadget
PATCH v2 7/14 trace2:data: add trace2 transport child classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 child classification for transport processes.
--- connect.c | 3 +++ transport-helper.c | 2 ++ 2 files changed, 5 insertions(+)
connect.c +3 -0
@@ -1251,6 +1251,7 @@ struct child_process *git_connect(int fd[2], const char *url, conn =NULL ; }else if (protocol ==PROTO_GIT ) { conn =git_connect_git (fd, hostandport, path, prog, version, flags); + conn->trace2_child_class ="transport/git" ; }else {struct strbuf cmd =STRBUF_INIT ;const char *const *var;
@@ -1293,9 +1294,11 @@ struct child_process *git_connect(int fd[2], const char *url,strbuf_release (&cmd);return NULL ; } + conn->trace2_child_class ="transport/ssh" ;fill_ssh_args (conn, ssh_host, port, version, flags); }else {transport_check_allowed ("file" ); + conn->trace2_child_class ="transport/file" ;if (version >0 ) {argv_array_pushf (&conn->env_array,GIT_PROTOCOL_ENVIRONMENT "=version=%d" , version);
transport-helper.c +2 -0
@@ -127,6 +127,8 @@ static struct child_process *get_helper(struct transport *transport)argv_array_pushf (&helper->env_array,"%s=%s" ,GIT_DIR_ENVIRONMENT ,get_git_dir ()); + helper->trace2_child_class = helper->args.argv[0 ];/* "remote-<name>" */ + code =start_command (helper);if (code <0 && errno ==ENOENT )die (_ ("unable to find remote helper for '%s'" ), data->name);
Signature · 1 line
gitgitgadget
PATCH v2 8/14 trace2:data: add trace2 hook classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Classify certain child processes as hooks.
--- builtin/am.c | 1 + builtin/receive-pack.c | 4 ++++ builtin/worktree.c | 1 + sequencer.c | 2 ++ transport.c | 1 + 5 files changed, 9 insertions(+)
builtin/am.c +1 -0
@@ -468,6 +468,7 @@ static int run_post_rewrite_hook(const struct am_state *state) cp.in =xopen (am_path (state,"rewritten" ),O_RDONLY ); cp.stdout_to_stderr =1 ; + cp.trace2_hook_name ="post-rewrite" ; ret =run_command (&cp);
builtin/receive-pack.c +4 -0
@@ -694,6 +694,8 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, proc.argv = argv; proc.in =-1 ; proc.stdout_to_stderr =1 ; + proc.trace2_hook_name = hook_name; +if (feed_state->push_options) {int i;for (i =0 ; i < feed_state->push_options->nr; i++)
@@ -807,6 +809,7 @@ static int run_update_hook(struct command *cmd) proc.stdout_to_stderr =1 ; proc.err = use_sideband ?-1 :0 ; proc.argv = argv; + proc.trace2_hook_name ="update" ; code =start_command (&proc);if (code)
@@ -1190,6 +1193,7 @@ static void run_update_post_hook(struct command *commands) proc.no_stdin =1 ; proc.stdout_to_stderr =1 ; proc.err = use_sideband ?-1 :0 ; + proc.trace2_hook_name ="post-update" ;if (!start_command (&proc)) {if (use_sideband)
builtin/worktree.c +1 -0
@@ -401,6 +401,7 @@ static int add_worktree(const char *path, const char *refname, cp.dir = path; cp.env = env; cp.argv =NULL ; + cp.trace2_hook_name ="post-checkout" ; argv_array_pushl(&cp.args, absolute_path(hook), oid_to_hex(&null_oid), oid_to_hex(&commit->object.oid),
sequencer.c +2 -0
@@ -1102,6 +1102,7 @@ static int run_rewrite_hook(const struct object_id *oldoid, proc.argv = argv; proc.in =-1 ; proc.stdout_to_stderr =1 ; + proc.trace2_hook_name ="post-rewrite" ; code =start_command (&proc);if (code)
@@ -3771,6 +3772,7 @@ static int pick_commits(struct repository *r, hook.in =open (rebase_path_rewritten_list (),O_RDONLY ); hook.stdout_to_stderr =1 ; + hook.trace2_hook_name ="post-rewrite" ;argv_array_push (&hook.args, post_rewrite_hook);argv_array_push (&hook.args,"rebase" );/* we don't care if this hook failed */
transport.c +1 -0
@@ -1061,6 +1061,7 @@ static int run_pre_push_hook(struct transport *transport, proc.argv = argv; proc.in =-1 ; + proc.trace2_hook_name ="pre-push" ;if (start_command (&proc)) {finish_command (&proc);
Signature · 1 line
gitgitgadget
PATCH v2 9/14 trace2:data: add trace2 instrumentation to index read/write
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 events to measure reading and writing the index.
--- read-cache.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-)
read-cache.c +46 -1
@@ -2232,6 +2232,14 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)load_index_extensions (&p); }munmap ((void *)mmap, mmap_size); + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_data_intmax ("index" , the_repository,"read/version" , istate->version); +trace2_data_intmax ("index" , the_repository,"read/cache_nr" , istate->cache_nr); +return istate->cache_nr; unmap:
@@ -2263,9 +2271,17 @@ int read_index_from(struct index_state *istate, const char *path,if (istate->initialized)return istate->cache_nr; +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_region_enter_printf ("index" ,"do_read_index" , the_repository, +"%s" , path);trace_performance_enter (); ret =do_read_index (istate, path,0 );trace_performance_leave ("read cache %s" , path); +trace2_region_leave_printf ("index" ,"do_read_index" , the_repository, +"%s" , path); split_index = istate->split_index;if (!split_index ||is_null_oid (&split_index->base_oid)) {
@@ -2281,7 +2297,11 @@ int read_index_from(struct index_state *istate, const char *path, base_oid_hex =oid_to_hex (&split_index->base_oid); base_path =xstrfmt ("%s/sharedindex.%s" , gitdir, base_oid_hex); +trace2_region_enter_printf ("index" ,"shared/do_read_index" , the_repository, +"%s" , base_path); ret =do_read_index (split_index->base, base_path,1 ); +trace2_region_leave_printf ("index" ,"shared/do_read_index" , the_repository, +"%s" , base_path);if (!oideq (&split_index->base_oid, &split_index->base->oid)) die(_("broken index, expect %s in %s, got %s"), base_oid_hex, base_path,
@@ -2988,6 +3008,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, istate->timestamp.sec = (unsigned int )st.st_mtime; istate->timestamp.nsec =ST_MTIME_NSEC (st);trace_performance_since (start,"write index, changed mask = %x" , istate->cache_changed); + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_data_intmax ("index" , the_repository,"write/version" , istate->version); +trace2_data_intmax ("index" , the_repository,"write/cache_nr" , istate->cache_nr); +return 0 ; }
@@ -3007,7 +3035,18 @@ static int commit_locked_index(struct lock_file *lk)static int do_write_locked_index (struct index_state *istate,struct lock_file *lock,unsigned flags) { -int ret =do_write_index (istate, lock->tempfile,0 ); +int ret; + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_region_enter_printf ("index" ,"do_write_index" , the_repository, +"%s" , lock->tempfile->filename.buf); + ret =do_write_index (istate, lock->tempfile,0 ); +trace2_region_leave_printf ("index" ,"do_write_index" , the_repository, +"%s" , lock->tempfile->filename.buf); +if (ret)return ret;if (flags &COMMIT_LOCK )
@@ -3092,7 +3131,13 @@ static int write_shared_index(struct index_state *istate,int ret;move_cache_to_base_index (istate); + +trace2_region_enter_printf ("index" ,"shared/do_write_index" , + the_repository,"%s" , (*temp)->filename.buf); ret =do_write_index (si->base, *temp,1 ); +trace2_region_enter_printf ("index" ,"shared/do_write_index" , + the_repository,"%s" , (*temp)->filename.buf); +if (ret)return ret; ret =adjust_shared_perm (get_tempfile_path (*temp));
Signature · 1 line
gitgitgadget
PATCH v2 11/14 trace2:data: add subverb to checkout command
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/checkout.c | 7 +++++++ 1 file changed, 7 insertions(+)
builtin/checkout.c +7 -0
@@ -262,6 +262,8 @@ static int checkout_paths(const struct checkout_opts *opts,struct lock_file lock_file =LOCK_INIT ;int nr_checkouts =0 ; +trace2_cmd_subverb (opts->patch_mode ?"patch" :"path" ); +if (opts->track !=BRANCH_TRACK_UNSPECIFIED )die (_ ("'%s' cannot be used with updating paths" ),"--track" );
@@ -952,6 +954,9 @@ static int switch_branches(const struct checkout_opts *opts,void *path_to_free;struct object_id rev;int flag, writeout_error =0 ; + +trace2_cmd_subverb ("branch" ); +memset (&old_branch_info,0 ,sizeof (old_branch_info)); old_branch_info.path = path_to_free =resolve_refdup ("HEAD" ,0 , &rev, &flag);if (old_branch_info.path)
@@ -1189,6 +1194,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)int status;struct strbuf branch_ref =STRBUF_INIT ; +trace2_cmd_subverb ("unborn" ); +if (!opts->new_branch)die (_ ("You are on a branch yet to be born" ));strbuf_addf (&branch_ref,"refs/heads/%s" , opts->new_branch);
Signature · 1 line
gitgitgadget
PATCH v2 12/14 trace2:data: add subverb to reset command
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/reset.c | 6 ++++++ 1 file changed, 6 insertions(+)
builtin/reset.c +6 -0
@@ -340,6 +340,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (patch_mode) {if (reset_type !=NONE )die (_ ("--patch is incompatible with --{hard,mixed,soft}" )); +trace2_cmd_subverb ("patch-interactive" );return run_add_interactive (rev,"--patch=reset" , &pathspec); }
@@ -356,6 +357,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==NONE ) reset_type =MIXED ;/* by default */ +if (pathspec.nr) +trace2_cmd_subverb ("path" ); +else +trace2_cmd_subverb (reset_type_names[reset_type]); +if (reset_type !=SOFT && (reset_type !=MIXED ||get_git_work_tree ()))setup_work_tree ();
Signature · 1 line
gitgitgadget
PATCH v2 13/14 trace2:data: add subverb for rebase
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/rebase.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
builtin/rebase.c +19 -0
@@ -819,6 +819,16 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)ACTION_EDIT_TODO ,ACTION_SHOW_CURRENT_PATCH , } action = NO_ACTION; +static const char *action_names[] = { +N_ ("undefined" ), +N_ ("continue" ), +N_ ("skip" ), +N_ ("abort" ), +N_ ("quit" ), +N_ ("edit_todo" ), +N_ ("show_current_patch" ), +NULL + };const char *gpg_sign =NULL ;struct string_list exec =STRING_LIST_INIT_NODUP ;const char *rebase_merges =NULL ;
@@ -1001,6 +1011,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("The --edit-todo action can only be used during " "interactive rebase." )); +if (trace2_is_enabled ()) { +if (is_interactive (&options)) +trace2_cmd_subverb ("interactive" ); +else if (exec.nr) +trace2_cmd_subverb ("interactive-exec" ); +else +trace2_cmd_subverb (action_names[action]); + } +switch (action) {case ACTION_CONTINUE : {struct object_id head;
Signature · 1 line
gitgitgadget
PATCH v2 14/14 trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Create unit tests for Trace2.
--- Makefile | 1 + t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 273 ++++++++++++++++++++++++++++++++++++++ t/t0210-trace2-normal.sh | 147 ++++++++++++++++++++ t/t0210/scrub_normal.perl | 48 +++++++ t/t0211-trace2-perf.sh | 163 +++++++++++++++++++++++ t/t0211/scrub_perf.perl | 76 +++++++++++ t/t0212-trace2-event.sh | 246 ++++++++++++++++++++++++++++++++++ t/t0212/parse_events.perl | 251 +++++++++++++++++++++++++++++++++++ 10 files changed, 1207 insertions(+) create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl
Makefile +1 -0
@@ -753,6 +753,7 @@ TEST_BUILTINS_OBJS += test-string-list.o TEST_BUILTINS_OBJS += test-submodule-config.o TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o +TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o TEST_BUILTINS_OBJS += test-wildmatch.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o
t/helper/test-tool.c +1 -0
@@ -49,6 +49,7 @@ static struct test_cmd cmds[] = { { "submodule-config", cmd__submodule_config }, { "submodule-nested-repo-config", cmd__submodule_nested_repo_config }, { "subprocess", cmd__subprocess }, + { "trace2", cmd__trace2 }, { "urlmatch-normalization", cmd__urlmatch_normalization }, { "wildmatch", cmd__wildmatch }, #ifdef GIT_WINDOWS_NATIVE
t/helper/test-tool.h +1 -0
@@ -44,6 +44,7 @@ int cmd__string_list(int argc, const char **argv);int cmd__submodule_config (int argc,const char **argv);int cmd__submodule_nested_repo_config (int argc,const char **argv);int cmd__subprocess (int argc,const char **argv); +int cmd__trace2 (int argc,const char **argv);int cmd__urlmatch_normalization (int argc,const char **argv);int cmd__wildmatch (int argc,const char **argv);#ifdef GIT_WINDOWS_NATIVE
t/helper/test-trace2.c new file +273 -0
273 lines not on this page · raw message
t/t0210-trace2-normal.sh new file +147 -0
147 lines not on this page · raw message
t/t0210/scrub_normal.perl new file +48 -0
48 lines not on this page · raw message
t/t0211-trace2-perf.sh new file +163 -0
163 lines not on this page · raw message
t/t0211/scrub_perf.perl new file +76 -0
76 lines not on this page · raw message
t/t0212-trace2-event.sh new file +246 -0
246 lines not on this page · raw message
t/t0212/parse_events.perl new file +251 -0
251 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v2 10/14 pack-objects: add trace2 regions
In reply to Jeff Hostetler via GitGitGadgetFrom: Derrick Stolee <[email protected]>
When studying the performance of 'git push' we would like to know how much time is spent at various parts of the command. One area that could cause performance trouble is 'git pack-objects'.
Add trace2 regions around the three main actions taken in this command:
1. Enumerate objects.
2. Prepare pack.
3. Write pack-file.
--- builtin/pack-objects.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
builtin/pack-objects.c +11 -1
@@ -33,6 +33,7 @@#include "object-store.h" #include "dir.h" #include "midx.h" +#include "trace2.h" #define IN_PACK (obj) oe_in_pack(&to_pack, obj)#define SIZE (obj) oe_size(&to_pack, obj)
@@ -3472,6 +3473,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } } +trace2_region_enter ("pack-objects" ,"enumerate-objects" , the_repository);prepare_packing_data (the_repository, &to_pack);if (progress)
@@ -3486,12 +3488,20 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)if (include_tag && nr_result)for_each_ref (add_ref_tag,NULL );stop_progress (&progress_state); +trace2_region_leave ("pack-objects" ,"enumerate-objects" , the_repository);if (non_empty && !nr_result)return 0 ; -if (nr_result) +if (nr_result) { +trace2_region_enter ("pack-objects" ,"prepare-pack" , the_repository);prepare_pack (window, depth); +trace2_region_leave ("pack-objects" ,"prepare-pack" , the_repository); + } + +trace2_region_enter ("pack-objects" ,"write-pack-file" , the_repository);write_pack_file (); +trace2_region_leave ("pack-objects" ,"write-pack-file" , the_repository); +if (progress) fprintf_ln(stderr, _("Total %"PRIu32" (delta %"PRIu32"),"
Signature · 1 line
gitgitgadget
PATCH v2 2/14 trace2: create new combined trace facility
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Create a new unified tracing facility for git. The eventual intent is to replace the current trace_printf* and trace_performance* routines with a unified set of git_trace2* routines.
In addition to the usual printf-style API, trace2 provides higer-level event verbs with fixed-fields allowing structured data to be written. This makes post-processing and analysis easier for external tools.
Trace2 defines 3 output targets. These are set using the environment variables "GIT_TR2", "GIT_TR2_PERF", and "GIT_TR2_EVENT". These may be set to "1" or to an absolute pathname (just like the current GIT_TRACE).
* GIT_TR2 is intended to be a replacement for GIT_TRACE and logs command summary data.
* GIT_TR2_PERF is intended as a replacement for GIT_TRACE_PERFORMANCE. It extends the output with columns for the command process, thread, repo, absolute and relative elapsed times. It reports events for child process start/stop, thread start/stop, and per-thread function nesting.
* GIT_TR2_EVENT is a new structured format. It writes event data as a series of JSON records.
Calls to trace2 functions log to any of the 3 output targets enabled without the need to call different trace_printf* or trace_performance* routines.
--- Makefile | 10 + builtin/submodule--helper.c | 11 +- cache.h | 1 + common-main.c | 12 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- config.c | 2 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 +++ remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 63 ++- run-command.h | 17 +- sh-i18n--envsubst.c | 3 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 3 + t/t0001-init.sh | 1 + trace2.c | 809 ++++++++++++++++++++++++++++++++++ trace2.h | 390 ++++++++++++++++ trace2/tr2_cfg.c | 92 ++++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 90 ++++ trace2/tr2_dst.h | 34 ++ trace2/tr2_sid.c | 67 +++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 ++ trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 126 ++++++ trace2/tr2_tgt_event.c | 606 +++++++++++++++++++++++++ trace2/tr2_tgt_normal.c | 331 ++++++++++++++ trace2/tr2_tgt_perf.c | 573 ++++++++++++++++++++++++ trace2/tr2_tls.c | 164 +++++++ trace2/tr2_tls.h | 95 ++++ trace2/tr2_verb.c | 30 ++ trace2/tr2_verb.h | 24 + usage.c | 31 ++ 39 files changed, 3774 insertions(+), 17 deletions(-) create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
Makefile +10 -0
@@ -996,6 +996,16 @@ LIB_OBJS += tempfile.o LIB_OBJS += thread-utils.o LIB_OBJS += tmp-objdir.o LIB_OBJS += trace.o +LIB_OBJS += trace2.o +LIB_OBJS += trace2/tr2_cfg.o +LIB_OBJS += trace2/tr2_dst.o +LIB_OBJS += trace2/tr2_sid.o +LIB_OBJS += trace2/tr2_tbuf.o +LIB_OBJS += trace2/tr2_tgt_event.o +LIB_OBJS += trace2/tr2_tgt_normal.o +LIB_OBJS += trace2/tr2_tgt_perf.o +LIB_OBJS += trace2/tr2_tls.o +LIB_OBJS += trace2/tr2_verb.o LIB_OBJS += trailer.o LIB_OBJS += transport.o LIB_OBJS += transport-helper.o
builtin/submodule--helper.c +6 -5
@@ -1813,11 +1813,12 @@ static int update_submodules(struct submodule_update_clone *suc) {int i; -run_processes_parallel (suc->max_jobs, - update_clone_get_next_task, - update_clone_start_failure, - update_clone_task_finished, - suc); +run_processes_parallel_tr2 (suc->max_jobs, + update_clone_get_next_task, + update_clone_start_failure, + update_clone_task_finished, + suc, +"submodule" ,"parallel/update" );/* * We saved the output and put it out all at once now.
cache.h +1 -0
@@ -9,6 +9,7 @@#include "gettext.h" #include "convert.h" #include "trace.h" +#include "trace2.h" #include "string-list.h" #include "pack-revindex.h" #include "hash.h"
common-main.c +10 -2
@@ -25,12 +25,18 @@ static void restore_sigpipe_to_default(void)int main (int argc,const char **argv) { +int result; +/* * Always open file descriptors 0/1/2 to avoid clobbering files * in die(). It also avoids messing up when the pipes are dup'ed * onto stdin/stdout/stderr in the child processes we spawn. */ sanitize_stdfds (); +restore_sigpipe_to_default (); + +trace2_initialize (); +trace2_cmd_start (argv);git_resolve_executable_dir (argv[0 ]);
@@ -40,7 +46,9 @@ int main(int argc, const char **argv)attr_start (); -restore_sigpipe_to_default (); + result =cmd_main (argc, argv); + +trace2_cmd_exit (result); -return cmd_main (argc, argv); +return result; }
compat/mingw.c +10 -1
@@ -1476,19 +1476,23 @@ static int try_shell_exec(const char *cmd, char *const *argv)return 0 ; prog =path_lookup (interpr,1 );if (prog) { +int exec_id;int argc =0 ;const char **argv2;while (argv[argc]) argc++;ALLOC_ARRAY (argv2, argc +1 ); argv2[0 ] = (char *)cmd;/* full path to the script file */ memcpy (&argv2[1 ], &argv[1 ],sizeof (*argv) * argc); + exec_id =trace2_exec (prog, argv2); pid =mingw_spawnv (prog, argv2,1 );if (pid >=0 ) {int status;if (waitpid (pid, &status,0 ) <0 ) status =255 ; +trace2_exec_result (exec_id, status);exit (status); } +trace2_exec_result (exec_id,-1 ); pid =1 ;/* indicate that we tried but failed */ free (prog);free (argv2);
@@ -1501,12 +1505,17 @@ int mingw_execv(const char *cmd, char *const *argv)/* check if git_command is a shell script */ if (!try_shell_exec (cmd, argv)) {int pid, status; +int exec_id; + exec_id =trace2_exec (cmd, (const char **)argv); pid =mingw_spawnv (cmd, (const char **)argv,0 ); -if (pid <0 ) +if (pid <0 ) { +trace2_exec_result (exec_id,-1 );return -1 ; + }if (waitpid (pid, &status,0 ) <0 ) status =255 ; +trace2_exec_result (exec_id, status);exit (status); }return -1 ;
compat/mingw.h +1 -2
@@ -147,8 +147,7 @@ static inline int fcntl(int fd, int cmd, ...) errno =EINVAL ;return -1 ; } -/* bash cannot reliably detect negative return codes as failure */ -#define exit (code) exit((code) & 0xff) +#define sigemptyset (x) (void)0static inline int sigaddset (sigset_t *set,int signum) {return 0 ; }
config.c +2 -0
@@ -2656,6 +2656,8 @@ int git_config_set_gently(const char *key, const char *value)void git_config_set (const char *key,const char *value) {git_config_set_multivar (key, value,NULL ,0 ); + +trace2_cmd_set_config (key, value); }/*
exec-cmd.c +2 -0
@@ -209,6 +209,8 @@ static int git_get_exec_path(struct strbuf *buf, const char *argv0)return -1 ; } +trace2_cmd_path (buf->buf); +return 0 ; }
git-compat-util.h +7 -0
@@ -1238,6 +1238,13 @@ static inline int is_missing_file_error(int errno_)extern int cmd_main (int ,const char **); +/* +* Intercept all calls to exit() and route them to trace2 to +* optionally emit a message before calling the real exit(). +*/ +int trace2_cmd_exit_fl (const char *file,int line,int code); +#define exit (code) exit(trace2_cmd_exit_fl(__FILE__, __LINE__, (code))) +/* * You can mark a stack variable with UNLEAK(var) to avoid it being * reported as a leak by tools like LSAN or valgrind. The argument
git.c +65 -0
127 lines not on this page · raw message
remote-curl.c +7 -0
@@ -1340,6 +1340,13 @@ int cmd_main(int argc, const char **argv)string_list_init (&options.deepen_not,1 );string_list_init (&options.push_options,1 ); +/* +* Just report "remote-curl" here (folding all the various aliases +* ("git-remote-http", "git-remote-https", and etc.) here since they +* are all just copies of the same actual executable. +*/ +trace2_cmd_verb ("remote-curl" ); + remote =remote_get (argv[1 ]);if (argc >2 ) {
repository.c +2 -0
@@ -119,6 +119,8 @@ static int repo_init_gitdir(struct repository *repo, const char *gitdir)void repo_set_worktree (struct repository *repo,const char *path) { repo->worktree =real_pathdup (path,1 ); + +trace2_def_repo (repo); } static int read_and_verify_repository_format(struct repository_format *format,
repository.h +3 -0
@@ -90,6 +90,9 @@ struct repository {/* Repository's current hash algorithm, as serialized on disk. */ const struct git_hash_algo *hash_algo; +/* A unique-id for tracing purposes. */ +int trace2_repo_id; +/* Configurations */ /* Indicate if a repository has a different 'commondir' from 'gitdir' */
run-command.c +62 -1
118 lines not on this page · raw message
run-command.h +16 -1
@@ -10,6 +10,12 @@ struct child_process {struct argv_array args;struct argv_array env_array;pid_t pid; + +int trace2_child_id; +uint64_t trace2_child_us_start; +const char *trace2_child_class; +const char *trace2_hook_name; +/* * Using .in, .out, .err: * - Specify 0 for no redirections (child inherits stdin, stdout,
@@ -73,12 +79,14 @@ extern int run_hook_ve(const char *const *env, const char *name, va_list args);#define RUN_USING_SHELL 16#define RUN_CLEAN_ON_EXIT 32int run_command_v_opt (const char **argv,int opt); - +int run_command_v_opt_tr2 (const char **argv,int opt,const char *tr2_class);/* * env (the environment) is to be formatted like environ: "VAR=VALUE". * To unset an environment variable use just "VAR". */ int run_command_v_opt_cd_env (const char **argv,int opt,const char *dir,const char *const *env); +int run_command_v_opt_cd_env_tr2 (const char **argv,int opt,const char *dir,const char *const *env, +const char *tr2_class);/** * Execute the given command, sending "in" to its stdin, and capturing its
@@ -220,5 +228,12 @@ int run_processes_parallel(int n, start_failure_fn, task_finished_fn, void *pp_cb); +int run_processes_parallel_tr2 (int n, +get_next_task_fn , +start_failure_fn , +task_finished_fn , +void *pp_cb, +const char *tr2_category, +const char *tr2_label);#endif
sh-i18n--envsubst.c +3 -0
@@ -14,6 +14,7 @@*/ #include "git-compat-util.h" +#include "trace2.h" /* Substitution of environment variables in shell format strings. Copyright (C) 2003-2007 Free Software Foundation, Inc.
@@ -67,6 +68,8 @@ cmd_main (int argc, const char *argv[])/* Default values for command line options. */ /* unsigned short int show_variables = 0; */ +trace2_cmd_verb ("sh-i18n--envsubst" ); +switch (argc) {case 1 :
submodule.c +6 -5
@@ -1374,11 +1374,12 @@ int fetch_populated_submodules(struct repository *r,/* default value, "--submodule-prefix" and its value are added later */ calculate_changed_submodule_paths (r); -run_processes_parallel (max_parallel_jobs, - get_next_submodule, - fetch_start_failure, - fetch_finish, - &spf); +run_processes_parallel_tr2 (max_parallel_jobs, + get_next_submodule, + fetch_start_failure, + fetch_finish, + &spf, +"submodule" ,"parallel/fetch" );argv_array_clear (&spf.args); out:
t/helper/test-parse-options.c +3 -0
@@ -2,6 +2,7 @@#include "cache.h" #include "parse-options.h" #include "string-list.h" +#include "trace2.h" static int boolean =0 ;static int integer =0 ;
@@ -153,6 +154,8 @@ int cmd__parse_options(int argc, const char **argv)int i;int ret =0 ; +trace2_cmd_verb ("_parse_" ); + argc =parse_options (argc, (const char **)argv, prefix, options, usage,0 );if (length_cb.called) {
t/helper/test-tool.c +3 -0
@@ -1,5 +1,6 @@#include "git-compat-util.h" #include "test-tool.h" +#include "trace2.h" struct test_cmd {const char *name;
@@ -78,6 +79,8 @@ int cmd_main(int argc, const char **argv)if (!strcmp (cmds[i].name, argv[1 ])) { argv++; argc--; +trace2_cmd_verb (cmds[i].name); +trace2_cmd_list_config ();return cmds[i].fn (argc, argv); } }
t/t0001-init.sh +1 -0
@@ -93,6 +93,7 @@ test_expect_success 'No extra GIT_* on alias scripts' ' sed -n \ -e "/^GIT_PREFIX=/d" \ -e "/^GIT_TEXTDOMAINDIR=/d" \ + -e "/^GIT_TR2_PARENT/d" \ -e "/^GIT_/s/=.*//p" | sort EOF
trace2.c new file +809 -0
809 lines not on this page · raw message
trace2.h new file +390 -0
390 lines not on this page · raw message
trace2/tr2_cfg.c new file +92 -0
92 lines not on this page · raw message
trace2/tr2_cfg.h new file +19 -0
@@ -0,0 +1,19 @@ +#ifndef TR2_CFG_H +#define TR2_CFG_H + +/* +* Iterate over all config settings and emit 'def_param' events for the +* "interesting" ones to TRACE2. +*/ +void tr2_cfg_list_config_fl (const char *file,int line); + +/* +* Emit a "def_param" event for the given key/value pair IF we consider +* the key to be "interesting". +*/ +void tr2_cfg_set_fl (const char *file,int line, +const char *key,const char *value); + +void tr2_cfg_free_patterns (void ); + +#endif /* TR2_CFG_H */
trace2/tr2_dst.c new file +90 -0
90 lines not on this page · raw message
trace2/tr2_dst.h new file +34 -0
@@ -0,0 +1,34 @@ +#ifndef TR2_DST_H +#define TR2_DST_H + +struct tr2_dst { +const char *const env_var_name; +int fd; +unsigned int initialized :1 ; +unsigned int need_close :1 ; +}; + +/* +* Disable TRACE2 on the destination. In TRACE2 a destination (DST) +* wraps a file descriptor; it is associated with a TARGET which +* defines the formatting. +*/ +void tr2_dst_trace_disable (struct tr2_dst *dst); + +/* +* Return the file descriptor for the DST. +* If 0, the dst is closed or disabled. +*/ +int tr2_dst_get_trace_fd (struct tr2_dst *dst); + +/* +* Return true if the DST is opened for writing. +*/ +int tr2_dst_trace_want (struct tr2_dst *dst); + +/* +* Write a single line/message to the trace file. +*/ +void tr2_dst_write_line (struct tr2_dst *dst,struct strbuf *buf_line); + +#endif /* TR2_DST_H */
trace2/tr2_sid.c new file +67 -0
67 lines not on this page · raw message
trace2/tr2_sid.h new file +18 -0
@@ -0,0 +1,18 @@ +#ifndef TR2_SID_H +#define TR2_SID_H + +/* +* Get our session id. Compute if necessary. +*/ +const char *tr2_sid_get (void ); + +/* +* Get our process depth. A top-level git process invoked from the +* command line will have depth=0. A child git process will have +* depth=1 and so on. +*/ +int tr2_sid_depth (void ); + +void tr2_sid_release (void ); + +#endif /* TR2_SID_H */
trace2/tr2_tbuf.c new file +32 -0
@@ -0,0 +1,32 @@ +#include "cache.h" +#include "tr2_tbuf.h" + +void tr2_tbuf_local_time (struct tr2_tbuf *tb) +{ +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +localtime_r (&secs, &tm); + +xsnprintf (tb->buf,sizeof (tb->buf),"%02d:%02d:%02d.%06ld" , + tm.tm_hour, tm.tm_min, tm.tm_sec, (long )tv.tv_usec); +} + +void tr2_tbuf_utc_time (struct tr2_tbuf *tb) +{ +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +gmtime_r (&secs, &tm); + +xsnprintf (tb->buf,sizeof (tb->buf),"%4d-%02d-%02d %02d:%02d:%02d.%06ld" , + tm.tm_year+1900 , tm.tm_mon+1 , tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, (long )tv.tv_usec); +} +
trace2/tr2_tbuf.h new file +23 -0
@@ -0,0 +1,23 @@ +#ifndef TR2_TBUF_H +#define TR2_TBUF_H + +/* +* A simple wrapper around a fixed buffer to avoid C syntax +* quirks and the need to pass around an additional size_t +* argument. +*/ +struct tr2_tbuf { +char buf[32 ]; +}; + +/* +* Fill buffer with formatted local time string. +*/ +void tr2_tbuf_local_time (struct tr2_tbuf *tb); + +/* +* Fill buffer with formatted UTC time string. +*/ +void tr2_tbuf_utc_time (struct tr2_tbuf *tb); + +#endif /* TR2_TBUF_H */
trace2/tr2_tgt.h new file +126 -0
126 lines not on this page · raw message
trace2/tr2_tgt_event.c new file +606 -0
606 lines not on this page · raw message
trace2/tr2_tgt_normal.c new file +331 -0
164 lines not on this page · raw message
trace2/tr2_tgt_perf.c new file +573 -0
573 lines not on this page · raw message
trace2/tr2_tls.c new file +164 -0
164 lines not on this page · raw message
trace2/tr2_tls.h new file +95 -0
95 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
PATCH v2 6/14 trace2:data: add trace2 sub-process classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 classification for long-running processes started in sub-process.c
--- sub-process.c | 1 + 1 file changed, 1 insertion(+)
sub-process.c +1 -0
@@ -88,6 +88,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co process->out =-1 ; process->clean_on_exit =1 ; process->clean_on_exit_handler = subprocess_exit_handler; + process->trace2_child_class ="subprocess" ; err =start_command (process);if (err) {
Signature · 1 line
gitgitgadget
PATCH v2 4/14 trace2:data: add trace2 regions to wt-status
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2_region_enter() and trace2_region_leave() calls around the various phases of a status scan. This gives elapsed time for each phase in the GIT_TR2_PERF and GIT_TR2_EVENT trace target.
Also, these Trace2 calls now use s->repo rather than the_repository.
--- wt-status.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-)
wt-status.c +21 -2
@@ -748,12 +748,23 @@ static int has_unmerged(struct wt_status *s)void wt_status_collect (struct wt_status *s) { +trace2_region_enter ("status" ,"worktrees" , s->repo);wt_status_collect_changes_worktree (s); -if (s->is_initial) +trace2_region_leave ("status" ,"worktrees" , s->repo); + +if (s->is_initial) { +trace2_region_enter ("status" ,"initial" , s->repo);wt_status_collect_changes_initial (s); -else +trace2_region_leave ("status" ,"initial" , s->repo); + }else { +trace2_region_enter ("status" ,"index" , s->repo);wt_status_collect_changes_index (s); +trace2_region_leave ("status" ,"index" , s->repo); + } + +trace2_region_enter ("status" ,"untracked" , s->repo);wt_status_collect_untracked (s); +trace2_region_leave ("status" ,"untracked" , s->repo);wt_status_get_state (s->repo, &s->state, s->branch && !strcmp (s->branch,"HEAD" ));if (s->state.merge_in_progress && !has_unmerged (s))
@@ -2291,6 +2302,12 @@ static void wt_porcelain_v2_print(struct wt_status *s)void wt_status_print (struct wt_status *s) { +trace2_data_intmax ("status" , s->repo,"count/changed" , s->change.nr); +trace2_data_intmax ("status" , s->repo,"count/untracked" , s->untracked.nr); +trace2_data_intmax ("status" , s->repo,"count/ignored" , s->ignored.nr); + +trace2_region_enter ("status" ,"print" , s->repo); +switch (s->status_format) {case STATUS_FORMAT_SHORT :wt_shortstatus_print (s);
@@ -2309,6 +2326,8 @@ void wt_status_print(struct wt_status *s)wt_longstatus_print (s);break ; } + +trace2_region_leave ("status" ,"print" , s->repo); }/**
Signature · 1 line
gitgitgadget
Re: [PATCH v2 14/14] trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
In reply to Jeff Hostetler via GitGitGadgett/t0211-trace2-perf.sh line 53
Quoted 5 lines · Jeff Hostetler via GitGitGadget
t/t0211-trace2-perf.sh new file +5
-0+test_expect_success 'perf stream, return code 0' ' + test_when_finished "rm trace.perf actual expect" && + GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF && + $TT trace2 001return 0 && + unset GIT_TR2_PERF &&
Do not do this. Stoppage in any different one of these steps would leave the state of the process in different state and affects later tests (e.g. TR2_PERF may or may not be exported).
Is $TT always "test-tool" (i.e. the binary, not a shell function)? If so, consider using a single-short export, i.e.
GIT_TR2_PERF="..." test-tool trace2 001return 0 &&
Re: [PATCH v2 14/14] trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
Quoted 12 lines · Junio C Hamano
t/t0211-trace2-perf.sh line 53
Quoted 5 lines · Jeff Hostetler via GitGitGadget
t/t0211-trace2-perf.sh new file +5
-0+test_expect_success 'perf stream, return code 0' ' + test_when_finished "rm trace.perf actual expect" && + GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF && + $TT trace2 001return 0 && + unset GIT_TR2_PERF &&Do not do this. Stoppage in any different one of these steps would leave the state of the process in different state and affects later tests (e.g.
TR2_PERFmay or may not be exported).
Quoted 5 lines
Is $TT always "test-tool" (i.e. the binary, not a shell function)? If so, consider using a single-short export, i.e.
GIT_TR2_PERF="..." test-tool trace2 001return 0 &&
I'll redo the fix for this to use the inline form
and send a new version tomorrow.
Thanks
Jeff
Quoted 10 lines · Josh Steadmon
Quoted 9 lines · Jeff Hostetler via GitGitGadget
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
A new design doc in
Documentation/technical/api-trace2.txt(in the first commit) explains the relationship of Trace2 to the current tracing facility. Calls to the current tracing facility have not been changed, rather new trace2 calls have been added so that both continue to work in parallel for the time being.
...
Quoted 8 lines
Several patches in this series have many style diffs as reported by clang-format. Not all the diffs actually improve readability, but many do. If you have clang-format installed, you can run:
git clang-format --style file --diff --extensions c,h ${commit}^ ${commit}
for each commit in the series to see what it thinks needs to be changed.
Thanks for the pointer. I'll address this in my V3 version. Yes, there were lots of complaints. Some were more noise than anything else, but there were some good ones in there.
Quoted 6 lines
Other than that, I don't have any comments apart from what the other reviewers have already mentioned.
Thanks for the series!
Thanks,
Jeff
PATCH v3 3/14 trace2: collect platform-specific process information
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add optional platform-specific code to log information about the current process.
On Windows, this includes whether git.exe is running under a debugger and information about the ancestors of the process.
The purpose of this information is to help indicate if the process was launched interactively or in the background by an IDE, for example.
--- common-main.c | 1 + compat/win32/ancestry.c | 101 ++++++++++++++++++++++++++++++++++++++++ config.mak.uname | 2 + trace2.h | 14 ++++++ 4 files changed, 118 insertions(+) create mode 100644 compat/win32/ancestry.c
common-main.c +1 -0
@@ -37,6 +37,7 @@ int main(int argc, const char **argv)trace2_initialize ();trace2_cmd_start (argv); +trace2_collect_process_info ();git_resolve_executable_dir (argv[0 ]);
compat/win32/ancestry.c new file +101 -0
101 lines not on this page · raw message
config.mak.uname +2 -0
@@ -391,6 +391,7 @@ ifeq ($(uname_S),Windows) BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE COMPAT_OBJS = compat/msvc.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ + compat/win32/ancestry.o \ compat/win32/dirent.o COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
@@ -545,6 +546,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ + compat/win32/ancestry.o \ compat/win32/path-utils.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o
trace2.h +14 -0
@@ -363,4 +363,18 @@ __attribute__((format (printf, 1, 2)))void trace2_printf (const char *fmt, ...);#endif +/* +* Optional platform-specific code to dump information about the +* current and any parent process(es). This is intended to allow +* post-processors to know who spawned this git instance and anything +* else the platform may be able to tell us about the current process. +*/ +#if defined(GIT_WINDOWS_NATIVE ) +void trace2_collect_process_info (void ); +#else +#define trace2_collect_process_info () \ + do { \ + } while (0) +#endif +#endif /* TRACE2_H */
Signature · 1 line
gitgitgadget
PATCH v3 4/14 trace2:data: add trace2 regions to wt-status
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2_region_enter() and trace2_region_leave() calls around the various phases of a status scan. This gives elapsed time for each phase in the GIT_TR2_PERF and GIT_TR2_EVENT trace target.
Also, these Trace2 calls now use s->repo rather than the_repository.
--- wt-status.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
wt-status.c +22 -2
@@ -748,12 +748,23 @@ static int has_unmerged(struct wt_status *s)void wt_status_collect (struct wt_status *s) { +trace2_region_enter ("status" ,"worktrees" , s->repo);wt_status_collect_changes_worktree (s); -if (s->is_initial) +trace2_region_leave ("status" ,"worktrees" , s->repo); + +if (s->is_initial) { +trace2_region_enter ("status" ,"initial" , s->repo);wt_status_collect_changes_initial (s); -else +trace2_region_leave ("status" ,"initial" , s->repo); + }else { +trace2_region_enter ("status" ,"index" , s->repo);wt_status_collect_changes_index (s); +trace2_region_leave ("status" ,"index" , s->repo); + } + +trace2_region_enter ("status" ,"untracked" , s->repo);wt_status_collect_untracked (s); +trace2_region_leave ("status" ,"untracked" , s->repo);wt_status_get_state (s->repo, &s->state, s->branch && !strcmp (s->branch,"HEAD" ));if (s->state.merge_in_progress && !has_unmerged (s))
@@ -2291,6 +2302,13 @@ static void wt_porcelain_v2_print(struct wt_status *s)void wt_status_print (struct wt_status *s) { +trace2_data_intmax ("status" , s->repo,"count/changed" , s->change.nr); +trace2_data_intmax ("status" , s->repo,"count/untracked" , + s->untracked.nr); +trace2_data_intmax ("status" , s->repo,"count/ignored" , s->ignored.nr); + +trace2_region_enter ("status" ,"print" , s->repo); +switch (s->status_format) {case STATUS_FORMAT_SHORT :wt_shortstatus_print (s);
@@ -2309,6 +2327,8 @@ void wt_status_print(struct wt_status *s)wt_longstatus_print (s);break ; } + +trace2_region_leave ("status" ,"print" , s->repo); }/**
Signature · 1 line
gitgitgadget
PATCH v3 1/14 trace2: Documentation/technical/api-trace2.txt
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Created design document for Trace2 feature.
--- Documentation/technical/api-trace2.txt | 1347 ++++++++++++++++++++++++ 1 file changed, 1347 insertions(+) create mode 100644 Documentation/technical/api-trace2.txt
Documentation/technical/api-trace2.txt new file +1347 -0
@@ -0,0 +1,1347 @@ += Trace2 API + +The Trace2 API can be used to print debug, performance, and telemetry +information to stderr or a file. The Trace2 feature is inactive unless +explicitly enabled by enabling one or more Trace2 Targets. + +The Trace2 API is intended to replace the existing (Trace1) +printf-style tracing provided by the existing `GIT_TRACE` and +`GIT_TRACE_PERFORMANCE` facilities. During initial implementation, +Trace2 and Trace1 may operate in parallel. + +The Trace2 API defines a set of high-level messages with known fields, +such as (`start`: `argv`) and (`exit`: {`exit-code`, `elapsed-time`}). + +Trace2 instrumentation throughout the Git code base sends Trace2 +messages to the enabled Trace2 Targets. Targets transform these +messages content into purpose-specific formats and write events to +their data streams. In this manner, the Trace2 API can drive +many different types of analysis. + +Targets are defined using a VTable allowing easy extension to other +formats in the future. This might be used to define a binary format, +for example. + +== Trace2 Targets + +Trace2 defines the following set of Trace2 Targets. +Format details are given in a later section. + +`GIT_TR2` (NORMAL):: + + a simple printf format like GIT_TRACE. ++ +------------ +$ export GIT_TR2=~/log.normal +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.normal +12:28:42.620009 common-main.c:38 version 2.20.1.155.g426c96fcdb +12:28:42.620989 common-main.c:39 start git version +12:28:42.621101 git.c:432 cmd_verb version (version) +12:28:42.621215 git.c:662 exit elapsed:0.001227 code:0 +12:28:42.621250 trace2/tr2_tgt_normal.c:124 atexit elapsed:0.001265 code:0 +------------ + +`GIT_TR2_PERF` (PERF):: + + a column-based format to replace GIT_TRACE_PERFORMANCE suitable for + development and testing, possibly to complement tools like gprof. ++ +------------ +$ export GIT_TR2_PERF=~/log.perf +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.perf +12:28:42.620675 common-main.c:38 | d0 | main | version | | | | | 2.20.1.155.g426c96fcdb +12:28:42.621001 common-main.c:39 | d0 | main | start | | | | | git version +12:28:42.621111 git.c:432 | d0 | main | cmd_verb | | | | | version (version) +12:28:42.621225 git.c:662 | d0 | main | exit | | 0.001227 | | | code:0 +12:28:42.621259 trace2/tr2_tgt_perf.c:211 | d0 | main | atexit | | 0.001265 | | | code:0 +------------ + +`GIT_TR2_EVENT` (EVENT):: + + a JSON-based format of event data suitable for telemetry analysis. ++ +------------ +$ export GIT_TR2_EVENT=~/log.event +$ git version +git version 2.20.1.155.g426c96fcdb +------------ ++ +------------ +$ cat ~/log.event +{"event":"version","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.620713","file":"common-main.c","line":38,"evt":"1","exe":"2.20.1.155.g426c96fcdb"} +{"event":"start","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621027","file":"common-main.c","line":39,"argv":["git","version"]} +{"event":"cmd_verb","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621122","file":"git.c","line":432,"name":"version","hierarchy":"version"} +{"event":"exit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621236","file":"git.c","line":662,"t_abs":0.001227,"code":0} +{"event":"atexit","sid":"1547659722619736-11614","thread":"main","time":"2019-01-16 17:28:42.621268","file":"trace2/tr2_tgt_event.c","line":163,"t_abs":0.001265,"code":0} +------------ + +== Enabling a Target + +A Trace2 Target is enabled when the corresponding environment variable +(`GIT_TR2`, `GIT_TR2_PERF`, or `GIT_TR2_EVENT`) is set. The following +values are recognized. + +`0`:: +`false`:: + + Disables the target. + +`1`:: +`true`:: + + Enables the target and writes stream to `STDERR`. + +`[2-9]`:: + + Enables the target and writes to the already opened file descriptor. + +`<absolute-pathname>`:: + + Enables the target, opens and writes to the file in append mode. + +`af_unix:<absolute-pathname>`:: + + Enables the target, opens and writes to a Unix Domain Socket + (on platforms that support them). + +== Trace2 API + +All public Trace2 functions and macros are defined in `trace2.h` and +`trace2.c`. All public symbols are prefixed with `trace2_`. + +There are no public Trace2 data structures. + +The Trace2 code also defines a set of private functions and data types +in the `trace2/` directory. These symbols are prefixed with `tr2_` +and should only be used by functions in `trace2.c`. + +== Conventions for Public Functions and Macros + +The functions defined by the Trace2 API are declared and documented +in `trace2.h`. It defines the API functions and wrapper macros for +Trace2. + +Some functions have a `_fl()` suffix to indicate that they take `file` +and `line-number` arguments. + +Some functions have a `_va_fl()` suffix to indicate that they also +take a `va_list` argument. + +Some functions have a `_printf_fl()` suffix to indicate that they also +take a varargs argument. + +There are CPP wrapper macros and ifdefs to hide most of these details. +See `trace2.h` for more details. The following discussion will only +describe the simplified forms. + +== Public API + +All Trace2 API functions send a messsage to all of the active +Trace2 Targets. This section describes the set of available +messages. + +It helps to divide these functions into groups for discussion +purposes. + +=== Basic Command Messages + +These are concerned with the lifetime of the overall git process. + +`void trace2_initialize()`:: + + Determines if any Trace2 Targets should be enabled and + initializes the Trace2 facility. This includes starting the + elapsed time clocks and thread local storage (TLS). ++ +This function emits a "version" message containing the version of git +and the Trace2 protocol. ++ +This function should be called from `main()` as early as possible in +the life of the process. + +`int trace2_is_enabled()`:: + + Returns 1 if Trace2 is enabled (at least one target is + active). + +`void trace2_cmd_start(int argc, const char **argv)`:: + + Emits a "start" message containing the process command line + arguments. + +`int trace2_cmd_exit(int exit_code)`:: + + Emits an "exit" message containing the process exit-code and + elapsed time. ++ +Returns the exit-code. + +`void trace2_cmd_error(const char *fmt, va_list ap)`:: + + Emits an "error" message containing a formatted error message. + +`void trace2_cmd_path(const char *pathname)`:: + + Emits a "cmd_path" message with the full pathname of the + current process. + +=== Command Detail Messages + +These are concerned with describing the specific Git command +after the command line, config, and environment are inspected. + +`void trace2_cmd_verb(const char *command_verb)`:: + + Emits a "cmd_verb" message with the canonical name of + (usually) builtin command, for example "status" or "checkout". + +`void trace2_cmd_subverb(const char *command_subverb)`:: + + Emits a "cmd_subverb" message with a qualifier name to further + describe the current git command. ++ +This message is intended to be used with git commands having multiple +major modes. For example, a "checkout" command can checkout a new +branch or can checkout a single file, so the checkout code could +emit a subverb message of "branch" or "file". + +`void trace2_cmd_alias(const char *alias, const char **argv_expansion)`:: + + Emits an "alias" message containing the alias used and the + argument expansion. + +`void trace2_def_param(const char *parameter, const char *value)`:: + + Emits a "def_param" message containing a key/value pair. ++ +This message is intended to report some global aspect of the current +command, such as a configuration setting or command line switch that +significantly affects program performance or behavior, such as +`core.abbrev`, `status.showUntrackedFiles`, or `--no-ahead-behind`. + +`void trace2_cmd_list_config()`:: + + Emits a "def_param" messages for "important" configuration + settings. ++ +The environment variable `GIT_TR2_CONFIG_PARAMS` can be set to a +list of patterns of important configuration settings, for example: +`core.*,remote.*.url`. This function will iterate over all config +settings and emit a "def_param" message for each match. + +`void trace2_cmd_set_config(const char *key, const char *value)`:: + + Emits a "def_param" message for a specific configuration + setting IFF it matches the `GIT_TR2_CONFIG_PARAMS` pattern. ++ +This is used to hook into `git_config_set()` and catch any +configuration changes and update a value previously reported by +`trace2_cmd_list_config()`. + +`void trace2_def_repo(struct repository *repo)`:: + + Registers a repository with the Trace2 layer. Assigns a + unique "repo-id" to `repo->trace2_repo_id`. ++ +Emits a "worktree" messages containing the repo-id and the worktree +pathname. ++ +Region and data messages (described later) may refer to this repo-id. ++ +The main/top-level repository will have repo-id value 1 (aka "r1"). ++ +The repo-id field is in anticipation of future in-proc submodule +repositories. + +=== Child Process Messages + +These are concerned with the various spawned child processes, +including shell scripts, git commands, editors, pagers, and hooks. + +`void trace2_child_start(struct child_process *cmd)`:: + + Emits a "child_start" message containing the "child-id", + "child-argv", and "child-classification". ++ +Before calling this, set `cmd->trace2_child_class` to a name +describing the type of child process, for example "editor". ++ +This function assigns a unique "child-id" to `cmd->trace2_child_id`. +This field is used later during the "child_exit" message to associate +it with the "child_start" message. ++ +This function should be called before spawning the child process. + +`void trace2_child_exit(struct child_proess *cmd, int child_exit_code)`:: + + Emits a "child_exit" message containing the "child-id", + the child's elapsed time and exit-code. ++ +The reported elapsed time includes the process creation overhead and +time spend waiting for it to exit, so it may be slightly longer than +the time reported by the child itself. ++ +This function should be called after reaping the child process. + +`int trace2_exec(const char *exe, const char **argv)`:: + + Emits a "exec" message containing the "exec-id" and the + argv of the new process. ++ +This function should be called before calling one of the `exec()` +variants, such as `execvp()`. ++ +This function returns a unique "exec-id". This value is used later +if the exec() fails and a "exec-result" message is necessary. + +`void trace2_exec_result(int exec_id, int error_code)`:: + + Emits a "exec_result" message containing the "exec-id" + and the error code. ++ +On Unix-based systems, `exec()` does not return if successful. +This message is used to indicate that the `exec()` failed and +that the current program is continuing. + +=== Git Thread Messages + +These messages are concerned with Git thread usage. + +`void trace2_thread_start(const char *thread_name)`:: + + Emits a "thread_start" message. ++ +The `thread_name` field should be a descriptive name, such as the +unique name of the thread-proc. A unique "thread-id" will be added +to the name to uniquely identify thread instances. ++ +Region and data messages (described later) may refer to this thread +name. ++ +This function must be called by the thread-proc of the new thread +(so that TLS data is properly initialized) and not by the caller +of `pthread_create()`. + +`void trace2_thread_exit()`:: + + Emits a "thread_exit" message containing the thread name + and the thread elapsed time. ++ +This function must be called by the thread-proc before it returns +(so that the coorect TLS data is used and cleaned up. It should +not be called by the caller of `pthread_join()`. + +=== Region and Data Messages + +These are concerned with recording performance data +over regions or spans of code. + +`void trace2_region_enter(const char *category, const char *label, const struct repository *repo)`:: + +`void trace2_region_enter_printf(const char *category, const char *label, const struct repository *repo, const char *fmt, ...)`:: + +`void trace2_region_enter_printf_va(const char *category, const char *label, const struct repository *repo, const char *fmt, va_list ap)`:: + + Emits a thread-relative "region_enter" message with optional + printf string. ++ +This function pushes a new region nesting stack level on the current +thread and starts a clock for the new stack frame. ++ +The `category` field is an arbitrary category name used to classify +regions by feature area, such as "status" or "index". At this time +it is only just printed along with the rest of the message. It may +be used in the future to filter messages. ++ +The `label` field is an arbitrary label used to describe the activity +being started, such as "read_recursive" or "do_read_index". ++ +The `repo` field, if set, will be used to get the "repo-id", so that +recursive oerations can be attributed to the correct repository. + +`void trace2_region_leave(const char *category, const char *label, const struct repository *repo)`:: + +`void trace2_region_leave_printf(const char *category, const char *label, const struct repository *repo, const char *fmt, ...)`:: + +`void trace2_region_leave_printf_va(const char *category, const char *label, const struct repository *repo, const char *fmt, va_list ap)`:: + + Emits a thread-relative "region_leave" message with optional + printf string. ++ +This function pops the region nesting stack on the current thread +and reports the elapsed time of the stack frame. ++ +The `category`, `label`, and `repo` fields are the same as above. +The `category` and `label` do not need to match the correpsonding +"region_enter" message, but it makes the data stream easier to +understand. + +`void trace2_data_string(const char *category, const struct repository *repo, const char *key, const char * value)`:: + +`void trace2_data_intmax(const char *category, const struct repository *repo, const char *key, intmax value)`:: + +`void trace2_data_json(const char *category, const struct repository *repo, const char *key, const struct json_writer *jw)`:: + + Emits a region- and thread-relative "data" or "data_json" message. ++ +This is a key/value pair message containing information about the +current thread, region stack, and repository. This could be used +to print the number of files in a directory during a multi-threaded +recursive tree walk. + +`void trace2_printf(const char *fmt, ...)`:: + +`void trace2_printf_va(const char *fmt, va_list ap)`:: + + Emits a region- and thread-relative "printf" message. + +== Trace2 Target Formats + +=== NORMAL Format + +NORMAL format is enabled when the `GIT_TR2` environment variable is +set. + +Events are written as lines of the form: + +------------ +[<time> SP <filename>:<line> SP+] <event-name> [[SP] <event-message>] LF +------------ + +`<event-name>`:: + + is the event name. + +`<event-message>`:: + is a free-form printf message intended for human consumption. ++ +Note that this may contain embedded LF or CRLF characters that are +not escaped, so the event may spill across multiple lines. + +If `GIT_TR2_BRIEF` is true, the `time`, `filename`, and `line` fields +are omitted. + +This target is intended to be more of a summary (like GIT_TRACE) and +less detailed than the other targets. It ignores thread, region, and +data messages, for example. + +=== PERF Format + +PERF format is enabled when the `GIT_TR2_PERF` environment variable +is set. + +Events are written as lines of the form: + +------------ +[<time> SP <filename>:<line> SP+ + BAR SP] d<depth> SP + BAR SP <thread-name> SP+ + BAR SP <event-name> SP+ + BAR SP [r<repo-id>] SP+ + BAR SP [<t_abs>] SP+ + BAR SP [<t_rel>] SP+ + BAR SP [<category>] SP+ + BAR SP DOTS* <perf-event-message> + LF +------------ + +`<depth>`:: + is the git process depth. This is the number of parent + git processes. A top-level git command has depth value "d0". + A child of it has depth value "d1". A second level child + has depth value "d2" and so on. + +`<thread-name>`:: + is a unique name for the thread. The primary thread + is called "main". Other thread names are of the form "th%d:%s" + and include a unique number and the name of the thread-proc. + +`<event-name>`:: + is the event name. + +`<repo-id>`:: + when present, is a number indicating the repository + in use. A `def_repo` event is emitted when a repository is + opened. This defines the repo-id and associated worktree. + Subsequent repo-specific events will reference this repo-id. ++ +Currently, this is always "r1" for the main repository. +This field is in anticipation of in-proc submodules in the future. + +`<t_abs>`:: + when present, is the absolute time in seconds since the + program started. + +`<t_rel>`:: + when present, is time in seconds relative to the start of + the current region. For a thread-exit event, it is the elapsed + time of the thread. + +`<category>`:: + is present on region and data events and is used to + indicate a broad category, such as "index" or "status". + +`<perf-event-message>`:: + is a free-form printf message intended for human consumption. + +------------ +15:33:33.532712 wt-status.c:2310 | d0 | main | region_enter | r1 | 0.126064 | | status | label:print +15:33:33.532712 wt-status.c:2331 | d0 | main | region_leave | r1 | 0.127568 | 0.001504 | status | label:print +------------ + +If `GIT_TR2_PERF_BRIEF` is true, the `time`, `file`, and `line` +fields are omitted. + +------------ +d0 | main | region_leave | r1 | 0.011717 | 0.009122 | index | label:preload +------------ + +The PERF target is intended for interactive performance analysis +during development and is quite noisy. + +=== EVENT Format + +EVENT format is enabled when the `GIT_TR2_EVENT` environment +variable is set. + +Each event is a JSON-object containing multiple key/value pairs +written as a single line and followed by a LF. + +------------ +'{' <key> ':' <value> [',' <key> ':' <value>]* '}' LF +------------ + +Some key/value pairs are common to all events and some are +event-specific. + +==== Common Key/Value Pairs + +The following key/value pairs are common to all events: + +------------ +{ + "event":"version", + "sid":"1547659722619736-11614", + "thread":"main", + "time":"2019-01-16 17:28:42.620713", + "file":"common-main.c", + "line":38, + ... +} +------------ + +`"event":<event>`:: + is the event name. + +`"sid":<sid>`:: + is the session-id. This is a unique string to identify the + process instance to allow all events emitted by a process to + be identified. A session-id is used instead of a PID because + PIDs are recycled by the OS. For child git processes, the + session-id is prepended with the session-id of the parent git + process to allow parent-child relationships to be identified + during post-processing. + +`"thread":<thread>`:: + is the thread name. + +`"time":<time>`:: + is the UTC time of the event. + +`"file":<filename>`:: + is source file generating the event. + +`"line":<line-number>`:: + is the integer source line number generating the event. + +`"repo":<repo-id>`:: + when present, is the integer repo-id as described previously. + +If `GIT_TR2_EVENT_BRIEF` is true, the `file` and `line` fields are omitted +from all events and the `time` field is only present on the "start" and +"atexit" events. + +==== Event-Specific Key/Value Pairs + +`"version"`:: + This event gives the version of the executable and the EVENT format. ++ +------------ +{ + "event":"version", + ... + "evt":"1", # EVENT format version + "exe":"2.20.1.155.g426c96fcdb" # git version +} +------------ + +`"start"`:: + This event contains the complete argv received by main(). ++ +------------ +{ + "event":"start", + ... + "argv":["git","version"] +} +------------ + +`"exit"`:: + This event is emitted when git calls `exit()`. ++ +------------ +{ + "event":"exit", + ... + "t_abs":0.001227, # elapsed time in seconds + "code":0 # exit code +} +------------ + +`"atexit"`:: + This event is emitted by the Trace2 `atexit` routine during + final shutdown. It should be the last event emitted by the + process. ++ +(The elapsed time reported here is greater than the time reported in +the "exit" event because it runs after all other atexit tasks have +completed.) ++ +------------ +{ + "event":"atexit", + ... + "t_abs":0.001227, # elapsed time in seconds + "code":0 # exit code +} +------------ + +`"signal"`:: + This event is emitted when the program is terminated by a user + signal. Depending on the platform, the signal event may + prevent the "atexit" event from being generated. ++ +------------ +{ + "event":"signal", + ... + "t_abs":0.001227, # elapsed time in seconds + "signal":13 # SIGTERM, SIGINT, etc. +} +------------ + +`"error"`:: + This event is emitted when one of the `error()`, `die()`, + or `usage()` functions are called. ++ +------------ +{ + "event":"error", + ... + "msg":"invalid option: --cahced", # formatted error message + "fmt":"invalid option: %s" # error format string +} +------------ ++ +The error event may be emitted more than once. The format string +allows post-processors to group errors by type without worrying +about specific error arguments. + +`"cmd_path"`:: + This event contains the discovered full path of the git + executable (on platforms that are configured to resolve it). ++ +------------ +{ + "event":"cmd_path", + ... + "path":"C:/work/gfw/git.exe" +} +------------ + +`"cmd_verb"`:: + This event contains the primary verb for this git process + and the hierarchy of verbs from parent git processes. ++ +------------ +{ + "event":"cmd_verb", + ... + "name":"pack-objects", + "hierarchy":"push/pack-objects" +} +------------ ++ +Normally, the "name" field contains the canonical name of the +command. When a canonical verb is not available, one of +these special values are used: ++ +------------ +"_query_" # "git --html-path" +"_run_dashed_" # when "git foo" tries to run "git-foo" +"_run_shell_alias_" # alias expansion to a shell command +"_run_git_alias_" # alias expansion to a git command +"_usage_" # usage error +------------ + +`"cmd_subverb"`:: + This event, when present, describes the variant of the main + verb. This event may be emitted more than once. ++ +------------ +{ + "event":"cmd_subverb", + ... + "name":"branch" +} +------------ ++ +The "name" field is an arbitrary string that makes sense in the +context of the primary verb. For example, checkout can checkout a +branch or an individual file. And these variations typically have +different performance characteristics that are not comparable. + +`"alias"`:: + This event is present when an alias is expanded. ++ +------------ +{ + "event":"alias", + ... + "alias":"l", # registered alias + "argv":["log","--graph"] # alias expansion +} +------------ + +`"child_start"`:: + This event describes a child process that is about to be + spawned. ++ +------------ +{ + "event":"child_start", + ... + "child_id":2, + "child_class":"?", + "use_shell":false, + "argv":["git","rev-list","--objects","--stdin","--not","--all","--quiet"] + + "hook_name":"<hook_name>" # present when child_class is "hook" + "cd":"<path>" # present when cd is required +} +------------ ++ +The "child_id" field can be used to match this child_start with the +corresponding child_exit event. ++ +The "child_class" field is a rough classification, such as "editor", +"pager", "transport/*", and "hook". Unclassified children are classified +with "?". + +`"child_exit"`:: + This event is generated after the current process has returned + from the waitpid() and collected the exit information from the + child. ++ +------------ +{ + "event":"child_exit", + ... + "child_id":2, + "pid":14708, # child PID + "code":0, # child exit-code + "t_rel":0.110605 # observed run-time of child process +} +------------ ++ +Note that the session-id of the child process is not available to +the current/spawning process, so the child's PID is reported here as +a hint for post-processing. (But it is only a hint because the child +proces may be a shell script which doesn't have a session-id.) ++ +Note that the `t_rel` field contains the observed run time in seconds +for the child process (starting before the fork/exec/spawn and +stopping after the waitpid() and includes OS process creation overhead). +So this time will be slightly larger than the atexit time reported by +the child process itself. + +`"exec"`:: + This event is generated before git attempts to `exec()` + another command rather than starting a child process. ++ +------------ +{ + "event":"exec", + ... + "exec_id":0, + "exe":"git", + "argv":["foo", "bar"] +} +------------ ++ +The "exec_id" field is a command-unique id and is only useful if the +`exec()` fails and a corresponding exec_result event is generated. + +`"exec_result"`:: + This event is generated if the `exec()` fails and control + returns to the current git command. ++ +------------ +{ + "event":"exec_result", + ... + "exec_id":0, + "code":1 # error code (errno) from exec() +} +------------ + +`"thread_start"`:: + This event is generated when a thread is started. It is + generated from *within* the new thread's thread-proc (for TLS + reasons). ++ +------------ +{ + "event":"thread_start", + ... + "thread":"th02:preload_thread" # thread name +} +------------ + +`"thread_exit"`:: + This event is generated when a thread exits. It is generated + from *within* the thread's thread-proc (for TLS reasons). ++ +------------ +{ + "event":"thread_exit", + ... + "thread":"th02:preload_thread", # thread name + "t_rel":0.007328 # thread elapsed time +} +------------ + +`"def_param"`:: + This event is generated to log a global parameter. ++ +------------ +{ + "event":"def_param", + ... + "param":"core.abbrev", + "value":"7" +} +------------ + +`"def_repo"`:: + This event defines a repo-id and associates it with the root + of the worktree. ++ +------------ +{ + "event":"def_repo", + ... + "repo":1, + "worktree":"/Users/jeffhost/work/gfw" +} +------------ ++ +As stated earlier, the repo-id is currently always 1, so there will +only be one def_repo event. Later, if in-proc submodules are +supported, a def_repo event should be emitted for each submodule +visited. + +`"region_enter"`:: + This event is generated when entering a region. ++ +------------ +{ + "event":"region_enter", + ... + "repo":1, # optional + "nesting":1, # current region stack depth + "category":"index", # optional + "label":"do_read_index", # optional + "msg":".git/index" # optional +} +------------ ++ +The `category` field may be used in a future enhancement to +do category-based filtering. ++ +The `GIT_TR2_EVENT_NESTING` environment variable can be used to +filter deeply nested regions and data events. It defaults to "2". + +`"region_leave"`:: + This event is generated when leaving a region. ++ +------------ +{ + "event":"region_leave", + ... + "repo":1, # optional + "t_rel":0.002876, # time spent in region in seconds + "nesting":1, # region stack depth + "category":"index", # optional + "label":"do_read_index", # optional + "msg":".git/index" # optional +} +------------ + +`"data"`:: + This event is generated to log a thread- and region-local + key/value pair. ++ +------------ +{ + "event":"data", + ... + "repo":1, # optional + "t_abs":0.024107, # absolute elapsed time + "t_rel":0.001031, # elapsed time in region/thread + "nesting":2, # region stack depth + "category":"index", + "key":"read/cache_nr", + "value":"3552" +} +------------ ++ +The "value" field may be an integer or a string. + +`"data-json"`:: + This event is generated to log a pre-formatted JSON string + containing structured data. ++ +------------ +{ + "event":"data_json", + ... + "repo":1, # optional + "t_abs":0.015905, + "t_rel":0.015905, + "nesting":1, + "category":"process", + "key":"windows/ancestry", + "value":["bash.exe","bash.exe"] +} +------------ + +== Example Trace2 API Usage + +Here is a hypothetical usage of the Trace2 API showing the intended +usage (without worrying about the actual Git details). + +Initialization:: + + Initialization happens in `main()`. Behind the scenes, an + `atexit` and `signal` handler are registered. ++ +---------------- +int main(int argc, const char **argv) +{ + int exit_code; + + trace2_initialize(); + trace2_cmd_start(argv); + + exit_code = cmd_main(argc, argv); + + trace2_cmd_exit(exit_code); + + return exit_code; +} +---------------- + +Command Details:: + + After the basics are established, additional command + information can be sent to Trace2 as it is discovered. ++ +---------------- +int cmd_checkout(int argc, const char **argv) +{ + trace2_cmd_verb("checkout"); + trace2_cmd_subverb("branch"); + trace2_def_repo(the_repository); + + // emit "def_param" messages for "interesting" config settings. + trace2_cmd_list_config(); + + if (do_something()) + trace2_cmd_error("Path '%s': cannot do something", path); + + return 0; +} +---------------- + +Child Processes:: + + Wrap code spawning child processes. ++ +---------------- +void run_child(...) +{ + int child_exit_code; + struct child_process cmd = CHILD_PROCESS_INIT; + ... + cmd.trace2_child_class = "editor"; + + trace2_child_start(&cmd); + child_exit_code = spawn_child_and_wait_for_it(); + trace2_child_exit(&cmd, child_exit_code); +} +---------------- ++ +For example, the following fetch command spawned ssh, index-pack, +rev-list, and gc. This example also shows that fetch took +5.199 seconds and of that 4.932 was in ssh. ++ +---------------- +$ export GIT_TR2_BRIEF=1 +$ export GIT_TR2=~/log.normal +$ git fetch origin +... +---------------- ++ +---------------- +$ cat ~/log.normal +version 2.20.1.vfs.1.1.47.g534dbe1ad1 +start git fetch origin +worktree /Users/jeffhost/work/gfw +cmd_verb fetch (fetch) +child_start[0] ssh [email protected] ... +child_start[1] git index-pack ... +... (Trace2 events from child processes omitted) +child_exit[1] pid:14707 code:0 elapsed:0.076353 +child_exit[0] pid:14706 code:0 elapsed:4.931869 +child_start[2] git rev-list ... +... (Trace2 events from child process omitted) +child_exit[2] pid:14708 code:0 elapsed:0.110605 +child_start[3] git gc --auto +... (Trace2 events from child process omitted) +child_exit[3] pid:14709 code:0 elapsed:0.006240 +exit elapsed:5.198503 code:0 +atexit elapsed:5.198541 code:0 +---------------- ++ +When a git process is a (direct or indirect) child of another +git process, it inherits Trace2 context information. This +allows the child to print a verb hierarchy. This example +shows gc as child[3] of fetch. When the gc process reports +its verb as "gc", it also reports the hierarchy as "fetch/gc". +(In this example, trace2 messages from the child process is +indented for clarity.) ++ +---------------- +$ export GIT_TR2_BRIEF=1 +$ export GIT_TR2=~/log.normal +$ git fetch origin +... +---------------- ++ +---------------- +$ cat ~/log.normal +version 2.20.1.160.g5676107ecd.dirty +start git fetch official +worktree /Users/jeffhost/work/gfw +cmd_verb fetch (fetch) +... +child_start[3] git gc --auto + version 2.20.1.160.g5676107ecd.dirty + start /Users/jeffhost/work/gfw/git gc --auto + worktree /Users/jeffhost/work/gfw + cmd_verb gc (fetch/gc) + exit elapsed:0.001959 code:0 + atexit elapsed:0.001997 code:0 +child_exit[3] pid:20303 code:0 elapsed:0.007564 +exit elapsed:3.868938 code:0 +atexit elapsed:3.868970 code:0 +---------------- + +Regions:: + + Regions can be use to time an interesting section of code. ++ +---------------- +void wt_status_collect(struct wt_status *s) +{ + trace2_region_enter("status", "worktrees", s->repo); + wt_status_collect_changes_worktree(s); + trace2_region_leave("status", "worktrees", s->repo); + + trace2_region_enter("status", "index", s->repo); + wt_status_collect_changes_index(s); + trace2_region_leave("status", "index", s->repo); + + trace2_region_enter("status", "untracked", s->repo); + wt_status_collect_untracked(s); + trace2_region_leave("status", "untracked", s->repo); +} + +void wt_status_print(struct wt_status *s) +{ + trace2_region_enter("status", "print", s->repo); + switch (s->status_format) { + ... + } + trace2_region_leave("status", "print", s->repo); +} +---------------- ++ +In this example, scanning for untracked files ran from +0.012568 to ++0.027149 (since the process started) and took 0.014581 seconds. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... + +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.160.g5676107ecd.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.010988 | | status | label:worktrees +d0 | main | region_leave | r1 | 0.011236 | 0.000248 | status | label:worktrees +d0 | main | region_enter | r1 | 0.011260 | | status | label:index +d0 | main | region_leave | r1 | 0.012542 | 0.001282 | status | label:index +d0 | main | region_enter | r1 | 0.012568 | | status | label:untracked +d0 | main | region_leave | r1 | 0.027149 | 0.014581 | status | label:untracked +d0 | main | region_enter | r1 | 0.027411 | | status | label:print +d0 | main | region_leave | r1 | 0.028741 | 0.001330 | status | label:print +d0 | main | exit | | 0.028778 | | | code:0 +d0 | main | atexit | | 0.028809 | | | code:0 +---------------- ++ +Regions may be nested. This causes messages to be indented in the +PERF target, for example. +Elapsed times are relative to the start of the correpsonding nesting +level as expected. For example, if we add region message to: ++ +---------------- +static enum path_treatment read_directory_recursive(struct dir_struct *dir, + struct index_state *istate, const char *base, int baselen, + struct untracked_cache_dir *untracked, int check_only, + int stop_at_first_file, const struct pathspec *pathspec) +{ + enum path_treatment state, subdir_state, dir_state = path_none; + + trace2_region_enter_printf("dir", "read_recursive", NULL, "%.*s", baselen, base); + ... + trace2_region_leave_printf("dir", "read_recursive", NULL, "%.*s", baselen, base); + return dir_state; +} +---------------- ++ +We can further investigate the time spent scanning for untracked files. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.162.gb4ccea44db.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +... +d0 | main | region_enter | r1 | 0.015047 | | status | label:untracked +d0 | main | region_enter | | 0.015132 | | dir | ..label:read_recursive +d0 | main | region_enter | | 0.016341 | | dir | ....label:read_recursive vcs-svn/ +d0 | main | region_leave | | 0.016422 | 0.000081 | dir | ....label:read_recursive vcs-svn/ +d0 | main | region_enter | | 0.016446 | | dir | ....label:read_recursive xdiff/ +d0 | main | region_leave | | 0.016522 | 0.000076 | dir | ....label:read_recursive xdiff/ +d0 | main | region_enter | | 0.016612 | | dir | ....label:read_recursive git-gui/ +d0 | main | region_enter | | 0.016698 | | dir | ......label:read_recursive git-gui/po/ +d0 | main | region_enter | | 0.016810 | | dir | ........label:read_recursive git-gui/po/glossary/ +d0 | main | region_leave | | 0.016863 | 0.000053 | dir | ........label:read_recursive git-gui/po/glossary/ +... +d0 | main | region_enter | | 0.031876 | | dir | ....label:read_recursive builtin/ +d0 | main | region_leave | | 0.032270 | 0.000394 | dir | ....label:read_recursive builtin/ +d0 | main | region_leave | | 0.032414 | 0.017282 | dir | ..label:read_recursive +d0 | main | region_leave | r1 | 0.032454 | 0.017407 | status | label:untracked +... +d0 | main | exit | | 0.034279 | | | code:0 +d0 | main | atexit | | 0.034322 | | | code:0 +---------------- ++ +Trace2 regions are similar to the existing trace_performance_enter() +and trace_performance_leave() routines, but are thread safe and +maintain per-thread stacks of timers. + +Data Messages:: + + Data messages added to a region. ++ +---------------- +int read_index_from(struct index_state *istate, const char *path, + const char *gitdir) +{ + trace2_region_enter_printf("index", "do_read_index", the_repository, "%s", path); + + ... + + trace2_data_intmax("index", the_repository, "read/version", istate->version); + trace2_data_intmax("index", the_repository, "read/cache_nr", istate->cache_nr); + + trace2_region_leave_printf("index", "do_read_index", the_repository, "%s", path); +} +---------------- ++ +This example shows that the index contained 3552 entries. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +d0 | main | version | | | | | 2.20.1.156.gf9916ae094.dirty +d0 | main | start | | | | | git status +d0 | main | def_repo | r1 | | | | worktree:/Users/jeffhost/work/gfw +d0 | main | cmd_verb | | | | | status (status) +d0 | main | region_enter | r1 | 0.001791 | | index | label:do_read_index .git/index +d0 | main | data | r1 | 0.002494 | 0.000703 | index | ..read/version:2 +d0 | main | data | r1 | 0.002520 | 0.000729 | index | ..read/cache_nr:3552 +d0 | main | region_leave | r1 | 0.002539 | 0.000748 | index | label:do_read_index .git/index +... +---------------- + +Thread Events:: + + Thread messages added to a thread-proc. ++ +For example, the multithreaded preload-index code can be +instrumented with a region around the thread pool and then +per-thread start and exit events within the threadproc. ++ +---------------- +static void *preload_thread(void *_data) +{ + // start the per-thread clock and emit a message. + trace2_thread_start("preload_thread"); + + // report which chunk of the array this thread was assigned. + trace2_data_intmax("index", the_repository, "offset", p->offset); + trace2_data_intmax("index", the_repository, "count", nr); + + do { + ... + } while (--nr > 0); + ... + + // report elapsed time taken by this thread. + trace2_thread_exit(); + return NULL; +} + +void preload_index(struct index_state *index, + const struct pathspec *pathspec, + unsigned int refresh_flags) +{ + trace2_region_enter("index", "preload", the_repository); + + for (i = 0; i < threads; i++) { + ... /* create thread */ + } + + for (i = 0; i < threads; i++) { + ... /* join thread */ + } + + trace2_region_leave("index", "preload", the_repository); +} +---------------- ++ +In this example preload_index() was executed by the `main` thread +and started the `preload` region. Seven threads, named +`th01:preload_thread` through `th07:preload_thread`, were started. +Events from each thread are atomically appended to the shared target +stream as they occur so they may appear in random order with respect +other threads. Finally, the main thread waits for the threads to +finish and leaves the region. ++ +Data events are tagged with the active thread name. They are used +to report the per-thread parameters. ++ +---------------- +$ export GIT_TR2_PERF_BRIEF=1 +$ export GIT_TR2_PERF=~/log.perf +$ git status +... +$ cat ~/log.perf +... +d0 | main | region_enter | r1 | 0.002595 | | index | label:preload +d0 | th01:preload_thread | thread_start | | 0.002699 | | | +d0 | th02:preload_thread | thread_start | | 0.002721 | | | +d0 | th01:preload_thread | data | r1 | 0.002736 | 0.000037 | index | offset:0 +d0 | th02:preload_thread | data | r1 | 0.002751 | 0.000030 | index | offset:2032 +d0 | th03:preload_thread | thread_start | | 0.002711 | | | +d0 | th06:preload_thread | thread_start | | 0.002739 | | | +d0 | th01:preload_thread | data | r1 | 0.002766 | 0.000067 | index | count:508 +d0 | th06:preload_thread | data | r1 | 0.002856 | 0.000117 | index | offset:2540 +d0 | th03:preload_thread | data | r1 | 0.002824 | 0.000113 | index | offset:1016 +d0 | th04:preload_thread | thread_start | | 0.002710 | | | +d0 | th02:preload_thread | data | r1 | 0.002779 | 0.000058 | index | count:508 +d0 | th06:preload_thread | data | r1 | 0.002966 | 0.000227 | index | count:508 +d0 | th07:preload_thread | thread_start | | 0.002741 | | | +d0 | th07:preload_thread | data | r1 | 0.003017 | 0.000276 | index | offset:3048 +d0 | th05:preload_thread | thread_start | | 0.002712 | | | +d0 | th05:preload_thread | data | r1 | 0.003067 | 0.000355 | index | offset:1524 +d0 | th05:preload_thread | data | r1 | 0.003090 | 0.000378 | index | count:508 +d0 | th07:preload_thread | data | r1 | 0.003037 | 0.000296 | index | count:504 +d0 | th03:preload_thread | data | r1 | 0.002971 | 0.000260 | index | count:508 +d0 | th04:preload_thread | data | r1 | 0.002983 | 0.000273 | index | offset:508 +d0 | th04:preload_thread | data | r1 | 0.007311 | 0.004601 | index | count:508 +d0 | th05:preload_thread | thread_exit | | 0.008781 | 0.006069 | | +d0 | th01:preload_thread | thread_exit | | 0.009561 | 0.006862 | | +d0 | th03:preload_thread | thread_exit | | 0.009742 | 0.007031 | | +d0 | th06:preload_thread | thread_exit | | 0.009820 | 0.007081 | | +d0 | th02:preload_thread | thread_exit | | 0.010274 | 0.007553 | | +d0 | th07:preload_thread | thread_exit | | 0.010477 | 0.007736 | | +d0 | th04:preload_thread | thread_exit | | 0.011657 | 0.008947 | | +d0 | main | region_leave | r1 | 0.011717 | 0.009122 | index | label:preload +... +d0 | main | exit | | 0.029996 | | | code:0 +d0 | main | atexit | | 0.030027 | | | code:0 +---------------- ++ +In this example, the preload region took 0.009122 seconds. The 7 threads +took between 0.006069 and 0.008947 seconds to work on their portion of +the index. Thread "th01" worked on 508 items at offset 0. Thread "th02" +worked on 508 items at offset 2032. Thread "th04" worked on 508 itemts +at offset 508. ++ +This example also shows that thread names are assigned in a racy manner +as each thread starts and allocates TLS storage. + +== Future Work + +=== Relationship to the Existing Trace Api (api-trace.txt) + +There are a few issues to resolve before we can completely +switch to Trace2. + +* Updating existing tests that assume GIT_TRACE format messages. + +* How to best handle custom GIT_TRACE_<key> messages? + +** The GIT_TRACE_<key> mechanism allows each <key> to write to a +different file (in addition to just stderr). + +** Do we want to maintain that ability or simply write to the existing +Trace2 targets (and convert <key> to a "category"). +
Signature · 1 line
gitgitgadget
PATCH v3 6/14 trace2:data: add trace2 sub-process classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 classification for long-running processes started in sub-process.c
--- sub-process.c | 1 + 1 file changed, 1 insertion(+)
sub-process.c +1 -0
@@ -88,6 +88,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co process->out =-1 ; process->clean_on_exit =1 ; process->clean_on_exit_handler = subprocess_exit_handler; + process->trace2_child_class ="subprocess" ; err =start_command (process);if (err) {
Signature · 1 line
gitgitgadget
PATCH v3 8/14 trace2:data: add trace2 hook classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Classify certain child processes as hooks.
--- builtin/am.c | 1 + builtin/receive-pack.c | 4 ++++ builtin/worktree.c | 1 + sequencer.c | 2 ++ transport.c | 1 + 5 files changed, 9 insertions(+)
builtin/am.c +1 -0
@@ -468,6 +468,7 @@ static int run_post_rewrite_hook(const struct am_state *state) cp.in =xopen (am_path (state,"rewritten" ),O_RDONLY ); cp.stdout_to_stderr =1 ; + cp.trace2_hook_name ="post-rewrite" ; ret =run_command (&cp);
builtin/receive-pack.c +4 -0
@@ -694,6 +694,8 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, proc.argv = argv; proc.in =-1 ; proc.stdout_to_stderr =1 ; + proc.trace2_hook_name = hook_name; +if (feed_state->push_options) {int i;for (i =0 ; i < feed_state->push_options->nr; i++)
@@ -807,6 +809,7 @@ static int run_update_hook(struct command *cmd) proc.stdout_to_stderr =1 ; proc.err = use_sideband ?-1 :0 ; proc.argv = argv; + proc.trace2_hook_name ="update" ; code =start_command (&proc);if (code)
@@ -1190,6 +1193,7 @@ static void run_update_post_hook(struct command *commands) proc.no_stdin =1 ; proc.stdout_to_stderr =1 ; proc.err = use_sideband ?-1 :0 ; + proc.trace2_hook_name ="post-update" ;if (!start_command (&proc)) {if (use_sideband)
builtin/worktree.c +1 -0
@@ -402,6 +402,7 @@ static int add_worktree(const char *path, const char *refname, cp.dir = path; cp.env = env; cp.argv =NULL ; + cp.trace2_hook_name ="post-checkout" ; argv_array_pushl(&cp.args, absolute_path(hook), oid_to_hex(&null_oid), oid_to_hex(&commit->object.oid),
sequencer.c +2 -0
@@ -1103,6 +1103,7 @@ static int run_rewrite_hook(const struct object_id *oldoid, proc.argv = argv; proc.in =-1 ; proc.stdout_to_stderr =1 ; + proc.trace2_hook_name ="post-rewrite" ; code =start_command (&proc);if (code)
@@ -3784,6 +3785,7 @@ static int pick_commits(struct repository *r, hook.in =open (rebase_path_rewritten_list (),O_RDONLY ); hook.stdout_to_stderr =1 ; + hook.trace2_hook_name ="post-rewrite" ;argv_array_push (&hook.args, post_rewrite_hook);argv_array_push (&hook.args,"rebase" );/* we don't care if this hook failed */
transport.c +1 -0
@@ -1061,6 +1061,7 @@ static int run_pre_push_hook(struct transport *transport, proc.argv = argv; proc.in =-1 ; + proc.trace2_hook_name ="pre-push" ;if (start_command (&proc)) {finish_command (&proc);
Signature · 1 line
gitgitgadget
PATCH v3 10/14 pack-objects: add trace2 regions
In reply to Jeff Hostetler via GitGitGadgetFrom: Derrick Stolee <[email protected]>
When studying the performance of 'git push' we would like to know how much time is spent at various parts of the command. One area that could cause performance trouble is 'git pack-objects'.
Add trace2 regions around the three main actions taken in this command:
1. Enumerate objects.
2. Prepare pack.
3. Write pack-file.
--- builtin/pack-objects.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
builtin/pack-objects.c +15 -1
@@ -33,6 +33,7 @@#include "object-store.h" #include "dir.h" #include "midx.h" +#include "trace2.h" #define IN_PACK (obj) oe_in_pack(&to_pack, obj)#define SIZE (obj) oe_size(&to_pack, obj)
@@ -3472,6 +3473,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } } +trace2_region_enter ("pack-objects" ,"enumerate-objects" , + the_repository);prepare_packing_data (the_repository, &to_pack);if (progress)
@@ -3486,12 +3489,23 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)if (include_tag && nr_result)for_each_ref (add_ref_tag,NULL );stop_progress (&progress_state); +trace2_region_leave ("pack-objects" ,"enumerate-objects" , + the_repository);if (non_empty && !nr_result)return 0 ; -if (nr_result) +if (nr_result) { +trace2_region_enter ("pack-objects" ,"prepare-pack" , + the_repository);prepare_pack (window, depth); +trace2_region_leave ("pack-objects" ,"prepare-pack" , + the_repository); + } + +trace2_region_enter ("pack-objects" ,"write-pack-file" , the_repository);write_pack_file (); +trace2_region_leave ("pack-objects" ,"write-pack-file" , the_repository); +if (progress) fprintf_ln(stderr, _("Total %"PRIu32" (delta %"PRIu32"),"
Signature · 1 line
gitgitgadget
PATCH v3 9/14 trace2:data: add trace2 instrumentation to index read/write
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 events to measure reading and writing the index.
--- read-cache.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-)
read-cache.c +50 -1
@@ -2232,6 +2232,16 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)load_index_extensions (&p); }munmap ((void *)mmap, mmap_size); + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_data_intmax ("index" , the_repository,"read/version" , + istate->version); +trace2_data_intmax ("index" , the_repository,"read/cache_nr" , + istate->cache_nr); +return istate->cache_nr; unmap:
@@ -2263,9 +2273,17 @@ int read_index_from(struct index_state *istate, const char *path,if (istate->initialized)return istate->cache_nr; +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_region_enter_printf ("index" ,"do_read_index" , the_repository, +"%s" , path);trace_performance_enter (); ret =do_read_index (istate, path,0 );trace_performance_leave ("read cache %s" , path); +trace2_region_leave_printf ("index" ,"do_read_index" , the_repository, +"%s" , path); split_index = istate->split_index;if (!split_index ||is_null_oid (&split_index->base_oid)) {
@@ -2281,7 +2299,11 @@ int read_index_from(struct index_state *istate, const char *path, base_oid_hex =oid_to_hex (&split_index->base_oid); base_path =xstrfmt ("%s/sharedindex.%s" , gitdir, base_oid_hex); +trace2_region_enter_printf ("index" ,"shared/do_read_index" , + the_repository,"%s" , base_path); ret =do_read_index (split_index->base, base_path,1 ); +trace2_region_leave_printf ("index" ,"shared/do_read_index" , + the_repository,"%s" , base_path);if (!oideq (&split_index->base_oid, &split_index->base->oid)) die(_("broken index, expect %s in %s, got %s"), base_oid_hex, base_path,
@@ -2988,6 +3010,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, istate->timestamp.sec = (unsigned int )st.st_mtime; istate->timestamp.nsec =ST_MTIME_NSEC (st);trace_performance_since (start,"write index, changed mask = %x" , istate->cache_changed); + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_data_intmax ("index" , the_repository,"write/version" , + istate->version); +trace2_data_intmax ("index" , the_repository,"write/cache_nr" , + istate->cache_nr); +return 0 ; }
@@ -3007,7 +3039,18 @@ static int commit_locked_index(struct lock_file *lk)static int do_write_locked_index (struct index_state *istate,struct lock_file *lock,unsigned flags) { -int ret =do_write_index (istate, lock->tempfile,0 ); +int ret; + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_region_enter_printf ("index" ,"do_write_index" , the_repository, +"%s" , lock->tempfile->filename.buf); + ret =do_write_index (istate, lock->tempfile,0 ); +trace2_region_leave_printf ("index" ,"do_write_index" , the_repository, +"%s" , lock->tempfile->filename.buf); +if (ret)return ret;if (flags &COMMIT_LOCK )
@@ -3092,7 +3135,13 @@ static int write_shared_index(struct index_state *istate,int ret;move_cache_to_base_index (istate); + +trace2_region_enter_printf ("index" ,"shared/do_write_index" , + the_repository,"%s" , (*temp)->filename.buf); ret =do_write_index (si->base, *temp,1 ); +trace2_region_enter_printf ("index" ,"shared/do_write_index" , + the_repository,"%s" , (*temp)->filename.buf); +if (ret)return ret; ret =adjust_shared_perm (get_tempfile_path (*temp));
Signature · 1 line
gitgitgadget
PATCH v3 5/14 trace2:data: add editor/pager child classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 process classification for editor and pager child processes.
--- editor.c | 1 + pager.c | 1 + 2 files changed, 2 insertions(+)
editor.c +1 -0
@@ -78,6 +78,7 @@ static int launch_specified_editor(const char *editor, const char *path, p.argv = args; p.env = env; p.use_shell =1 ; + p.trace2_child_class ="editor" ;if (start_command (&p) <0 )return error ("unable to start editor '%s'" , editor);
pager.c +1 -0
@@ -100,6 +100,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)argv_array_push (&pager_process->args, pager); pager_process->use_shell =1 ;setup_pager_env (&pager_process->env_array); + pager_process->trace2_child_class ="pager" ; }void setup_pager (void )
Signature · 1 line
gitgitgadget
PATCH v3 11/14 trace2:data: add subverb to checkout command
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/checkout.c | 7 +++++++ 1 file changed, 7 insertions(+)
builtin/checkout.c +7 -0
@@ -262,6 +262,8 @@ static int checkout_paths(const struct checkout_opts *opts,struct lock_file lock_file =LOCK_INIT ;int nr_checkouts =0 ; +trace2_cmd_subverb (opts->patch_mode ?"patch" :"path" ); +if (opts->track !=BRANCH_TRACK_UNSPECIFIED )die (_ ("'%s' cannot be used with updating paths" ),"--track" );
@@ -952,6 +954,9 @@ static int switch_branches(const struct checkout_opts *opts,void *path_to_free;struct object_id rev;int flag, writeout_error =0 ; + +trace2_cmd_subverb ("branch" ); +memset (&old_branch_info,0 ,sizeof (old_branch_info)); old_branch_info.path = path_to_free =resolve_refdup ("HEAD" ,0 , &rev, &flag);if (old_branch_info.path)
@@ -1189,6 +1194,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)int status;struct strbuf branch_ref =STRBUF_INIT ; +trace2_cmd_subverb ("unborn" ); +if (!opts->new_branch)die (_ ("You are on a branch yet to be born" ));strbuf_addf (&branch_ref,"refs/heads/%s" , opts->new_branch);
Signature · 1 line
gitgitgadget
v3 cover Trace2 tracing facility
In reply to Jeff Hostetler via GitGitGadgetV3 addresses: [] re-fix the trace2 tests using an inline environment variable rather than exporting and unsetting. [] overhaul the design document to include prototype declarations and more file format information. [] incorporate most of the suggestions from clang-format. [] add ability to trace to a unix domain socket. [] added forward declarations suggested by Ramsay. [] rebased onto current master to fixup conflict with sb/submodule-recursive-fetch-gets-the-tip that was merged yesterday.
----------------------------------------------------------------------------
V2 addresses: [] "jh/trace2" bad interaction with "js/vsts-ci" in "pu". [] coccinelle warnings in trace2/tr2_tgt_perf.c reported during CI testing.
----------------------------------------------------------------------------
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
A new design doc in Documentation/technical/api-trace2.txt (in the first commit) explains the relationship of Trace2 to the current tracing facility. Calls to the current tracing facility have not been changed, rather new trace2 calls have been added so that both continue to work in parallel for the time being.
[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (1):
pack-objects: add trace2 regions
Jeff Hostetler (13):
trace2: Documentation/technical/api-trace2.txt
trace2: create new combined trace facility
trace2: collect platform-specific process information trace2:data: add trace2 regions to wt-status trace2:data: add editor/pager child classification trace2:data: add trace2 sub-process classification trace2:data: add trace2 transport child classification trace2:data: add trace2 hook classification trace2:data: add trace2 instrumentation to index read/write trace2:data: add subverb to checkout command trace2:data: add subverb to reset command trace2:data: add subverb for rebase
trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
Documentation/technical/api-trace2.txt | 1347 ++++++++++++++++++++++++ Makefile | 11 + builtin/am.c | 1 + builtin/checkout.c | 7 + builtin/pack-objects.c | 16 +- builtin/rebase.c | 17 + builtin/receive-pack.c | 4 + builtin/reset.c | 6 + builtin/submodule--helper.c | 9 +- builtin/worktree.c | 1 + cache.h | 1 + common-main.c | 13 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- compat/win32/ancestry.c | 101 ++ config.c | 2 + config.mak.uname | 2 + connect.c | 3 + editor.c | 1 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 ++ pager.c | 1 + read-cache.c | 51 +- remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 59 +- run-command.h | 13 +- sequencer.c | 2 + sh-i18n--envsubst.c | 3 + sub-process.c | 1 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 4 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 274 +++++ t/t0001-init.sh | 1 + t/t0210-trace2-normal.sh | 135 +++ t/t0210/scrub_normal.perl | 48 + t/t0211-trace2-perf.sh | 153 +++ t/t0211/scrub_perf.perl | 76 ++ t/t0212-trace2-event.sh | 234 ++++ t/t0212/parse_events.perl | 251 +++++ trace2.c | 807 ++++++++++++++ trace2.h | 380 +++++++ trace2/tr2_cfg.c | 90 ++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 198 ++++ trace2/tr2_dst.h | 36 + trace2/tr2_sid.c | 67 ++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 + trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 131 +++ trace2/tr2_tgt_event.c | 590 +++++++++++ trace2/tr2_tgt_normal.c | 325 ++++++ trace2/tr2_tgt_perf.c | 536 ++++++++++ trace2/tr2_tls.c | 164 +++ trace2/tr2_tls.h | 97 ++ trace2/tr2_verb.c | 30 + trace2/tr2_verb.h | 24 + transport-helper.c | 2 + transport.c | 1 + usage.c | 31 + wt-status.c | 24 +- 66 files changed, 6567 insertions(+), 21 deletions(-) create mode 100644 Documentation/technical/api-trace2.txt create mode 100644 compat/win32/ancestry.c create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
base-commit: b5101f929789889c2e536d915698f58d5c5c6b7a
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-108%2Fjeffhostetler%2Fcore-trace2-2019-v0-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-108/jeffhostetler/core-trace2-2019-v0-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/108
Range-diff vs v2:
1: 1a90de9dab < -: ---------- trace2: Documentation/technical/api-trace2.txt
-: ---------- > 1: 60b56d6a8f trace2: Documentation/technical/api-trace2.txt
2: ea39b76d31 ! 2: bfe2fe0bbe trace2: create new combined trace facility
@@ -65,12 +65,10 @@
- update_clone_start_failure,
- update_clone_task_finished,
- suc);
-+ run_processes_parallel_tr2(suc->max_jobs,
-+ update_clone_get_next_task,
++ run_processes_parallel_tr2(suc->max_jobs, update_clone_get_next_task,
+ update_clone_start_failure,
-+ update_clone_task_finished,
-+ suc,
-+ "submodule", "parallel/update");
++ update_clone_task_finished, suc, "submodule",
++ "parallel/update");
/*
* We saved the output and put it out all at once now.
@@ -427,7 +425,7 @@
+ * we skip this for Windows because the compat layer already
+ * has to emulate the execvp() call anyway.
+ */
-+ int exec_id = trace2_exec(file, (const char**)argv);
++ int exec_id = trace2_exec(file, (const char **)argv);
+#endif
+
if (!execvp(file, argv))
@@ -493,8 +491,8 @@
+ return run_command_v_opt_cd_env_tr2(argv, opt, dir, env, NULL);
+}
+
-+int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir, const char *const *env,
-+ const char *tr2_class)
++int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,
++ const char *const *env, const char *tr2_class)
{
struct child_process cmd = CHILD_PROCESS_INIT;
cmd.argv = argv;
@@ -519,18 +517,15 @@
return 0;
}
+
-+int run_processes_parallel_tr2(int n,
-+ get_next_task_fn get_next_task,
++int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task,
+ start_failure_fn start_failure,
-+ task_finished_fn task_finished,
-+ void *pp_cb,
-+ const char *tr2_category,
-+ const char *tr2_label)
++ task_finished_fn task_finished, void *pp_cb,
++ const char *tr2_category, const char *tr2_label)
+{
+ int result;
+
-+ trace2_region_enter_printf(tr2_category, tr2_label, NULL,
-+ "max:%d", ((n < 1) ? online_cpus() : n));
++ trace2_region_enter_printf(tr2_category, tr2_label, NULL, "max:%d",
++ ((n < 1) ? online_cpus() : n));
+
+ result = run_processes_parallel(n, get_next_task, start_failure,
+ task_finished, pp_cb);
@@ -539,7 +534,6 @@
+
+ return result;
+}
-+
diff --git a/run-command.h b/run-command.h
--- a/run-command.h
@@ -568,8 +562,8 @@
* To unset an environment variable use just "VAR".
*/
int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);
-+int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir, const char *const *env,
-+ const char *tr2_class);
++int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir,
++ const char *const *env, const char *tr2_class);
/**
* Execute the given command, sending "in" to its stdin, and capturing its
@@ -577,13 +571,9 @@
start_failure_fn,
task_finished_fn,
void *pp_cb);
-+int run_processes_parallel_tr2(int n,
-+ get_next_task_fn,
-+ start_failure_fn,
-+ task_finished_fn,
-+ void *pp_cb,
-+ const char *tr2_category,
-+ const char *tr2_label);
++int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn,
++ task_finished_fn, void *pp_cb,
++ const char *tr2_category, const char *tr2_label);
#endif
@@ -612,9 +602,9 @@
--- a/submodule.c
+++ b/submodule.c
@@
- /* default value, "--submodule-prefix" and its value are added later */
- calculate_changed_submodule_paths(r);
+ calculate_changed_submodule_paths(r, &spf.changed_submodule_names);
+ string_list_sort(&spf.changed_submodule_names);
- run_processes_parallel(max_parallel_jobs,
- get_next_submodule,
- fetch_start_failure,
@@ -727,9 +717,8 @@
+ tgt_j; \
+ j++, tgt_j = tr2_tgt_builtins[j])
+
-+#define for_each_wanted_builtin(j, tgt_j) \
-+ for_each_builtin(j, tgt_j) \
-+ if (tr2_dst_trace_want(tgt_j->pdst))
++#define for_each_wanted_builtin(j, tgt_j) \
++ for_each_builtin(j, tgt_j) if (tr2_dst_trace_want(tgt_j->pdst))
+
+/*
+ * Force (rather than lazily) initialize any of the requested
@@ -745,7 +734,8 @@
+ int j;
+ int sum = 0;
+
-+ for_each_builtin(j, tgt_j) {
++ for_each_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_init())
+ sum++;
+ }
@@ -763,7 +753,8 @@
+ struct tr2_tgt *tgt_j;
+ int j;
+
-+ for_each_builtin(j, tgt_j) {
++ for_each_builtin(j, tgt_j)
++ {
+ tgt_j->pfn_term();
+ }
+}
@@ -794,8 +785,9 @@
+ * the trace output if someone calls die(), for example.
+ */
+ tr2tls_pop_unwind_self();
-+
-+ for_each_wanted_builtin(j, tgt_j) {
++
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_atexit)
+ tgt_j->pfn_atexit(us_elapsed_absolute,
+ tr2main_exit_code);
@@ -821,7 +813,8 @@
+ us_now = getnanotime() / 1000;
+ us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_signal)
+ tgt_j->pfn_signal(us_elapsed_absolute, signo);
+ }
@@ -851,7 +844,8 @@
+ /*
+ * Emit 'version' message on each active builtin target.
+ */
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_version_fl)
+ tgt_j->pfn_version_fl(file, line);
+ }
@@ -870,7 +864,8 @@
+ if (!trace2_enabled)
+ return;
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_start_fl)
+ tgt_j->pfn_start_fl(file, line, argv);
+ }
@@ -893,16 +888,18 @@
+ us_now = getnanotime() / 1000;
+ us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_exit_fl)
-+ tgt_j->pfn_exit_fl(file, line, us_elapsed_absolute, code);
++ tgt_j->pfn_exit_fl(file, line, us_elapsed_absolute,
++ code);
+ }
+
+ return code;
+}
+
-+void trace2_cmd_error_va_fl(const char *file, int line,
-+ const char *fmt, va_list ap)
++void trace2_cmd_error_va_fl(const char *file, int line, const char *fmt,
++ va_list ap)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
@@ -914,7 +911,8 @@
+ * We expect each target function to treat 'ap' as constant
+ * and use va_copy (because an 'ap' can only be walked once).
+ */
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_error_va_fl)
+ tgt_j->pfn_error_va_fl(file, line, fmt, ap);
+ }
@@ -928,7 +926,8 @@
+ if (!trace2_enabled)
+ return;
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_command_path_fl)
+ tgt_j->pfn_command_path_fl(file, line, pathname);
+ }
@@ -946,7 +945,8 @@
+ tr2_verb_append_hierarchy(command_verb);
+ hierarchy = tr2_verb_get_hierarchy();
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_command_verb_fl)
+ tgt_j->pfn_command_verb_fl(file, line, command_verb,
+ hierarchy);
@@ -962,15 +962,16 @@
+ if (!trace2_enabled)
+ return;
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_command_subverb_fl)
+ tgt_j->pfn_command_subverb_fl(file, line,
+ command_subverb);
+ }
+}
+
-+void trace2_cmd_alias_fl(const char *file, int line,
-+ const char *alias, const char **argv)
++void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
++ const char **argv)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
@@ -978,7 +979,8 @@
+ if (!trace2_enabled)
+ return;
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_alias_fl)
+ tgt_j->pfn_alias_fl(file, line, alias, argv);
+ }
@@ -992,8 +994,8 @@
+ tr2_cfg_list_config_fl(file, line);
+}
+
-+void trace2_cmd_set_config_fl(const char *file, int line,
-+ const char *key, const char *value)
++void trace2_cmd_set_config_fl(const char *file, int line, const char *key,
++ const char *value)
+{
+ if (!trace2_enabled)
+ return;
@@ -1018,15 +1020,15 @@
+ cmd->trace2_child_id = tr2tls_locked_increment(&tr2_next_child_id);
+ cmd->trace2_child_us_start = us_now;
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_child_start_fl)
+ tgt_j->pfn_child_start_fl(file, line,
+ us_elapsed_absolute, cmd);
+ }
+}
+
-+void trace2_child_exit_fl(const char *file, int line,
-+ struct child_process *cmd,
++void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
+ int child_exit_code)
+{
+ struct tr2_tgt *tgt_j;
@@ -1040,24 +1042,25 @@
+
+ us_now = getnanotime() / 1000;
+ us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
-+
++
+ if (cmd->trace2_child_us_start)
+ us_elapsed_child = us_now - cmd->trace2_child_us_start;
+ else
+ us_elapsed_child = 0;
-+
-+ for_each_wanted_builtin(j, tgt_j) {
++
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_child_exit_fl)
-+ tgt_j->pfn_child_exit_fl(
-+ file, line, us_elapsed_absolute,
-+ cmd->trace2_child_id,
-+ cmd->pid,
-+ child_exit_code, us_elapsed_child);
++ tgt_j->pfn_child_exit_fl(file, line,
++ us_elapsed_absolute,
++ cmd->trace2_child_id, cmd->pid,
++ child_exit_code,
++ us_elapsed_child);
+ }
+}
+
-+int trace2_exec_fl(const char *file, int line,
-+ const char *exe, const char **argv)
++int trace2_exec_fl(const char *file, int line, const char *exe,
++ const char **argv)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
@@ -1073,7 +1076,8 @@
+
+ exec_id = tr2tls_locked_increment(&tr2_next_exec_id);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_exec_fl)
+ tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute,
+ exec_id, exe, argv);
@@ -1095,16 +1099,15 @@
+ us_now = getnanotime() / 1000;
+ us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_exec_result_fl)
-+ tgt_j->pfn_exec_result_fl(file, line,
-+ us_elapsed_absolute,
-+ exec_id, code);
++ tgt_j->pfn_exec_result_fl(
++ file, line, us_elapsed_absolute, exec_id, code);
+ }
+}
+
-+void trace2_thread_start_fl(const char *file, int line,
-+ const char *thread_name)
++void trace2_thread_start_fl(const char *file, int line, const char *thread_name)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
@@ -1114,8 +1117,7 @@
+ if (!trace2_enabled)
+ return;
+
-+ if (tr2tls_is_main_thread())
-+ {
++ if (tr2tls_is_main_thread()) {
+ /*
+ * We should only be called from the new thread's thread-proc,
+ * so this is technically a bug. But in those cases where the
@@ -1123,7 +1125,7 @@
+ * are built with threading disabled), we need to allow it.
+ *
+ * Convert this call to a region-enter so the nesting looks
-+ * looks correct.
++ * correct.
+ */
+ trace2_region_enter_printf_fl(file, line, NULL, NULL, NULL,
+ "thread-proc on main: %s",
@@ -1136,7 +1138,8 @@
+
+ tr2tls_create_self(thread_name);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_thread_start_fl)
+ tgt_j->pfn_thread_start_fl(file, line,
+ us_elapsed_absolute);
@@ -1154,16 +1157,16 @@
+ if (!trace2_enabled)
+ return;
+
-+ if (tr2tls_is_main_thread())
-+ {
++ if (tr2tls_is_main_thread()) {
+ /*
-+ * We should only be called from the exiting thread's thread-proc,
-+ * so this is technically a bug. But in those cases where the
-+ * main thread also runs the thread-proc function (or when we
-+ * are built with threading disabled), we need to allow it.
++ * We should only be called from the exiting thread's
++ * thread-proc, so this is technically a bug. But in
++ * those cases where the main thread also runs the
++ * thread-proc function (or when we are built with
++ * threading disabled), we need to allow it.
+ *
-+ * Convert this call to a region-leave so the nesting looks
-+ * looks correct.
++ * Convert this call to a region-leave so the nesting
++ * looks correct.
+ */
+ trace2_region_leave_printf_fl(file, line, NULL, NULL, NULL,
+ "thread-proc on main");
@@ -1181,7 +1184,8 @@
+ tr2tls_pop_unwind_self();
+ us_elapsed_thread = tr2tls_region_elasped_self(us_now);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_thread_exit_fl)
+ tgt_j->pfn_thread_exit_fl(file, line,
+ us_elapsed_absolute,
@@ -1191,8 +1195,8 @@
+ tr2tls_unset_self();
+}
+
-+void trace2_def_param_fl(const char *file, int line,
-+ const char *param, const char *value)
++void trace2_def_param_fl(const char *file, int line, const char *param,
++ const char *value)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
@@ -1200,14 +1204,14 @@
+ if (!trace2_enabled)
+ return;
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_param_fl)
+ tgt_j->pfn_param_fl(file, line, param, value);
+ }
+}
+
-+void trace2_def_repo_fl(const char *file, int line,
-+ struct repository *repo)
++void trace2_def_repo_fl(const char *file, int line, struct repository *repo)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
@@ -1220,15 +1224,15 @@
+
+ repo->trace2_repo_id = tr2tls_locked_increment(&tr2_next_repo_id);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_repo_fl)
+ tgt_j->pfn_repo_fl(file, line, repo);
+ }
+}
+
+void trace2_region_enter_printf_va_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
++ const char *category, const char *label,
+ const struct repository *repo,
+ const char *fmt, va_list ap)
+{
@@ -1250,59 +1254,53 @@
+ * We expect each target function to treat 'ap' as constant
+ * and use va_copy.
+ */
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_region_enter_printf_va_fl)
+ tgt_j->pfn_region_enter_printf_va_fl(
-+ file, line, us_elapsed_absolute,
-+ category, label, repo, fmt, ap);
++ file, line, us_elapsed_absolute, category,
++ label, repo, fmt, ap);
+ }
+
+ tr2tls_push_self(us_now);
+}
+
-+void trace2_region_enter_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
-+ const struct repository *repo)
++void trace2_region_enter_fl(const char *file, int line, const char *category,
++ const char *label, const struct repository *repo)
+{
-+ trace2_region_enter_printf_va_fl(file, line,
-+ category, label, repo,
++ trace2_region_enter_printf_va_fl(file, line, category, label, repo,
+ NULL, NULL);
+}
+
+void trace2_region_enter_printf_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
++ const char *category, const char *label,
+ const struct repository *repo,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
-+ trace2_region_enter_printf_va_fl(file, line,
-+ category, label, repo,
-+ fmt, ap);
++ trace2_region_enter_printf_va_fl(file, line, category, label, repo, fmt,
++ ap);
+ va_end(ap);
+}
+
+#ifndef HAVE_VARIADIC_MACROS
+void trace2_region_enter_printf(const char *category, const char *label,
-+ const struct repository *repo,
-+ const char *fmt, ...)
++ const struct repository *repo, const char *fmt,
++ ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
-+ trace2_region_enter_printf_va_fl(NULL, 0,
-+ category, label, repo,
-+ fmt, ap);
++ trace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,
++ ap);
+ va_end(ap);
+}
+#endif
+
+void trace2_region_leave_printf_va_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
++ const char *category, const char *label,
+ const struct repository *repo,
+ const char *fmt, va_list ap)
+{
@@ -1325,67 +1323,59 @@
+ * it lines up with the corresponding push/enter.
+ */
+ us_elapsed_region = tr2tls_region_elasped_self(us_now);
-+
++
+ tr2tls_pop_self();
+
+ /*
+ * We expect each target function to treat 'ap' as constant
+ * and use va_copy.
+ */
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_region_leave_printf_va_fl)
+ tgt_j->pfn_region_leave_printf_va_fl(
+ file, line, us_elapsed_absolute,
-+ us_elapsed_region,
-+ category, label, repo,
-+ fmt, ap);
++ us_elapsed_region, category, label, repo, fmt,
++ ap);
+ }
+}
+
-+void trace2_region_leave_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
-+ const struct repository *repo)
++void trace2_region_leave_fl(const char *file, int line, const char *category,
++ const char *label, const struct repository *repo)
+{
-+ trace2_region_leave_printf_va_fl(file, line,
-+ category, label, repo,
++ trace2_region_leave_printf_va_fl(file, line, category, label, repo,
+ NULL, NULL);
+}
+
+void trace2_region_leave_printf_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
++ const char *category, const char *label,
+ const struct repository *repo,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
-+ trace2_region_leave_printf_va_fl(file, line,
-+ category, label, repo,
-+ fmt, ap);
++ trace2_region_leave_printf_va_fl(file, line, category, label, repo, fmt,
++ ap);
+ va_end(ap);
+}
+
+#ifndef HAVE_VARIADIC_MACROS
+void trace2_region_leave_printf(const char *category, const char *label,
-+ const struct repository *repo,
-+ const char *fmt, ...)
++ const struct repository *repo, const char *fmt,
++ ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
-+ trace2_region_leave_printf_va_fl(NULL, 0,
-+ category, label, repo,
-+ fmt, ap);
++ trace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,
++ ap);
+ va_end(ap);
+}
+#endif
+
-+void trace2_data_string_fl(const char *file, int line,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++void trace2_data_string_fl(const char *file, int line, const char *category,
++ const struct repository *repo, const char *key,
+ const char *value)
+{
+ struct tr2_tgt *tgt_j;
@@ -1401,18 +1391,17 @@
+ us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+ us_elapsed_region = tr2tls_region_elasped_self(us_now);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_data_fl)
+ tgt_j->pfn_data_fl(file, line, us_elapsed_absolute,
-+ us_elapsed_region,
-+ category, repo, key, value);
++ us_elapsed_region, category, repo,
++ key, value);
+ }
+}
+
-+void trace2_data_intmax_fl(const char *file, int line,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++void trace2_data_intmax_fl(const char *file, int line, const char *category,
++ const struct repository *repo, const char *key,
+ intmax_t value)
+{
+ struct strbuf buf_string = STRBUF_INIT;
@@ -1420,15 +1409,13 @@
+ if (!trace2_enabled)
+ return;
+
-+ strbuf_addf(&buf_string, "%"PRIdMAX, value);
++ strbuf_addf(&buf_string, "%" PRIdMAX, value);
+ trace2_data_string_fl(file, line, category, repo, key, buf_string.buf);
+ strbuf_release(&buf_string);
+}
+
-+void trace2_data_json_fl(const char *file, int line,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++void trace2_data_json_fl(const char *file, int line, const char *category,
++ const struct repository *repo, const char *key,
+ const struct json_writer *value)
+{
+ struct tr2_tgt *tgt_j;
@@ -1444,16 +1431,17 @@
+ us_elapsed_absolute = tr2tls_absolute_elapsed(us_now);
+ us_elapsed_region = tr2tls_region_elasped_self(us_now);
+
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_data_fl)
+ tgt_j->pfn_data_json_fl(file, line, us_elapsed_absolute,
-+ us_elapsed_region,
-+ category, repo, key, value);
++ us_elapsed_region, category,
++ repo, key, value);
+ }
+}
+
-+void trace2_printf_va_fl(const char *file, int line,
-+ const char *fmt, va_list ap)
++void trace2_printf_va_fl(const char *file, int line, const char *fmt,
++ va_list ap)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
@@ -1470,15 +1458,15 @@
+ * We expect each target function to treat 'ap' as constant
+ * and use va_copy.
+ */
-+ for_each_wanted_builtin(j, tgt_j) {
++ for_each_wanted_builtin(j, tgt_j)
++ {
+ if (tgt_j->pfn_printf_va_fl)
+ tgt_j->pfn_printf_va_fl(file, line, us_elapsed_absolute,
+ fmt, ap);
+ }
+}
+
-+void trace2_printf_fl(const char *file, int line,
-+ const char *fmt, ...)
++void trace2_printf_fl(const char *file, int line, const char *fmt, ...)
+{
+ va_list ap;
+
@@ -1565,8 +1553,8 @@
+ *
+ * Write an error message to the TRACE2 targets.
+ */
-+void trace2_cmd_error_va_fl(const char *file, int line,
-+ const char *fmt, va_list ap);
++void trace2_cmd_error_va_fl(const char *file, int line, const char *fmt,
++ va_list ap);
+
+#define trace2_cmd_error_va(fmt, ap) \
+ trace2_cmd_error_va_fl(__FILE__, __LINE__, (fmt), (ap))
@@ -1579,8 +1567,7 @@
+ */
+void trace2_cmd_path_fl(const char *file, int line, const char *pathname);
+
-+#define trace2_cmd_path(p) \
-+ trace2_cmd_path_fl(__FILE__, __LINE__, (p))
++#define trace2_cmd_path(p) trace2_cmd_path_fl(__FILE__, __LINE__, (p))
+
+/*
+ * Emit a 'cmd_verb' event with the canonical name of the (usually)
@@ -1589,8 +1576,7 @@
+ */
+void trace2_cmd_verb_fl(const char *file, int line, const char *command_verb);
+
-+#define trace2_cmd_verb(v) \
-+ trace2_cmd_verb_fl(__FILE__, __LINE__, (v))
++#define trace2_cmd_verb(v) trace2_cmd_verb_fl(__FILE__, __LINE__, (v))
+
+/*
+ * Emit a 'cmd_subverb' event to further describe the command being run.
@@ -1601,14 +1587,13 @@
+void trace2_cmd_subverb_fl(const char *file, int line,
+ const char *command_subverb);
+
-+#define trace2_cmd_subverb(sv) \
-+ trace2_cmd_subverb_fl(__FILE__, __LINE__, (sv))
++#define trace2_cmd_subverb(sv) trace2_cmd_subverb_fl(__FILE__, __LINE__, (sv))
+
+/*
+ * Emit an 'alias' expansion event.
+ */
-+void trace2_cmd_alias_fl(const char *file, int line,
-+ const char *alias, const char **argv);
++void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
++ const char **argv);
+
+#define trace2_cmd_alias(alias, argv) \
+ trace2_cmd_alias_fl(__FILE__, __LINE__, (alias), (argv))
@@ -1630,8 +1615,7 @@
+ */
+void trace2_cmd_list_config_fl(const char *file, int line);
+
-+#define trace2_cmd_list_config() \
-+ trace2_cmd_list_config_fl(__FILE__, __LINE__)
++#define trace2_cmd_list_config() trace2_cmd_list_config_fl(__FILE__, __LINE__)
+
+/*
+ * Emit a "def_param" event for the given config key/value pair IF
@@ -1640,8 +1624,8 @@
+ * Use this for new/updated config settings created/updated after
+ * trace2_cmd_list_config() is called.
+ */
-+void trace2_cmd_set_config_fl(const char *file, int line,
-+ const char *key, const char *value);
++void trace2_cmd_set_config_fl(const char *file, int line, const char *key,
++ const char *value);
+
+#define trace2_cmd_set_config(k, v) \
+ trace2_cmd_set_config_fl(__FILE__, __LINE__, (k), (v))
@@ -1656,14 +1640,12 @@
+void trace2_child_start_fl(const char *file, int line,
+ struct child_process *cmd);
+
-+#define trace2_child_start(cmd) \
-+ trace2_child_start_fl(__FILE__, __LINE__, (cmd))
++#define trace2_child_start(cmd) trace2_child_start_fl(__FILE__, __LINE__, (cmd))
+
+/*
-+ * Emit a 'child_exit' event after the child process completes.
++ * Emit a 'child_exit' event after the child process completes.
+ */
-+void trace2_child_exit_fl(const char *file, int line,
-+ struct child_process *cmd,
++void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd,
+ int child_exit_code);
+
+#define trace2_child_exit(cmd, code) \
@@ -1678,11 +1660,10 @@
+ *
+ * Returns the "exec_id".
+ */
-+int trace2_exec_fl(const char *file, int line,
-+ const char *exe, const char **argv);
++int trace2_exec_fl(const char *file, int line, const char *exe,
++ const char **argv);
+
-+#define trace2_exec(exe, argv) \
-+ trace2_exec_fl(__FILE__, __LINE__, (exe), (argv))
++#define trace2_exec(exe, argv) trace2_exec_fl(__FILE__, __LINE__, (exe), (argv))
+
+/*
+ * Emit an 'exec_result' when possible. On Unix-derived systems,
@@ -1705,7 +1686,7 @@
+ * Thread names will be decorated with an instance number automatically.
+ */
+void trace2_thread_start_fl(const char *file, int line,
-+ const char *thread_name);
++ const char *thread_name);
+
+#define trace2_thread_start(thread_name) \
+ trace2_thread_start_fl(__FILE__, __LINE__, (thread_name))
@@ -1725,9 +1706,9 @@
+ * Write a "<param> = <value>" pair describing some aspect of the
+ * run such as an important configuration setting or command line
+ * option that significantly changes command behavior.
-+ */
-+void trace2_def_param_fl(const char *file, int line,
-+ const char *param, const char *value);
++ */
++void trace2_def_param_fl(const char *file, int line, const char *param,
++ const char *value);
+
+#define trace2_def_param(param, value) \
+ trace2_def_param_fl(__FILE__, __LINE__, (param), (value))
@@ -1738,11 +1719,9 @@
+ *
+ * Emits a 'worktree' event for this repo instance.
+ */
-+void trace2_def_repo_fl(const char *file, int line,
-+ struct repository *repo);
++void trace2_def_repo_fl(const char *file, int line, struct repository *repo);
+
-+#define trace2_def_repo(repo) \
-+ trace2_def_repo_fl(__FILE__, __LINE__, repo)
++#define trace2_def_repo(repo) trace2_def_repo_fl(__FILE__, __LINE__, repo)
+
+/*
+ * Emit a 'region_enter' event for <category>.<label> with optional
@@ -1752,24 +1731,20 @@
+ * current time. This controls the indenting of all subsequent events
+ * on this thread.
+ */
-+void trace2_region_enter_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
-+ const struct repository *repo);
++void trace2_region_enter_fl(const char *file, int line, const char *category,
++ const char *label, const struct repository *repo);
+
+#define trace2_region_enter(category, label, repo) \
-+ trace2_region_enter_fl( \
-+ __FILE__, __LINE__, (category), (label), (repo))
++ trace2_region_enter_fl(__FILE__, __LINE__, (category), (label), (repo))
+
+void trace2_region_enter_printf_va_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
++ const char *category, const char *label,
+ const struct repository *repo,
+ const char *fmt, va_list ap);
+
-+#define trace2_region_enter_printf_va(category, label, repo, fmt, ap) \
-+ trace2_region_enter_printf_va_fl( \
-+ __FILE__, __LINE__, (category), (label), (repo), (fmt), (ap))
++#define trace2_region_enter_printf_va(category, label, repo, fmt, ap) \
++ trace2_region_enter_printf_va_fl(__FILE__, __LINE__, (category), \
++ (label), (repo), (fmt), (ap))
+
+void trace2_region_enter_printf_fl(const char *file, int line,
+ const char *category, const char *label,
@@ -1777,14 +1752,14 @@
+ const char *fmt, ...);
+
+#ifdef HAVE_VARIADIC_MACROS
-+#define trace2_region_enter_printf(category, label, repo, ...) \
-+ trace2_region_enter_printf_fl( \
-+ __FILE__, __LINE__, (category), (label), (repo), __VA_ARGS__)
++#define trace2_region_enter_printf(category, label, repo, ...) \
++ trace2_region_enter_printf_fl(__FILE__, __LINE__, (category), (label), \
++ (repo), __VA_ARGS__)
+#else
+__attribute__((format (region_enter_printf, 4, 5)))
+void trace2_region_enter_printf(const char *category, const char *label,
-+ const struct repository *repo,
-+ const char *fmt, ...);
++ const struct repository *repo, const char *fmt,
++ ...);
+#endif
+
+/*
@@ -1794,24 +1769,20 @@
+ * Leave current nesting level and report the elapsed time spent
+ * in this nesting level.
+ */
-+void trace2_region_leave_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
-+ const struct repository *repo);
++void trace2_region_leave_fl(const char *file, int line, const char *category,
++ const char *label, const struct repository *repo);
+
+#define trace2_region_leave(category, label, repo) \
-+ trace2_region_leave_fl( \
-+ __FILE__, __LINE__, (category), (label), (repo))
++ trace2_region_leave_fl(__FILE__, __LINE__, (category), (label), (repo))
+
+void trace2_region_leave_printf_va_fl(const char *file, int line,
-+ const char *category,
-+ const char *label,
++ const char *category, const char *label,
+ const struct repository *repo,
+ const char *fmt, va_list ap);
+
-+#define trace2_region_leave_printf_va(category, label, repo, fmt, ap) \
-+ trace2_region_leave_printf_va_fl( \
-+ __FILE__, __LINE__, (category), (label), (repo), (fmt), (ap))
++#define trace2_region_leave_printf_va(category, label, repo, fmt, ap) \
++ trace2_region_leave_printf_va_fl(__FILE__, __LINE__, (category), \
++ (label), (repo), (fmt), (ap))
+
+void trace2_region_leave_printf_fl(const char *file, int line,
+ const char *category, const char *label,
@@ -1819,14 +1790,14 @@
+ const char *fmt, ...);
+
+#ifdef HAVE_VARIADIC_MACROS
-+#define trace2_region_leave_printf(category, label, repo, ...) \
-+ trace2_region_leave_printf_fl( \
-+ __FILE__, __LINE__, (category), (label), (repo), __VA_ARGS__)
++#define trace2_region_leave_printf(category, label, repo, ...) \
++ trace2_region_leave_printf_fl(__FILE__, __LINE__, (category), (label), \
++ (repo), __VA_ARGS__)
+#else
+__attribute__((format (region_leave_printf, 4, 5)))
+void trace2_region_leave_printf(const char *category, const char *label,
-+ const struct repository *repo,
-+ const char *fmt, ...);
++ const struct repository *repo, const char *fmt,
++ ...);
+#endif
+
+/*
@@ -1838,35 +1809,29 @@
+ * for post-processing. On printf-based TRACE2 targets, this is converted
+ * into a fixed-format printf message.
+ */
-+void trace2_data_string_fl(const char *file, int line,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++void trace2_data_string_fl(const char *file, int line, const char *category,
++ const struct repository *repo, const char *key,
+ const char *value);
+
-+#define trace2_data_string(category, repo, key, value) \
-+ trace2_data_string_fl( \
-+ __FILE__, __LINE__, (category), (repo), (key), (value))
++#define trace2_data_string(category, repo, key, value) \
++ trace2_data_string_fl(__FILE__, __LINE__, (category), (repo), (key), \
++ (value))
+
-+void trace2_data_intmax_fl(const char *file, int line,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++void trace2_data_intmax_fl(const char *file, int line, const char *category,
++ const struct repository *repo, const char *key,
+ intmax_t value);
+
-+#define trace2_data_intmax(category, repo, key, value) \
-+ trace2_data_intmax_fl( \
-+ __FILE__, __LINE__, (category), (repo), (key), (value))
++#define trace2_data_intmax(category, repo, key, value) \
++ trace2_data_intmax_fl(__FILE__, __LINE__, (category), (repo), (key), \
++ (value))
+
-+void trace2_data_json_fl(const char *file, int line,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++void trace2_data_json_fl(const char *file, int line, const char *category,
++ const struct repository *repo, const char *key,
+ const struct json_writer *jw);
+
-+#define trace2_data_json(category, repo, key, value) \
-+ trace2_data_json_fl( \
-+ __FILE__, __LINE__, (category), (repo), (key), (value))
++#define trace2_data_json(category, repo, key, value) \
++ trace2_data_json_fl(__FILE__, __LINE__, (category), (repo), (key), \
++ (value))
+
+/*
+ * Emit a 'printf' event.
@@ -1876,8 +1841,8 @@
+ * any formatting guidelines. Post-processors may choose to ignore
+ * them.
+ */
-+void trace2_printf_va_fl(const char *file, int line,
-+ const char *fmt, va_list ap);
++void trace2_printf_va_fl(const char *file, int line, const char *fmt,
++ va_list ap);
+
+#define trace2_printf_va(fmt, ap) \
+ trace2_printf_va_fl(__FILE__, __LINE__, (fmt), (ap))
@@ -1885,8 +1850,7 @@
+void trace2_printf_fl(const char *file, int line, const char *fmt, ...);
+
+#ifdef HAVE_VARIADIC_MACROS
-+#define trace2_printf(...) \
-+ trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)
++#define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)
+#else
+__attribute__((format (printf, 1, 2)))
+void trace2_printf(const char *fmt, ...);
@@ -1948,8 +1912,7 @@
+ tr2_cfg_loaded = 0;
+}
+
-+struct tr2_cfg_data
-+{
++struct tr2_cfg_data {
+ const char *file;
+ int line;
+};
@@ -1982,15 +1945,14 @@
+ read_early_config(tr2_cfg_cb, &data);
+}
+
-+void tr2_cfg_set_fl(const char *file, int line,
-+ const char *key, const char *value)
++void tr2_cfg_set_fl(const char *file, int line, const char *key,
++ const char *value)
+{
+ struct tr2_cfg_data data = { file, line };
+
+ if (tr2_cfg_load_patterns() > 0)
+ tr2_cfg_cb(key, value, &data);
+}
-+
diff --git a/trace2/tr2_cfg.h b/trace2/tr2_cfg.h
new file mode 100644
@@ -2010,8 +1972,8 @@
+ * Emit a "def_param" event for the given key/value pair IF we consider
+ * the key to be "interesting".
+ */
-+void tr2_cfg_set_fl(const char *file, int line,
-+ const char *key, const char *value);
++void tr2_cfg_set_fl(const char *file, int line, const char *key,
++ const char *value);
+
+void tr2_cfg_free_patterns(void);
+
@@ -2025,6 +1987,32 @@
+#include "cache.h"
+#include "trace2/tr2_dst.h"
+
++/*
++ * If a Trace2 target cannot be opened for writing, we should issue a
++ * warning to stderr, but this is very annoying if the target is a pipe
++ * or socket and beyond the user's control -- especially since every
++ * git command (and sub-command) will print the message. So we silently
++ * eat these warnings and just discard the trace data.
++ *
++ * Enable the following environment variable to see these warnings.
++ */
++#define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG"
++
++static int tr2_dst_want_warning(void)
++{
++ static int tr2env_dst_debug = -1;
++
++ if (tr2env_dst_debug == -1) {
++ const char *env_value = getenv(TR2_ENVVAR_DST_DEBUG);
++ if (!env_value || !*env_value)
++ tr2env_dst_debug = 0;
++ else
++ tr2env_dst_debug = atoi(env_value) > 0;
++ }
++
++ return tr2env_dst_debug;
++}
++
+void tr2_dst_trace_disable(struct tr2_dst *dst)
+{
+ if (dst->need_close)
@@ -2034,50 +2022,129 @@
+ dst->need_close = 0;
+}
+
++static int tr2_dst_try_path(struct tr2_dst *dst, const char *tgt_value)
++{
++ int fd = open(tgt_value, O_WRONLY | O_APPEND | O_CREAT, 0666);
++ if (fd == -1) {
++ if (tr2_dst_want_warning())
++ warning("trace2: could not open '%s' for '%s' tracing: %s",
++ tgt_value, dst->env_var_name, strerror(errno));
++
++ tr2_dst_trace_disable(dst);
++ return 0;
++ }
++
++ dst->fd = fd;
++ dst->need_close = 1;
++ dst->initialized = 1;
++
++ return dst->fd;
++}
++
++#ifndef NO_UNIX_SOCKETS
++#define PREFIX_AF_UNIX "af_unix:"
++#define PREFIX_AF_UNIX_LEN (8)
++
++static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
++ const char *tgt_value)
++{
++ int fd;
++ struct sockaddr_un sa;
++ const char *path = tgt_value + PREFIX_AF_UNIX_LEN;
++ int path_len = strlen(path);
++
++ if (!is_absolute_path(path) || path_len >= sizeof(sa.sun_path)) {
++ if (tr2_dst_want_warning())
++ warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing",
++ path, dst->env_var_name);
++
++ tr2_dst_trace_disable(dst);
++ return 0;
++ }
++
++ sa.sun_family = AF_UNIX;
++ strlcpy(sa.sun_path, path, sizeof(sa.sun_path));
++ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
++ connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
++ if (tr2_dst_want_warning())
++ warning("trace2: could not connect to socket '%s' for '%s' tracing: %s",
++ path, dst->env_var_name, strerror(errno));
++
++ tr2_dst_trace_disable(dst);
++ return 0;
++ }
++
++ dst->fd = fd;
++ dst->need_close = 1;
++ dst->initialized = 1;
++
++ return dst->fd;
++}
++#endif
++
++static void tr2_dst_malformed_warning(struct tr2_dst *dst,
++ const char *tgt_value)
++{
++ struct strbuf buf = STRBUF_INIT;
++
++ strbuf_addf(&buf, "trace2: unknown trace value for '%s': '%s'",
++ dst->env_var_name, tgt_value);
++ strbuf_addstr(
++ &buf,
++ "\n If you want to trace into a file, then please set it"
++ "\n to an absolute pathname.");
++#ifndef NO_UNIX_SOCKETS
++ strbuf_addstr(
++ &buf,
++ "\n If you want to trace to a unix domain socket, prefix"
++ "\n the absolute pathname with \"af_unix:\".");
++#endif
++
++ warning("%s", buf.buf);
++
++ strbuf_release(&buf);
++}
++
+int tr2_dst_get_trace_fd(struct tr2_dst *dst)
+{
-+ const char *trace;
++ const char *tgt_value;
+
+ /* don't open twice */
+ if (dst->initialized)
+ return dst->fd;
+
-+ trace = getenv(dst->env_var_name);
++ dst->initialized = 1;
++
++ tgt_value = getenv(dst->env_var_name);
+
-+ if (!trace || !strcmp(trace, "") ||
-+ !strcmp(trace, "0") || !strcasecmp(trace, "false"))
++ if (!tgt_value || !strcmp(tgt_value, "") || !strcmp(tgt_value, "0") ||
++ !strcasecmp(tgt_value, "false")) {
+ dst->fd = 0;
-+ else if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
++ return dst->fd;
++ }
++
++ if (!strcmp(tgt_value, "1") || !strcasecmp(tgt_value, "true")) {
+ dst->fd = STDERR_FILENO;
-+ else if (strlen(trace) == 1 && isdigit(*trace))
-+ dst->fd = atoi(trace);
-+ else if (is_absolute_path(trace)) {
-+ int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
-+ if (fd == -1) {
-+ /*
-+ * Silently eat the error and disable tracing on this
-+ * destination. This will cause us to lose events,
-+ * but that is better than causing a stream of warnings
-+ * for each git command they run.
-+ *
-+ * warning("could not open '%s' for tracing: %s",
-+ * trace, strerror(errno));
-+ */
-+ tr2_dst_trace_disable(dst);
-+ } else {
-+ dst->fd = fd;
-+ dst->need_close = 1;
-+ }
-+ } else {
-+ warning("unknown trace value for '%s': %s\n"
-+ " If you want to trace into a file, then please set %s\n"
-+ " to an absolute pathname (starting with /)",
-+ dst->env_var_name, trace, dst->env_var_name);
-+ tr2_dst_trace_disable(dst);
++ return dst->fd;
+ }
+
-+ dst->initialized = 1;
-+ return dst->fd;
++ if (strlen(tgt_value) == 1 && isdigit(*tgt_value)) {
++ dst->fd = atoi(tgt_value);
++ return dst->fd;
++ }
++
++ if (is_absolute_path(tgt_value))
++ return tr2_dst_try_path(dst, tgt_value);
++
++#ifndef NO_UNIX_SOCKETS
++ if (!strncmp(tgt_value, PREFIX_AF_UNIX, PREFIX_AF_UNIX_LEN))
++ return tr2_dst_try_unix_domain_socket(dst, tgt_value);
++#endif
++
++ /* Always warn about malformed values. */
++ tr2_dst_malformed_warning(dst, tgt_value);
++ tr2_dst_trace_disable(dst);
++ return 0;
+}
+
+int tr2_dst_trace_want(struct tr2_dst *dst)
@@ -2105,12 +2172,15 @@
+ * If we get an IO error, just close the trace dst.
+ */
+
-+ if (write(tr2_dst_get_trace_fd(dst),
-+ buf_line->buf, buf_line->len) < 0) {
-+ warning("unable to write trace for '%s': %s",
-+ dst->env_var_name, strerror(errno));
-+ tr2_dst_trace_disable(dst);
-+ }
++ int fd = tr2_dst_get_trace_fd(dst);
++
++ if (write(fd, buf_line->buf, buf_line->len) >= 0)
++ return;
++
++ if (tr2_dst_want_warning())
++ warning("unable to write trace to '%s': %s", dst->env_var_name,
++ strerror(errno));
++ tr2_dst_trace_disable(dst);
+}
diff --git a/trace2/tr2_dst.h b/trace2/tr2_dst.h
@@ -2121,11 +2191,13 @@
+#ifndef TR2_DST_H
+#define TR2_DST_H
+
++struct strbuf;
++
+struct tr2_dst {
-+ const char * const env_var_name;
++ const char *const env_var_name;
+ int fd;
+ unsigned int initialized : 1;
-+ unsigned int need_close : 1;
++ unsigned int need_close : 1;
+};
+
+/*
@@ -2199,8 +2271,8 @@
+ }
+
+ us_now = getnanotime() / 1000;
-+ strbuf_addf(&tr2sid_buf, "%"PRIuMAX"-%"PRIdMAX,
-+ (uintmax_t)us_now, (intmax_t)getpid());
++ strbuf_addf(&tr2sid_buf, "%" PRIuMAX "-%" PRIdMAX, (uintmax_t)us_now,
++ (intmax_t)getpid());
+
+ setenv(TR2_ENVVAR_PARENT_SID, tr2sid_buf.buf, 1);
+}
@@ -2268,8 +2340,8 @@
+ secs = tv.tv_sec;
+ localtime_r(&secs, &tm);
+
-+ xsnprintf(tb->buf, sizeof(tb->buf), "%02d:%02d:%02d.%06ld",
-+ tm.tm_hour, tm.tm_min, tm.tm_sec, (long)tv.tv_usec);
++ xsnprintf(tb->buf, sizeof(tb->buf), "%02d:%02d:%02d.%06ld", tm.tm_hour,
++ tm.tm_min, tm.tm_sec, (long)tv.tv_usec);
+}
+
+void tr2_tbuf_utc_time(struct tr2_tbuf *tb)
@@ -2282,11 +2354,11 @@
+ secs = tv.tv_sec;
+ gmtime_r(&secs, &tm);
+
-+ xsnprintf(tb->buf, sizeof(tb->buf), "%4d-%02d-%02d %02d:%02d:%02d.%06ld",
-+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
-+ tm.tm_hour, tm.tm_min, tm.tm_sec, (long)tv.tv_usec);
++ xsnprintf(tb->buf, sizeof(tb->buf),
++ "%4d-%02d-%02d %02d:%02d:%02d.%06ld", tm.tm_year + 1900,
++ tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
++ (long)tv.tv_usec);
+}
-+
diff --git a/trace2/tr2_tbuf.h b/trace2/tr2_tbuf.h
new file mode 100644
@@ -2325,94 +2397,99 @@
+#ifndef TR2_TGT_H
+#define TR2_TGT_H
+
++struct child_process;
++struct repository;
++struct json_writer;
++
+/*
+ * Function prototypes for a TRACE2 "target" vtable.
+ */
+
-+typedef int (tr2_tgt_init_t)(void);
-+typedef void (tr2_tgt_term_t)(void);
-+
-+typedef void (tr2_tgt_evt_version_fl_t)
-+ (const char *file, int line);
-+
-+typedef void (tr2_tgt_evt_start_fl_t)
-+ (const char *file, int line, const char **argv);
-+typedef void (tr2_tgt_evt_exit_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute, int code);
-+typedef void (tr2_tgt_evt_signal_t)
-+ (uint64_t us_elapsed_absolute, int signo);
-+typedef void (tr2_tgt_evt_atexit_t)
-+ (uint64_t us_elapsed_absolute, int code);
-+
-+typedef void (tr2_tgt_evt_error_va_fl_t)
-+ (const char *file, int line, const char *fmt, va_list ap);
-+
-+typedef void (tr2_tgt_evt_command_path_fl_t)
-+ (const char *file, int line, const char *command_path);
-+typedef void (tr2_tgt_evt_command_verb_fl_t)
-+ (const char *file, int line, const char *command_verb,
-+ const char *hierarchy);
-+typedef void (tr2_tgt_evt_command_subverb_fl_t)
-+ (const char *file, int line, const char *command_subverb);
-+
-+typedef void (tr2_tgt_evt_alias_fl_t)
-+ (const char *file, int line, const char *alias, const char **argv);
-+
-+typedef void (tr2_tgt_evt_child_start_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ const struct child_process *cmd);
-+typedef void (tr2_tgt_evt_child_exit_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute, int cid,
-+ int pid, int code, uint64_t us_elapsed_child);
-+
-+typedef void (tr2_tgt_evt_thread_start_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute);
-+typedef void (tr2_tgt_evt_thread_exit_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_thread);
-+
-+typedef void (tr2_tgt_evt_exec_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ int exec_id, const char *exe, const char **argv);
-+typedef void (tr2_tgt_evt_exec_result_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ int exec_id, int code);
-+
-+typedef void (tr2_tgt_evt_param_fl_t)
-+ (const char *file, int line, const char *param, const char *value);
-+
-+typedef void (tr2_tgt_evt_repo_fl_t)
-+ (const char *file, int line, const struct repository *repo);
-+
-+typedef void (tr2_tgt_evt_region_enter_printf_va_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ const char *category, const char *label, const struct repository *repo,
-+ const char *fmt, va_list ap);
-+typedef void (tr2_tgt_evt_region_leave_printf_va_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region, const char *category, const char *label,
-+ const struct repository *repo, const char *fmt, va_list ap);
-+
-+typedef void (tr2_tgt_evt_data_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region, const char *category,
-+ const struct repository *repo, const char *key, const char *value);
-+typedef void (tr2_tgt_evt_data_json_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region, const char *category,
-+ const struct repository *repo,
-+ const char *key, const struct json_writer *value);
-+
-+typedef void (tr2_tgt_evt_printf_va_fl_t)
-+ (const char *file, int line, uint64_t us_elapsed_absolute,
-+ const char *fmt, va_list ap);
++typedef int(tr2_tgt_init_t)(void);
++typedef void(tr2_tgt_term_t)(void);
++
++typedef void(tr2_tgt_evt_version_fl_t)(const char *file, int line);
++
++typedef void(tr2_tgt_evt_start_fl_t)(const char *file, int line,
++ const char **argv);
++typedef void(tr2_tgt_evt_exit_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute, int code);
++typedef void(tr2_tgt_evt_signal_t)(uint64_t us_elapsed_absolute, int signo);
++typedef void(tr2_tgt_evt_atexit_t)(uint64_t us_elapsed_absolute, int code);
++
++typedef void(tr2_tgt_evt_error_va_fl_t)(const char *file, int line,
++ const char *fmt, va_list ap);
++
++typedef void(tr2_tgt_evt_command_path_fl_t)(const char *file, int line,
++ const char *command_path);
++typedef void(tr2_tgt_evt_command_verb_fl_t)(const char *file, int line,
++ const char *command_verb,
++ const char *hierarchy);
++typedef void(tr2_tgt_evt_command_subverb_fl_t)(const char *file, int line,
++ const char *command_subverb);
++
++typedef void(tr2_tgt_evt_alias_fl_t)(const char *file, int line,
++ const char *alias, const char **argv);
++
++typedef void(tr2_tgt_evt_child_start_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute,
++ const struct child_process *cmd);
++typedef void(tr2_tgt_evt_child_exit_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute, int cid,
++ int pid, int code,
++ uint64_t us_elapsed_child);
++
++typedef void(tr2_tgt_evt_thread_start_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute);
++typedef void(tr2_tgt_evt_thread_exit_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute,
++ uint64_t us_elapsed_thread);
++
++typedef void(tr2_tgt_evt_exec_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute, int exec_id,
++ const char *exe, const char **argv);
++typedef void(tr2_tgt_evt_exec_result_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute,
++ int exec_id, int code);
++
++typedef void(tr2_tgt_evt_param_fl_t)(const char *file, int line,
++ const char *param, const char *value);
++
++typedef void(tr2_tgt_evt_repo_fl_t)(const char *file, int line,
++ const struct repository *repo);
++
++typedef void(tr2_tgt_evt_region_enter_printf_va_fl_t)(
++ const char *file, int line, uint64_t us_elapsed_absolute,
++ const char *category, const char *label, const struct repository *repo,
++ const char *fmt, va_list ap);
++typedef void(tr2_tgt_evt_region_leave_printf_va_fl_t)(
++ const char *file, int line, uint64_t us_elapsed_absolute,
++ uint64_t us_elapsed_region, const char *category, const char *label,
++ const struct repository *repo, const char *fmt, va_list ap);
++
++typedef void(tr2_tgt_evt_data_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute,
++ uint64_t us_elapsed_region,
++ const char *category,
++ const struct repository *repo,
++ const char *key, const char *value);
++typedef void(tr2_tgt_evt_data_json_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute,
++ uint64_t us_elapsed_region,
++ const char *category,
++ const struct repository *repo,
++ const char *key,
++ const struct json_writer *value);
++
++typedef void(tr2_tgt_evt_printf_va_fl_t)(const char *file, int line,
++ uint64_t us_elapsed_absolute,
++ const char *fmt, va_list ap);
+
+/*
+ * "vtable" for a TRACE2 target. Use NULL if a target does not want
+ * to emit that message.
+ */
-+struct tr2_tgt
-+{
++struct tr2_tgt {
+ struct tr2_dst *pdst;
+
+ tr2_tgt_init_t *pfn_init;
@@ -2465,7 +2542,7 @@
+#include "trace2/tr2_tgt.h"
+#include "trace2/tr2_tls.h"
+
-+static struct tr2_dst tr2dst_event = { "GIT_TR2_EVENT", 0, 0, 0 };
++static struct tr2_dst tr2dst_event = { "GIT_TR2_EVENT", 0, 0, 0 };
+
+/*
+ * The version number of the JSON data generated by the EVENT target
@@ -2534,9 +2611,8 @@
+ * "line":<line_number>
+ * "repo":<repo_id>
+ */
-+static void event_fmt_prepare(const char *event_name,
-+ const char *file, int line,
-+ const struct repository *repo,
++static void event_fmt_prepare(const char *event_name, const char *file,
++ int line, const struct repository *repo,
+ struct json_writer *jw)
+{
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
@@ -2549,8 +2625,7 @@
+ /*
+ * In brief mode, only emit <time> on these 2 event types.
+ */
-+ if (!tr2env_event_brief ||
-+ !strcmp(event_name, "version") ||
++ if (!tr2env_event_brief || !strcmp(event_name, "version") ||
+ !strcmp(event_name, "atexit")) {
+ tr2_tbuf_utc_time(&tb_now);
+ jw_object_string(jw, "time", tb_now.buf);
@@ -2596,8 +2671,8 @@
+ jw_release(&jw);
+}
+
-+static void fn_exit_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute, int code)
++static void fn_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute,
++ int code)
+{
+ const char *event_name = "exit";
+ struct json_writer jw = JSON_WRITER_INIT;
@@ -2645,8 +2720,7 @@
+ jw_release(&jw);
+}
+
-+static void maybe_add_string_va(struct json_writer *jw,
-+ const char *field_name,
++static void maybe_add_string_va(struct json_writer *jw, const char *field_name,
+ const char *fmt, va_list ap)
+{
+ if (fmt && *fmt && ap) {
@@ -2668,8 +2742,8 @@
+ }
+}
+
-+static void fn_error_va_fl(const char *file, int line,
-+ const char *fmt, va_list ap)
++static void fn_error_va_fl(const char *file, int line, const char *fmt,
++ va_list ap)
+{
+ const char *event_name = "error";
+ struct json_writer jw = JSON_WRITER_INIT;
@@ -2691,8 +2765,7 @@
+ jw_release(&jw);
+}
+
-+static void fn_command_path_fl(const char *file, int line,
-+ const char *pathname)
++static void fn_command_path_fl(const char *file, int line, const char *pathname)
+{
+ const char *event_name = "cmd_path";
+ struct json_writer jw = JSON_WRITER_INIT;
@@ -2725,7 +2798,7 @@
+}
+
+static void fn_command_subverb_fl(const char *file, int line,
-+ const char *command_subverb)
++ const char *command_subverb)
+{
+ const char *event_name = "cmd_subverb";
+ struct json_writer jw = JSON_WRITER_INIT;
@@ -2739,8 +2812,8 @@
+ jw_release(&jw);
+}
+
-+static void fn_alias_fl(const char *file, int line,
-+ const char *alias, const char **argv)
++static void fn_alias_fl(const char *file, int line, const char *alias,
++ const char **argv)
+{
+ const char *event_name = "alias";
+ struct json_writer jw = JSON_WRITER_INIT;
@@ -2771,8 +2844,8 @@
+ jw_object_string(&jw, "child_class", "hook");
+ jw_object_string(&jw, "hook_name", cmd->trace2_hook_name);
+ } else {
-+ const char *child_class = ((cmd->trace2_child_class) ?
-+ cmd->trace2_child_class : "?");
++ const char *child_class =
++ cmd->trace2_child_class ? cmd->trace2_child_class : "?";
+ jw_object_string(&jw, "child_class", child_class);
+ }
+ if (cmd->dir)
@@ -2790,9 +2863,8 @@
+}
+
+static void fn_child_exit_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ int cid, int pid, int code,
-+ uint64_t us_elapsed_child)
++ uint64_t us_elapsed_absolute, int cid, int pid,
++ int code, uint64_t us_elapsed_child)
+{
+ const char *event_name = "child_exit";
+ struct json_writer jw = JSON_WRITER_INIT;
@@ -2842,8 +2914,7 @@
+ jw_release(&jw);
+}
+
-+static void fn_exec_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
++static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
+ int exec_id, const char *exe, const char **argv)
+{
+ const char *event_name = "exec";
@@ -2864,8 +2935,8 @@
+}
+
+static void fn_exec_result_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ int exec_id, int code)
++ uint64_t us_elapsed_absolute, int exec_id,
++ int code)
+{
+ const char *event_name = "exec_result";
+ struct json_writer jw = JSON_WRITER_INIT;
@@ -2880,8 +2951,8 @@
+ jw_release(&jw);
+}
+
-+static void fn_param_fl(const char *file, int line,
-+ const char *param, const char *value)
++static void fn_param_fl(const char *file, int line, const char *param,
++ const char *value)
+{
+ const char *event_name = "def_param";
+ struct json_writer jw = JSON_WRITER_INIT;
@@ -2924,8 +2995,7 @@
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
-+ event_fmt_prepare(
-+ event_name, file, line, repo, &jw);
++ event_fmt_prepare(event_name, file, line, repo, &jw);
+ jw_object_intmax(&jw, "nesting", ctx->nr_open_regions);
+ if (category)
+ jw_object_string(&jw, "category", category);
@@ -2939,13 +3009,10 @@
+ }
+}
+
-+static void fn_region_leave_printf_va_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region,
-+ const char *category,
-+ const char *label,
-+ const struct repository *repo,
-+ const char *fmt, va_list ap)
++static void fn_region_leave_printf_va_fl(
++ const char *file, int line, uint64_t us_elapsed_absolute,
++ uint64_t us_elapsed_region, const char *category, const char *label,
++ const struct repository *repo, const char *fmt, va_list ap)
+{
+ const char *event_name = "region_leave";
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
@@ -2969,12 +3036,9 @@
+ }
+}
+
-+static void fn_data_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++static void fn_data_fl(const char *file, int line, uint64_t us_elapsed_absolute,
++ uint64_t us_elapsed_region, const char *category,
++ const struct repository *repo, const char *key,
+ const char *value)
+{
+ const char *event_name = "data";
@@ -3001,10 +3065,8 @@
+
+static void fn_data_json_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++ uint64_t us_elapsed_region, const char *category,
++ const struct repository *repo, const char *key,
+ const struct json_writer *value)
+{
+ const char *event_name = "data_json";
@@ -3029,8 +3091,7 @@
+ }
+}
+
-+struct tr2_tgt tr2_tgt_event =
-+{
++struct tr2_tgt tr2_tgt_event = {
+ &tr2dst_event,
+
+ fn_init,
@@ -3087,7 +3148,7 @@
+#define TR2_ENVVAR_NORMAL_BRIEF "GIT_TR2_BRIEF"
+static int tr2env_normal_brief;
+
-+#define TR2FMT_NORMAL_FL_WIDTH (50)
++#define TR2FMT_NORMAL_FL_WIDTH (50)
+
+static int fn_init(void)
+{
@@ -3113,7 +3174,7 @@
+
+static void normal_fmt_prepare(const char *file, int line, struct strbuf *buf)
+{
-+ strbuf_setlen(buf,0);
++ strbuf_setlen(buf, 0);
+
+ if (!tr2env_normal_brief) {
+ struct tr2_tbuf tb_now;
@@ -3159,14 +3220,13 @@
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_exit_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute, int code)
++static void fn_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute,
++ int code)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed_absolute / 1000000.0;
+
-+ strbuf_addf(&buf_payload, "exit elapsed:%.6f code:%d",
-+ elapsed, code);
++ strbuf_addf(&buf_payload, "exit elapsed:%.6f code:%d", elapsed, code);
+ normal_io_write_fl(file, line, &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3176,8 +3236,8 @@
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed_absolute / 1000000.0;
+
-+ strbuf_addf(&buf_payload, "signal elapsed:%.6f code:%d",
-+ elapsed, signo);
++ strbuf_addf(&buf_payload, "signal elapsed:%.6f code:%d", elapsed,
++ signo);
+ normal_io_write_fl(__FILE__, __LINE__, &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3187,14 +3247,13 @@
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed_absolute / 1000000.0;
+
-+ strbuf_addf(&buf_payload, "atexit elapsed:%.6f code:%d",
-+ elapsed, code);
++ strbuf_addf(&buf_payload, "atexit elapsed:%.6f code:%d", elapsed, code);
+ normal_io_write_fl(__FILE__, __LINE__, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void maybe_append_string_va(struct strbuf *buf,
-+ const char *fmt, va_list ap)
++static void maybe_append_string_va(struct strbuf *buf, const char *fmt,
++ va_list ap)
+{
+ if (fmt && *fmt && ap) {
+ va_list copy_ap;
@@ -3211,8 +3270,8 @@
+ }
+}
+
-+static void fn_error_va_fl(const char *file, int line,
-+ const char *fmt, va_list ap)
++static void fn_error_va_fl(const char *file, int line, const char *fmt,
++ va_list ap)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+
@@ -3222,8 +3281,7 @@
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_command_path_fl(const char *file, int line,
-+ const char *pathname)
++static void fn_command_path_fl(const char *file, int line, const char *pathname)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+
@@ -3246,7 +3304,7 @@
+}
+
+static void fn_command_subverb_fl(const char *file, int line,
-+ const char *command_subverb)
++ const char *command_subverb)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+
@@ -3294,9 +3352,8 @@
+}
+
+static void fn_child_exit_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ int cid, int pid, int code,
-+ uint64_t us_elapsed_child)
++ uint64_t us_elapsed_absolute, int cid, int pid,
++ int code, uint64_t us_elapsed_child)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed_child / 1000000.0;
@@ -3307,8 +3364,7 @@
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_exec_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
++static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
+ int exec_id, const char *exe, const char **argv)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
@@ -3322,8 +3378,8 @@
+}
+
+static void fn_exec_result_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ int exec_id, int code)
++ uint64_t us_elapsed_absolute, int exec_id,
++ int code)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+
@@ -3356,8 +3412,8 @@
+}
+
+static void fn_printf_va_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ const char *fmt, va_list ap)
++ uint64_t us_elapsed_absolute, const char *fmt,
++ va_list ap)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+
@@ -3366,8 +3422,7 @@
+ strbuf_release(&buf_payload);
+}
+
-+struct tr2_tgt tr2_tgt_normal =
-+{
++struct tr2_tgt tr2_tgt_normal = {
+ &tr2dst_normal,
+
+ fn_init,
@@ -3415,7 +3470,7 @@
+#include "trace2/tr2_tgt.h"
+#include "trace2/tr2_tls.h"
+
-+static struct tr2_dst tr2dst_perf = { "GIT_TR2_PERF", 0, 0, 0 };
++static struct tr2_dst tr2dst_perf = { "GIT_TR2_PERF", 0, 0, 0 };
+
+/*
+ * Set this environment variable to true to omit the "<time> <file>:<line>"
@@ -3426,9 +3481,9 @@
+#define TR2_ENVVAR_PERF_BRIEF "GIT_TR2_PERF_BRIEF"
+static int tr2env_perf_brief;
+
-+#define TR2FMT_PERF_FL_WIDTH (50)
++#define TR2FMT_PERF_FL_WIDTH (50)
+#define TR2FMT_PERF_MAX_EVENT_NAME (12)
-+#define TR2FMT_PERF_REPO_WIDTH (4)
++#define TR2FMT_PERF_REPO_WIDTH (4)
+#define TR2FMT_PERF_CATEGORY_WIDTH (10)
+
+#define TR2_DOTS_BUFFER_SIZE (100)
@@ -3471,18 +3526,18 @@
+ * [<elapsed_absolute>] [<elapsed_relative>] <bar>
+ * [<category>] <bar> [<dots>] "
+ */
-+static void perf_fmt_prepare(
-+ const char *event_name, struct tr2tls_thread_ctx *ctx,
-+ const char *file, int line, const struct repository *repo,
-+ uint64_t *p_us_elapsed_absolute, uint64_t *p_us_elapsed_relative,
-+ const char *category, struct strbuf *buf)
++static void perf_fmt_prepare(const char *event_name,
++ struct tr2tls_thread_ctx *ctx, const char *file,
++ int line, const struct repository *repo,
++ uint64_t *p_us_elapsed_absolute,
++ uint64_t *p_us_elapsed_relative,
++ const char *category, struct strbuf *buf)
+{
+ int len;
+
-+ strbuf_setlen(buf,0);
++ strbuf_setlen(buf, 0);
+
+ if (!tr2env_perf_brief) {
-+
+ struct tr2_tbuf tb_now;
+
+ tr2_tbuf_local_time(&tb_now);
@@ -3498,15 +3553,15 @@
+ }
+
+ strbuf_addf(buf, "d%d | ", tr2_sid_depth());
-+ strbuf_addf(buf, "%-*s | %-*s | ",
-+ TR2_MAX_THREAD_NAME, ctx->thread_name.buf,
-+ TR2FMT_PERF_MAX_EVENT_NAME, event_name);
++ strbuf_addf(buf, "%-*s | %-*s | ", TR2_MAX_THREAD_NAME,
++ ctx->thread_name.buf, TR2FMT_PERF_MAX_EVENT_NAME,
++ event_name);
+
+ len = buf->len + TR2FMT_PERF_REPO_WIDTH;
+ if (repo)
+ strbuf_addf(buf, "r%d ", repo->trace2_repo_id);
+ while (buf->len < len)
-+ strbuf_addch(buf, ' ' );
++ strbuf_addch(buf, ' ');
+ strbuf_addstr(buf, "| ");
+
+ if (p_us_elapsed_absolute)
@@ -3520,7 +3575,7 @@
+ ((double)(*p_us_elapsed_relative)) / 1000000.0);
+ else
+ strbuf_addf(buf, "%9s | ", " ");
-+
++
+ strbuf_addf(buf, "%-*s | ", TR2FMT_PERF_CATEGORY_WIDTH,
+ (category ? category : ""));
+
@@ -3534,21 +3589,19 @@
+ }
+}
+
-+static void perf_io_write_fl(
-+ const char *file, int line,
-+ const char *event_name,
-+ const struct repository *repo,
-+ uint64_t *p_us_elapsed_absolute,
-+ uint64_t *p_us_elapsed_relative,
-+ const char *category,
-+ const struct strbuf *buf_payload)
++static void perf_io_write_fl(const char *file, int line, const char *event_name,
++ const struct repository *repo,
++ uint64_t *p_us_elapsed_absolute,
++ uint64_t *p_us_elapsed_relative,
++ const char *category,
++ const struct strbuf *buf_payload)
+{
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+ struct strbuf buf_line = STRBUF_INIT;
+
+ perf_fmt_prepare(event_name, ctx, file, line, repo,
-+ p_us_elapsed_absolute, p_us_elapsed_relative,
-+ category, &buf_line);
++ p_us_elapsed_absolute, p_us_elapsed_relative, category,
++ &buf_line);
+ strbuf_addbuf(&buf_line, buf_payload);
+ tr2_dst_write_line(&tr2dst_perf, &buf_line);
+ strbuf_release(&buf_line);
@@ -3561,8 +3614,7 @@
+
+ strbuf_addstr(&buf_payload, git_version_string);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3574,23 +3626,21 @@
+
+ sq_quote_argv_pretty(&buf_payload, argv);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_exit_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute, int code)
++static void fn_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute,
++ int code)
+{
+ const char *event_name = "exit";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "code:%d", code);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ &us_elapsed_absolute, NULL, NULL,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
++ NULL, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
@@ -3602,8 +3652,7 @@
+ strbuf_addf(&buf_payload, "signo:%d", signo);
+
+ perf_io_write_fl(__FILE__, __LINE__, event_name, NULL,
-+ &us_elapsed_absolute, NULL, NULL,
-+ &buf_payload);
++ &us_elapsed_absolute, NULL, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
@@ -3615,13 +3664,12 @@
+ strbuf_addf(&buf_payload, "code:%d", code);
+
+ perf_io_write_fl(__FILE__, __LINE__, event_name, NULL,
-+ &us_elapsed_absolute, NULL, NULL,
-+ &buf_payload);
++ &us_elapsed_absolute, NULL, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void maybe_append_string_va(struct strbuf *buf,
-+ const char *fmt, va_list ap)
++static void maybe_append_string_va(struct strbuf *buf, const char *fmt,
++ va_list ap)
+{
+ if (fmt && *fmt && ap) {
+ va_list copy_ap;
@@ -3638,30 +3686,27 @@
+ }
+}
+
-+static void fn_error_va_fl(const char *file, int line,
-+ const char *fmt, va_list ap)
++static void fn_error_va_fl(const char *file, int line, const char *fmt,
++ va_list ap)
+{
+ const char *event_name = "error";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ maybe_append_string_va(&buf_payload, fmt, ap);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_command_path_fl(const char *file, int line,
-+ const char *pathname)
++static void fn_command_path_fl(const char *file, int line, const char *pathname)
+{
+ const char *event_name = "cmd_path";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addstr(&buf_payload, pathname);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3677,22 +3722,20 @@
+ if (verb_hierarchy && *verb_hierarchy)
+ strbuf_addf(&buf_payload, " (%s)", verb_hierarchy);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
+static void fn_command_subverb_fl(const char *file, int line,
-+ const char *command_subverb)
++ const char *command_subverb)
+{
+ const char *event_name = "cmd_subverb";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addstr(&buf_payload, command_subverb);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3706,8 +3749,7 @@
+ strbuf_addf(&buf_payload, "alias:%s argv:", alias);
+ sq_quote_argv_pretty(&buf_payload, argv);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3723,8 +3765,8 @@
+ strbuf_addf(&buf_payload, "[ch%d] class:hook hook:%s",
+ cmd->trace2_child_id, cmd->trace2_hook_name);
+ } else {
-+ const char *child_class = ((cmd->trace2_child_class) ?
-+ cmd->trace2_child_class : "?");
++ const char *child_class =
++ cmd->trace2_child_class ? cmd->trace2_child_class : "?";
+ strbuf_addf(&buf_payload, "[ch%d] class:%s",
+ cmd->trace2_child_id, child_class);
+ }
@@ -3739,37 +3781,33 @@
+ strbuf_addstr(&buf_payload, " git");
+ sq_quote_argv_pretty(&buf_payload, cmd->argv);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ &us_elapsed_absolute, NULL, NULL,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
++ NULL, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
+static void fn_child_exit_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ int cid, int pid, int code,
-+ uint64_t us_elapsed_child)
++ uint64_t us_elapsed_absolute, int cid, int pid,
++ int code, uint64_t us_elapsed_child)
+{
+ const char *event_name = "child_exit";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "[ch%d] pid:%d code:%d", cid, pid, code);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ &us_elapsed_absolute, &us_elapsed_child, NULL,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
++ &us_elapsed_child, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
+static void fn_thread_start_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute)
++ uint64_t us_elapsed_absolute)
+{
+ const char *event_name = "thread_start";
+ struct strbuf buf_payload = STRBUF_INIT;
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ &us_elapsed_absolute, NULL, NULL,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
++ NULL, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
@@ -3780,14 +3818,12 @@
+ const char *event_name = "thread_exit";
+ struct strbuf buf_payload = STRBUF_INIT;
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ &us_elapsed_absolute, &us_elapsed_thread, NULL,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
++ &us_elapsed_thread, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_exec_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
++static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute,
+ int exec_id, const char *exe, const char **argv)
+{
+ const char *event_name = "exec";
@@ -3799,15 +3835,14 @@
+ strbuf_addf(&buf_payload, " %s", exe);
+ sq_quote_argv_pretty(&buf_payload, argv);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ &us_elapsed_absolute, NULL, NULL,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
++ NULL, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
+static void fn_exec_result_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ int exec_id, int code)
++ uint64_t us_elapsed_absolute, int exec_id,
++ int code)
+{
+ const char *event_name = "exec_result";
+ struct strbuf buf_payload = STRBUF_INIT;
@@ -3816,22 +3851,20 @@
+ if (code > 0)
+ strbuf_addf(&buf_payload, " err:%s", strerror(code));
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ &us_elapsed_absolute, NULL, NULL,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
++ NULL, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_param_fl(const char *file, int line,
-+ const char *param, const char *value)
++static void fn_param_fl(const char *file, int line, const char *param,
++ const char *value)
+{
+ const char *event_name = "def_param";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "%s:%s", param, value);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3845,8 +3878,7 @@
+ strbuf_addstr(&buf_payload, "worktree:");
+ sq_quote_buf_pretty(&buf_payload, repo->worktree);
+
-+ perf_io_write_fl(file, line, event_name, repo,
-+ NULL, NULL, NULL,
++ perf_io_write_fl(file, line, event_name, repo, NULL, NULL, NULL,
+ &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3865,19 +3897,15 @@
+ strbuf_addf(&buf_payload, "label:%s ", label);
+ maybe_append_string_va(&buf_payload, fmt, ap);
+
-+ perf_io_write_fl(file, line, event_name, repo,
-+ &us_elapsed_absolute, NULL, category,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute,
++ NULL, category, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_region_leave_printf_va_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region,
-+ const char *category,
-+ const char *label,
-+ const struct repository *repo,
-+ const char *fmt, va_list ap)
++static void fn_region_leave_printf_va_fl(
++ const char *file, int line, uint64_t us_elapsed_absolute,
++ uint64_t us_elapsed_region, const char *category, const char *label,
++ const struct repository *repo, const char *fmt, va_list ap)
+{
+ const char *event_name = "region_leave";
+ struct strbuf buf_payload = STRBUF_INIT;
@@ -3886,18 +3914,14 @@
+ strbuf_addf(&buf_payload, "label:%s ", label);
+ maybe_append_string_va(&buf_payload, fmt, ap);
+
-+ perf_io_write_fl(file, line, event_name, repo,
-+ &us_elapsed_absolute, &us_elapsed_region, category,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute,
++ &us_elapsed_region, category, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_data_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++static void fn_data_fl(const char *file, int line, uint64_t us_elapsed_absolute,
++ uint64_t us_elapsed_region, const char *category,
++ const struct repository *repo, const char *key,
+ const char *value)
+{
+ const char *event_name = "data";
@@ -3905,18 +3929,15 @@
+
+ strbuf_addf(&buf_payload, "%s:%s", key, value);
+
-+ perf_io_write_fl(file, line, event_name, repo,
-+ &us_elapsed_absolute, &us_elapsed_region, category,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute,
++ &us_elapsed_region, category, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
+static void fn_data_json_fl(const char *file, int line,
+ uint64_t us_elapsed_absolute,
-+ uint64_t us_elapsed_region,
-+ const char *category,
-+ const struct repository *repo,
-+ const char *key,
++ uint64_t us_elapsed_region, const char *category,
++ const struct repository *repo, const char *key,
+ const struct json_writer *value)
+{
+ const char *event_name = "data_json";
@@ -3924,29 +3945,26 @@
+
+ strbuf_addf(&buf_payload, "%s:%s", key, value->json.buf);
+
-+ perf_io_write_fl(file, line, event_name, repo,
-+ &us_elapsed_absolute, &us_elapsed_region, category,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute,
++ &us_elapsed_region, category, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
+static void fn_printf_va_fl(const char *file, int line,
-+ uint64_t us_elapsed_absolute,
-+ const char *fmt, va_list ap)
++ uint64_t us_elapsed_absolute, const char *fmt,
++ va_list ap)
+{
+ const char *event_name = "printf";
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ maybe_append_string_va(&buf_payload, fmt, ap);
+
-+ perf_io_write_fl(file, line, event_name, NULL,
-+ &us_elapsed_absolute, NULL, NULL,
-+ &buf_payload);
++ perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute,
++ NULL, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+struct tr2_tgt tr2_tgt_perf =
-+{
++struct tr2_tgt tr2_tgt_perf = {
+ &tr2dst_perf,
+
+ fn_init,
@@ -4012,11 +4030,11 @@
+ * application run time.
+ */
+ ctx->alloc = TR2_REGION_NESTING_INITIAL_SIZE;
-+ ctx->array_us_start = (uint64_t*)xcalloc(ctx->alloc, sizeof(uint64_t));
++ ctx->array_us_start = (uint64_t *)xcalloc(ctx->alloc, sizeof(uint64_t));
+ ctx->array_us_start[ctx->nr_open_regions++] = us_now;
+
+ ctx->thread_id = tr2tls_locked_increment(&tr2_next_thread_id);
-+
++
+ strbuf_init(&ctx->thread_name, 0);
+ if (ctx->thread_id)
+ strbuf_addf(&ctx->thread_name, "th%02d:", ctx->thread_id);
@@ -4031,7 +4049,7 @@
+
+struct tr2tls_thread_ctx *tr2tls_get_self(void)
+{
-+ struct tr2tls_thread_ctx * ctx = pthread_getspecific(tr2tls_key);
++ struct tr2tls_thread_ctx *ctx = pthread_getspecific(tr2tls_key);
+
+ /*
+ * If the thread-proc did not call trace2_thread_start(), we won't
@@ -4046,14 +4064,14 @@
+
+int tr2tls_is_main_thread(void)
+{
-+ struct tr2tls_thread_ctx * ctx = pthread_getspecific(tr2tls_key);
++ struct tr2tls_thread_ctx *ctx = pthread_getspecific(tr2tls_key);
+
+ return ctx == tr2tls_thread_main;
+}
+
+void tr2tls_unset_self(void)
+{
-+ struct tr2tls_thread_ctx * ctx;
++ struct tr2tls_thread_ctx *ctx;
+
+ ctx = tr2tls_get_self();
+
@@ -4097,7 +4115,7 @@
+ ctx = tr2tls_get_self();
+ if (!ctx->nr_open_regions)
+ return 0;
-+
++
+ us_start = ctx->array_us_start[ctx->nr_open_regions - 1];
+
+ return us - us_start;
@@ -4155,6 +4173,8 @@
+#ifndef TR2_TLS_H
+#define TR2_TLS_H
+
++struct strbuf;
++
+/*
+ * Arbitry limit for thread names for column alignment.
+ */
3: 5ac061e14a ! 3: e8b8226640 trace2: collect platform-specific process information
@@ -80,7 +80,6 @@
+ pid = pe32.th32ParentProcessID;
+
+ while (find_pid(pid, hSnapshot, &pe32)) {
-+
+ jw_array_string(jw, pe32.szExeFile);
+
+ pid = pe32.th32ParentProcessID;
@@ -102,8 +101,8 @@
+ get_processes(&jw, hSnapshot);
+ jw_end(&jw);
+
-+ trace2_data_json("process", the_repository,
-+ "windows/ancestry", &jw);
++ trace2_data_json("process", the_repository, "windows/ancestry",
++ &jw);
+
+ jw_release(&jw);
+ CloseHandle(hSnapshot);
@@ -122,7 +121,7 @@
+{
+ if (IsDebuggerPresent())
+ trace2_data_intmax("process", the_repository,
-+ "windows/debugger_present", 1);
++ "windows/debugger_present", 1);
+}
+
+void trace2_collect_process_info(void)
@@ -171,7 +170,8 @@
+void trace2_collect_process_info(void);
+#else
+#define trace2_collect_process_info() \
-+ do {} while (0)
++ do { \
++ } while (0)
+#endif
+
#endif /* TRACE2_H */
4: f9d689a54b ! 4: 0d59a6f2eb trace2:data: add trace2 regions to wt-status
@@ -44,7 +44,8 @@
void wt_status_print(struct wt_status *s)
{
+ trace2_data_intmax("status", s->repo, "count/changed", s->change.nr);
-+ trace2_data_intmax("status", s->repo, "count/untracked", s->untracked.nr);
++ trace2_data_intmax("status", s->repo, "count/untracked",
++ s->untracked.nr);
+ trace2_data_intmax("status", s->repo, "count/ignored", s->ignored.nr);
+
+ trace2_region_enter("status", "print", s->repo);
5: 6be101d520 = 5: 2513091f77 trace2:data: add editor/pager child classification
6: b590f19100 = 6: 785f6f866e trace2:data: add trace2 sub-process classification
7: 68192b8dfb = 7: b7e6644f4b trace2:data: add trace2 transport child classification
8: b373ab640b = 8: 52aace533b trace2:data: add trace2 hook classification
9: 548ea52742 ! 9: a2bcc6fd28 trace2:data: add trace2 instrumentation to index read/write
@@ -18,8 +18,10 @@
+ * TODO trace2: replace "the_repository" with the actual repo instance
+ * that is associated with the given "istate".
+ */
-+ trace2_data_intmax("index", the_repository, "read/version", istate->version);
-+ trace2_data_intmax("index", the_repository, "read/cache_nr", istate->cache_nr);
++ trace2_data_intmax("index", the_repository, "read/version",
++ istate->version);
++ trace2_data_intmax("index", the_repository, "read/cache_nr",
++ istate->cache_nr);
+
return istate->cache_nr;
@@ -46,11 +48,11 @@
base_oid_hex = oid_to_hex(&split_index->base_oid);
base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
-+ trace2_region_enter_printf("index", "shared/do_read_index", the_repository,
-+ "%s", base_path);
++ trace2_region_enter_printf("index", "shared/do_read_index",
++ the_repository, "%s", base_path);
ret = do_read_index(split_index->base, base_path, 1);
-+ trace2_region_leave_printf("index", "shared/do_read_index", the_repository,
-+ "%s", base_path);
++ trace2_region_leave_printf("index", "shared/do_read_index",
++ the_repository, "%s", base_path);
if (!oideq(&split_index->base_oid, &split_index->base->oid))
die(_("broken index, expect %s in %s, got %s"),
base_oid_hex, base_path,
@@ -63,8 +65,10 @@
+ * TODO trace2: replace "the_repository" with the actual repo instance
+ * that is associated with the given "istate".
+ */
-+ trace2_data_intmax("index", the_repository, "write/version", istate->version);
-+ trace2_data_intmax("index", the_repository, "write/cache_nr", istate->cache_nr);
++ trace2_data_intmax("index", the_repository, "write/version",
++ istate->version);
++ trace2_data_intmax("index", the_repository, "write/cache_nr",
++ istate->cache_nr);
+
return 0;
}
10: 3458917811 ! 10: d508322638 pack-objects: add trace2 regions
@@ -14,6 +14,7 @@
3. Write pack-file.
Signed-off-by: Derrick Stolee <[email protected]>
+ Signed-off-by: Jeff Hostetler <[email protected]>
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
--- a/builtin/pack-objects.c
@@ -30,7 +31,8 @@
}
}
-+ trace2_region_enter("pack-objects", "enumerate-objects", the_repository);
++ trace2_region_enter("pack-objects", "enumerate-objects",
++ the_repository);
prepare_packing_data(the_repository, &to_pack);
if (progress)
@@ -38,15 +40,18 @@
if (include_tag && nr_result)
for_each_ref(add_ref_tag, NULL);
stop_progress(&progress_state);
-+ trace2_region_leave("pack-objects", "enumerate-objects", the_repository);
++ trace2_region_leave("pack-objects", "enumerate-objects",
++ the_repository);
if (non_empty && !nr_result)
return 0;
- if (nr_result)
+ if (nr_result) {
-+ trace2_region_enter("pack-objects", "prepare-pack", the_repository);
++ trace2_region_enter("pack-objects", "prepare-pack",
++ the_repository);
prepare_pack(window, depth);
-+ trace2_region_leave("pack-objects", "prepare-pack", the_repository);
++ trace2_region_leave("pack-objects", "prepare-pack",
++ the_repository);
+ }
+
+ trace2_region_enter("pack-objects", "write-pack-file", the_repository);
11: 86feec03e2 = 11: 33e431be88 trace2:data: add subverb to checkout command
12: 9abbdf9ccd = 12: e46e92bc7c trace2:data: add subverb to reset command
13: 06ccce9632 ! 13: 0a05e15978 trace2:data: add subverb for rebase
@@ -11,16 +11,14 @@
ACTION_EDIT_TODO,
ACTION_SHOW_CURRENT_PATCH,
} action = NO_ACTION;
-+ static const char *action_names[] = {
-+ N_("undefined"),
-+ N_("continue"),
-+ N_("skip"),
-+ N_("abort"),
-+ N_("quit"),
-+ N_("edit_todo"),
-+ N_("show_current_patch"),
-+ NULL
-+ };
++ static const char *action_names[] = { N_("undefined"),
++ N_("continue"),
++ N_("skip"),
++ N_("abort"),
++ N_("quit"),
++ N_("edit_todo"),
++ N_("show_current_patch"),
++ NULL };
const char *gpg_sign = NULL;
struct string_list exec = STRING_LIST_INIT_NODUP;
const char *rebase_merges = NULL;
14: 851aa8f34d ! 14: aeaf990b1b trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
@@ -54,16 +54,15 @@
+#include "exec-cmd.h"
+#include "config.h"
+
-+typedef int (fn_unit_test)(int argc, const char **argv);
++typedef int(fn_unit_test)(int argc, const char **argv);
+
-+struct unit_test
-+{
++struct unit_test {
+ fn_unit_test *ut_fn;
-+ const char *ut_name;
-+ const char *ut_usage;
++ const char *ut_name;
++ const char *ut_usage;
+};
+
-+#define MyOk 0
++#define MyOk 0
+#define MyError 1
+
+static int get_i(int *p_value, const char *data)
@@ -91,7 +90,8 @@
+ * [] the "code" field in the "exit" trace2 event.
+ * [] the "code" field in the "atexit" trace2 event.
+ * [] the "name" field in the "cmd_verb" trace2 event.
-+ * [] "def_param" events for all of the "interesting" pre-defined config settings.
++ * [] "def_param" events for all of the "interesting" pre-defined
++ * config settings.
+ */
+static int ut_001return(int argc, const char **argv)
+{
@@ -110,7 +110,8 @@
+ * [] the "code" field in the "exit" trace2 event.
+ * [] the "code" field in the "atexit" trace2 event.
+ * [] the "name" field in the "cmd_verb" trace2 event.
-+ * [] "def_param" events for all of the "interesting" pre-defined config settings.
++ * [] "def_param" events for all of the "interesting" pre-defined
++ * config settings.
+ */
+static int ut_002exit(int argc, const char **argv)
+{
@@ -213,7 +214,7 @@
+ *
+ * Test harness can confirm (on platforms with a real exec() function):
+ * [] TODO talk about process replacement and how it affects SID.
-+ */
++ */
+static int ut_005exec(int argc, const char **argv)
+{
+ int result;
@@ -234,8 +235,7 @@
+ die("%s", usage_error);
+
+ while (argc) {
-+ if (!argv[0] || !*argv[0] ||
-+ !argv[1] || !*argv[1] ||
++ if (!argv[0] || !*argv[0] || !argv[1] || !*argv[1] ||
+ !argv[2] || !*argv[2])
+ die("%s", usage_error);
+
@@ -252,7 +252,7 @@
+ * test-tool trace2 <ut_name_1> <ut_usage_1>
+ * test-tool trace2 <ut_name_2> <ut_usage_2>
+ * ...
-+ */
++ */
+#define USAGE_PREFIX "test-tool trace2"
+
+static struct unit_test ut_table[] = {
@@ -275,9 +275,10 @@
+ struct unit_test *ut_k;
+
+ fprintf(stderr, "usage:\n");
-+ for_each_ut(k, ut_k) {
-+ fprintf(stderr, "\t%s %s %s\n",
-+ USAGE_PREFIX, ut_k->ut_name, ut_k->ut_usage);
++ for_each_ut(k, ut_k)
++ {
++ fprintf(stderr, "\t%s %s %s\n", USAGE_PREFIX, ut_k->ut_name,
++ ut_k->ut_usage);
+ }
+
+ return 129;
@@ -307,11 +308,12 @@
+ int k;
+ struct unit_test *ut_k;
+
-+ argc--; /* skip over "trace2" arg */
++ argc--; /* skip over "trace2" arg */
+ argv++;
+
+ if (argc) {
-+ for_each_ut(k, ut_k) {
++ for_each_ut(k, ut_k)
++ {
+ if (!strcmp(argv[0], ut_k->ut_name))
+ return ut_k->ut_fn(argc - 1, argv + 1);
+ }
@@ -319,7 +321,6 @@
+
+ return print_usage();
+}
-+
diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh
new file mode 100755
@@ -337,8 +338,6 @@
+TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR
+PATH="$TTDIR:$PATH" && export PATH
+
-+TT="test-tool" && export TT
-+
+# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe
+# Warning: to do the actual diff/comparison, so the HEREDOCs here
+# Warning: only cover our actual calls to test-tool and/or git.
@@ -384,9 +383,7 @@
+
+test_expect_success 'normal stream, return code 0' '
+ test_when_finished "rm trace.normal actual expect" &&
-+ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
-+ $TT trace2 001return 0 &&
-+ unset GIT_TR2 &&
++ GIT_TR2="$(pwd)/trace.normal" test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -400,9 +397,7 @@
+
+test_expect_success 'normal stream, return code 1' '
+ test_when_finished "rm trace.normal actual expect" &&
-+ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
-+ test_must_fail $TT trace2 001return 1 &&
-+ unset GIT_TR2 &&
++ test_must_fail env GIT_TR2="$(pwd)/trace.normal" test-tool trace2 001return 1 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -420,9 +415,7 @@
+
+test_expect_success 'normal stream, exit code 0' '
+ test_when_finished "rm trace.normal actual expect" &&
-+ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
-+ $TT trace2 002exit 0 &&
-+ unset GIT_TR2 &&
++ GIT_TR2="$(pwd)/trace.normal" test-tool trace2 002exit 0 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -436,9 +429,7 @@
+
+test_expect_success 'normal stream, exit code 1' '
+ test_when_finished "rm trace.normal actual expect" &&
-+ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
-+ test_must_fail $TT trace2 002exit 1 &&
-+ unset GIT_TR2 &&
++ test_must_fail env GIT_TR2="$(pwd)/trace.normal" test-tool trace2 002exit 1 &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -456,9 +447,7 @@
+
+test_expect_success 'normal stream, error event' '
+ test_when_finished "rm trace.normal actual expect" &&
-+ GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 &&
-+ $TT trace2 003error "hello world" "this is a test" &&
-+ unset GIT_TR2 &&
++ GIT_TR2="$(pwd)/trace.normal" test-tool trace2 003error "hello world" "this is a test" &&
+ perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+ cat >expect <<-EOF &&
+ version $V
@@ -544,8 +533,6 @@
+TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR
+PATH="$TTDIR:$PATH" && export PATH
+
-+TT="test-tool" && export TT
-+
+# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe
+# Warning: to do the actual diff/comparison, so the HEREDOCs here
+# Warning: only cover our actual calls to test-tool and/or git.
@@ -583,9 +570,7 @@
+
+test_expect_success 'perf stream, return code 0' '
+ test_when_finished "rm trace.perf actual expect" &&
-+ GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF &&
-+ $TT trace2 001return 0 &&
-+ unset GIT_TR2_PERF &&
++ GIT_TR2_PERF="$(pwd)/trace.perf" test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
@@ -599,9 +584,7 @@
+
+test_expect_success 'perf stream, return code 1' '
+ test_when_finished "rm trace.perf actual expect" &&
-+ GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF &&
-+ test_must_fail $TT trace2 001return 1 &&
-+ unset GIT_TR2_PERF &&
++ test_must_fail env GIT_TR2_PERF="$(pwd)/trace.perf" test-tool trace2 001return 1 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
@@ -619,9 +602,7 @@
+
+test_expect_success 'perf stream, error event' '
+ test_when_finished "rm trace.perf actual expect" &&
-+ GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF &&
-+ $TT trace2 003error "hello world" "this is a test" &&
-+ unset GIT_TR2_PERF &&
++ GIT_TR2_PERF="$(pwd)/trace.perf" test-tool trace2 003error "hello world" "this is a test" &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
@@ -667,19 +648,17 @@
+
+test_expect_success 'perf stream, child processes' '
+ test_when_finished "rm trace.perf actual expect" &&
-+ GIT_TR2_PERF="$(pwd)/trace.perf" && export GIT_TR2_PERF &&
-+ $TT trace2 004child $TT trace2 004child $TT trace2 001return 0 &&
-+ unset GIT_TR2_PERF &&
++ GIT_TR2_PERF="$(pwd)/trace.perf" test-tool trace2 004child test-tool trace2 004child test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" <trace.perf >actual &&
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
-+ d0|main|start|||||_EXE_ trace2 004child $TT trace2 004child $TT trace2 001return 0
++ d0|main|start|||||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0
+ d0|main|cmd_verb|||||trace2 (trace2)
-+ d0|main|child_start||_T_ABS_|||[ch0] class:? argv: $TT trace2 004child $TT trace2 001return 0
++ d0|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 004child test-tool trace2 001return 0
+ d1|main|version|||||$V
-+ d1|main|start|||||_EXE_ trace2 004child $TT trace2 001return 0
++ d1|main|start|||||_EXE_ trace2 004child test-tool trace2 001return 0
+ d1|main|cmd_verb|||||trace2 (trace2/trace2)
-+ d1|main|child_start||_T_ABS_|||[ch0] class:? argv: $TT trace2 001return 0
++ d1|main|child_start||_T_ABS_|||[ch0] class:? argv: test-tool trace2 001return 0
+ d2|main|version|||||$V
+ d2|main|start|||||_EXE_ trace2 001return 0
+ d2|main|cmd_verb|||||trace2 (trace2/trace2/trace2)
@@ -795,8 +774,6 @@
+TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR
+PATH="$TTDIR:$PATH" && export PATH
+
-+TT="test-tool" && export TT
-+
+# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe
+# Warning: to do the actual diff/comparison, so the HEREDOCs here
+# Warning: only cover our actual calls to test-tool and/or git.
@@ -828,9 +805,7 @@
+
+test_expect_success 'event stream, error event' '
+ test_when_finished "rm trace.event actual expect" &&
-+ GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT &&
-+ $TT trace2 003error "hello world" "this is a test" &&
-+ unset GIT_TR2_EVENT &&
++ GIT_TR2_EVENT="$(pwd)/trace.event" test-tool trace2 003error "hello world" "this is a test" &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
@@ -866,10 +841,8 @@
+# P3: |--- TT trace2 001return 0
+
+test_expect_success 'event stream, return code 0' '
-+# test_when_finished "rm trace.event actual expect" &&
-+ GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT &&
-+ $TT trace2 004child $TT trace2 004child $TT trace2 001return 0 &&
-+ unset GIT_TR2_EVENT &&
++ test_when_finished "rm trace.event actual expect" &&
++ GIT_TR2_EVENT="$(pwd)/trace.event" test-tool trace2 004child test-tool trace2 004child test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
@@ -878,10 +851,10 @@
+ | "_EXE_",
+ | "trace2",
+ | "004child",
-+ | "$TT",
++ | "test-tool",
+ | "trace2",
+ | "004child",
-+ | "$TT",
++ | "test-tool",
+ | "trace2",
+ | "001return",
+ | "0"
@@ -892,7 +865,7 @@
+ | "_EXE_",
+ | "trace2",
+ | "004child",
-+ | "$TT",
++ | "test-tool",
+ | "trace2",
+ | "001return",
+ | "0"
@@ -912,7 +885,7 @@
+ | "_EXE_",
+ | "trace2",
+ | "004child",
-+ | "$TT",
++ | "test-tool",
+ | "trace2",
+ | "001return",
+ | "0"
@@ -958,11 +931,7 @@
+ test_when_finished "rm trace.event actual expect" &&
+ git config --local t0212.abc 1 &&
+ git config --local t0212.def "hello world" &&
-+ # delete events generated by the above config commands
-+ rm trace.event &&
-+ GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT &&
-+ GIT_TR2_CONFIG_PARAMS="t0212.*" $TT trace2 001return 0 &&
-+ unset GIT_TR2_EVENT &&
++ GIT_TR2_EVENT="$(pwd)/trace.event" GIT_TR2_CONFIG_PARAMS="t0212.*" test-tool trace2 001return 0 &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {
@@ -995,9 +964,7 @@
+
+test_expect_success 'basic trace2_data' '
+ test_when_finished "rm trace.event actual expect" &&
-+ GIT_TR2_EVENT="$(pwd)/trace.event" && export GIT_TR2_EVENT &&
-+ $TT trace2 006data test_category k1 v1 test_category k2 v2 &&
-+ unset GIT_TR2_EVENT &&
++ GIT_TR2_EVENT="$(pwd)/trace.event" test-tool trace2 006data test_category k1 v1 test_category k2 v2 &&
+ perl "$TEST_DIRECTORY/t0212/parse_events.perl" <trace.event >actual &&
+ sed -e "s/^|//" >expect <<-EOF &&
+ |VAR1 = {Signature · 1 line
gitgitgadget
PATCH v3 12/14 trace2:data: add subverb to reset command
From: Jeff Hostetler <[email protected]>
--- builtin/reset.c | 6 ++++++ 1 file changed, 6 insertions(+)
builtin/reset.c +6 -0
@@ -340,6 +340,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (patch_mode) {if (reset_type !=NONE )die (_ ("--patch is incompatible with --{hard,mixed,soft}" )); +trace2_cmd_subverb ("patch-interactive" );return run_add_interactive (rev,"--patch=reset" , &pathspec); }
@@ -356,6 +357,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==NONE ) reset_type =MIXED ;/* by default */ +if (pathspec.nr) +trace2_cmd_subverb ("path" ); +else +trace2_cmd_subverb (reset_type_names[reset_type]); +if (reset_type !=SOFT && (reset_type !=MIXED ||get_git_work_tree ()))setup_work_tree ();
Signature · 1 line
gitgitgadget
PATCH v3 14/14 trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Create unit tests for Trace2.
--- Makefile | 1 + t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 274 ++++++++++++++++++++++++++++++++++++++ t/t0210-trace2-normal.sh | 135 +++++++++++++++++++ t/t0210/scrub_normal.perl | 48 +++++++ t/t0211-trace2-perf.sh | 153 +++++++++++++++++++++ t/t0211/scrub_perf.perl | 76 +++++++++++ t/t0212-trace2-event.sh | 234 ++++++++++++++++++++++++++++++++ t/t0212/parse_events.perl | 251 ++++++++++++++++++++++++++++++++++ 10 files changed, 1174 insertions(+) create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl
Makefile +1 -0
@@ -762,6 +762,7 @@ TEST_BUILTINS_OBJS += test-string-list.o TEST_BUILTINS_OBJS += test-submodule-config.o TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o +TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o TEST_BUILTINS_OBJS += test-wildmatch.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o
t/helper/test-tool.c +1 -0
@@ -51,6 +51,7 @@ static struct test_cmd cmds[] = { { "submodule-config", cmd__submodule_config }, { "submodule-nested-repo-config", cmd__submodule_nested_repo_config }, { "subprocess", cmd__subprocess }, + { "trace2", cmd__trace2 }, { "urlmatch-normalization", cmd__urlmatch_normalization }, { "wildmatch", cmd__wildmatch }, #ifdef GIT_WINDOWS_NATIVE
t/helper/test-tool.h +1 -0
@@ -46,6 +46,7 @@ int cmd__string_list(int argc, const char **argv);int cmd__submodule_config (int argc,const char **argv);int cmd__submodule_nested_repo_config (int argc,const char **argv);int cmd__subprocess (int argc,const char **argv); +int cmd__trace2 (int argc,const char **argv);int cmd__urlmatch_normalization (int argc,const char **argv);int cmd__wildmatch (int argc,const char **argv);#ifdef GIT_WINDOWS_NATIVE
t/helper/test-trace2.c new file +274 -0
274 lines not on this page · raw message
t/t0210-trace2-normal.sh new file +135 -0
135 lines not on this page · raw message
t/t0210/scrub_normal.perl new file +48 -0
48 lines not on this page · raw message
t/t0211-trace2-perf.sh new file +153 -0
153 lines not on this page · raw message
t/t0211/scrub_perf.perl new file +76 -0
76 lines not on this page · raw message
t/t0212-trace2-event.sh new file +234 -0
234 lines not on this page · raw message
t/t0212/parse_events.perl new file +251 -0
251 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v3 2/14 trace2: create new combined trace facility
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Create a new unified tracing facility for git. The eventual intent is to replace the current trace_printf* and trace_performance* routines with a unified set of git_trace2* routines.
In addition to the usual printf-style API, trace2 provides higer-level event verbs with fixed-fields allowing structured data to be written. This makes post-processing and analysis easier for external tools.
Trace2 defines 3 output targets. These are set using the environment variables "GIT_TR2", "GIT_TR2_PERF", and "GIT_TR2_EVENT". These may be set to "1" or to an absolute pathname (just like the current GIT_TRACE).
* GIT_TR2 is intended to be a replacement for GIT_TRACE and logs command summary data.
* GIT_TR2_PERF is intended as a replacement for GIT_TRACE_PERFORMANCE. It extends the output with columns for the command process, thread, repo, absolute and relative elapsed times. It reports events for child process start/stop, thread start/stop, and per-thread function nesting.
* GIT_TR2_EVENT is a new structured format. It writes event data as a series of JSON records.
Calls to trace2 functions log to any of the 3 output targets enabled without the need to call different trace_printf* or trace_performance* routines.
--- Makefile | 10 + builtin/submodule--helper.c | 9 +- cache.h | 1 + common-main.c | 12 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- config.c | 2 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 +++ remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 59 ++- run-command.h | 13 +- sh-i18n--envsubst.c | 3 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 3 + t/t0001-init.sh | 1 + trace2.c | 807 ++++++++++++++++++++++++++++++++++ trace2.h | 366 +++++++++++++++ trace2/tr2_cfg.c | 90 ++++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 198 +++++++++ trace2/tr2_dst.h | 36 ++ trace2/tr2_sid.c | 67 +++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 ++ trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 131 ++++++ trace2/tr2_tgt_event.c | 590 +++++++++++++++++++++++++ trace2/tr2_tgt_normal.c | 325 ++++++++++++++ trace2/tr2_tgt_perf.c | 536 ++++++++++++++++++++++ trace2/tr2_tls.c | 164 +++++++ trace2/tr2_tls.h | 97 ++++ trace2/tr2_verb.c | 30 ++ trace2/tr2_verb.h | 24 + usage.c | 31 ++ 39 files changed, 3794 insertions(+), 17 deletions(-) create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
Makefile +10 -0
@@ -1005,6 +1005,16 @@ LIB_OBJS += tempfile.o LIB_OBJS += thread-utils.o LIB_OBJS += tmp-objdir.o LIB_OBJS += trace.o +LIB_OBJS += trace2.o +LIB_OBJS += trace2/tr2_cfg.o +LIB_OBJS += trace2/tr2_dst.o +LIB_OBJS += trace2/tr2_sid.o +LIB_OBJS += trace2/tr2_tbuf.o +LIB_OBJS += trace2/tr2_tgt_event.o +LIB_OBJS += trace2/tr2_tgt_normal.o +LIB_OBJS += trace2/tr2_tgt_perf.o +LIB_OBJS += trace2/tr2_tls.o +LIB_OBJS += trace2/tr2_verb.o LIB_OBJS += trailer.o LIB_OBJS += transport.o LIB_OBJS += transport-helper.o
builtin/submodule--helper.c +4 -5
@@ -1815,11 +1815,10 @@ static int update_submodules(struct submodule_update_clone *suc) {int i; -run_processes_parallel (suc->max_jobs, - update_clone_get_next_task, - update_clone_start_failure, - update_clone_task_finished, - suc); +run_processes_parallel_tr2 (suc->max_jobs, update_clone_get_next_task, + update_clone_start_failure, + update_clone_task_finished, suc,"submodule" , +"parallel/update" );/* * We saved the output and put it out all at once now.
cache.h +1 -0
@@ -9,6 +9,7 @@#include "gettext.h" #include "convert.h" #include "trace.h" +#include "trace2.h" #include "string-list.h" #include "pack-revindex.h" #include "hash.h"
common-main.c +10 -2
@@ -25,12 +25,18 @@ static void restore_sigpipe_to_default(void)int main (int argc,const char **argv) { +int result; +/* * Always open file descriptors 0/1/2 to avoid clobbering files * in die(). It also avoids messing up when the pipes are dup'ed * onto stdin/stdout/stderr in the child processes we spawn. */ sanitize_stdfds (); +restore_sigpipe_to_default (); + +trace2_initialize (); +trace2_cmd_start (argv);git_resolve_executable_dir (argv[0 ]);
@@ -40,7 +46,9 @@ int main(int argc, const char **argv)attr_start (); -restore_sigpipe_to_default (); + result =cmd_main (argc, argv); + +trace2_cmd_exit (result); -return cmd_main (argc, argv); +return result; }
compat/mingw.c +10 -1
@@ -1476,19 +1476,23 @@ static int try_shell_exec(const char *cmd, char *const *argv)return 0 ; prog =path_lookup (interpr,1 );if (prog) { +int exec_id;int argc =0 ;const char **argv2;while (argv[argc]) argc++;ALLOC_ARRAY (argv2, argc +1 ); argv2[0 ] = (char *)cmd;/* full path to the script file */ memcpy (&argv2[1 ], &argv[1 ],sizeof (*argv) * argc); + exec_id =trace2_exec (prog, argv2); pid =mingw_spawnv (prog, argv2,1 );if (pid >=0 ) {int status;if (waitpid (pid, &status,0 ) <0 ) status =255 ; +trace2_exec_result (exec_id, status);exit (status); } +trace2_exec_result (exec_id,-1 ); pid =1 ;/* indicate that we tried but failed */ free (prog);free (argv2);
@@ -1501,12 +1505,17 @@ int mingw_execv(const char *cmd, char *const *argv)/* check if git_command is a shell script */ if (!try_shell_exec (cmd, argv)) {int pid, status; +int exec_id; + exec_id =trace2_exec (cmd, (const char **)argv); pid =mingw_spawnv (cmd, (const char **)argv,0 ); -if (pid <0 ) +if (pid <0 ) { +trace2_exec_result (exec_id,-1 );return -1 ; + }if (waitpid (pid, &status,0 ) <0 ) status =255 ; +trace2_exec_result (exec_id, status);exit (status); }return -1 ;
compat/mingw.h +1 -2
@@ -147,8 +147,7 @@ static inline int fcntl(int fd, int cmd, ...) errno =EINVAL ;return -1 ; } -/* bash cannot reliably detect negative return codes as failure */ -#define exit (code) exit((code) & 0xff) +#define sigemptyset (x) (void)0static inline int sigaddset (sigset_t *set,int signum) {return 0 ; }
config.c +2 -0
@@ -2656,6 +2656,8 @@ int git_config_set_gently(const char *key, const char *value)void git_config_set (const char *key,const char *value) {git_config_set_multivar (key, value,NULL ,0 ); + +trace2_cmd_set_config (key, value); }/*
exec-cmd.c +2 -0
@@ -209,6 +209,8 @@ static int git_get_exec_path(struct strbuf *buf, const char *argv0)return -1 ; } +trace2_cmd_path (buf->buf); +return 0 ; }
git-compat-util.h +7 -0
@@ -1251,6 +1251,13 @@ static inline int is_missing_file_error(int errno_)extern int cmd_main (int ,const char **); +/* +* Intercept all calls to exit() and route them to trace2 to +* optionally emit a message before calling the real exit(). +*/ +int trace2_cmd_exit_fl (const char *file,int line,int code); +#define exit (code) exit(trace2_cmd_exit_fl(__FILE__, __LINE__, (code))) +/* * You can mark a stack variable with UNLEAK(var) to avoid it being * reported as a leak by tools like LSAN or valgrind. The argument
git.c +65 -0
127 lines not on this page · raw message
remote-curl.c +7 -0
@@ -1360,6 +1360,13 @@ int cmd_main(int argc, const char **argv)string_list_init (&options.deepen_not,1 );string_list_init (&options.push_options,1 ); +/* +* Just report "remote-curl" here (folding all the various aliases +* ("git-remote-http", "git-remote-https", and etc.) here since they +* are all just copies of the same actual executable. +*/ +trace2_cmd_verb ("remote-curl" ); + remote =remote_get (argv[1 ]);if (argc >2 ) {
repository.c +2 -0
@@ -119,6 +119,8 @@ static int repo_init_gitdir(struct repository *repo, const char *gitdir)void repo_set_worktree (struct repository *repo,const char *path) { repo->worktree =real_pathdup (path,1 ); + +trace2_def_repo (repo); } static int read_and_verify_repository_format(struct repository_format *format,
repository.h +3 -0
@@ -90,6 +90,9 @@ struct repository {/* Repository's current hash algorithm, as serialized on disk. */ const struct git_hash_algo *hash_algo; +/* A unique-id for tracing purposes. */ +int trace2_repo_id; +/* Configurations */ /* Indicate if a repository has a different 'commondir' from 'gitdir' */
run-command.c +58 -1
114 lines not on this page · raw message
run-command.h +12 -1
@@ -10,6 +10,12 @@ struct child_process {struct argv_array args;struct argv_array env_array;pid_t pid; + +int trace2_child_id; +uint64_t trace2_child_us_start; +const char *trace2_child_class; +const char *trace2_hook_name; +/* * Using .in, .out, .err: * - Specify 0 for no redirections (child inherits stdin, stdout,
@@ -73,12 +79,14 @@ extern int run_hook_ve(const char *const *env, const char *name, va_list args);#define RUN_USING_SHELL 16#define RUN_CLEAN_ON_EXIT 32int run_command_v_opt (const char **argv,int opt); - +int run_command_v_opt_tr2 (const char **argv,int opt,const char *tr2_class);/* * env (the environment) is to be formatted like environ: "VAR=VALUE". * To unset an environment variable use just "VAR". */ int run_command_v_opt_cd_env (const char **argv,int opt,const char *dir,const char *const *env); +int run_command_v_opt_cd_env_tr2 (const char **argv,int opt,const char *dir, +const char *const *env,const char *tr2_class);/** * Execute the given command, sending "in" to its stdin, and capturing its
@@ -220,5 +228,8 @@ int run_processes_parallel(int n, start_failure_fn, task_finished_fn, void *pp_cb); +int run_processes_parallel_tr2 (int n,get_next_task_fn ,start_failure_fn , +task_finished_fn ,void *pp_cb, +const char *tr2_category,const char *tr2_label);#endif
sh-i18n--envsubst.c +3 -0
@@ -14,6 +14,7 @@*/ #include "git-compat-util.h" +#include "trace2.h" /* Substitution of environment variables in shell format strings. Copyright (C) 2003-2007 Free Software Foundation, Inc.
@@ -67,6 +68,8 @@ cmd_main (int argc, const char *argv[])/* Default values for command line options. */ /* unsigned short int show_variables = 0; */ +trace2_cmd_verb ("sh-i18n--envsubst" ); +switch (argc) {case 1 :
submodule.c +6 -5
@@ -1568,11 +1568,12 @@ int fetch_populated_submodules(struct repository *r,calculate_changed_submodule_paths (r, &spf.changed_submodule_names);string_list_sort (&spf.changed_submodule_names); -run_processes_parallel (max_parallel_jobs, - get_next_submodule, - fetch_start_failure, - fetch_finish, - &spf); +run_processes_parallel_tr2 (max_parallel_jobs, + get_next_submodule, + fetch_start_failure, + fetch_finish, + &spf, +"submodule" ,"parallel/fetch" );argv_array_clear (&spf.args); out:
t/helper/test-parse-options.c +3 -0
@@ -2,6 +2,7 @@#include "cache.h" #include "parse-options.h" #include "string-list.h" +#include "trace2.h" static int boolean =0 ;static int integer =0 ;
@@ -153,6 +154,8 @@ int cmd__parse_options(int argc, const char **argv)int i;int ret =0 ; +trace2_cmd_verb ("_parse_" ); + argc =parse_options (argc, (const char **)argv, prefix, options, usage,0 );if (length_cb.called) {
t/helper/test-tool.c +3 -0
@@ -1,5 +1,6 @@#include "git-compat-util.h" #include "test-tool.h" +#include "trace2.h" struct test_cmd {const char *name;
@@ -80,6 +81,8 @@ int cmd_main(int argc, const char **argv)if (!strcmp (cmds[i].name, argv[1 ])) { argv++; argc--; +trace2_cmd_verb (cmds[i].name); +trace2_cmd_list_config ();return cmds[i].fn (argc, argv); } }
t/t0001-init.sh +1 -0
@@ -93,6 +93,7 @@ test_expect_success 'No extra GIT_* on alias scripts' ' sed -n \ -e "/^GIT_PREFIX=/d" \ -e "/^GIT_TEXTDOMAINDIR=/d" \ + -e "/^GIT_TR2_PARENT/d" \ -e "/^GIT_/s/=.*//p" | sort EOF
trace2.c new file +807 -0
807 lines not on this page · raw message
trace2.h new file +366 -0
366 lines not on this page · raw message
trace2/tr2_cfg.c new file +90 -0
90 lines not on this page · raw message
trace2/tr2_cfg.h new file +19 -0
@@ -0,0 +1,19 @@ +#ifndef TR2_CFG_H +#define TR2_CFG_H + +/* +* Iterate over all config settings and emit 'def_param' events for the +* "interesting" ones to TRACE2. +*/ +void tr2_cfg_list_config_fl (const char *file,int line); + +/* +* Emit a "def_param" event for the given key/value pair IF we consider +* the key to be "interesting". +*/ +void tr2_cfg_set_fl (const char *file,int line,const char *key, +const char *value); + +void tr2_cfg_free_patterns (void ); + +#endif /* TR2_CFG_H */
trace2/tr2_dst.c new file +198 -0
198 lines not on this page · raw message
trace2/tr2_dst.h new file +36 -0
@@ -0,0 +1,36 @@ +#ifndef TR2_DST_H +#define TR2_DST_H + +struct strbuf ; + +struct tr2_dst { +const char *const env_var_name; +int fd; +unsigned int initialized :1 ; +unsigned int need_close :1 ; +}; + +/* +* Disable TRACE2 on the destination. In TRACE2 a destination (DST) +* wraps a file descriptor; it is associated with a TARGET which +* defines the formatting. +*/ +void tr2_dst_trace_disable (struct tr2_dst *dst); + +/* +* Return the file descriptor for the DST. +* If 0, the dst is closed or disabled. +*/ +int tr2_dst_get_trace_fd (struct tr2_dst *dst); + +/* +* Return true if the DST is opened for writing. +*/ +int tr2_dst_trace_want (struct tr2_dst *dst); + +/* +* Write a single line/message to the trace file. +*/ +void tr2_dst_write_line (struct tr2_dst *dst,struct strbuf *buf_line); + +#endif /* TR2_DST_H */
trace2/tr2_sid.c new file +67 -0
67 lines not on this page · raw message
trace2/tr2_sid.h new file +18 -0
@@ -0,0 +1,18 @@ +#ifndef TR2_SID_H +#define TR2_SID_H + +/* +* Get our session id. Compute if necessary. +*/ +const char *tr2_sid_get (void ); + +/* +* Get our process depth. A top-level git process invoked from the +* command line will have depth=0. A child git process will have +* depth=1 and so on. +*/ +int tr2_sid_depth (void ); + +void tr2_sid_release (void ); + +#endif /* TR2_SID_H */
trace2/tr2_tbuf.c new file +32 -0
@@ -0,0 +1,32 @@ +#include "cache.h" +#include "tr2_tbuf.h" + +void tr2_tbuf_local_time (struct tr2_tbuf *tb) +{ +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +localtime_r (&secs, &tm); + +xsnprintf (tb->buf,sizeof (tb->buf),"%02d:%02d:%02d.%06ld" , tm.tm_hour, + tm.tm_min, tm.tm_sec, (long )tv.tv_usec); +} + +void tr2_tbuf_utc_time (struct tr2_tbuf *tb) +{ +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +gmtime_r (&secs, &tm); + +xsnprintf (tb->buf,sizeof (tb->buf), +"%4d-%02d-%02d %02d:%02d:%02d.%06ld" , tm.tm_year +1900 , + tm.tm_mon +1 , tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + (long )tv.tv_usec); +}
trace2/tr2_tbuf.h new file +23 -0
@@ -0,0 +1,23 @@ +#ifndef TR2_TBUF_H +#define TR2_TBUF_H + +/* +* A simple wrapper around a fixed buffer to avoid C syntax +* quirks and the need to pass around an additional size_t +* argument. +*/ +struct tr2_tbuf { +char buf[32 ]; +}; + +/* +* Fill buffer with formatted local time string. +*/ +void tr2_tbuf_local_time (struct tr2_tbuf *tb); + +/* +* Fill buffer with formatted UTC time string. +*/ +void tr2_tbuf_utc_time (struct tr2_tbuf *tb); + +#endif /* TR2_TBUF_H */
trace2/tr2_tgt.h new file +131 -0
131 lines not on this page · raw message
trace2/tr2_tgt_event.c new file +590 -0
590 lines not on this page · raw message
trace2/tr2_tgt_normal.c new file +325 -0
103 lines not on this page · raw message
trace2/tr2_tgt_perf.c new file +536 -0
536 lines not on this page · raw message
trace2/tr2_tls.c new file +164 -0
164 lines not on this page · raw message
trace2/tr2_tls.h new file +97 -0
97 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
PATCH v3 13/14 trace2:data: add subverb for rebase
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/rebase.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
builtin/rebase.c +17 -0
@@ -850,6 +850,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)ACTION_EDIT_TODO ,ACTION_SHOW_CURRENT_PATCH , } action = NO_ACTION; +static const char *action_names[] = {N_ ("undefined" ), +N_ ("continue" ), +N_ ("skip" ), +N_ ("abort" ), +N_ ("quit" ), +N_ ("edit_todo" ), +N_ ("show_current_patch" ), +NULL };const char *gpg_sign =NULL ;struct string_list exec =STRING_LIST_INIT_NODUP ;const char *rebase_merges =NULL ;
@@ -1039,6 +1047,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("The --edit-todo action can only be used during " "interactive rebase." )); +if (trace2_is_enabled ()) { +if (is_interactive (&options)) +trace2_cmd_subverb ("interactive" ); +else if (exec.nr) +trace2_cmd_subverb ("interactive-exec" ); +else +trace2_cmd_subverb (action_names[action]); + } +switch (action) {case ACTION_CONTINUE : {struct object_id head;
Signature · 1 line
gitgitgadget
PATCH v3 7/14 trace2:data: add trace2 transport child classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 child classification for transport processes.
--- connect.c | 3 +++ transport-helper.c | 2 ++ 2 files changed, 5 insertions(+)
connect.c +3 -0
@@ -1251,6 +1251,7 @@ struct child_process *git_connect(int fd[2], const char *url, conn =NULL ; }else if (protocol ==PROTO_GIT ) { conn =git_connect_git (fd, hostandport, path, prog, version, flags); + conn->trace2_child_class ="transport/git" ; }else {struct strbuf cmd =STRBUF_INIT ;const char *const *var;
@@ -1293,9 +1294,11 @@ struct child_process *git_connect(int fd[2], const char *url,strbuf_release (&cmd);return NULL ; } + conn->trace2_child_class ="transport/ssh" ;fill_ssh_args (conn, ssh_host, port, version, flags); }else {transport_check_allowed ("file" ); + conn->trace2_child_class ="transport/file" ;if (version >0 ) {argv_array_pushf (&conn->env_array,GIT_PROTOCOL_ENVIRONMENT "=version=%d" , version);
transport-helper.c +2 -0
@@ -127,6 +127,8 @@ static struct child_process *get_helper(struct transport *transport)argv_array_pushf (&helper->env_array,"%s=%s" ,GIT_DIR_ENVIRONMENT ,get_git_dir ()); + helper->trace2_child_class = helper->args.argv[0 ];/* "remote-<name>" */ + code =start_command (helper);if (code <0 && errno ==ENOENT )die (_ ("unable to find remote helper for '%s'" ), data->name);
Signature · 1 line
gitgitgadget
v4 cover Trace2 tracing facility
In reply to Jeff Hostetler via GitGitGadgetSorry to spam the list, but here is V4. After building V3 on 3 platforms without error and submitting, the compilers on platforms 4 and 5 complained about a variable declaration. (sigh) [] fix declaration after first statement [] add -DNO_UNIX_SOCKETS to BASIC_CFLAGS when NO_UNIX_SOCKETS is defined in the Makefile.
----------------------------------------------------------------------------
V3 addresses: [] re-fix the trace2 tests using an inline environment variable rather than exporting and unsetting. [] overhaul the design document to include prototype declarations and more file format information. [] incorporate most of the suggestions from clang-format. [] add ability to trace to a unix domain socket. [] added forward declarations suggested by Ramsay. [] rebased onto current master to fixup conflict with sb/submodule-recursive-fetch-gets-the-tip that was merged yesterday.
----------------------------------------------------------------------------
V2 addresses: [] "jh/trace2" bad interaction with "js/vsts-ci" in "pu". [] coccinelle warnings in trace2/tr2_tgt_perf.c reported during CI testing.
----------------------------------------------------------------------------
This patch series contains a greatly refactored version of my original Trace2 series [1] from August 2018.
A new design doc in Documentation/technical/api-trace2.txt (in the first commit) explains the relationship of Trace2 to the current tracing facility. Calls to the current tracing facility have not been changed, rather new trace2 calls have been added so that both continue to work in parallel for the time being.
[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (1):
pack-objects: add trace2 regions
Jeff Hostetler (13):
trace2: Documentation/technical/api-trace2.txt
trace2: create new combined trace facility
trace2: collect platform-specific process information trace2:data: add trace2 regions to wt-status trace2:data: add editor/pager child classification trace2:data: add trace2 sub-process classification trace2:data: add trace2 transport child classification trace2:data: add trace2 hook classification trace2:data: add trace2 instrumentation to index read/write trace2:data: add subverb to checkout command trace2:data: add subverb to reset command trace2:data: add subverb for rebase
trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
Documentation/technical/api-trace2.txt | 1347 ++++++++++++++++++++++++ Makefile | 15 +- builtin/am.c | 1 + builtin/checkout.c | 7 + builtin/pack-objects.c | 16 +- builtin/rebase.c | 17 + builtin/receive-pack.c | 4 + builtin/reset.c | 6 + builtin/submodule--helper.c | 9 +- builtin/worktree.c | 1 + cache.h | 1 + common-main.c | 13 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- compat/win32/ancestry.c | 101 ++ config.c | 2 + config.mak.uname | 2 + connect.c | 3 + editor.c | 1 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 ++ pager.c | 1 + read-cache.c | 51 +- remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 59 +- run-command.h | 13 +- sequencer.c | 2 + sh-i18n--envsubst.c | 3 + sub-process.c | 1 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 4 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 274 +++++ t/t0001-init.sh | 1 + t/t0210-trace2-normal.sh | 135 +++ t/t0210/scrub_normal.perl | 48 + t/t0211-trace2-perf.sh | 153 +++ t/t0211/scrub_perf.perl | 76 ++ t/t0212-trace2-event.sh | 234 ++++ t/t0212/parse_events.perl | 251 +++++ trace2.c | 807 ++++++++++++++ trace2.h | 380 +++++++ trace2/tr2_cfg.c | 90 ++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 197 ++++ trace2/tr2_dst.h | 36 + trace2/tr2_sid.c | 67 ++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 + trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 131 +++ trace2/tr2_tgt_event.c | 590 +++++++++++ trace2/tr2_tgt_normal.c | 325 ++++++ trace2/tr2_tgt_perf.c | 536 ++++++++++ trace2/tr2_tls.c | 164 +++ trace2/tr2_tls.h | 97 ++ trace2/tr2_verb.c | 30 + trace2/tr2_verb.h | 24 + transport-helper.c | 2 + transport.c | 1 + usage.c | 31 + wt-status.c | 24 +- 66 files changed, 6569 insertions(+), 22 deletions(-) create mode 100644 Documentation/technical/api-trace2.txt create mode 100644 compat/win32/ancestry.c create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
base-commit: b5101f929789889c2e536d915698f58d5c5c6b7a
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-108%2Fjeffhostetler%2Fcore-trace2-2019-v0-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-108/jeffhostetler/core-trace2-2019-v0-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/108
Range-diff vs v3:
1: 60b56d6a8f = 1: 60b56d6a8f trace2: Documentation/technical/api-trace2.txt
2: bfe2fe0bbe ! 2: 8c9687830e trace2: create new combined trace facility
@@ -52,6 +52,17 @@
LIB_OBJS += trailer.o
LIB_OBJS += transport.o
LIB_OBJS += transport-helper.o
+@@
+ LIB_OBJS += compat/inet_pton.o
+ BASIC_CFLAGS += -DNO_INET_PTON
+ endif
+-ifndef NO_UNIX_SOCKETS
++ifdef NO_UNIX_SOCKETS
++ BASIC_CFLAGS += -DNO_UNIX_SOCKETS
++else
+ LIB_OBJS += unix-socket.o
+ PROGRAM_OBJS += credential-cache.o
+ PROGRAM_OBJS += credential-cache--daemon.o
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
--- a/builtin/submodule--helper.c
@@ -2154,6 +2165,8 @@
+
+void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line)
+{
++ int fd = tr2_dst_get_trace_fd(dst);
++
+ strbuf_complete_line(buf_line); /* ensure final NL on buffer */
+
+ /*
@@ -2171,9 +2184,6 @@
+ *
+ * If we get an IO error, just close the trace dst.
+ */
-+
-+ int fd = tr2_dst_get_trace_fd(dst);
-+
+ if (write(fd, buf_line->buf, buf_line->len) >= 0)
+ return;
+
3: e8b8226640 = 3: 6167aba76c trace2: collect platform-specific process information
4: 0d59a6f2eb = 4: 3624c6cbd9 trace2:data: add trace2 regions to wt-status
5: 2513091f77 = 5: 638fdb294f trace2:data: add editor/pager child classification
6: 785f6f866e = 6: f4f0453e6b trace2:data: add trace2 sub-process classification
7: b7e6644f4b = 7: 660b83c453 trace2:data: add trace2 transport child classification
8: 52aace533b = 8: ab26888126 trace2:data: add trace2 hook classification
9: a2bcc6fd28 = 9: 87a73af338 trace2:data: add trace2 instrumentation to index read/write
10: d508322638 = 10: be707ea960 pack-objects: add trace2 regions
11: 33e431be88 = 11: b4c8ff53dd trace2:data: add subverb to checkout command
12: e46e92bc7c = 12: ed1d612006 trace2:data: add subverb to reset command
13: 0a05e15978 = 13: 4f55f33624 trace2:data: add subverb for rebase
14: aeaf990b1b = 14: 8d47cd71ed trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.shSignature · 1 line
gitgitgadget
PATCH v4 13/14 trace2:data: add subverb for rebase
From: Jeff Hostetler <[email protected]>
--- builtin/rebase.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+)
builtin/rebase.c +17 -0
@@ -850,6 +850,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)ACTION_EDIT_TODO ,ACTION_SHOW_CURRENT_PATCH , } action = NO_ACTION; +static const char *action_names[] = {N_ ("undefined" ), +N_ ("continue" ), +N_ ("skip" ), +N_ ("abort" ), +N_ ("quit" ), +N_ ("edit_todo" ), +N_ ("show_current_patch" ), +NULL };const char *gpg_sign =NULL ;struct string_list exec =STRING_LIST_INIT_NODUP ;const char *rebase_merges =NULL ;
@@ -1039,6 +1047,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("The --edit-todo action can only be used during " "interactive rebase." )); +if (trace2_is_enabled ()) { +if (is_interactive (&options)) +trace2_cmd_subverb ("interactive" ); +else if (exec.nr) +trace2_cmd_subverb ("interactive-exec" ); +else +trace2_cmd_subverb (action_names[action]); + } +switch (action) {case ACTION_CONTINUE : {struct object_id head;
Signature · 1 line
gitgitgadget
PATCH v4 14/14 trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Create unit tests for Trace2.
--- Makefile | 1 + t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/helper/test-trace2.c | 274 ++++++++++++++++++++++++++++++++++++++ t/t0210-trace2-normal.sh | 135 +++++++++++++++++++ t/t0210/scrub_normal.perl | 48 +++++++ t/t0211-trace2-perf.sh | 153 +++++++++++++++++++++ t/t0211/scrub_perf.perl | 76 +++++++++++ t/t0212-trace2-event.sh | 234 ++++++++++++++++++++++++++++++++ t/t0212/parse_events.perl | 251 ++++++++++++++++++++++++++++++++++ 10 files changed, 1174 insertions(+) create mode 100644 t/helper/test-trace2.c create mode 100755 t/t0210-trace2-normal.sh create mode 100644 t/t0210/scrub_normal.perl create mode 100755 t/t0211-trace2-perf.sh create mode 100644 t/t0211/scrub_perf.perl create mode 100755 t/t0212-trace2-event.sh create mode 100644 t/t0212/parse_events.perl
Makefile +1 -0
@@ -762,6 +762,7 @@ TEST_BUILTINS_OBJS += test-string-list.o TEST_BUILTINS_OBJS += test-submodule-config.o TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o +TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o TEST_BUILTINS_OBJS += test-wildmatch.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o
t/helper/test-tool.c +1 -0
@@ -51,6 +51,7 @@ static struct test_cmd cmds[] = { { "submodule-config", cmd__submodule_config }, { "submodule-nested-repo-config", cmd__submodule_nested_repo_config }, { "subprocess", cmd__subprocess }, + { "trace2", cmd__trace2 }, { "urlmatch-normalization", cmd__urlmatch_normalization }, { "wildmatch", cmd__wildmatch }, #ifdef GIT_WINDOWS_NATIVE
t/helper/test-tool.h +1 -0
@@ -46,6 +46,7 @@ int cmd__string_list(int argc, const char **argv);int cmd__submodule_config (int argc,const char **argv);int cmd__submodule_nested_repo_config (int argc,const char **argv);int cmd__subprocess (int argc,const char **argv); +int cmd__trace2 (int argc,const char **argv);int cmd__urlmatch_normalization (int argc,const char **argv);int cmd__wildmatch (int argc,const char **argv);#ifdef GIT_WINDOWS_NATIVE
t/helper/test-trace2.c new file +274 -0
274 lines not on this page · raw message
t/t0210-trace2-normal.sh new file +135 -0
135 lines not on this page · raw message
t/t0210/scrub_normal.perl new file +48 -0
48 lines not on this page · raw message
t/t0211-trace2-perf.sh new file +153 -0
153 lines not on this page · raw message
t/t0211/scrub_perf.perl new file +76 -0
76 lines not on this page · raw message
t/t0212-trace2-event.sh new file +234 -0
234 lines not on this page · raw message
t/t0212/parse_events.perl new file +251 -0
251 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v4 12/14 trace2:data: add subverb to reset command
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/reset.c | 6 ++++++ 1 file changed, 6 insertions(+)
builtin/reset.c +6 -0
@@ -340,6 +340,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (patch_mode) {if (reset_type !=NONE )die (_ ("--patch is incompatible with --{hard,mixed,soft}" )); +trace2_cmd_subverb ("patch-interactive" );return run_add_interactive (rev,"--patch=reset" , &pathspec); }
@@ -356,6 +357,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==NONE ) reset_type =MIXED ;/* by default */ +if (pathspec.nr) +trace2_cmd_subverb ("path" ); +else +trace2_cmd_subverb (reset_type_names[reset_type]); +if (reset_type !=SOFT && (reset_type !=MIXED ||get_git_work_tree ()))setup_work_tree ();
Signature · 1 line
gitgitgadget
PATCH v4 11/14 trace2:data: add subverb to checkout command
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- builtin/checkout.c | 7 +++++++ 1 file changed, 7 insertions(+)
builtin/checkout.c +7 -0
@@ -262,6 +262,8 @@ static int checkout_paths(const struct checkout_opts *opts,struct lock_file lock_file =LOCK_INIT ;int nr_checkouts =0 ; +trace2_cmd_subverb (opts->patch_mode ?"patch" :"path" ); +if (opts->track !=BRANCH_TRACK_UNSPECIFIED )die (_ ("'%s' cannot be used with updating paths" ),"--track" );
@@ -952,6 +954,9 @@ static int switch_branches(const struct checkout_opts *opts,void *path_to_free;struct object_id rev;int flag, writeout_error =0 ; + +trace2_cmd_subverb ("branch" ); +memset (&old_branch_info,0 ,sizeof (old_branch_info)); old_branch_info.path = path_to_free =resolve_refdup ("HEAD" ,0 , &rev, &flag);if (old_branch_info.path)
@@ -1189,6 +1194,8 @@ static int switch_unborn_to_new_branch(const struct checkout_opts *opts)int status;struct strbuf branch_ref =STRBUF_INIT ; +trace2_cmd_subverb ("unborn" ); +if (!opts->new_branch)die (_ ("You are on a branch yet to be born" ));strbuf_addf (&branch_ref,"refs/heads/%s" , opts->new_branch);
Signature · 1 line
gitgitgadget
PATCH v4 2/14 trace2: create new combined trace facility
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Create a new unified tracing facility for git. The eventual intent is to replace the current trace_printf* and trace_performance* routines with a unified set of git_trace2* routines.
In addition to the usual printf-style API, trace2 provides higer-level event verbs with fixed-fields allowing structured data to be written. This makes post-processing and analysis easier for external tools.
Trace2 defines 3 output targets. These are set using the environment variables "GIT_TR2", "GIT_TR2_PERF", and "GIT_TR2_EVENT". These may be set to "1" or to an absolute pathname (just like the current GIT_TRACE).
* GIT_TR2 is intended to be a replacement for GIT_TRACE and logs command summary data.
* GIT_TR2_PERF is intended as a replacement for GIT_TRACE_PERFORMANCE. It extends the output with columns for the command process, thread, repo, absolute and relative elapsed times. It reports events for child process start/stop, thread start/stop, and per-thread function nesting.
* GIT_TR2_EVENT is a new structured format. It writes event data as a series of JSON records.
Calls to trace2 functions log to any of the 3 output targets enabled without the need to call different trace_printf* or trace_performance* routines.
--- Makefile | 14 +- builtin/submodule--helper.c | 9 +- cache.h | 1 + common-main.c | 12 +- compat/mingw.c | 11 +- compat/mingw.h | 3 +- config.c | 2 + exec-cmd.c | 2 + git-compat-util.h | 7 + git.c | 65 +++ remote-curl.c | 7 + repository.c | 2 + repository.h | 3 + run-command.c | 59 ++- run-command.h | 13 +- sh-i18n--envsubst.c | 3 + submodule.c | 11 +- t/helper/test-parse-options.c | 3 + t/helper/test-tool.c | 3 + t/t0001-init.sh | 1 + trace2.c | 807 ++++++++++++++++++++++++++++++++++ trace2.h | 366 +++++++++++++++ trace2/tr2_cfg.c | 90 ++++ trace2/tr2_cfg.h | 19 + trace2/tr2_dst.c | 197 +++++++++ trace2/tr2_dst.h | 36 ++ trace2/tr2_sid.c | 67 +++ trace2/tr2_sid.h | 18 + trace2/tr2_tbuf.c | 32 ++ trace2/tr2_tbuf.h | 23 + trace2/tr2_tgt.h | 131 ++++++ trace2/tr2_tgt_event.c | 590 +++++++++++++++++++++++++ trace2/tr2_tgt_normal.c | 325 ++++++++++++++ trace2/tr2_tgt_perf.c | 536 ++++++++++++++++++++++ trace2/tr2_tls.c | 164 +++++++ trace2/tr2_tls.h | 97 ++++ trace2/tr2_verb.c | 30 ++ trace2/tr2_verb.h | 24 + usage.c | 31 ++ 39 files changed, 3796 insertions(+), 18 deletions(-) create mode 100644 trace2.c create mode 100644 trace2.h create mode 100644 trace2/tr2_cfg.c create mode 100644 trace2/tr2_cfg.h create mode 100644 trace2/tr2_dst.c create mode 100644 trace2/tr2_dst.h create mode 100644 trace2/tr2_sid.c create mode 100644 trace2/tr2_sid.h create mode 100644 trace2/tr2_tbuf.c create mode 100644 trace2/tr2_tbuf.h create mode 100644 trace2/tr2_tgt.h create mode 100644 trace2/tr2_tgt_event.c create mode 100644 trace2/tr2_tgt_normal.c create mode 100644 trace2/tr2_tgt_perf.c create mode 100644 trace2/tr2_tls.c create mode 100644 trace2/tr2_tls.h create mode 100644 trace2/tr2_verb.c create mode 100644 trace2/tr2_verb.h
Makefile +13 -1
@@ -1005,6 +1005,16 @@ LIB_OBJS += tempfile.o LIB_OBJS += thread-utils.o LIB_OBJS += tmp-objdir.o LIB_OBJS += trace.o +LIB_OBJS += trace2.o +LIB_OBJS += trace2/tr2_cfg.o +LIB_OBJS += trace2/tr2_dst.o +LIB_OBJS += trace2/tr2_sid.o +LIB_OBJS += trace2/tr2_tbuf.o +LIB_OBJS += trace2/tr2_tgt_event.o +LIB_OBJS += trace2/tr2_tgt_normal.o +LIB_OBJS += trace2/tr2_tgt_perf.o +LIB_OBJS += trace2/tr2_tls.o +LIB_OBJS += trace2/tr2_verb.o LIB_OBJS += trailer.o LIB_OBJS += transport.o LIB_OBJS += transport-helper.o
@@ -1581,7 +1591,9 @@ ifdef NO_INET_PTON LIB_OBJS += compat/inet_pton.o BASIC_CFLAGS += -DNO_INET_PTON endif -ifndef NO_UNIX_SOCKETS +ifdef NO_UNIX_SOCKETS + BASIC_CFLAGS += -DNO_UNIX_SOCKETS +else LIB_OBJS += unix-socket.o PROGRAM_OBJS += credential-cache.o PROGRAM_OBJS += credential-cache--daemon.o
builtin/submodule--helper.c +4 -5
@@ -1815,11 +1815,10 @@ static int update_submodules(struct submodule_update_clone *suc) {int i; -run_processes_parallel (suc->max_jobs, - update_clone_get_next_task, - update_clone_start_failure, - update_clone_task_finished, - suc); +run_processes_parallel_tr2 (suc->max_jobs, update_clone_get_next_task, + update_clone_start_failure, + update_clone_task_finished, suc,"submodule" , +"parallel/update" );/* * We saved the output and put it out all at once now.
cache.h +1 -0
@@ -9,6 +9,7 @@#include "gettext.h" #include "convert.h" #include "trace.h" +#include "trace2.h" #include "string-list.h" #include "pack-revindex.h" #include "hash.h"
common-main.c +10 -2
@@ -25,12 +25,18 @@ static void restore_sigpipe_to_default(void)int main (int argc,const char **argv) { +int result; +/* * Always open file descriptors 0/1/2 to avoid clobbering files * in die(). It also avoids messing up when the pipes are dup'ed * onto stdin/stdout/stderr in the child processes we spawn. */ sanitize_stdfds (); +restore_sigpipe_to_default (); + +trace2_initialize (); +trace2_cmd_start (argv);git_resolve_executable_dir (argv[0 ]);
@@ -40,7 +46,9 @@ int main(int argc, const char **argv)attr_start (); -restore_sigpipe_to_default (); + result =cmd_main (argc, argv); + +trace2_cmd_exit (result); -return cmd_main (argc, argv); +return result; }
compat/mingw.c +10 -1
@@ -1476,19 +1476,23 @@ static int try_shell_exec(const char *cmd, char *const *argv)return 0 ; prog =path_lookup (interpr,1 );if (prog) { +int exec_id;int argc =0 ;const char **argv2;while (argv[argc]) argc++;ALLOC_ARRAY (argv2, argc +1 ); argv2[0 ] = (char *)cmd;/* full path to the script file */ memcpy (&argv2[1 ], &argv[1 ],sizeof (*argv) * argc); + exec_id =trace2_exec (prog, argv2); pid =mingw_spawnv (prog, argv2,1 );if (pid >=0 ) {int status;if (waitpid (pid, &status,0 ) <0 ) status =255 ; +trace2_exec_result (exec_id, status);exit (status); } +trace2_exec_result (exec_id,-1 ); pid =1 ;/* indicate that we tried but failed */ free (prog);free (argv2);
@@ -1501,12 +1505,17 @@ int mingw_execv(const char *cmd, char *const *argv)/* check if git_command is a shell script */ if (!try_shell_exec (cmd, argv)) {int pid, status; +int exec_id; + exec_id =trace2_exec (cmd, (const char **)argv); pid =mingw_spawnv (cmd, (const char **)argv,0 ); -if (pid <0 ) +if (pid <0 ) { +trace2_exec_result (exec_id,-1 );return -1 ; + }if (waitpid (pid, &status,0 ) <0 ) status =255 ; +trace2_exec_result (exec_id, status);exit (status); }return -1 ;
compat/mingw.h +1 -2
@@ -147,8 +147,7 @@ static inline int fcntl(int fd, int cmd, ...) errno =EINVAL ;return -1 ; } -/* bash cannot reliably detect negative return codes as failure */ -#define exit (code) exit((code) & 0xff) +#define sigemptyset (x) (void)0static inline int sigaddset (sigset_t *set,int signum) {return 0 ; }
config.c +2 -0
@@ -2656,6 +2656,8 @@ int git_config_set_gently(const char *key, const char *value)void git_config_set (const char *key,const char *value) {git_config_set_multivar (key, value,NULL ,0 ); + +trace2_cmd_set_config (key, value); }/*
exec-cmd.c +2 -0
@@ -209,6 +209,8 @@ static int git_get_exec_path(struct strbuf *buf, const char *argv0)return -1 ; } +trace2_cmd_path (buf->buf); +return 0 ; }
git-compat-util.h +7 -0
@@ -1251,6 +1251,13 @@ static inline int is_missing_file_error(int errno_)extern int cmd_main (int ,const char **); +/* +* Intercept all calls to exit() and route them to trace2 to +* optionally emit a message before calling the real exit(). +*/ +int trace2_cmd_exit_fl (const char *file,int line,int code); +#define exit (code) exit(trace2_cmd_exit_fl(__FILE__, __LINE__, (code))) +/* * You can mark a stack variable with UNLEAK(var) to avoid it being * reported as a leak by tools like LSAN or valgrind. The argument
git.c +65 -0
127 lines not on this page · raw message
remote-curl.c +7 -0
@@ -1360,6 +1360,13 @@ int cmd_main(int argc, const char **argv)string_list_init (&options.deepen_not,1 );string_list_init (&options.push_options,1 ); +/* +* Just report "remote-curl" here (folding all the various aliases +* ("git-remote-http", "git-remote-https", and etc.) here since they +* are all just copies of the same actual executable. +*/ +trace2_cmd_verb ("remote-curl" ); + remote =remote_get (argv[1 ]);if (argc >2 ) {
repository.c +2 -0
@@ -119,6 +119,8 @@ static int repo_init_gitdir(struct repository *repo, const char *gitdir)void repo_set_worktree (struct repository *repo,const char *path) { repo->worktree =real_pathdup (path,1 ); + +trace2_def_repo (repo); } static int read_and_verify_repository_format(struct repository_format *format,
repository.h +3 -0
@@ -90,6 +90,9 @@ struct repository {/* Repository's current hash algorithm, as serialized on disk. */ const struct git_hash_algo *hash_algo; +/* A unique-id for tracing purposes. */ +int trace2_repo_id; +/* Configurations */ /* Indicate if a repository has a different 'commondir' from 'gitdir' */
run-command.c +58 -1
114 lines not on this page · raw message
run-command.h +12 -1
@@ -10,6 +10,12 @@ struct child_process {struct argv_array args;struct argv_array env_array;pid_t pid; + +int trace2_child_id; +uint64_t trace2_child_us_start; +const char *trace2_child_class; +const char *trace2_hook_name; +/* * Using .in, .out, .err: * - Specify 0 for no redirections (child inherits stdin, stdout,
@@ -73,12 +79,14 @@ extern int run_hook_ve(const char *const *env, const char *name, va_list args);#define RUN_USING_SHELL 16#define RUN_CLEAN_ON_EXIT 32int run_command_v_opt (const char **argv,int opt); - +int run_command_v_opt_tr2 (const char **argv,int opt,const char *tr2_class);/* * env (the environment) is to be formatted like environ: "VAR=VALUE". * To unset an environment variable use just "VAR". */ int run_command_v_opt_cd_env (const char **argv,int opt,const char *dir,const char *const *env); +int run_command_v_opt_cd_env_tr2 (const char **argv,int opt,const char *dir, +const char *const *env,const char *tr2_class);/** * Execute the given command, sending "in" to its stdin, and capturing its
@@ -220,5 +228,8 @@ int run_processes_parallel(int n, start_failure_fn, task_finished_fn, void *pp_cb); +int run_processes_parallel_tr2 (int n,get_next_task_fn ,start_failure_fn , +task_finished_fn ,void *pp_cb, +const char *tr2_category,const char *tr2_label);#endif
sh-i18n--envsubst.c +3 -0
@@ -14,6 +14,7 @@*/ #include "git-compat-util.h" +#include "trace2.h" /* Substitution of environment variables in shell format strings. Copyright (C) 2003-2007 Free Software Foundation, Inc.
@@ -67,6 +68,8 @@ cmd_main (int argc, const char *argv[])/* Default values for command line options. */ /* unsigned short int show_variables = 0; */ +trace2_cmd_verb ("sh-i18n--envsubst" ); +switch (argc) {case 1 :
submodule.c +6 -5
@@ -1568,11 +1568,12 @@ int fetch_populated_submodules(struct repository *r,calculate_changed_submodule_paths (r, &spf.changed_submodule_names);string_list_sort (&spf.changed_submodule_names); -run_processes_parallel (max_parallel_jobs, - get_next_submodule, - fetch_start_failure, - fetch_finish, - &spf); +run_processes_parallel_tr2 (max_parallel_jobs, + get_next_submodule, + fetch_start_failure, + fetch_finish, + &spf, +"submodule" ,"parallel/fetch" );argv_array_clear (&spf.args); out:
t/helper/test-parse-options.c +3 -0
@@ -2,6 +2,7 @@#include "cache.h" #include "parse-options.h" #include "string-list.h" +#include "trace2.h" static int boolean =0 ;static int integer =0 ;
@@ -153,6 +154,8 @@ int cmd__parse_options(int argc, const char **argv)int i;int ret =0 ; +trace2_cmd_verb ("_parse_" ); + argc =parse_options (argc, (const char **)argv, prefix, options, usage,0 );if (length_cb.called) {
t/helper/test-tool.c +3 -0
@@ -1,5 +1,6 @@#include "git-compat-util.h" #include "test-tool.h" +#include "trace2.h" struct test_cmd {const char *name;
@@ -80,6 +81,8 @@ int cmd_main(int argc, const char **argv)if (!strcmp (cmds[i].name, argv[1 ])) { argv++; argc--; +trace2_cmd_verb (cmds[i].name); +trace2_cmd_list_config ();return cmds[i].fn (argc, argv); } }
t/t0001-init.sh +1 -0
@@ -93,6 +93,7 @@ test_expect_success 'No extra GIT_* on alias scripts' ' sed -n \ -e "/^GIT_PREFIX=/d" \ -e "/^GIT_TEXTDOMAINDIR=/d" \ + -e "/^GIT_TR2_PARENT/d" \ -e "/^GIT_/s/=.*//p" | sort EOF
trace2.c new file +807 -0
807 lines not on this page · raw message
trace2.h new file +366 -0
366 lines not on this page · raw message
trace2/tr2_cfg.c new file +90 -0
90 lines not on this page · raw message
trace2/tr2_cfg.h new file +19 -0
@@ -0,0 +1,19 @@ +#ifndef TR2_CFG_H +#define TR2_CFG_H + +/* +* Iterate over all config settings and emit 'def_param' events for the +* "interesting" ones to TRACE2. +*/ +void tr2_cfg_list_config_fl (const char *file,int line); + +/* +* Emit a "def_param" event for the given key/value pair IF we consider +* the key to be "interesting". +*/ +void tr2_cfg_set_fl (const char *file,int line,const char *key, +const char *value); + +void tr2_cfg_free_patterns (void ); + +#endif /* TR2_CFG_H */
trace2/tr2_dst.c new file +197 -0
197 lines not on this page · raw message
trace2/tr2_dst.h new file +36 -0
@@ -0,0 +1,36 @@ +#ifndef TR2_DST_H +#define TR2_DST_H + +struct strbuf ; + +struct tr2_dst { +const char *const env_var_name; +int fd; +unsigned int initialized :1 ; +unsigned int need_close :1 ; +}; + +/* +* Disable TRACE2 on the destination. In TRACE2 a destination (DST) +* wraps a file descriptor; it is associated with a TARGET which +* defines the formatting. +*/ +void tr2_dst_trace_disable (struct tr2_dst *dst); + +/* +* Return the file descriptor for the DST. +* If 0, the dst is closed or disabled. +*/ +int tr2_dst_get_trace_fd (struct tr2_dst *dst); + +/* +* Return true if the DST is opened for writing. +*/ +int tr2_dst_trace_want (struct tr2_dst *dst); + +/* +* Write a single line/message to the trace file. +*/ +void tr2_dst_write_line (struct tr2_dst *dst,struct strbuf *buf_line); + +#endif /* TR2_DST_H */
trace2/tr2_sid.c new file +67 -0
67 lines not on this page · raw message
trace2/tr2_sid.h new file +18 -0
@@ -0,0 +1,18 @@ +#ifndef TR2_SID_H +#define TR2_SID_H + +/* +* Get our session id. Compute if necessary. +*/ +const char *tr2_sid_get (void ); + +/* +* Get our process depth. A top-level git process invoked from the +* command line will have depth=0. A child git process will have +* depth=1 and so on. +*/ +int tr2_sid_depth (void ); + +void tr2_sid_release (void ); + +#endif /* TR2_SID_H */
trace2/tr2_tbuf.c new file +32 -0
@@ -0,0 +1,32 @@ +#include "cache.h" +#include "tr2_tbuf.h" + +void tr2_tbuf_local_time (struct tr2_tbuf *tb) +{ +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +localtime_r (&secs, &tm); + +xsnprintf (tb->buf,sizeof (tb->buf),"%02d:%02d:%02d.%06ld" , tm.tm_hour, + tm.tm_min, tm.tm_sec, (long )tv.tv_usec); +} + +void tr2_tbuf_utc_time (struct tr2_tbuf *tb) +{ +struct timeval tv; +struct tm tm; +time_t secs; + +gettimeofday (&tv,NULL ); + secs = tv.tv_sec; +gmtime_r (&secs, &tm); + +xsnprintf (tb->buf,sizeof (tb->buf), +"%4d-%02d-%02d %02d:%02d:%02d.%06ld" , tm.tm_year +1900 , + tm.tm_mon +1 , tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, + (long )tv.tv_usec); +}
trace2/tr2_tbuf.h new file +23 -0
@@ -0,0 +1,23 @@ +#ifndef TR2_TBUF_H +#define TR2_TBUF_H + +/* +* A simple wrapper around a fixed buffer to avoid C syntax +* quirks and the need to pass around an additional size_t +* argument. +*/ +struct tr2_tbuf { +char buf[32 ]; +}; + +/* +* Fill buffer with formatted local time string. +*/ +void tr2_tbuf_local_time (struct tr2_tbuf *tb); + +/* +* Fill buffer with formatted UTC time string. +*/ +void tr2_tbuf_utc_time (struct tr2_tbuf *tb); + +#endif /* TR2_TBUF_H */
trace2/tr2_tgt.h new file +131 -0
131 lines not on this page · raw message
trace2/tr2_tgt_event.c new file +590 -0
590 lines not on this page · raw message
trace2/tr2_tgt_normal.c new file +325 -0
94 lines not on this page · raw message
trace2/tr2_tgt_perf.c new file +536 -0
536 lines not on this page · raw message
trace2/tr2_tls.c new file +164 -0
164 lines not on this page · raw message
trace2/tr2_tls.h new file +97 -0
97 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
PATCH v4 9/14 trace2:data: add trace2 instrumentation to index read/write
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 events to measure reading and writing the index.
--- read-cache.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-)
read-cache.c +50 -1
@@ -2232,6 +2232,16 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)load_index_extensions (&p); }munmap ((void *)mmap, mmap_size); + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_data_intmax ("index" , the_repository,"read/version" , + istate->version); +trace2_data_intmax ("index" , the_repository,"read/cache_nr" , + istate->cache_nr); +return istate->cache_nr; unmap:
@@ -2263,9 +2273,17 @@ int read_index_from(struct index_state *istate, const char *path,if (istate->initialized)return istate->cache_nr; +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_region_enter_printf ("index" ,"do_read_index" , the_repository, +"%s" , path);trace_performance_enter (); ret =do_read_index (istate, path,0 );trace_performance_leave ("read cache %s" , path); +trace2_region_leave_printf ("index" ,"do_read_index" , the_repository, +"%s" , path); split_index = istate->split_index;if (!split_index ||is_null_oid (&split_index->base_oid)) {
@@ -2281,7 +2299,11 @@ int read_index_from(struct index_state *istate, const char *path, base_oid_hex =oid_to_hex (&split_index->base_oid); base_path =xstrfmt ("%s/sharedindex.%s" , gitdir, base_oid_hex); +trace2_region_enter_printf ("index" ,"shared/do_read_index" , + the_repository,"%s" , base_path); ret =do_read_index (split_index->base, base_path,1 ); +trace2_region_leave_printf ("index" ,"shared/do_read_index" , + the_repository,"%s" , base_path);if (!oideq (&split_index->base_oid, &split_index->base->oid)) die(_("broken index, expect %s in %s, got %s"), base_oid_hex, base_path,
@@ -2988,6 +3010,16 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, istate->timestamp.sec = (unsigned int )st.st_mtime; istate->timestamp.nsec =ST_MTIME_NSEC (st);trace_performance_since (start,"write index, changed mask = %x" , istate->cache_changed); + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_data_intmax ("index" , the_repository,"write/version" , + istate->version); +trace2_data_intmax ("index" , the_repository,"write/cache_nr" , + istate->cache_nr); +return 0 ; }
@@ -3007,7 +3039,18 @@ static int commit_locked_index(struct lock_file *lk)static int do_write_locked_index (struct index_state *istate,struct lock_file *lock,unsigned flags) { -int ret =do_write_index (istate, lock->tempfile,0 ); +int ret; + +/* +* TODO trace2: replace "the_repository" with the actual repo instance +* that is associated with the given "istate". +*/ +trace2_region_enter_printf ("index" ,"do_write_index" , the_repository, +"%s" , lock->tempfile->filename.buf); + ret =do_write_index (istate, lock->tempfile,0 ); +trace2_region_leave_printf ("index" ,"do_write_index" , the_repository, +"%s" , lock->tempfile->filename.buf); +if (ret)return ret;if (flags &COMMIT_LOCK )
@@ -3092,7 +3135,13 @@ static int write_shared_index(struct index_state *istate,int ret;move_cache_to_base_index (istate); + +trace2_region_enter_printf ("index" ,"shared/do_write_index" , + the_repository,"%s" , (*temp)->filename.buf); ret =do_write_index (si->base, *temp,1 ); +trace2_region_enter_printf ("index" ,"shared/do_write_index" , + the_repository,"%s" , (*temp)->filename.buf); +if (ret)return ret; ret =adjust_shared_perm (get_tempfile_path (*temp));
Signature · 1 line
gitgitgadget
PATCH v4 10/14 pack-objects: add trace2 regions
In reply to Jeff Hostetler via GitGitGadgetFrom: Derrick Stolee <[email protected]>
When studying the performance of 'git push' we would like to know how much time is spent at various parts of the command. One area that could cause performance trouble is 'git pack-objects'.
Add trace2 regions around the three main actions taken in this command:
1. Enumerate objects.
2. Prepare pack.
3. Write pack-file.
--- builtin/pack-objects.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
builtin/pack-objects.c +15 -1
@@ -33,6 +33,7 @@#include "object-store.h" #include "dir.h" #include "midx.h" +#include "trace2.h" #define IN_PACK (obj) oe_in_pack(&to_pack, obj)#define SIZE (obj) oe_size(&to_pack, obj)
@@ -3472,6 +3473,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) } } +trace2_region_enter ("pack-objects" ,"enumerate-objects" , + the_repository);prepare_packing_data (the_repository, &to_pack);if (progress)
@@ -3486,12 +3489,23 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)if (include_tag && nr_result)for_each_ref (add_ref_tag,NULL );stop_progress (&progress_state); +trace2_region_leave ("pack-objects" ,"enumerate-objects" , + the_repository);if (non_empty && !nr_result)return 0 ; -if (nr_result) +if (nr_result) { +trace2_region_enter ("pack-objects" ,"prepare-pack" , + the_repository);prepare_pack (window, depth); +trace2_region_leave ("pack-objects" ,"prepare-pack" , + the_repository); + } + +trace2_region_enter ("pack-objects" ,"write-pack-file" , the_repository);write_pack_file (); +trace2_region_leave ("pack-objects" ,"write-pack-file" , the_repository); +if (progress) fprintf_ln(stderr, _("Total %"PRIu32" (delta %"PRIu32"),"
Signature · 1 line
gitgitgadget
PATCH v4 7/14 trace2:data: add trace2 transport child classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 child classification for transport processes.
--- connect.c | 3 +++ transport-helper.c | 2 ++ 2 files changed, 5 insertions(+)
connect.c +3 -0
@@ -1251,6 +1251,7 @@ struct child_process *git_connect(int fd[2], const char *url, conn =NULL ; }else if (protocol ==PROTO_GIT ) { conn =git_connect_git (fd, hostandport, path, prog, version, flags); + conn->trace2_child_class ="transport/git" ; }else {struct strbuf cmd =STRBUF_INIT ;const char *const *var;
@@ -1293,9 +1294,11 @@ struct child_process *git_connect(int fd[2], const char *url,strbuf_release (&cmd);return NULL ; } + conn->trace2_child_class ="transport/ssh" ;fill_ssh_args (conn, ssh_host, port, version, flags); }else {transport_check_allowed ("file" ); + conn->trace2_child_class ="transport/file" ;if (version >0 ) {argv_array_pushf (&conn->env_array,GIT_PROTOCOL_ENVIRONMENT "=version=%d" , version);
transport-helper.c +2 -0
@@ -127,6 +127,8 @@ static struct child_process *get_helper(struct transport *transport)argv_array_pushf (&helper->env_array,"%s=%s" ,GIT_DIR_ENVIRONMENT ,get_git_dir ()); + helper->trace2_child_class = helper->args.argv[0 ];/* "remote-<name>" */ + code =start_command (helper);if (code <0 && errno ==ENOENT )die (_ ("unable to find remote helper for '%s'" ), data->name);
Signature · 1 line
gitgitgadget
PATCH v4 8/14 trace2:data: add trace2 hook classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Classify certain child processes as hooks.
--- builtin/am.c | 1 + builtin/receive-pack.c | 4 ++++ builtin/worktree.c | 1 + sequencer.c | 2 ++ transport.c | 1 + 5 files changed, 9 insertions(+)
builtin/am.c +1 -0
@@ -468,6 +468,7 @@ static int run_post_rewrite_hook(const struct am_state *state) cp.in =xopen (am_path (state,"rewritten" ),O_RDONLY ); cp.stdout_to_stderr =1 ; + cp.trace2_hook_name ="post-rewrite" ; ret =run_command (&cp);
builtin/receive-pack.c +4 -0
@@ -694,6 +694,8 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, proc.argv = argv; proc.in =-1 ; proc.stdout_to_stderr =1 ; + proc.trace2_hook_name = hook_name; +if (feed_state->push_options) {int i;for (i =0 ; i < feed_state->push_options->nr; i++)
@@ -807,6 +809,7 @@ static int run_update_hook(struct command *cmd) proc.stdout_to_stderr =1 ; proc.err = use_sideband ?-1 :0 ; proc.argv = argv; + proc.trace2_hook_name ="update" ; code =start_command (&proc);if (code)
@@ -1190,6 +1193,7 @@ static void run_update_post_hook(struct command *commands) proc.no_stdin =1 ; proc.stdout_to_stderr =1 ; proc.err = use_sideband ?-1 :0 ; + proc.trace2_hook_name ="post-update" ;if (!start_command (&proc)) {if (use_sideband)
builtin/worktree.c +1 -0
@@ -402,6 +402,7 @@ static int add_worktree(const char *path, const char *refname, cp.dir = path; cp.env = env; cp.argv =NULL ; + cp.trace2_hook_name ="post-checkout" ; argv_array_pushl(&cp.args, absolute_path(hook), oid_to_hex(&null_oid), oid_to_hex(&commit->object.oid),
sequencer.c +2 -0
@@ -1103,6 +1103,7 @@ static int run_rewrite_hook(const struct object_id *oldoid, proc.argv = argv; proc.in =-1 ; proc.stdout_to_stderr =1 ; + proc.trace2_hook_name ="post-rewrite" ; code =start_command (&proc);if (code)
@@ -3784,6 +3785,7 @@ static int pick_commits(struct repository *r, hook.in =open (rebase_path_rewritten_list (),O_RDONLY ); hook.stdout_to_stderr =1 ; + hook.trace2_hook_name ="post-rewrite" ;argv_array_push (&hook.args, post_rewrite_hook);argv_array_push (&hook.args,"rebase" );/* we don't care if this hook failed */
transport.c +1 -0
@@ -1061,6 +1061,7 @@ static int run_pre_push_hook(struct transport *transport, proc.argv = argv; proc.in =-1 ; + proc.trace2_hook_name ="pre-push" ;if (start_command (&proc)) {finish_command (&proc);
Signature · 1 line
gitgitgadget
PATCH v4 6/14 trace2:data: add trace2 sub-process classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 classification for long-running processes started in sub-process.c
--- sub-process.c | 1 + 1 file changed, 1 insertion(+)
sub-process.c +1 -0
@@ -88,6 +88,7 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co process->out =-1 ; process->clean_on_exit =1 ; process->clean_on_exit_handler = subprocess_exit_handler; + process->trace2_child_class ="subprocess" ; err =start_command (process);if (err) {
Signature · 1 line
gitgitgadget
PATCH v4 5/14 trace2:data: add editor/pager child classification
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2 process classification for editor and pager child processes.
--- editor.c | 1 + pager.c | 1 + 2 files changed, 2 insertions(+)
editor.c +1 -0
@@ -78,6 +78,7 @@ static int launch_specified_editor(const char *editor, const char *path, p.argv = args; p.env = env; p.use_shell =1 ; + p.trace2_child_class ="editor" ;if (start_command (&p) <0 )return error ("unable to start editor '%s'" , editor);
pager.c +1 -0
@@ -100,6 +100,7 @@ void prepare_pager_args(struct child_process *pager_process, const char *pager)argv_array_push (&pager_process->args, pager); pager_process->use_shell =1 ;setup_pager_env (&pager_process->env_array); + pager_process->trace2_child_class ="pager" ; }void setup_pager (void )
Signature · 1 line
gitgitgadget
PATCH v4 4/14 trace2:data: add trace2 regions to wt-status
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add trace2_region_enter() and trace2_region_leave() calls around the various phases of a status scan. This gives elapsed time for each phase in the GIT_TR2_PERF and GIT_TR2_EVENT trace target.
Also, these Trace2 calls now use s->repo rather than the_repository.
--- wt-status.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
wt-status.c +22 -2
@@ -748,12 +748,23 @@ static int has_unmerged(struct wt_status *s)void wt_status_collect (struct wt_status *s) { +trace2_region_enter ("status" ,"worktrees" , s->repo);wt_status_collect_changes_worktree (s); -if (s->is_initial) +trace2_region_leave ("status" ,"worktrees" , s->repo); + +if (s->is_initial) { +trace2_region_enter ("status" ,"initial" , s->repo);wt_status_collect_changes_initial (s); -else +trace2_region_leave ("status" ,"initial" , s->repo); + }else { +trace2_region_enter ("status" ,"index" , s->repo);wt_status_collect_changes_index (s); +trace2_region_leave ("status" ,"index" , s->repo); + } + +trace2_region_enter ("status" ,"untracked" , s->repo);wt_status_collect_untracked (s); +trace2_region_leave ("status" ,"untracked" , s->repo);wt_status_get_state (s->repo, &s->state, s->branch && !strcmp (s->branch,"HEAD" ));if (s->state.merge_in_progress && !has_unmerged (s))
@@ -2291,6 +2302,13 @@ static void wt_porcelain_v2_print(struct wt_status *s)void wt_status_print (struct wt_status *s) { +trace2_data_intmax ("status" , s->repo,"count/changed" , s->change.nr); +trace2_data_intmax ("status" , s->repo,"count/untracked" , + s->untracked.nr); +trace2_data_intmax ("status" , s->repo,"count/ignored" , s->ignored.nr); + +trace2_region_enter ("status" ,"print" , s->repo); +switch (s->status_format) {case STATUS_FORMAT_SHORT :wt_shortstatus_print (s);
@@ -2309,6 +2327,8 @@ void wt_status_print(struct wt_status *s)wt_longstatus_print (s);break ; } + +trace2_region_leave ("status" ,"print" , s->repo); }/**
Signature · 1 line
gitgitgadget
PATCH v4 3/14 trace2: collect platform-specific process information
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add optional platform-specific code to log information about the current process.
On Windows, this includes whether git.exe is running under a debugger and information about the ancestors of the process.
The purpose of this information is to help indicate if the process was launched interactively or in the background by an IDE, for example.
--- common-main.c | 1 + compat/win32/ancestry.c | 101 ++++++++++++++++++++++++++++++++++++++++ config.mak.uname | 2 + trace2.h | 14 ++++++ 4 files changed, 118 insertions(+) create mode 100644 compat/win32/ancestry.c
common-main.c +1 -0
@@ -37,6 +37,7 @@ int main(int argc, const char **argv)trace2_initialize ();trace2_cmd_start (argv); +trace2_collect_process_info ();git_resolve_executable_dir (argv[0 ]);
compat/win32/ancestry.c new file +101 -0
101 lines not on this page · raw message
config.mak.uname +2 -0
@@ -391,6 +391,7 @@ ifeq ($(uname_S),Windows) BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE COMPAT_OBJS = compat/msvc.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ + compat/win32/ancestry.o \ compat/win32/dirent.o COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
@@ -545,6 +546,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ + compat/win32/ancestry.o \ compat/win32/path-utils.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o
trace2.h +14 -0
@@ -363,4 +363,18 @@ __attribute__((format (printf, 1, 2)))void trace2_printf (const char *fmt, ...);#endif +/* +* Optional platform-specific code to dump information about the +* current and any parent process(es). This is intended to allow +* post-processors to know who spawned this git instance and anything +* else the platform may be able to tell us about the current process. +*/ +#if defined(GIT_WINDOWS_NATIVE ) +void trace2_collect_process_info (void ); +#else +#define trace2_collect_process_info () \ + do { \ + } while (0) +#endif +#endif /* TRACE2_H */
Signature · 1 line
gitgitgadget