From d99a30a1a77f0f23468dba987da08b32dd9a92fa Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 2 Feb 2026 15:32:05 +0000 Subject: [PATCH 1/4] trace2: add macOS process ancestry tracing In 353d3d77 (trace2: collect Windows-specific process information) Windows-specific process ancestry information was added as a data_json event to TRACE2. Furthermore in 2f732bf1 (tr2: log parent process name) similar functionality was added for Linux-based systems, using procfs. Teach Git to also log process ancestry on macOS using the sysctl with KERN_PROC to get process information (PPID and process name). Like the Linux implementation, we use the cmd_ancestry TRACE2 event rather than using a data_json event and creating another custom data point. Signed-off-by: Matthew John Cheetham --- compat/darwin/procinfo.c | 99 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 compat/darwin/procinfo.c diff --git a/compat/darwin/procinfo.c b/compat/darwin/procinfo.c new file mode 100644 index 00000000000000..e0d76d81ec09d3 --- /dev/null +++ b/compat/darwin/procinfo.c @@ -0,0 +1,99 @@ +#define USE_THE_REPOSITORY_VARIABLE + +#include "git-compat-util.h" +#include "strbuf.h" +#include "strvec.h" +#include "trace2.h" +#include + +/* + * An arbitrarily chosen value to limit the depth of the ancestor chain. + */ +#define NR_PIDS_LIMIT 10 + +/* + * Get the process name and parent PID for a given PID using sysctl(). + * Returns 0 on success, -1 on failure. + */ +static int get_proc_info(pid_t pid, struct strbuf *name, pid_t *ppid) +{ + int mib[4]; + struct kinfo_proc proc; + size_t size = sizeof(proc); + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + if (sysctl(mib, 4, &proc, &size, NULL, 0) < 0) + return -1; + + if (size == 0) + return -1; + + strbuf_addstr(name, proc.kp_proc.p_comm); + *ppid = proc.kp_eproc.e_ppid; + + return 0; +} + +/* + * Recursively push process names onto the ancestry array. + * We guard against cycles by limiting the depth to NR_PIDS_LIMIT. + */ +static void push_ancestry_name(struct strvec *names, pid_t pid, int depth) +{ + struct strbuf name = STRBUF_INIT; + pid_t ppid; + + if (depth >= NR_PIDS_LIMIT) + return; + + if (pid <= 0) + return; + + if (get_proc_info(pid, &name, &ppid) < 0) + goto cleanup; + + strvec_push(names, name.buf); + + /* + * Recurse to the parent process. Stop if ppid is 0 or 1 + * (init/launchd) or if we've reached ourselves (cycle). + */ + if (ppid > 1 && ppid != pid) + push_ancestry_name(names, ppid, depth + 1); + +cleanup: + strbuf_release(&name); +} + +void trace2_collect_process_info(enum trace2_process_info_reason reason) +{ + struct strvec names = STRVEC_INIT; + + if (!trace2_is_enabled()) + return; + + switch (reason) { + case TRACE2_PROCESS_INFO_STARTUP: + push_ancestry_name(&names, getppid(), 0); + if (names.nr) + trace2_cmd_ancestry(names.v); + + strvec_clear(&names); + break; + + case TRACE2_PROCESS_INFO_EXIT: + /* + * The Windows version of this calls its + * get_peak_memory_info() here. We may want to insert + * similar process-end statistics here in the future. + */ + break; + + default: + BUG("trace2_collect_process_info: unknown reason '%d'", reason); + } +} From c786a038f31f0d3b5f612e9124e3dfb23e56a027 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 2 Feb 2026 15:44:24 +0000 Subject: [PATCH 2/4] build: include procinfo.c impl for macOS Include an implementation of trace2_collect_process_info for macOS. Signed-off-by: Matthew John Cheetham --- config.mak.uname | 2 ++ contrib/buildsystems/CMakeLists.txt | 2 ++ meson.build | 2 ++ 3 files changed, 6 insertions(+) diff --git a/config.mak.uname b/config.mak.uname index 1691c6ae6e01e3..baa5018461c3db 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -148,6 +148,8 @@ ifeq ($(uname_S),Darwin) HAVE_NS_GET_EXECUTABLE_PATH = YesPlease CSPRNG_METHOD = arc4random USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS = YesPlease + HAVE_PLATFORM_PROCINFO = YesPlease + COMPAT_OBJS += compat/darwin/procinfo.o # Workaround for `gettext` being keg-only and not even being linked via # `brew link --force gettext`, should be obsolete as of diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index edb0fc04ad7649..d489f0cadab4de 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -274,6 +274,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows") elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") add_compile_definitions(PROCFS_EXECUTABLE_PATH="/proc/self/exe" HAVE_DEV_TTY ) list(APPEND compat_SOURCES unix-socket.c unix-stream-server.c compat/linux/procinfo.c) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + list(APPEND compat_SOURCES compat/darwin/procinfo.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Windows") diff --git a/meson.build b/meson.build index 1f95a06edb7829..32d470e4f73739 100644 --- a/meson.build +++ b/meson.build @@ -1292,6 +1292,8 @@ if host_machine.system() == 'linux' libgit_sources += 'compat/linux/procinfo.c' elif host_machine.system() == 'windows' libgit_sources += 'compat/win32/trace2_win32_process_info.c' +elif host_machine.system() == 'darwin' + libgit_sources += 'compat/darwin/procinfo.c' else libgit_sources += 'compat/stub/procinfo.c' endif From 7ccd0a9a6d89decaa5856494a184c71bc0d678e9 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 2 Feb 2026 15:49:53 +0000 Subject: [PATCH 3/4] trace2: refactor Windows process ancestry trace2 event In 353d3d77 (trace2: collect Windows-specific process information) we added process ancestry information for Windows to TRACE2 via a data_json event. It was only later in 2f732bf1 (tr2: log parent process name) that the specific cmd_ancestry event was added to TRACE2. In a future commit we will emit the ancestry information with the newer cmd_ancestry TRACE2 event. Right now, we rework this implementation of trace2_collect_process_info to separate the calculation of ancestors from building and emiting the JSON array via a data_json event. Signed-off-by: Matthew John Cheetham --- compat/win32/trace2_win32_process_info.c | 50 ++++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c index f147da706a240e..aceea054301501 100644 --- a/compat/win32/trace2_win32_process_info.c +++ b/compat/win32/trace2_win32_process_info.c @@ -3,6 +3,7 @@ #include "../../git-compat-util.h" #include "../../json-writer.h" #include "../../repository.h" +#include "../../strvec.h" #include "../../trace2.h" #include "lazyload.h" #include @@ -32,12 +33,7 @@ static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32) } /* - * Accumulate JSON array of our parent processes: - * [ - * exe-name-parent, - * exe-name-grand-parent, - * ... - * ] + * Accumulate array of our parent process names. * * Note: we only report the filename of the process executable; the * only way to get its full pathname is to use OpenProcess() @@ -73,7 +69,7 @@ static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32) * simple and avoid the alloc/realloc overhead. It is OK if we * truncate the search and return a partial answer. */ -static void get_processes(struct json_writer *jw, HANDLE hSnapshot) +static void get_processes(struct strvec *names, HANDLE hSnapshot) { PROCESSENTRY32 pe32; DWORD pid; @@ -82,19 +78,19 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot) pid = GetCurrentProcessId(); while (find_pid(pid, hSnapshot, &pe32)) { - /* Only report parents. Omit self from the JSON output. */ + /* Only report parents. Omit self from the output. */ if (nr_pids) - jw_array_string(jw, pe32.szExeFile); + strvec_push(names, pe32.szExeFile); /* Check for cycle in snapshot. (Yes, it happened.) */ for (k = 0; k < nr_pids; k++) if (pid == pid_list[k]) { - jw_array_string(jw, "(cycle)"); + strvec_push(names, "(cycle)"); return; } if (nr_pids == NR_PIDS_LIMIT) { - jw_array_string(jw, "(truncated)"); + strvec_push(names, "(truncated)"); return; } @@ -105,24 +101,14 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot) } /* - * Emit JSON data for the current and parent processes. Individual - * trace2 targets can decide how to actually print it. + * Collect the list of parent process names. */ -static void get_ancestry(void) +static void get_ancestry(struct strvec *names) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot != INVALID_HANDLE_VALUE) { - struct json_writer jw = JSON_WRITER_INIT; - - jw_array_begin(&jw, 0); - get_processes(&jw, hSnapshot); - jw_end(&jw); - - trace2_data_json("process", the_repository, "windows/ancestry", - &jw); - - jw_release(&jw); + get_processes(names, hSnapshot); CloseHandle(hSnapshot); } } @@ -176,13 +162,27 @@ static void get_peak_memory_info(void) void trace2_collect_process_info(enum trace2_process_info_reason reason) { + struct strvec names = STRVEC_INIT; + if (!trace2_is_enabled()) return; switch (reason) { case TRACE2_PROCESS_INFO_STARTUP: get_is_being_debugged(); - get_ancestry(); + get_ancestry(&names); + if (names.nr) { + struct json_writer jw = JSON_WRITER_INIT; + jw_array_begin(&jw, 0); + for (size_t i = 0; i < names.nr; i++) + jw_array_string(&jw, names.v[i]); + jw_end(&jw); + trace2_data_json("process", the_repository, + "windows/ancestry", &jw); + jw_release(&jw); + } + + strvec_clear(&names); return; case TRACE2_PROCESS_INFO_EXIT: From a06344dc753698ece37f8d066b0a51931b7fa16f Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 2 Feb 2026 15:57:26 +0000 Subject: [PATCH 4/4] trace2: emit cmd_ancestry data for Windows Since 2f732bf1 (tr2: log parent process name) it is now possible to emit a specific process ancestry event in TRACE2. We should emit the Windows process ancestry data with the correct event type. To not break existing consumers of the data_json "windows/ancestry" event, we continue to emit the ancestry data as a JSON event. Signed-off-by: Matthew John Cheetham --- compat/win32/trace2_win32_process_info.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c index aceea054301501..6a6a396078899c 100644 --- a/compat/win32/trace2_win32_process_info.c +++ b/compat/win32/trace2_win32_process_info.c @@ -172,6 +172,11 @@ void trace2_collect_process_info(enum trace2_process_info_reason reason) get_is_being_debugged(); get_ancestry(&names); if (names.nr) { + /* + Emit the ancestry data as a data_json event to + maintain compatibility for consumers of the older + "windows/ancestry" event. + */ struct json_writer jw = JSON_WRITER_INIT; jw_array_begin(&jw, 0); for (size_t i = 0; i < names.nr; i++) @@ -180,6 +185,9 @@ void trace2_collect_process_info(enum trace2_process_info_reason reason) trace2_data_json("process", the_repository, "windows/ancestry", &jw); jw_release(&jw); + + /* Emit the ancestry data with the new event. */ + trace2_cmd_ancestry(names.v); } strvec_clear(&names);