From 450756fe91fbd7d5be9c8abbc47f0d3c0886f065 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 2 Oct 2018 21:55:11 +0200 Subject: [PATCH 01/25] fixup! tests: replace mingw_test_cmp with a helper in C The /dev/null trick does not even work... It makes `git diff --no-index` fail because it tries to access the XDG config as `\\.\GLOBALROOT\Device\Null/.config/git/config`, which is not even a valid path (and therefore the `access_error_is_ok()` check fails). Let's just use a known-missing path on Windows. Signed-off-by: Johannes Schindelin --- t/helper/test-cmp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/t/helper/test-cmp.c b/t/helper/test-cmp.c index 98dd0791520fe0..1c646a54bf609b 100644 --- a/t/helper/test-cmp.c +++ b/t/helper/test-cmp.c @@ -5,6 +5,12 @@ #include "parse-options.h" #include "run-command.h" +#ifdef WIN32 +#define NO_SUCH_DIR "\\\\.\\GLOBALROOT\\invalid" +#else +#define NO_SUCH_DIR "/dev/null" +#endif + static int run_diff(const char *path1, const char *path2) { const char *argv[] = { @@ -12,8 +18,8 @@ static int run_diff(const char *path1, const char *path2) }; const char *env[] = { "GIT_PAGER=cat", - "GIT_DIR=/dev/null", - "HOME=/dev/null", + "GIT_DIR=" NO_SUCH_DIR, + "HOME=" NO_SUCH_DIR, NULL }; From 05eeede60df27e4655c5502aa8d057b754e2cc61 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 2 Oct 2018 22:05:17 +0200 Subject: [PATCH 02/25] t0001: fix on case-insensitive filesystems On a case-insensitive filesystem, such as HFS+ or NTFS, it is possible that the idea Bash has of the current directory differs in case from what Git thinks it is. That's totally okay, though, and we should not expect otherwise. Reported by Jameson Miller. Signed-off-by: Johannes Schindelin --- t/t0001-init.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 7c48ed2aba2843..7f19368fc10b43 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -307,10 +307,20 @@ test_expect_success 'init prefers command line to GIT_DIR' ' test_path_is_missing otherdir/refs ' +downcase_on_case_insensitive_fs () { + test false = "$(git config --get core.filemode)" || return 0 + for f + do + tr A-Z a-z <"$f" >"$f".downcased && + mv -f "$f".downcased "$f" || return 1 + done +} + test_expect_success 'init with separate gitdir' ' rm -rf newdir && git init --separate-git-dir realgitdir newdir && echo "gitdir: $(pwd)/realgitdir" >expected && + downcase_on_case_insensitive_fs expected newdir/.git && test_cmp expected newdir/.git && test_path_is_dir realgitdir/refs ' @@ -365,6 +375,7 @@ test_expect_success 're-init to update git link' ' git init --separate-git-dir ../surrealgitdir ) && echo "gitdir: $(pwd)/surrealgitdir" >expected && + downcase_on_case_insensitive_fs expected newdir/.git && test_cmp expected newdir/.git && test_path_is_dir surrealgitdir/refs && test_path_is_missing realgitdir/refs @@ -378,6 +389,7 @@ test_expect_success 're-init to move gitdir' ' git init --separate-git-dir ../realgitdir ) && echo "gitdir: $(pwd)/realgitdir" >expected && + downcase_on_case_insensitive_fs expected newdir/.git && test_cmp expected newdir/.git && test_path_is_dir realgitdir/refs ' From 4aee99aac2947c34f2061fd1466051922267cafa Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Wed, 17 Oct 2018 11:05:49 -0400 Subject: [PATCH 03/25] trace2: general cleanup/refactoring after v1 * let common-main call trace2_cmd_exit * add subverb event * clarify exec/exec_result events * add trace2_is_enabled() * split cmd_start out of initialize Signed-off-by: Jeff Hostetler --- common-main.c | 11 +++++-- compat/mingw.c | 13 +++++++-- exec-cmd.c | 8 +---- git.c | 14 +++++---- run-command.c | 20 +++++++++++++ t/helper/test-parse-options.c | 3 ++ trace2.c | 55 ++++++++++++++++++++++++++++++----- trace2.h | 46 ++++++++++++++++++++++------- trace2/tr2_tgt.h | 8 +++-- trace2/tr2_tgt_event.c | 23 +++++++++++++-- trace2/tr2_tgt_normal.c | 19 +++++++++--- trace2/tr2_tgt_perf.c | 22 ++++++++++++-- 12 files changed, 196 insertions(+), 46 deletions(-) diff --git a/common-main.c b/common-main.c index bfea45c18d82f3..6dbdc4adf272ef 100644 --- a/common-main.c +++ b/common-main.c @@ -25,6 +25,8 @@ 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 @@ -33,7 +35,8 @@ int main(int argc, const char **argv) sanitize_stdfds(); restore_sigpipe_to_default(); - trace2_initialize(argv); + trace2_initialize(); + trace2_cmd_start(argv); git_resolve_executable_dir(argv[0]); @@ -43,5 +46,9 @@ int main(int argc, const char **argv) attr_start(); - return cmd_main(argc, argv); + result = cmd_main(argc, argv); + + trace2_cmd_exit(result); + + return result; } diff --git a/compat/mingw.c b/compat/mingw.c index ac4c1beecf05a3..4c58534be4c264 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1957,6 +1957,7 @@ 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; #ifndef _MSC_VER const @@ -1966,14 +1967,16 @@ static int try_shell_exec(const char *cmd, char *const *argv) 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, interpr); if (pid >= 0) { int status; if (waitpid(pid, &status, 0) < 0) status = 255; - trace2_exec_result(status); + 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); @@ -1986,13 +1989,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, NULL); - 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(status); + trace2_exec_result(exec_id, status); exit(status); } return -1; diff --git a/exec-cmd.c b/exec-cmd.c index 640003564e611c..7deeab30397cf2 100644 --- a/exec-cmd.c +++ b/exec-cmd.c @@ -330,22 +330,16 @@ const char **prepare_git_cmd(struct argv_array *out, const char **argv) int execv_git_cmd(const char **argv) { struct argv_array nargv = ARGV_ARRAY_INIT; - int err; prepare_git_cmd(&nargv, argv); trace_argv_printf(nargv.argv, "trace: exec:"); - trace2_exec("git", (const char **)nargv.argv); /* execvp() can only ever return if it fails */ sane_execvp("git", (char **)nargv.argv); - err = errno; - trace_printf("trace: exec failed: %s\n", strerror(err)); - trace2_exec_result(err); + trace_printf("trace: exec failed: %s\n", strerror(errno)); argv_array_clear(&nargv); - - errno = err; return -1; } diff --git a/git.c b/git.c index a85c48508e6c48..d378381edeeb0d 100644 --- a/git.c +++ b/git.c @@ -752,21 +752,25 @@ static void execv_dashed_external(const char **argv) if (run_pre_command_hook(cmd.args.argv)) die("pre-command hook aborted command"); + /* + * The code in run_command() logs trace2 child_start/child_exit + * events, so we do not need to report exec/exec_result events here. + */ trace_argv_printf(cmd.args.argv, "trace: exec:"); - trace2_exec(NULL, cmd.args.argv); /* * If we fail because the command is not found, it is * OK to return. Otherwise, we just pass along the status code, * or our usual generic code if we were not even able to exec * the program. - * + */ + exit_code = status = run_command(&cmd); + + /* * If the child process ran and we are now going to exit, emit a * generic string as our trace2 command verb to indicate that we * launched a dashed command. */ - exit_code = status = run_command(&cmd); - trace2_exec_result(status); if (status >= 0) { trace2_cmd_verb("_run_dashed_"); exit(status); @@ -935,5 +939,5 @@ int cmd_main(int argc, const char **argv) fprintf(stderr, _("failed to run command '%s': %s\n"), cmd, strerror(errno)); - return trace2_cmd_exit(1); + return 1; } diff --git a/run-command.c b/run-command.c index 1c5a6c224ecf43..e746afa3dd0a39 100644 --- a/run-command.c +++ b/run-command.c @@ -220,9 +220,29 @@ static int exists_in_PATH(const char *file) int sane_execvp(const char *file, char * const argv[]) { +#ifndef GIT_WINDOWS_NATIVE + /* + * execvp() doesn't return, so we all we can do is tell trace2 + * what we are about to do and let it leave a hint in the log + * (unless of course the execvp() fails). + * + * we skip this for Windows because the compat layer already + * has to emulate the execvp() call anyway. + */ + int exec_id = trace2_exec(file, (const char**)argv); +#endif + if (!execvp(file, argv)) return 0; /* cannot happen ;-) */ +#ifndef GIT_WINDOWS_NATIVE + { + int ec = errno; + trace2_exec_result(exec_id, ec); + errno = ec; + } +#endif + /* * When a command can't be found because one of the directories * listed in $PATH is unsearchable, execvp reports EACCES, but diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index 630c76d1275485..a176235db25589 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -1,6 +1,7 @@ #include "cache.h" #include "parse-options.h" #include "string-list.h" +#include "trace2.h" static int boolean = 0; static int integer = 0; @@ -152,6 +153,8 @@ int cmd_main(int argc, const char **argv) int i; int ret = 0; + trace2_cmd_verb("_parse_"); + argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); if (length_cb.called) { diff --git a/trace2.c b/trace2.c index c9844bc742e597..c977a9ac1e7886 100644 --- a/trace2.c +++ b/trace2.c @@ -15,6 +15,7 @@ static int trace2_enabled; static int tr2_next_child_id; /* modify under lock */ +static int tr2_next_exec_id; /* modify under lock */ static int tr2_next_repo_id = 1; /* modify under lock. zero is reserved */ /* @@ -145,7 +146,7 @@ static void tr2main_signal_handler(int signo) * TODO remove this section header *****************************************************************/ -void trace2_initialize_fl(const char *file, int line, const char **argv) +void trace2_initialize_fl(const char *file, int line) { struct tr2_tgt *tgt_j; int j; @@ -164,11 +165,28 @@ void trace2_initialize_fl(const char *file, int line, const char **argv) tr2tls_init(); /* - * Emit 'version' and 'start' messages on each active builtin target. + * Emit 'version' message on each active builtin target. */ 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) +{ + return trace2_enabled; +} + +void trace2_cmd_start_fl(const char *file, int line, const char **argv) +{ + struct tr2_tgt *tgt_j; + int j; + + if (!trace2_enabled) + return; + + for_each_wanted_builtin(j, tgt_j) { if (tgt_j->pfn_start_fl) tgt_j->pfn_start_fl(file, line, argv); } @@ -246,6 +264,22 @@ void trace2_cmd_verb_fl(const char *file, int line, const char *command_verb) } } +void trace2_cmd_subverb_fl(const char *file, int line, + const char *command_subverb) +{ + struct tr2_tgt *tgt_j; + int j; + + 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); + } +} + void trace2_cmd_alias_fl(const char *file, int line, const char *alias, const char **argv) { @@ -332,28 +366,33 @@ void trace2_child_exit_fl(const char *file, int line, } } -void trace2_exec_fl(const char *file, int line, - const char *exe, const char **argv) +int trace2_exec_fl(const char *file, int line, + const char *exe, const char **argv) { struct tr2_tgt *tgt_j; int j; + int exec_id; uint64_t us_now; uint64_t us_elapsed_absolute; if (!trace2_enabled) - return; + return -1; us_now = getnanotime() / 1000; us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); + exec_id = tr2tls_locked_increment(&tr2_next_exec_id); + for_each_wanted_builtin(j, tgt_j) { if (tgt_j->pfn_exec_fl) tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute, - exe, argv); + exec_id, exe, argv); } + + return exec_id; } -void trace2_exec_result_fl(const char *file, int line, int code) +void trace2_exec_result_fl(const char *file, int line, int exec_id, int code) { struct tr2_tgt *tgt_j; int j; @@ -370,7 +409,7 @@ void trace2_exec_result_fl(const char *file, int line, int code) if (tgt_j->pfn_exec_result_fl) tgt_j->pfn_exec_result_fl(file, line, us_elapsed_absolute, - code); + exec_id, code); } } diff --git a/trace2.h b/trace2.h index 8cb75a57305307..830930ef2ab586 100644 --- a/trace2.h +++ b/trace2.h @@ -18,18 +18,28 @@ struct repository; * [] trace2_printf* -- legacy trace[1] messages. */ - /* * Initialize TRACE2 tracing facility if any of the builtin TRACE2 - * targets are enabled in the environment. Emits 'version' and - * 'start' events. + * targets are enabled in the environment. Emits a 'version' event. * * Cleanup/Termination is handled automatically by a registered * atexit() routine. */ -void trace2_initialize_fl(const char *file, int line, const char **argv); +void trace2_initialize_fl(const char *file, int line); + +#define trace2_initialize() trace2_initialize_fl(__FILE__, __LINE__) + +/* + * Return true if trace2 is enabled. + */ +int trace2_is_enabled(void); + +/* + * Emit a 'start' event with the original (unmodified) argv. + */ +void trace2_cmd_start_fl(const char *file, int line, const char **argv); -#define trace2_initialize(argv) trace2_initialize_fl(__FILE__, __LINE__, argv) +#define trace2_cmd_start(argv) trace2_cmd_start_fl(__FILE__, __LINE__, (argv)) /* * Emit an 'exit' event. @@ -76,6 +86,18 @@ void trace2_cmd_verb_fl(const char *file, int line, const char *command_verb); #define trace2_cmd_verb(v) \ trace2_cmd_verb_fl(__FILE__, __LINE__, (v)) +/* + * Emit a 'cmd_subverb' 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); + +#define trace2_cmd_subverb(sv) \ + trace2_cmd_subverb_fl(__FILE__, __LINE__, (sv)) + /* * Emit an 'alias' expansion event. */ @@ -147,9 +169,11 @@ void trace2_child_exit_fl(const char *file, int line, * last event emitted for the current process, unless the exec * fails. On Windows, exec() behaves like 'child_start' and a * waitpid(), so additional events may be emitted. + * + * Returns the "exec_id". */ -void trace2_exec_fl(const char *file, int line, - const char *exe, const char **argv); +int trace2_exec_fl(const char *file, int line, + const char *exe, const char **argv); #define trace2_exec(exe, argv) \ trace2_exec_fl(__FILE__, __LINE__, (exe), (argv)) @@ -159,11 +183,13 @@ void trace2_exec_fl(const char *file, int line, * this should be called after exec() returns (which only happens * when there is an error starting the new process). On Windows, * this should be called after the waitpid(). + * + * The "exec_id" should be the value returned from trace2_exec(). */ -void trace2_exec_result_fl(const char *file, int line, int code); +void trace2_exec_result_fl(const char *file, int line, int exec_id, int code); -#define trace2_exec_result(code) \ - trace2_exec_result_fl(__FILE__, __LINE__, (code)) +#define trace2_exec_result(id, code) \ + trace2_exec_result_fl(__FILE__, __LINE__, (id), (code)) /* * Emit a 'thread_start' event. This must be called from inside the diff --git a/trace2/tr2_tgt.h b/trace2/tr2_tgt.h index 9d93add2236a69..4e42dd11398999 100644 --- a/trace2/tr2_tgt.h +++ b/trace2/tr2_tgt.h @@ -31,6 +31,8 @@ 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_subverb_fl_t) + (const char *file, int line, const char *command_subverb); typedef void (tr2_tgt_evt_alias_fl_t) (const char *file, int line, const char *alias, const char **argv); @@ -50,9 +52,10 @@ typedef void (tr2_tgt_evt_thread_exit_fl_t) typedef void (tr2_tgt_evt_exec_fl_t) (const char *file, int line, uint64_t us_elapsed_absolute, - const char *exe, const char **argv); + int exec_id, const char *exe, const char **argv); typedef void (tr2_tgt_evt_exec_result_fl_t) - (const char *file, int line, uint64_t us_elapsed_absolute, int code); + (const char *file, int line, uint64_t us_elapsed_absolute, + int exec_id, int code); typedef void (tr2_tgt_evt_param_fl_t) (const char *file, int line, const char *param, const char *value); @@ -97,6 +100,7 @@ struct tr2_tgt 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_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; diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index 9e653d111b9846..7f8b299157dfb4 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -249,6 +249,21 @@ static void fn_command_verb_fl(const char *file, int line, jw_release(&jw); } +static void fn_command_subverb_fl(const char *file, int line, + const char *command_subverb) +{ + const char *event_name = "cmd_subverb"; + 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_end(&jw); + + tr2_dst_write_line(&tr2dst_event, &jw.json); + jw_release(&jw); +} + static void fn_alias_fl(const char *file, int line, const char *alias, const char **argv) { @@ -348,13 +363,14 @@ static void fn_thread_exit_fl(const char *file, int line, static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, - const char *exe, const char **argv) + int exec_id, const char *exe, const char **argv) { const char *event_name = "exec"; struct json_writer jw = JSON_WRITER_INIT; jw_object_begin(&jw, 0); event_fmt_prepare(event_name, file, line, NULL, &jw); + jw_object_intmax(&jw, "exec_id", exec_id); if (exe) jw_object_string(&jw, "exe", exe); jw_object_inline_begin_array(&jw, "argv"); @@ -367,13 +383,15 @@ static void fn_exec_fl(const char *file, int line, } static void fn_exec_result_fl(const char *file, int line, - uint64_t us_elapsed_absolute, int code) + uint64_t us_elapsed_absolute, + int exec_id, int code) { const char *event_name = "exec_result"; struct json_writer jw = JSON_WRITER_INIT; jw_object_begin(&jw, 0); event_fmt_prepare(event_name, file, line, NULL, &jw); + jw_object_intmax(&jw, "exec_id", exec_id); jw_object_intmax(&jw, "code", code); jw_end(&jw); @@ -515,6 +533,7 @@ struct tr2_tgt tr2_tgt_event = fn_error_va_fl, fn_command_path_fl, fn_command_verb_fl, + fn_command_subverb_fl, fn_alias_fl, fn_child_start_fl, fn_child_exit_fl, diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index bab32100630273..7bac5e46d2ebc1 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -178,6 +178,16 @@ static void fn_command_verb_fl(const char *file, int line, strbuf_release(&buf_payload); } +static void fn_command_subverb_fl(const char *file, int line, + const char *command_subverb) +{ + struct strbuf buf_payload = STRBUF_INIT; + + strbuf_addf(&buf_payload, "cmd_subverb %s", command_subverb); + normal_io_write_fl(file, line, &buf_payload); + strbuf_release(&buf_payload); +} + static void fn_alias_fl(const char *file, int line, const char *alias, const char **argv) { @@ -232,11 +242,11 @@ static void fn_child_exit_fl(const char *file, int line, static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, - const char *exe, const char **argv) + int exec_id, const char *exe, const char **argv) { struct strbuf buf_payload = STRBUF_INIT; - strbuf_addstr(&buf_payload, "exec "); + strbuf_addf(&buf_payload, "exec[%d] ", exec_id); if (exe) strbuf_addstr(&buf_payload, exe); sq_quote_argv_pretty(&buf_payload, argv); @@ -246,11 +256,11 @@ static void fn_exec_fl(const char *file, int line, static void fn_exec_result_fl(const char *file, int line, uint64_t us_elapsed_absolute, - int code) + int exec_id, int code) { struct strbuf buf_payload = STRBUF_INIT; - strbuf_addf(&buf_payload, "exec_result code:%d", code); + strbuf_addf(&buf_payload, "exec_result[%d] code:%d", exec_id, code); if (code > 0) strbuf_addf(&buf_payload, " err:%s", strerror(code)); normal_io_write_fl(file, line, &buf_payload); @@ -304,6 +314,7 @@ struct tr2_tgt tr2_tgt_normal = fn_error_va_fl, fn_command_path_fl, fn_command_verb_fl, + fn_command_subverb_fl, fn_alias_fl, fn_child_start_fl, fn_child_exit_fl, diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c index f2b8089a8c8156..bcb271e5ba4c3f 100644 --- a/trace2/tr2_tgt_perf.c +++ b/trace2/tr2_tgt_perf.c @@ -277,6 +277,20 @@ static void fn_command_verb_fl(const char *file, int line, strbuf_release(&buf_payload); } +static void fn_command_subverb_fl(const char *file, int line, + const char *command_subverb) +{ + const char *event_name = "cmd_subverb"; + struct strbuf buf_payload = STRBUF_INIT; + + strbuf_addstr(&buf_payload, command_subverb); + + perf_io_write_fl(file, line, event_name, NULL, + NULL, NULL, NULL, + &buf_payload); + strbuf_release(&buf_payload); +} + static void fn_alias_fl(const char *file, int line, const char *alias, const char **argv) { @@ -362,11 +376,12 @@ static void fn_thread_exit_fl(const char *file, int line, static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, - const char *exe, const char **argv) + int exec_id, const char *exe, const char **argv) { const char *event_name = "exec"; struct strbuf buf_payload = STRBUF_INIT; + strbuf_addf(&buf_payload, "id:%d ", exec_id); strbuf_addstr(&buf_payload, "argv:"); if (exe) strbuf_addf(&buf_payload, " %s", exe); @@ -380,12 +395,12 @@ static void fn_exec_fl(const char *file, int line, static void fn_exec_result_fl(const char *file, int line, uint64_t us_elapsed_absolute, - int code) + int exec_id, int code) { const char *event_name = "exec_result"; struct strbuf buf_payload = STRBUF_INIT; - strbuf_addf(&buf_payload, "code:%d", code); + strbuf_addf(&buf_payload, "id:%d code:%d", exec_id, code); if (code > 0) strbuf_addf(&buf_payload, " err:%s", strerror(code)); @@ -514,6 +529,7 @@ struct tr2_tgt tr2_tgt_perf = fn_error_va_fl, fn_command_path_fl, fn_command_verb_fl, + fn_command_subverb_fl, fn_alias_fl, fn_child_start_fl, fn_child_exit_fl, From 5d414e6f34c9b31aae3b5932c17d972ad1ae10b1 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 26 Nov 2018 07:07:16 -0500 Subject: [PATCH 04/25] trace2: define macro for env var used for SID field Signed-off-by: Jeff Hostetler --- trace2/tr2_sid.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/trace2/tr2_sid.c b/trace2/tr2_sid.c index 7fba400e9766f6..ed0d1161535579 100644 --- a/trace2/tr2_sid.c +++ b/trace2/tr2_sid.c @@ -5,6 +5,8 @@ * This file contains "private/protected" routines for TRACE2. */ +#define TR2_ENVVAR_PARENT_SID "GIT_TR2_PARENT_SID" + static struct strbuf tr2sid_buf = STRBUF_INIT; static int tr2sid_nr_git_parents; @@ -28,7 +30,7 @@ static void tr2_sid_compute(void) if (tr2sid_buf.len) return; - parent_sid = getenv("SLOG_PARENT_SID"); + parent_sid = getenv(TR2_ENVVAR_PARENT_SID); if (parent_sid && *parent_sid) { const char *p; for (p = parent_sid; *p; p++) @@ -44,7 +46,7 @@ static void tr2_sid_compute(void) strbuf_addf(&tr2sid_buf, "%"PRIuMAX"-%"PRIdMAX, (uintmax_t)us_now, (intmax_t)getpid()); - setenv("SLOG_PARENT_SID", tr2sid_buf.buf, 1); + setenv(TR2_ENVVAR_PARENT_SID, tr2sid_buf.buf, 1); } const char *tr2_sid_get(void) From 84ba198c31ebd95757d4250268c63c12b3e45f35 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Fri, 7 Dec 2018 19:25:13 +0000 Subject: [PATCH 05/25] trace2: fix t0001 when trace2 is enabled Trace2 adds GIT_TR2_PARENT_SID and GIT_TR2_PARENT_VERB to the environment for child processes to inherit. Exclude them from the test. Signed-off-by: Jeff Hostetler --- t/t0001-init.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 7f19368fc10b43..530184689fd552 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -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 From 6394d75d57d1909bf72d7df764624d2825756771 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 12 Nov 2018 07:12:22 -0500 Subject: [PATCH 06/25] trace2: use UTC for time fields in event stream rather than localtime Signed-off-by: Jeff Hostetler --- trace2/tr2_tbuf.c | 18 +++++++++++++++++- trace2/tr2_tbuf.h | 5 +++-- trace2/tr2_tgt_event.c | 2 +- trace2/tr2_tgt_normal.c | 2 +- trace2/tr2_tgt_perf.c | 2 +- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/trace2/tr2_tbuf.c b/trace2/tr2_tbuf.c index 98c4c25770adb8..b79adcba371d97 100644 --- a/trace2/tr2_tbuf.c +++ b/trace2/tr2_tbuf.c @@ -1,7 +1,8 @@ #include "cache.h" #include "tr2_tbuf.h" -void tr2_tbuf_current_time(struct tr2_tbuf *tb) + +void tr2_tbuf_local_time(struct tr2_tbuf *tb) { struct timeval tv; struct tm tm; @@ -15,3 +16,18 @@ void tr2_tbuf_current_time(struct tr2_tbuf *tb) 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); +} + diff --git a/trace2/tr2_tbuf.h b/trace2/tr2_tbuf.h index 63a035b6e9fa94..4c5626b7c5027e 100644 --- a/trace2/tr2_tbuf.h +++ b/trace2/tr2_tbuf.h @@ -11,9 +11,10 @@ * argument. */ struct tr2_tbuf { - char buf[24]; + char buf[32]; }; -void tr2_tbuf_current_time(struct tr2_tbuf *tb); +void tr2_tbuf_local_time(struct tr2_tbuf *tb); +void tr2_tbuf_utc_time(struct tr2_tbuf *tb); #endif /* TR2_TBUF_H */ diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index 7f8b299157dfb4..83739ce7ed347d 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -81,7 +81,7 @@ static void event_fmt_prepare(const char *event_name, jw_object_string(jw, "sid", tr2_sid_get()); jw_object_string(jw, "thread", ctx->thread_name.buf); - tr2_tbuf_current_time(&tb_now); + tr2_tbuf_utc_time(&tb_now); jw_object_string(jw, "time", tb_now.buf); if (file && *file) { diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index 7bac5e46d2ebc1..e97077b11c717c 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -54,7 +54,7 @@ static void normal_fmt_prepare(const char *file, int line, struct strbuf *buf) if (!tr2env_normal_bare) { struct tr2_tbuf tb_now; - tr2_tbuf_current_time(&tb_now); + tr2_tbuf_local_time(&tb_now); strbuf_addstr(buf, tb_now.buf); strbuf_addch(buf, ' '); diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c index bcb271e5ba4c3f..277e6d7ead26e5 100644 --- a/trace2/tr2_tgt_perf.c +++ b/trace2/tr2_tgt_perf.c @@ -83,7 +83,7 @@ static void perf_fmt_prepare( struct tr2_tbuf tb_now; - tr2_tbuf_current_time(&tb_now); + tr2_tbuf_local_time(&tb_now); strbuf_addstr(buf, tb_now.buf); strbuf_addch(buf, ' '); From 63180c07fe9b6add830ea8228b88f81806522c62 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 12 Nov 2018 14:24:02 -0500 Subject: [PATCH 07/25] trace2: add data_json event type Signed-off-by: Jeff Hostetler --- trace2.c | 27 +++++++++++++++++++++++++++ trace2.h | 11 +++++++++++ trace2/tr2_tgt.h | 6 ++++++ trace2/tr2_tgt_event.c | 31 +++++++++++++++++++++++++++++++ trace2/tr2_tgt_normal.c | 1 + trace2/tr2_tgt_perf.c | 21 +++++++++++++++++++++ 6 files changed, 97 insertions(+) diff --git a/trace2.c b/trace2.c index c977a9ac1e7886..4e72ce6fb1161f 100644 --- a/trace2.c +++ b/trace2.c @@ -735,6 +735,33 @@ void trace2_data_intmax_fl(const char *file, int line, strbuf_release(&buf_string); } +void trace2_data_json_fl(const char *file, int line, + const char *category, + const struct repository *repo, + const char *key, + const struct json_writer *value) +{ + struct tr2_tgt *tgt_j; + int j; + uint64_t us_now; + uint64_t us_elapsed_absolute; + uint64_t us_elapsed_region; + + if (!trace2_enabled) + return; + + us_now = getnanotime() / 1000; + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); + us_elapsed_region = tr2tls_region_elasped_self(us_now); + + 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, va_list ap) { diff --git a/trace2.h b/trace2.h index 830930ef2ab586..ce89d040b91a50 100644 --- a/trace2.h +++ b/trace2.h @@ -3,6 +3,7 @@ struct child_process; struct repository; +struct json_writer; /* * The public TRACE2 routines are grouped into the following groups: @@ -352,6 +353,16 @@ void trace2_data_intmax_fl(const char *file, int line, trace2_data_intmax_fl( \ __FILE__, __LINE__, (category), (repo), (key), (value)) +void trace2_data_json_fl(const char *file, int line, + const char *category, + const struct repository *repo, + const char *key, + const struct json_writer *jw); + +#define trace2_data_json(category, repo, key, value) \ + trace2_data_json_fl( \ + __FILE__, __LINE__, (category), (repo), (key), (value)) + /* * Emit a 'printf' event. * diff --git a/trace2/tr2_tgt.h b/trace2/tr2_tgt.h index 4e42dd11398999..a90944584dd06e 100644 --- a/trace2/tr2_tgt.h +++ b/trace2/tr2_tgt.h @@ -76,6 +76,11 @@ typedef void (tr2_tgt_evt_data_fl_t) (const char *file, int line, uint64_t us_elapsed_absolute, uint64_t us_elapsed_region, const char *category, const struct repository *repo, const char *key, const char *value); +typedef void (tr2_tgt_evt_data_json_fl_t) + (const char *file, int line, uint64_t us_elapsed_absolute, + uint64_t us_elapsed_region, const char *category, + const struct repository *repo, + const char *key, const struct json_writer *value); typedef void (tr2_tgt_evt_printf_va_fl_t) (const char *file, int line, uint64_t us_elapsed_absolute, @@ -113,6 +118,7 @@ struct tr2_tgt tr2_tgt_evt_region_enter_printf_va_fl_t *pfn_region_enter_printf_va_fl; tr2_tgt_evt_region_leave_printf_va_fl_t *pfn_region_leave_printf_va_fl; tr2_tgt_evt_data_fl_t *pfn_data_fl; + tr2_tgt_evt_data_json_fl_t *pfn_data_json_fl; tr2_tgt_evt_printf_va_fl_t *pfn_printf_va_fl; }; diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index 83739ce7ed347d..9975d535f02046 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -518,6 +518,36 @@ static void fn_data_fl(const char *file, int line, } } +static void fn_data_json_fl(const char *file, int line, + uint64_t us_elapsed_absolute, + uint64_t us_elapsed_region, + const char *category, + const struct repository *repo, + const char *key, + const struct json_writer *value) +{ + const char *event_name = "data_json"; + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); + if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { + struct json_writer jw = JSON_WRITER_INIT; + double t_abs = (double)us_elapsed_absolute / 1000000.0; + double t_rel = (double)us_elapsed_region / 1000000.0; + + jw_object_begin(&jw, 0); + event_fmt_prepare(event_name, file, line, repo, &jw); + jw_object_double(&jw, "t_abs", 6, t_abs); + jw_object_double(&jw, "t_rel", 6, t_rel); + jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); + jw_object_string(&jw, "category", category); + jw_object_string(&jw, "key", key); + jw_object_sub_jw(&jw, "value", value); + jw_end(&jw); + + tr2_dst_write_line(&tr2dst_event, &jw.json); + jw_release(&jw); + } +} + struct tr2_tgt tr2_tgt_event = { &tr2dst_event, @@ -546,5 +576,6 @@ struct tr2_tgt tr2_tgt_event = fn_region_enter_printf_va_fl, fn_region_leave_printf_va_fl, fn_data_fl, + fn_data_json_fl, NULL, /* printf */ }; diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index e97077b11c717c..b5ed6bf82a4b88 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -327,5 +327,6 @@ struct tr2_tgt tr2_tgt_normal = NULL, /* region_enter */ NULL, /* region_leave */ NULL, /* data */ + NULL, /* data_json */ fn_printf_va_fl, }; diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c index 277e6d7ead26e5..8df8728ffeb607 100644 --- a/trace2/tr2_tgt_perf.c +++ b/trace2/tr2_tgt_perf.c @@ -3,6 +3,7 @@ #include "run-command.h" #include "quote.h" #include "version.h" +#include "json-writer.h" #include "trace2/tr2_dst.h" #include "trace2/tr2_sid.h" #include "trace2/tr2_tbuf.h" @@ -499,6 +500,25 @@ static void fn_data_fl(const char *file, int line, strbuf_release(&buf_payload); } +static void fn_data_json_fl(const char *file, int line, + uint64_t us_elapsed_absolute, + uint64_t us_elapsed_region, + const char *category, + const struct repository *repo, + const char *key, + const struct json_writer *value) +{ + const char *event_name = "data_json"; + struct strbuf buf_payload = STRBUF_INIT; + + strbuf_addf(&buf_payload, "%s:%s", key, value->json.buf); + + perf_io_write_fl(file, line, event_name, repo, + &us_elapsed_absolute, &us_elapsed_region, category, + &buf_payload); + strbuf_release(&buf_payload); +} + static void fn_printf_va_fl(const char *file, int line, uint64_t us_elapsed_absolute, const char *fmt, va_list ap) @@ -542,5 +562,6 @@ struct tr2_tgt tr2_tgt_perf = fn_region_enter_printf_va_fl, fn_region_leave_printf_va_fl, fn_data_fl, + fn_data_json_fl, fn_printf_va_fl, }; From a7746dfb59abd3a7846854b6a043402d105ba112 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 19 Nov 2018 15:47:30 -0500 Subject: [PATCH 08/25] trace2: add child-pid to child_exit event Signed-off-by: Jeff Hostetler --- trace2.c | 1 + trace2/tr2_tgt.h | 2 +- trace2/tr2_tgt_event.c | 3 ++- trace2/tr2_tgt_normal.c | 6 +++--- trace2/tr2_tgt_perf.c | 5 +++-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/trace2.c b/trace2.c index 4e72ce6fb1161f..85d05589964d69 100644 --- a/trace2.c +++ b/trace2.c @@ -362,6 +362,7 @@ void trace2_child_exit_fl(const char *file, int line, tgt_j->pfn_child_exit_fl( file, line, us_elapsed_absolute, cmd->trace2_child_id, + cmd->pid, child_exit_code, us_elapsed_child); } } diff --git a/trace2/tr2_tgt.h b/trace2/tr2_tgt.h index a90944584dd06e..f0c81d1cc5653e 100644 --- a/trace2/tr2_tgt.h +++ b/trace2/tr2_tgt.h @@ -42,7 +42,7 @@ typedef void (tr2_tgt_evt_child_start_fl_t) const struct child_process *cmd); typedef void (tr2_tgt_evt_child_exit_fl_t) (const char *file, int line, uint64_t us_elapsed_absolute, int cid, - int code, uint64_t us_elapsed_child); + int pid, int code, uint64_t us_elapsed_child); typedef void (tr2_tgt_evt_thread_start_fl_t) (const char *file, int line, uint64_t us_elapsed_absolute); diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index 9975d535f02046..a75f5c50d416bc 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -311,7 +311,7 @@ static void fn_child_start_fl(const char *file, int line, static void fn_child_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute, - int cid, int code, + int cid, int pid, int code, uint64_t us_elapsed_child) { const char *event_name = "child_exit"; @@ -321,6 +321,7 @@ static void fn_child_exit_fl(const char *file, int line, jw_object_begin(&jw, 0); event_fmt_prepare(event_name, file, line, NULL, &jw); jw_object_intmax(&jw, "child_id", cid); + jw_object_intmax(&jw, "pid", pid); jw_object_intmax(&jw, "code", code); jw_object_double(&jw, "t_rel", 6, t_rel); jw_end(&jw); diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index b5ed6bf82a4b88..1b0a98b9683ced 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -228,14 +228,14 @@ static void fn_child_start_fl(const char *file, int line, static void fn_child_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute, - int cid, int code, + int cid, int pid, int code, uint64_t us_elapsed_child) { struct strbuf buf_payload = STRBUF_INIT; double elapsed = (double)us_elapsed_child / 1000000.0; - strbuf_addf(&buf_payload, "child_exit[%d] code:%d elapsed:%.6f", - cid, code, elapsed); + strbuf_addf(&buf_payload, "child_exit[%d] pid:%d code:%d elapsed:%.6f", + cid, pid, code, elapsed); normal_io_write_fl(file, line, &buf_payload); strbuf_release(&buf_payload); } diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c index 8df8728ffeb607..e3b32993766053 100644 --- a/trace2/tr2_tgt_perf.c +++ b/trace2/tr2_tgt_perf.c @@ -337,12 +337,13 @@ static void fn_child_start_fl(const char *file, int line, static void fn_child_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute, - int cid, int code, uint64_t us_elapsed_child) + int cid, int pid, int code, + uint64_t us_elapsed_child) { const char *event_name = "child_exit"; struct strbuf buf_payload = STRBUF_INIT; - strbuf_addf(&buf_payload, "[ch%d] code:%d", cid, code); + strbuf_addf(&buf_payload, "[ch%d] pid:%d code:%d", cid, pid, code); perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, &us_elapsed_child, NULL, From a08386229e131aacd0adb9a207d718c784c33e55 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 12 Nov 2018 13:54:16 -0500 Subject: [PATCH 09/25] trace2: add optional platform process-info event Add platform-specific "data" events with information about the current process. On Windows, this includes whether git.exe is running under a debugger and information about the process ancestors. This lets post-processors know if git.exe was launched interactively or in the background by an IDE or GVFS daemon, for example. Signed-off-by: Jeff Hostetler --- common-main.c | 1 + compat/win32/ancestry.c | 102 ++++++++++++++++++++++++++++++++++++++++ config.mak.uname | 6 ++- trace2.h | 13 +++++ 4 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 compat/win32/ancestry.c diff --git a/common-main.c b/common-main.c index 6dbdc4adf272ef..d484aec20979ac 100644 --- a/common-main.c +++ b/common-main.c @@ -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]); diff --git a/compat/win32/ancestry.c b/compat/win32/ancestry.c new file mode 100644 index 00000000000000..7ed3cc66618c95 --- /dev/null +++ b/compat/win32/ancestry.c @@ -0,0 +1,102 @@ +#include "../../cache.h" +#include "../../json-writer.h" +#include +#include + +/* + * Find the process data for the given PID in the given snapshot + * and update the PROCESSENTRY32 data. + */ +static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32) +{ + pe32->dwSize = sizeof(PROCESSENTRY32); + + if (Process32First(hSnapshot, pe32)) { + do { + if (pe32->th32ProcessID == pid) + return 1; + } while (Process32Next(hSnapshot, pe32)); + } + return 0; +} + +/* + * Accumulate JSON array: + * [ + * exe-name-parent, + * exe-name-grand-parent, + * ... + * ] + * + * Note: we only report the filename of the process executable; the + * only way to get its full pathname is to use OpenProcess() + * and GetModuleFileNameEx() or QueryfullProcessImageName() + * and that seems rather expensive (on top of the cost of + * getting the snapshot). + */ +static void get_processes(struct json_writer *jw, HANDLE hSnapshot) +{ + PROCESSENTRY32 pe32; + DWORD pid; + + 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); + + pid = pe32.th32ParentProcessID; + } +} + +/* + * Emit JSON data for the current and parent processes. Individual + * trace2 targets can decide how to actually print it. + */ +static void get_ancestry(void) +{ + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (hSnapshot != INVALID_HANDLE_VALUE) { + struct json_writer jw = JSON_WRITER_INIT; + + jw_array_begin(&jw, 0); + get_processes(&jw, hSnapshot); + jw_end(&jw); + + trace2_data_json("process", the_repository, + "windows/ancestry", &jw); + + jw_release(&jw); + CloseHandle(hSnapshot); + } +} + +/* + * Is a debugger attached to the current process? + * + * This will catch debug runs (where the debugger started the process). + * This is the normal case. Since this code is called during our startup, + * it will not report instances where a debugger is attached dynamically + * to a running git process, but that is relatively rare. + */ +static void get_is_being_debugged(void) +{ + if (IsDebuggerPresent()) + trace2_data_intmax("process", the_repository, + "windows/debugger_present", 1); +} + +void trace2_collect_process_info(void) +{ + if (!trace2_is_enabled()) + return; + + get_is_being_debugged(); + get_ancestry(); +} diff --git a/config.mak.uname b/config.mak.uname index fded5c253012bf..fa399e14b454e6 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -427,7 +427,8 @@ ifeq ($(uname_S),Windows) BASIC_CFLAGS = -nologo -I. -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/dirent.o compat/win32/fscache.o + compat/win32/dirent.o compat/win32/fscache.o \ + compat/win32/ancestry.o COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DDETECT_MSYS_TTY -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE # invalidcontinue.obj allows Git's source code to close the same file @@ -591,7 +592,8 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ - compat/win32/dirent.o compat/win32/fscache.o + compat/win32/dirent.o compat/win32/fscache.o \ + compat/win32/ancestry.o BASIC_CFLAGS += -DWIN32 -DPROTECT_NTFS_DEFAULT=1 EXTLIBS += -lws2_32 GITLIBS += git.res diff --git a/trace2.h b/trace2.h index ce89d040b91a50..1909d9d60394c9 100644 --- a/trace2.h +++ b/trace2.h @@ -387,4 +387,17 @@ __attribute__((format (printf, 1, 2))) void trace2_printf(const char *fmt, ...); #endif +/* + * Optional platform-specific code to dump information about the + * current and any parent process(es). This is intended to allow + * post-processors to know who spawned this git instance and anything + * else the platform may be able to tell us about the current process. + */ +#if defined(GIT_WINDOWS_NATIVE) +void trace2_collect_process_info(void); +#else +#define trace2_collect_process_info() \ + do {} while (0) +#endif + #endif /* TRACE2_H */ From d90532199e3655a76a9624f667fee8a62661cfb0 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Thu, 11 Oct 2018 16:35:36 -0400 Subject: [PATCH 10/25] trace2: t/helper/test-trace2 Signed-off-by: Jeff Hostetler --- 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 | 141 ++++++++++++++++++++ t/t0210/scrub_normal.perl | 42 ++++++ t/t0211-trace2-perf.sh | 162 ++++++++++++++++++++++ t/t0211/scrub_perf.perl | 71 ++++++++++ t/t0212-trace2-event.sh | 237 +++++++++++++++++++++++++++++++++ t/t0212/parse_events.perl | 245 ++++++++++++++++++++++++++++++++++ 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 diff --git a/Makefile b/Makefile index b8b0942d96ab8c..bd1e55d8a87f9d 100644 --- a/Makefile +++ b/Makefile @@ -740,6 +740,7 @@ TEST_BUILTINS_OBJS += test-strcmp-offset.o TEST_BUILTINS_OBJS += test-string-list.o TEST_BUILTINS_OBJS += test-submodule-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 diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index 1ac1e29d8a2851..e5342b1422464c 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -45,6 +45,7 @@ static struct test_cmd cmds[] = { { "string-list", cmd__string_list }, { "submodule-config", cmd__submodule_config }, { "subprocess", cmd__subprocess }, + { "trace2", cmd__trace2 }, { "urlmatch-normalization", cmd__urlmatch_normalization }, { "wildmatch", cmd__wildmatch }, #ifdef GIT_WINDOWS_NATIVE diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 55a90fbd12a1c8..12b99f367a3072 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -40,6 +40,7 @@ int cmd__strcmp_offset(int argc, const char **argv); int cmd__string_list(int argc, const char **argv); int cmd__submodule_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 diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c new file mode 100644 index 00000000000000..b867f58d32cb36 --- /dev/null +++ b/t/helper/test-trace2.c @@ -0,0 +1,273 @@ +#include "test-tool.h" +#include "cache.h" +#include "argv-array.h" +#include "run-command.h" +#include "exec-cmd.h" +#include "config.h" + +typedef int (fn_unit_test)(int argc, const char **argv); + +struct unit_test +{ + fn_unit_test *ut_fn; + const char *ut_name; + const char *ut_usage; +}; + +#define MyOk 0 +#define MyError 1 + +static int get_i(int *p_value, const char *data) +{ + char *endptr; + + if (!data || !*data) + return MyError; + + *p_value = strtol(data, &endptr, 10); + if (*endptr || errno == ERANGE) + return MyError; + + return MyOk; +} + +/* + * Cause process to exit with the requested value via "return". + * + * Rely on test-tool.c:cmd_main() to call trace2_cmd_exit() + * with our result. + * + * Test harness can confirm: + * [] 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. + * [] "def_param" events for all of the "interesting" pre-defined config settings. + */ +static int ut_001return(int argc, const char **argv) +{ + int rc; + + if (get_i(&rc, argv[0])) + die("expect "); + + return rc; +} + +/* + * Cause the process to exit with the requested value via "exit()". + * + * 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. + * [] "def_param" events for all of the "interesting" pre-defined config settings. + */ +static int ut_002exit(int argc, const char **argv) +{ + int rc; + + if (get_i(&rc, argv[0])) + die("expect "); + + exit(rc); +} + +/* + * Send an "error" event with each value in argv. Normally, git only issues + * a single "error" event immediately before issuing an "exit" event (such + * as in die() or BUG()), but multiple "error" events are allowed. + * + * Test harness can confirm: + * [] a trace2 "error" event for each value in argv. + * [] the "name" field in the "cmd_verb" trace2 event. + * [] (optional) the file:line in the "exit" event refers to this function. + */ +static int ut_003error(int argc, const char **argv) +{ + int k; + + if (!argv[0] || !*argv[0]) + die("expect "); + + for (k = 0; k < argc; k++) + error("%s", argv[k]); + + return 0; +} + +/* + * Run a child process and wait for it to finish and exit with its return code. + * test-tool trace2 004child [] + * + * For example: + * test-tool trace2 004child git version + * test-tool trace2 004child test-tool trace2 001return 0 + * test-tool trace2 004child test-tool trace2 004child test-tool trace2 004child + * test-tool trace2 004child git -c alias.xyz=version xyz + * + * Test harness can confirm: + * [] the "name" field in the "cmd_verb" 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. + * [] if the child process is an instrumented executable: + * [] that "version", "start", ..., "exit", and "atexit" events are + * generated by the child process. + * [] that the child process events have a multiple component SID (or + * depth "dN+1" in the PERF stream). + * [] that the child exit code is propagated to the parent process "exit" + * and "atexit" events.. + * [] (optional) that the "t_abs" field in the child process "atexit" event + * is less than the "t_rel" field in the "child_exit" event of the parent + * process. + * [] if the child process is like the alias example above, + * [] (optional) the child process attempts to run "git-xyx" as a dashed + * command. + * [] the child process emits an "alias" event with "xyz" => "version" + * [] the child process runs "git version" as a child process. + * [] the child process has a 3 component SID (or depth "d2" in the PERF + * stream). + */ +static int ut_004child(int argc, const char **argv) +{ + int result; + + /* + * Allow empty so we can do arbitrarily deep + * command nesting and let the last one be null. + */ + if (!argc) + return 0; + + result = run_command_v_opt(argv, 0); + exit(result); +} + +/* + * Exec a git command. This may either create a child process (Windows) + * or replace the existing process. + * test-tool trace2 005exec + * + * For example: + * test-tool trace2 005exec version + * + * Test harness can confirm (on Windows): + * [] the "name" field in the "cmd_verb" 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 + * process (since the Windows compatibility layer fakes an exec() with + * a CreateProcess(), WaitForSingleObject(), and exit()). + * [] that the child process has multiple component SID (or depth "dN+1" + * in the PERF stream). + * + * Test harness can confirm (on platforms with a real exec() function): + * [] TODO talk about process replacement and how it affects SID. + */ +static int ut_005exec(int argc, const char **argv) +{ + int result; + + if (!argc) + return 0; + + result = execv_git_cmd(argv); + return result; +} + +static int ut_006data(int argc, const char **argv) +{ + const char *usage_error = + "expect [ [...]]"; + + if (argc % 3 != 0) + die("%s", usage_error); + + while (argc) { + if (!argv[0] || !*argv[0] || + !argv[1] || !*argv[1] || + !argv[2] || !*argv[2]) + die("%s", usage_error); + + trace2_data_string(argv[0], the_repository, argv[1], argv[2]); + argv += 3; + argc -= 3; + } + + return 0; +} + +/* + * Usage: + * test-tool trace2 + * test-tool trace2 + * ... + */ +#define USAGE_PREFIX "test-tool trace2" + +static struct unit_test ut_table[] = { + { ut_001return, "001return", "" }, + { ut_002exit, "002exit", "" }, + { ut_003error, "003error", "+" }, + { ut_004child, "004child", "[]" }, + { ut_005exec, "005exec", "" }, + { ut_006data, "006data", "[ ]+" }, +}; + +#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]) + +static int print_usage(void) +{ + int k; + struct unit_test *ut_k; + + fprintf(stderr, "usage:\n"); + for_each_ut(k, ut_k) { + fprintf(stderr, "\t%s %s %s\n", + USAGE_PREFIX, ut_k->ut_name, ut_k->ut_usage); + } + + return 129; +} + +/* + * Issue various trace2 events for testing. + * + * 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()] + * [] 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. + * [] this writes various "def_param" events for interesting config values. + * + * We further assume that if we return (rather than exit()), trace2_cmd_exit() + * will be called by test-tool.c:cmd_main(). + */ +int cmd__trace2(int argc, const char **argv) +{ + int k; + struct unit_test *ut_k; + + argc--; /* skip over "trace2" arg */ + argv++; + + 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(); +} + diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh new file mode 100755 index 00000000000000..985fbd710420fe --- /dev/null +++ b/t/t0210-trace2-normal.sh @@ -0,0 +1,141 @@ +#!/bin/sh + +test_description='test trace2 facility (normal target)' +. ./test-lib.sh + +# 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. +TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR +PATH="$TTDIR:$PATH" && export PATH + +TT="test-tool" && export TT + +# Warning: use of 'test_cmp' may run test-tool.exe and/or git.exe +# Warning: to do the actual diff/comparison, so the HEREDOCs here +# Warning: only cover our actual calls to test-tool and/or git. +# Warning: So you may see extra lines in artifact files when +# Warning: interactively debugging. + +# Turn off any inherited trace2 settings for this test. +unset GIT_TR2 GIT_TR2_PERF GIT_TR2_EVENT +unset GIT_TR2_BARE +unset GIT_TR2_CONFIG_PARAMS + +V=$(git version | sed -e 's/^git version //') && export V + +# There are multiple trace2 targets: normal, perf, and event. +# Trace2 events will/can be written to each active target (subject +# to whatever filtering that target decides to do). +# This script tests the normal target in isolation. + +# Enable "normal" trace2 target. +GIT_TR2="$(pwd)/trace.normal" && export GIT_TR2 +# Enable "bare" feature which turns off " : " prefix. +GIT_TR2_BARE=1 && export GIT_TR2_BARE + +# Basic tests of the trace2 normal stream. Since this stream is used +# primarily with printf-style debugging/tracing, we do limited testing +# here. +# +# We do confirm the following API features: +# [] the 'version ' event +# [] the 'start ' event +# [] the 'cmd_path ' event +# [] the 'cmd_verb ' event +# [] the 'exit