Trace2 tracing facility
Subsystems (3)
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 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 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 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 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 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 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 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 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
Re: [PATCH v4 14/14] trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
Quoted 29 lines · Jeff Hostetler via GitGitGadget
Create unit tests for Trace2.
t/t0212-trace2-event.sh new file +20
-0@@ -0,0 +1,234 @@ +test_expect_success 'event stream, return code 0' ' + 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 = { + | "_SID0_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "001return", + | "0" + | ], + | "child":{
t/t0212-trace2-event.sh line 107
Quoted 10 lines
t/t0212-trace2-event.sh new file +10
-0+ | "0":{ + | "child_argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "001return", + | "0" + | ],
Just curious about the vertically aligned '|'s: are they there to circumvent 'git diff's big red whitespace warnings on these deeply indented lines?
Re: [PATCH v4 14/14] trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
Quoted 36 lines · SZEDER Gábor
Quoted 35 lines · Jeff Hostetler via GitGitGadget
Create unit tests for Trace2.
t/t0212-trace2-event.sh new file +26
-0@@ -0,0 +1,234 @@ +test_expect_success 'event stream, return code 0' ' + 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 = { + | "_SID0_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "001return", + | "0" + | ], + | "child":{ + | "0":{ + | "child_argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool",
Quoted 8 lines
t/t0212-trace2-event.sh line 96
Quoted 4 lines
t/t0212-trace2-event.sh new file +4
-0+ | "trace2", + | "001return", + | "0" + | ],Just curious about the vertically aligned '|'s: are they there to circumvent 'git diff's big red whitespace warnings on these deeply indented lines?
I suspect that the reason is some of the lines have 8-spaces or more after '|', that can be tabified, but when tabified, <<-EOF will strip that second and subsequent tabs.
Re: [PATCH v4 14/14] trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
Quoted 47 lines · Junio C Hamano
Quoted 36 lines · SZEDER Gábor
Quoted 35 lines · Jeff Hostetler via GitGitGadget
Create unit tests for Trace2.
t/t0212-trace2-event.sh new file +26
-0@@ -0,0 +1,234 @@ +test_expect_success 'event stream, return code 0' ' + 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 = { + | "_SID0_":{ + | "argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "004child", + | "test-tool", + | "trace2", + | "001return", + | "0" + | ], + | "child":{ + | "0":{ + | "child_argv":[ + | "_EXE_", + | "trace2", + | "004child", + | "test-tool",Quoted 8 lines
t/t0212-trace2-event.sh line 96
Quoted 4 lines
t/t0212-trace2-event.sh new file +4
-0+ | "trace2", + | "001return", + | "0" + | ],Just curious about the vertically aligned '|'s: are they there to circumvent 'git diff's big red whitespace warnings on these deeply indented lines?
Quoted 4 lines
I suspect that the reason is some of the lines have 8-spaces or more after '|', that can be tabified, but when tabified, <<
-EOFwill strip that second and subsequent tabs.
Yes, the TAB BAR prefixes are to avoid the red whitespace warnings and indicate the exact whitespace in actual output and avoid the effects of the <<-EOF. This technique has been used before in other tests. See t0019 and t1502.
Jeff
Quoted 5 lines · Jeff Hostetler via GitGitGadget
Sorry 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_CFLAGSwhenNO_UNIX_SOCKETSis defined in theMakefile.
OK.
By the way, if somebody can teach gitgitgadget the same Date: header trick that is used by git-send-email, so that the messages sort well, it would be very much appreciated. Here is how I see this thread in my MUA:
. [1366: Jeff Hostetler via GitG] [PATCH v4 01/14] trace2: Documentat...
R. [ 43: SZEDER Gábor ]
. [4459: Jeff Hostetler via GitG] [PATCH v4 02/14] trace2: create new... . [ 69: Jeff Hostetler via GitG] [PATCH v4 04/14] trace2:data: add t... . [ 186: Jeff Hostetler via GitG] [PATCH v4 03/14] trace2: collect pl... . [ 38: Jeff Hostetler via GitG] [PATCH v4 05/14] trace2:data: add e... . [ 25: Jeff Hostetler via GitG] [PATCH v4 06/14] trace2:data: add t... . [ 50: Jeff Hostetler via GitG] [PATCH v4 07/14] trace2:data: add t... . [ 101: Jeff Hostetler via GitG] [PATCH v4 08/14] trace2:data: add t... . [ 114: Jeff Hostetler via GitG] [PATCH v4 09/14] trace2:data: add t... . [ 68: Derrick Stolee via GitG] [PATCH v4 10/14] pack-objects: add ... . [ 42: Jeff Hostetler via GitG] [PATCH v4 11/14] trace2:data: add s... . [ 34: Jeff Hostetler via GitG] [PATCH v4 12/14] trace2:data: add s... . [ 45: Jeff Hostetler via GitG] [PATCH v4 13/14] trace2:data: add s... . [1276: Jeff Hostetler via GitG] [PATCH v4 14/14] trace2: t/helper/t...
and all but 4 and 3 appear in the right order owes mostly because they have Date: header that increases by 1 second interval, but 3 and 4 share the same timestamp.
I can obviously cope with it, but if a tool at the sending end can eliminate the need to cope with it in the first place, that would help not just me but others ;-)
v5 cover Trace2 tracing facility
In reply to Jeff Hostetler via GitGitGadgetV5 addresses: [] renames "verb" and "subverb" to "cmd_name" and "cmd_mode" in code and documentation. [] updates clang-format config to not complain about my for_each macros. [] update formatting around each use of my for_each macros. [] update the platform-specific process info commit to indicate it only handles Windows and leaves other platform for future efforts. [] alters title of pack-objects commit to include "trace2:data:" to match the other data-only commits.
I think this version addresses all of the feedback received do date.
Thanks Jeff
----------------------------------------------------------------------------
Sorry 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):
trace2:data: pack-objects: add trace2 regions
Jeff Hostetler (14):
trace2: Documentation/technical/api-trace2.txt
trace2: create new combined trace facility
trace2: collect Windows-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
trace2: add for_each macros to clang-format
.clang-format | 2 +- 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/trace2_win32_process_info.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 | 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 | 234 ++++ t/t0212/parse_events.perl | 251 ++++ trace2.c | 762 ++++++++++++ trace2.h | 385 +++++++ trace2/tr2_cfg.c | 90 ++ trace2/tr2_cfg.h | 19 + trace2/tr2_cmd_name.c | 30 + trace2/tr2_cmd_name.h | 24 + 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 | 133 +++ trace2/tr2_tgt_event.c | 589 ++++++++++ trace2/tr2_tgt_normal.c | 324 ++++++ trace2/tr2_tgt_perf.c | 535 +++++++++ trace2/tr2_tls.c | 164 +++ trace2/tr2_tls.h | 97 ++ transport-helper.c | 2 + transport.c | 1 + usage.c | 31 + wt-status.c | 24 +- 67 files changed, 6528 insertions(+), 23 deletions(-) create mode 100644 Documentation/technical/api-trace2.txt create mode 100644 compat/win32/trace2_win32_process_info.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_cmd_name.c create mode 100644 trace2/tr2_cmd_name.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
base-commit: b5101f929789889c2e536d915698f58d5c5c6b7a
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-108%2Fjeffhostetler%2Fcore-trace2-2019-v0-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-108/jeffhostetler/core-trace2-2019-v0-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/108
Range-diff vs v4:
1: 60b56d6a8f ! 1: 4c006f4995 trace2: Documentation/technical/api-trace2.txt
@@ -54,7 +54,7 @@
+$ 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.621101 git.c:432 cmd_name 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
+------------
@@ -74,7 +74,7 @@
+$ 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.621111 git.c:432 | d0 | main | cmd_name | | | | | 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
+------------
@@ -93,7 +93,7 @@
+$ 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":"cmd_name","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}
+------------
@@ -213,20 +213,20 @@
+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)`::
++`void trace2_cmd_name(const char *name)`::
+
-+ Emits a "cmd_verb" message with the canonical name of
-+ (usually) builtin command, for example "status" or "checkout".
++ Emits a "cmd_name" message with the canonical name of the
++ command, for example "status" or "checkout".
+
-+`void trace2_cmd_subverb(const char *command_subverb)`::
++`void trace2_cmd_mode(const char *mode)`::
+
-+ Emits a "cmd_subverb" message with a qualifier name to further
++ Emits a "cmd_mode" 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".
++branch or it can checkout a single file, so the checkout code could
++emit a cmd_mode message of "branch" or "file".
+
+`void trace2_cmd_alias(const char *alias, const char **argv_expansion)`::
+
@@ -682,13 +682,13 @@
+}
+------------
+
-+`"cmd_verb"`::
-+ This event contains the primary verb for this git process
-+ and the hierarchy of verbs from parent git processes.
++`"cmd_name"`::
++ This event contains the command name for this git process
++ and the hierarchy of commands from parent git processes.
++
+------------
+{
-+ "event":"cmd_verb",
++ "event":"cmd_name",
+ ...
+ "name":"pack-objects",
+ "hierarchy":"push/pack-objects"
@@ -696,7 +696,7 @@
+------------
++
+Normally, the "name" field contains the canonical name of the
-+command. When a canonical verb is not available, one of
++command. When a canonical name is not available, one of
+these special values are used:
++
+------------
@@ -707,22 +707,22 @@
+"_usage_" # usage error
+------------
+
-+`"cmd_subverb"`::
-+ This event, when present, describes the variant of the main
-+ verb. This event may be emitted more than once.
++`"cmd_mode"`::
++ This event, when present, describes the command variant This
++ event may be emitted more than once.
++
+------------
+{
-+ "event":"cmd_subverb",
++ "event":"cmd_mode",
+ ...
+ "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.
++The "name" field is an arbitrary string to describe the command mode.
++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.
@@ -983,8 +983,8 @@
+----------------
+int cmd_checkout(int argc, const char **argv)
+{
-+ trace2_cmd_verb("checkout");
-+ trace2_cmd_subverb("branch");
++ trace2_cmd_name("checkout");
++ trace2_cmd_mode("branch");
+ trace2_def_repo(the_repository);
+
+ // emit "def_param" messages for "interesting" config settings.
@@ -1031,7 +1031,7 @@
+version 2.20.1.vfs.1.1.47.g534dbe1ad1
+start git fetch origin
+worktree /Users/jeffhost/work/gfw
-+cmd_verb fetch (fetch)
++cmd_name fetch (fetch)
+child_start[0] ssh [email protected] ...
+child_start[1] git index-pack ...
+... (Trace2 events from child processes omitted)
@@ -1049,9 +1049,9 @@
++
+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
++allows the child to print the command 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".
++its name as "gc", it also reports the hierarchy as "fetch/gc".
+(In this example, trace2 messages from the child process is
+indented for clarity.)
++
@@ -1067,13 +1067,13 @@
+version 2.20.1.160.g5676107ecd.dirty
+start git fetch official
+worktree /Users/jeffhost/work/gfw
-+cmd_verb fetch (fetch)
++cmd_name 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)
++ cmd_name 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
@@ -1124,7 +1124,7 @@
+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 | cmd_name | | | | | status (status)
+...
+d0 | main | region_enter | r1 | 0.010988 | | status | label:worktrees
+d0 | main | region_leave | r1 | 0.011236 | 0.000248 | status | label:worktrees
@@ -1169,7 +1169,7 @@
+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 | cmd_name | | | | | status (status)
+...
+d0 | main | region_enter | r1 | 0.015047 | | status | label:untracked
+d0 | main | region_enter | | 0.015132 | | dir | ..label:read_recursive
@@ -1225,7 +1225,7 @@
+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 | cmd_name | | | | | 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
2: 8c9687830e ! 2: 6120ce1bbe trace2: create new combined trace facility
@@ -41,6 +41,7 @@
LIB_OBJS += trace.o
+LIB_OBJS += trace2.o
+LIB_OBJS += trace2/tr2_cfg.o
++LIB_OBJS += trace2/tr2_cmd_name.o
+LIB_OBJS += trace2/tr2_dst.o
+LIB_OBJS += trace2/tr2_sid.o
+LIB_OBJS += trace2/tr2_tbuf.o
@@ -48,7 +49,6 @@
+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
@@ -243,20 +243,20 @@
git_set_exec_path(cmd + 1);
else {
puts(git_exec_path());
-+ trace2_cmd_verb("_query_");
++ trace2_cmd_name("_query_");
exit(0);
}
} else if (!strcmp(cmd, "--html-path")) {
puts(system_path(GIT_HTML_PATH));
-+ trace2_cmd_verb("_query_");
++ trace2_cmd_name("_query_");
exit(0);
} else if (!strcmp(cmd, "--man-path")) {
puts(system_path(GIT_MAN_PATH));
-+ trace2_cmd_verb("_query_");
++ trace2_cmd_name("_query_");
exit(0);
} else if (!strcmp(cmd, "--info-path")) {
puts(system_path(GIT_INFO_PATH));
-+ trace2_cmd_verb("_query_");
++ trace2_cmd_name("_query_");
exit(0);
} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
use_pager = 1;
@@ -264,7 +264,7 @@
(*argv)++;
(*argc)--;
} else if (skip_prefix(cmd, "--list-cmds=", &cmd)) {
-+ trace2_cmd_verb("_query_");
++ trace2_cmd_name("_query_");
if (!strcmp(cmd, "parseopt")) {
struct string_list list = STRING_LIST_INIT_DUP;
int i;
@@ -278,7 +278,7 @@
+ trace2_cmd_alias(alias_command, child.args.argv);
+ trace2_cmd_list_config();
-+ trace2_cmd_verb("_run_shell_alias_");
++ trace2_cmd_name("_run_shell_alias_");
+
ret = run_command(&child);
if (ret >= 0) /* normal exit */
@@ -297,7 +297,7 @@
setup_work_tree();
trace_argv_printf(argv, "trace: built-in: git");
-+ trace2_cmd_verb(p->cmd);
++ trace2_cmd_name(p->cmd);
+ trace2_cmd_list_config();
validate_cache_entries(&the_index);
@@ -308,7 +308,7 @@
cmd.silent_exec_failure = 1;
+ cmd.trace2_child_class = "dashed";
-+ trace2_cmd_verb("_run_dashed_");
++ trace2_cmd_name("_run_dashed_");
+
+ /*
+ * The code in run_command() logs trace2 child_start/child_exit
@@ -346,7 +346,7 @@
+ * command verb to indicate this. Note that the child
+ * process will log the actual verb when it runs.
+ */
-+ trace2_cmd_verb("_run_git_alias_");
++ trace2_cmd_name("_run_git_alias_");
+
+ if (get_super_prefix())
+ die("%s doesn't support --super-prefix", **argv);
@@ -387,7 +387,7 @@
+ * ("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");
++ trace2_cmd_name("remote-curl");
+
remote = remote_get(argv[1]);
@@ -603,7 +603,7 @@
/* Default values for command line options. */
/* unsigned short int show_variables = 0; */
-+ trace2_cmd_verb("sh-i18n--envsubst");
++ trace2_cmd_name("sh-i18n--envsubst");
+
switch (argc)
{
@@ -646,7 +646,7 @@
int i;
int ret = 0;
-+ trace2_cmd_verb("_parse_");
++ trace2_cmd_name("_parse_");
+
argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
@@ -666,7 +666,7 @@
if (!strcmp(cmds[i].name, argv[1])) {
argv++;
argc--;
-+ trace2_cmd_verb(cmds[i].name);
++ trace2_cmd_name(cmds[i].name);
+ trace2_cmd_list_config();
return cmds[i].fn(argc, argv);
}
@@ -698,11 +698,11 @@
+#include "thread-utils.h"
+#include "version.h"
+#include "trace2/tr2_cfg.h"
++#include "trace2/tr2_cmd_name.h"
+#include "trace2/tr2_dst.h"
+#include "trace2/tr2_sid.h"
+#include "trace2/tr2_tgt.h"
+#include "trace2/tr2_tls.h"
-+#include "trace2/tr2_verb.h"
+
+static int trace2_enabled;
+
@@ -715,6 +715,7 @@
+ * enabled or disabled. Each TRACE2 API method will try to write an event to
+ * *each* of the enabled targets.
+ */
++/* clang-format off */
+static struct tr2_tgt *tr2_tgt_builtins[] =
+{
+ &tr2_tgt_normal,
@@ -722,14 +723,20 @@
+ &tr2_tgt_event,
+ NULL
+};
++/* clang-format on */
+
++/* clang-format off */
+#define for_each_builtin(j, tgt_j) \
+ for (j = 0, tgt_j = tr2_tgt_builtins[j]; \
+ tgt_j; \
+ j++, tgt_j = tr2_tgt_builtins[j])
++/* clang-format on */
+
-+#define for_each_wanted_builtin(j, tgt_j) \
-+ for_each_builtin(j, tgt_j) if (tr2_dst_trace_want(tgt_j->pdst))
++/* clang-format off */
++#define for_each_wanted_builtin(j, tgt_j) \
++ for_each_builtin(j, tgt_j) \
++ if (tr2_dst_trace_want(tgt_j->pdst))
++/* clang-format on */
+
+/*
+ * Force (rather than lazily) initialize any of the requested
@@ -745,11 +752,9 @@
+ int j;
+ int sum = 0;
+
-+ for_each_builtin(j, tgt_j)
-+ {
++ for_each_builtin (j, tgt_j)
+ if (tgt_j->pfn_init())
+ sum++;
-+ }
+
+ return sum;
+}
@@ -764,10 +769,8 @@
+ struct tr2_tgt *tgt_j;
+ int j;
+
-+ for_each_builtin(j, tgt_j)
-+ {
++ for_each_builtin (j, tgt_j)
+ tgt_j->pfn_term();
-+ }
+}
+
+static int tr2main_exit_code;
@@ -797,18 +800,16 @@
+ */
+ 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);
-+ }
+
+ tr2_tgt_disable_builtins();
+
+ tr2tls_release();
+ tr2_sid_release();
-+ tr2_verb_release();
++ tr2_cmd_name_release();
+ tr2_cfg_free_patterns();
+
+ trace2_enabled = 0;
@@ -824,11 +825,9 @@
+ 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);
-+ }
+
+ sigchain_pop(signo);
+ raise(signo);
@@ -855,11 +854,9 @@
+ /*
+ * 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);
-+ }
+}
+
+int trace2_is_enabled(void)
@@ -875,11 +872,9 @@
+ 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);
-+ }
+}
+
+int trace2_cmd_exit_fl(const char *file, int line, int code)
@@ -899,12 +894,10 @@
+ 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);
-+ }
+
+ return code;
+}
@@ -922,11 +915,9 @@
+ * 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);
-+ }
+}
+
+void trace2_cmd_path_fl(const char *file, int line, const char *pathname)
@@ -937,14 +928,12 @@
+ 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);
-+ }
+}
+
-+void trace2_cmd_verb_fl(const char *file, int line, const char *command_verb)
++void trace2_cmd_name_fl(const char *file, int line, const char *name)
+{
+ struct tr2_tgt *tgt_j;
+ const char *hierarchy;
@@ -953,19 +942,16 @@
+ if (!trace2_enabled)
+ return;
+
-+ tr2_verb_append_hierarchy(command_verb);
-+ hierarchy = tr2_verb_get_hierarchy();
++ tr2_cmd_name_append_hierarchy(name);
++ hierarchy = tr2_cmd_name_get_hierarchy();
+
-+ for_each_wanted_builtin(j, tgt_j)
-+ {
-+ if (tgt_j->pfn_command_verb_fl)
-+ tgt_j->pfn_command_verb_fl(file, line, command_verb,
++ for_each_wanted_builtin (j, tgt_j)
++ if (tgt_j->pfn_command_name_fl)
++ tgt_j->pfn_command_name_fl(file, line, name,
+ hierarchy);
-+ }
+}
+
-+void trace2_cmd_subverb_fl(const char *file, int line,
-+ const char *command_subverb)
++void trace2_cmd_mode_fl(const char *file, int line, const char *mode)
+{
+ struct tr2_tgt *tgt_j;
+ int j;
@@ -973,12 +959,9 @@
+ if (!trace2_enabled)
+ return;
+
-+ for_each_wanted_builtin(j, tgt_j)
-+ {
-+ if (tgt_j->pfn_command_subverb_fl)
-+ tgt_j->pfn_command_subverb_fl(file, line,
-+ command_subverb);
-+ }
++ for_each_wanted_builtin (j, tgt_j)
++ if (tgt_j->pfn_command_mode_fl)
++ tgt_j->pfn_command_mode_fl(file, line, mode);
+}
+
+void trace2_cmd_alias_fl(const char *file, int line, const char *alias,
@@ -990,11 +973,9 @@
+ 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);
-+ }
+}
+
+void trace2_cmd_list_config_fl(const char *file, int line)
@@ -1031,12 +1012,10 @@
+ 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,
@@ -1059,15 +1038,13 @@
+ 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);
-+ }
+}
+
+int trace2_exec_fl(const char *file, int line, const char *exe,
@@ -1087,12 +1064,10 @@
+
+ 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);
-+ }
+
+ return exec_id;
+}
@@ -1110,12 +1085,10 @@
+ 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);
-+ }
+}
+
+void trace2_thread_start_fl(const char *file, int line, const char *thread_name)
@@ -1149,12 +1122,10 @@
+
+ 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);
-+ }
+}
+
+void trace2_thread_exit_fl(const char *file, int line)
@@ -1195,13 +1166,11 @@
+ 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,
+ us_elapsed_thread);
-+ }
+
+ tr2tls_unset_self();
+}
@@ -1215,11 +1184,9 @@
+ 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)
@@ -1235,11 +1202,9 @@
+
+ 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,
@@ -1265,13 +1230,11 @@
+ * 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);
-+ }
+
+ tr2tls_push_self(us_now);
+}
@@ -1341,14 +1304,12 @@
+ * 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);
-+ }
+}
+
+void trace2_region_leave_fl(const char *file, int line, const char *category,
@@ -1402,13 +1363,11 @@
+ 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);
-+ }
+}
+
+void trace2_data_intmax_fl(const char *file, int line, const char *category,
@@ -1442,13 +1401,11 @@
+ 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);
-+ }
+}
+
+void trace2_printf_va_fl(const char *file, int line, const char *fmt,
@@ -1469,12 +1426,10 @@
+ * 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, ...)
@@ -1581,24 +1536,23 @@
+#define trace2_cmd_path(p) trace2_cmd_path_fl(__FILE__, __LINE__, (p))
+
+/*
-+ * Emit a 'cmd_verb' event with the canonical name of the (usually)
-+ * builtin command. This gives post-processors a simple field
-+ * to identify the command verb without having to parse the argv.
++ * Emit a 'cmd_name' event with the canonical name of the command.
++ * This gives post-processors a simple field to identify the command
++ * without having to parse the argv.
+ */
-+void trace2_cmd_verb_fl(const char *file, int line, const char *command_verb);
++void trace2_cmd_name_fl(const char *file, int line, const char *name);
+
-+#define trace2_cmd_verb(v) trace2_cmd_verb_fl(__FILE__, __LINE__, (v))
++#define trace2_cmd_name(v) trace2_cmd_name_fl(__FILE__, __LINE__, (v))
+
+/*
-+ * Emit a 'cmd_subverb' event to further describe the command being run.
++ * Emit a 'cmd_mode' event to further describe the command being run.
+ * For example, "checkout" can checkout a single file or can checkout a
+ * different branch. This gives post-processors a simple field to compare
+ * equivalent commands without having to parse the argv.
+ */
-+void trace2_cmd_subverb_fl(const char *file, int line,
-+ const char *command_subverb);
++void trace2_cmd_mode_fl(const char *file, int line, const char *mode);
+
-+#define trace2_cmd_subverb(sv) trace2_cmd_subverb_fl(__FILE__, __LINE__, (sv))
++#define trace2_cmd_mode(sv) trace2_cmd_mode_fl(__FILE__, __LINE__, (sv))
+
+/*
+ * Emit an 'alias' expansion event.
@@ -1767,10 +1721,12 @@
+ trace2_region_enter_printf_fl(__FILE__, __LINE__, (category), (label), \
+ (repo), __VA_ARGS__)
+#else
++/* clang-format off */
+__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,
+ ...);
++/* clang-format on */
+#endif
+
+/*
@@ -1805,10 +1761,12 @@
+ trace2_region_leave_printf_fl(__FILE__, __LINE__, (category), (label), \
+ (repo), __VA_ARGS__)
+#else
++/* clang-format off */
+__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,
+ ...);
++/* clang-format on */
+#endif
+
+/*
@@ -1863,8 +1821,10 @@
+#ifdef HAVE_VARIADIC_MACROS
+#define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)
+#else
++/* clang-format off */
+__attribute__((format (printf, 1, 2)))
+void trace2_printf(const char *fmt, ...);
++/* clang-format on */
+#endif
+
+#endif /* TRACE2_H */
@@ -1990,6 +1950,72 @@
+
+#endif /* TR2_CFG_H */
+ diff --git a/trace2/tr2_cmd_name.c b/trace2/tr2_cmd_name.c
+ new file mode 100644
+ --- /dev/null
+ +++ b/trace2/tr2_cmd_name.c
+@@
++#include "cache.h"
++#include "trace2/tr2_cmd_name.h"
++
++#define TR2_ENVVAR_PARENT_NAME "GIT_TR2_PARENT_NAME"
++
++static struct strbuf tr2cmdname_hierarchy = STRBUF_INIT;
++
++void tr2_cmd_name_append_hierarchy(const char *name)
++{
++ const char *parent_name = getenv(TR2_ENVVAR_PARENT_NAME);
++
++ strbuf_reset(&tr2cmdname_hierarchy);
++ if (parent_name && *parent_name) {
++ strbuf_addstr(&tr2cmdname_hierarchy, parent_name);
++ strbuf_addch(&tr2cmdname_hierarchy, '/');
++ }
++ strbuf_addstr(&tr2cmdname_hierarchy, name);
++
++ setenv(TR2_ENVVAR_PARENT_NAME, tr2cmdname_hierarchy.buf, 1);
++}
++
++const char *tr2_cmd_name_get_hierarchy(void)
++{
++ return tr2cmdname_hierarchy.buf;
++}
++
++void tr2_cmd_name_release(void)
++{
++ strbuf_release(&tr2cmdname_hierarchy);
++}
+
+ diff --git a/trace2/tr2_cmd_name.h b/trace2/tr2_cmd_name.h
+ new file mode 100644
+ --- /dev/null
+ +++ b/trace2/tr2_cmd_name.h
+@@
++#ifndef TR2_CMD_NAME_H
++#define TR2_CMD_NAME_H
++
++/*
++ * Append the current command name to the list being maintained
++ * in the environment.
++ *
++ * The hierarchy for a top-level git command is just the current
++ * command name. For a child git process, the hierarchy includes the
++ * names of the parent processes.
++ *
++ * The hierarchy for the current process will be exported to the
++ * environment and inherited by child processes.
++ */
++void tr2_cmd_name_append_hierarchy(const char *name);
++
++/*
++ * Get the command name hierarchy for the current process.
++ */
++const char *tr2_cmd_name_get_hierarchy(void);
++
++void tr2_cmd_name_release(void);
++
++#endif /* TR2_CMD_NAME_H */
+
diff --git a/trace2/tr2_dst.c b/trace2/tr2_dst.c
new file mode 100644
--- /dev/null
@@ -2432,11 +2458,11 @@
+
+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,
++typedef void(tr2_tgt_evt_command_name_fl_t)(const char *file, int line,
++ const char *name,
+ 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_command_mode_fl_t)(const char *file, int line,
++ const char *mode);
+
+typedef void(tr2_tgt_evt_alias_fl_t)(const char *file, int line,
+ const char *alias, const char **argv);
@@ -2499,6 +2525,7 @@
+ * "vtable" for a TRACE2 target. Use NULL if a target does not want
+ * to emit that message.
+ */
++/* clang-format off */
+struct tr2_tgt {
+ struct tr2_dst *pdst;
+
@@ -2512,8 +2539,8 @@
+ tr2_tgt_evt_atexit_t *pfn_atexit;
+ tr2_tgt_evt_error_va_fl_t *pfn_error_va_fl;
+ tr2_tgt_evt_command_path_fl_t *pfn_command_path_fl;
-+ tr2_tgt_evt_command_verb_fl_t *pfn_command_verb_fl;
-+ tr2_tgt_evt_command_subverb_fl_t *pfn_command_subverb_fl;
++ tr2_tgt_evt_command_name_fl_t *pfn_command_name_fl;
++ tr2_tgt_evt_command_mode_fl_t *pfn_command_mode_fl;
+ tr2_tgt_evt_alias_fl_t *pfn_alias_fl;
+ tr2_tgt_evt_child_start_fl_t *pfn_child_start_fl;
+ tr2_tgt_evt_child_exit_fl_t *pfn_child_exit_fl;
@@ -2529,6 +2556,7 @@
+ tr2_tgt_evt_data_json_fl_t *pfn_data_json_fl;
+ tr2_tgt_evt_printf_va_fl_t *pfn_printf_va_fl;
+};
++/* clang-format on */
+
+extern struct tr2_tgt tr2_tgt_event;
+extern struct tr2_tgt tr2_tgt_normal;
@@ -2789,33 +2817,32 @@
+ jw_release(&jw);
+}
+
-+static void fn_command_verb_fl(const char *file, int line,
-+ const char *command_verb,
-+ const char *verb_hierarchy)
++static void fn_command_name_fl(const char *file, int line,
++ const char *name,
++ const char *hierarchy)
+{
-+ const char *event_name = "cmd_verb";
++ const char *event_name = "cmd_name";
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ event_fmt_prepare(event_name, file, line, NULL, &jw);
-+ jw_object_string(&jw, "name", command_verb);
-+ if (verb_hierarchy && *verb_hierarchy)
-+ jw_object_string(&jw, "hierarchy", verb_hierarchy);
++ jw_object_string(&jw, "name", name);
++ if (hierarchy && *hierarchy)
++ jw_object_string(&jw, "hierarchy", hierarchy);
+ jw_end(&jw);
+
+ tr2_dst_write_line(&tr2dst_event, &jw.json);
+ jw_release(&jw);
+}
+
-+static void fn_command_subverb_fl(const char *file, int line,
-+ const char *command_subverb)
++static void fn_command_mode_fl(const char *file, int line, const char *mode)
+{
-+ const char *event_name = "cmd_subverb";
++ const char *event_name = "cmd_mode";
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ event_fmt_prepare(event_name, file, line, NULL, &jw);
-+ jw_object_string(&jw, "name", command_subverb);
++ jw_object_string(&jw, "name", mode);
+ jw_end(&jw);
+
+ tr2_dst_write_line(&tr2dst_event, &jw.json);
@@ -3114,8 +3141,8 @@
+ fn_atexit,
+ fn_error_va_fl,
+ fn_command_path_fl,
-+ fn_command_verb_fl,
-+ fn_command_subverb_fl,
++ fn_command_name_fl,
++ fn_command_mode_fl,
+ fn_alias_fl,
+ fn_child_start_fl,
+ fn_child_exit_fl,
@@ -3300,25 +3327,24 @@
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_command_verb_fl(const char *file, int line,
-+ const char *command_verb,
-+ const char *verb_hierarchy)
++static void fn_command_name_fl(const char *file, int line,
++ const char *name,
++ const char *hierarchy)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+
-+ strbuf_addf(&buf_payload, "cmd_verb %s", command_verb);
-+ if (verb_hierarchy && *verb_hierarchy)
-+ strbuf_addf(&buf_payload, " (%s)", verb_hierarchy);
++ strbuf_addf(&buf_payload, "cmd_name %s", name);
++ if (hierarchy && *hierarchy)
++ strbuf_addf(&buf_payload, " (%s)", hierarchy);
+ normal_io_write_fl(file, line, &buf_payload);
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_command_subverb_fl(const char *file, int line,
-+ const char *command_subverb)
++static void fn_command_mode_fl(const char *file, int line, const char *mode)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
+
-+ strbuf_addf(&buf_payload, "cmd_subverb %s", command_subverb);
++ strbuf_addf(&buf_payload, "cmd_mode %s", mode);
+ normal_io_write_fl(file, line, &buf_payload);
+ strbuf_release(&buf_payload);
+}
@@ -3445,8 +3471,8 @@
+ fn_atexit,
+ fn_error_va_fl,
+ fn_command_path_fl,
-+ fn_command_verb_fl,
-+ fn_command_subverb_fl,
++ fn_command_name_fl,
++ fn_command_mode_fl,
+ fn_alias_fl,
+ fn_child_start_fl,
+ fn_child_exit_fl,
@@ -3721,29 +3747,28 @@
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_command_verb_fl(const char *file, int line,
-+ const char *command_verb,
-+ const char *verb_hierarchy)
++static void fn_command_name_fl(const char *file, int line,
++ const char *name,
++ const char *hierarchy)
+{
-+ const char *event_name = "cmd_verb";
++ const char *event_name = "cmd_name";
+ struct strbuf buf_payload = STRBUF_INIT;
+
-+ strbuf_addstr(&buf_payload, command_verb);
-+ if (verb_hierarchy && *verb_hierarchy)
-+ strbuf_addf(&buf_payload, " (%s)", verb_hierarchy);
++ strbuf_addstr(&buf_payload, name);
++ if (hierarchy && *hierarchy)
++ strbuf_addf(&buf_payload, " (%s)", hierarchy);
+
+ 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)
++static void fn_command_mode_fl(const char *file, int line, const char *mode)
+{
-+ const char *event_name = "cmd_subverb";
++ const char *event_name = "cmd_mode";
+ struct strbuf buf_payload = STRBUF_INIT;
+
-+ strbuf_addstr(&buf_payload, command_subverb);
++ strbuf_addstr(&buf_payload, mode);
+
+ perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL,
+ &buf_payload);
@@ -3987,8 +4012,8 @@
+ fn_atexit,
+ fn_error_va_fl,
+ fn_command_path_fl,
-+ fn_command_verb_fl,
-+ fn_command_subverb_fl,
++ fn_command_name_fl,
++ fn_command_mode_fl,
+ fn_alias_fl,
+ fn_child_start_fl,
+ fn_child_exit_fl,
@@ -4278,72 +4303,6 @@
+
+#endif /* TR2_TLS_H */
- diff --git a/trace2/tr2_verb.c b/trace2/tr2_verb.c
- new file mode 100644
- --- /dev/null
- +++ b/trace2/tr2_verb.c
-@@
-+#include "cache.h"
-+#include "trace2/tr2_verb.h"
-+
-+#define TR2_ENVVAR_PARENT_VERB "GIT_TR2_PARENT_VERB"
-+
-+static struct strbuf tr2verb_hierarchy = STRBUF_INIT;
-+
-+void tr2_verb_append_hierarchy(const char *verb)
-+{
-+ const char *parent_verb = getenv(TR2_ENVVAR_PARENT_VERB);
-+
-+ strbuf_reset(&tr2verb_hierarchy);
-+ if (parent_verb && *parent_verb) {
-+ strbuf_addstr(&tr2verb_hierarchy, parent_verb);
-+ strbuf_addch(&tr2verb_hierarchy, '/');
-+ }
-+ strbuf_addstr(&tr2verb_hierarchy, verb);
-+
-+ setenv(TR2_ENVVAR_PARENT_VERB, tr2verb_hierarchy.buf, 1);
-+}
-+
-+const char *tr2_verb_get_hierarchy(void)
-+{
-+ return tr2verb_hierarchy.buf;
-+}
-+
-+void tr2_verb_release(void)
-+{
-+ strbuf_release(&tr2verb_hierarchy);
-+}
-
- diff --git a/trace2/tr2_verb.h b/trace2/tr2_verb.h
- new file mode 100644
- --- /dev/null
- +++ b/trace2/tr2_verb.h
-@@
-+#ifndef TR2_VERB_H
-+#define TR2_VERB_H
-+
-+/*
-+ * Append the current git command's "verb" to the list being maintained
-+ * in the environment.
-+ *
-+ * The hierarchy for a top-level git command is just the current verb.
-+ * For a child git process, the hierarchy lists the verbs of the parent
-+ * git processes (much like the SID).
-+ *
-+ * The hierarchy for the current process will be exported to the environment
-+ * and inherited by child processes.
-+ */
-+void tr2_verb_append_hierarchy(const char *verb);
-+
-+/*
-+ * Get the verb hierarchy for the current process.
-+ */
-+const char *tr2_verb_get_hierarchy(void);
-+
-+void tr2_verb_release(void);
-+
-+#endif /* TR2_VERB_H */
-
diff --git a/usage.c b/usage.c
--- a/usage.c
+++ b/usage.c
@@ -4357,7 +4316,7 @@
+ * cmd_main(), we don't know what verb to report. Force it to this
+ * to facilitate post-processing.
+ */
-+ trace2_cmd_verb("_usage_");
++ trace2_cmd_name("_usage_");
+
+ /*
+ * Currently, the (err, params) are usually just the static usage
3: 6167aba76c ! 3: 7c987cde86 trace2: collect platform-specific process information
@@ -1,16 +1,14 @@
Author: Jeff Hostetler <[email protected]>
- trace2: collect platform-specific process information
+ trace2: collect Windows-specific process information
- Add optional platform-specific code to log information about
- the current process.
+ Add platform-specific interface 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.
+ On Windows, this interface is used to indicate whether the git process
+ is running under a debugger and list names of the process ancestors.
- The purpose of this information is to help indicate if the
- process was launched interactively or in the background by
- an IDE, for example.
+ Information for other platforms is left for a future effort.
Signed-off-by: Jeff Hostetler <[email protected]>
@@ -26,10 +24,10 @@
git_resolve_executable_dir(argv[0]);
- diff --git a/compat/win32/ancestry.c b/compat/win32/ancestry.c
+ diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
new file mode 100644
--- /dev/null
- +++ b/compat/win32/ancestry.c
+ +++ b/compat/win32/trace2_win32_process_info.c
@@
+#include "../../cache.h"
+#include "../../json-writer.h"
@@ -140,7 +138,7 @@
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/trace2_win32_process_info.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
@@ -148,7 +146,7 @@
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/trace2_win32_process_info.o \
compat/win32/path-utils.o \
compat/win32/pthread.o compat/win32/syslog.o \
compat/win32/dirent.o
@@ -157,7 +155,7 @@
--- a/trace2.h
+++ b/trace2.h
@@
- void trace2_printf(const char *fmt, ...);
+ /* clang-format on */
#endif
+/*
4: 3624c6cbd9 = 4: cd860799f8 trace2:data: add trace2 regions to wt-status
5: 638fdb294f = 5: 30bcea9435 trace2:data: add editor/pager child classification
6: f4f0453e6b = 6: c869de8063 trace2:data: add trace2 sub-process classification
7: 660b83c453 = 7: 3cd525b80e trace2:data: add trace2 transport child classification
8: ab26888126 = 8: c2c1ea5ce3 trace2:data: add trace2 hook classification
9: 87a73af338 = 9: 57b23542b3 trace2:data: add trace2 instrumentation to index read/write
10: be707ea960 ! 10: ab74ef5f23 pack-objects: add trace2 regions @@ -1,6 +1,6 @@
Author: Derrick Stolee <[email protected]>
- pack-objects: add trace2 regions
+ trace2:data: pack-objects: add trace2 regions
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
11: b4c8ff53dd ! 11: 6a82426a83 trace2:data: add subverb to checkout command
@@ -11,7 +11,7 @@
struct lock_file lock_file = LOCK_INIT;
int nr_checkouts = 0;
-+ trace2_cmd_subverb(opts->patch_mode ? "patch" : "path");
++ trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
+
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
die(_("'%s' cannot be used with updating paths"), "--track");
@@ -21,7 +21,7 @@
struct object_id rev;
int flag, writeout_error = 0;
+
-+ trace2_cmd_subverb("branch");
++ trace2_cmd_mode("branch");
+
memset(&old_branch_info, 0, sizeof(old_branch_info));
old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
@@ -30,7 +30,7 @@
int status;
struct strbuf branch_ref = STRBUF_INIT;
-+ trace2_cmd_subverb("unborn");
++ trace2_cmd_mode("unborn");
+
if (!opts->new_branch)
die(_("You are on a branch yet to be born"));
12: ed1d612006 ! 12: 5c7f0de228 trace2:data: add subverb to reset command
@@ -11,7 +11,7 @@
if (patch_mode) {
if (reset_type != NONE)
die(_("--patch is incompatible with --{hard,mixed,soft}"));
-+ trace2_cmd_subverb("patch-interactive");
++ trace2_cmd_mode("patch-interactive");
return run_add_interactive(rev, "--patch=reset", &pathspec);
}
@@ -20,9 +20,9 @@
reset_type = MIXED; /* by default */
+ if (pathspec.nr)
-+ trace2_cmd_subverb("path");
++ trace2_cmd_mode("path");
+ else
-+ trace2_cmd_subverb(reset_type_names[reset_type]);
++ trace2_cmd_mode(reset_type_names[reset_type]);
+
if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree()))
setup_work_tree();
13: 4f55f33624 ! 13: cc61201061 trace2:data: add subverb for rebase
@@ -28,11 +28,11 @@
+ if (trace2_is_enabled()) {
+ if (is_interactive(&options))
-+ trace2_cmd_subverb("interactive");
++ trace2_cmd_mode("interactive");
+ else if (exec.nr)
-+ trace2_cmd_subverb("interactive-exec");
++ trace2_cmd_mode("interactive-exec");
+ else
-+ trace2_cmd_subverb(action_names[action]);
++ trace2_cmd_mode(action_names[action]);
+ }
+
switch (action) {
14: 8d47cd71ed ! 14: 00b25da38b trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
@@ -89,7 +89,7 @@
+ * [] the process-exit value.
+ * [] 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.
++ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] "def_param" events for all of the "interesting" pre-defined
+ * config settings.
+ */
@@ -109,7 +109,7 @@
+ * Test harness can confirm:
+ * [] 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.
++ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] "def_param" events for all of the "interesting" pre-defined
+ * config settings.
+ */
@@ -130,7 +130,7 @@
+ *
+ * Test harness can confirm:
+ * [] a trace2 "error" event for each value in argv.
-+ * [] the "name" field in the "cmd_verb" trace2 event.
++ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] (optional) the file:line in the "exit" event refers to this function.
+ */
+static int ut_003error(int argc, const char **argv)
@@ -157,7 +157,7 @@
+ * test-tool trace2 004child git -c alias.xyz=version xyz
+ *
+ * Test harness can confirm:
-+ * [] the "name" field in the "cmd_verb" trace2 event.
++ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] that the outer process has a single component SID (or depth "d0" in
+ * the PERF stream).
+ * [] that "child_start" and "child_exit" events are generated for the child.
@@ -203,7 +203,7 @@
+ * test-tool trace2 005exec version
+ *
+ * Test harness can confirm (on Windows):
-+ * [] the "name" field in the "cmd_verb" trace2 event.
++ * [] the "name" field in the "cmd_name" trace2 event.
+ * [] that the outer process has a single component SID (or depth "d0" in
+ * the PERF stream).
+ * [] that "exec" and "exec_result" events are generated for the child
@@ -255,6 +255,7 @@
+ */
+#define USAGE_PREFIX "test-tool trace2"
+
++/* clang-format off */
+static struct unit_test ut_table[] = {
+ { ut_001return, "001return", "<exit_code>" },
+ { ut_002exit, "002exit", "<exit_code>" },
@@ -263,11 +264,14 @@
+ { ut_005exec, "005exec", "<git_command_args>" },
+ { ut_006data, "006data", "[<category> <key> <value>]+" },
+};
++/* clang-format on */
+
++/* clang-format off */
+#define for_each_ut(k, ut_k) \
+ for (k = 0, ut_k = &ut_table[k]; \
+ k < ARRAY_SIZE(ut_table); \
+ k++, ut_k = &ut_table[k])
++/* clang-format on */
+
+static int print_usage(void)
+{
@@ -275,11 +279,9 @@
+ struct unit_test *ut_k;
+
+ fprintf(stderr, "usage:\n");
-+ for_each_ut(k, ut_k)
-+ {
++ 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;
+}
@@ -290,14 +292,14 @@
+ * We assume that these trace2 routines has already been called:
+ * [] trace2_initialize() [common-main.c:main()]
+ * [] trace2_cmd_start() [common-main.c:main()]
-+ * [] trace2_cmd_verb() [test-tool.c:cmd_main()]
++ * [] trace2_cmd_name() [test-tool.c:cmd_main()]
+ * [] tracd2_cmd_list_config() [test-tool.c:cmd_main()]
+ * So that:
+ * [] the various trace2 streams are open.
+ * [] the process SID has been created.
+ * [] the "version" event has been generated.
+ * [] the "start" event has been generated.
-+ * [] the "verb" event has been generated.
++ * [] the "cmd_name" event has been generated.
+ * [] this writes various "def_param" events for interesting config values.
+ *
+ * We further assume that if we return (rather than exit()), trace2_cmd_exit()
@@ -311,13 +313,10 @@
+ argc--; /* skip over "trace2" arg */
+ argv++;
+
-+ if (argc) {
-+ for_each_ut(k, ut_k)
-+ {
++ if (argc)
++ for_each_ut (k, ut_k)
+ if (!strcmp(argv[0], ut_k->ut_name))
+ return ut_k->ut_fn(argc - 1, argv + 1);
-+ }
-+ }
+
+ return print_usage();
+}
@@ -370,7 +369,7 @@
+# We do confirm the following API features:
+# [] the 'version <v>' event
+# [] the 'start <argv>' event
-+# [] the 'cmd_verb <verb>' event
++# [] the 'cmd_name <name>' event
+# [] the 'exit <time> code:<code>' event
+# [] the 'atexit <time> code:<code>' event
+#
@@ -388,7 +387,7 @@
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 001return 0
-+ cmd_verb trace2 (trace2)
++ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:0
+ atexit elapsed:_TIME_ code:0
+ EOF
@@ -402,7 +401,7 @@
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 001return 1
-+ cmd_verb trace2 (trace2)
++ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:1
+ atexit elapsed:_TIME_ code:1
+ EOF
@@ -420,7 +419,7 @@
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 002exit 0
-+ cmd_verb trace2 (trace2)
++ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:0
+ atexit elapsed:_TIME_ code:0
+ EOF
@@ -434,7 +433,7 @@
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 002exit 1
-+ cmd_verb trace2 (trace2)
++ cmd_name trace2 (trace2)
+ exit elapsed:_TIME_ code:1
+ atexit elapsed:_TIME_ code:1
+ EOF
@@ -452,7 +451,7 @@
+ cat >expect <<-EOF &&
+ version $V
+ start _EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
-+ cmd_verb trace2 (trace2)
++ cmd_name trace2 (trace2)
+ error hello world
+ error this is a test
+ exit elapsed:_TIME_ code:0
@@ -575,7 +574,7 @@
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start|||||_EXE_ trace2 001return 0
-+ d0|main|cmd_verb|||||trace2 (trace2)
++ d0|main|cmd_name|||||trace2 (trace2)
+ d0|main|exit||_T_ABS_|||code:0
+ d0|main|atexit||_T_ABS_|||code:0
+ EOF
@@ -589,7 +588,7 @@
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start|||||_EXE_ trace2 001return 1
-+ d0|main|cmd_verb|||||trace2 (trace2)
++ d0|main|cmd_name|||||trace2 (trace2)
+ d0|main|exit||_T_ABS_|||code:1
+ d0|main|atexit||_T_ABS_|||code:1
+ EOF
@@ -607,7 +606,7 @@
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start|||||_EXE_ trace2 003error '\''hello world'\'' '\''this is a test'\''
-+ d0|main|cmd_verb|||||trace2 (trace2)
++ d0|main|cmd_name|||||trace2 (trace2)
+ d0|main|error|||||hello world
+ d0|main|error|||||this is a test
+ d0|main|exit||_T_ABS_|||code:0
@@ -628,15 +627,15 @@
+# Which should generate events:
+# P1: version
+# P1: start
-+# P1: cmd_verb
++# P1: cmd_name
+# P1: child_start
+# P2: version
+# P2: start
-+# P2: cmd_verb
++# P2: cmd_name
+# P2: child_start
+# P3: version
+# P3: start
-+# P3: cmd_verb
++# P3: cmd_name
+# P3: exit
+# P3: atexit
+# P2: child_exit
@@ -653,15 +652,15 @@
+ cat >expect <<-EOF &&
+ d0|main|version|||||$V
+ d0|main|start|||||_EXE_ trace2 004child test-tool trace2 004child test-tool trace2 001return 0
-+ d0|main|cmd_verb|||||trace2 (trace2)
++ d0|main|cmd_name|||||trace2 (trace2)
+ 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 test-tool trace2 001return 0
-+ d1|main|cmd_verb|||||trace2 (trace2/trace2)
++ d1|main|cmd_name|||||trace2 (trace2/trace2)
+ 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)
++ d2|main|cmd_name|||||trace2 (trace2/trace2/trace2)
+ d2|main|exit||_T_ABS_|||code:0
+ d2|main|atexit||_T_ABS_|||code:0
+ d1|main|child_exit||_T_ABS_|_T_REL_||[ch0] pid:_PID_ code:0
@@ -822,8 +821,8 @@
+ | "%s"
+ | ],
+ | "exit_code":0,
-+ | "verb":"trace2",
-+ | "verb_hierarchy":"trace2",
++ | "hierarchy":"trace2",
++ | "name":"trace2",
+ | "version":"$V"
+ | }
+ |};
@@ -876,8 +875,8 @@
+ | }
+ | },
+ | "exit_code":0,
-+ | "verb":"trace2",
-+ | "verb_hierarchy":"trace2",
++ | "hierarchy":"trace2",
++ | "name":"trace2",
+ | "version":"$V"
+ | },
+ | "_SID0_/_SID1_":{
@@ -904,8 +903,8 @@
+ | }
+ | },
+ | "exit_code":0,
-+ | "verb":"trace2",
-+ | "verb_hierarchy":"trace2/trace2",
++ | "hierarchy":"trace2/trace2",
++ | "name":"trace2",
+ | "version":"$V"
+ | },
+ | "_SID0_/_SID1_/_SID2_":{
@@ -916,8 +915,8 @@
+ | "0"
+ | ],
+ | "exit_code":0,
-+ | "verb":"trace2",
-+ | "verb_hierarchy":"trace2/trace2/trace2",
++ | "hierarchy":"trace2/trace2/trace2",
++ | "name":"trace2",
+ | "version":"$V"
+ | }
+ |};
@@ -943,6 +942,8 @@
+ | "0"
+ | ],
+ | "exit_code":0,
++ | "hierarchy":"trace2",
++ | "name":"trace2",
+ | "params":[
+ | {
+ | "param":"t0212.abc",
@@ -953,8 +954,6 @@
+ | "value":"hello world"
+ | }
+ | ],
-+ | "verb":"trace2",
-+ | "verb_hierarchy":"trace2",
+ | "version":"$V"
+ | }
+ |};
@@ -987,8 +986,8 @@
+ | }
+ | },
+ | "exit_code":0,
-+ | "verb":"trace2",
-+ | "verb_hierarchy":"trace2",
++ | "hierarchy":"trace2",
++ | "name":"trace2",
+ | "version":"$V"
+ | }
+ |};
@@ -1138,9 +1137,9 @@
+ # $processes->{$sid}->{'path'} = "_EXE_";
+ }
+
-+ elsif ($event eq 'cmd_verb') {
-+ $processes->{$sid}->{'verb'} = $line->{'name'};
-+ $processes->{$sid}->{'verb_hierarchy'} = $line->{'hierarchy'};
++ elsif ($event eq 'cmd_name') {
++ $processes->{$sid}->{'name'} = $line->{'name'};
++ $processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'};
+ }
+
+ elsif ($event eq 'alias') { -: ---------- > 15: 5f77c9b633 trace2: add for_each macros to clang-format
Signature · 1 line
gitgitgadget
PATCH v5 1/15 trace2: Documentation/technical/api-trace2.txt
From: 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_name 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_name | | | | | 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_name","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_name(const char *name)`:: + + Emits a "cmd_name" message with the canonical name of the + command, for example "status" or "checkout". + +`void trace2_cmd_mode(const char *mode)`:: + + Emits a "cmd_mode" 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 it can checkout a single file, so the checkout code could +emit a cmd_mode 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_name"`:: + This event contains the command name for this git process + and the hierarchy of commands from parent git processes. ++ +------------ +{ + "event":"cmd_name", + ... + "name":"pack-objects", + "hierarchy":"push/pack-objects" +} +------------ ++ +Normally, the "name" field contains the canonical name of the +command. When a canonical name 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_mode"`:: + This event, when present, describes the command variant This + event may be emitted more than once. ++ +------------ +{ + "event":"cmd_mode", + ... + "name":"branch" +} +------------ ++ +The "name" field is an arbitrary string to describe the command mode. +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_name("checkout"); + trace2_cmd_mode("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_name 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 the command hierarchy. This example +shows gc as child[3] of fetch. When the gc process reports +its name 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_name 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_name 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_name | | | | | 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_name | | | | | 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_name | | | | | 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 v5 2/15 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 | 762 ++++++++++++++++++++++++++++++++++ trace2.h | 371 +++++++++++++++++ trace2/tr2_cfg.c | 90 ++++ trace2/tr2_cfg.h | 19 + trace2/tr2_cmd_name.c | 30 ++ trace2/tr2_cmd_name.h | 24 ++ 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 | 133 ++++++ trace2/tr2_tgt_event.c | 589 ++++++++++++++++++++++++++ trace2/tr2_tgt_normal.c | 324 +++++++++++++++ trace2/tr2_tgt_perf.c | 535 ++++++++++++++++++++++++ trace2/tr2_tls.c | 164 ++++++++ trace2/tr2_tls.h | 97 +++++ usage.c | 31 ++ 39 files changed, 3755 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_cmd_name.c create mode 100644 trace2/tr2_cmd_name.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
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_cmd_name.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 += 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_name ("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_name ("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_name ("_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_name (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 +762 -0
762 lines not on this page · raw message
trace2.h new file +371 -0
371 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_cmd_name.c new file +30 -0
@@ -0,0 +1,30 @@ +#include "cache.h" +#include "trace2/tr2_cmd_name.h" + +#define TR2_ENVVAR_PARENT_NAME "GIT_TR2_PARENT_NAME" + +static struct strbuf tr2cmdname_hierarchy =STRBUF_INIT ; + +void tr2_cmd_name_append_hierarchy (const char *name) +{ +const char *parent_name =getenv (TR2_ENVVAR_PARENT_NAME ); + +strbuf_reset (&tr2cmdname_hierarchy); +if (parent_name && *parent_name) { +strbuf_addstr (&tr2cmdname_hierarchy, parent_name); +strbuf_addch (&tr2cmdname_hierarchy,'/' ); + } +strbuf_addstr (&tr2cmdname_hierarchy, name); + +setenv (TR2_ENVVAR_PARENT_NAME , tr2cmdname_hierarchy.buf,1 ); +} + +const char *tr2_cmd_name_get_hierarchy (void ) +{ +return tr2cmdname_hierarchy.buf; +} + +void tr2_cmd_name_release (void ) +{ +strbuf_release (&tr2cmdname_hierarchy); +}
trace2/tr2_cmd_name.h new file +24 -0
@@ -0,0 +1,24 @@ +#ifndef TR2_CMD_NAME_H +#define TR2_CMD_NAME_H + +/* +* Append the current command name to the list being maintained +* in the environment. +* +* The hierarchy for a top-level git command is just the current +* command name. For a child git process, the hierarchy includes the +* names of the parent processes. +* +* The hierarchy for the current process will be exported to the +* environment and inherited by child processes. +*/ +void tr2_cmd_name_append_hierarchy (const char *name); + +/* +* Get the command name hierarchy for the current process. +*/ +const char *tr2_cmd_name_get_hierarchy (void ); + +void tr2_cmd_name_release (void ); + +#endif /* TR2_CMD_NAME_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 +133 -0
133 lines not on this page · raw message
trace2/tr2_tgt_event.c new file +589 -0
589 lines not on this page · raw message
trace2/tr2_tgt_normal.c new file +324 -0
79 lines not on this page · raw message
trace2/tr2_tgt_perf.c new file +535 -0
535 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 v5 3/15 trace2: collect Windows-specific process information
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add platform-specific interface to log information about the current process.
On Windows, this interface is used to indicate whether the git process is running under a debugger and list names of the process ancestors.
Information for other platforms is left for a future effort.
--- common-main.c | 1 + compat/win32/trace2_win32_process_info.c | 101 +++++++++++++++++++++++ config.mak.uname | 2 + trace2.h | 14 ++++ 4 files changed, 118 insertions(+) create mode 100644 compat/win32/trace2_win32_process_info.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/trace2_win32_process_info.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/trace2_win32_process_info.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/trace2_win32_process_info.o \ compat/win32/path-utils.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o
trace2.h +14 -0
@@ -368,4 +368,18 @@ void trace2_printf(const char *fmt, ...);/* clang-format on */ #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 v5 4/15 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 v5 5/15 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 v5 6/15 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 v5 7/15 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 v5 8/15 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 v5 9/15 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 v5 10/15 trace2:data: 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 v5 11/15 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_mode (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_mode ("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_mode ("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 v5 12/15 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_mode ("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_mode ("path" ); +else +trace2_cmd_mode (reset_type_names[reset_type]); +if (reset_type !=SOFT && (reset_type !=MIXED ||get_git_work_tree ()))setup_work_tree ();
Signature · 1 line
gitgitgadget
PATCH v5 13/15 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_mode ("interactive" ); +else if (exec.nr) +trace2_cmd_mode ("interactive-exec" ); +else +trace2_cmd_mode (action_names[action]); + } +switch (action) {case ACTION_CONTINUE : {struct object_id head;
Signature · 1 line
gitgitgadget
PATCH v5 14/15 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 | 234 ++++++++++++++++++++++++++++++++ t/t0212/parse_events.perl | 251 +++++++++++++++++++++++++++++++++++ 10 files changed, 1173 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 +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 +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
Re: [PATCH v5 14/15] trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
t/t0212-trace2-event.sh line 44
Quoted 4 lines · Jeff Hostetler via GitGitGadget
t/t0212-trace2-event.sh new file +4
-0+test_expect_success 'event stream, error event' ' + test_when_finished "rm trace.event actual expect" && + 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 &&
These & similar tests on some systems ...
t/t0212/parse_events.perl line 25
Quoted 13 lines
[...]
t/t0212/parse_events.perl new file +5
-0@@ -0,0 +1,251 @@ +use strict; +use warnings; +use JSON::PP; +use Data::Dumper; +use Getopt::Long;
...because JSON::PP is not a core perl module. See
e.g. t/t9501-gitweb-standalone-http-status.sh for how we deal with this, i.e.:
perl -MJSON::PP -e 0 >/dev/null 2>&1 && test_set_prereq JSON_PP
Then later e.g.:
test_expect_success JSON_PP 'event stream, error event' '[...]
PATCH v5 15/15 trace2: add for_each macros to clang-format
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
.clang-format +1 -1
@@ -149,7 +149,7 @@ Cpp11BracedListStyle: false # A list of macros that should be interpreted as foreach loops instead of as # function calls. -ForEachMacros: ['for_each_string_list_item'] +ForEachMacros: ['for_each_string_list_item', 'for_each_wanted_builtin', 'for_each_builtin', 'for_each_ut'] # The maximum number of consecutive empty lines to keep. MaxEmptyLinesToKeep: 1
Signature · 1 line
gitgitgadget
v6 cover Trace2 tracing facility
In reply to Jeff Hostetler via GitGitGadgetV6 addresses: [] The remaining hdr-check warning in trace2/tr2_tls.h
There are no other outstanding comments that I'm aware of.
Thanks Jeff
----------------------------------------------------------------------------
V5 addresses: [] renames "verb" and "subverb" to "cmd_name" and "cmd_mode" in code and documentation. [] updates clang-format config to not complain about my for_each macros. [] update formatting around each use of my for_each macros. [] update the platform-specific process info commit to indicate it only handles Windows and leaves other platform for future efforts. [] alters title of pack-objects commit to include "trace2:data:" to match the other data-only commits.
I think this version addresses all of the feedback received do date.
Thanks Jeff
----------------------------------------------------------------------------
Sorry 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):
trace2:data: pack-objects: add trace2 regions
Jeff Hostetler (14):
trace2: Documentation/technical/api-trace2.txt
trace2: create new combined trace facility
trace2: collect Windows-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
trace2: add for_each macros to clang-format
.clang-format | 2 +- 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/trace2_win32_process_info.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 | 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 | 234 ++++ t/t0212/parse_events.perl | 251 ++++ trace2.c | 762 ++++++++++++ trace2.h | 385 +++++++ trace2/tr2_cfg.c | 90 ++ trace2/tr2_cfg.h | 19 + trace2/tr2_cmd_name.c | 30 + trace2/tr2_cmd_name.h | 24 + 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 | 133 +++ trace2/tr2_tgt_event.c | 589 ++++++++++ trace2/tr2_tgt_normal.c | 324 ++++++ trace2/tr2_tgt_perf.c | 535 +++++++++ trace2/tr2_tls.c | 164 +++ trace2/tr2_tls.h | 97 ++ transport-helper.c | 2 + transport.c | 1 + usage.c | 31 + wt-status.c | 24 +- 67 files changed, 6528 insertions(+), 23 deletions(-) create mode 100644 Documentation/technical/api-trace2.txt create mode 100644 compat/win32/trace2_win32_process_info.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_cmd_name.c create mode 100644 trace2/tr2_cmd_name.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
base-commit: b5101f929789889c2e536d915698f58d5c5c6b7a
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-108%2Fjeffhostetler%2Fcore-trace2-2019-v0-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-108/jeffhostetler/core-trace2-2019-v0-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/108
Range-diff vs v5:
1: 4c006f4995 = 1: 4c006f4995 trace2: Documentation/technical/api-trace2.txt
2: 6120ce1bbe ! 2: 6bad326bbd trace2: create new combined trace facility
@@ -4208,7 +4208,7 @@
+#ifndef TR2_TLS_H
+#define TR2_TLS_H
+
-+struct strbuf;
++#include "strbuf.h"
+
+/*
+ * Arbitry limit for thread names for column alignment.
3: 7c987cde86 = 3: 12de7e42de trace2: collect Windows-specific process information
4: cd860799f8 = 4: 5835edbd01 trace2:data: add trace2 regions to wt-status
5: 30bcea9435 = 5: 99d13ef478 trace2:data: add editor/pager child classification
6: c869de8063 = 6: d7ce85b702 trace2:data: add trace2 sub-process classification
7: 3cd525b80e = 7: b70c289903 trace2:data: add trace2 transport child classification
8: c2c1ea5ce3 = 8: 2a0da88579 trace2:data: add trace2 hook classification
9: 57b23542b3 = 9: bca6a7f0d6 trace2:data: add trace2 instrumentation to index read/write
10: ab74ef5f23 = 10: 68269ee060 trace2:data: pack-objects: add trace2 regions
11: 6a82426a83 = 11: 0191283fff trace2:data: add subverb to checkout command
12: 5c7f0de228 = 12: 24d8d8d768 trace2:data: add subverb to reset command
13: cc61201061 = 13: 90cec071cf trace2:data: add subverb for rebase
14: 00b25da38b = 14: b9f0c6fd66 trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
15: 5f77c9b633 = 15: 93a25d09a1 trace2: add for_each macros to clang-formatSignature · 1 line
gitgitgadget
PATCH v6 1/15 trace2: Documentation/technical/api-trace2.txt
From: 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_name 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_name | | | | | 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_name","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_name(const char *name)`:: + + Emits a "cmd_name" message with the canonical name of the + command, for example "status" or "checkout". + +`void trace2_cmd_mode(const char *mode)`:: + + Emits a "cmd_mode" 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 it can checkout a single file, so the checkout code could +emit a cmd_mode 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_name"`:: + This event contains the command name for this git process + and the hierarchy of commands from parent git processes. ++ +------------ +{ + "event":"cmd_name", + ... + "name":"pack-objects", + "hierarchy":"push/pack-objects" +} +------------ ++ +Normally, the "name" field contains the canonical name of the +command. When a canonical name 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_mode"`:: + This event, when present, describes the command variant This + event may be emitted more than once. ++ +------------ +{ + "event":"cmd_mode", + ... + "name":"branch" +} +------------ ++ +The "name" field is an arbitrary string to describe the command mode. +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_name("checkout"); + trace2_cmd_mode("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_name 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 the command hierarchy. This example +shows gc as child[3] of fetch. When the gc process reports +its name 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_name 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_name 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_name | | | | | 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_name | | | | | 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_name | | | | | 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 v6 2/15 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 | 762 ++++++++++++++++++++++++++++++++++ trace2.h | 371 +++++++++++++++++ trace2/tr2_cfg.c | 90 ++++ trace2/tr2_cfg.h | 19 + trace2/tr2_cmd_name.c | 30 ++ trace2/tr2_cmd_name.h | 24 ++ 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 | 133 ++++++ trace2/tr2_tgt_event.c | 589 ++++++++++++++++++++++++++ trace2/tr2_tgt_normal.c | 324 +++++++++++++++ trace2/tr2_tgt_perf.c | 535 ++++++++++++++++++++++++ trace2/tr2_tls.c | 164 ++++++++ trace2/tr2_tls.h | 97 +++++ usage.c | 31 ++ 39 files changed, 3755 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_cmd_name.c create mode 100644 trace2/tr2_cmd_name.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
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_cmd_name.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 += 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_name ("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_name ("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_name ("_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_name (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 +762 -0
762 lines not on this page · raw message
trace2.h new file +371 -0
371 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_cmd_name.c new file +30 -0
@@ -0,0 +1,30 @@ +#include "cache.h" +#include "trace2/tr2_cmd_name.h" + +#define TR2_ENVVAR_PARENT_NAME "GIT_TR2_PARENT_NAME" + +static struct strbuf tr2cmdname_hierarchy =STRBUF_INIT ; + +void tr2_cmd_name_append_hierarchy (const char *name) +{ +const char *parent_name =getenv (TR2_ENVVAR_PARENT_NAME ); + +strbuf_reset (&tr2cmdname_hierarchy); +if (parent_name && *parent_name) { +strbuf_addstr (&tr2cmdname_hierarchy, parent_name); +strbuf_addch (&tr2cmdname_hierarchy,'/' ); + } +strbuf_addstr (&tr2cmdname_hierarchy, name); + +setenv (TR2_ENVVAR_PARENT_NAME , tr2cmdname_hierarchy.buf,1 ); +} + +const char *tr2_cmd_name_get_hierarchy (void ) +{ +return tr2cmdname_hierarchy.buf; +} + +void tr2_cmd_name_release (void ) +{ +strbuf_release (&tr2cmdname_hierarchy); +}
trace2/tr2_cmd_name.h new file +24 -0
@@ -0,0 +1,24 @@ +#ifndef TR2_CMD_NAME_H +#define TR2_CMD_NAME_H + +/* +* Append the current command name to the list being maintained +* in the environment. +* +* The hierarchy for a top-level git command is just the current +* command name. For a child git process, the hierarchy includes the +* names of the parent processes. +* +* The hierarchy for the current process will be exported to the +* environment and inherited by child processes. +*/ +void tr2_cmd_name_append_hierarchy (const char *name); + +/* +* Get the command name hierarchy for the current process. +*/ +const char *tr2_cmd_name_get_hierarchy (void ); + +void tr2_cmd_name_release (void ); + +#endif /* TR2_CMD_NAME_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 +133 -0
133 lines not on this page · raw message
trace2/tr2_tgt_event.c new file +589 -0
589 lines not on this page · raw message
trace2/tr2_tgt_normal.c new file +324 -0
79 lines not on this page · raw message
trace2/tr2_tgt_perf.c new file +535 -0
535 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.
Re: [PATCH v6 02/15] trace2: create new combined trace facility
trace2/tr2_dst.c line 89
Quoted 11 lines · Jeff Hostetler via GitGitGadget
trace2/tr2_dst.c new file +11
-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 ; + }
Just curious, what do you use af_unix:* for? Is this provided by some Windows emulation ?
On Linux with systemd, this does not work for logging to systemd's /dev/log. You're opening the socket with SOCK_STREAM, but it needs SOCK_DGRAM. I.e.:
# fail
echo '{"yay": "testing"}' | nc -U /dev/log
# works
echo '{"yay": "testing"}' | nc -uU /dev/logSo that gives something that'll "work" for "jounalctl -f", but on my system ends up being munged to the invalid JSON:
'{"yay"[PID]: "testing"}'.I found, and this may be specific to the systemd/rsyslog setup on RedHat I'm working with, that what /dev/log is expecting is a payload in the syslog format: https://tools.ietf.org/html/rfc5424
So related to my question in
https://public-inbox.org/git/[email protected]/ I wonder what we should do about this. It seems the bare minimal thing that's needed it some way to open a socket with SOCK_DGRAM, maybe we should just do that unconditionally if the connect() fails with EPROTOTYPE?
But it seems that for that to be useful for systemd we'd need to support the syslog.h interface (so different from af_unix:*), or learn how to write a standard syslog packet to an af_unix:* SOCK_DGRAM socket.
I guess we can start with just supporting that by hardcoding the ident & LOG_* values to something sensible, e.g.:
openlog("git", LOG_PID | (tr2_dst_want_warning() ? LOG_CONS : 0), LOG_USER);
syslog(LOG_USER|LOG_DEBUG, message_or_json);
closelog();I can write the patches for this. As in the earlier message I'm just looking for some context, whether this is stuff you have WIP already, and whether you have any other/related ideas. Thanks.
Re: [PATCH v6 02/15] trace2: create new combined trace facility
Quoted 17 lines · Ævar Arnfjörð Bjarmason
trace2/tr2_dst.c line 89
Quoted 11 lines · Jeff Hostetler via GitGitGadget
trace2/tr2_dst.c new file +11
-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 ; + }Just curious, what do you use af_unix:* for? Is this provided by some Windows emulation ?
On the Mac, I want to send data to a server that only handles stream data. I thought about making the prefix something like
"af_unix:stream:<path>"
and then we could have the correpsonding dgram version too, but I didn't add that.
I think I'll add this in the next re-roll.
Quoted 32 lines
On Linux with systemd, this does not work for logging to systemd's
/dev/log. You're opening the socket withSOCK_STREAM, but it needsSOCK_DGRAM. I.e.:# fail echo '{"yay": "testing"}' | nc -U /dev/log # works echo '{"yay": "testing"}' | nc -uU /dev/logSo that gives something that'll "work" for "jounalctl -f", but on my system ends up being munged to the invalid JSON:
'{"yay"[PID]: "testing"}'.I found, and this may be specific to the systemd/rsyslog setup on RedHat I'm working with, that what
/dev/logis expecting is a payload in the syslog format: https://tools.ietf.org/html/rfc5424So related to my question in
https://public-inbox.org/git/[email protected]/ I wonder what we should do about this. It seems the bare minimal thing that's needed it some way to open a socket withSOCK_DGRAM, maybe we should just do that unconditionally if theconnect()fails with EPROTOTYPE?But it seems that for that to be useful for systemd we'd need to support the
syslog.hinterface (so different from af_unix:*), or learn how to write a standard syslog packet to an af_unix:*SOCK_DGRAMsocket.I guess we can start with just supporting that by hardcoding the ident & LOG_* values to something sensible, e.g.:
Quoted 8 lines
openlog("git", LOG_PID | (tr2_dst_want_warning() ? LOG_CONS : 0), LOG_USER); syslog(LOG_USER|LOG_DEBUG, message_or_json); closelog();I can write the patches for this. As in the earlier message I'm just looking for some context, whether this is stuff you have WIP already, and whether you have any other/related ideas. Thanks.
When I created the "af_unix:" prefix for Unix domain sockets, I thought we could later do other types of sockets if we wanted to. The common theme being they just need an "fd".
For syslog(), rather than changing the af_unix socket code, you could create a new Trace2 target. For example, copy one of the existing targets:
trace2/tr2_tgt_normal.c that defines GIT_TR2 trace2/tr2_tgt_event.c that defines GIT_TR2_EVENT trace2/tr2_tgt_perf.c that defines GIT_TR2_PERF
and create:
trace2/tr2_tgt_syslog.c that defines GIT_TR2_SYSLOG.
Then in tr2_tgt_syslog.c you have complete control of the formatting of the data you send to syslog(). You can omit fields as necessary. Or you can printf format it rather than JSON encode it.
Then in trace2/tr2_dst.c add a parser for "syslog:<whatever>". All it has to do is set a bit so that tr2_dst_trace_want() returns true if you were able to openlog().
Hope that helps,
Jeff
PATCH v6 3/15 trace2: collect Windows-specific process information
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add platform-specific interface to log information about the current process.
On Windows, this interface is used to indicate whether the git process is running under a debugger and list names of the process ancestors.
Information for other platforms is left for a future effort.
--- common-main.c | 1 + compat/win32/trace2_win32_process_info.c | 101 +++++++++++++++++++++++ config.mak.uname | 2 + trace2.h | 14 ++++ 4 files changed, 118 insertions(+) create mode 100644 compat/win32/trace2_win32_process_info.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/trace2_win32_process_info.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/trace2_win32_process_info.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/trace2_win32_process_info.o \ compat/win32/path-utils.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o
trace2.h +14 -0
@@ -368,4 +368,18 @@ void trace2_printf(const char *fmt, ...);/* clang-format on */ #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 v6 4/15 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 v6 5/15 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 v6 6/15 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 v6 7/15 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 v6 8/15 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 v6 9/15 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 v6 10/15 trace2:data: 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 v6 11/15 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_mode (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_mode ("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_mode ("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 v6 12/15 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_mode ("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_mode ("path" ); +else +trace2_cmd_mode (reset_type_names[reset_type]); +if (reset_type !=SOFT && (reset_type !=MIXED ||get_git_work_tree ()))setup_work_tree ();
Signature · 1 line
gitgitgadget
PATCH v6 13/15 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_mode ("interactive" ); +else if (exec.nr) +trace2_cmd_mode ("interactive-exec" ); +else +trace2_cmd_mode (action_names[action]); + } +switch (action) {case ACTION_CONTINUE : {struct object_id head;
Signature · 1 line
gitgitgadget
PATCH v6 14/15 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 | 234 ++++++++++++++++++++++++++++++++ t/t0212/parse_events.perl | 251 +++++++++++++++++++++++++++++++++++ 10 files changed, 1173 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 +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 +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 v6 15/15 trace2: add for_each macros to clang-format
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
.clang-format +1 -1
@@ -149,7 +149,7 @@ Cpp11BracedListStyle: false # A list of macros that should be interpreted as foreach loops instead of as # function calls. -ForEachMacros: ['for_each_string_list_item'] +ForEachMacros: ['for_each_string_list_item', 'for_each_wanted_builtin', 'for_each_builtin', 'for_each_ut'] # The maximum number of consecutive empty lines to keep. MaxEmptyLinesToKeep: 1
Signature · 1 line
gitgitgadget
Quoted 3 lines · Jeff Hostetler via GitGitGadget
V6 addresses: [] The remaining hdr-check warning in
trace2/tr2_tls.hThere are no other outstanding comments that I'm aware of.
Not a comment on this, just a follow-up question. I started looking into whether this could be driven by config instead of getenv(). A lot easier to set up in some cases than injecting env variables, especialy if the log target supported a strftime() string, is any of that something you've looked into already (so I don't do dupe work...).
There's the chicken & egg problem with wanting to do traces way before we get to reading config, so I expect that such a facility would need to work by always trace record at the beginning until we get far enough to write the config, and then either stop and throw away the buffer, or write out the existing trace to the configured target, and continue.
Quoted 13 lines · Ævar Arnfjörð Bjarmason
Quoted 3 lines · Jeff Hostetler via GitGitGadget
V6 addresses: [] The remaining hdr-check warning in
trace2/tr2_tls.hThere are no other outstanding comments that I'm aware of.
Not a comment on this, just a follow-up question. I started looking into whether this could be driven by config instead of
getenv(). A lot easier to set up in some cases than injecting env variables, especialy if the log target supported astrftime()string, is any of that something you've looked into already (so I don't do dupe work...).
Quoted 6 lines
There's the chicken & egg problem with wanting to do traces way before we get to reading config, so I expect that such a facility would need to work by always trace record at the beginning until we get far enough to write the config, and then either stop and throw away the buffer, or write out the existing trace to the configured target, and continue.
Yes, I beat my head against the config settings for quite a while before settling on using an env var.
I wanted to get the:
() full process elapsed time,
() the full original argv,
() all of the command dispatch, run-dashed, and alias expansion, () and for my atexit() to run last.
So I hooked into main() before the config is loaded.
In most commands, the config is processed about the same time as parse_options() is called. And we have to insert code in git_default_config() to load my settings. This happens after all of the .git dir discovery, "-c" and "-C" processing, alias expansion, command dispatch and etc. And the argv received in the cmd_*() function has been modified. So we lose some information. (I tried this for a while and didn't like the results.)
I also tried using read_early_config() various places, but there were problems here too. Too early and the "-c" settings weren't parsed yet. And there was an issue about when .git dir was discovered, so local config settings weren't ready yet.
I also recall having a problem when doing an early iteration with side effects (or rather the early iteration caused something to be set that caused the real iteration (in cmd_*()) to short-cut), but I don't remember the details.
So we have a custom installer that also sets the environment variable after git is installed and haven't had any problems.
I hesitate to say we should always start allocating a bunch of data and spinning up the TLS data and etc. before we know if tracing is wanted. Just seems expensive for most users.
I could see having a "~/.git_tr2_config" or something similar in some place like "/etc" that only contained the Trace2 settings. It would be safe to read very early inside main() and we would not consider it to be part of the real config. For example, "git config" would not know about it. Then you could enforce a system-wide setting without any of the env var issues.
WRT the strftime() question, we could either add syntax to the env var value (or the tr2 config setting) to have some tokens for that. I just stuck with absolute pathnames since I started by copying what was done for GIT_TRACE.
Jeff
Quoted 58 lines · Jeff Hostetler
Quoted 13 lines · Ævar Arnfjörð Bjarmason
Quoted 3 lines · Jeff Hostetler via GitGitGadget
V6 addresses: [] The remaining hdr-check warning in
trace2/tr2_tls.hThere are no other outstanding comments that I'm aware of.
Not a comment on this, just a follow-up question. I started looking into whether this could be driven by config instead of
getenv(). A lot easier to set up in some cases than injecting env variables, especialy if the log target supported astrftime()string, is any of that something you've looked into already (so I don't do dupe work...).Quoted 6 lines
There's the chicken & egg problem with wanting to do traces way before we get to reading config, so I expect that such a facility would need to work by always trace record at the beginning until we get far enough to write the config, and then either stop and throw away the buffer, or write out the existing trace to the configured target, and continue.
Yes, I beat my head against the config settings for quite a while before settling on using an env var.
I wanted to get the:
() full process elapsed time,
() the full original argv,
() all of the command dispatch, run-dashed, and alias expansion, () and for myatexit()to run last.
So I hooked intomain()before the config is loaded.In most commands, the config is processed about the same time as
parse_options()is called. And we have to insert code ingit_default_config()to load my settings. This happens after all of the .git dir discovery, "-c" and "-C" processing, alias expansion, command dispatch and etc. And the argv received in the cmd_*() function has been modified. So we lose some information. (I tried this for a while and didn't like the results.)I also tried using
read_early_config()various places, but there were problems here too. Too early and the "-c" settings weren't parsed yet. And there was an issue about when .git dir was discovered, so local config settings weren't ready yet.I also recall having a problem when doing an early iteration with side effects (or rather the early iteration caused something to be set that caused the real iteration (in cmd_*()) to short-cut), but I don't remember the details.
So we have a custom installer that also sets the environment variable after git is installed and haven't had any problems.
I hesitate to say we should always start allocating a bunch of data and spinning up the TLS data and etc. before we know if tracing is wanted. Just seems expensive for most users.
Quoted 6 lines
I could see having a "~/.git_tr2_config" or something similar in some place like "/etc" that only contained the Trace2 settings. It would be safe to read very early inside
main()and we would not consider it to be part of the real config. For example, "git config" would not know about it. Then you could enforce a system-wide setting without any of the env var issues.
I haven't written a patch for this, but it seems to me that we could just start reading /etc/gitconfig via some custom config callback code early as e.g. 58b284a2e91 ("worktree: add per-worktree config files", 2018-10-21) does for the worktree config.
It would ignore everything except trace.* or wherever namespace we'll put this in, and "-c" etc. wouldn't work. We could just document that as a limitation for now which could be fixed later.
It wouldn't make things worse, and would mean you could easily set logging system-wide without needing to inject environment variables in lots of custom (and sometimes hard-to-do) places, which I expect is the most common use-case, and is the one I have.
Quoted 6 lines
WRT the
strftime()question, we could either add syntax to the env var value (or the tr2 config setting) to have some tokens for that. I just stuck with absolute pathnames since I started by copying what was done forGIT_TRACE.Jeff
Hi Ævar,
Quoted 68 lines · Ævar Arnfjörð Bjarmason
Quoted 58 lines · Jeff Hostetler
Quoted 13 lines · Ævar Arnfjörð Bjarmason
Quoted 3 lines · Jeff Hostetler via GitGitGadget
V6 addresses: [] The remaining hdr-check warning in
trace2/tr2_tls.hThere are no other outstanding comments that I'm aware of.
Not a comment on this, just a follow-up question. I started looking into whether this could be driven by config instead of
getenv(). A lot easier to set up in some cases than injecting env variables, especialy if the log target supported astrftime()string, is any of that something you've looked into already (so I don't do dupe work...).Quoted 6 lines
There's the chicken & egg problem with wanting to do traces way before we get to reading config, so I expect that such a facility would need to work by always trace record at the beginning until we get far enough to write the config, and then either stop and throw away the buffer, or write out the existing trace to the configured target, and continue.
Yes, I beat my head against the config settings for quite a while before settling on using an env var.
I wanted to get the:
() full process elapsed time,
() the full original argv,
() all of the command dispatch, run-dashed, and alias expansion, () and for myatexit()to run last.
So I hooked intomain()before the config is loaded.In most commands, the config is processed about the same time as
parse_options()is called. And we have to insert code ingit_default_config()to load my settings. This happens after all of the .git dir discovery, "-c" and "-C" processing, alias expansion, command dispatch and etc. And the argv received in the cmd_*() function has been modified. So we lose some information. (I tried this for a while and didn't like the results.)I also tried using
read_early_config()various places, but there were problems here too. Too early and the "-c" settings weren't parsed yet. And there was an issue about when .git dir was discovered, so local config settings weren't ready yet.I also recall having a problem when doing an early iteration with side effects (or rather the early iteration caused something to be set that caused the real iteration (in cmd_*()) to short-cut), but I don't remember the details.
So we have a custom installer that also sets the environment variable after git is installed and haven't had any problems.
I hesitate to say we should always start allocating a bunch of data and spinning up the TLS data and etc. before we know if tracing is wanted. Just seems expensive for most users.
Quoted 6 lines
I could see having a "~/.git_tr2_config" or something similar in some place like "/etc" that only contained the Trace2 settings. It would be safe to read very early inside
main()and we would not consider it to be part of the real config. For example, "git config" would not know about it. Then you could enforce a system-wide setting without any of the env var issues.
Quoted 4 lines
I haven't written a patch for this, but it seems to me that we could just start reading
/etc/gitconfigvia some custom config callback code early as e.g.58b284a2e91("worktree: add per-worktree config files", 2018-10-21) does for the worktree config.
Oy. Oy, oy, oy.
Maybe use read_early_config() instead? That would be *a lot* cleaner. You could maybe use a9bcf6586d1a (alias: use the early config machinery to expand aliases, 2017-06-14) as an inspiration.
Quoted 8 lines
It would ignore everything except trace.* or wherever namespace we'll put this in, and "-c" etc. wouldn't work. We could just document that as a limitation for now which could be fixed later.
It wouldn't make things worse, and would mean you could easily set logging system-wide without needing to inject environment variables in lots of custom (and sometimes hard-to-do) places, which I expect is the most common use-case, and is the one I have.
Yes, I agree, those are good goals to address.
Ciao,
Dscho
Quoted 7 lines
Quoted 6 lines
WRT the
strftime()question, we could either add syntax to the env var value (or the tr2 config setting) to have some tokens for that. I just stuck with absolute pathnames since I started by copying what was done forGIT_TRACE.Jeff
Quoted 77 lines · Johannes Schindelin
Hi Ævar,
Quoted 68 lines · Ævar Arnfjörð Bjarmason
Quoted 58 lines · Jeff Hostetler
Quoted 13 lines · Ævar Arnfjörð Bjarmason
Quoted 3 lines · Jeff Hostetler via GitGitGadget
V6 addresses: [] The remaining hdr-check warning in
trace2/tr2_tls.hThere are no other outstanding comments that I'm aware of.
Not a comment on this, just a follow-up question. I started looking into whether this could be driven by config instead of
getenv(). A lot easier to set up in some cases than injecting env variables, especialy if the log target supported astrftime()string, is any of that something you've looked into already (so I don't do dupe work...).Quoted 6 lines
There's the chicken & egg problem with wanting to do traces way before we get to reading config, so I expect that such a facility would need to work by always trace record at the beginning until we get far enough to write the config, and then either stop and throw away the buffer, or write out the existing trace to the configured target, and continue.
Yes, I beat my head against the config settings for quite a while before settling on using an env var.
I wanted to get the:
() full process elapsed time,
() the full original argv,
() all of the command dispatch, run-dashed, and alias expansion, () and for myatexit()to run last.
So I hooked intomain()before the config is loaded.In most commands, the config is processed about the same time as
parse_options()is called. And we have to insert code ingit_default_config()to load my settings. This happens after all of the .git dir discovery, "-c" and "-C" processing, alias expansion, command dispatch and etc. And the argv received in the cmd_*() function has been modified. So we lose some information. (I tried this for a while and didn't like the results.)I also tried using
read_early_config()various places, but there were problems here too. Too early and the "-c" settings weren't parsed yet. And there was an issue about when .git dir was discovered, so local config settings weren't ready yet.I also recall having a problem when doing an early iteration with side effects (or rather the early iteration caused something to be set that caused the real iteration (in cmd_*()) to short-cut), but I don't remember the details.
So we have a custom installer that also sets the environment variable after git is installed and haven't had any problems.
I hesitate to say we should always start allocating a bunch of data and spinning up the TLS data and etc. before we know if tracing is wanted. Just seems expensive for most users.
Quoted 6 lines
I could see having a "~/.git_tr2_config" or something similar in some place like "/etc" that only contained the Trace2 settings. It would be safe to read very early inside
main()and we would not consider it to be part of the real config. For example, "git config" would not know about it. Then you could enforce a system-wide setting without any of the env var issues.Quoted 4 lines
I haven't written a patch for this, but it seems to me that we could just start reading
/etc/gitconfigvia some custom config callback code early as e.g.58b284a2e91("worktree: add per-worktree config files", 2018-10-21) does for the worktree config.
Quoted 5 lines
Oy. Oy, oy, oy.
Maybe use
read_early_config()instead? That would be *a lot* cleaner. You could maybe usea9bcf6586d1a(alias: use the early config machinery to expand aliases, 2017-06-14) as an inspiration.
Thanks. I was thinking *only* to do /etc/gitconfig and not the whole .git/config -> ~/.gitconfig etc. sequence just in terms of saving critical time (this is the performance trace path, after all...).
But on a second reading I see that read_early_config() can do that if you set config_source->file, opts->respect_includes etc. I.e. it just (depending on options) resolves to git_config_from_file() which 58b284a2e91 used directly.
Quoted 21 lines
Quoted 8 lines
It would ignore everything except trace.* or wherever namespace we'll put this in, and "-c" etc. wouldn't work. We could just document that as a limitation for now which could be fixed later.
It wouldn't make things worse, and would mean you could easily set logging system-wide without needing to inject environment variables in lots of custom (and sometimes hard-to-do) places, which I expect is the most common use-case, and is the one I have.
Yes, I agree, those are good goals to address.
Ciao,
DschoQuoted 7 lines
Quoted 6 lines
WRT the
strftime()question, we could either add syntax to the env var value (or the tr2 config setting) to have some tokens for that. I just stuck with absolute pathnames since I started by copying what was done forGIT_TRACE.Jeff
Hi Ævar,
Quoted 25 lines · Ævar Arnfjörð Bjarmason
Quoted 17 lines · Johannes Schindelin
Quoted 10 lines · Ævar Arnfjörð Bjarmason
Quoted 6 lines · Jeff Hostetler
I could see having a "~/.git_tr2_config" or something similar in some place like "/etc" that only contained the Trace2 settings. It would be safe to read very early inside
main()and we would not consider it to be part of the real config. For example, "git config" would not know about it. Then you could enforce a system-wide setting without any of the env var issues.Quoted 4 lines
I haven't written a patch for this, but it seems to me that we could just start reading
/etc/gitconfigvia some custom config callback code early as e.g.58b284a2e91("worktree: add per-worktree config files", 2018-10-21) does for the worktree config.Quoted 5 lines
Oy. Oy, oy, oy.
Maybe use
read_early_config()instead? That would be *a lot* cleaner. You could maybe usea9bcf6586d1a(alias: use the early config machinery to expand aliases, 2017-06-14) as an inspiration.
Quoted 8 lines
Thanks. I was thinking *only* to do
/etc/gitconfigand not the whole .git/config -> ~/.gitconfig etc. sequence just in terms of saving critical time (this is the performance trace path, after all...).But on a second reading I see that
read_early_config()can do that if you setconfig_source->file,opts->respect_includesetc. I.e. it just (depending on options) resolves togit_config_from_file()which58b284a2e91used directly.
Sure, it can exclude the repo and user config, but would that not be rather confusing?
Ciao,
Dscho
Quoted 33 lines · Johannes Schindelin
Hi Ævar,
Quoted 29 lines · Ævar Arnfjörð Bjarmason
Quoted 17 lines · Johannes Schindelin
Quoted 10 lines · Ævar Arnfjörð Bjarmason
Quoted 6 lines · Jeff Hostetler
I could see having a "~/.git_tr2_config" or something similar in some place like "/etc" that only contained the Trace2 settings. It would be safe to read very early inside
main()and we would not consider it to be part of the real config. For example, "git config" would not know about it. Then you could enforce a system-wide setting without any of the env var issues.Quoted 4 lines
I haven't written a patch for this, but it seems to me that we could just start reading
/etc/gitconfigvia some custom config callback code early as e.g.58b284a2e91("worktree: add per-worktree config files", 2018-10-21) does for the worktree config.Quoted 5 lines
Oy. Oy, oy, oy.
Maybe use
read_early_config()instead? That would be *a lot* cleaner. You could maybe usea9bcf6586d1a(alias: use the early config machinery to expand aliases, 2017-06-14) as an inspiration.Thanks. I was thinking *only* to do
/etc/gitconfigand not the whole .git/config -> ~/.gitconfig etc. sequence just in terms of saving critical time (this is the performance trace path, after all...).
Quoted 7 lines
Quoted 4 lines
But on a second reading I see that
read_early_config()can do that if you setconfig_source->file,opts->respect_includesetc. I.e. it just (depending on options) resolves togit_config_from_file()which58b284a2e91used directly.Sure, it can exclude the repo and user config, but would that not be rather confusing?
This was hidden in my earlier message.
There's a lot a config machinery here with lots of chicken-n-egg problems. I want to know at the top of main() as quickly as possible whether trace2 should be enabled.
I don't want to slow down git by spinning up a bunch of trace2 state and wait until the git-dir is discovered, the "-c" args are processed, and we dispatch into the builtin layer and the config is enumerated to know if it should really be on or not.
I also didn't want to introduce another full iteration of the full config system for startup performance reasons.
I played with read_early_config() at one point and it always seemed to introduce side-effects. Perhaps I was calling it earlier than it was expecting and that triggered some of the git-dir discovery or something. I don't remember all the details, I just remember that it changed some behaviors in subtle ways.
Perhaps I could call something like git_config_from_file() with the right set of magic bits to get it to parse exactly 1 system config file that would contain my trace2 settings. Hopefully, this will not have any side-effects.
But if we lump them in with the main /etc/gitconfig settings, we would have to explain that these trace2 config settings are system-only and ARE NOT overridden by "-c", global, local, ... config settings. This would get confusing if the user tried to set local values and did:
git config --list --show-origin
and it showed system and local values but yet "stubbornly" refused to use the local values over the system values. (I think this was Johannes' point.)
That's why I was suggesting a system trace2 config file that is a peer of /etc/gitconfig (maybe /etc/gittrace2) that would have these values and not be expected to interact with the main config system. That is, we just use the git_config_ routines to parse the file format, rather than inventing another file format, but not change the expectation of the established config value inheritance system.
If there's no objections, I'll take a look at doing this.
Thanks
Jeff
Quoted 62 lines · Jeff Hostetler
Quoted 33 lines · Johannes Schindelin
Hi Ævar,
Quoted 29 lines · Ævar Arnfjörð Bjarmason
Quoted 17 lines · Johannes Schindelin
Quoted 10 lines · Ævar Arnfjörð Bjarmason
Quoted 6 lines · Jeff Hostetler
I could see having a "~/.git_tr2_config" or something similar in some place like "/etc" that only contained the Trace2 settings. It would be safe to read very early inside
main()and we would not consider it to be part of the real config. For example, "git config" would not know about it. Then you could enforce a system-wide setting without any of the env var issues.Quoted 4 lines
I haven't written a patch for this, but it seems to me that we could just start reading
/etc/gitconfigvia some custom config callback code early as e.g.58b284a2e91("worktree: add per-worktree config files", 2018-10-21) does for the worktree config.Quoted 5 lines
Oy. Oy, oy, oy.
Maybe use
read_early_config()instead? That would be *a lot* cleaner. You could maybe usea9bcf6586d1a(alias: use the early config machinery to expand aliases, 2017-06-14) as an inspiration.Thanks. I was thinking *only* to do
/etc/gitconfigand not the whole .git/config -> ~/.gitconfig etc. sequence just in terms of saving critical time (this is the performance trace path, after all...).Quoted 7 lines
Quoted 4 lines
But on a second reading I see that
read_early_config()can do that if you setconfig_source->file,opts->respect_includesetc. I.e. it just (depending on options) resolves togit_config_from_file()which58b284a2e91used directly.Sure, it can exclude the repo and user config, but would that not be rather confusing?
This was hidden in my earlier message.
There's a lot a config machinery here with lots of chicken-n-egg problems. I want to know at the top of
main()as quickly as possible whether trace2 should be enabled.I don't want to slow down git by spinning up a bunch of trace2 state and wait until the git-dir is discovered, the "-c" args are processed, and we dispatch into the builtin layer and the config is enumerated to know if it should really be on or not.
I also didn't want to introduce another full iteration of the full config system for startup performance reasons.
I played with
read_early_config()at one point and it always seemed to introduce side-effects. Perhaps I was calling it earlier than it was expecting and that triggered some of the git-dir discovery or something. I don't remember all the details, I just remember that it changed some behaviors in subtle ways.
Quoted 4 lines
Perhaps I could call something like
git_config_from_file()with the right set of magic bits to get it to parse exactly 1 system config file that would contain my trace2 settings. Hopefully, this will not have any side-effects.
Right, it also occurred to me that e.g. /home tends to be on NFS on some systems, but very rarely /etc. I'd hate for trace reporting for git to stall because NFS slows to a halt, so aside from temporary implementation details (e.g. -c on the CLI not working) there's a good case to be made for "read this from /etc/gitconfig only".
Quoted 10 lines
But if we lump them in with the main
/etc/gitconfigsettings, we would have to explain that these trace2 config settings are system-only and ARE NOT overridden by "-c", global, local, ... config settings. This would get confusing if the user tried to set local values and did:git config --list --show-originand it showed system and local values but yet "stubbornly" refused to use the local values over the system values. (I think this was Johannes' point.)
Quoted 8 lines
That's why I was suggesting a system trace2 config file that is a peer of
/etc/gitconfig(maybe/etc/gittrace2) that would have these values and not be expected to interact with the main config system. That is, we just use thegit_config_routines to parse the file format, rather than inventing another file format, but not change the expectation of the established config value inheritance system.If there's no objections, I'll take a look at doing this.
I'd much rather just drop it in /etc/gitconfig with documented caveats than introduce a new and permanent thing like /etc/gittrace2 due to a current implementation detail.
Unlike something in core.* or whatever this facility is specialized enough that I think it's fine to make it a bit of a special snowflake given what it does and the target audience.
But even with those caveats it's still useful to see it in 'git config -l --show-origin' for inspecting, and e.g. have it just work out of the box with say the default puppetry for the likes of GitLab that now knows how to set stuff in its /etc/gitconfig, but would need a special case just for this.
Quoted 103 lines · Ævar Arnfjörð Bjarmason
Quoted 62 lines · Jeff Hostetler
Quoted 33 lines · Johannes Schindelin
Hi Ævar,
Quoted 29 lines · Ævar Arnfjörð Bjarmason
Quoted 17 lines · Johannes Schindelin
Quoted 10 lines · Ævar Arnfjörð Bjarmason
Quoted 6 lines · Jeff Hostetler
I could see having a "~/.git_tr2_config" or something similar in some place like "/etc" that only contained the Trace2 settings. It would be safe to read very early inside
main()and we would not consider it to be part of the real config. For example, "git config" would not know about it. Then you could enforce a system-wide setting without any of the env var issues.Quoted 4 lines
I haven't written a patch for this, but it seems to me that we could just start reading
/etc/gitconfigvia some custom config callback code early as e.g.58b284a2e91("worktree: add per-worktree config files", 2018-10-21) does for the worktree config.Quoted 5 lines
Oy. Oy, oy, oy.
Maybe use
read_early_config()instead? That would be *a lot* cleaner. You could maybe usea9bcf6586d1a(alias: use the early config machinery to expand aliases, 2017-06-14) as an inspiration.Thanks. I was thinking *only* to do
/etc/gitconfigand not the whole .git/config -> ~/.gitconfig etc. sequence just in terms of saving critical time (this is the performance trace path, after all...).Quoted 7 lines
Quoted 4 lines
But on a second reading I see that
read_early_config()can do that if you setconfig_source->file,opts->respect_includesetc. I.e. it just (depending on options) resolves togit_config_from_file()which58b284a2e91used directly.Sure, it can exclude the repo and user config, but would that not be rather confusing?
This was hidden in my earlier message.
There's a lot a config machinery here with lots of chicken-n-egg problems. I want to know at the top of
main()as quickly as possible whether trace2 should be enabled.I don't want to slow down git by spinning up a bunch of trace2 state and wait until the git-dir is discovered, the "-c" args are processed, and we dispatch into the builtin layer and the config is enumerated to know if it should really be on or not.
I also didn't want to introduce another full iteration of the full config system for startup performance reasons.
I played with
read_early_config()at one point and it always seemed to introduce side-effects. Perhaps I was calling it earlier than it was expecting and that triggered some of the git-dir discovery or something. I don't remember all the details, I just remember that it changed some behaviors in subtle ways.Quoted 4 lines
Perhaps I could call something like
git_config_from_file()with the right set of magic bits to get it to parse exactly 1 system config file that would contain my trace2 settings. Hopefully, this will not have any side-effects.Right, it also occurred to me that e.g. /home tends to be on NFS on some systems, but very rarely /etc. I'd hate for trace reporting for git to stall because NFS slows to a halt, so aside from temporary implementation details (e.g. -c on the CLI not working) there's a good case to be made for "read this from
/etc/gitconfigonly".Quoted 10 lines
But if we lump them in with the main
/etc/gitconfigsettings, we would have to explain that these trace2 config settings are system-only and ARE NOT overridden by "-c", global, local, ... config settings. This would get confusing if the user tried to set local values and did:git config --list --show-originand it showed system and local values but yet "stubbornly" refused to use the local values over the system values. (I think this was Johannes' point.)
Quoted 8 lines
That's why I was suggesting a system trace2 config file that is a peer of
/etc/gitconfig(maybe/etc/gittrace2) that would have these values and not be expected to interact with the main config system. That is, we just use thegit_config_routines to parse the file format, rather than inventing another file format, but not change the expectation of the established config value inheritance system.If there's no objections, I'll take a look at doing this.
I'd much rather just drop it in
/etc/gitconfigwith documented caveats than introduce a new and permanent thing like/etc/gittrace2due to a current implementation detail.Unlike something in core.* or whatever this facility is specialized enough that I think it's fine to make it a bit of a special snowflake given what it does and the target audience.
Quoted 6 lines
But even with those caveats it's still useful to see it in 'git config -l --show-origin' for inspecting, and e.g. have it just work out of the box with say the default puppetry for the likes of GitLab that now knows how to set stuff in its
/etc/gitconfig, but would need a special case just for this.
ok, if we're comfortable with those caveats, i'll proceed as you suggested. thanks for the quick response.
Jeff
v7 cover Trace2 tracing facility
In reply to Jeff Hostetler via GitGitGadgetV7 addresses: [] squashes in the fixup for windows process ancestry calculation. [] adds optional socket_type parameter to the unix domain socket syntax. [] allow unix domain socket syntax to try both stream and dgram if socket_type not specified. [] add Perl prereq checks for JSON:PP in trace2 tests. [] minor clang-format cleanup.
Thanks Jeff
Derrick Stolee (1):
trace2:data: pack-objects: add trace2 regions
Jeff Hostetler (14):
trace2: Documentation/technical/api-trace2.txt
trace2: create new combined trace facility
trace2: collect Windows-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
trace2: add for_each macros to clang-format
.clang-format | 2 +- Documentation/technical/api-trace2.txt | 1350 ++++++++++++++++++++++ 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/trace2_win32_process_info.c | 147 +++ 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 | 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 | 236 ++++ t/t0212/parse_events.perl | 251 ++++ trace2.c | 761 ++++++++++++ trace2.h | 385 ++++++ trace2/tr2_cfg.c | 90 ++ trace2/tr2_cfg.h | 19 + trace2/tr2_cmd_name.c | 30 + trace2/tr2_cmd_name.h | 24 + trace2/tr2_dst.c | 252 ++++ 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 | 133 +++ trace2/tr2_tgt_event.c | 588 ++++++++++ trace2/tr2_tgt_normal.c | 323 ++++++ trace2/tr2_tgt_perf.c | 534 +++++++++ trace2/tr2_tls.c | 164 +++ trace2/tr2_tls.h | 97 ++ transport-helper.c | 2 + transport.c | 1 + usage.c | 31 + wt-status.c | 24 +- 67 files changed, 6630 insertions(+), 23 deletions(-) create mode 100644 Documentation/technical/api-trace2.txt create mode 100644 compat/win32/trace2_win32_process_info.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_cmd_name.c create mode 100644 trace2/tr2_cmd_name.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
base-commit: b5101f929789889c2e536d915698f58d5c5c6b7a
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-108%2Fjeffhostetler%2Fcore-trace2-2019-v0-v7
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-108/jeffhostetler/core-trace2-2019-v0-v7
Pull-Request: https://github.com/gitgitgadget/git/pull/108
Range-diff vs v6:
1: 4c006f4995 ! 1: 8ac6acf88d trace2: Documentation/technical/api-trace2.txt
@@ -122,10 +122,13 @@
+
+ Enables the target, opens and writes to the file in append mode.
+
-+`af_unix:<absolute-pathname>`::
++`af_unix:[<socket_type>:]<absolute-pathname>`::
+
+ Enables the target, opens and writes to a Unix Domain Socket
+ (on platforms that support them).
+++
++Socket type can be either `stream` or `dgram`. If the socket type is
++omitted, Git will try both.
+
+== Trace2 API
+
2: 6bad326bbd ! 2: 90e4e6af93 trace2: create new combined trace facility
@@ -947,8 +947,7 @@
+
+ for_each_wanted_builtin (j, tgt_j)
+ if (tgt_j->pfn_command_name_fl)
-+ tgt_j->pfn_command_name_fl(file, line, name,
-+ hierarchy);
++ tgt_j->pfn_command_name_fl(file, line, name, hierarchy);
+}
+
+void trace2_cmd_mode_fl(const char *file, int line, const char *mode)
@@ -2080,37 +2079,103 @@
+
+#ifndef NO_UNIX_SOCKETS
+#define PREFIX_AF_UNIX "af_unix:"
-+#define PREFIX_AF_UNIX_LEN (8)
++#define PREFIX_AF_UNIX_STREAM "af_unix:stream:"
++#define PREFIX_AF_UNIX_DGRAM "af_unix:dgram:"
++
++static int tr2_dst_try_uds_connect(const char *path, int sock_type, int *out_fd)
++{
++ int fd;
++ struct sockaddr_un sa;
++
++ fd = socket(AF_UNIX, sock_type, 0);
++ if (fd == -1)
++ return errno;
++
++ sa.sun_family = AF_UNIX;
++ strlcpy(sa.sun_path, path, sizeof(sa.sun_path));
++
++ if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
++ int e = errno;
++ close(fd);
++ return e;
++ }
++
++ *out_fd = fd;
++ return 0;
++}
++
++#define TR2_DST_UDS_TRY_STREAM (1 << 0)
++#define TR2_DST_UDS_TRY_DGRAM (1 << 1)
+
+static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
+ const char *tgt_value)
+{
++ unsigned int uds_try = 0;
+ int fd;
-+ struct sockaddr_un sa;
-+ const char *path = tgt_value + PREFIX_AF_UNIX_LEN;
-+ int path_len = strlen(path);
++ int e;
++ const char *path = NULL;
++
++ /*
++ * Allow "af_unix:[<type>:]<absolute_path>"
++ *
++ * Trace2 always writes complete individual messages (without
++ * chunking), so we can talk to either DGRAM or STREAM type sockets.
++ *
++ * Allow the user to explicitly request the socket type.
++ *
++ * If they omit the socket type, try one and then the other.
++ */
+
-+ if (!is_absolute_path(path) || path_len >= sizeof(sa.sun_path)) {
++ if (skip_prefix(tgt_value, PREFIX_AF_UNIX_STREAM, &path))
++ uds_try |= TR2_DST_UDS_TRY_STREAM;
++
++ else if (skip_prefix(tgt_value, PREFIX_AF_UNIX_DGRAM, &path))
++ uds_try |= TR2_DST_UDS_TRY_DGRAM;
++
++ else if (skip_prefix(tgt_value, PREFIX_AF_UNIX, &path))
++ uds_try |= TR2_DST_UDS_TRY_STREAM | TR2_DST_UDS_TRY_DGRAM;
++
++ if (!path || !*path) {
+ if (tr2_dst_want_warning())
-+ warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing",
-+ path, dst->env_var_name);
++ warning("trace2: invalid AF_UNIX value '%s' for '%s' tracing",
++ tgt_value, 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 (!is_absolute_path(path) ||
++ strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) {
+ if (tr2_dst_want_warning())
-+ warning("trace2: could not connect to socket '%s' for '%s' tracing: %s",
-+ path, dst->env_var_name, strerror(errno));
++ warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing",
++ path, dst->env_var_name);
+
+ tr2_dst_trace_disable(dst);
+ return 0;
+ }
+
++ if (uds_try & TR2_DST_UDS_TRY_STREAM) {
++ e = tr2_dst_try_uds_connect(path, SOCK_STREAM, &fd);
++ if (!e)
++ goto connected;
++ if (e != EPROTOTYPE)
++ goto error;
++ }
++ if (uds_try & TR2_DST_UDS_TRY_DGRAM) {
++ e = tr2_dst_try_uds_connect(path, SOCK_DGRAM, &fd);
++ if (!e)
++ goto connected;
++ }
++
++error:
++ if (tr2_dst_want_warning())
++ warning("trace2: could not connect to socket '%s' for '%s' tracing: %s",
++ path, dst->env_var_name, strerror(e));
++
++ tr2_dst_trace_disable(dst);
++ return 0;
++
++connected:
+ dst->fd = fd;
+ dst->need_close = 1;
+ dst->initialized = 1;
@@ -2124,19 +2189,8 @@
+{
+ struct strbuf buf = STRBUF_INIT;
+
-+ strbuf_addf(&buf, "trace2: unknown trace value for '%s': '%s'",
++ strbuf_addf(&buf, "trace2: unknown 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);
@@ -2174,7 +2228,7 @@
+ return tr2_dst_try_path(dst, tgt_value);
+
+#ifndef NO_UNIX_SOCKETS
-+ if (!strncmp(tgt_value, PREFIX_AF_UNIX, PREFIX_AF_UNIX_LEN))
++ if (starts_with(tgt_value, PREFIX_AF_UNIX))
+ return tr2_dst_try_unix_domain_socket(dst, tgt_value);
+#endif
+
@@ -2817,8 +2871,7 @@
+ jw_release(&jw);
+}
+
-+static void fn_command_name_fl(const char *file, int line,
-+ const char *name,
++static void fn_command_name_fl(const char *file, int line, const char *name,
+ const char *hierarchy)
+{
+ const char *event_name = "cmd_name";
@@ -3327,8 +3380,7 @@
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_command_name_fl(const char *file, int line,
-+ const char *name,
++static void fn_command_name_fl(const char *file, int line, const char *name,
+ const char *hierarchy)
+{
+ struct strbuf buf_payload = STRBUF_INIT;
@@ -3747,8 +3799,7 @@
+ strbuf_release(&buf_payload);
+}
+
-+static void fn_command_name_fl(const char *file, int line,
-+ const char *name,
++static void fn_command_name_fl(const char *file, int line, const char *name,
+ const char *hierarchy)
+{
+ const char *event_name = "cmd_name";
3: 12de7e42de ! 3: 50f689dc95 trace2: collect Windows-specific process information
@@ -35,6 +35,12 @@
+#include <tlHelp32.h>
+
+/*
++ * An arbitrarily chosen value to limit the size of the ancestor
++ * array built in git_processes().
++ */
++#define NR_PIDS_LIMIT 10
++
++/*
+ * Find the process data for the given PID in the given snapshot
+ * and update the PROCESSENTRY32 data.
+ */
@@ -52,7 +58,7 @@
+}
+
+/*
-+ * Accumulate JSON array:
++ * Accumulate JSON array of our parent processes:
+ * [
+ * exe-name-parent,
+ * exe-name-grand-parent,
@@ -64,21 +70,61 @@
+ * and GetModuleFileNameEx() or QueryfullProcessImageName()
+ * and that seems rather expensive (on top of the cost of
+ * getting the snapshot).
++ *
++ * Note: we compute the set of parent processes by walking the PPID
++ * link in each visited PROCESSENTRY32 record. This search
++ * stops when an ancestor process is not found in the snapshot
++ * (because it exited before the current or intermediate parent
++ * process exited).
++ *
++ * This search may compute an incorrect result if the PPID link
++ * refers to the PID of an exited parent and that PID has been
++ * recycled and given to a new unrelated process.
++ *
++ * Worse, it is possible for a child or descendant of the
++ * current process to be given the recycled PID and cause a
++ * PPID-cycle. This would cause an infinite loop building our
++ * parent process array.
++ *
++ * Note: for completeness, the "System Idle" process has PID=0 and
++ * PPID=0 and could cause another PPID-cycle. We don't expect
++ * Git to be a descendant of the idle process, but because of
++ * PID recycling, it might be possible to get a PPID link value
++ * of 0. This too would cause an infinite loop.
++ *
++ * Therefore, we keep an array of the visited PPIDs to guard against
++ * cycles.
++ *
++ * We use a fixed-size array rather than ALLOC_GROW to keep things
++ * simple and avoid the alloc/realloc overhead. It is OK if we
++ * truncate the search and return a partial answer.
+ */
+static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
+{
+ PROCESSENTRY32 pe32;
+ DWORD pid;
++ DWORD pid_list[NR_PIDS_LIMIT];
++ int k, nr_pids = 0;
+
+ pid = GetCurrentProcessId();
-+
-+ /* We only want parent processes, so skip self. */
-+ if (!find_pid(pid, hSnapshot, &pe32))
-+ return;
-+ pid = pe32.th32ParentProcessID;
-+
+ while (find_pid(pid, hSnapshot, &pe32)) {
-+ jw_array_string(jw, pe32.szExeFile);
++ /* Only report parents. Omit self from the JSON output. */
++ if (nr_pids)
++ jw_array_string(jw, pe32.szExeFile);
++
++ /* Check for cycle in snapshot. (Yes, it happened.) */
++ for (k = 0; k < nr_pids; k++)
++ if (pid == pid_list[k]) {
++ jw_array_string(jw, "(cycle)");
++ return;
++ }
++
++ if (nr_pids == NR_PIDS_LIMIT) {
++ jw_array_string(jw, "(truncated)");
++ return;
++ }
++
++ pid_list[nr_pids++] = pid;
+
+ pid = pe32.th32ParentProcessID;
+ }
4: 5835edbd01 = 4: 40411100c3 trace2:data: add trace2 regions to wt-status
5: 99d13ef478 = 5: 18ff59d6fe trace2:data: add editor/pager child classification
6: d7ce85b702 = 6: ec7d7cd4c7 trace2:data: add trace2 sub-process classification
7: b70c289903 = 7: 485a4f2a68 trace2:data: add trace2 transport child classification
8: 2a0da88579 = 8: 18659d61da trace2:data: add trace2 hook classification
9: bca6a7f0d6 = 9: 08be5aadbe trace2:data: add trace2 instrumentation to index read/write
10: 68269ee060 = 10: 4e679241fc trace2:data: pack-objects: add trace2 regions
11: 0191283fff = 11: 2a5aa3a8dc trace2:data: add subverb to checkout command
12: 24d8d8d768 = 12: ebaa9b1d80 trace2:data: add subverb to reset command
13: 90cec071cf = 13: 3e6fee40b0 trace2:data: add subverb for rebase
14: b9f0c6fd66 ! 14: 398edb7738 trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
@@ -767,6 +767,8 @@
+test_description='test trace2 facility'
+. ./test-lib.sh
+
++perl -MJSON::PP -e 0 >/dev/null 2>&1 && test_set_prereq JSON_PP
++
+# Add t/helper directory to PATH so that we can use a relative
+# path to run nested instances of test-tool.exe (see 004child).
+# This helps with HEREDOC comparisons later.
@@ -802,7 +804,7 @@
+#
+# To the above, add multiple 'error <msg>' events
+
-+test_expect_success 'event stream, error event' '
++test_expect_success JSON_PP 'event stream, error event' '
+ test_when_finished "rm trace.event actual expect" &&
+ 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 &&
@@ -839,7 +841,7 @@
+# P2: |--- TT trace2 004child
+# P3: |--- TT trace2 001return 0
+
-+test_expect_success 'event stream, return code 0' '
++test_expect_success JSON_PP 'event stream, return code 0' '
+ 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 &&
@@ -926,7 +928,7 @@
+
+# Test listing of all "interesting" config settings.
+
-+test_expect_success 'event stream, list config' '
++test_expect_success JSON_PP 'event stream, list config' '
+ test_when_finished "rm trace.event actual expect" &&
+ git config --local t0212.abc 1 &&
+ git config --local t0212.def "hello world" &&
@@ -961,7 +963,7 @@
+ test_cmp expect actual
+'
+
-+test_expect_success 'basic trace2_data' '
++test_expect_success JSON_PP 'basic trace2_data' '
+ test_when_finished "rm trace.event actual expect" &&
+ 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 && 15: 93a25d09a1 = 15: 662201f49e trace2: add for_each macros to clang-format
Signature · 1 line
gitgitgadget
PATCH v7 1/15 trace2: Documentation/technical/api-trace2.txt
From: Jeff Hostetler <[email protected]>
Created design document for Trace2 feature.
--- Documentation/technical/api-trace2.txt | 1350 ++++++++++++++++++++++++ 1 file changed, 1350 insertions(+) create mode 100644 Documentation/technical/api-trace2.txt
Documentation/technical/api-trace2.txt new file +1350 -0
@@ -0,0 +1,1350 @@ += 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_name 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_name | | | | | 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_name","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:[<socket_type>:]<absolute-pathname>`:: + + Enables the target, opens and writes to a Unix Domain Socket + (on platforms that support them). ++ +Socket type can be either `stream` or `dgram`. If the socket type is +omitted, Git will try both. + +== 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_name(const char *name)`:: + + Emits a "cmd_name" message with the canonical name of the + command, for example "status" or "checkout". + +`void trace2_cmd_mode(const char *mode)`:: + + Emits a "cmd_mode" 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 it can checkout a single file, so the checkout code could +emit a cmd_mode 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_name"`:: + This event contains the command name for this git process + and the hierarchy of commands from parent git processes. ++ +------------ +{ + "event":"cmd_name", + ... + "name":"pack-objects", + "hierarchy":"push/pack-objects" +} +------------ ++ +Normally, the "name" field contains the canonical name of the +command. When a canonical name 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_mode"`:: + This event, when present, describes the command variant This + event may be emitted more than once. ++ +------------ +{ + "event":"cmd_mode", + ... + "name":"branch" +} +------------ ++ +The "name" field is an arbitrary string to describe the command mode. +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_name("checkout"); + trace2_cmd_mode("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_name 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 the command hierarchy. This example +shows gc as child[3] of fetch. When the gc process reports +its name 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_name 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_name 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_name | | | | | 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_name | | | | | 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_name | | | | | 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 v7 2/15 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 | 761 ++++++++++++++++++++++++++++++++++ trace2.h | 371 +++++++++++++++++ trace2/tr2_cfg.c | 90 ++++ trace2/tr2_cfg.h | 19 + trace2/tr2_cmd_name.c | 30 ++ trace2/tr2_cmd_name.h | 24 ++ trace2/tr2_dst.c | 252 +++++++++++ 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 | 133 ++++++ trace2/tr2_tgt_event.c | 588 ++++++++++++++++++++++++++ trace2/tr2_tgt_normal.c | 323 +++++++++++++++ trace2/tr2_tgt_perf.c | 534 ++++++++++++++++++++++++ trace2/tr2_tls.c | 164 ++++++++ trace2/tr2_tls.h | 97 +++++ usage.c | 31 ++ 39 files changed, 3806 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_cmd_name.c create mode 100644 trace2/tr2_cmd_name.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
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_cmd_name.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 += 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_name ("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_name ("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_name ("_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_name (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 +761 -0
761 lines not on this page · raw message
trace2.h new file +371 -0
371 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_cmd_name.c new file +30 -0
@@ -0,0 +1,30 @@ +#include "cache.h" +#include "trace2/tr2_cmd_name.h" + +#define TR2_ENVVAR_PARENT_NAME "GIT_TR2_PARENT_NAME" + +static struct strbuf tr2cmdname_hierarchy =STRBUF_INIT ; + +void tr2_cmd_name_append_hierarchy (const char *name) +{ +const char *parent_name =getenv (TR2_ENVVAR_PARENT_NAME ); + +strbuf_reset (&tr2cmdname_hierarchy); +if (parent_name && *parent_name) { +strbuf_addstr (&tr2cmdname_hierarchy, parent_name); +strbuf_addch (&tr2cmdname_hierarchy,'/' ); + } +strbuf_addstr (&tr2cmdname_hierarchy, name); + +setenv (TR2_ENVVAR_PARENT_NAME , tr2cmdname_hierarchy.buf,1 ); +} + +const char *tr2_cmd_name_get_hierarchy (void ) +{ +return tr2cmdname_hierarchy.buf; +} + +void tr2_cmd_name_release (void ) +{ +strbuf_release (&tr2cmdname_hierarchy); +}
trace2/tr2_cmd_name.h new file +24 -0
@@ -0,0 +1,24 @@ +#ifndef TR2_CMD_NAME_H +#define TR2_CMD_NAME_H + +/* +* Append the current command name to the list being maintained +* in the environment. +* +* The hierarchy for a top-level git command is just the current +* command name. For a child git process, the hierarchy includes the +* names of the parent processes. +* +* The hierarchy for the current process will be exported to the +* environment and inherited by child processes. +*/ +void tr2_cmd_name_append_hierarchy (const char *name); + +/* +* Get the command name hierarchy for the current process. +*/ +const char *tr2_cmd_name_get_hierarchy (void ); + +void tr2_cmd_name_release (void ); + +#endif /* TR2_CMD_NAME_H */
trace2/tr2_dst.c new file +252 -0
252 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 +133 -0
133 lines not on this page · raw message
trace2/tr2_tgt_event.c new file +588 -0
588 lines not on this page · raw message
trace2/tr2_tgt_normal.c new file +323 -0
26 lines not on this page · raw message
trace2/tr2_tgt_perf.c new file +534 -0
534 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.
Re: [PATCH v7 02/15] trace2: create new combined trace facility
Quoted 17 lines · Jeff Hostetler via GitGitGadget
From: 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 currentGIT_TRACE).*
GIT_TR2is intended to be a replacement forGIT_TRACEand logs command summary data.
Quoted 8 lines
*
GIT_TR2_PERFis intended as a replacement forGIT_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_EVENTis a new structured format. It writes event data as a series of JSON records.
Please document these new environment variables in 'Documentation/git.txt', where the other environment variables, including GIT_TRACE_*, are already documented.
While doing so, please note that the description about the possible values of these variables and of GIT_TRACE above is incomplete, because it's not just "1" or an absolute pathname. Quoting the description of GIT_TRACE:
If this variable is set to "1", "2" or "true" (comparison is case insensitive), trace messages will be printed to stderr.
If the variable is set to an integer value greater than 2 and lower than 10 (strictly) then Git will interpret this value as an open file descriptor and will try to write the trace messages into this file descriptor.
The way I see it in tr2_dst_get_trace_fd() below, this applies to GIT_TRACE2* as well, and it even offers the possibility to specify a Unix Domain Socket, too.
And sorry for barging in with a big bucket of paint this late, but, really... why GIT_TR2 instead of GIT_TRACE2?
Quoted 41 lines
trace2/tr2_dst.c new file +41
-0+int tr2_dst_get_trace_fd (struct tr2_dst *dst) +{ +const char *tgt_value; + +/* don't open twice */ +if (dst->initialized) +return dst->fd; + + dst->initialized =1 ; + + tgt_value =getenv (dst->env_var_name); + +if (!tgt_value || !strcmp (tgt_value,"" ) || !strcmp (tgt_value,"0" ) || + !strcasecmp (tgt_value,"false" )) { + dst->fd =0 ; +return dst->fd; + } + +if (!strcmp (tgt_value,"1" ) || !strcasecmp (tgt_value,"true" )) { + dst->fd =STDERR_FILENO ; +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 (starts_with (tgt_value,PREFIX_AF_UNIX )) +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 ; +}
Re: [PATCH v7 02/15] trace2: create new combined trace facility
Quoted 9 lines · SZEDER Gábor
Quoted 4 lines · Jeff Hostetler via GitGitGadget
From: Jeff Hostetler <[email protected]>
*
GIT_TR2_EVENTis a new structured format. It writes event data as a series of JSON records.Please document these new environment variables in '
Documentation/git.txt', where the other environment variables, including GIT_TRACE_*, are already documented.
Thanks for the report!
Quoted 8 lines
While doing so, please note that the description about the possible values of these variables and of
GIT_TRACEabove is incomplete, because it's not just "1" or an absolute pathname. Quoting the description ofGIT_TRACE:If this variable is set to "1", "2" or "true" (comparison is case insensitive), trace messages will be printed to stderr.
Quoted 8 lines
If the variable is set to an integer value greater than 2 and lower than 10 (strictly) then Git will interpret this value as an open file descriptor and will try to write the trace messages into this file descriptor.
The way I see it in
tr2_dst_get_trace_fd()below, this applies to GIT_TRACE2* as well, and it even offers the possibility to specify a Unix Domain Socket, too.
Please see [1] for a possible direction here.
[1] https://public-inbox.org/git/[email protected]/
Quoted 2 lines
And sorry for barging in with a big bucket of paint this late, but, really... why
GIT_TR2instead ofGIT_TRACE2?
I did not do anything in response to this. I'll leave that discussion to others.
Thanks,
-Stolee
PATCH v7 3/15 trace2: collect Windows-specific process information
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
Add platform-specific interface to log information about the current process.
On Windows, this interface is used to indicate whether the git process is running under a debugger and list names of the process ancestors.
Information for other platforms is left for a future effort.
--- common-main.c | 1 + compat/win32/trace2_win32_process_info.c | 147 +++++++++++++++++++++++ config.mak.uname | 2 + trace2.h | 14 +++ 4 files changed, 164 insertions(+) create mode 100644 compat/win32/trace2_win32_process_info.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/trace2_win32_process_info.c new file +147 -0
147 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/trace2_win32_process_info.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/trace2_win32_process_info.o \ compat/win32/path-utils.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o
trace2.h +14 -0
@@ -368,4 +368,18 @@ void trace2_printf(const char *fmt, ...);/* clang-format on */ #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 v7 4/15 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 v7 5/15 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 v7 6/15 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 v7 7/15 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 v7 8/15 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 v7 9/15 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 1/1 trace2: fix up a missing "leave" entry point
Fix a trivial bug that's been here since the shared/do_write_index tracing was added in 42fee7a388 ("trace2:data: add trace2 instrumentation to index read/write", 2019-02-22). We should have enter/leave points, not two enter/enter points. This resulted in an "enter" event without a corresponding "leave" event.
--- read-cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
read-cache.c +1 -1
@@ -3131,7 +3131,7 @@ static int write_shared_index(struct index_state *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" , +trace2_region_leave_printf ("index" ,"shared/do_write_index" , the_repository,"%s" , (*temp)->filename.buf);if (ret)
Signature · 1 line
2.21.0.1020.gf2820cf01a
Re: [PATCH] trace2: fix up a missing "leave" entry point
Quoted 17 lines · Ævar Arnfjörð Bjarmason
Fix a trivial bug that's been here since the shared/do_write_index tracing was added in
42fee7a388("trace2:data: add trace2 instrumentation to index read/write", 2019-02-22). We should have enter/leave points, not two enter/enter points. This resulted in an "enter" event without a corresponding "leave" event.Signed-off-by Ævar Arnfjörð Bjarmason <[email protected]>--- read-cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)read-cache.c +0
-0
read-cache.c static int write_shared_index(struct index_state *istate, · line 3137
Quoted 8 lines
read-cache.c +1
-1@@ -3131,7 +3131,7 @@ static int write_shared_index(struct index_state *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" , +trace2_region_leave_printf ("index" ,"shared/do_write_index" , the_repository,"%s" , (*temp)->filename.buf);if (ret)
Thanks! Obviously correct.
-Stolee
Re: [PATCH] trace2: fix up a missing "leave" entry point
In reply to Ævar Arnfjörð BjarmasonQuoted 17 lines · Ævar Arnfjörð Bjarmason
Fix a trivial bug that's been here since the shared/do_write_index tracing was added in
42fee7a388("trace2:data: add trace2 instrumentation to index read/write", 2019-02-22). We should have enter/leave points, not two enter/enter points. This resulted in an "enter" event without a corresponding "leave" event.Signed-off-by Ævar Arnfjörð Bjarmason <[email protected]>--- read-cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)read-cache.c +0
-0
read-cache.c static int write_shared_index(struct index_state *istate, · line 3137
Quoted 8 lines
read-cache.c +1
-1@@ -3131,7 +3131,7 @@ static int write_shared_index(struct index_state *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" , +trace2_region_leave_printf ("index" ,"shared/do_write_index" , the_repository,"%s" , (*temp)->filename.buf);if (ret)
Good catch!
Thanks,
Jeff
PATCH v7 10/15 trace2:data: 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 v7 11/15 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_mode (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_mode ("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_mode ("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 v7 12/15 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_mode ("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_mode ("path" ); +else +trace2_cmd_mode (reset_type_names[reset_type]); +if (reset_type !=SOFT && (reset_type !=MIXED ||get_git_work_tree ()))setup_work_tree ();
Signature · 1 line
gitgitgadget
PATCH v7 13/15 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_mode ("interactive" ); +else if (exec.nr) +trace2_cmd_mode ("interactive-exec" ); +else +trace2_cmd_mode (action_names[action]); + } +switch (action) {case ACTION_CONTINUE : {struct object_id head;
Signature · 1 line
gitgitgadget
PATCH v7 14/15 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 | 236 ++++++++++++++++++++++++++++++++ t/t0212/parse_events.perl | 251 +++++++++++++++++++++++++++++++++++ 10 files changed, 1175 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 +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 +236 -0
236 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 v7 15/15 trace2: add for_each macros to clang-format
In reply to Jeff Hostetler via GitGitGadgetFrom: Jeff Hostetler <[email protected]>
--- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
.clang-format +1 -1
@@ -149,7 +149,7 @@ Cpp11BracedListStyle: false # A list of macros that should be interpreted as foreach loops instead of as # function calls. -ForEachMacros: ['for_each_string_list_item'] +ForEachMacros: ['for_each_string_list_item', 'for_each_wanted_builtin', 'for_each_builtin', 'for_each_ut'] # The maximum number of consecutive empty lines to keep. MaxEmptyLinesToKeep: 1
Signature · 1 line
gitgitgadget
Quoted 20 lines · Jeff Hostetler via GitGitGadget
V3 addresses:
[] incorporate most of the suggestions from clang-format.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@@ -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 @@
Quoted 8 lines
+ struct tr2_tgt *tgt_j;
+ int j;
+
-+ for_each_builtin(j, tgt_j) {
++ for_each_builtin(j, tgt_j)
++ {
+ tgt_j->pfn_term();
+ }Our CodingGuidelines prefer the opening brace on the same line after the if/for/while/struct/etc. statement, and even omitting the braces if the if arm or loop body consists of a single statement. So unfortunately a considerable part of this range diff goes in the wrong direction.
Quoted 30 lines · SZEDER Gábor
Quoted 20 lines · Jeff Hostetler via GitGitGadget
V3 addresses:
[] incorporate most of the suggestions from clang-format.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@@ -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 @@Quoted 8 lines
+ struct tr2_tgt *tgt_j; + int j; + -+ for_each_builtin(j, tgt_j) { ++ for_each_builtin(j, tgt_j) ++ { + tgt_j->pfn_term(); + }
Quoted 5 lines
Our CodingGuidelines prefer the opening brace on the same line after the if/for/while/struct/etc. statement, and even omitting the braces if the if arm or loop body consists of a single statement. So unfortunately a considerable part of this range diff goes in the wrong direction.
I know they do and I had them on the same line originally.
Clang-format was complaining about every use of the for_each_builtin macro, so I changed them to be on the next line to quiet it.
I hesitate to remove braces around a statement adjacent to a for_each macro trick for the usual safety reasons.
Jeff
Quoted 12 lines · Jeff Hostetler
Quoted 11 lines
Quoted 5 lines
-+ for_each_builtin(j, tgt_j) { ++ for_each_builtin(j, tgt_j) ++ { + tgt_j->pfn_term(); + }Our CodingGuidelines prefer the opening brace on the same line after the if/for/while/struct/etc. statement, and even omitting the braces if the if arm or loop body consists of a single statement. So unfortunately a considerable part of this range diff goes in the wrong direction.
Quoted 4 lines
I know they do and I had them on the same line originally.
Clang-format was complaining about every use of the
for_each_builtinmacro, so I changed them to be on the next line to quiet it.
Well, clang-format is wrong then ;-)
Quoted 2 lines
I hesitate to remove braces around a statement adjacent to a
for_eachmacro trick for the usual safety reasons.
Sorry, but what's "usual safety reasons"? Isn't a macro that requires {} in order to work correctly simply broken?
I see (from a previous iteration---sorry, but I haven't caught up)
#define for_each_builtin(j, tgt_j) \
for (j = 0, tgt_j = tr2_tgt_builtins[j]; \
tgt_j; \
j++, tgt_j = tr2_tgt_builtins[j])and I do not think
for (j = 0, tgt_j = ...; tgt_j; j++, tgt_j = ...) statement;
is unsafe (iow, your macro is not broken).
Puzzled.
Quoted 14 lines · Junio C Hamano
Quoted 12 lines · Jeff Hostetler
Quoted 11 lines
Quoted 5 lines
-+ for_each_builtin(j, tgt_j) { ++ for_each_builtin(j, tgt_j) ++ { + tgt_j->pfn_term(); + }Our CodingGuidelines prefer the opening brace on the same line after the if/for/while/struct/etc. statement, and even omitting the braces if the if arm or loop body consists of a single statement. So unfortunately a considerable part of this range diff goes in the wrong direction.
Quoted 6 lines
Quoted 4 lines
I know they do and I had them on the same line originally.
Clang-format was complaining about every use of the
for_each_builtinmacro, so I changed them to be on the next line to quiet it.Well, clang-format is wrong then ;-)
Ok. I had never even heard of clang-format until Josh suggested that it flagged several commits in my series. The last thing I wanted to do was to start hacking up its config file (in the dark) assuming that is even an option.
I can undo my formatting changes if we want to update the settings. I'll give that a try if there are no objections.
Quoted 15 lines
Quoted 3 lines
I hesitate to remove braces around a statement adjacent to a
for_eachmacro trick for the usual safety reasons.Sorry, but what's "usual safety reasons"? Isn't a macro that requires {} in order to work correctly simply broken?
I see (from a previous iteration---sorry, but I haven't caught up)
#define for_each_builtin(j, tgt_j) \ for (j = 0, tgt_j = tr2_tgt_builtins[j]; \ tgt_j; \ j++, tgt_j = tr2_tgt_builtins[j])
Quoted 9 lines
and I do not think
for (j = 0, tgt_j = ...; tgt_j; j++, tgt_j = ...) statement;is unsafe (iow, your macro is not broken).
Puzzled.
Right, I don't think my macro is broken either. It is more my personal paranoia about preventing accidents (like wrapping macro parameters in parens when using them in the macro body).
I can remove them if we want.
Jeff
Quoted 1 line · Jeff Hostetler
...
Quoted 4 lines
I can undo my formatting changes if we want to update the settings. I'll give that a try if there are no objections.
I'll push up a V5 in the morning that adds the foreach macros to the .clang-format and updates the code accordingly. Currently, waiting for the CI builds to finish.
Jeff
Quoted 10 lines · Jeff Hostetler
Quoted 1 line · Jeff Hostetler
...
Quoted 4 lines
I can undo my formatting changes if we want to update the settings. I'll give that a try if there are no objections.
I'll push up a V5 in the morning that adds the foreach macros to the .clang-format and updates the code accordingly.
Wonderful. Thanks for being thorough.
Quoted 3 lines
Currently, waiting for the CI builds to finish.
Jeff