/src/git/compat/linux/procinfo.c
Line | Count | Source |
1 | | #include "git-compat-util.h" |
2 | | |
3 | | #include "strbuf.h" |
4 | | #include "strvec.h" |
5 | | #include "trace2.h" |
6 | | |
7 | | /* |
8 | | * We need more complex parsing in stat_parent_pid() and |
9 | | * parse_proc_stat() below than a dumb fscanf(). That's because while |
10 | | * the statcomm field is surrounded by parentheses, the process itself |
11 | | * is free to insert any arbitrary byte sequence its its name. That |
12 | | * can include newlines, spaces, closing parentheses etc. |
13 | | * |
14 | | * See do_task_stat() in fs/proc/array.c in linux.git, this is in |
15 | | * contrast with the escaped version of the name found in |
16 | | * /proc/%d/status. |
17 | | * |
18 | | * So instead of using fscanf() we'll read N bytes from it, look for |
19 | | * the first "(", and then the last ")", anything in-between is our |
20 | | * process name. |
21 | | * |
22 | | * How much N do we need? On Linux /proc/sys/kernel/pid_max is 2^15 by |
23 | | * default, but it can be raised set to values of up to 2^22. So |
24 | | * that's 7 digits for a PID. We have 2 PIDs in the first four fields |
25 | | * we're interested in, so 2 * 7 = 14. |
26 | | * |
27 | | * We then have 3 spaces between those four values, and we'd like to |
28 | | * get to the space between the 4th and the 5th (the "pgrp" field) to |
29 | | * make sure we read the entire "ppid" field. So that brings us up to |
30 | | * 14 + 3 + 1 = 18. Add the two parentheses around the "comm" value |
31 | | * and it's 20. The "state" value itself is then one character (now at |
32 | | * 21). |
33 | | * |
34 | | * Finally the maximum length of the "comm" name itself is 15 |
35 | | * characters, e.g. a setting of "123456789abcdefg" will be truncated |
36 | | * to "123456789abcdef". See PR_SET_NAME in prctl(2). So all in all |
37 | | * we'd need to read 21 + 15 = 36 bytes. |
38 | | * |
39 | | * Let's just read 2^6 (64) instead for good measure. If PID_MAX ever |
40 | | * grows past 2^22 we'll be future-proof. We'll then anchor at the |
41 | | * last ")" we find to locate the parent PID. |
42 | | */ |
43 | 0 | #define STAT_PARENT_PID_READ_N 64 |
44 | | |
45 | | static int parse_proc_stat(struct strbuf *sb, struct strbuf *name, |
46 | | int *statppid) |
47 | 0 | { |
48 | 0 | const char *comm_lhs = strchr(sb->buf, '('); |
49 | 0 | const char *comm_rhs = strrchr(sb->buf, ')'); |
50 | 0 | const char *ppid_lhs, *ppid_rhs; |
51 | 0 | char *p; |
52 | 0 | pid_t ppid; |
53 | |
|
54 | 0 | if (!comm_lhs || !comm_rhs) |
55 | 0 | goto bad_kernel; |
56 | | |
57 | | /* |
58 | | * We're at the ")", that's followed by " X ", where X is a |
59 | | * single "state" character. So advance by 4 bytes. |
60 | | */ |
61 | 0 | ppid_lhs = comm_rhs + 4; |
62 | | |
63 | | /* |
64 | | * Read until the space between the "ppid" and "pgrp" fields |
65 | | * to make sure we're anchored after the untruncated "ppid" |
66 | | * field.. |
67 | | */ |
68 | 0 | ppid_rhs = strchr(ppid_lhs, ' '); |
69 | 0 | if (!ppid_rhs) |
70 | 0 | goto bad_kernel; |
71 | | |
72 | 0 | ppid = strtol(ppid_lhs, &p, 10); |
73 | 0 | if (ppid_rhs == p) { |
74 | 0 | const char *comm = comm_lhs + 1; |
75 | 0 | size_t commlen = comm_rhs - comm; |
76 | |
|
77 | 0 | strbuf_add(name, comm, commlen); |
78 | 0 | *statppid = ppid; |
79 | |
|
80 | 0 | return 0; |
81 | 0 | } |
82 | | |
83 | 0 | bad_kernel: |
84 | | /* |
85 | | * We were able to read our STAT_PARENT_PID_READ_N bytes from |
86 | | * /proc/%d/stat, but the content is bad. Broken kernel? |
87 | | * Should not happen, but handle it gracefully. |
88 | | */ |
89 | 0 | return -1; |
90 | 0 | } |
91 | | |
92 | | static int stat_parent_pid(pid_t pid, struct strbuf *name, int *statppid) |
93 | 0 | { |
94 | 0 | struct strbuf procfs_path = STRBUF_INIT; |
95 | 0 | struct strbuf sb = STRBUF_INIT; |
96 | 0 | FILE *fp; |
97 | 0 | int ret = -1; |
98 | | |
99 | | /* try to use procfs if it's present. */ |
100 | 0 | strbuf_addf(&procfs_path, "/proc/%d/stat", pid); |
101 | 0 | fp = fopen(procfs_path.buf, "r"); |
102 | 0 | if (!fp) |
103 | 0 | goto cleanup; |
104 | | |
105 | | /* |
106 | | * We could be more strict here and assert that we read at |
107 | | * least STAT_PARENT_PID_READ_N. My reading of procfs(5) is |
108 | | * that on any modern kernel (at least since 2.6.0 released in |
109 | | * 2003) even if all the mandatory numeric fields were zero'd |
110 | | * out we'd get at least 100 bytes, but let's just check that |
111 | | * we got anything at all and trust the parse_proc_stat() |
112 | | * function to handle its "Bad Kernel?" error checking. |
113 | | */ |
114 | 0 | if (!strbuf_fread(&sb, STAT_PARENT_PID_READ_N, fp)) |
115 | 0 | goto cleanup; |
116 | 0 | if (parse_proc_stat(&sb, name, statppid) < 0) |
117 | 0 | goto cleanup; |
118 | | |
119 | 0 | ret = 0; |
120 | 0 | cleanup: |
121 | 0 | if (fp) |
122 | 0 | fclose(fp); |
123 | 0 | strbuf_release(&procfs_path); |
124 | 0 | strbuf_release(&sb); |
125 | |
|
126 | 0 | return ret; |
127 | 0 | } |
128 | | |
129 | | static void push_ancestry_name(struct strvec *names, pid_t pid) |
130 | 0 | { |
131 | 0 | struct strbuf name = STRBUF_INIT; |
132 | 0 | int ppid; |
133 | |
|
134 | 0 | if (stat_parent_pid(pid, &name, &ppid) < 0) |
135 | 0 | goto cleanup; |
136 | | |
137 | 0 | strvec_push(names, name.buf); |
138 | | |
139 | | /* |
140 | | * Both errors and reaching the end of the process chain are |
141 | | * reported as fields of 0 by proc(5) |
142 | | */ |
143 | 0 | if (ppid) |
144 | 0 | push_ancestry_name(names, ppid); |
145 | 0 | cleanup: |
146 | 0 | strbuf_release(&name); |
147 | |
|
148 | 0 | return; |
149 | 0 | } |
150 | | |
151 | | void trace2_collect_process_info(enum trace2_process_info_reason reason) |
152 | 0 | { |
153 | 0 | struct strvec names = STRVEC_INIT; |
154 | |
|
155 | 0 | if (!trace2_is_enabled()) |
156 | 0 | return; |
157 | | |
158 | 0 | switch (reason) { |
159 | 0 | case TRACE2_PROCESS_INFO_EXIT: |
160 | | /* |
161 | | * The Windows version of this calls its |
162 | | * get_peak_memory_info() here. We may want to insert |
163 | | * similar process-end statistics here in the future. |
164 | | */ |
165 | 0 | break; |
166 | 0 | case TRACE2_PROCESS_INFO_STARTUP: |
167 | 0 | push_ancestry_name(&names, getppid()); |
168 | |
|
169 | 0 | if (names.nr) |
170 | 0 | trace2_cmd_ancestry(names.v); |
171 | 0 | strvec_clear(&names); |
172 | 0 | break; |
173 | 0 | } |
174 | | |
175 | 0 | return; |
176 | 0 | } |