From 1423f908a9ba5b92cdc1d679cf7de4b79a0feef9 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Wed, 17 Oct 2018 11:05:49 -0400 Subject: [PATCH 01/23] 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 53ad239cf03faa..c695e5a5a07c91 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1945,6 +1945,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 @@ -1954,14 +1955,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); @@ -1974,13 +1977,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 1ac52320a424a1..fff26d3d9e9019 100644 --- a/git.c +++ b/git.c @@ -760,21 +760,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); @@ -965,5 +969,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 d51250dbf7b339..67d2fb14eaea1e 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 47fee660b86a6e..5318681739d9c6 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -2,6 +2,7 @@ #include "cache.h" #include "parse-options.h" #include "string-list.h" +#include "trace2.h" static int boolean = 0; static int integer = 0; @@ -153,6 +154,8 @@ int cmd__parse_options(int argc, const char **argv) int i; int ret = 0; + trace2_cmd_verb("_parse_"); + argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); if (length_cb.called) { 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 2d6541481477e822369538ce6714cbb5c0a42669 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 26 Nov 2018 07:07:16 -0500 Subject: [PATCH 02/23] 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 62fe2f28032fc7a57852aca0010e11c6f7a5b54a Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Fri, 7 Dec 2018 19:25:13 +0000 Subject: [PATCH 03/23] 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 a6c5fe9e1475ae..d13aa011e00431 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 67c7bcee27ceb9f07bb72495cf4c42e4731987e9 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 12 Nov 2018 07:12:22 -0500 Subject: [PATCH 04/23] 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 72fb12bece4217653dd685156e894b85a9f64239 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 12 Nov 2018 14:24:02 -0500 Subject: [PATCH 05/23] 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 ab6ffefe68630e5eb2c505678520a5939d904da5 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 19 Nov 2018 15:47:30 -0500 Subject: [PATCH 06/23] 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 e20723514e080e20fa2394420599c65f5a910bc5 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Mon, 12 Nov 2018 13:54:16 -0500 Subject: [PATCH 07/23] 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 c329b2d1204095..7d8289a261f7e4 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -424,7 +424,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 -ENTRY:wmainCRTStartup -SUBSYSTEM:CONSOLE # invalidcontinue.obj allows Git's source code to close the same file @@ -586,7 +587,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 91c27d9bf2b948dbf6601b174984f74f6be8f990 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Thu, 11 Oct 2018 16:35:36 -0400 Subject: [PATCH 08/23] 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 9e2a349c9106ef..347728adaa1d2e 100644 --- a/Makefile +++ b/Makefile @@ -756,6 +756,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 diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index cfb46a720ea919..da2301e5dd0b0c 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -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 diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 955490ec9f29f1..07e8fa174ee183 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -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 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