From 0c1c3c861eacc7e27e627c80cc081ec7c00c743d Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Thu, 7 Mar 2024 15:22:27 +0000 Subject: [PATCH 1/3] t0211: demonstrate missing 'def_param' events for certain commands Some Git commands fail to emit 'def_param' events for interesting config and environment variable settings. Add unit tests to demonstrate this. Most commands are considered "builtin" and are based upon git.c. These typically do emit 'def_param' events. Exceptions are some of the "query" commands, the "run-dashed" mechanism, and alias handling. Commands built from remote-curl.c (instead of git.c), such as "git-remote-https", do not emit 'def_param' events. Likewise, "git-http-fetch" is built http-fetch.c and does not emit them. Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- t/t0211-trace2-perf.sh | 231 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh index 290b6eaaab..588c5bad03 100755 --- a/t/t0211-trace2-perf.sh +++ b/t/t0211-trace2-perf.sh @@ -287,4 +287,235 @@ test_expect_success 'unsafe URLs are redacted by default' ' grep "d0|main|def_param|.*|remote.origin.url:https://user:pwd@example.com" actual ' +# Confirm that the requested command produces a "cmd_name" and a +# set of "def_param" events. +# +try_simple () { + test_when_finished "rm prop.perf actual" && + + cmd=$1 && + cmd_name=$2 && + + test_config_global "trace2.configParams" "cfg.prop.*" && + test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" && + + test_config_global "cfg.prop.foo" "red" && + + ENV_PROP_FOO=blue \ + GIT_TRACE2_PERF="$(pwd)/prop.perf" \ + $cmd && + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" actual && + grep "d0|main|cmd_name|.*|$cmd_name" actual && + grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual +} + +# Representative mainstream builtin Git command dispatched +# in run_builtin() in git.c +# +test_expect_success 'expect def_params for normal builtin command' ' + try_simple "git version" "version" +' + +# Representative query command dispatched in handle_options() +# in git.c +# +test_expect_failure 'expect def_params for query command' ' + try_simple "git --man-path" "_query_" +' + +# remote-curl.c does not use the builtin setup in git.c, so confirm +# that executables built from remote-curl.c emit def_params. +# +# Also tests the dashed-command handling where "git foo" silently +# spawns "git-foo". Make sure that both commands should emit +# def_params. +# +# Pass bogus arguments to remote-https and allow the command to fail +# because we don't actually have a remote to fetch from. We just want +# to see the run-dashed code run an executable built from +# remote-curl.c rather than git.c. Confirm that we get def_param +# events from both layers. +# +test_expect_failure 'expect def_params for remote-curl and _run_dashed_' ' + test_when_finished "rm prop.perf actual" && + + test_config_global "trace2.configParams" "cfg.prop.*" && + test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" && + + test_config_global "cfg.prop.foo" "red" && + + test_might_fail env \ + ENV_PROP_FOO=blue \ + GIT_TRACE2_PERF="$(pwd)/prop.perf" \ + git remote-http x y && + + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" actual && + + grep "d0|main|cmd_name|.*|_run_dashed_" actual && + grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + + grep "d1|main|cmd_name|.*|remote-curl" actual && + grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual +' + +# Similarly, `git-http-fetch` is not built from git.c so do a +# trivial fetch so that the main git.c run-dashed code spawns +# an executable built from http-fetch.c. Confirm that we get +# def_param events from both layers. +# +test_expect_failure 'expect def_params for http-fetch and _run_dashed_' ' + test_when_finished "rm prop.perf actual" && + + test_config_global "trace2.configParams" "cfg.prop.*" && + test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" && + + test_config_global "cfg.prop.foo" "red" && + + test_might_fail env \ + ENV_PROP_FOO=blue \ + GIT_TRACE2_PERF="$(pwd)/prop.perf" \ + git http-fetch --stdin file:/// <<-EOF && + EOF + + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" actual && + + grep "d0|main|cmd_name|.*|_run_dashed_" actual && + grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + + grep "d1|main|cmd_name|.*|http-fetch" actual && + grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual +' + +# Historically, alias expansion explicitly emitted the def_param +# events (independent of whether the command was a builtin, a Git +# command or arbitrary shell command) so that it wasn't dependent +# upon the unpeeling of the alias. Let's make sure that we preserve +# the net effect. +# +test_expect_success 'expect def_params during git alias expansion' ' + test_when_finished "rm prop.perf actual" && + + test_config_global "trace2.configParams" "cfg.prop.*" && + test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" && + + test_config_global "cfg.prop.foo" "red" && + + test_config_global "alias.xxx" "version" && + + ENV_PROP_FOO=blue \ + GIT_TRACE2_PERF="$(pwd)/prop.perf" \ + git xxx && + + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" actual && + + # "git xxx" is first mapped to "git-xxx" and the child will fail. + grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && + + # We unpeel that and substitute "version" into "xxx" (giving + # "git version") and update the cmd_name event. + grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_git_alias_)" actual && + + # These def_param events could be associated with either of the + # above cmd_name events. It does not matter. + grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + + # The "git version" child sees a different cmd_name hierarchy. + # Also test the def_param (only for completeness). + grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_git_alias_/version)" actual && + grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual +' + +test_expect_success 'expect def_params during shell alias expansion' ' + test_when_finished "rm prop.perf actual" && + + test_config_global "trace2.configParams" "cfg.prop.*" && + test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" && + + test_config_global "cfg.prop.foo" "red" && + + test_config_global "alias.xxx" "!git version" && + + ENV_PROP_FOO=blue \ + GIT_TRACE2_PERF="$(pwd)/prop.perf" \ + git xxx && + + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" actual && + + # "git xxx" is first mapped to "git-xxx" and the child will fail. + grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && + + # We unpeel that and substitute "git version" for "git xxx" (as a + # shell command. Another cmd_name event is emitted as we unpeel. + grep "d0|main|cmd_name|.*|_run_shell_alias_ (_run_dashed_/_run_shell_alias_)" actual && + + # These def_param events could be associated with either of the + # above cmd_name events. It does not matter. + grep "d0|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + + # We get the following only because we used a git command for the + # shell command. In general, it could have been a shell script and + # we would see nothing. + # + # The child knows the cmd_name hierarchy so it includes it. + grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_shell_alias_/version)" actual && + grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual +' + +test_expect_failure 'expect def_params during nested git alias expansion' ' + test_when_finished "rm prop.perf actual" && + + test_config_global "trace2.configParams" "cfg.prop.*" && + test_config_global "trace2.envvars" "ENV_PROP_FOO,ENV_PROP_BAR" && + + test_config_global "cfg.prop.foo" "red" && + + test_config_global "alias.xxx" "yyy" && + test_config_global "alias.yyy" "version" && + + ENV_PROP_FOO=blue \ + GIT_TRACE2_PERF="$(pwd)/prop.perf" \ + git xxx && + + perl "$TEST_DIRECTORY/t0211/scrub_perf.perl" actual && + + # "git xxx" is first mapped to "git-xxx" and try to spawn "git-xxx" + # and the child will fail. + grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_)" actual && + grep "d0|main|child_start|.*|.* class:dashed argv:\[git-xxx\]" actual && + + # We unpeel that and substitute "yyy" into "xxx" (giving "git yyy") + # and spawn "git-yyy" and the child will fail. + grep "d0|main|alias|.*|alias:xxx argv:\[yyy\]" actual && + grep "d0|main|cmd_name|.*|_run_dashed_ (_run_dashed_/_run_dashed_)" actual && + grep "d0|main|child_start|.*|.* class:dashed argv:\[git-yyy\]" actual && + + # We unpeel that and substitute "version" into "xxx" (giving + # "git version") and update the cmd_name event. + grep "d0|main|alias|.*|alias:yyy argv:\[version\]" actual && + grep "d0|main|cmd_name|.*|_run_git_alias_ (_run_dashed_/_run_dashed_/_run_git_alias_)" actual && + + # These def_param events could be associated with any of the + # above cmd_name events. It does not matter. + grep "d0|main|def_param|.*|cfg.prop.foo:red" actual >actual.matches && + grep "d0|main|def_param|.*|ENV_PROP_FOO:blue" actual && + + # However, we do not want them repeated each time we unpeel. + test_line_count = 1 actual.matches && + + # The "git version" child sees a different cmd_name hierarchy. + # Also test the def_param (only for completeness). + grep "d1|main|cmd_name|.*|version (_run_dashed_/_run_dashed_/_run_git_alias_/version)" actual && + grep "d1|main|def_param|.*|cfg.prop.foo:red" actual && + grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual +' + test_done From 520cf668149d43820a25869dc5f2eb7ff2ac5687 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Thu, 7 Mar 2024 15:22:28 +0000 Subject: [PATCH 2/3] trace2: avoid emitting 'def_param' set more than once During nested alias expansion it is possible for "trace2_cmd_list_config()" and "trace2_cmd_list_env_vars()" to be called more than once. This causes a full set of 'def_param' events to be emitted each time. Let's avoid that. Add code to those two functions to only emit them once. Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- t/t0211-trace2-perf.sh | 2 +- trace2.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh index 588c5bad03..7b35319539 100755 --- a/t/t0211-trace2-perf.sh +++ b/t/t0211-trace2-perf.sh @@ -470,7 +470,7 @@ test_expect_success 'expect def_params during shell alias expansion' ' grep "d1|main|def_param|.*|ENV_PROP_FOO:blue" actual ' -test_expect_failure 'expect def_params during nested git alias expansion' ' +test_expect_success 'expect def_params during nested git alias expansion' ' test_when_finished "rm prop.perf actual" && test_config_global "trace2.configParams" "cfg.prop.*" && diff --git a/trace2.c b/trace2.c index f1e268bd15..facce641ef 100644 --- a/trace2.c +++ b/trace2.c @@ -464,17 +464,29 @@ void trace2_cmd_alias_fl(const char *file, int line, const char *alias, void trace2_cmd_list_config_fl(const char *file, int line) { + static int emitted = 0; + if (!trace2_enabled) return; + if (emitted) + return; + emitted = 1; + tr2_cfg_list_config_fl(file, line); } void trace2_cmd_list_env_vars_fl(const char *file, int line) { + static int emitted = 0; + if (!trace2_enabled) return; + if (emitted) + return; + emitted = 1; + tr2_list_env_vars_fl(file, line); } From 6111252cbf21abb175411da5c5a2cde65bb8f3e9 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Thu, 7 Mar 2024 15:22:29 +0000 Subject: [PATCH 3/3] trace2: emit 'def_param' set with 'cmd_name' event Some commands do not cause a set of 'def_param' events to be emitted. This includes "git-remote-https", "git-http-fetch", and various "query" commands, like "git --man-path". Since all of these commands do emit a 'cmd_name' event, add code to the "trace2_cmd_name()" function to generate the set of 'def_param' events. Remove explicit calls to "trace2_cmd_list_config()" and "trace2_cmd_list_env_vars()" in git.c since they are no longer needed. Reviewed-by: Josh Steadmon Signed-off-by: Jeff Hostetler Signed-off-by: Junio C Hamano --- git.c | 6 ------ t/t0211-trace2-perf.sh | 6 +++--- trace2.c | 3 +++ 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/git.c b/git.c index 7068a184b0..a769d72ab8 100644 --- a/git.c +++ b/git.c @@ -373,8 +373,6 @@ static int handle_alias(int *argcp, const char ***argv) strvec_pushv(&child.args, (*argv) + 1); trace2_cmd_alias(alias_command, child.args.v); - trace2_cmd_list_config(); - trace2_cmd_list_env_vars(); trace2_cmd_name("_run_shell_alias_"); ret = run_command(&child); @@ -411,8 +409,6 @@ static int handle_alias(int *argcp, const char ***argv) COPY_ARRAY(new_argv + count, *argv + 1, *argcp); trace2_cmd_alias(alias_command, new_argv); - trace2_cmd_list_config(); - trace2_cmd_list_env_vars(); *argv = new_argv; *argcp += count - 1; @@ -462,8 +458,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) trace_argv_printf(argv, "trace: built-in: git"); trace2_cmd_name(p->cmd); - trace2_cmd_list_config(); - trace2_cmd_list_env_vars(); validate_cache_entries(the_repository->index); status = p->fn(argc, argv, prefix); diff --git a/t/t0211-trace2-perf.sh b/t/t0211-trace2-perf.sh index 7b35319539..13ef69b92f 100755 --- a/t/t0211-trace2-perf.sh +++ b/t/t0211-trace2-perf.sh @@ -320,7 +320,7 @@ test_expect_success 'expect def_params for normal builtin command' ' # Representative query command dispatched in handle_options() # in git.c # -test_expect_failure 'expect def_params for query command' ' +test_expect_success 'expect def_params for query command' ' try_simple "git --man-path" "_query_" ' @@ -337,7 +337,7 @@ test_expect_failure 'expect def_params for query command' ' # remote-curl.c rather than git.c. Confirm that we get def_param # events from both layers. # -test_expect_failure 'expect def_params for remote-curl and _run_dashed_' ' +test_expect_success 'expect def_params for remote-curl and _run_dashed_' ' test_when_finished "rm prop.perf actual" && test_config_global "trace2.configParams" "cfg.prop.*" && @@ -366,7 +366,7 @@ test_expect_failure 'expect def_params for remote-curl and _run_dashed_' ' # an executable built from http-fetch.c. Confirm that we get # def_param events from both layers. # -test_expect_failure 'expect def_params for http-fetch and _run_dashed_' ' +test_expect_success 'expect def_params for http-fetch and _run_dashed_' ' test_when_finished "rm prop.perf actual" && test_config_global "trace2.configParams" "cfg.prop.*" && diff --git a/trace2.c b/trace2.c index facce641ef..f894532d05 100644 --- a/trace2.c +++ b/trace2.c @@ -433,6 +433,9 @@ void trace2_cmd_name_fl(const char *file, int line, const char *name) for_each_wanted_builtin (j, tgt_j) if (tgt_j->pfn_command_name_fl) tgt_j->pfn_command_name_fl(file, line, name, hierarchy); + + trace2_cmd_list_config(); + trace2_cmd_list_env_vars(); } void trace2_cmd_mode_fl(const char *file, int line, const char *mode)