Merge branch 'es/trace2-log-parent-process-name'
trace2 logs learned to show parent process name to see in what context Git was invoked. * es/trace2-log-parent-process-name: tr2: log parent process name tr2: make process info collection platform-genericmaint
						commit
						6f64eeab60
					
				|  | @ -493,6 +493,20 @@ about specific error arguments. | ||||||
| } | } | ||||||
| ------------ | ------------ | ||||||
|  |  | ||||||
|  | `"cmd_ancestry"`:: | ||||||
|  | 	This event contains the text command name for the parent (and earlier | ||||||
|  | 	generations of parents) of the current process, in an array ordered from | ||||||
|  | 	nearest parent to furthest great-grandparent. It may not be implemented | ||||||
|  | 	on all platforms. | ||||||
|  | + | ||||||
|  | ------------ | ||||||
|  | { | ||||||
|  | 	"event":"cmd_ancestry", | ||||||
|  | 	... | ||||||
|  | 	"ancestry":["bash","tmux: server","systemd"] | ||||||
|  | } | ||||||
|  | ------------ | ||||||
|  |  | ||||||
| `"cmd_name"`:: | `"cmd_name"`:: | ||||||
| 	This event contains the command name for this git process | 	This event contains the command name for this git process | ||||||
| 	and the hierarchy of commands from parent git processes. | 	and the hierarchy of commands from parent git processes. | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										4
									
								
								Makefile
								
								
								
								
							|  | @ -1917,6 +1917,10 @@ ifneq ($(PROCFS_EXECUTABLE_PATH),) | ||||||
| 	BASIC_CFLAGS += '-DPROCFS_EXECUTABLE_PATH="$(procfs_executable_path_SQ)"' | 	BASIC_CFLAGS += '-DPROCFS_EXECUTABLE_PATH="$(procfs_executable_path_SQ)"' | ||||||
| endif | endif | ||||||
|  |  | ||||||
|  | ifndef HAVE_PLATFORM_PROCINFO | ||||||
|  | 	COMPAT_OBJS += compat/stub/procinfo.o | ||||||
|  | endif | ||||||
|  |  | ||||||
| ifdef HAVE_NS_GET_EXECUTABLE_PATH | ifdef HAVE_NS_GET_EXECUTABLE_PATH | ||||||
| 	BASIC_CFLAGS += -DHAVE_NS_GET_EXECUTABLE_PATH | 	BASIC_CFLAGS += -DHAVE_NS_GET_EXECUTABLE_PATH | ||||||
| endif | endif | ||||||
|  |  | ||||||
|  | @ -0,0 +1,55 @@ | ||||||
|  | #include "cache.h" | ||||||
|  |  | ||||||
|  | #include "strbuf.h" | ||||||
|  | #include "strvec.h" | ||||||
|  | #include "trace2.h" | ||||||
|  |  | ||||||
|  | static void get_ancestry_names(struct strvec *names) | ||||||
|  | { | ||||||
|  | 	/* | ||||||
|  | 	 * NEEDSWORK: We could gather the entire pstree into an array to match | ||||||
|  | 	 * functionality with compat/win32/trace2_win32_process_info.c. | ||||||
|  | 	 * To do so, we may want to examine /proc/<pid>/stat. For now, just | ||||||
|  | 	 * gather the immediate parent name which is readily accessible from | ||||||
|  | 	 * /proc/$(getppid())/comm. | ||||||
|  | 	 */ | ||||||
|  | 	struct strbuf procfs_path = STRBUF_INIT; | ||||||
|  | 	struct strbuf name = STRBUF_INIT; | ||||||
|  |  | ||||||
|  | 	/* try to use procfs if it's present. */ | ||||||
|  | 	strbuf_addf(&procfs_path, "/proc/%d/comm", getppid()); | ||||||
|  | 	if (strbuf_read_file(&name, procfs_path.buf, 0)) { | ||||||
|  | 		strbuf_release(&procfs_path); | ||||||
|  | 		strbuf_trim_trailing_newline(&name); | ||||||
|  | 		strvec_push(names, strbuf_detach(&name, NULL)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return; | ||||||
|  | 	/* NEEDSWORK: add non-procfs-linux implementations here */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void trace2_collect_process_info(enum trace2_process_info_reason reason) | ||||||
|  | { | ||||||
|  | 	if (!trace2_is_enabled()) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	/* someday we may want to write something extra here, but not today */ | ||||||
|  | 	if (reason == TRACE2_PROCESS_INFO_EXIT) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (reason == TRACE2_PROCESS_INFO_STARTUP) { | ||||||
|  | 		/* | ||||||
|  | 		 * NEEDSWORK: we could do the entire ptree in an array instead, | ||||||
|  | 		 * see compat/win32/trace2_win32_process_info.c. | ||||||
|  | 		 */ | ||||||
|  | 		struct strvec names = STRVEC_INIT; | ||||||
|  |  | ||||||
|  | 		get_ancestry_names(&names); | ||||||
|  |  | ||||||
|  | 		if (names.nr) | ||||||
|  | 			trace2_cmd_ancestry(names.v); | ||||||
|  | 		strvec_clear(&names); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | #include "git-compat-util.h" | ||||||
|  |  | ||||||
|  | #include "trace2.h" | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Stub. See sample implementations in compat/linux/procinfo.c and | ||||||
|  |  * compat/win32/trace2_win32_process_info.c. | ||||||
|  |  */ | ||||||
|  | void trace2_collect_process_info(enum trace2_process_info_reason reason) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | @ -58,6 +58,8 @@ ifeq ($(uname_S),Linux) | ||||||
| 	FREAD_READS_DIRECTORIES = UnfortunatelyYes | 	FREAD_READS_DIRECTORIES = UnfortunatelyYes | ||||||
| 	BASIC_CFLAGS += -DHAVE_SYSINFO | 	BASIC_CFLAGS += -DHAVE_SYSINFO | ||||||
| 	PROCFS_EXECUTABLE_PATH = /proc/self/exe | 	PROCFS_EXECUTABLE_PATH = /proc/self/exe | ||||||
|  | 	HAVE_PLATFORM_PROCINFO = YesPlease | ||||||
|  | 	COMPAT_OBJS += compat/linux/procinfo.o | ||||||
| endif | endif | ||||||
| ifeq ($(uname_S),GNU/kFreeBSD) | ifeq ($(uname_S),GNU/kFreeBSD) | ||||||
| 	HAVE_ALLOCA_H = YesPlease | 	HAVE_ALLOCA_H = YesPlease | ||||||
|  | @ -617,6 +619,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) | ||||||
| 	ETAGS_TARGET = ETAGS | 	ETAGS_TARGET = ETAGS | ||||||
| 	NO_POSIX_GOODIES = UnfortunatelyYes | 	NO_POSIX_GOODIES = UnfortunatelyYes | ||||||
| 	DEFAULT_HELP_FORMAT = html | 	DEFAULT_HELP_FORMAT = html | ||||||
|  | 	HAVE_PLATFORM_PROCINFO = YesPlease | ||||||
| 	BASIC_LDFLAGS += -municode | 	BASIC_LDFLAGS += -municode | ||||||
| 	COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32 | 	COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32 | ||||||
| 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" | 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" | ||||||
|  |  | ||||||
|  | @ -42,6 +42,12 @@ while (<>) { | ||||||
| 	# so just omit it for testing purposes. | 	# so just omit it for testing purposes. | ||||||
| 	# print "cmd_path _EXE_\n"; | 	# print "cmd_path _EXE_\n"; | ||||||
|     } |     } | ||||||
|  |     elsif ($line =~ m/^cmd_ancestry/) { | ||||||
|  | 	# 'cmd_ancestry' is not implemented everywhere, so for portability's | ||||||
|  | 	# sake, skip it when parsing normal. | ||||||
|  | 	# | ||||||
|  | 	# print "$line"; | ||||||
|  |     } | ||||||
|     else { |     else { | ||||||
| 	print "$line"; | 	print "$line"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -44,6 +44,11 @@ while (<>) { | ||||||
| 	# $tokens[$col_rest] = "_EXE_"; | 	# $tokens[$col_rest] = "_EXE_"; | ||||||
| 	goto SKIP_LINE; | 	goto SKIP_LINE; | ||||||
|     } |     } | ||||||
|  |     elsif ($tokens[$col_event] =~ m/cmd_ancestry/) { | ||||||
|  | 	# 'cmd_ancestry' is platform-specific and not implemented everywhere, | ||||||
|  | 	# so skip it. | ||||||
|  | 	goto SKIP_LINE; | ||||||
|  |     } | ||||||
|     elsif ($tokens[$col_event] =~ m/child_exit/) { |     elsif ($tokens[$col_event] =~ m/child_exit/) { | ||||||
| 	$tokens[$col_rest] =~ s/ pid:\d* / pid:_PID_ /; | 	$tokens[$col_rest] =~ s/ pid:\d* / pid:_PID_ /; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -132,7 +132,10 @@ while (<>) { | ||||||
| 	# just omit it for testing purposes. | 	# just omit it for testing purposes. | ||||||
| 	# $processes->{$sid}->{'path'} = "_EXE_"; | 	# $processes->{$sid}->{'path'} = "_EXE_"; | ||||||
|     } |     } | ||||||
|      |     elsif ($event eq 'cmd_ancestry') { | ||||||
|  | 	# 'cmd_ancestry' is platform-specific and not implemented everywhere, so | ||||||
|  | 	# just skip it for testing purposes. | ||||||
|  |     } | ||||||
|     elsif ($event eq 'cmd_name') { |     elsif ($event eq 'cmd_name') { | ||||||
| 	$processes->{$sid}->{'name'} = $line->{'name'}; | 	$processes->{$sid}->{'name'} = $line->{'name'}; | ||||||
| 	$processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'}; | 	$processes->{$sid}->{'hierarchy'} = $line->{'hierarchy'}; | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								trace2.c
								
								
								
								
							
							
						
						
									
										13
									
								
								trace2.c
								
								
								
								
							|  | @ -260,6 +260,19 @@ void trace2_cmd_path_fl(const char *file, int line, const char *pathname) | ||||||
| 			tgt_j->pfn_command_path_fl(file, line, pathname); | 			tgt_j->pfn_command_path_fl(file, line, pathname); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void trace2_cmd_ancestry_fl(const char *file, int line, const char **parent_names) | ||||||
|  | { | ||||||
|  | 	struct tr2_tgt *tgt_j; | ||||||
|  | 	int j; | ||||||
|  |  | ||||||
|  | 	if (!trace2_enabled) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	for_each_wanted_builtin (j, tgt_j) | ||||||
|  | 		if (tgt_j->pfn_command_ancestry_fl) | ||||||
|  | 			tgt_j->pfn_command_ancestry_fl(file, line, parent_names); | ||||||
|  | } | ||||||
|  |  | ||||||
| void trace2_cmd_name_fl(const char *file, int line, const char *name) | void trace2_cmd_name_fl(const char *file, int line, const char *name) | ||||||
| { | { | ||||||
| 	struct tr2_tgt *tgt_j; | 	struct tr2_tgt *tgt_j; | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								trace2.h
								
								
								
								
							
							
						
						
									
										16
									
								
								trace2.h
								
								
								
								
							|  | @ -133,6 +133,16 @@ void trace2_cmd_path_fl(const char *file, int line, const char *pathname); | ||||||
|  |  | ||||||
| #define trace2_cmd_path(p) trace2_cmd_path_fl(__FILE__, __LINE__, (p)) | #define trace2_cmd_path(p) trace2_cmd_path_fl(__FILE__, __LINE__, (p)) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Emit an 'ancestry' event with the process name of the current process's | ||||||
|  |  * parent process. | ||||||
|  |  * This gives post-processors a way to determine what invoked the command and | ||||||
|  |  * learn more about usage patterns. | ||||||
|  |  */ | ||||||
|  | void trace2_cmd_ancestry_fl(const char *file, int line, const char **parent_names); | ||||||
|  |  | ||||||
|  | #define trace2_cmd_ancestry(v) trace2_cmd_ancestry_fl(__FILE__, __LINE__, (v)) | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Emit a 'cmd_name' event with the canonical name of the command. |  * Emit a 'cmd_name' event with the canonical name of the command. | ||||||
|  * This gives post-processors a simple field to identify the command |  * This gives post-processors a simple field to identify the command | ||||||
|  | @ -492,13 +502,7 @@ enum trace2_process_info_reason { | ||||||
| 	TRACE2_PROCESS_INFO_EXIT, | 	TRACE2_PROCESS_INFO_EXIT, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| #if defined(GIT_WINDOWS_NATIVE) |  | ||||||
| void trace2_collect_process_info(enum trace2_process_info_reason reason); | void trace2_collect_process_info(enum trace2_process_info_reason reason); | ||||||
| #else |  | ||||||
| #define trace2_collect_process_info(reason) \ |  | ||||||
| 	do {                                \ |  | ||||||
| 	} while (0) |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| const char *trace2_session_id(void); | const char *trace2_session_id(void); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -27,6 +27,8 @@ typedef void(tr2_tgt_evt_error_va_fl_t)(const char *file, int line, | ||||||
|  |  | ||||||
| typedef void(tr2_tgt_evt_command_path_fl_t)(const char *file, int line, | typedef void(tr2_tgt_evt_command_path_fl_t)(const char *file, int line, | ||||||
| 					    const char *command_path); | 					    const char *command_path); | ||||||
|  | typedef void(tr2_tgt_evt_command_ancestry_fl_t)(const char *file, int line, | ||||||
|  | 						const char **parent_names); | ||||||
| typedef void(tr2_tgt_evt_command_name_fl_t)(const char *file, int line, | typedef void(tr2_tgt_evt_command_name_fl_t)(const char *file, int line, | ||||||
| 					    const char *name, | 					    const char *name, | ||||||
| 					    const char *hierarchy); | 					    const char *hierarchy); | ||||||
|  | @ -108,6 +110,7 @@ struct tr2_tgt { | ||||||
| 	tr2_tgt_evt_atexit_t                    *pfn_atexit; | 	tr2_tgt_evt_atexit_t                    *pfn_atexit; | ||||||
| 	tr2_tgt_evt_error_va_fl_t               *pfn_error_va_fl; | 	tr2_tgt_evt_error_va_fl_t               *pfn_error_va_fl; | ||||||
| 	tr2_tgt_evt_command_path_fl_t           *pfn_command_path_fl; | 	tr2_tgt_evt_command_path_fl_t           *pfn_command_path_fl; | ||||||
|  | 	tr2_tgt_evt_command_ancestry_fl_t	*pfn_command_ancestry_fl; | ||||||
| 	tr2_tgt_evt_command_name_fl_t           *pfn_command_name_fl; | 	tr2_tgt_evt_command_name_fl_t           *pfn_command_name_fl; | ||||||
| 	tr2_tgt_evt_command_mode_fl_t           *pfn_command_mode_fl; | 	tr2_tgt_evt_command_mode_fl_t           *pfn_command_mode_fl; | ||||||
| 	tr2_tgt_evt_alias_fl_t                  *pfn_alias_fl; | 	tr2_tgt_evt_alias_fl_t                  *pfn_alias_fl; | ||||||
|  |  | ||||||
|  | @ -261,6 +261,26 @@ static void fn_command_path_fl(const char *file, int line, const char *pathname) | ||||||
| 	jw_release(&jw); | 	jw_release(&jw); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void fn_command_ancestry_fl(const char *file, int line, const char **parent_names) | ||||||
|  | { | ||||||
|  | 	const char *event_name = "cmd_ancestry"; | ||||||
|  | 	const char *parent_name = NULL; | ||||||
|  | 	struct json_writer jw = JSON_WRITER_INIT; | ||||||
|  |  | ||||||
|  | 	jw_object_begin(&jw, 0); | ||||||
|  | 	event_fmt_prepare(event_name, file, line, NULL, &jw); | ||||||
|  | 	jw_object_inline_begin_array(&jw, "ancestry"); | ||||||
|  |  | ||||||
|  | 	while ((parent_name = *parent_names++)) | ||||||
|  | 		jw_array_string(&jw, parent_name); | ||||||
|  |  | ||||||
|  | 	jw_end(&jw); /* 'ancestry' array */ | ||||||
|  | 	jw_end(&jw); /* event object */ | ||||||
|  |  | ||||||
|  | 	tr2_dst_write_line(&tr2dst_event, &jw.json); | ||||||
|  | 	jw_release(&jw); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void fn_command_name_fl(const char *file, int line, const char *name, | static void fn_command_name_fl(const char *file, int line, const char *name, | ||||||
| 			       const char *hierarchy) | 			       const char *hierarchy) | ||||||
| { | { | ||||||
|  | @ -584,6 +604,7 @@ struct tr2_tgt tr2_tgt_event = { | ||||||
| 	fn_atexit, | 	fn_atexit, | ||||||
| 	fn_error_va_fl, | 	fn_error_va_fl, | ||||||
| 	fn_command_path_fl, | 	fn_command_path_fl, | ||||||
|  | 	fn_command_ancestry_fl, | ||||||
| 	fn_command_name_fl, | 	fn_command_name_fl, | ||||||
| 	fn_command_mode_fl, | 	fn_command_mode_fl, | ||||||
| 	fn_alias_fl, | 	fn_alias_fl, | ||||||
|  |  | ||||||
|  | @ -160,6 +160,24 @@ static void fn_command_path_fl(const char *file, int line, const char *pathname) | ||||||
| 	strbuf_release(&buf_payload); | 	strbuf_release(&buf_payload); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void fn_command_ancestry_fl(const char *file, int line, const char **parent_names) | ||||||
|  | { | ||||||
|  | 	const char *parent_name = NULL; | ||||||
|  | 	struct strbuf buf_payload = STRBUF_INIT; | ||||||
|  |  | ||||||
|  | 	/* cmd_ancestry parent <- grandparent <- great-grandparent */ | ||||||
|  | 	strbuf_addstr(&buf_payload, "cmd_ancestry "); | ||||||
|  | 	while ((parent_name = *parent_names++)) { | ||||||
|  | 		strbuf_addstr(&buf_payload, parent_name); | ||||||
|  | 		/* if we'll write another one after this, add a delimiter */ | ||||||
|  | 		if (parent_names && *parent_names) | ||||||
|  | 			strbuf_addstr(&buf_payload, " <- "); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	normal_io_write_fl(file, line, &buf_payload); | ||||||
|  | 	strbuf_release(&buf_payload); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void fn_command_name_fl(const char *file, int line, const char *name, | static void fn_command_name_fl(const char *file, int line, const char *name, | ||||||
| 			       const char *hierarchy) | 			       const char *hierarchy) | ||||||
| { | { | ||||||
|  | @ -306,6 +324,7 @@ struct tr2_tgt tr2_tgt_normal = { | ||||||
| 	fn_atexit, | 	fn_atexit, | ||||||
| 	fn_error_va_fl, | 	fn_error_va_fl, | ||||||
| 	fn_command_path_fl, | 	fn_command_path_fl, | ||||||
|  | 	fn_command_ancestry_fl, | ||||||
| 	fn_command_name_fl, | 	fn_command_name_fl, | ||||||
| 	fn_command_mode_fl, | 	fn_command_mode_fl, | ||||||
| 	fn_alias_fl, | 	fn_alias_fl, | ||||||
|  |  | ||||||
|  | @ -253,6 +253,21 @@ static void fn_command_path_fl(const char *file, int line, const char *pathname) | ||||||
| 	strbuf_release(&buf_payload); | 	strbuf_release(&buf_payload); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void fn_command_ancestry_fl(const char *file, int line, const char **parent_names) | ||||||
|  | { | ||||||
|  | 	const char *event_name = "cmd_ancestry"; | ||||||
|  | 	struct strbuf buf_payload = STRBUF_INIT; | ||||||
|  |  | ||||||
|  | 	strbuf_addstr(&buf_payload, "ancestry:["); | ||||||
|  | 	/* It's not an argv but the rules are basically the same. */ | ||||||
|  | 	sq_append_quote_argv_pretty(&buf_payload, parent_names); | ||||||
|  | 	strbuf_addch(&buf_payload, ']'); | ||||||
|  |  | ||||||
|  | 	perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, | ||||||
|  | 			 &buf_payload); | ||||||
|  | 	strbuf_release(&buf_payload); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void fn_command_name_fl(const char *file, int line, const char *name, | static void fn_command_name_fl(const char *file, int line, const char *name, | ||||||
| 			       const char *hierarchy) | 			       const char *hierarchy) | ||||||
| { | { | ||||||
|  | @ -532,6 +547,7 @@ struct tr2_tgt tr2_tgt_perf = { | ||||||
| 	fn_atexit, | 	fn_atexit, | ||||||
| 	fn_error_va_fl, | 	fn_error_va_fl, | ||||||
| 	fn_command_path_fl, | 	fn_command_path_fl, | ||||||
|  | 	fn_command_ancestry_fl, | ||||||
| 	fn_command_name_fl, | 	fn_command_name_fl, | ||||||
| 	fn_command_mode_fl, | 	fn_command_mode_fl, | ||||||
| 	fn_alias_fl, | 	fn_alias_fl, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano