Git fork

trace2: create new combined trace facility

Create a new unified tracing facility for git. The eventual intent is to
replace the current trace_printf* and trace_performance* routines with a
unified set of git_trace2* routines.

In addition to the usual printf-style API, trace2 provides higer-level
event verbs with fixed-fields allowing structured data to be written.
This makes post-processing and analysis easier for external tools.

Trace2 defines 3 output targets. These are set using the environment
variables "GIT_TR2", "GIT_TR2_PERF", and "GIT_TR2_EVENT". These may be
set to "1" or to an absolute pathname (just like the current GIT_TRACE).

* GIT_TR2 is intended to be a replacement for GIT_TRACE and logs command
summary data.

* GIT_TR2_PERF is intended as a replacement for GIT_TRACE_PERFORMANCE.
It extends the output with columns for the command process, thread,
repo, absolute and relative elapsed times. It reports events for
child process start/stop, thread start/stop, and per-thread function
nesting.

* GIT_TR2_EVENT is a new structured format. It writes event data as a
series of JSON records.

Calls to trace2 functions log to any of the 3 output targets enabled
without the need to call different trace_printf* or trace_performance*
routines.

Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Jeff Hostetler and committed by
Junio C Hamano
ee4512ed e544221d

+3806 -18
+13 -1
Makefile
··· 1017 1017 LIB_OBJS += thread-utils.o 1018 1018 LIB_OBJS += tmp-objdir.o 1019 1019 LIB_OBJS += trace.o 1020 + LIB_OBJS += trace2.o 1021 + LIB_OBJS += trace2/tr2_cfg.o 1022 + LIB_OBJS += trace2/tr2_cmd_name.o 1023 + LIB_OBJS += trace2/tr2_dst.o 1024 + LIB_OBJS += trace2/tr2_sid.o 1025 + LIB_OBJS += trace2/tr2_tbuf.o 1026 + LIB_OBJS += trace2/tr2_tgt_event.o 1027 + LIB_OBJS += trace2/tr2_tgt_normal.o 1028 + LIB_OBJS += trace2/tr2_tgt_perf.o 1029 + LIB_OBJS += trace2/tr2_tls.o 1020 1030 LIB_OBJS += trailer.o 1021 1031 LIB_OBJS += transport.o 1022 1032 LIB_OBJS += transport-helper.o ··· 1596 1606 LIB_OBJS += compat/inet_pton.o 1597 1607 BASIC_CFLAGS += -DNO_INET_PTON 1598 1608 endif 1599 - ifndef NO_UNIX_SOCKETS 1609 + ifdef NO_UNIX_SOCKETS 1610 + BASIC_CFLAGS += -DNO_UNIX_SOCKETS 1611 + else 1600 1612 LIB_OBJS += unix-socket.o 1601 1613 PROGRAM_OBJS += credential-cache.o 1602 1614 PROGRAM_OBJS += credential-cache--daemon.o
+4 -5
builtin/submodule--helper.c
··· 1816 1816 { 1817 1817 int i; 1818 1818 1819 - run_processes_parallel(suc->max_jobs, 1820 - update_clone_get_next_task, 1821 - update_clone_start_failure, 1822 - update_clone_task_finished, 1823 - suc); 1819 + run_processes_parallel_tr2(suc->max_jobs, update_clone_get_next_task, 1820 + update_clone_start_failure, 1821 + update_clone_task_finished, suc, "submodule", 1822 + "parallel/update"); 1824 1823 1825 1824 /* 1826 1825 * We saved the output and put it out all at once now.
+1
cache.h
··· 9 9 #include "gettext.h" 10 10 #include "convert.h" 11 11 #include "trace.h" 12 + #include "trace2.h" 12 13 #include "string-list.h" 13 14 #include "pack-revindex.h" 14 15 #include "hash.h"
+10 -2
common-main.c
··· 25 25 26 26 int main(int argc, const char **argv) 27 27 { 28 + int result; 29 + 28 30 /* 29 31 * Always open file descriptors 0/1/2 to avoid clobbering files 30 32 * in die(). It also avoids messing up when the pipes are dup'ed 31 33 * onto stdin/stdout/stderr in the child processes we spawn. 32 34 */ 33 35 sanitize_stdfds(); 36 + restore_sigpipe_to_default(); 37 + 38 + trace2_initialize(); 39 + trace2_cmd_start(argv); 34 40 35 41 git_resolve_executable_dir(argv[0]); 36 42 ··· 40 46 41 47 attr_start(); 42 48 43 - restore_sigpipe_to_default(); 49 + result = cmd_main(argc, argv); 44 50 45 - return cmd_main(argc, argv); 51 + trace2_cmd_exit(result); 52 + 53 + return result; 46 54 }
+10 -1
compat/mingw.c
··· 1551 1551 return 0; 1552 1552 prog = path_lookup(interpr, 1); 1553 1553 if (prog) { 1554 + int exec_id; 1554 1555 int argc = 0; 1555 1556 const char **argv2; 1556 1557 while (argv[argc]) argc++; 1557 1558 ALLOC_ARRAY(argv2, argc + 1); 1558 1559 argv2[0] = (char *)cmd; /* full path to the script file */ 1559 1560 memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc); 1561 + exec_id = trace2_exec(prog, argv2); 1560 1562 pid = mingw_spawnv(prog, argv2, 1); 1561 1563 if (pid >= 0) { 1562 1564 int status; 1563 1565 if (waitpid(pid, &status, 0) < 0) 1564 1566 status = 255; 1567 + trace2_exec_result(exec_id, status); 1565 1568 exit(status); 1566 1569 } 1570 + trace2_exec_result(exec_id, -1); 1567 1571 pid = 1; /* indicate that we tried but failed */ 1568 1572 free(prog); 1569 1573 free(argv2); ··· 1576 1580 /* check if git_command is a shell script */ 1577 1581 if (!try_shell_exec(cmd, argv)) { 1578 1582 int pid, status; 1583 + int exec_id; 1579 1584 1585 + exec_id = trace2_exec(cmd, (const char **)argv); 1580 1586 pid = mingw_spawnv(cmd, (const char **)argv, 0); 1581 - if (pid < 0) 1587 + if (pid < 0) { 1588 + trace2_exec_result(exec_id, -1); 1582 1589 return -1; 1590 + } 1583 1591 if (waitpid(pid, &status, 0) < 0) 1584 1592 status = 255; 1593 + trace2_exec_result(exec_id, status); 1585 1594 exit(status); 1586 1595 } 1587 1596 return -1;
+1 -2
compat/mingw.h
··· 147 147 errno = EINVAL; 148 148 return -1; 149 149 } 150 - /* bash cannot reliably detect negative return codes as failure */ 151 - #define exit(code) exit((code) & 0xff) 150 + 152 151 #define sigemptyset(x) (void)0 153 152 static inline int sigaddset(sigset_t *set, int signum) 154 153 { return 0; }
+2
config.c
··· 2655 2655 void git_config_set(const char *key, const char *value) 2656 2656 { 2657 2657 git_config_set_multivar(key, value, NULL, 0); 2658 + 2659 + trace2_cmd_set_config(key, value); 2658 2660 } 2659 2661 2660 2662 /*
+2
exec-cmd.c
··· 209 209 return -1; 210 210 } 211 211 212 + trace2_cmd_path(buf->buf); 213 + 212 214 return 0; 213 215 } 214 216
+7
git-compat-util.h
··· 1260 1260 extern int cmd_main(int, const char **); 1261 1261 1262 1262 /* 1263 + * Intercept all calls to exit() and route them to trace2 to 1264 + * optionally emit a message before calling the real exit(). 1265 + */ 1266 + int trace2_cmd_exit_fl(const char *file, int line, int code); 1267 + #define exit(code) exit(trace2_cmd_exit_fl(__FILE__, __LINE__, (code))) 1268 + 1269 + /* 1263 1270 * You can mark a stack variable with UNLEAK(var) to avoid it being 1264 1271 * reported as a leak by tools like LSAN or valgrind. The argument 1265 1272 * should generally be the variable itself (not its address and not what
+65
git.c
··· 147 147 git_set_exec_path(cmd + 1); 148 148 else { 149 149 puts(git_exec_path()); 150 + trace2_cmd_name("_query_"); 150 151 exit(0); 151 152 } 152 153 } else if (!strcmp(cmd, "--html-path")) { 153 154 puts(system_path(GIT_HTML_PATH)); 155 + trace2_cmd_name("_query_"); 154 156 exit(0); 155 157 } else if (!strcmp(cmd, "--man-path")) { 156 158 puts(system_path(GIT_MAN_PATH)); 159 + trace2_cmd_name("_query_"); 157 160 exit(0); 158 161 } else if (!strcmp(cmd, "--info-path")) { 159 162 puts(system_path(GIT_INFO_PATH)); 163 + trace2_cmd_name("_query_"); 160 164 exit(0); 161 165 } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) { 162 166 use_pager = 1; ··· 285 289 (*argv)++; 286 290 (*argc)--; 287 291 } else if (skip_prefix(cmd, "--list-cmds=", &cmd)) { 292 + trace2_cmd_name("_query_"); 288 293 if (!strcmp(cmd, "parseopt")) { 289 294 struct string_list list = STRING_LIST_INIT_DUP; 290 295 int i; ··· 332 337 commit_pager_choice(); 333 338 334 339 child.use_shell = 1; 340 + child.trace2_child_class = "shell_alias"; 335 341 argv_array_push(&child.args, alias_string + 1); 336 342 argv_array_pushv(&child.args, (*argv) + 1); 337 343 344 + trace2_cmd_alias(alias_command, child.args.argv); 345 + trace2_cmd_list_config(); 346 + trace2_cmd_name("_run_shell_alias_"); 347 + 338 348 ret = run_command(&child); 339 349 if (ret >= 0) /* normal exit */ 340 350 exit(ret); ··· 368 378 REALLOC_ARRAY(new_argv, count + *argcp); 369 379 /* insert after command name */ 370 380 memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp); 381 + 382 + trace2_cmd_alias(alias_command, new_argv); 383 + trace2_cmd_list_config(); 371 384 372 385 *argv = new_argv; 373 386 *argcp += count - 1; ··· 417 430 setup_work_tree(); 418 431 419 432 trace_argv_printf(argv, "trace: built-in: git"); 433 + trace2_cmd_name(p->cmd); 434 + trace2_cmd_list_config(); 420 435 421 436 validate_cache_entries(the_repository->index); 422 437 status = p->fn(argc, argv, prefix); ··· 666 681 cmd.clean_on_exit = 1; 667 682 cmd.wait_after_clean = 1; 668 683 cmd.silent_exec_failure = 1; 684 + cmd.trace2_child_class = "dashed"; 669 685 686 + trace2_cmd_name("_run_dashed_"); 687 + 688 + /* 689 + * The code in run_command() logs trace2 child_start/child_exit 690 + * events, so we do not need to report exec/exec_result events here. 691 + */ 670 692 trace_argv_printf(cmd.args.argv, "trace: exec:"); 671 693 672 694 /* ··· 676 698 * the program. 677 699 */ 678 700 status = run_command(&cmd); 701 + 702 + /* 703 + * If the child process ran and we are now going to exit, emit a 704 + * generic string as our trace2 command verb to indicate that we 705 + * launched a dashed command. 706 + */ 679 707 if (status >= 0) 680 708 exit(status); 681 709 else if (errno != ENOENT) ··· 700 728 */ 701 729 if (!done_alias) 702 730 handle_builtin(*argcp, *argv); 731 + 732 + #if 0 // TODO In GFW, need to amend a7924b655e940b06cb547c235d6bed9767929673 to include trace2_ and _tr2 lines. 733 + else if (get_builtin(**argv)) { 734 + struct argv_array args = ARGV_ARRAY_INIT; 735 + int i; 736 + 737 + /* 738 + * The current process is committed to launching a 739 + * child process to run the command named in (**argv) 740 + * and exiting. Log a generic string as the trace2 741 + * command verb to indicate this. Note that the child 742 + * process will log the actual verb when it runs. 743 + */ 744 + trace2_cmd_name("_run_git_alias_"); 745 + 746 + if (get_super_prefix()) 747 + die("%s doesn't support --super-prefix", **argv); 748 + 749 + commit_pager_choice(); 750 + 751 + argv_array_push(&args, "git"); 752 + for (i = 0; i < *argcp; i++) 753 + argv_array_push(&args, (*argv)[i]); 754 + 755 + trace_argv_printf(args.argv, "trace: exec:"); 756 + 757 + /* 758 + * if we fail because the command is not found, it is 759 + * OK to return. Otherwise, we just pass along the status code. 760 + */ 761 + i = run_command_v_opt_tr2(args.argv, RUN_SILENT_EXEC_FAILURE | 762 + RUN_CLEAN_ON_EXIT, "git_alias"); 763 + if (i >= 0 || errno != ENOENT) 764 + exit(i); 765 + die("could not execute builtin %s", **argv); 766 + } 767 + #endif // a7924b655e940b06cb547c235d6bed9767929673 703 768 704 769 /* .. then try the external ones */ 705 770 execv_dashed_external(*argv);
+7
remote-curl.c
··· 1385 1385 string_list_init(&options.deepen_not, 1); 1386 1386 string_list_init(&options.push_options, 1); 1387 1387 1388 + /* 1389 + * Just report "remote-curl" here (folding all the various aliases 1390 + * ("git-remote-http", "git-remote-https", and etc.) here since they 1391 + * are all just copies of the same actual executable. 1392 + */ 1393 + trace2_cmd_name("remote-curl"); 1394 + 1388 1395 remote = remote_get(argv[1]); 1389 1396 1390 1397 if (argc > 2) {
+2
repository.c
··· 126 126 void repo_set_worktree(struct repository *repo, const char *path) 127 127 { 128 128 repo->worktree = real_pathdup(path, 1); 129 + 130 + trace2_def_repo(repo); 129 131 } 130 132 131 133 static int read_and_verify_repository_format(struct repository_format *format,
+3
repository.h
··· 92 92 /* Repository's current hash algorithm, as serialized on disk. */ 93 93 const struct git_hash_algo *hash_algo; 94 94 95 + /* A unique-id for tracing purposes. */ 96 + int trace2_repo_id; 97 + 95 98 /* Configurations */ 96 99 97 100 /* Indicate if a repository has a different 'commondir' from 'gitdir' */
+58 -1
run-command.c
··· 219 219 220 220 int sane_execvp(const char *file, char * const argv[]) 221 221 { 222 + #ifndef GIT_WINDOWS_NATIVE 223 + /* 224 + * execvp() doesn't return, so we all we can do is tell trace2 225 + * what we are about to do and let it leave a hint in the log 226 + * (unless of course the execvp() fails). 227 + * 228 + * we skip this for Windows because the compat layer already 229 + * has to emulate the execvp() call anyway. 230 + */ 231 + int exec_id = trace2_exec(file, (const char **)argv); 232 + #endif 233 + 222 234 if (!execvp(file, argv)) 223 235 return 0; /* cannot happen ;-) */ 236 + 237 + #ifndef GIT_WINDOWS_NATIVE 238 + { 239 + int ec = errno; 240 + trace2_exec_result(exec_id, ec); 241 + errno = ec; 242 + } 243 + #endif 224 244 225 245 /* 226 246 * When a command can't be found because one of the directories ··· 712 732 cmd->err = fderr[0]; 713 733 } 714 734 735 + trace2_child_start(cmd); 715 736 trace_run_command(cmd); 716 737 717 738 fflush(NULL); ··· 926 947 #endif 927 948 928 949 if (cmd->pid < 0) { 950 + trace2_child_exit(cmd, -1); 951 + 929 952 if (need_in) 930 953 close_pair(fdin); 931 954 else if (cmd->in) ··· 964 987 int finish_command(struct child_process *cmd) 965 988 { 966 989 int ret = wait_or_whine(cmd->pid, cmd->argv[0], 0); 990 + trace2_child_exit(cmd, ret); 967 991 child_process_clear(cmd); 968 992 return ret; 969 993 } 970 994 971 995 int finish_command_in_signal(struct child_process *cmd) 972 996 { 973 - return wait_or_whine(cmd->pid, cmd->argv[0], 1); 997 + int ret = wait_or_whine(cmd->pid, cmd->argv[0], 1); 998 + trace2_child_exit(cmd, ret); 999 + return ret; 974 1000 } 975 1001 976 1002 ··· 992 1018 return run_command_v_opt_cd_env(argv, opt, NULL, NULL); 993 1019 } 994 1020 1021 + int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class) 1022 + { 1023 + return run_command_v_opt_cd_env_tr2(argv, opt, NULL, NULL, tr2_class); 1024 + } 1025 + 995 1026 int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env) 1027 + { 1028 + return run_command_v_opt_cd_env_tr2(argv, opt, dir, env, NULL); 1029 + } 1030 + 1031 + int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir, 1032 + const char *const *env, const char *tr2_class) 996 1033 { 997 1034 struct child_process cmd = CHILD_PROCESS_INIT; 998 1035 cmd.argv = argv; ··· 1004 1041 cmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0; 1005 1042 cmd.dir = dir; 1006 1043 cmd.env = env; 1044 + cmd.trace2_child_class = tr2_class; 1007 1045 return run_command(&cmd); 1008 1046 } 1009 1047 ··· 1319 1357 hook.env = env; 1320 1358 hook.no_stdin = 1; 1321 1359 hook.stdout_to_stderr = 1; 1360 + hook.trace2_hook_name = name; 1322 1361 1323 1362 return run_command(&hook); 1324 1363 } ··· 1807 1846 pp_cleanup(&pp); 1808 1847 return 0; 1809 1848 } 1849 + 1850 + int run_processes_parallel_tr2(int n, get_next_task_fn get_next_task, 1851 + start_failure_fn start_failure, 1852 + task_finished_fn task_finished, void *pp_cb, 1853 + const char *tr2_category, const char *tr2_label) 1854 + { 1855 + int result; 1856 + 1857 + trace2_region_enter_printf(tr2_category, tr2_label, NULL, "max:%d", 1858 + ((n < 1) ? online_cpus() : n)); 1859 + 1860 + result = run_processes_parallel(n, get_next_task, start_failure, 1861 + task_finished, pp_cb); 1862 + 1863 + trace2_region_leave(tr2_category, tr2_label, NULL); 1864 + 1865 + return result; 1866 + }
+12 -1
run-command.h
··· 10 10 struct argv_array args; 11 11 struct argv_array env_array; 12 12 pid_t pid; 13 + 14 + int trace2_child_id; 15 + uint64_t trace2_child_us_start; 16 + const char *trace2_child_class; 17 + const char *trace2_hook_name; 18 + 13 19 /* 14 20 * Using .in, .out, .err: 15 21 * - Specify 0 for no redirections (child inherits stdin, stdout, ··· 73 79 #define RUN_USING_SHELL 16 74 80 #define RUN_CLEAN_ON_EXIT 32 75 81 int run_command_v_opt(const char **argv, int opt); 76 - 82 + int run_command_v_opt_tr2(const char **argv, int opt, const char *tr2_class); 77 83 /* 78 84 * env (the environment) is to be formatted like environ: "VAR=VALUE". 79 85 * To unset an environment variable use just "VAR". 80 86 */ 81 87 int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env); 88 + int run_command_v_opt_cd_env_tr2(const char **argv, int opt, const char *dir, 89 + const char *const *env, const char *tr2_class); 82 90 83 91 /** 84 92 * Execute the given command, sending "in" to its stdin, and capturing its ··· 220 228 start_failure_fn, 221 229 task_finished_fn, 222 230 void *pp_cb); 231 + int run_processes_parallel_tr2(int n, get_next_task_fn, start_failure_fn, 232 + task_finished_fn, void *pp_cb, 233 + const char *tr2_category, const char *tr2_label); 223 234 224 235 #endif
+3
sh-i18n--envsubst.c
··· 14 14 */ 15 15 16 16 #include "git-compat-util.h" 17 + #include "trace2.h" 17 18 18 19 /* Substitution of environment variables in shell format strings. 19 20 Copyright (C) 2003-2007 Free Software Foundation, Inc. ··· 66 67 { 67 68 /* Default values for command line options. */ 68 69 /* unsigned short int show_variables = 0; */ 70 + 71 + trace2_cmd_name("sh-i18n--envsubst"); 69 72 70 73 switch (argc) 71 74 {
+6 -5
submodule.c
··· 1609 1609 1610 1610 calculate_changed_submodule_paths(r, &spf.changed_submodule_names); 1611 1611 string_list_sort(&spf.changed_submodule_names); 1612 - run_processes_parallel(max_parallel_jobs, 1613 - get_next_submodule, 1614 - fetch_start_failure, 1615 - fetch_finish, 1616 - &spf); 1612 + run_processes_parallel_tr2(max_parallel_jobs, 1613 + get_next_submodule, 1614 + fetch_start_failure, 1615 + fetch_finish, 1616 + &spf, 1617 + "submodule", "parallel/fetch"); 1617 1618 1618 1619 argv_array_clear(&spf.args); 1619 1620 out:
+3
t/helper/test-parse-options.c
··· 2 2 #include "cache.h" 3 3 #include "parse-options.h" 4 4 #include "string-list.h" 5 + #include "trace2.h" 5 6 6 7 static int boolean = 0; 7 8 static int integer = 0; ··· 152 153 }; 153 154 int i; 154 155 int ret = 0; 156 + 157 + trace2_cmd_name("_parse_"); 155 158 156 159 argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); 157 160
+3
t/helper/test-tool.c
··· 1 1 #include "git-compat-util.h" 2 2 #include "test-tool.h" 3 + #include "trace2.h" 3 4 4 5 struct test_cmd { 5 6 const char *name; ··· 82 83 if (!strcmp(cmds[i].name, argv[1])) { 83 84 argv++; 84 85 argc--; 86 + trace2_cmd_name(cmds[i].name); 87 + trace2_cmd_list_config(); 85 88 return cmds[i].fn(argc, argv); 86 89 } 87 90 }
+1
t/t0001-init.sh
··· 93 93 sed -n \ 94 94 -e "/^GIT_PREFIX=/d" \ 95 95 -e "/^GIT_TEXTDOMAINDIR=/d" \ 96 + -e "/^GIT_TR2_PARENT/d" \ 96 97 -e "/^GIT_/s/=.*//p" | 97 98 sort 98 99 EOF
+761
trace2.c
··· 1 + #include "cache.h" 2 + #include "config.h" 3 + #include "json-writer.h" 4 + #include "quote.h" 5 + #include "run-command.h" 6 + #include "sigchain.h" 7 + #include "thread-utils.h" 8 + #include "version.h" 9 + #include "trace2/tr2_cfg.h" 10 + #include "trace2/tr2_cmd_name.h" 11 + #include "trace2/tr2_dst.h" 12 + #include "trace2/tr2_sid.h" 13 + #include "trace2/tr2_tgt.h" 14 + #include "trace2/tr2_tls.h" 15 + 16 + static int trace2_enabled; 17 + 18 + static int tr2_next_child_id; /* modify under lock */ 19 + static int tr2_next_exec_id; /* modify under lock */ 20 + static int tr2_next_repo_id = 1; /* modify under lock. zero is reserved */ 21 + 22 + /* 23 + * A table of the builtin TRACE2 targets. Each of these may be independently 24 + * enabled or disabled. Each TRACE2 API method will try to write an event to 25 + * *each* of the enabled targets. 26 + */ 27 + /* clang-format off */ 28 + static struct tr2_tgt *tr2_tgt_builtins[] = 29 + { 30 + &tr2_tgt_normal, 31 + &tr2_tgt_perf, 32 + &tr2_tgt_event, 33 + NULL 34 + }; 35 + /* clang-format on */ 36 + 37 + /* clang-format off */ 38 + #define for_each_builtin(j, tgt_j) \ 39 + for (j = 0, tgt_j = tr2_tgt_builtins[j]; \ 40 + tgt_j; \ 41 + j++, tgt_j = tr2_tgt_builtins[j]) 42 + /* clang-format on */ 43 + 44 + /* clang-format off */ 45 + #define for_each_wanted_builtin(j, tgt_j) \ 46 + for_each_builtin(j, tgt_j) \ 47 + if (tr2_dst_trace_want(tgt_j->pdst)) 48 + /* clang-format on */ 49 + 50 + /* 51 + * Force (rather than lazily) initialize any of the requested 52 + * builtin TRACE2 targets at startup (and before we've seen an 53 + * actual TRACE2 event call) so we can see if we need to setup 54 + * the TR2 and TLS machinery. 55 + * 56 + * Return the number of builtin targets enabled. 57 + */ 58 + static int tr2_tgt_want_builtins(void) 59 + { 60 + struct tr2_tgt *tgt_j; 61 + int j; 62 + int sum = 0; 63 + 64 + for_each_builtin (j, tgt_j) 65 + if (tgt_j->pfn_init()) 66 + sum++; 67 + 68 + return sum; 69 + } 70 + 71 + /* 72 + * Properly terminate each builtin target. Give each target 73 + * a chance to write a summary event and/or flush if necessary 74 + * and then close the fd. 75 + */ 76 + static void tr2_tgt_disable_builtins(void) 77 + { 78 + struct tr2_tgt *tgt_j; 79 + int j; 80 + 81 + for_each_builtin (j, tgt_j) 82 + tgt_j->pfn_term(); 83 + } 84 + 85 + static int tr2main_exit_code; 86 + 87 + /* 88 + * Our atexit routine should run after everything has finished. 89 + * 90 + * Note that events generated here might not actually appear if 91 + * we are writing to fd 1 or 2 and our atexit routine runs after 92 + * the pager's atexit routine (since it closes them to shutdown 93 + * the pipes). 94 + */ 95 + static void tr2main_atexit_handler(void) 96 + { 97 + struct tr2_tgt *tgt_j; 98 + int j; 99 + uint64_t us_now; 100 + uint64_t us_elapsed_absolute; 101 + 102 + us_now = getnanotime() / 1000; 103 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 104 + 105 + /* 106 + * Clear any unbalanced regions so that our atexit message 107 + * does not appear nested. This improves the appearance of 108 + * the trace output if someone calls die(), for example. 109 + */ 110 + tr2tls_pop_unwind_self(); 111 + 112 + for_each_wanted_builtin (j, tgt_j) 113 + if (tgt_j->pfn_atexit) 114 + tgt_j->pfn_atexit(us_elapsed_absolute, 115 + tr2main_exit_code); 116 + 117 + tr2_tgt_disable_builtins(); 118 + 119 + tr2tls_release(); 120 + tr2_sid_release(); 121 + tr2_cmd_name_release(); 122 + tr2_cfg_free_patterns(); 123 + 124 + trace2_enabled = 0; 125 + } 126 + 127 + static void tr2main_signal_handler(int signo) 128 + { 129 + struct tr2_tgt *tgt_j; 130 + int j; 131 + uint64_t us_now; 132 + uint64_t us_elapsed_absolute; 133 + 134 + us_now = getnanotime() / 1000; 135 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 136 + 137 + for_each_wanted_builtin (j, tgt_j) 138 + if (tgt_j->pfn_signal) 139 + tgt_j->pfn_signal(us_elapsed_absolute, signo); 140 + 141 + sigchain_pop(signo); 142 + raise(signo); 143 + } 144 + 145 + void trace2_initialize_fl(const char *file, int line) 146 + { 147 + struct tr2_tgt *tgt_j; 148 + int j; 149 + 150 + if (trace2_enabled) 151 + return; 152 + 153 + if (!tr2_tgt_want_builtins()) 154 + return; 155 + trace2_enabled = 1; 156 + 157 + tr2_sid_get(); 158 + 159 + atexit(tr2main_atexit_handler); 160 + sigchain_push(SIGPIPE, tr2main_signal_handler); 161 + tr2tls_init(); 162 + 163 + /* 164 + * Emit 'version' message on each active builtin target. 165 + */ 166 + for_each_wanted_builtin (j, tgt_j) 167 + if (tgt_j->pfn_version_fl) 168 + tgt_j->pfn_version_fl(file, line); 169 + } 170 + 171 + int trace2_is_enabled(void) 172 + { 173 + return trace2_enabled; 174 + } 175 + 176 + void trace2_cmd_start_fl(const char *file, int line, const char **argv) 177 + { 178 + struct tr2_tgt *tgt_j; 179 + int j; 180 + 181 + if (!trace2_enabled) 182 + return; 183 + 184 + for_each_wanted_builtin (j, tgt_j) 185 + if (tgt_j->pfn_start_fl) 186 + tgt_j->pfn_start_fl(file, line, argv); 187 + } 188 + 189 + int trace2_cmd_exit_fl(const char *file, int line, int code) 190 + { 191 + struct tr2_tgt *tgt_j; 192 + int j; 193 + uint64_t us_now; 194 + uint64_t us_elapsed_absolute; 195 + 196 + code &= 0xff; 197 + 198 + if (!trace2_enabled) 199 + return code; 200 + 201 + tr2main_exit_code = code; 202 + 203 + us_now = getnanotime() / 1000; 204 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 205 + 206 + for_each_wanted_builtin (j, tgt_j) 207 + if (tgt_j->pfn_exit_fl) 208 + tgt_j->pfn_exit_fl(file, line, us_elapsed_absolute, 209 + code); 210 + 211 + return code; 212 + } 213 + 214 + void trace2_cmd_error_va_fl(const char *file, int line, const char *fmt, 215 + va_list ap) 216 + { 217 + struct tr2_tgt *tgt_j; 218 + int j; 219 + 220 + if (!trace2_enabled) 221 + return; 222 + 223 + /* 224 + * We expect each target function to treat 'ap' as constant 225 + * and use va_copy (because an 'ap' can only be walked once). 226 + */ 227 + for_each_wanted_builtin (j, tgt_j) 228 + if (tgt_j->pfn_error_va_fl) 229 + tgt_j->pfn_error_va_fl(file, line, fmt, ap); 230 + } 231 + 232 + void trace2_cmd_path_fl(const char *file, int line, const char *pathname) 233 + { 234 + struct tr2_tgt *tgt_j; 235 + int j; 236 + 237 + if (!trace2_enabled) 238 + return; 239 + 240 + for_each_wanted_builtin (j, tgt_j) 241 + if (tgt_j->pfn_command_path_fl) 242 + tgt_j->pfn_command_path_fl(file, line, pathname); 243 + } 244 + 245 + void trace2_cmd_name_fl(const char *file, int line, const char *name) 246 + { 247 + struct tr2_tgt *tgt_j; 248 + const char *hierarchy; 249 + int j; 250 + 251 + if (!trace2_enabled) 252 + return; 253 + 254 + tr2_cmd_name_append_hierarchy(name); 255 + hierarchy = tr2_cmd_name_get_hierarchy(); 256 + 257 + for_each_wanted_builtin (j, tgt_j) 258 + if (tgt_j->pfn_command_name_fl) 259 + tgt_j->pfn_command_name_fl(file, line, name, hierarchy); 260 + } 261 + 262 + void trace2_cmd_mode_fl(const char *file, int line, const char *mode) 263 + { 264 + struct tr2_tgt *tgt_j; 265 + int j; 266 + 267 + if (!trace2_enabled) 268 + return; 269 + 270 + for_each_wanted_builtin (j, tgt_j) 271 + if (tgt_j->pfn_command_mode_fl) 272 + tgt_j->pfn_command_mode_fl(file, line, mode); 273 + } 274 + 275 + void trace2_cmd_alias_fl(const char *file, int line, const char *alias, 276 + const char **argv) 277 + { 278 + struct tr2_tgt *tgt_j; 279 + int j; 280 + 281 + if (!trace2_enabled) 282 + return; 283 + 284 + for_each_wanted_builtin (j, tgt_j) 285 + if (tgt_j->pfn_alias_fl) 286 + tgt_j->pfn_alias_fl(file, line, alias, argv); 287 + } 288 + 289 + void trace2_cmd_list_config_fl(const char *file, int line) 290 + { 291 + if (!trace2_enabled) 292 + return; 293 + 294 + tr2_cfg_list_config_fl(file, line); 295 + } 296 + 297 + void trace2_cmd_set_config_fl(const char *file, int line, const char *key, 298 + const char *value) 299 + { 300 + if (!trace2_enabled) 301 + return; 302 + 303 + tr2_cfg_set_fl(file, line, key, value); 304 + } 305 + 306 + void trace2_child_start_fl(const char *file, int line, 307 + struct child_process *cmd) 308 + { 309 + struct tr2_tgt *tgt_j; 310 + int j; 311 + uint64_t us_now; 312 + uint64_t us_elapsed_absolute; 313 + 314 + if (!trace2_enabled) 315 + return; 316 + 317 + us_now = getnanotime() / 1000; 318 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 319 + 320 + cmd->trace2_child_id = tr2tls_locked_increment(&tr2_next_child_id); 321 + cmd->trace2_child_us_start = us_now; 322 + 323 + for_each_wanted_builtin (j, tgt_j) 324 + if (tgt_j->pfn_child_start_fl) 325 + tgt_j->pfn_child_start_fl(file, line, 326 + us_elapsed_absolute, cmd); 327 + } 328 + 329 + void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd, 330 + int child_exit_code) 331 + { 332 + struct tr2_tgt *tgt_j; 333 + int j; 334 + uint64_t us_now; 335 + uint64_t us_elapsed_absolute; 336 + uint64_t us_elapsed_child; 337 + 338 + if (!trace2_enabled) 339 + return; 340 + 341 + us_now = getnanotime() / 1000; 342 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 343 + 344 + if (cmd->trace2_child_us_start) 345 + us_elapsed_child = us_now - cmd->trace2_child_us_start; 346 + else 347 + us_elapsed_child = 0; 348 + 349 + for_each_wanted_builtin (j, tgt_j) 350 + if (tgt_j->pfn_child_exit_fl) 351 + tgt_j->pfn_child_exit_fl(file, line, 352 + us_elapsed_absolute, 353 + cmd->trace2_child_id, cmd->pid, 354 + child_exit_code, 355 + us_elapsed_child); 356 + } 357 + 358 + int trace2_exec_fl(const char *file, int line, const char *exe, 359 + const char **argv) 360 + { 361 + struct tr2_tgt *tgt_j; 362 + int j; 363 + int exec_id; 364 + uint64_t us_now; 365 + uint64_t us_elapsed_absolute; 366 + 367 + if (!trace2_enabled) 368 + return -1; 369 + 370 + us_now = getnanotime() / 1000; 371 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 372 + 373 + exec_id = tr2tls_locked_increment(&tr2_next_exec_id); 374 + 375 + for_each_wanted_builtin (j, tgt_j) 376 + if (tgt_j->pfn_exec_fl) 377 + tgt_j->pfn_exec_fl(file, line, us_elapsed_absolute, 378 + exec_id, exe, argv); 379 + 380 + return exec_id; 381 + } 382 + 383 + void trace2_exec_result_fl(const char *file, int line, int exec_id, int code) 384 + { 385 + struct tr2_tgt *tgt_j; 386 + int j; 387 + uint64_t us_now; 388 + uint64_t us_elapsed_absolute; 389 + 390 + if (!trace2_enabled) 391 + return; 392 + 393 + us_now = getnanotime() / 1000; 394 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 395 + 396 + for_each_wanted_builtin (j, tgt_j) 397 + if (tgt_j->pfn_exec_result_fl) 398 + tgt_j->pfn_exec_result_fl( 399 + file, line, us_elapsed_absolute, exec_id, code); 400 + } 401 + 402 + void trace2_thread_start_fl(const char *file, int line, const char *thread_name) 403 + { 404 + struct tr2_tgt *tgt_j; 405 + int j; 406 + uint64_t us_now; 407 + uint64_t us_elapsed_absolute; 408 + 409 + if (!trace2_enabled) 410 + return; 411 + 412 + if (tr2tls_is_main_thread()) { 413 + /* 414 + * We should only be called from the new thread's thread-proc, 415 + * so this is technically a bug. But in those cases where the 416 + * main thread also runs the thread-proc function (or when we 417 + * are built with threading disabled), we need to allow it. 418 + * 419 + * Convert this call to a region-enter so the nesting looks 420 + * correct. 421 + */ 422 + trace2_region_enter_printf_fl(file, line, NULL, NULL, NULL, 423 + "thread-proc on main: %s", 424 + thread_name); 425 + return; 426 + } 427 + 428 + us_now = getnanotime() / 1000; 429 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 430 + 431 + tr2tls_create_self(thread_name); 432 + 433 + for_each_wanted_builtin (j, tgt_j) 434 + if (tgt_j->pfn_thread_start_fl) 435 + tgt_j->pfn_thread_start_fl(file, line, 436 + us_elapsed_absolute); 437 + } 438 + 439 + void trace2_thread_exit_fl(const char *file, int line) 440 + { 441 + struct tr2_tgt *tgt_j; 442 + int j; 443 + uint64_t us_now; 444 + uint64_t us_elapsed_absolute; 445 + uint64_t us_elapsed_thread; 446 + 447 + if (!trace2_enabled) 448 + return; 449 + 450 + if (tr2tls_is_main_thread()) { 451 + /* 452 + * We should only be called from the exiting thread's 453 + * thread-proc, so this is technically a bug. But in 454 + * those cases where the main thread also runs the 455 + * thread-proc function (or when we are built with 456 + * threading disabled), we need to allow it. 457 + * 458 + * Convert this call to a region-leave so the nesting 459 + * looks correct. 460 + */ 461 + trace2_region_leave_printf_fl(file, line, NULL, NULL, NULL, 462 + "thread-proc on main"); 463 + return; 464 + } 465 + 466 + us_now = getnanotime() / 1000; 467 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 468 + 469 + /* 470 + * Clear any unbalanced regions and then get the relative time 471 + * for the outer-most region (which we pushed when the thread 472 + * started). This gives us the run time of the thread. 473 + */ 474 + tr2tls_pop_unwind_self(); 475 + us_elapsed_thread = tr2tls_region_elasped_self(us_now); 476 + 477 + for_each_wanted_builtin (j, tgt_j) 478 + if (tgt_j->pfn_thread_exit_fl) 479 + tgt_j->pfn_thread_exit_fl(file, line, 480 + us_elapsed_absolute, 481 + us_elapsed_thread); 482 + 483 + tr2tls_unset_self(); 484 + } 485 + 486 + void trace2_def_param_fl(const char *file, int line, const char *param, 487 + const char *value) 488 + { 489 + struct tr2_tgt *tgt_j; 490 + int j; 491 + 492 + if (!trace2_enabled) 493 + return; 494 + 495 + for_each_wanted_builtin (j, tgt_j) 496 + if (tgt_j->pfn_param_fl) 497 + tgt_j->pfn_param_fl(file, line, param, value); 498 + } 499 + 500 + void trace2_def_repo_fl(const char *file, int line, struct repository *repo) 501 + { 502 + struct tr2_tgt *tgt_j; 503 + int j; 504 + 505 + if (!trace2_enabled) 506 + return; 507 + 508 + if (repo->trace2_repo_id) 509 + return; 510 + 511 + repo->trace2_repo_id = tr2tls_locked_increment(&tr2_next_repo_id); 512 + 513 + for_each_wanted_builtin (j, tgt_j) 514 + if (tgt_j->pfn_repo_fl) 515 + tgt_j->pfn_repo_fl(file, line, repo); 516 + } 517 + 518 + void trace2_region_enter_printf_va_fl(const char *file, int line, 519 + const char *category, const char *label, 520 + const struct repository *repo, 521 + const char *fmt, va_list ap) 522 + { 523 + struct tr2_tgt *tgt_j; 524 + int j; 525 + uint64_t us_now; 526 + uint64_t us_elapsed_absolute; 527 + 528 + if (!trace2_enabled) 529 + return; 530 + 531 + us_now = getnanotime() / 1000; 532 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 533 + 534 + /* 535 + * Print the region-enter message at the current nesting 536 + * (indentation) level and then push a new level. 537 + * 538 + * We expect each target function to treat 'ap' as constant 539 + * and use va_copy. 540 + */ 541 + for_each_wanted_builtin (j, tgt_j) 542 + if (tgt_j->pfn_region_enter_printf_va_fl) 543 + tgt_j->pfn_region_enter_printf_va_fl( 544 + file, line, us_elapsed_absolute, category, 545 + label, repo, fmt, ap); 546 + 547 + tr2tls_push_self(us_now); 548 + } 549 + 550 + void trace2_region_enter_fl(const char *file, int line, const char *category, 551 + const char *label, const struct repository *repo) 552 + { 553 + trace2_region_enter_printf_va_fl(file, line, category, label, repo, 554 + NULL, NULL); 555 + } 556 + 557 + void trace2_region_enter_printf_fl(const char *file, int line, 558 + const char *category, const char *label, 559 + const struct repository *repo, 560 + const char *fmt, ...) 561 + { 562 + va_list ap; 563 + 564 + va_start(ap, fmt); 565 + trace2_region_enter_printf_va_fl(file, line, category, label, repo, fmt, 566 + ap); 567 + va_end(ap); 568 + } 569 + 570 + #ifndef HAVE_VARIADIC_MACROS 571 + void trace2_region_enter_printf(const char *category, const char *label, 572 + const struct repository *repo, const char *fmt, 573 + ...) 574 + { 575 + va_list ap; 576 + 577 + va_start(ap, fmt); 578 + trace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt, 579 + ap); 580 + va_end(ap); 581 + } 582 + #endif 583 + 584 + void trace2_region_leave_printf_va_fl(const char *file, int line, 585 + const char *category, const char *label, 586 + const struct repository *repo, 587 + const char *fmt, va_list ap) 588 + { 589 + struct tr2_tgt *tgt_j; 590 + int j; 591 + uint64_t us_now; 592 + uint64_t us_elapsed_absolute; 593 + uint64_t us_elapsed_region; 594 + 595 + if (!trace2_enabled) 596 + return; 597 + 598 + us_now = getnanotime() / 1000; 599 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 600 + 601 + /* 602 + * Get the elapsed time in the current region before we 603 + * pop it off the stack. Pop the stack. And then print 604 + * the perf message at the new (shallower) level so that 605 + * it lines up with the corresponding push/enter. 606 + */ 607 + us_elapsed_region = tr2tls_region_elasped_self(us_now); 608 + 609 + tr2tls_pop_self(); 610 + 611 + /* 612 + * We expect each target function to treat 'ap' as constant 613 + * and use va_copy. 614 + */ 615 + for_each_wanted_builtin (j, tgt_j) 616 + if (tgt_j->pfn_region_leave_printf_va_fl) 617 + tgt_j->pfn_region_leave_printf_va_fl( 618 + file, line, us_elapsed_absolute, 619 + us_elapsed_region, category, label, repo, fmt, 620 + ap); 621 + } 622 + 623 + void trace2_region_leave_fl(const char *file, int line, const char *category, 624 + const char *label, const struct repository *repo) 625 + { 626 + trace2_region_leave_printf_va_fl(file, line, category, label, repo, 627 + NULL, NULL); 628 + } 629 + 630 + void trace2_region_leave_printf_fl(const char *file, int line, 631 + const char *category, const char *label, 632 + const struct repository *repo, 633 + const char *fmt, ...) 634 + { 635 + va_list ap; 636 + 637 + va_start(ap, fmt); 638 + trace2_region_leave_printf_va_fl(file, line, category, label, repo, fmt, 639 + ap); 640 + va_end(ap); 641 + } 642 + 643 + #ifndef HAVE_VARIADIC_MACROS 644 + void trace2_region_leave_printf(const char *category, const char *label, 645 + const struct repository *repo, const char *fmt, 646 + ...) 647 + { 648 + va_list ap; 649 + 650 + va_start(ap, fmt); 651 + trace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt, 652 + ap); 653 + va_end(ap); 654 + } 655 + #endif 656 + 657 + void trace2_data_string_fl(const char *file, int line, const char *category, 658 + const struct repository *repo, const char *key, 659 + const char *value) 660 + { 661 + struct tr2_tgt *tgt_j; 662 + int j; 663 + uint64_t us_now; 664 + uint64_t us_elapsed_absolute; 665 + uint64_t us_elapsed_region; 666 + 667 + if (!trace2_enabled) 668 + return; 669 + 670 + us_now = getnanotime() / 1000; 671 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 672 + us_elapsed_region = tr2tls_region_elasped_self(us_now); 673 + 674 + for_each_wanted_builtin (j, tgt_j) 675 + if (tgt_j->pfn_data_fl) 676 + tgt_j->pfn_data_fl(file, line, us_elapsed_absolute, 677 + us_elapsed_region, category, repo, 678 + key, value); 679 + } 680 + 681 + void trace2_data_intmax_fl(const char *file, int line, const char *category, 682 + const struct repository *repo, const char *key, 683 + intmax_t value) 684 + { 685 + struct strbuf buf_string = STRBUF_INIT; 686 + 687 + if (!trace2_enabled) 688 + return; 689 + 690 + strbuf_addf(&buf_string, "%" PRIdMAX, value); 691 + trace2_data_string_fl(file, line, category, repo, key, buf_string.buf); 692 + strbuf_release(&buf_string); 693 + } 694 + 695 + void trace2_data_json_fl(const char *file, int line, const char *category, 696 + const struct repository *repo, const char *key, 697 + const struct json_writer *value) 698 + { 699 + struct tr2_tgt *tgt_j; 700 + int j; 701 + uint64_t us_now; 702 + uint64_t us_elapsed_absolute; 703 + uint64_t us_elapsed_region; 704 + 705 + if (!trace2_enabled) 706 + return; 707 + 708 + us_now = getnanotime() / 1000; 709 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 710 + us_elapsed_region = tr2tls_region_elasped_self(us_now); 711 + 712 + for_each_wanted_builtin (j, tgt_j) 713 + if (tgt_j->pfn_data_fl) 714 + tgt_j->pfn_data_json_fl(file, line, us_elapsed_absolute, 715 + us_elapsed_region, category, 716 + repo, key, value); 717 + } 718 + 719 + void trace2_printf_va_fl(const char *file, int line, const char *fmt, 720 + va_list ap) 721 + { 722 + struct tr2_tgt *tgt_j; 723 + int j; 724 + uint64_t us_now; 725 + uint64_t us_elapsed_absolute; 726 + 727 + if (!trace2_enabled) 728 + return; 729 + 730 + us_now = getnanotime() / 1000; 731 + us_elapsed_absolute = tr2tls_absolute_elapsed(us_now); 732 + 733 + /* 734 + * We expect each target function to treat 'ap' as constant 735 + * and use va_copy. 736 + */ 737 + for_each_wanted_builtin (j, tgt_j) 738 + if (tgt_j->pfn_printf_va_fl) 739 + tgt_j->pfn_printf_va_fl(file, line, us_elapsed_absolute, 740 + fmt, ap); 741 + } 742 + 743 + void trace2_printf_fl(const char *file, int line, const char *fmt, ...) 744 + { 745 + va_list ap; 746 + 747 + va_start(ap, fmt); 748 + trace2_printf_va_fl(file, line, fmt, ap); 749 + va_end(ap); 750 + } 751 + 752 + #ifndef HAVE_VARIADIC_MACROS 753 + void trace2_printf(const char *fmt, ...) 754 + { 755 + va_list ap; 756 + 757 + va_start(ap, fmt); 758 + trace2_printf_va_fl(NULL, 0, fmt, ap); 759 + va_end(ap); 760 + } 761 + #endif
+371
trace2.h
··· 1 + #ifndef TRACE2_H 2 + #define TRACE2_H 3 + 4 + struct child_process; 5 + struct repository; 6 + struct json_writer; 7 + 8 + /* 9 + * The public TRACE2 routines are grouped into the following groups: 10 + * 11 + * [] trace2_initialize -- initialization. 12 + * [] trace2_cmd_* -- emit command/control messages. 13 + * [] trace2_child* -- emit child start/stop messages. 14 + * [] trace2_exec* -- emit exec start/stop messages. 15 + * [] trace2_thread* -- emit thread start/stop messages. 16 + * [] trace2_def* -- emit definition/parameter mesasges. 17 + * [] trace2_region* -- emit region nesting messages. 18 + * [] trace2_data* -- emit region/thread/repo data messages. 19 + * [] trace2_printf* -- legacy trace[1] messages. 20 + */ 21 + 22 + /* 23 + * Initialize TRACE2 tracing facility if any of the builtin TRACE2 24 + * targets are enabled in the environment. Emits a 'version' event. 25 + * 26 + * Cleanup/Termination is handled automatically by a registered 27 + * atexit() routine. 28 + */ 29 + void trace2_initialize_fl(const char *file, int line); 30 + 31 + #define trace2_initialize() trace2_initialize_fl(__FILE__, __LINE__) 32 + 33 + /* 34 + * Return true if trace2 is enabled. 35 + */ 36 + int trace2_is_enabled(void); 37 + 38 + /* 39 + * Emit a 'start' event with the original (unmodified) argv. 40 + */ 41 + void trace2_cmd_start_fl(const char *file, int line, const char **argv); 42 + 43 + #define trace2_cmd_start(argv) trace2_cmd_start_fl(__FILE__, __LINE__, (argv)) 44 + 45 + /* 46 + * Emit an 'exit' event. 47 + * 48 + * Write the exit-code that will be passed to exit() or returned 49 + * from main(). 50 + * 51 + * Use this prior to actually calling exit(). 52 + * See "#define exit()" in git-compat-util.h 53 + */ 54 + int trace2_cmd_exit_fl(const char *file, int line, int code); 55 + 56 + #define trace2_cmd_exit(code) (trace2_cmd_exit_fl(__FILE__, __LINE__, (code))) 57 + 58 + /* 59 + * Emit an 'error' event. 60 + * 61 + * Write an error message to the TRACE2 targets. 62 + */ 63 + void trace2_cmd_error_va_fl(const char *file, int line, const char *fmt, 64 + va_list ap); 65 + 66 + #define trace2_cmd_error_va(fmt, ap) \ 67 + trace2_cmd_error_va_fl(__FILE__, __LINE__, (fmt), (ap)) 68 + 69 + /* 70 + * Emit a 'pathname' event with the canonical pathname of the current process 71 + * This gives post-processors a simple field to identify the command without 72 + * having to parse the argv. For example, to distinguish invocations from 73 + * installed versus debug executables. 74 + */ 75 + void trace2_cmd_path_fl(const char *file, int line, const char *pathname); 76 + 77 + #define trace2_cmd_path(p) trace2_cmd_path_fl(__FILE__, __LINE__, (p)) 78 + 79 + /* 80 + * Emit a 'cmd_name' event with the canonical name of the command. 81 + * This gives post-processors a simple field to identify the command 82 + * without having to parse the argv. 83 + */ 84 + void trace2_cmd_name_fl(const char *file, int line, const char *name); 85 + 86 + #define trace2_cmd_name(v) trace2_cmd_name_fl(__FILE__, __LINE__, (v)) 87 + 88 + /* 89 + * Emit a 'cmd_mode' event to further describe the command being run. 90 + * For example, "checkout" can checkout a single file or can checkout a 91 + * different branch. This gives post-processors a simple field to compare 92 + * equivalent commands without having to parse the argv. 93 + */ 94 + void trace2_cmd_mode_fl(const char *file, int line, const char *mode); 95 + 96 + #define trace2_cmd_mode(sv) trace2_cmd_mode_fl(__FILE__, __LINE__, (sv)) 97 + 98 + /* 99 + * Emit an 'alias' expansion event. 100 + */ 101 + void trace2_cmd_alias_fl(const char *file, int line, const char *alias, 102 + const char **argv); 103 + 104 + #define trace2_cmd_alias(alias, argv) \ 105 + trace2_cmd_alias_fl(__FILE__, __LINE__, (alias), (argv)) 106 + 107 + /* 108 + * Emit one or more 'def_param' events for "interesting" configuration 109 + * settings. 110 + * 111 + * The environment variable "GIT_TR2_CONFIG_PARAMS" can be set to a 112 + * list of patterns considered important. For example: 113 + * 114 + * GIT_TR2_CONFIG_PARAMS="core.*,remote.*.url" 115 + * 116 + * Note: this routine does a read-only iteration on the config data 117 + * (using read_early_config()), so it must not be called until enough 118 + * of the process environment has been established. This includes the 119 + * location of the git and worktree directories, expansion of any "-c" 120 + * and "-C" command line options, and etc. 121 + */ 122 + void trace2_cmd_list_config_fl(const char *file, int line); 123 + 124 + #define trace2_cmd_list_config() trace2_cmd_list_config_fl(__FILE__, __LINE__) 125 + 126 + /* 127 + * Emit a "def_param" event for the given config key/value pair IF 128 + * we consider the key to be "interesting". 129 + * 130 + * Use this for new/updated config settings created/updated after 131 + * trace2_cmd_list_config() is called. 132 + */ 133 + void trace2_cmd_set_config_fl(const char *file, int line, const char *key, 134 + const char *value); 135 + 136 + #define trace2_cmd_set_config(k, v) \ 137 + trace2_cmd_set_config_fl(__FILE__, __LINE__, (k), (v)) 138 + 139 + /* 140 + * Emit a 'child_start' event prior to spawning a child process. 141 + * 142 + * Before calling optionally set "cmd->trace2_child_class" to a string 143 + * describing the type of the child process. For example, "editor" or 144 + * "pager". 145 + */ 146 + void trace2_child_start_fl(const char *file, int line, 147 + struct child_process *cmd); 148 + 149 + #define trace2_child_start(cmd) trace2_child_start_fl(__FILE__, __LINE__, (cmd)) 150 + 151 + /* 152 + * Emit a 'child_exit' event after the child process completes. 153 + */ 154 + void trace2_child_exit_fl(const char *file, int line, struct child_process *cmd, 155 + int child_exit_code); 156 + 157 + #define trace2_child_exit(cmd, code) \ 158 + trace2_child_exit_fl(__FILE__, __LINE__, (cmd), (code)) 159 + 160 + /* 161 + * Emit an 'exec' event prior to calling one of exec(), execv(), 162 + * execvp(), and etc. On Unix-derived systems, this will be the 163 + * last event emitted for the current process, unless the exec 164 + * fails. On Windows, exec() behaves like 'child_start' and a 165 + * waitpid(), so additional events may be emitted. 166 + * 167 + * Returns the "exec_id". 168 + */ 169 + int trace2_exec_fl(const char *file, int line, const char *exe, 170 + const char **argv); 171 + 172 + #define trace2_exec(exe, argv) trace2_exec_fl(__FILE__, __LINE__, (exe), (argv)) 173 + 174 + /* 175 + * Emit an 'exec_result' when possible. On Unix-derived systems, 176 + * this should be called after exec() returns (which only happens 177 + * when there is an error starting the new process). On Windows, 178 + * this should be called after the waitpid(). 179 + * 180 + * The "exec_id" should be the value returned from trace2_exec(). 181 + */ 182 + void trace2_exec_result_fl(const char *file, int line, int exec_id, int code); 183 + 184 + #define trace2_exec_result(id, code) \ 185 + trace2_exec_result_fl(__FILE__, __LINE__, (id), (code)) 186 + 187 + /* 188 + * Emit a 'thread_start' event. This must be called from inside the 189 + * thread-proc to set up the trace2 TLS data for the thread. 190 + * 191 + * Thread names should be descriptive, like "preload_index". 192 + * Thread names will be decorated with an instance number automatically. 193 + */ 194 + void trace2_thread_start_fl(const char *file, int line, 195 + const char *thread_name); 196 + 197 + #define trace2_thread_start(thread_name) \ 198 + trace2_thread_start_fl(__FILE__, __LINE__, (thread_name)) 199 + 200 + /* 201 + * Emit a 'thread_exit' event. This must be called from inside the 202 + * thread-proc to report thread-specific data and cleanup TLS data 203 + * for the thread. 204 + */ 205 + void trace2_thread_exit_fl(const char *file, int line); 206 + 207 + #define trace2_thread_exit() trace2_thread_exit_fl(__FILE__, __LINE__) 208 + 209 + /* 210 + * Emit a 'param' event. 211 + * 212 + * Write a "<param> = <value>" pair describing some aspect of the 213 + * run such as an important configuration setting or command line 214 + * option that significantly changes command behavior. 215 + */ 216 + void trace2_def_param_fl(const char *file, int line, const char *param, 217 + const char *value); 218 + 219 + #define trace2_def_param(param, value) \ 220 + trace2_def_param_fl(__FILE__, __LINE__, (param), (value)) 221 + 222 + /* 223 + * Tell trace2 about a newly instantiated repo object and assign 224 + * a trace2-repo-id to be used in subsequent activity events. 225 + * 226 + * Emits a 'worktree' event for this repo instance. 227 + */ 228 + void trace2_def_repo_fl(const char *file, int line, struct repository *repo); 229 + 230 + #define trace2_def_repo(repo) trace2_def_repo_fl(__FILE__, __LINE__, repo) 231 + 232 + /* 233 + * Emit a 'region_enter' event for <category>.<label> with optional 234 + * repo-id and printf message. 235 + * 236 + * Enter a new nesting level on the current thread and remember the 237 + * current time. This controls the indenting of all subsequent events 238 + * on this thread. 239 + */ 240 + void trace2_region_enter_fl(const char *file, int line, const char *category, 241 + const char *label, const struct repository *repo); 242 + 243 + #define trace2_region_enter(category, label, repo) \ 244 + trace2_region_enter_fl(__FILE__, __LINE__, (category), (label), (repo)) 245 + 246 + void trace2_region_enter_printf_va_fl(const char *file, int line, 247 + const char *category, const char *label, 248 + const struct repository *repo, 249 + const char *fmt, va_list ap); 250 + 251 + #define trace2_region_enter_printf_va(category, label, repo, fmt, ap) \ 252 + trace2_region_enter_printf_va_fl(__FILE__, __LINE__, (category), \ 253 + (label), (repo), (fmt), (ap)) 254 + 255 + void trace2_region_enter_printf_fl(const char *file, int line, 256 + const char *category, const char *label, 257 + const struct repository *repo, 258 + const char *fmt, ...); 259 + 260 + #ifdef HAVE_VARIADIC_MACROS 261 + #define trace2_region_enter_printf(category, label, repo, ...) \ 262 + trace2_region_enter_printf_fl(__FILE__, __LINE__, (category), (label), \ 263 + (repo), __VA_ARGS__) 264 + #else 265 + /* clang-format off */ 266 + __attribute__((format (region_enter_printf, 4, 5))) 267 + void trace2_region_enter_printf(const char *category, const char *label, 268 + const struct repository *repo, const char *fmt, 269 + ...); 270 + /* clang-format on */ 271 + #endif 272 + 273 + /* 274 + * Emit a 'region_leave' event for <category>.<label> with optional 275 + * repo-id and printf message. 276 + * 277 + * Leave current nesting level and report the elapsed time spent 278 + * in this nesting level. 279 + */ 280 + void trace2_region_leave_fl(const char *file, int line, const char *category, 281 + const char *label, const struct repository *repo); 282 + 283 + #define trace2_region_leave(category, label, repo) \ 284 + trace2_region_leave_fl(__FILE__, __LINE__, (category), (label), (repo)) 285 + 286 + void trace2_region_leave_printf_va_fl(const char *file, int line, 287 + const char *category, const char *label, 288 + const struct repository *repo, 289 + const char *fmt, va_list ap); 290 + 291 + #define trace2_region_leave_printf_va(category, label, repo, fmt, ap) \ 292 + trace2_region_leave_printf_va_fl(__FILE__, __LINE__, (category), \ 293 + (label), (repo), (fmt), (ap)) 294 + 295 + void trace2_region_leave_printf_fl(const char *file, int line, 296 + const char *category, const char *label, 297 + const struct repository *repo, 298 + const char *fmt, ...); 299 + 300 + #ifdef HAVE_VARIADIC_MACROS 301 + #define trace2_region_leave_printf(category, label, repo, ...) \ 302 + trace2_region_leave_printf_fl(__FILE__, __LINE__, (category), (label), \ 303 + (repo), __VA_ARGS__) 304 + #else 305 + /* clang-format off */ 306 + __attribute__((format (region_leave_printf, 4, 5))) 307 + void trace2_region_leave_printf(const char *category, const char *label, 308 + const struct repository *repo, const char *fmt, 309 + ...); 310 + /* clang-format on */ 311 + #endif 312 + 313 + /* 314 + * Emit a key-value pair 'data' event of the form <category>.<key> = <value>. 315 + * This event implicitly contains information about thread, nesting region, 316 + * and optional repo-id. 317 + * 318 + * On event-based TRACE2 targets, this generates a 'data' event suitable 319 + * for post-processing. On printf-based TRACE2 targets, this is converted 320 + * into a fixed-format printf message. 321 + */ 322 + void trace2_data_string_fl(const char *file, int line, const char *category, 323 + const struct repository *repo, const char *key, 324 + const char *value); 325 + 326 + #define trace2_data_string(category, repo, key, value) \ 327 + trace2_data_string_fl(__FILE__, __LINE__, (category), (repo), (key), \ 328 + (value)) 329 + 330 + void trace2_data_intmax_fl(const char *file, int line, const char *category, 331 + const struct repository *repo, const char *key, 332 + intmax_t value); 333 + 334 + #define trace2_data_intmax(category, repo, key, value) \ 335 + trace2_data_intmax_fl(__FILE__, __LINE__, (category), (repo), (key), \ 336 + (value)) 337 + 338 + void trace2_data_json_fl(const char *file, int line, const char *category, 339 + const struct repository *repo, const char *key, 340 + const struct json_writer *jw); 341 + 342 + #define trace2_data_json(category, repo, key, value) \ 343 + trace2_data_json_fl(__FILE__, __LINE__, (category), (repo), (key), \ 344 + (value)) 345 + 346 + /* 347 + * Emit a 'printf' event. 348 + * 349 + * Write an arbitrary formatted message to the TRACE2 targets. These 350 + * text messages should be considered as human-readable strings without 351 + * any formatting guidelines. Post-processors may choose to ignore 352 + * them. 353 + */ 354 + void trace2_printf_va_fl(const char *file, int line, const char *fmt, 355 + va_list ap); 356 + 357 + #define trace2_printf_va(fmt, ap) \ 358 + trace2_printf_va_fl(__FILE__, __LINE__, (fmt), (ap)) 359 + 360 + void trace2_printf_fl(const char *file, int line, const char *fmt, ...); 361 + 362 + #ifdef HAVE_VARIADIC_MACROS 363 + #define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__) 364 + #else 365 + /* clang-format off */ 366 + __attribute__((format (printf, 1, 2))) 367 + void trace2_printf(const char *fmt, ...); 368 + /* clang-format on */ 369 + #endif 370 + 371 + #endif /* TRACE2_H */
+90
trace2/tr2_cfg.c
··· 1 + #include "cache.h" 2 + #include "config.h" 3 + #include "tr2_cfg.h" 4 + 5 + #define TR2_ENVVAR_CFG_PARAM "GIT_TR2_CONFIG_PARAMS" 6 + 7 + static struct strbuf **tr2_cfg_patterns; 8 + static int tr2_cfg_count_patterns; 9 + static int tr2_cfg_loaded; 10 + 11 + /* 12 + * Parse a string containing a comma-delimited list of config keys 13 + * or wildcard patterns into a list of strbufs. 14 + */ 15 + static int tr2_cfg_load_patterns(void) 16 + { 17 + struct strbuf **s; 18 + const char *envvar; 19 + 20 + if (tr2_cfg_loaded) 21 + return tr2_cfg_count_patterns; 22 + tr2_cfg_loaded = 1; 23 + 24 + envvar = getenv(TR2_ENVVAR_CFG_PARAM); 25 + if (!envvar || !*envvar) 26 + return tr2_cfg_count_patterns; 27 + 28 + tr2_cfg_patterns = strbuf_split_buf(envvar, strlen(envvar), ',', -1); 29 + for (s = tr2_cfg_patterns; *s; s++) { 30 + struct strbuf *buf = *s; 31 + 32 + if (buf->len && buf->buf[buf->len - 1] == ',') 33 + strbuf_setlen(buf, buf->len - 1); 34 + strbuf_trim_trailing_newline(*s); 35 + strbuf_trim(*s); 36 + } 37 + 38 + tr2_cfg_count_patterns = s - tr2_cfg_patterns; 39 + return tr2_cfg_count_patterns; 40 + } 41 + 42 + void tr2_cfg_free_patterns(void) 43 + { 44 + if (tr2_cfg_patterns) 45 + strbuf_list_free(tr2_cfg_patterns); 46 + tr2_cfg_count_patterns = 0; 47 + tr2_cfg_loaded = 0; 48 + } 49 + 50 + struct tr2_cfg_data { 51 + const char *file; 52 + int line; 53 + }; 54 + 55 + /* 56 + * See if the given config key matches any of our patterns of interest. 57 + */ 58 + static int tr2_cfg_cb(const char *key, const char *value, void *d) 59 + { 60 + struct strbuf **s; 61 + struct tr2_cfg_data *data = (struct tr2_cfg_data *)d; 62 + 63 + for (s = tr2_cfg_patterns; *s; s++) { 64 + struct strbuf *buf = *s; 65 + int wm = wildmatch(buf->buf, key, WM_CASEFOLD); 66 + if (wm == WM_MATCH) { 67 + trace2_def_param_fl(data->file, data->line, key, value); 68 + return 0; 69 + } 70 + } 71 + 72 + return 0; 73 + } 74 + 75 + void tr2_cfg_list_config_fl(const char *file, int line) 76 + { 77 + struct tr2_cfg_data data = { file, line }; 78 + 79 + if (tr2_cfg_load_patterns() > 0) 80 + read_early_config(tr2_cfg_cb, &data); 81 + } 82 + 83 + void tr2_cfg_set_fl(const char *file, int line, const char *key, 84 + const char *value) 85 + { 86 + struct tr2_cfg_data data = { file, line }; 87 + 88 + if (tr2_cfg_load_patterns() > 0) 89 + tr2_cfg_cb(key, value, &data); 90 + }
+19
trace2/tr2_cfg.h
··· 1 + #ifndef TR2_CFG_H 2 + #define TR2_CFG_H 3 + 4 + /* 5 + * Iterate over all config settings and emit 'def_param' events for the 6 + * "interesting" ones to TRACE2. 7 + */ 8 + void tr2_cfg_list_config_fl(const char *file, int line); 9 + 10 + /* 11 + * Emit a "def_param" event for the given key/value pair IF we consider 12 + * the key to be "interesting". 13 + */ 14 + void tr2_cfg_set_fl(const char *file, int line, const char *key, 15 + const char *value); 16 + 17 + void tr2_cfg_free_patterns(void); 18 + 19 + #endif /* TR2_CFG_H */
+30
trace2/tr2_cmd_name.c
··· 1 + #include "cache.h" 2 + #include "trace2/tr2_cmd_name.h" 3 + 4 + #define TR2_ENVVAR_PARENT_NAME "GIT_TR2_PARENT_NAME" 5 + 6 + static struct strbuf tr2cmdname_hierarchy = STRBUF_INIT; 7 + 8 + void tr2_cmd_name_append_hierarchy(const char *name) 9 + { 10 + const char *parent_name = getenv(TR2_ENVVAR_PARENT_NAME); 11 + 12 + strbuf_reset(&tr2cmdname_hierarchy); 13 + if (parent_name && *parent_name) { 14 + strbuf_addstr(&tr2cmdname_hierarchy, parent_name); 15 + strbuf_addch(&tr2cmdname_hierarchy, '/'); 16 + } 17 + strbuf_addstr(&tr2cmdname_hierarchy, name); 18 + 19 + setenv(TR2_ENVVAR_PARENT_NAME, tr2cmdname_hierarchy.buf, 1); 20 + } 21 + 22 + const char *tr2_cmd_name_get_hierarchy(void) 23 + { 24 + return tr2cmdname_hierarchy.buf; 25 + } 26 + 27 + void tr2_cmd_name_release(void) 28 + { 29 + strbuf_release(&tr2cmdname_hierarchy); 30 + }
+24
trace2/tr2_cmd_name.h
··· 1 + #ifndef TR2_CMD_NAME_H 2 + #define TR2_CMD_NAME_H 3 + 4 + /* 5 + * Append the current command name to the list being maintained 6 + * in the environment. 7 + * 8 + * The hierarchy for a top-level git command is just the current 9 + * command name. For a child git process, the hierarchy includes the 10 + * names of the parent processes. 11 + * 12 + * The hierarchy for the current process will be exported to the 13 + * environment and inherited by child processes. 14 + */ 15 + void tr2_cmd_name_append_hierarchy(const char *name); 16 + 17 + /* 18 + * Get the command name hierarchy for the current process. 19 + */ 20 + const char *tr2_cmd_name_get_hierarchy(void); 21 + 22 + void tr2_cmd_name_release(void); 23 + 24 + #endif /* TR2_CMD_NAME_H */
+252
trace2/tr2_dst.c
··· 1 + #include "cache.h" 2 + #include "trace2/tr2_dst.h" 3 + 4 + /* 5 + * If a Trace2 target cannot be opened for writing, we should issue a 6 + * warning to stderr, but this is very annoying if the target is a pipe 7 + * or socket and beyond the user's control -- especially since every 8 + * git command (and sub-command) will print the message. So we silently 9 + * eat these warnings and just discard the trace data. 10 + * 11 + * Enable the following environment variable to see these warnings. 12 + */ 13 + #define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG" 14 + 15 + static int tr2_dst_want_warning(void) 16 + { 17 + static int tr2env_dst_debug = -1; 18 + 19 + if (tr2env_dst_debug == -1) { 20 + const char *env_value = getenv(TR2_ENVVAR_DST_DEBUG); 21 + if (!env_value || !*env_value) 22 + tr2env_dst_debug = 0; 23 + else 24 + tr2env_dst_debug = atoi(env_value) > 0; 25 + } 26 + 27 + return tr2env_dst_debug; 28 + } 29 + 30 + void tr2_dst_trace_disable(struct tr2_dst *dst) 31 + { 32 + if (dst->need_close) 33 + close(dst->fd); 34 + dst->fd = 0; 35 + dst->initialized = 1; 36 + dst->need_close = 0; 37 + } 38 + 39 + static int tr2_dst_try_path(struct tr2_dst *dst, const char *tgt_value) 40 + { 41 + int fd = open(tgt_value, O_WRONLY | O_APPEND | O_CREAT, 0666); 42 + if (fd == -1) { 43 + if (tr2_dst_want_warning()) 44 + warning("trace2: could not open '%s' for '%s' tracing: %s", 45 + tgt_value, dst->env_var_name, strerror(errno)); 46 + 47 + tr2_dst_trace_disable(dst); 48 + return 0; 49 + } 50 + 51 + dst->fd = fd; 52 + dst->need_close = 1; 53 + dst->initialized = 1; 54 + 55 + return dst->fd; 56 + } 57 + 58 + #ifndef NO_UNIX_SOCKETS 59 + #define PREFIX_AF_UNIX "af_unix:" 60 + #define PREFIX_AF_UNIX_STREAM "af_unix:stream:" 61 + #define PREFIX_AF_UNIX_DGRAM "af_unix:dgram:" 62 + 63 + static int tr2_dst_try_uds_connect(const char *path, int sock_type, int *out_fd) 64 + { 65 + int fd; 66 + struct sockaddr_un sa; 67 + 68 + fd = socket(AF_UNIX, sock_type, 0); 69 + if (fd == -1) 70 + return errno; 71 + 72 + sa.sun_family = AF_UNIX; 73 + strlcpy(sa.sun_path, path, sizeof(sa.sun_path)); 74 + 75 + if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { 76 + int e = errno; 77 + close(fd); 78 + return e; 79 + } 80 + 81 + *out_fd = fd; 82 + return 0; 83 + } 84 + 85 + #define TR2_DST_UDS_TRY_STREAM (1 << 0) 86 + #define TR2_DST_UDS_TRY_DGRAM (1 << 1) 87 + 88 + static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst, 89 + const char *tgt_value) 90 + { 91 + unsigned int uds_try = 0; 92 + int fd; 93 + int e; 94 + const char *path = NULL; 95 + 96 + /* 97 + * Allow "af_unix:[<type>:]<absolute_path>" 98 + * 99 + * Trace2 always writes complete individual messages (without 100 + * chunking), so we can talk to either DGRAM or STREAM type sockets. 101 + * 102 + * Allow the user to explicitly request the socket type. 103 + * 104 + * If they omit the socket type, try one and then the other. 105 + */ 106 + 107 + if (skip_prefix(tgt_value, PREFIX_AF_UNIX_STREAM, &path)) 108 + uds_try |= TR2_DST_UDS_TRY_STREAM; 109 + 110 + else if (skip_prefix(tgt_value, PREFIX_AF_UNIX_DGRAM, &path)) 111 + uds_try |= TR2_DST_UDS_TRY_DGRAM; 112 + 113 + else if (skip_prefix(tgt_value, PREFIX_AF_UNIX, &path)) 114 + uds_try |= TR2_DST_UDS_TRY_STREAM | TR2_DST_UDS_TRY_DGRAM; 115 + 116 + if (!path || !*path) { 117 + if (tr2_dst_want_warning()) 118 + warning("trace2: invalid AF_UNIX value '%s' for '%s' tracing", 119 + tgt_value, dst->env_var_name); 120 + 121 + tr2_dst_trace_disable(dst); 122 + return 0; 123 + } 124 + 125 + if (!is_absolute_path(path) || 126 + strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) { 127 + if (tr2_dst_want_warning()) 128 + warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing", 129 + path, dst->env_var_name); 130 + 131 + tr2_dst_trace_disable(dst); 132 + return 0; 133 + } 134 + 135 + if (uds_try & TR2_DST_UDS_TRY_STREAM) { 136 + e = tr2_dst_try_uds_connect(path, SOCK_STREAM, &fd); 137 + if (!e) 138 + goto connected; 139 + if (e != EPROTOTYPE) 140 + goto error; 141 + } 142 + if (uds_try & TR2_DST_UDS_TRY_DGRAM) { 143 + e = tr2_dst_try_uds_connect(path, SOCK_DGRAM, &fd); 144 + if (!e) 145 + goto connected; 146 + } 147 + 148 + error: 149 + if (tr2_dst_want_warning()) 150 + warning("trace2: could not connect to socket '%s' for '%s' tracing: %s", 151 + path, dst->env_var_name, strerror(e)); 152 + 153 + tr2_dst_trace_disable(dst); 154 + return 0; 155 + 156 + connected: 157 + dst->fd = fd; 158 + dst->need_close = 1; 159 + dst->initialized = 1; 160 + 161 + return dst->fd; 162 + } 163 + #endif 164 + 165 + static void tr2_dst_malformed_warning(struct tr2_dst *dst, 166 + const char *tgt_value) 167 + { 168 + struct strbuf buf = STRBUF_INIT; 169 + 170 + strbuf_addf(&buf, "trace2: unknown value for '%s': '%s'", 171 + dst->env_var_name, tgt_value); 172 + warning("%s", buf.buf); 173 + 174 + strbuf_release(&buf); 175 + } 176 + 177 + int tr2_dst_get_trace_fd(struct tr2_dst *dst) 178 + { 179 + const char *tgt_value; 180 + 181 + /* don't open twice */ 182 + if (dst->initialized) 183 + return dst->fd; 184 + 185 + dst->initialized = 1; 186 + 187 + tgt_value = getenv(dst->env_var_name); 188 + 189 + if (!tgt_value || !strcmp(tgt_value, "") || !strcmp(tgt_value, "0") || 190 + !strcasecmp(tgt_value, "false")) { 191 + dst->fd = 0; 192 + return dst->fd; 193 + } 194 + 195 + if (!strcmp(tgt_value, "1") || !strcasecmp(tgt_value, "true")) { 196 + dst->fd = STDERR_FILENO; 197 + return dst->fd; 198 + } 199 + 200 + if (strlen(tgt_value) == 1 && isdigit(*tgt_value)) { 201 + dst->fd = atoi(tgt_value); 202 + return dst->fd; 203 + } 204 + 205 + if (is_absolute_path(tgt_value)) 206 + return tr2_dst_try_path(dst, tgt_value); 207 + 208 + #ifndef NO_UNIX_SOCKETS 209 + if (starts_with(tgt_value, PREFIX_AF_UNIX)) 210 + return tr2_dst_try_unix_domain_socket(dst, tgt_value); 211 + #endif 212 + 213 + /* Always warn about malformed values. */ 214 + tr2_dst_malformed_warning(dst, tgt_value); 215 + tr2_dst_trace_disable(dst); 216 + return 0; 217 + } 218 + 219 + int tr2_dst_trace_want(struct tr2_dst *dst) 220 + { 221 + return !!tr2_dst_get_trace_fd(dst); 222 + } 223 + 224 + void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line) 225 + { 226 + int fd = tr2_dst_get_trace_fd(dst); 227 + 228 + strbuf_complete_line(buf_line); /* ensure final NL on buffer */ 229 + 230 + /* 231 + * We do not use write_in_full() because we do not want 232 + * a short-write to try again. We are using O_APPEND mode 233 + * files and the kernel handles the atomic seek+write. If 234 + * another thread or git process is concurrently writing to 235 + * this fd or file, our remainder-write may not be contiguous 236 + * with our initial write of this message. And that will 237 + * confuse readers. So just don't bother. 238 + * 239 + * It is assumed that TRACE2 messages are short enough that 240 + * the system can write them in 1 attempt and we won't see 241 + * a short-write. 242 + * 243 + * If we get an IO error, just close the trace dst. 244 + */ 245 + if (write(fd, buf_line->buf, buf_line->len) >= 0) 246 + return; 247 + 248 + if (tr2_dst_want_warning()) 249 + warning("unable to write trace to '%s': %s", dst->env_var_name, 250 + strerror(errno)); 251 + tr2_dst_trace_disable(dst); 252 + }
+36
trace2/tr2_dst.h
··· 1 + #ifndef TR2_DST_H 2 + #define TR2_DST_H 3 + 4 + struct strbuf; 5 + 6 + struct tr2_dst { 7 + const char *const env_var_name; 8 + int fd; 9 + unsigned int initialized : 1; 10 + unsigned int need_close : 1; 11 + }; 12 + 13 + /* 14 + * Disable TRACE2 on the destination. In TRACE2 a destination (DST) 15 + * wraps a file descriptor; it is associated with a TARGET which 16 + * defines the formatting. 17 + */ 18 + void tr2_dst_trace_disable(struct tr2_dst *dst); 19 + 20 + /* 21 + * Return the file descriptor for the DST. 22 + * If 0, the dst is closed or disabled. 23 + */ 24 + int tr2_dst_get_trace_fd(struct tr2_dst *dst); 25 + 26 + /* 27 + * Return true if the DST is opened for writing. 28 + */ 29 + int tr2_dst_trace_want(struct tr2_dst *dst); 30 + 31 + /* 32 + * Write a single line/message to the trace file. 33 + */ 34 + void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line); 35 + 36 + #endif /* TR2_DST_H */
+67
trace2/tr2_sid.c
··· 1 + #include "cache.h" 2 + #include "trace2/tr2_sid.h" 3 + 4 + #define TR2_ENVVAR_PARENT_SID "GIT_TR2_PARENT_SID" 5 + 6 + static struct strbuf tr2sid_buf = STRBUF_INIT; 7 + static int tr2sid_nr_git_parents; 8 + 9 + /* 10 + * Compute a "unique" session id (SID) for the current process. This allows 11 + * all events from this process to have a single label (much like a PID). 12 + * 13 + * Export this into our environment so that all child processes inherit it. 14 + * 15 + * If we were started by another git instance, use our parent's SID as a 16 + * prefix. (This lets us track parent/child relationships even if there 17 + * is an intermediate shell process.) 18 + * 19 + * Additionally, count the number of nested git processes. 20 + */ 21 + static void tr2_sid_compute(void) 22 + { 23 + uint64_t us_now; 24 + const char *parent_sid; 25 + 26 + if (tr2sid_buf.len) 27 + return; 28 + 29 + parent_sid = getenv(TR2_ENVVAR_PARENT_SID); 30 + if (parent_sid && *parent_sid) { 31 + const char *p; 32 + for (p = parent_sid; *p; p++) 33 + if (*p == '/') 34 + tr2sid_nr_git_parents++; 35 + 36 + strbuf_addstr(&tr2sid_buf, parent_sid); 37 + strbuf_addch(&tr2sid_buf, '/'); 38 + tr2sid_nr_git_parents++; 39 + } 40 + 41 + us_now = getnanotime() / 1000; 42 + strbuf_addf(&tr2sid_buf, "%" PRIuMAX "-%" PRIdMAX, (uintmax_t)us_now, 43 + (intmax_t)getpid()); 44 + 45 + setenv(TR2_ENVVAR_PARENT_SID, tr2sid_buf.buf, 1); 46 + } 47 + 48 + const char *tr2_sid_get(void) 49 + { 50 + if (!tr2sid_buf.len) 51 + tr2_sid_compute(); 52 + 53 + return tr2sid_buf.buf; 54 + } 55 + 56 + int tr2_sid_depth(void) 57 + { 58 + if (!tr2sid_buf.len) 59 + tr2_sid_compute(); 60 + 61 + return tr2sid_nr_git_parents; 62 + } 63 + 64 + void tr2_sid_release(void) 65 + { 66 + strbuf_release(&tr2sid_buf); 67 + }
+18
trace2/tr2_sid.h
··· 1 + #ifndef TR2_SID_H 2 + #define TR2_SID_H 3 + 4 + /* 5 + * Get our session id. Compute if necessary. 6 + */ 7 + const char *tr2_sid_get(void); 8 + 9 + /* 10 + * Get our process depth. A top-level git process invoked from the 11 + * command line will have depth=0. A child git process will have 12 + * depth=1 and so on. 13 + */ 14 + int tr2_sid_depth(void); 15 + 16 + void tr2_sid_release(void); 17 + 18 + #endif /* TR2_SID_H */
+32
trace2/tr2_tbuf.c
··· 1 + #include "cache.h" 2 + #include "tr2_tbuf.h" 3 + 4 + void tr2_tbuf_local_time(struct tr2_tbuf *tb) 5 + { 6 + struct timeval tv; 7 + struct tm tm; 8 + time_t secs; 9 + 10 + gettimeofday(&tv, NULL); 11 + secs = tv.tv_sec; 12 + localtime_r(&secs, &tm); 13 + 14 + xsnprintf(tb->buf, sizeof(tb->buf), "%02d:%02d:%02d.%06ld", tm.tm_hour, 15 + tm.tm_min, tm.tm_sec, (long)tv.tv_usec); 16 + } 17 + 18 + void tr2_tbuf_utc_time(struct tr2_tbuf *tb) 19 + { 20 + struct timeval tv; 21 + struct tm tm; 22 + time_t secs; 23 + 24 + gettimeofday(&tv, NULL); 25 + secs = tv.tv_sec; 26 + gmtime_r(&secs, &tm); 27 + 28 + xsnprintf(tb->buf, sizeof(tb->buf), 29 + "%4d-%02d-%02d %02d:%02d:%02d.%06ld", tm.tm_year + 1900, 30 + tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, 31 + (long)tv.tv_usec); 32 + }
+23
trace2/tr2_tbuf.h
··· 1 + #ifndef TR2_TBUF_H 2 + #define TR2_TBUF_H 3 + 4 + /* 5 + * A simple wrapper around a fixed buffer to avoid C syntax 6 + * quirks and the need to pass around an additional size_t 7 + * argument. 8 + */ 9 + struct tr2_tbuf { 10 + char buf[32]; 11 + }; 12 + 13 + /* 14 + * Fill buffer with formatted local time string. 15 + */ 16 + void tr2_tbuf_local_time(struct tr2_tbuf *tb); 17 + 18 + /* 19 + * Fill buffer with formatted UTC time string. 20 + */ 21 + void tr2_tbuf_utc_time(struct tr2_tbuf *tb); 22 + 23 + #endif /* TR2_TBUF_H */
+133
trace2/tr2_tgt.h
··· 1 + #ifndef TR2_TGT_H 2 + #define TR2_TGT_H 3 + 4 + struct child_process; 5 + struct repository; 6 + struct json_writer; 7 + 8 + /* 9 + * Function prototypes for a TRACE2 "target" vtable. 10 + */ 11 + 12 + typedef int(tr2_tgt_init_t)(void); 13 + typedef void(tr2_tgt_term_t)(void); 14 + 15 + typedef void(tr2_tgt_evt_version_fl_t)(const char *file, int line); 16 + 17 + typedef void(tr2_tgt_evt_start_fl_t)(const char *file, int line, 18 + const char **argv); 19 + typedef void(tr2_tgt_evt_exit_fl_t)(const char *file, int line, 20 + uint64_t us_elapsed_absolute, int code); 21 + typedef void(tr2_tgt_evt_signal_t)(uint64_t us_elapsed_absolute, int signo); 22 + typedef void(tr2_tgt_evt_atexit_t)(uint64_t us_elapsed_absolute, int code); 23 + 24 + typedef void(tr2_tgt_evt_error_va_fl_t)(const char *file, int line, 25 + const char *fmt, va_list ap); 26 + 27 + typedef void(tr2_tgt_evt_command_path_fl_t)(const char *file, int line, 28 + const char *command_path); 29 + typedef void(tr2_tgt_evt_command_name_fl_t)(const char *file, int line, 30 + const char *name, 31 + const char *hierarchy); 32 + typedef void(tr2_tgt_evt_command_mode_fl_t)(const char *file, int line, 33 + const char *mode); 34 + 35 + typedef void(tr2_tgt_evt_alias_fl_t)(const char *file, int line, 36 + const char *alias, const char **argv); 37 + 38 + typedef void(tr2_tgt_evt_child_start_fl_t)(const char *file, int line, 39 + uint64_t us_elapsed_absolute, 40 + const struct child_process *cmd); 41 + typedef void(tr2_tgt_evt_child_exit_fl_t)(const char *file, int line, 42 + uint64_t us_elapsed_absolute, int cid, 43 + int pid, int code, 44 + uint64_t us_elapsed_child); 45 + 46 + typedef void(tr2_tgt_evt_thread_start_fl_t)(const char *file, int line, 47 + uint64_t us_elapsed_absolute); 48 + typedef void(tr2_tgt_evt_thread_exit_fl_t)(const char *file, int line, 49 + uint64_t us_elapsed_absolute, 50 + uint64_t us_elapsed_thread); 51 + 52 + typedef void(tr2_tgt_evt_exec_fl_t)(const char *file, int line, 53 + uint64_t us_elapsed_absolute, int exec_id, 54 + const char *exe, const char **argv); 55 + typedef void(tr2_tgt_evt_exec_result_fl_t)(const char *file, int line, 56 + uint64_t us_elapsed_absolute, 57 + int exec_id, int code); 58 + 59 + typedef void(tr2_tgt_evt_param_fl_t)(const char *file, int line, 60 + const char *param, const char *value); 61 + 62 + typedef void(tr2_tgt_evt_repo_fl_t)(const char *file, int line, 63 + const struct repository *repo); 64 + 65 + typedef void(tr2_tgt_evt_region_enter_printf_va_fl_t)( 66 + const char *file, int line, uint64_t us_elapsed_absolute, 67 + const char *category, const char *label, const struct repository *repo, 68 + const char *fmt, va_list ap); 69 + typedef void(tr2_tgt_evt_region_leave_printf_va_fl_t)( 70 + const char *file, int line, uint64_t us_elapsed_absolute, 71 + uint64_t us_elapsed_region, const char *category, const char *label, 72 + const struct repository *repo, const char *fmt, va_list ap); 73 + 74 + typedef void(tr2_tgt_evt_data_fl_t)(const char *file, int line, 75 + uint64_t us_elapsed_absolute, 76 + uint64_t us_elapsed_region, 77 + const char *category, 78 + const struct repository *repo, 79 + const char *key, const char *value); 80 + typedef void(tr2_tgt_evt_data_json_fl_t)(const char *file, int line, 81 + uint64_t us_elapsed_absolute, 82 + uint64_t us_elapsed_region, 83 + const char *category, 84 + const struct repository *repo, 85 + const char *key, 86 + const struct json_writer *value); 87 + 88 + typedef void(tr2_tgt_evt_printf_va_fl_t)(const char *file, int line, 89 + uint64_t us_elapsed_absolute, 90 + const char *fmt, va_list ap); 91 + 92 + /* 93 + * "vtable" for a TRACE2 target. Use NULL if a target does not want 94 + * to emit that message. 95 + */ 96 + /* clang-format off */ 97 + struct tr2_tgt { 98 + struct tr2_dst *pdst; 99 + 100 + tr2_tgt_init_t *pfn_init; 101 + tr2_tgt_term_t *pfn_term; 102 + 103 + tr2_tgt_evt_version_fl_t *pfn_version_fl; 104 + tr2_tgt_evt_start_fl_t *pfn_start_fl; 105 + tr2_tgt_evt_exit_fl_t *pfn_exit_fl; 106 + tr2_tgt_evt_signal_t *pfn_signal; 107 + tr2_tgt_evt_atexit_t *pfn_atexit; 108 + tr2_tgt_evt_error_va_fl_t *pfn_error_va_fl; 109 + tr2_tgt_evt_command_path_fl_t *pfn_command_path_fl; 110 + tr2_tgt_evt_command_name_fl_t *pfn_command_name_fl; 111 + tr2_tgt_evt_command_mode_fl_t *pfn_command_mode_fl; 112 + tr2_tgt_evt_alias_fl_t *pfn_alias_fl; 113 + tr2_tgt_evt_child_start_fl_t *pfn_child_start_fl; 114 + tr2_tgt_evt_child_exit_fl_t *pfn_child_exit_fl; 115 + tr2_tgt_evt_thread_start_fl_t *pfn_thread_start_fl; 116 + tr2_tgt_evt_thread_exit_fl_t *pfn_thread_exit_fl; 117 + tr2_tgt_evt_exec_fl_t *pfn_exec_fl; 118 + tr2_tgt_evt_exec_result_fl_t *pfn_exec_result_fl; 119 + tr2_tgt_evt_param_fl_t *pfn_param_fl; 120 + tr2_tgt_evt_repo_fl_t *pfn_repo_fl; 121 + tr2_tgt_evt_region_enter_printf_va_fl_t *pfn_region_enter_printf_va_fl; 122 + tr2_tgt_evt_region_leave_printf_va_fl_t *pfn_region_leave_printf_va_fl; 123 + tr2_tgt_evt_data_fl_t *pfn_data_fl; 124 + tr2_tgt_evt_data_json_fl_t *pfn_data_json_fl; 125 + tr2_tgt_evt_printf_va_fl_t *pfn_printf_va_fl; 126 + }; 127 + /* clang-format on */ 128 + 129 + extern struct tr2_tgt tr2_tgt_event; 130 + extern struct tr2_tgt tr2_tgt_normal; 131 + extern struct tr2_tgt tr2_tgt_perf; 132 + 133 + #endif /* TR2_TGT_H */
+588
trace2/tr2_tgt_event.c
··· 1 + #include "cache.h" 2 + #include "config.h" 3 + #include "json-writer.h" 4 + #include "run-command.h" 5 + #include "version.h" 6 + #include "trace2/tr2_dst.h" 7 + #include "trace2/tr2_tbuf.h" 8 + #include "trace2/tr2_sid.h" 9 + #include "trace2/tr2_tgt.h" 10 + #include "trace2/tr2_tls.h" 11 + 12 + static struct tr2_dst tr2dst_event = { "GIT_TR2_EVENT", 0, 0, 0 }; 13 + 14 + /* 15 + * The version number of the JSON data generated by the EVENT target 16 + * in this source file. Update this if you make a significant change 17 + * to the JSON fields or message structure. You probably do not need 18 + * to update this if you just add another call to one of the existing 19 + * TRACE2 API methods. 20 + */ 21 + #define TR2_EVENT_VERSION "1" 22 + 23 + /* 24 + * Region nesting limit for messages written to the event target. 25 + * 26 + * The "region_enter" and "region_leave" messages (especially recursive 27 + * messages such as those produced while diving the worktree or index) 28 + * are primarily intended for the performance target during debugging. 29 + * 30 + * Some of the outer-most messages, however, may be of interest to the 31 + * event target. Set this environment variable to a larger integer for 32 + * more detail in the event target. 33 + */ 34 + #define TR2_ENVVAR_EVENT_NESTING "GIT_TR2_EVENT_NESTING" 35 + static int tr2env_event_nesting_wanted = 2; 36 + 37 + /* 38 + * Set this environment variable to true to omit the <time>, <file>, and 39 + * <line> fields from most events. 40 + */ 41 + #define TR2_ENVVAR_EVENT_BRIEF "GIT_TR2_EVENT_BRIEF" 42 + static int tr2env_event_brief; 43 + 44 + static int fn_init(void) 45 + { 46 + int want = tr2_dst_trace_want(&tr2dst_event); 47 + int want_nesting; 48 + int want_brief; 49 + char *nesting; 50 + char *brief; 51 + 52 + if (!want) 53 + return want; 54 + 55 + nesting = getenv(TR2_ENVVAR_EVENT_NESTING); 56 + if (nesting && ((want_nesting = atoi(nesting)) > 0)) 57 + tr2env_event_nesting_wanted = want_nesting; 58 + 59 + brief = getenv(TR2_ENVVAR_EVENT_BRIEF); 60 + if (brief && ((want_brief = atoi(brief)) > 0)) 61 + tr2env_event_brief = want_brief; 62 + 63 + return want; 64 + } 65 + 66 + static void fn_term(void) 67 + { 68 + tr2_dst_trace_disable(&tr2dst_event); 69 + } 70 + 71 + /* 72 + * Append common key-value pairs to the currently open JSON object. 73 + * "event:"<event_name>" 74 + * "sid":"<sid>" 75 + * "thread":"<thread_name>" 76 + * "time":"<time>" 77 + * "file":"<filename>" 78 + * "line":<line_number> 79 + * "repo":<repo_id> 80 + */ 81 + static void event_fmt_prepare(const char *event_name, const char *file, 82 + int line, const struct repository *repo, 83 + struct json_writer *jw) 84 + { 85 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 86 + struct tr2_tbuf tb_now; 87 + 88 + jw_object_string(jw, "event", event_name); 89 + jw_object_string(jw, "sid", tr2_sid_get()); 90 + jw_object_string(jw, "thread", ctx->thread_name.buf); 91 + 92 + /* 93 + * In brief mode, only emit <time> on these 2 event types. 94 + */ 95 + if (!tr2env_event_brief || !strcmp(event_name, "version") || 96 + !strcmp(event_name, "atexit")) { 97 + tr2_tbuf_utc_time(&tb_now); 98 + jw_object_string(jw, "time", tb_now.buf); 99 + } 100 + 101 + if (!tr2env_event_brief && file && *file) { 102 + jw_object_string(jw, "file", file); 103 + jw_object_intmax(jw, "line", line); 104 + } 105 + 106 + if (repo) 107 + jw_object_intmax(jw, "repo", repo->trace2_repo_id); 108 + } 109 + 110 + static void fn_version_fl(const char *file, int line) 111 + { 112 + const char *event_name = "version"; 113 + struct json_writer jw = JSON_WRITER_INIT; 114 + 115 + jw_object_begin(&jw, 0); 116 + event_fmt_prepare(event_name, file, line, NULL, &jw); 117 + jw_object_string(&jw, "evt", TR2_EVENT_VERSION); 118 + jw_object_string(&jw, "exe", git_version_string); 119 + jw_end(&jw); 120 + 121 + tr2_dst_write_line(&tr2dst_event, &jw.json); 122 + jw_release(&jw); 123 + } 124 + 125 + static void fn_start_fl(const char *file, int line, const char **argv) 126 + { 127 + const char *event_name = "start"; 128 + struct json_writer jw = JSON_WRITER_INIT; 129 + 130 + jw_object_begin(&jw, 0); 131 + event_fmt_prepare(event_name, file, line, NULL, &jw); 132 + jw_object_inline_begin_array(&jw, "argv"); 133 + jw_array_argv(&jw, argv); 134 + jw_end(&jw); 135 + jw_end(&jw); 136 + 137 + tr2_dst_write_line(&tr2dst_event, &jw.json); 138 + jw_release(&jw); 139 + } 140 + 141 + static void fn_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute, 142 + int code) 143 + { 144 + const char *event_name = "exit"; 145 + struct json_writer jw = JSON_WRITER_INIT; 146 + double t_abs = (double)us_elapsed_absolute / 1000000.0; 147 + 148 + jw_object_begin(&jw, 0); 149 + event_fmt_prepare(event_name, file, line, NULL, &jw); 150 + jw_object_double(&jw, "t_abs", 6, t_abs); 151 + jw_object_intmax(&jw, "code", code); 152 + jw_end(&jw); 153 + 154 + tr2_dst_write_line(&tr2dst_event, &jw.json); 155 + jw_release(&jw); 156 + } 157 + 158 + static void fn_signal(uint64_t us_elapsed_absolute, int signo) 159 + { 160 + const char *event_name = "signal"; 161 + struct json_writer jw = JSON_WRITER_INIT; 162 + double t_abs = (double)us_elapsed_absolute / 1000000.0; 163 + 164 + jw_object_begin(&jw, 0); 165 + event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw); 166 + jw_object_double(&jw, "t_abs", 6, t_abs); 167 + jw_object_intmax(&jw, "signo", signo); 168 + jw_end(&jw); 169 + 170 + tr2_dst_write_line(&tr2dst_event, &jw.json); 171 + jw_release(&jw); 172 + } 173 + 174 + static void fn_atexit(uint64_t us_elapsed_absolute, int code) 175 + { 176 + const char *event_name = "atexit"; 177 + struct json_writer jw = JSON_WRITER_INIT; 178 + double t_abs = (double)us_elapsed_absolute / 1000000.0; 179 + 180 + jw_object_begin(&jw, 0); 181 + event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw); 182 + jw_object_double(&jw, "t_abs", 6, t_abs); 183 + jw_object_intmax(&jw, "code", code); 184 + jw_end(&jw); 185 + 186 + tr2_dst_write_line(&tr2dst_event, &jw.json); 187 + jw_release(&jw); 188 + } 189 + 190 + static void maybe_add_string_va(struct json_writer *jw, const char *field_name, 191 + const char *fmt, va_list ap) 192 + { 193 + if (fmt && *fmt && ap) { 194 + va_list copy_ap; 195 + struct strbuf buf = STRBUF_INIT; 196 + 197 + va_copy(copy_ap, ap); 198 + strbuf_vaddf(&buf, fmt, copy_ap); 199 + va_end(copy_ap); 200 + 201 + jw_object_string(jw, field_name, buf.buf); 202 + strbuf_release(&buf); 203 + return; 204 + } 205 + 206 + if (fmt && *fmt) { 207 + jw_object_string(jw, field_name, fmt); 208 + return; 209 + } 210 + } 211 + 212 + static void fn_error_va_fl(const char *file, int line, const char *fmt, 213 + va_list ap) 214 + { 215 + const char *event_name = "error"; 216 + struct json_writer jw = JSON_WRITER_INIT; 217 + 218 + jw_object_begin(&jw, 0); 219 + event_fmt_prepare(event_name, file, line, NULL, &jw); 220 + maybe_add_string_va(&jw, "msg", fmt, ap); 221 + /* 222 + * Also emit the format string as a field in case 223 + * post-processors want to aggregate common error 224 + * messages by type without argument fields (such 225 + * as pathnames or branch names) cluttering it up. 226 + */ 227 + if (fmt && *fmt) 228 + jw_object_string(&jw, "fmt", fmt); 229 + jw_end(&jw); 230 + 231 + tr2_dst_write_line(&tr2dst_event, &jw.json); 232 + jw_release(&jw); 233 + } 234 + 235 + static void fn_command_path_fl(const char *file, int line, const char *pathname) 236 + { 237 + const char *event_name = "cmd_path"; 238 + struct json_writer jw = JSON_WRITER_INIT; 239 + 240 + jw_object_begin(&jw, 0); 241 + event_fmt_prepare(event_name, file, line, NULL, &jw); 242 + jw_object_string(&jw, "path", pathname); 243 + jw_end(&jw); 244 + 245 + tr2_dst_write_line(&tr2dst_event, &jw.json); 246 + jw_release(&jw); 247 + } 248 + 249 + static void fn_command_name_fl(const char *file, int line, const char *name, 250 + const char *hierarchy) 251 + { 252 + const char *event_name = "cmd_name"; 253 + struct json_writer jw = JSON_WRITER_INIT; 254 + 255 + jw_object_begin(&jw, 0); 256 + event_fmt_prepare(event_name, file, line, NULL, &jw); 257 + jw_object_string(&jw, "name", name); 258 + if (hierarchy && *hierarchy) 259 + jw_object_string(&jw, "hierarchy", hierarchy); 260 + jw_end(&jw); 261 + 262 + tr2_dst_write_line(&tr2dst_event, &jw.json); 263 + jw_release(&jw); 264 + } 265 + 266 + static void fn_command_mode_fl(const char *file, int line, const char *mode) 267 + { 268 + const char *event_name = "cmd_mode"; 269 + struct json_writer jw = JSON_WRITER_INIT; 270 + 271 + jw_object_begin(&jw, 0); 272 + event_fmt_prepare(event_name, file, line, NULL, &jw); 273 + jw_object_string(&jw, "name", mode); 274 + jw_end(&jw); 275 + 276 + tr2_dst_write_line(&tr2dst_event, &jw.json); 277 + jw_release(&jw); 278 + } 279 + 280 + static void fn_alias_fl(const char *file, int line, const char *alias, 281 + const char **argv) 282 + { 283 + const char *event_name = "alias"; 284 + struct json_writer jw = JSON_WRITER_INIT; 285 + 286 + jw_object_begin(&jw, 0); 287 + event_fmt_prepare(event_name, file, line, NULL, &jw); 288 + jw_object_string(&jw, "alias", alias); 289 + jw_object_inline_begin_array(&jw, "argv"); 290 + jw_array_argv(&jw, argv); 291 + jw_end(&jw); 292 + jw_end(&jw); 293 + 294 + tr2_dst_write_line(&tr2dst_event, &jw.json); 295 + jw_release(&jw); 296 + } 297 + 298 + static void fn_child_start_fl(const char *file, int line, 299 + uint64_t us_elapsed_absolute, 300 + const struct child_process *cmd) 301 + { 302 + const char *event_name = "child_start"; 303 + struct json_writer jw = JSON_WRITER_INIT; 304 + 305 + jw_object_begin(&jw, 0); 306 + event_fmt_prepare(event_name, file, line, NULL, &jw); 307 + jw_object_intmax(&jw, "child_id", cmd->trace2_child_id); 308 + if (cmd->trace2_hook_name) { 309 + jw_object_string(&jw, "child_class", "hook"); 310 + jw_object_string(&jw, "hook_name", cmd->trace2_hook_name); 311 + } else { 312 + const char *child_class = 313 + cmd->trace2_child_class ? cmd->trace2_child_class : "?"; 314 + jw_object_string(&jw, "child_class", child_class); 315 + } 316 + if (cmd->dir) 317 + jw_object_string(&jw, "cd", cmd->dir); 318 + jw_object_bool(&jw, "use_shell", cmd->use_shell); 319 + jw_object_inline_begin_array(&jw, "argv"); 320 + if (cmd->git_cmd) 321 + jw_array_string(&jw, "git"); 322 + jw_array_argv(&jw, cmd->argv); 323 + jw_end(&jw); 324 + jw_end(&jw); 325 + 326 + tr2_dst_write_line(&tr2dst_event, &jw.json); 327 + jw_release(&jw); 328 + } 329 + 330 + static void fn_child_exit_fl(const char *file, int line, 331 + uint64_t us_elapsed_absolute, int cid, int pid, 332 + int code, uint64_t us_elapsed_child) 333 + { 334 + const char *event_name = "child_exit"; 335 + struct json_writer jw = JSON_WRITER_INIT; 336 + double t_rel = (double)us_elapsed_child / 1000000.0; 337 + 338 + jw_object_begin(&jw, 0); 339 + event_fmt_prepare(event_name, file, line, NULL, &jw); 340 + jw_object_intmax(&jw, "child_id", cid); 341 + jw_object_intmax(&jw, "pid", pid); 342 + jw_object_intmax(&jw, "code", code); 343 + jw_object_double(&jw, "t_rel", 6, t_rel); 344 + jw_end(&jw); 345 + 346 + tr2_dst_write_line(&tr2dst_event, &jw.json); 347 + 348 + jw_release(&jw); 349 + } 350 + 351 + static void fn_thread_start_fl(const char *file, int line, 352 + uint64_t us_elapsed_absolute) 353 + { 354 + const char *event_name = "thread_start"; 355 + struct json_writer jw = JSON_WRITER_INIT; 356 + 357 + jw_object_begin(&jw, 0); 358 + event_fmt_prepare(event_name, file, line, NULL, &jw); 359 + jw_end(&jw); 360 + 361 + tr2_dst_write_line(&tr2dst_event, &jw.json); 362 + jw_release(&jw); 363 + } 364 + 365 + static void fn_thread_exit_fl(const char *file, int line, 366 + uint64_t us_elapsed_absolute, 367 + uint64_t us_elapsed_thread) 368 + { 369 + const char *event_name = "thread_exit"; 370 + struct json_writer jw = JSON_WRITER_INIT; 371 + double t_rel = (double)us_elapsed_thread / 1000000.0; 372 + 373 + jw_object_begin(&jw, 0); 374 + event_fmt_prepare(event_name, file, line, NULL, &jw); 375 + jw_object_double(&jw, "t_rel", 6, t_rel); 376 + jw_end(&jw); 377 + 378 + tr2_dst_write_line(&tr2dst_event, &jw.json); 379 + jw_release(&jw); 380 + } 381 + 382 + static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, 383 + int exec_id, const char *exe, const char **argv) 384 + { 385 + const char *event_name = "exec"; 386 + struct json_writer jw = JSON_WRITER_INIT; 387 + 388 + jw_object_begin(&jw, 0); 389 + event_fmt_prepare(event_name, file, line, NULL, &jw); 390 + jw_object_intmax(&jw, "exec_id", exec_id); 391 + if (exe) 392 + jw_object_string(&jw, "exe", exe); 393 + jw_object_inline_begin_array(&jw, "argv"); 394 + jw_array_argv(&jw, argv); 395 + jw_end(&jw); 396 + jw_end(&jw); 397 + 398 + tr2_dst_write_line(&tr2dst_event, &jw.json); 399 + jw_release(&jw); 400 + } 401 + 402 + static void fn_exec_result_fl(const char *file, int line, 403 + uint64_t us_elapsed_absolute, int exec_id, 404 + int code) 405 + { 406 + const char *event_name = "exec_result"; 407 + struct json_writer jw = JSON_WRITER_INIT; 408 + 409 + jw_object_begin(&jw, 0); 410 + event_fmt_prepare(event_name, file, line, NULL, &jw); 411 + jw_object_intmax(&jw, "exec_id", exec_id); 412 + jw_object_intmax(&jw, "code", code); 413 + jw_end(&jw); 414 + 415 + tr2_dst_write_line(&tr2dst_event, &jw.json); 416 + jw_release(&jw); 417 + } 418 + 419 + static void fn_param_fl(const char *file, int line, const char *param, 420 + const char *value) 421 + { 422 + const char *event_name = "def_param"; 423 + struct json_writer jw = JSON_WRITER_INIT; 424 + 425 + jw_object_begin(&jw, 0); 426 + event_fmt_prepare(event_name, file, line, NULL, &jw); 427 + jw_object_string(&jw, "param", param); 428 + jw_object_string(&jw, "value", value); 429 + jw_end(&jw); 430 + 431 + tr2_dst_write_line(&tr2dst_event, &jw.json); 432 + jw_release(&jw); 433 + } 434 + 435 + static void fn_repo_fl(const char *file, int line, 436 + const struct repository *repo) 437 + { 438 + const char *event_name = "def_repo"; 439 + struct json_writer jw = JSON_WRITER_INIT; 440 + 441 + jw_object_begin(&jw, 0); 442 + event_fmt_prepare(event_name, file, line, repo, &jw); 443 + jw_object_string(&jw, "worktree", repo->worktree); 444 + jw_end(&jw); 445 + 446 + tr2_dst_write_line(&tr2dst_event, &jw.json); 447 + jw_release(&jw); 448 + } 449 + 450 + static void fn_region_enter_printf_va_fl(const char *file, int line, 451 + uint64_t us_elapsed_absolute, 452 + const char *category, 453 + const char *label, 454 + const struct repository *repo, 455 + const char *fmt, va_list ap) 456 + { 457 + const char *event_name = "region_enter"; 458 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 459 + if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { 460 + struct json_writer jw = JSON_WRITER_INIT; 461 + 462 + jw_object_begin(&jw, 0); 463 + event_fmt_prepare(event_name, file, line, repo, &jw); 464 + jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); 465 + if (category) 466 + jw_object_string(&jw, "category", category); 467 + if (label) 468 + jw_object_string(&jw, "label", label); 469 + maybe_add_string_va(&jw, "msg", fmt, ap); 470 + jw_end(&jw); 471 + 472 + tr2_dst_write_line(&tr2dst_event, &jw.json); 473 + jw_release(&jw); 474 + } 475 + } 476 + 477 + static void fn_region_leave_printf_va_fl( 478 + const char *file, int line, uint64_t us_elapsed_absolute, 479 + uint64_t us_elapsed_region, const char *category, const char *label, 480 + const struct repository *repo, const char *fmt, va_list ap) 481 + { 482 + const char *event_name = "region_leave"; 483 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 484 + if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { 485 + struct json_writer jw = JSON_WRITER_INIT; 486 + double t_rel = (double)us_elapsed_region / 1000000.0; 487 + 488 + jw_object_begin(&jw, 0); 489 + event_fmt_prepare(event_name, file, line, repo, &jw); 490 + jw_object_double(&jw, "t_rel", 6, t_rel); 491 + jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); 492 + if (category) 493 + jw_object_string(&jw, "category", category); 494 + if (label) 495 + jw_object_string(&jw, "label", label); 496 + maybe_add_string_va(&jw, "msg", fmt, ap); 497 + jw_end(&jw); 498 + 499 + tr2_dst_write_line(&tr2dst_event, &jw.json); 500 + jw_release(&jw); 501 + } 502 + } 503 + 504 + static void fn_data_fl(const char *file, int line, uint64_t us_elapsed_absolute, 505 + uint64_t us_elapsed_region, const char *category, 506 + const struct repository *repo, const char *key, 507 + const char *value) 508 + { 509 + const char *event_name = "data"; 510 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 511 + if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { 512 + struct json_writer jw = JSON_WRITER_INIT; 513 + double t_abs = (double)us_elapsed_absolute / 1000000.0; 514 + double t_rel = (double)us_elapsed_region / 1000000.0; 515 + 516 + jw_object_begin(&jw, 0); 517 + event_fmt_prepare(event_name, file, line, repo, &jw); 518 + jw_object_double(&jw, "t_abs", 6, t_abs); 519 + jw_object_double(&jw, "t_rel", 6, t_rel); 520 + jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); 521 + jw_object_string(&jw, "category", category); 522 + jw_object_string(&jw, "key", key); 523 + jw_object_string(&jw, "value", value); 524 + jw_end(&jw); 525 + 526 + tr2_dst_write_line(&tr2dst_event, &jw.json); 527 + jw_release(&jw); 528 + } 529 + } 530 + 531 + static void fn_data_json_fl(const char *file, int line, 532 + uint64_t us_elapsed_absolute, 533 + uint64_t us_elapsed_region, const char *category, 534 + const struct repository *repo, const char *key, 535 + const struct json_writer *value) 536 + { 537 + const char *event_name = "data_json"; 538 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 539 + if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { 540 + struct json_writer jw = JSON_WRITER_INIT; 541 + double t_abs = (double)us_elapsed_absolute / 1000000.0; 542 + double t_rel = (double)us_elapsed_region / 1000000.0; 543 + 544 + jw_object_begin(&jw, 0); 545 + event_fmt_prepare(event_name, file, line, repo, &jw); 546 + jw_object_double(&jw, "t_abs", 6, t_abs); 547 + jw_object_double(&jw, "t_rel", 6, t_rel); 548 + jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); 549 + jw_object_string(&jw, "category", category); 550 + jw_object_string(&jw, "key", key); 551 + jw_object_sub_jw(&jw, "value", value); 552 + jw_end(&jw); 553 + 554 + tr2_dst_write_line(&tr2dst_event, &jw.json); 555 + jw_release(&jw); 556 + } 557 + } 558 + 559 + struct tr2_tgt tr2_tgt_event = { 560 + &tr2dst_event, 561 + 562 + fn_init, 563 + fn_term, 564 + 565 + fn_version_fl, 566 + fn_start_fl, 567 + fn_exit_fl, 568 + fn_signal, 569 + fn_atexit, 570 + fn_error_va_fl, 571 + fn_command_path_fl, 572 + fn_command_name_fl, 573 + fn_command_mode_fl, 574 + fn_alias_fl, 575 + fn_child_start_fl, 576 + fn_child_exit_fl, 577 + fn_thread_start_fl, 578 + fn_thread_exit_fl, 579 + fn_exec_fl, 580 + fn_exec_result_fl, 581 + fn_param_fl, 582 + fn_repo_fl, 583 + fn_region_enter_printf_va_fl, 584 + fn_region_leave_printf_va_fl, 585 + fn_data_fl, 586 + fn_data_json_fl, 587 + NULL, /* printf */ 588 + };
+323
trace2/tr2_tgt_normal.c
··· 1 + #include "cache.h" 2 + #include "config.h" 3 + #include "run-command.h" 4 + #include "quote.h" 5 + #include "version.h" 6 + #include "trace2/tr2_dst.h" 7 + #include "trace2/tr2_tbuf.h" 8 + #include "trace2/tr2_tgt.h" 9 + #include "trace2/tr2_tls.h" 10 + 11 + static struct tr2_dst tr2dst_normal = { "GIT_TR2", 0, 0, 0 }; 12 + 13 + /* 14 + * Set this environment variable to true to omit the "<time> <file>:<line>" 15 + * fields from each line written to the builtin normal target. 16 + * 17 + * Unit tests may want to use this to help with testing. 18 + */ 19 + #define TR2_ENVVAR_NORMAL_BRIEF "GIT_TR2_BRIEF" 20 + static int tr2env_normal_brief; 21 + 22 + #define TR2FMT_NORMAL_FL_WIDTH (50) 23 + 24 + static int fn_init(void) 25 + { 26 + int want = tr2_dst_trace_want(&tr2dst_normal); 27 + int want_brief; 28 + char *brief; 29 + 30 + if (!want) 31 + return want; 32 + 33 + brief = getenv(TR2_ENVVAR_NORMAL_BRIEF); 34 + if (brief && *brief && 35 + ((want_brief = git_parse_maybe_bool(brief)) != -1)) 36 + tr2env_normal_brief = want_brief; 37 + 38 + return want; 39 + } 40 + 41 + static void fn_term(void) 42 + { 43 + tr2_dst_trace_disable(&tr2dst_normal); 44 + } 45 + 46 + static void normal_fmt_prepare(const char *file, int line, struct strbuf *buf) 47 + { 48 + strbuf_setlen(buf, 0); 49 + 50 + if (!tr2env_normal_brief) { 51 + struct tr2_tbuf tb_now; 52 + 53 + tr2_tbuf_local_time(&tb_now); 54 + strbuf_addstr(buf, tb_now.buf); 55 + strbuf_addch(buf, ' '); 56 + 57 + if (file && *file) 58 + strbuf_addf(buf, "%s:%d ", file, line); 59 + while (buf->len < TR2FMT_NORMAL_FL_WIDTH) 60 + strbuf_addch(buf, ' '); 61 + } 62 + } 63 + 64 + static void normal_io_write_fl(const char *file, int line, 65 + const struct strbuf *buf_payload) 66 + { 67 + struct strbuf buf_line = STRBUF_INIT; 68 + 69 + normal_fmt_prepare(file, line, &buf_line); 70 + strbuf_addbuf(&buf_line, buf_payload); 71 + tr2_dst_write_line(&tr2dst_normal, &buf_line); 72 + strbuf_release(&buf_line); 73 + } 74 + 75 + static void fn_version_fl(const char *file, int line) 76 + { 77 + struct strbuf buf_payload = STRBUF_INIT; 78 + 79 + strbuf_addf(&buf_payload, "version %s", git_version_string); 80 + normal_io_write_fl(file, line, &buf_payload); 81 + strbuf_release(&buf_payload); 82 + } 83 + 84 + static void fn_start_fl(const char *file, int line, const char **argv) 85 + { 86 + struct strbuf buf_payload = STRBUF_INIT; 87 + 88 + strbuf_addstr(&buf_payload, "start "); 89 + sq_quote_argv_pretty(&buf_payload, argv); 90 + normal_io_write_fl(file, line, &buf_payload); 91 + strbuf_release(&buf_payload); 92 + } 93 + 94 + static void fn_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute, 95 + int code) 96 + { 97 + struct strbuf buf_payload = STRBUF_INIT; 98 + double elapsed = (double)us_elapsed_absolute / 1000000.0; 99 + 100 + strbuf_addf(&buf_payload, "exit elapsed:%.6f code:%d", elapsed, code); 101 + normal_io_write_fl(file, line, &buf_payload); 102 + strbuf_release(&buf_payload); 103 + } 104 + 105 + static void fn_signal(uint64_t us_elapsed_absolute, int signo) 106 + { 107 + struct strbuf buf_payload = STRBUF_INIT; 108 + double elapsed = (double)us_elapsed_absolute / 1000000.0; 109 + 110 + strbuf_addf(&buf_payload, "signal elapsed:%.6f code:%d", elapsed, 111 + signo); 112 + normal_io_write_fl(__FILE__, __LINE__, &buf_payload); 113 + strbuf_release(&buf_payload); 114 + } 115 + 116 + static void fn_atexit(uint64_t us_elapsed_absolute, int code) 117 + { 118 + struct strbuf buf_payload = STRBUF_INIT; 119 + double elapsed = (double)us_elapsed_absolute / 1000000.0; 120 + 121 + strbuf_addf(&buf_payload, "atexit elapsed:%.6f code:%d", elapsed, code); 122 + normal_io_write_fl(__FILE__, __LINE__, &buf_payload); 123 + strbuf_release(&buf_payload); 124 + } 125 + 126 + static void maybe_append_string_va(struct strbuf *buf, const char *fmt, 127 + va_list ap) 128 + { 129 + if (fmt && *fmt && ap) { 130 + va_list copy_ap; 131 + 132 + va_copy(copy_ap, ap); 133 + strbuf_vaddf(buf, fmt, copy_ap); 134 + va_end(copy_ap); 135 + return; 136 + } 137 + 138 + if (fmt && *fmt) { 139 + strbuf_addstr(buf, fmt); 140 + return; 141 + } 142 + } 143 + 144 + static void fn_error_va_fl(const char *file, int line, const char *fmt, 145 + va_list ap) 146 + { 147 + struct strbuf buf_payload = STRBUF_INIT; 148 + 149 + strbuf_addstr(&buf_payload, "error "); 150 + maybe_append_string_va(&buf_payload, fmt, ap); 151 + normal_io_write_fl(file, line, &buf_payload); 152 + strbuf_release(&buf_payload); 153 + } 154 + 155 + static void fn_command_path_fl(const char *file, int line, const char *pathname) 156 + { 157 + struct strbuf buf_payload = STRBUF_INIT; 158 + 159 + strbuf_addf(&buf_payload, "cmd_path %s", pathname); 160 + normal_io_write_fl(file, line, &buf_payload); 161 + strbuf_release(&buf_payload); 162 + } 163 + 164 + static void fn_command_name_fl(const char *file, int line, const char *name, 165 + const char *hierarchy) 166 + { 167 + struct strbuf buf_payload = STRBUF_INIT; 168 + 169 + strbuf_addf(&buf_payload, "cmd_name %s", name); 170 + if (hierarchy && *hierarchy) 171 + strbuf_addf(&buf_payload, " (%s)", hierarchy); 172 + normal_io_write_fl(file, line, &buf_payload); 173 + strbuf_release(&buf_payload); 174 + } 175 + 176 + static void fn_command_mode_fl(const char *file, int line, const char *mode) 177 + { 178 + struct strbuf buf_payload = STRBUF_INIT; 179 + 180 + strbuf_addf(&buf_payload, "cmd_mode %s", mode); 181 + normal_io_write_fl(file, line, &buf_payload); 182 + strbuf_release(&buf_payload); 183 + } 184 + 185 + static void fn_alias_fl(const char *file, int line, const char *alias, 186 + const char **argv) 187 + { 188 + struct strbuf buf_payload = STRBUF_INIT; 189 + 190 + strbuf_addf(&buf_payload, "alias %s ->", alias); 191 + sq_quote_argv_pretty(&buf_payload, argv); 192 + normal_io_write_fl(file, line, &buf_payload); 193 + strbuf_release(&buf_payload); 194 + } 195 + 196 + static void fn_child_start_fl(const char *file, int line, 197 + uint64_t us_elapsed_absolute, 198 + const struct child_process *cmd) 199 + { 200 + struct strbuf buf_payload = STRBUF_INIT; 201 + 202 + strbuf_addf(&buf_payload, "child_start[%d] ", cmd->trace2_child_id); 203 + 204 + if (cmd->dir) { 205 + strbuf_addstr(&buf_payload, " cd"); 206 + sq_quote_buf_pretty(&buf_payload, cmd->dir); 207 + strbuf_addstr(&buf_payload, "; "); 208 + } 209 + 210 + /* 211 + * TODO if (cmd->env) { Consider dumping changes to environment. } 212 + * See trace_add_env() in run-command.c as used by original trace.c 213 + */ 214 + 215 + if (cmd->git_cmd) 216 + strbuf_addstr(&buf_payload, "git"); 217 + sq_quote_argv_pretty(&buf_payload, cmd->argv); 218 + 219 + normal_io_write_fl(file, line, &buf_payload); 220 + strbuf_release(&buf_payload); 221 + } 222 + 223 + static void fn_child_exit_fl(const char *file, int line, 224 + uint64_t us_elapsed_absolute, int cid, int pid, 225 + int code, uint64_t us_elapsed_child) 226 + { 227 + struct strbuf buf_payload = STRBUF_INIT; 228 + double elapsed = (double)us_elapsed_child / 1000000.0; 229 + 230 + strbuf_addf(&buf_payload, "child_exit[%d] pid:%d code:%d elapsed:%.6f", 231 + cid, pid, code, elapsed); 232 + normal_io_write_fl(file, line, &buf_payload); 233 + strbuf_release(&buf_payload); 234 + } 235 + 236 + static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, 237 + int exec_id, const char *exe, const char **argv) 238 + { 239 + struct strbuf buf_payload = STRBUF_INIT; 240 + 241 + strbuf_addf(&buf_payload, "exec[%d] ", exec_id); 242 + if (exe) 243 + strbuf_addstr(&buf_payload, exe); 244 + sq_quote_argv_pretty(&buf_payload, argv); 245 + normal_io_write_fl(file, line, &buf_payload); 246 + strbuf_release(&buf_payload); 247 + } 248 + 249 + static void fn_exec_result_fl(const char *file, int line, 250 + uint64_t us_elapsed_absolute, int exec_id, 251 + int code) 252 + { 253 + struct strbuf buf_payload = STRBUF_INIT; 254 + 255 + strbuf_addf(&buf_payload, "exec_result[%d] code:%d", exec_id, code); 256 + if (code > 0) 257 + strbuf_addf(&buf_payload, " err:%s", strerror(code)); 258 + normal_io_write_fl(file, line, &buf_payload); 259 + strbuf_release(&buf_payload); 260 + } 261 + 262 + static void fn_param_fl(const char *file, int line, const char *param, 263 + const char *value) 264 + { 265 + struct strbuf buf_payload = STRBUF_INIT; 266 + 267 + strbuf_addf(&buf_payload, "def_param %s=%s", param, value); 268 + normal_io_write_fl(file, line, &buf_payload); 269 + strbuf_release(&buf_payload); 270 + } 271 + 272 + static void fn_repo_fl(const char *file, int line, 273 + const struct repository *repo) 274 + { 275 + struct strbuf buf_payload = STRBUF_INIT; 276 + 277 + strbuf_addstr(&buf_payload, "worktree "); 278 + sq_quote_buf_pretty(&buf_payload, repo->worktree); 279 + normal_io_write_fl(file, line, &buf_payload); 280 + strbuf_release(&buf_payload); 281 + } 282 + 283 + static void fn_printf_va_fl(const char *file, int line, 284 + uint64_t us_elapsed_absolute, const char *fmt, 285 + va_list ap) 286 + { 287 + struct strbuf buf_payload = STRBUF_INIT; 288 + 289 + maybe_append_string_va(&buf_payload, fmt, ap); 290 + normal_io_write_fl(file, line, &buf_payload); 291 + strbuf_release(&buf_payload); 292 + } 293 + 294 + struct tr2_tgt tr2_tgt_normal = { 295 + &tr2dst_normal, 296 + 297 + fn_init, 298 + fn_term, 299 + 300 + fn_version_fl, 301 + fn_start_fl, 302 + fn_exit_fl, 303 + fn_signal, 304 + fn_atexit, 305 + fn_error_va_fl, 306 + fn_command_path_fl, 307 + fn_command_name_fl, 308 + fn_command_mode_fl, 309 + fn_alias_fl, 310 + fn_child_start_fl, 311 + fn_child_exit_fl, 312 + NULL, /* thread_start */ 313 + NULL, /* thread_exit */ 314 + fn_exec_fl, 315 + fn_exec_result_fl, 316 + fn_param_fl, 317 + fn_repo_fl, 318 + NULL, /* region_enter */ 319 + NULL, /* region_leave */ 320 + NULL, /* data */ 321 + NULL, /* data_json */ 322 + fn_printf_va_fl, 323 + };
+534
trace2/tr2_tgt_perf.c
··· 1 + #include "cache.h" 2 + #include "config.h" 3 + #include "run-command.h" 4 + #include "quote.h" 5 + #include "version.h" 6 + #include "json-writer.h" 7 + #include "trace2/tr2_dst.h" 8 + #include "trace2/tr2_sid.h" 9 + #include "trace2/tr2_tbuf.h" 10 + #include "trace2/tr2_tgt.h" 11 + #include "trace2/tr2_tls.h" 12 + 13 + static struct tr2_dst tr2dst_perf = { "GIT_TR2_PERF", 0, 0, 0 }; 14 + 15 + /* 16 + * Set this environment variable to true to omit the "<time> <file>:<line>" 17 + * fields from each line written to the builtin performance target. 18 + * 19 + * Unit tests may want to use this to help with testing. 20 + */ 21 + #define TR2_ENVVAR_PERF_BRIEF "GIT_TR2_PERF_BRIEF" 22 + static int tr2env_perf_brief; 23 + 24 + #define TR2FMT_PERF_FL_WIDTH (50) 25 + #define TR2FMT_PERF_MAX_EVENT_NAME (12) 26 + #define TR2FMT_PERF_REPO_WIDTH (4) 27 + #define TR2FMT_PERF_CATEGORY_WIDTH (10) 28 + 29 + #define TR2_DOTS_BUFFER_SIZE (100) 30 + #define TR2_INDENT (2) 31 + #define TR2_INDENT_LENGTH(ctx) (((ctx)->nr_open_regions - 1) * TR2_INDENT) 32 + 33 + static struct strbuf dots = STRBUF_INIT; 34 + 35 + static int fn_init(void) 36 + { 37 + int want = tr2_dst_trace_want(&tr2dst_perf); 38 + int want_brief; 39 + char *brief; 40 + 41 + if (!want) 42 + return want; 43 + 44 + strbuf_addchars(&dots, '.', TR2_DOTS_BUFFER_SIZE); 45 + 46 + brief = getenv(TR2_ENVVAR_PERF_BRIEF); 47 + if (brief && *brief && 48 + ((want_brief = git_parse_maybe_bool(brief)) != -1)) 49 + tr2env_perf_brief = want_brief; 50 + 51 + return want; 52 + } 53 + 54 + static void fn_term(void) 55 + { 56 + tr2_dst_trace_disable(&tr2dst_perf); 57 + 58 + strbuf_release(&dots); 59 + } 60 + 61 + /* 62 + * Format trace line prefix in human-readable classic format for 63 + * the performance target: 64 + * "[<time> [<file>:<line>] <bar>] <nr_parents> <bar> 65 + * <thread_name> <bar> <event_name> <bar> [<repo>] <bar> 66 + * [<elapsed_absolute>] [<elapsed_relative>] <bar> 67 + * [<category>] <bar> [<dots>] " 68 + */ 69 + static void perf_fmt_prepare(const char *event_name, 70 + struct tr2tls_thread_ctx *ctx, const char *file, 71 + int line, const struct repository *repo, 72 + uint64_t *p_us_elapsed_absolute, 73 + uint64_t *p_us_elapsed_relative, 74 + const char *category, struct strbuf *buf) 75 + { 76 + int len; 77 + 78 + strbuf_setlen(buf, 0); 79 + 80 + if (!tr2env_perf_brief) { 81 + struct tr2_tbuf tb_now; 82 + 83 + tr2_tbuf_local_time(&tb_now); 84 + strbuf_addstr(buf, tb_now.buf); 85 + strbuf_addch(buf, ' '); 86 + 87 + if (file && *file) 88 + strbuf_addf(buf, "%s:%d ", file, line); 89 + while (buf->len < TR2FMT_PERF_FL_WIDTH) 90 + strbuf_addch(buf, ' '); 91 + 92 + strbuf_addstr(buf, "| "); 93 + } 94 + 95 + strbuf_addf(buf, "d%d | ", tr2_sid_depth()); 96 + strbuf_addf(buf, "%-*s | %-*s | ", TR2_MAX_THREAD_NAME, 97 + ctx->thread_name.buf, TR2FMT_PERF_MAX_EVENT_NAME, 98 + event_name); 99 + 100 + len = buf->len + TR2FMT_PERF_REPO_WIDTH; 101 + if (repo) 102 + strbuf_addf(buf, "r%d ", repo->trace2_repo_id); 103 + while (buf->len < len) 104 + strbuf_addch(buf, ' '); 105 + strbuf_addstr(buf, "| "); 106 + 107 + if (p_us_elapsed_absolute) 108 + strbuf_addf(buf, "%9.6f | ", 109 + ((double)(*p_us_elapsed_absolute)) / 1000000.0); 110 + else 111 + strbuf_addf(buf, "%9s | ", " "); 112 + 113 + if (p_us_elapsed_relative) 114 + strbuf_addf(buf, "%9.6f | ", 115 + ((double)(*p_us_elapsed_relative)) / 1000000.0); 116 + else 117 + strbuf_addf(buf, "%9s | ", " "); 118 + 119 + strbuf_addf(buf, "%-*s | ", TR2FMT_PERF_CATEGORY_WIDTH, 120 + (category ? category : "")); 121 + 122 + if (ctx->nr_open_regions > 0) { 123 + int len_indent = TR2_INDENT_LENGTH(ctx); 124 + while (len_indent > dots.len) { 125 + strbuf_addbuf(buf, &dots); 126 + len_indent -= dots.len; 127 + } 128 + strbuf_addf(buf, "%.*s", len_indent, dots.buf); 129 + } 130 + } 131 + 132 + static void perf_io_write_fl(const char *file, int line, const char *event_name, 133 + const struct repository *repo, 134 + uint64_t *p_us_elapsed_absolute, 135 + uint64_t *p_us_elapsed_relative, 136 + const char *category, 137 + const struct strbuf *buf_payload) 138 + { 139 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 140 + struct strbuf buf_line = STRBUF_INIT; 141 + 142 + perf_fmt_prepare(event_name, ctx, file, line, repo, 143 + p_us_elapsed_absolute, p_us_elapsed_relative, category, 144 + &buf_line); 145 + strbuf_addbuf(&buf_line, buf_payload); 146 + tr2_dst_write_line(&tr2dst_perf, &buf_line); 147 + strbuf_release(&buf_line); 148 + } 149 + 150 + static void fn_version_fl(const char *file, int line) 151 + { 152 + const char *event_name = "version"; 153 + struct strbuf buf_payload = STRBUF_INIT; 154 + 155 + strbuf_addstr(&buf_payload, git_version_string); 156 + 157 + perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, 158 + &buf_payload); 159 + strbuf_release(&buf_payload); 160 + } 161 + 162 + static void fn_start_fl(const char *file, int line, const char **argv) 163 + { 164 + const char *event_name = "start"; 165 + struct strbuf buf_payload = STRBUF_INIT; 166 + 167 + sq_quote_argv_pretty(&buf_payload, argv); 168 + 169 + perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, 170 + &buf_payload); 171 + strbuf_release(&buf_payload); 172 + } 173 + 174 + static void fn_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute, 175 + int code) 176 + { 177 + const char *event_name = "exit"; 178 + struct strbuf buf_payload = STRBUF_INIT; 179 + 180 + strbuf_addf(&buf_payload, "code:%d", code); 181 + 182 + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, 183 + NULL, NULL, &buf_payload); 184 + strbuf_release(&buf_payload); 185 + } 186 + 187 + static void fn_signal(uint64_t us_elapsed_absolute, int signo) 188 + { 189 + const char *event_name = "signal"; 190 + struct strbuf buf_payload = STRBUF_INIT; 191 + 192 + strbuf_addf(&buf_payload, "signo:%d", signo); 193 + 194 + perf_io_write_fl(__FILE__, __LINE__, event_name, NULL, 195 + &us_elapsed_absolute, NULL, NULL, &buf_payload); 196 + strbuf_release(&buf_payload); 197 + } 198 + 199 + static void fn_atexit(uint64_t us_elapsed_absolute, int code) 200 + { 201 + const char *event_name = "atexit"; 202 + struct strbuf buf_payload = STRBUF_INIT; 203 + 204 + strbuf_addf(&buf_payload, "code:%d", code); 205 + 206 + perf_io_write_fl(__FILE__, __LINE__, event_name, NULL, 207 + &us_elapsed_absolute, NULL, NULL, &buf_payload); 208 + strbuf_release(&buf_payload); 209 + } 210 + 211 + static void maybe_append_string_va(struct strbuf *buf, const char *fmt, 212 + va_list ap) 213 + { 214 + if (fmt && *fmt && ap) { 215 + va_list copy_ap; 216 + 217 + va_copy(copy_ap, ap); 218 + strbuf_vaddf(buf, fmt, copy_ap); 219 + va_end(copy_ap); 220 + return; 221 + } 222 + 223 + if (fmt && *fmt) { 224 + strbuf_addstr(buf, fmt); 225 + return; 226 + } 227 + } 228 + 229 + static void fn_error_va_fl(const char *file, int line, const char *fmt, 230 + va_list ap) 231 + { 232 + const char *event_name = "error"; 233 + struct strbuf buf_payload = STRBUF_INIT; 234 + 235 + maybe_append_string_va(&buf_payload, fmt, ap); 236 + 237 + perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, 238 + &buf_payload); 239 + strbuf_release(&buf_payload); 240 + } 241 + 242 + static void fn_command_path_fl(const char *file, int line, const char *pathname) 243 + { 244 + const char *event_name = "cmd_path"; 245 + struct strbuf buf_payload = STRBUF_INIT; 246 + 247 + strbuf_addstr(&buf_payload, pathname); 248 + 249 + perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, 250 + &buf_payload); 251 + strbuf_release(&buf_payload); 252 + } 253 + 254 + static void fn_command_name_fl(const char *file, int line, const char *name, 255 + const char *hierarchy) 256 + { 257 + const char *event_name = "cmd_name"; 258 + struct strbuf buf_payload = STRBUF_INIT; 259 + 260 + strbuf_addstr(&buf_payload, name); 261 + if (hierarchy && *hierarchy) 262 + strbuf_addf(&buf_payload, " (%s)", hierarchy); 263 + 264 + perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, 265 + &buf_payload); 266 + strbuf_release(&buf_payload); 267 + } 268 + 269 + static void fn_command_mode_fl(const char *file, int line, const char *mode) 270 + { 271 + const char *event_name = "cmd_mode"; 272 + struct strbuf buf_payload = STRBUF_INIT; 273 + 274 + strbuf_addstr(&buf_payload, mode); 275 + 276 + perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, 277 + &buf_payload); 278 + strbuf_release(&buf_payload); 279 + } 280 + 281 + static void fn_alias_fl(const char *file, int line, const char *alias, 282 + const char **argv) 283 + { 284 + const char *event_name = "alias"; 285 + struct strbuf buf_payload = STRBUF_INIT; 286 + 287 + strbuf_addf(&buf_payload, "alias:%s argv:", alias); 288 + sq_quote_argv_pretty(&buf_payload, argv); 289 + 290 + perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, 291 + &buf_payload); 292 + strbuf_release(&buf_payload); 293 + } 294 + 295 + static void fn_child_start_fl(const char *file, int line, 296 + uint64_t us_elapsed_absolute, 297 + const struct child_process *cmd) 298 + { 299 + const char *event_name = "child_start"; 300 + struct strbuf buf_payload = STRBUF_INIT; 301 + 302 + if (cmd->trace2_hook_name) { 303 + strbuf_addf(&buf_payload, "[ch%d] class:hook hook:%s", 304 + cmd->trace2_child_id, cmd->trace2_hook_name); 305 + } else { 306 + const char *child_class = 307 + cmd->trace2_child_class ? cmd->trace2_child_class : "?"; 308 + strbuf_addf(&buf_payload, "[ch%d] class:%s", 309 + cmd->trace2_child_id, child_class); 310 + } 311 + 312 + if (cmd->dir) { 313 + strbuf_addstr(&buf_payload, " cd:"); 314 + sq_quote_buf_pretty(&buf_payload, cmd->dir); 315 + } 316 + 317 + strbuf_addstr(&buf_payload, " argv:"); 318 + if (cmd->git_cmd) 319 + strbuf_addstr(&buf_payload, " git"); 320 + sq_quote_argv_pretty(&buf_payload, cmd->argv); 321 + 322 + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, 323 + NULL, NULL, &buf_payload); 324 + strbuf_release(&buf_payload); 325 + } 326 + 327 + static void fn_child_exit_fl(const char *file, int line, 328 + uint64_t us_elapsed_absolute, int cid, int pid, 329 + int code, uint64_t us_elapsed_child) 330 + { 331 + const char *event_name = "child_exit"; 332 + struct strbuf buf_payload = STRBUF_INIT; 333 + 334 + strbuf_addf(&buf_payload, "[ch%d] pid:%d code:%d", cid, pid, code); 335 + 336 + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, 337 + &us_elapsed_child, NULL, &buf_payload); 338 + strbuf_release(&buf_payload); 339 + } 340 + 341 + static void fn_thread_start_fl(const char *file, int line, 342 + uint64_t us_elapsed_absolute) 343 + { 344 + const char *event_name = "thread_start"; 345 + struct strbuf buf_payload = STRBUF_INIT; 346 + 347 + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, 348 + NULL, NULL, &buf_payload); 349 + strbuf_release(&buf_payload); 350 + } 351 + 352 + static void fn_thread_exit_fl(const char *file, int line, 353 + uint64_t us_elapsed_absolute, 354 + uint64_t us_elapsed_thread) 355 + { 356 + const char *event_name = "thread_exit"; 357 + struct strbuf buf_payload = STRBUF_INIT; 358 + 359 + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, 360 + &us_elapsed_thread, NULL, &buf_payload); 361 + strbuf_release(&buf_payload); 362 + } 363 + 364 + static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, 365 + int exec_id, const char *exe, const char **argv) 366 + { 367 + const char *event_name = "exec"; 368 + struct strbuf buf_payload = STRBUF_INIT; 369 + 370 + strbuf_addf(&buf_payload, "id:%d ", exec_id); 371 + strbuf_addstr(&buf_payload, "argv:"); 372 + if (exe) 373 + strbuf_addf(&buf_payload, " %s", exe); 374 + sq_quote_argv_pretty(&buf_payload, argv); 375 + 376 + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, 377 + NULL, NULL, &buf_payload); 378 + strbuf_release(&buf_payload); 379 + } 380 + 381 + static void fn_exec_result_fl(const char *file, int line, 382 + uint64_t us_elapsed_absolute, int exec_id, 383 + int code) 384 + { 385 + const char *event_name = "exec_result"; 386 + struct strbuf buf_payload = STRBUF_INIT; 387 + 388 + strbuf_addf(&buf_payload, "id:%d code:%d", exec_id, code); 389 + if (code > 0) 390 + strbuf_addf(&buf_payload, " err:%s", strerror(code)); 391 + 392 + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, 393 + NULL, NULL, &buf_payload); 394 + strbuf_release(&buf_payload); 395 + } 396 + 397 + static void fn_param_fl(const char *file, int line, const char *param, 398 + const char *value) 399 + { 400 + const char *event_name = "def_param"; 401 + struct strbuf buf_payload = STRBUF_INIT; 402 + 403 + strbuf_addf(&buf_payload, "%s:%s", param, value); 404 + 405 + perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, 406 + &buf_payload); 407 + strbuf_release(&buf_payload); 408 + } 409 + 410 + static void fn_repo_fl(const char *file, int line, 411 + const struct repository *repo) 412 + { 413 + const char *event_name = "def_repo"; 414 + struct strbuf buf_payload = STRBUF_INIT; 415 + 416 + strbuf_addstr(&buf_payload, "worktree:"); 417 + sq_quote_buf_pretty(&buf_payload, repo->worktree); 418 + 419 + perf_io_write_fl(file, line, event_name, repo, NULL, NULL, NULL, 420 + &buf_payload); 421 + strbuf_release(&buf_payload); 422 + } 423 + 424 + static void fn_region_enter_printf_va_fl(const char *file, int line, 425 + uint64_t us_elapsed_absolute, 426 + const char *category, 427 + const char *label, 428 + const struct repository *repo, 429 + const char *fmt, va_list ap) 430 + { 431 + const char *event_name = "region_enter"; 432 + struct strbuf buf_payload = STRBUF_INIT; 433 + 434 + if (label) 435 + strbuf_addf(&buf_payload, "label:%s ", label); 436 + maybe_append_string_va(&buf_payload, fmt, ap); 437 + 438 + perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute, 439 + NULL, category, &buf_payload); 440 + strbuf_release(&buf_payload); 441 + } 442 + 443 + static void fn_region_leave_printf_va_fl( 444 + const char *file, int line, uint64_t us_elapsed_absolute, 445 + uint64_t us_elapsed_region, const char *category, const char *label, 446 + const struct repository *repo, const char *fmt, va_list ap) 447 + { 448 + const char *event_name = "region_leave"; 449 + struct strbuf buf_payload = STRBUF_INIT; 450 + 451 + if (label) 452 + strbuf_addf(&buf_payload, "label:%s ", label); 453 + maybe_append_string_va(&buf_payload, fmt, ap); 454 + 455 + perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute, 456 + &us_elapsed_region, category, &buf_payload); 457 + strbuf_release(&buf_payload); 458 + } 459 + 460 + static void fn_data_fl(const char *file, int line, uint64_t us_elapsed_absolute, 461 + uint64_t us_elapsed_region, const char *category, 462 + const struct repository *repo, const char *key, 463 + const char *value) 464 + { 465 + const char *event_name = "data"; 466 + struct strbuf buf_payload = STRBUF_INIT; 467 + 468 + strbuf_addf(&buf_payload, "%s:%s", key, value); 469 + 470 + perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute, 471 + &us_elapsed_region, category, &buf_payload); 472 + strbuf_release(&buf_payload); 473 + } 474 + 475 + static void fn_data_json_fl(const char *file, int line, 476 + uint64_t us_elapsed_absolute, 477 + uint64_t us_elapsed_region, const char *category, 478 + const struct repository *repo, const char *key, 479 + const struct json_writer *value) 480 + { 481 + const char *event_name = "data_json"; 482 + struct strbuf buf_payload = STRBUF_INIT; 483 + 484 + strbuf_addf(&buf_payload, "%s:%s", key, value->json.buf); 485 + 486 + perf_io_write_fl(file, line, event_name, repo, &us_elapsed_absolute, 487 + &us_elapsed_region, category, &buf_payload); 488 + strbuf_release(&buf_payload); 489 + } 490 + 491 + static void fn_printf_va_fl(const char *file, int line, 492 + uint64_t us_elapsed_absolute, const char *fmt, 493 + va_list ap) 494 + { 495 + const char *event_name = "printf"; 496 + struct strbuf buf_payload = STRBUF_INIT; 497 + 498 + maybe_append_string_va(&buf_payload, fmt, ap); 499 + 500 + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, 501 + NULL, NULL, &buf_payload); 502 + strbuf_release(&buf_payload); 503 + } 504 + 505 + struct tr2_tgt tr2_tgt_perf = { 506 + &tr2dst_perf, 507 + 508 + fn_init, 509 + fn_term, 510 + 511 + fn_version_fl, 512 + fn_start_fl, 513 + fn_exit_fl, 514 + fn_signal, 515 + fn_atexit, 516 + fn_error_va_fl, 517 + fn_command_path_fl, 518 + fn_command_name_fl, 519 + fn_command_mode_fl, 520 + fn_alias_fl, 521 + fn_child_start_fl, 522 + fn_child_exit_fl, 523 + fn_thread_start_fl, 524 + fn_thread_exit_fl, 525 + fn_exec_fl, 526 + fn_exec_result_fl, 527 + fn_param_fl, 528 + fn_repo_fl, 529 + fn_region_enter_printf_va_fl, 530 + fn_region_leave_printf_va_fl, 531 + fn_data_fl, 532 + fn_data_json_fl, 533 + fn_printf_va_fl, 534 + };
+164
trace2/tr2_tls.c
··· 1 + #include "cache.h" 2 + #include "thread-utils.h" 3 + #include "trace2/tr2_tls.h" 4 + 5 + /* 6 + * Initialize size of the thread stack for nested regions. 7 + * This is used to store nested region start times. Note that 8 + * this stack is per-thread and not per-trace-key. 9 + */ 10 + #define TR2_REGION_NESTING_INITIAL_SIZE (100) 11 + 12 + static struct tr2tls_thread_ctx *tr2tls_thread_main; 13 + static uint64_t tr2tls_us_start_main; 14 + 15 + static pthread_mutex_t tr2tls_mutex; 16 + static pthread_key_t tr2tls_key; 17 + 18 + static int tr2_next_thread_id; /* modify under lock */ 19 + 20 + struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name) 21 + { 22 + uint64_t us_now = getnanotime() / 1000; 23 + struct tr2tls_thread_ctx *ctx = xcalloc(1, sizeof(*ctx)); 24 + 25 + /* 26 + * Implicitly "tr2tls_push_self()" to capture the thread's start 27 + * time in array_us_start[0]. For the main thread this gives us the 28 + * application run time. 29 + */ 30 + ctx->alloc = TR2_REGION_NESTING_INITIAL_SIZE; 31 + ctx->array_us_start = (uint64_t *)xcalloc(ctx->alloc, sizeof(uint64_t)); 32 + ctx->array_us_start[ctx->nr_open_regions++] = us_now; 33 + 34 + ctx->thread_id = tr2tls_locked_increment(&tr2_next_thread_id); 35 + 36 + strbuf_init(&ctx->thread_name, 0); 37 + if (ctx->thread_id) 38 + strbuf_addf(&ctx->thread_name, "th%02d:", ctx->thread_id); 39 + strbuf_addstr(&ctx->thread_name, thread_name); 40 + if (ctx->thread_name.len > TR2_MAX_THREAD_NAME) 41 + strbuf_setlen(&ctx->thread_name, TR2_MAX_THREAD_NAME); 42 + 43 + pthread_setspecific(tr2tls_key, ctx); 44 + 45 + return ctx; 46 + } 47 + 48 + struct tr2tls_thread_ctx *tr2tls_get_self(void) 49 + { 50 + struct tr2tls_thread_ctx *ctx = pthread_getspecific(tr2tls_key); 51 + 52 + /* 53 + * If the thread-proc did not call trace2_thread_start(), we won't 54 + * have any TLS data associated with the current thread. Fix it 55 + * here and silently continue. 56 + */ 57 + if (!ctx) 58 + ctx = tr2tls_create_self("unknown"); 59 + 60 + return ctx; 61 + } 62 + 63 + int tr2tls_is_main_thread(void) 64 + { 65 + struct tr2tls_thread_ctx *ctx = pthread_getspecific(tr2tls_key); 66 + 67 + return ctx == tr2tls_thread_main; 68 + } 69 + 70 + void tr2tls_unset_self(void) 71 + { 72 + struct tr2tls_thread_ctx *ctx; 73 + 74 + ctx = tr2tls_get_self(); 75 + 76 + pthread_setspecific(tr2tls_key, NULL); 77 + 78 + free(ctx->array_us_start); 79 + free(ctx); 80 + } 81 + 82 + void tr2tls_push_self(uint64_t us_now) 83 + { 84 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 85 + 86 + ALLOC_GROW(ctx->array_us_start, ctx->nr_open_regions + 1, ctx->alloc); 87 + ctx->array_us_start[ctx->nr_open_regions++] = us_now; 88 + } 89 + 90 + void tr2tls_pop_self(void) 91 + { 92 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 93 + 94 + if (!ctx->nr_open_regions) 95 + BUG("no open regions in thread '%s'", ctx->thread_name.buf); 96 + 97 + ctx->nr_open_regions--; 98 + } 99 + 100 + void tr2tls_pop_unwind_self(void) 101 + { 102 + struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); 103 + 104 + while (ctx->nr_open_regions > 1) 105 + tr2tls_pop_self(); 106 + } 107 + 108 + uint64_t tr2tls_region_elasped_self(uint64_t us) 109 + { 110 + struct tr2tls_thread_ctx *ctx; 111 + uint64_t us_start; 112 + 113 + ctx = tr2tls_get_self(); 114 + if (!ctx->nr_open_regions) 115 + return 0; 116 + 117 + us_start = ctx->array_us_start[ctx->nr_open_regions - 1]; 118 + 119 + return us - us_start; 120 + } 121 + 122 + uint64_t tr2tls_absolute_elapsed(uint64_t us) 123 + { 124 + if (!tr2tls_thread_main) 125 + return 0; 126 + 127 + return us - tr2tls_us_start_main; 128 + } 129 + 130 + void tr2tls_init(void) 131 + { 132 + pthread_key_create(&tr2tls_key, NULL); 133 + init_recursive_mutex(&tr2tls_mutex); 134 + 135 + tr2tls_thread_main = tr2tls_create_self("main"); 136 + /* 137 + * Keep a copy of the absolute start time of the main thread 138 + * in a fixed variable since other threads need to access it. 139 + * This also eliminates the need to lock accesses to the main 140 + * thread's array (because of reallocs). 141 + */ 142 + tr2tls_us_start_main = tr2tls_thread_main->array_us_start[0]; 143 + } 144 + 145 + void tr2tls_release(void) 146 + { 147 + tr2tls_unset_self(); 148 + tr2tls_thread_main = NULL; 149 + 150 + pthread_mutex_destroy(&tr2tls_mutex); 151 + pthread_key_delete(tr2tls_key); 152 + } 153 + 154 + int tr2tls_locked_increment(int *p) 155 + { 156 + int current_value; 157 + 158 + pthread_mutex_lock(&tr2tls_mutex); 159 + current_value = *p; 160 + *p = current_value + 1; 161 + pthread_mutex_unlock(&tr2tls_mutex); 162 + 163 + return current_value; 164 + }
+97
trace2/tr2_tls.h
··· 1 + #ifndef TR2_TLS_H 2 + #define TR2_TLS_H 3 + 4 + #include "strbuf.h" 5 + 6 + /* 7 + * Arbitry limit for thread names for column alignment. 8 + */ 9 + #define TR2_MAX_THREAD_NAME (24) 10 + 11 + struct tr2tls_thread_ctx { 12 + struct strbuf thread_name; 13 + uint64_t *array_us_start; 14 + int alloc; 15 + int nr_open_regions; /* plays role of "nr" in ALLOC_GROW */ 16 + int thread_id; 17 + }; 18 + 19 + /* 20 + * Create TLS data for the current thread. This gives us a place to 21 + * put per-thread data, such as thread start time, function nesting 22 + * and a per-thread label for our messages. 23 + * 24 + * We assume the first thread is "main". Other threads are given 25 + * non-zero thread-ids to help distinguish messages from concurrent 26 + * threads. 27 + * 28 + * Truncate the thread name if necessary to help with column alignment 29 + * in printf-style messages. 30 + * 31 + * In this and all following functions the term "self" refers to the 32 + * current thread. 33 + */ 34 + struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name); 35 + 36 + /* 37 + * Get our TLS data. 38 + */ 39 + struct tr2tls_thread_ctx *tr2tls_get_self(void); 40 + 41 + /* 42 + * return true if the current thread is the main thread. 43 + */ 44 + int tr2tls_is_main_thread(void); 45 + 46 + /* 47 + * Free our TLS data. 48 + */ 49 + void tr2tls_unset_self(void); 50 + 51 + /* 52 + * Begin a new nested region and remember the start time. 53 + */ 54 + void tr2tls_push_self(uint64_t us_now); 55 + 56 + /* 57 + * End the innermost nested region. 58 + */ 59 + void tr2tls_pop_self(void); 60 + 61 + /* 62 + * Pop any extra (above the first) open regions on the current 63 + * thread and discard. During a thread-exit, we should only 64 + * have region[0] that was pushed in trace2_thread_start() if 65 + * the thread exits normally. 66 + */ 67 + void tr2tls_pop_unwind_self(void); 68 + 69 + /* 70 + * Compute the elapsed time since the innermost region in the 71 + * current thread started and the given time (usually now). 72 + */ 73 + uint64_t tr2tls_region_elasped_self(uint64_t us); 74 + 75 + /* 76 + * Compute the elapsed time since the main thread started 77 + * and the given time (usually now). This is assumed to 78 + * be the absolute run time of the process. 79 + */ 80 + uint64_t tr2tls_absolute_elapsed(uint64_t us); 81 + 82 + /* 83 + * Initialize the tr2 TLS system. 84 + */ 85 + void tr2tls_init(void); 86 + 87 + /* 88 + * Free all tr2 TLS resources. 89 + */ 90 + void tr2tls_release(void); 91 + 92 + /* 93 + * Protected increment of an integer. 94 + */ 95 + int tr2tls_locked_increment(int *p); 96 + 97 + #endif /* TR2_TLS_H */
+31
usage.c
··· 22 22 static NORETURN void usage_builtin(const char *err, va_list params) 23 23 { 24 24 vreportf("usage: ", err, params); 25 + 26 + /* 27 + * When we detect a usage error *before* the command dispatch in 28 + * cmd_main(), we don't know what verb to report. Force it to this 29 + * to facilitate post-processing. 30 + */ 31 + trace2_cmd_name("_usage_"); 32 + 33 + /* 34 + * Currently, the (err, params) are usually just the static usage 35 + * string which isn't very useful here. Usually, the call site 36 + * manually calls fprintf(stderr,...) with the actual detailed 37 + * syntax error before calling usage(). 38 + * 39 + * TODO It would be nice to update the call sites to pass both 40 + * the static usage string and the detailed error message. 41 + */ 42 + 25 43 exit(129); 26 44 } 27 45 28 46 static NORETURN void die_builtin(const char *err, va_list params) 29 47 { 48 + /* 49 + * We call this trace2 function first and expect it to va_copy 'params' 50 + * before using it (because an 'ap' can only be walked once). 51 + */ 52 + trace2_cmd_error_va(err, params); 53 + 30 54 vreportf("fatal: ", err, params); 55 + 31 56 exit(128); 32 57 } 33 58 34 59 static void error_builtin(const char *err, va_list params) 35 60 { 61 + /* 62 + * We call this trace2 function first and expect it to va_copy 'params' 63 + * before using it (because an 'ap' can only be walked once). 64 + */ 65 + trace2_cmd_error_va(err, params); 66 + 36 67 vreportf("error: ", err, params); 37 68 } 38 69