trace2: add macOS process ancestry tracing
In 353d3d77f4 (trace2: collect Windows-specific process information, 2019-02-22) Windows-specific process ancestry information was added as a data_json event to TRACE2. Furthermore in 2f732bf15e (tr2: log parent process name, 2021-07-21) 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 <mjcheetham@outlook.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Matthew John Cheetham committed
Feb 13, 2026 at 19:54 UTC
fd104ef15deb16fae9ed48ad548f1d7a40e17222
1 file changed
+97
compat/darwin/procinfo.c
new
+97
@@ -0,0 +1,97 @@
1
+#include "git-compat-util.h"
2
+#include "strbuf.h"
3
+#include "strvec.h"
4
+#include "trace2.h"
5
+#include <sys/sysctl.h>
6
+
7
+/*
8
+ * An arbitrarily chosen value to limit the depth of the ancestor chain.
9
+ */
10
+#define NR_PIDS_LIMIT 10
11
+
12
+/*
13
+ * Get the process name and parent PID for a given PID using sysctl().
14
+ * Returns 0 on success, -1 on failure.
15
+ */
16
+static int get_proc_info(pid_t pid, struct strbuf *name, pid_t *ppid)
17
+{
18
+ int mib[4];
19
+ struct kinfo_proc proc;
20
+ size_t size = sizeof(proc);
21
+
22
+ mib[0] = CTL_KERN;
23
+ mib[1] = KERN_PROC;
24
+ mib[2] = KERN_PROC_PID;
25
+ mib[3] = pid;
26
+
27
+ if (sysctl(mib, 4, &proc, &size, NULL, 0) < 0)
28
+ return -1;
29
+
30
+ if (size == 0)
31
+ return -1;
32
+
33
+ strbuf_addstr(name, proc.kp_proc.p_comm);
34
+ *ppid = proc.kp_eproc.e_ppid;
35
+
36
+ return 0;
37
+}
38
+
39
+/*
40
+ * Recursively push process names onto the ancestry array.
41
+ * We guard against cycles by limiting the depth to NR_PIDS_LIMIT.
42
+ */
43
+static void push_ancestry_name(struct strvec *names, pid_t pid, int depth)
44
+{
45
+ struct strbuf name = STRBUF_INIT;
46
+ pid_t ppid;
47
+
48
+ if (depth >= NR_PIDS_LIMIT)
49
+ return;
50
+
51
+ if (pid <= 0)
52
+ return;
53
+
54
+ if (get_proc_info(pid, &name, &ppid) < 0)
55
+ goto cleanup;
56
+
57
+ strvec_push(names, name.buf);
58
+
59
+ /*
60
+ * Recurse to the parent process. Stop if ppid not valid
61
+ * or if we've reached ourselves (cycle).
62
+ */
63
+ if (ppid && ppid != pid)
64
+ push_ancestry_name(names, ppid, depth + 1);
65
+
66
+cleanup:
67
+ strbuf_release(&name);
68
+}
69
+
70
+void trace2_collect_process_info(enum trace2_process_info_reason reason)
71
+{
72
+ struct strvec names = STRVEC_INIT;
73
+
74
+ if (!trace2_is_enabled())
75
+ return;
76
+
77
+ switch (reason) {
78
+ case TRACE2_PROCESS_INFO_STARTUP:
79
+ push_ancestry_name(&names, getppid(), 0);
80
+ if (names.nr)
81
+ trace2_cmd_ancestry(names.v);
82
+
83
+ strvec_clear(&names);
84
+ break;
85
+
86
+ case TRACE2_PROCESS_INFO_EXIT:
87
+ /*
88
+ * The Windows version of this calls its
89
+ * get_peak_memory_info() here. We may want to insert
90
+ * similar process-end statistics here in the future.
91
+ */
92
+ break;
93
+
94
+ default:
95
+ BUG("trace2_collect_process_info: unknown reason '%d'", reason);
96
+ }
97
+}