Skip to content

Commit 318670b

Browse files
committed
Merge branch 'mc/tr2-process-ancestry-cleanup' into seen
Add process ancestry data to trace2 on macOS to match what we already do on Linux and Windows. Also adjust the way Windows implementation reports this information to match the other two. * mc/tr2-process-ancestry-cleanup: trace2: emit cmd_ancestry data for Windows trace2: refactor Windows process ancestry trace2 event build: include procinfo.c impl for macOS trace2: add macOS process ancestry tracing
2 parents 14f2e05 + b2ef8b1 commit 318670b

File tree

5 files changed

+138
-25
lines changed

5 files changed

+138
-25
lines changed

compat/darwin/procinfo.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#define USE_THE_REPOSITORY_VARIABLE
2+
3+
#include "git-compat-util.h"
4+
#include "strbuf.h"
5+
#include "strvec.h"
6+
#include "trace2.h"
7+
#include <sys/sysctl.h>
8+
9+
/*
10+
* An arbitrarily chosen value to limit the depth of the ancestor chain.
11+
*/
12+
#define NR_PIDS_LIMIT 10
13+
14+
/*
15+
* Get the process name and parent PID for a given PID using sysctl().
16+
* Returns 0 on success, -1 on failure.
17+
*/
18+
static int get_proc_info(pid_t pid, struct strbuf *name, pid_t *ppid)
19+
{
20+
int mib[4];
21+
struct kinfo_proc proc;
22+
size_t size = sizeof(proc);
23+
24+
mib[0] = CTL_KERN;
25+
mib[1] = KERN_PROC;
26+
mib[2] = KERN_PROC_PID;
27+
mib[3] = pid;
28+
29+
if (sysctl(mib, 4, &proc, &size, NULL, 0) < 0)
30+
return -1;
31+
32+
if (size == 0)
33+
return -1;
34+
35+
strbuf_addstr(name, proc.kp_proc.p_comm);
36+
*ppid = proc.kp_eproc.e_ppid;
37+
38+
return 0;
39+
}
40+
41+
/*
42+
* Recursively push process names onto the ancestry array.
43+
* We guard against cycles by limiting the depth to NR_PIDS_LIMIT.
44+
*/
45+
static void push_ancestry_name(struct strvec *names, pid_t pid, int depth)
46+
{
47+
struct strbuf name = STRBUF_INIT;
48+
pid_t ppid;
49+
50+
if (depth >= NR_PIDS_LIMIT)
51+
return;
52+
53+
if (pid <= 0)
54+
return;
55+
56+
if (get_proc_info(pid, &name, &ppid) < 0)
57+
goto cleanup;
58+
59+
strvec_push(names, name.buf);
60+
61+
/*
62+
* Recurse to the parent process. Stop if ppid is 0 or 1
63+
* (init/launchd) or if we've reached ourselves (cycle).
64+
*/
65+
if (ppid > 1 && ppid != pid)
66+
push_ancestry_name(names, ppid, depth + 1);
67+
68+
cleanup:
69+
strbuf_release(&name);
70+
}
71+
72+
void trace2_collect_process_info(enum trace2_process_info_reason reason)
73+
{
74+
struct strvec names = STRVEC_INIT;
75+
76+
if (!trace2_is_enabled())
77+
return;
78+
79+
switch (reason) {
80+
case TRACE2_PROCESS_INFO_STARTUP:
81+
push_ancestry_name(&names, getppid(), 0);
82+
if (names.nr)
83+
trace2_cmd_ancestry(names.v);
84+
85+
strvec_clear(&names);
86+
break;
87+
88+
case TRACE2_PROCESS_INFO_EXIT:
89+
/*
90+
* The Windows version of this calls its
91+
* get_peak_memory_info() here. We may want to insert
92+
* similar process-end statistics here in the future.
93+
*/
94+
break;
95+
96+
default:
97+
BUG("trace2_collect_process_info: unknown reason '%d'", reason);
98+
}
99+
}

compat/win32/trace2_win32_process_info.c

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "../../git-compat-util.h"
44
#include "../../json-writer.h"
55
#include "../../repository.h"
6+
#include "../../strvec.h"
67
#include "../../trace2.h"
78
#include "lazyload.h"
89
#include <psapi.h>
@@ -32,12 +33,7 @@ static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32)
3233
}
3334

3435
/*
35-
* Accumulate JSON array of our parent processes:
36-
* [
37-
* exe-name-parent,
38-
* exe-name-grand-parent,
39-
* ...
40-
* ]
36+
* Accumulate array of our parent process names.
4137
*
4238
* Note: we only report the filename of the process executable; the
4339
* 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)
7369
* simple and avoid the alloc/realloc overhead. It is OK if we
7470
* truncate the search and return a partial answer.
7571
*/
76-
static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
72+
static void get_processes(struct strvec *names, HANDLE hSnapshot)
7773
{
7874
PROCESSENTRY32 pe32;
7975
DWORD pid;
@@ -82,19 +78,19 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
8278

8379
pid = GetCurrentProcessId();
8480
while (find_pid(pid, hSnapshot, &pe32)) {
85-
/* Only report parents. Omit self from the JSON output. */
81+
/* Only report parents. Omit self from the output. */
8682
if (nr_pids)
87-
jw_array_string(jw, pe32.szExeFile);
83+
strvec_push(names, pe32.szExeFile);
8884

8985
/* Check for cycle in snapshot. (Yes, it happened.) */
9086
for (k = 0; k < nr_pids; k++)
9187
if (pid == pid_list[k]) {
92-
jw_array_string(jw, "(cycle)");
88+
strvec_push(names, "(cycle)");
9389
return;
9490
}
9591

9692
if (nr_pids == NR_PIDS_LIMIT) {
97-
jw_array_string(jw, "(truncated)");
93+
strvec_push(names, "(truncated)");
9894
return;
9995
}
10096

@@ -105,24 +101,14 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
105101
}
106102

107103
/*
108-
* Emit JSON data for the current and parent processes. Individual
109-
* trace2 targets can decide how to actually print it.
104+
* Collect the list of parent process names.
110105
*/
111-
static void get_ancestry(void)
106+
static void get_ancestry(struct strvec *names)
112107
{
113108
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
114109

115110
if (hSnapshot != INVALID_HANDLE_VALUE) {
116-
struct json_writer jw = JSON_WRITER_INIT;
117-
118-
jw_array_begin(&jw, 0);
119-
get_processes(&jw, hSnapshot);
120-
jw_end(&jw);
121-
122-
trace2_data_json("process", the_repository, "windows/ancestry",
123-
&jw);
124-
125-
jw_release(&jw);
111+
get_processes(names, hSnapshot);
126112
CloseHandle(hSnapshot);
127113
}
128114
}
@@ -176,13 +162,35 @@ static void get_peak_memory_info(void)
176162

177163
void trace2_collect_process_info(enum trace2_process_info_reason reason)
178164
{
165+
struct strvec names = STRVEC_INIT;
166+
179167
if (!trace2_is_enabled())
180168
return;
181169

182170
switch (reason) {
183171
case TRACE2_PROCESS_INFO_STARTUP:
184172
get_is_being_debugged();
185-
get_ancestry();
173+
get_ancestry(&names);
174+
if (names.nr) {
175+
/*
176+
Emit the ancestry data as a data_json event to
177+
maintain compatibility for consumers of the older
178+
"windows/ancestry" event.
179+
*/
180+
struct json_writer jw = JSON_WRITER_INIT;
181+
jw_array_begin(&jw, 0);
182+
for (size_t i = 0; i < names.nr; i++)
183+
jw_array_string(&jw, names.v[i]);
184+
jw_end(&jw);
185+
trace2_data_json("process", the_repository,
186+
"windows/ancestry", &jw);
187+
jw_release(&jw);
188+
189+
/* Emit the ancestry data with the new event. */
190+
trace2_cmd_ancestry(names.v);
191+
}
192+
193+
strvec_clear(&names);
186194
return;
187195

188196
case TRACE2_PROCESS_INFO_EXIT:

config.mak.uname

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ ifeq ($(uname_S),Darwin)
159159
HAVE_NS_GET_EXECUTABLE_PATH = YesPlease
160160
CSPRNG_METHOD = arc4random
161161
USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS = YesPlease
162+
HAVE_PLATFORM_PROCINFO = YesPlease
163+
COMPAT_OBJS += compat/darwin/procinfo.o
162164

163165
ifeq ($(uname_M),arm64)
164166
HOMEBREW_PREFIX = /opt/homebrew

contrib/buildsystems/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
274274
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
275275
add_compile_definitions(PROCFS_EXECUTABLE_PATH="/proc/self/exe" HAVE_DEV_TTY )
276276
list(APPEND compat_SOURCES unix-socket.c unix-stream-server.c compat/linux/procinfo.c)
277+
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
278+
list(APPEND compat_SOURCES compat/darwin/procinfo.c)
277279
endif()
278280

279281
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")

meson.build

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,6 +1294,8 @@ if host_machine.system() == 'linux'
12941294
libgit_sources += 'compat/linux/procinfo.c'
12951295
elif host_machine.system() == 'windows'
12961296
libgit_sources += 'compat/win32/trace2_win32_process_info.c'
1297+
elif host_machine.system() == 'darwin'
1298+
libgit_sources += 'compat/darwin/procinfo.c'
12971299
else
12981300
libgit_sources += 'compat/stub/procinfo.c'
12991301
endif

0 commit comments

Comments
 (0)