]>
Commit | Line | Data |
---|---|---|
13da1c97 | 1 | /* Linux-specific PROCFS manipulation routines. |
1d506c26 | 2 | Copyright (C) 2009-2024 Free Software Foundation, Inc. |
13da1c97 LM |
3 | |
4 | This file is part of GDB. | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
18 | ||
13da1c97 | 19 | #include "linux-procfs.h" |
268a13a5 | 20 | #include "gdbsupport/filestuff.h" |
8784d563 | 21 | #include <dirent.h> |
2db9a427 | 22 | #include <sys/stat.h> |
c930a077 TJB |
23 | #include <unordered_set> |
24 | #include <utility> | |
13da1c97 LM |
25 | |
26 | /* Return the TGID of LWPID from /proc/pid/status. Returns -1 if not | |
27 | found. */ | |
28 | ||
87b0bb13 | 29 | static int |
8784d563 | 30 | linux_proc_get_int (pid_t lwpid, const char *field, int warn) |
13da1c97 | 31 | { |
87b0bb13 | 32 | size_t field_len = strlen (field); |
13da1c97 | 33 | char buf[100]; |
87b0bb13 | 34 | int retval = -1; |
13da1c97 LM |
35 | |
36 | snprintf (buf, sizeof (buf), "/proc/%d/status", (int) lwpid); | |
d419f42d | 37 | gdb_file_up status_file = gdb_fopen_cloexec (buf, "r"); |
87b0bb13 | 38 | if (status_file == NULL) |
13da1c97 | 39 | { |
8784d563 PA |
40 | if (warn) |
41 | warning (_("unable to open /proc file '%s'"), buf); | |
87b0bb13 | 42 | return -1; |
13da1c97 LM |
43 | } |
44 | ||
d419f42d | 45 | while (fgets (buf, sizeof (buf), status_file.get ())) |
87b0bb13 JK |
46 | if (strncmp (buf, field, field_len) == 0 && buf[field_len] == ':') |
47 | { | |
48 | retval = strtol (&buf[field_len + 1], NULL, 10); | |
49 | break; | |
50 | } | |
51 | ||
87b0bb13 | 52 | return retval; |
13da1c97 | 53 | } |
644cebc9 | 54 | |
87b0bb13 JK |
55 | /* Return the TGID of LWPID from /proc/pid/status. Returns -1 if not |
56 | found. */ | |
644cebc9 PA |
57 | |
58 | int | |
87b0bb13 | 59 | linux_proc_get_tgid (pid_t lwpid) |
644cebc9 | 60 | { |
8784d563 | 61 | return linux_proc_get_int (lwpid, "Tgid", 1); |
87b0bb13 | 62 | } |
644cebc9 | 63 | |
87b0bb13 JK |
64 | /* See linux-procfs.h. */ |
65 | ||
66 | pid_t | |
8784d563 | 67 | linux_proc_get_tracerpid_nowarn (pid_t lwpid) |
87b0bb13 | 68 | { |
8784d563 | 69 | return linux_proc_get_int (lwpid, "TracerPid", 0); |
644cebc9 | 70 | } |
5f572dec | 71 | |
d617208b PA |
72 | /* Process states as discovered in the 'State' line of |
73 | /proc/PID/status. Not all possible states are represented here, | |
74 | only those that we care about. */ | |
75 | ||
76 | enum proc_state | |
77 | { | |
78 | /* Some state we don't handle. */ | |
79 | PROC_STATE_UNKNOWN, | |
80 | ||
81 | /* Stopped on a signal. */ | |
82 | PROC_STATE_STOPPED, | |
83 | ||
84 | /* Tracing stop. */ | |
85 | PROC_STATE_TRACING_STOP, | |
86 | ||
87 | /* Dead. */ | |
88 | PROC_STATE_DEAD, | |
89 | ||
90 | /* Zombie. */ | |
91 | PROC_STATE_ZOMBIE, | |
92 | }; | |
93 | ||
94 | /* Parse a PROC_STATE out of STATE, a buffer with the state found in | |
95 | the 'State:' line of /proc/PID/status. */ | |
96 | ||
97 | static enum proc_state | |
98 | parse_proc_status_state (const char *state) | |
99 | { | |
f1735a53 | 100 | state = skip_spaces (state); |
d617208b PA |
101 | |
102 | switch (state[0]) | |
103 | { | |
0e1a6a51 PA |
104 | case 't': |
105 | return PROC_STATE_TRACING_STOP; | |
d617208b | 106 | case 'T': |
0e1a6a51 | 107 | /* Before Linux 2.6.33, tracing stop used uppercase T. */ |
5c811d30 | 108 | if (strcmp (state, "T (stopped)\n") == 0) |
d617208b | 109 | return PROC_STATE_STOPPED; |
5c811d30 JK |
110 | else /* "T (tracing stop)\n" */ |
111 | return PROC_STATE_TRACING_STOP; | |
d617208b PA |
112 | case 'X': |
113 | return PROC_STATE_DEAD; | |
114 | case 'Z': | |
115 | return PROC_STATE_ZOMBIE; | |
116 | } | |
117 | ||
118 | return PROC_STATE_UNKNOWN; | |
119 | } | |
120 | ||
121 | ||
122 | /* Fill in STATE, a buffer with BUFFER_SIZE bytes with the 'State' | |
8784d563 PA |
123 | line of /proc/PID/status. Returns -1 on failure to open the /proc |
124 | file, 1 if the line is found, and 0 if not found. If WARN, warn on | |
125 | failure to open the /proc file. */ | |
5f572dec | 126 | |
87b0bb13 | 127 | static int |
d617208b | 128 | linux_proc_pid_get_state (pid_t pid, int warn, enum proc_state *state) |
5f572dec | 129 | { |
5f572dec | 130 | int have_state; |
d617208b | 131 | char buffer[100]; |
5f572dec | 132 | |
d617208b | 133 | xsnprintf (buffer, sizeof (buffer), "/proc/%d/status", (int) pid); |
d419f42d | 134 | gdb_file_up procfile = gdb_fopen_cloexec (buffer, "r"); |
5f572dec JK |
135 | if (procfile == NULL) |
136 | { | |
8784d563 PA |
137 | if (warn) |
138 | warning (_("unable to open /proc file '%s'"), buffer); | |
139 | return -1; | |
5f572dec JK |
140 | } |
141 | ||
142 | have_state = 0; | |
d419f42d | 143 | while (fgets (buffer, sizeof (buffer), procfile.get ()) != NULL) |
61012eef | 144 | if (startswith (buffer, "State:")) |
5f572dec JK |
145 | { |
146 | have_state = 1; | |
d617208b | 147 | *state = parse_proc_status_state (buffer + sizeof ("State:") - 1); |
5f572dec JK |
148 | break; |
149 | } | |
8784d563 PA |
150 | return have_state; |
151 | } | |
152 | ||
153 | /* See linux-procfs.h declaration. */ | |
154 | ||
155 | int | |
156 | linux_proc_pid_is_gone (pid_t pid) | |
157 | { | |
8784d563 | 158 | int have_state; |
d617208b | 159 | enum proc_state state; |
8784d563 | 160 | |
d617208b | 161 | have_state = linux_proc_pid_get_state (pid, 0, &state); |
8784d563 PA |
162 | if (have_state < 0) |
163 | { | |
164 | /* If we can't open the status file, assume the thread has | |
165 | disappeared. */ | |
166 | return 1; | |
167 | } | |
168 | else if (have_state == 0) | |
169 | { | |
170 | /* No "State:" line, assume thread is alive. */ | |
171 | return 0; | |
172 | } | |
173 | else | |
d617208b | 174 | return (state == PROC_STATE_ZOMBIE || state == PROC_STATE_DEAD); |
8784d563 PA |
175 | } |
176 | ||
177 | /* Return non-zero if 'State' of /proc/PID/status contains STATE. If | |
178 | WARN, warn on failure to open the /proc file. */ | |
179 | ||
180 | static int | |
d617208b | 181 | linux_proc_pid_has_state (pid_t pid, enum proc_state state, int warn) |
8784d563 | 182 | { |
8784d563 | 183 | int have_state; |
d617208b | 184 | enum proc_state cur_state; |
8784d563 | 185 | |
d617208b PA |
186 | have_state = linux_proc_pid_get_state (pid, warn, &cur_state); |
187 | return (have_state > 0 && cur_state == state); | |
5f572dec | 188 | } |
87b0bb13 JK |
189 | |
190 | /* Detect `T (stopped)' in `/proc/PID/status'. | |
191 | Other states including `T (tracing stop)' are reported as false. */ | |
192 | ||
193 | int | |
194 | linux_proc_pid_is_stopped (pid_t pid) | |
195 | { | |
d617208b | 196 | return linux_proc_pid_has_state (pid, PROC_STATE_STOPPED, 1); |
8784d563 PA |
197 | } |
198 | ||
d617208b | 199 | /* Detect `t (tracing stop)' in `/proc/PID/status'. |
23f238d3 PA |
200 | Other states including `T (stopped)' are reported as false. */ |
201 | ||
202 | int | |
203 | linux_proc_pid_is_trace_stopped_nowarn (pid_t pid) | |
204 | { | |
d617208b | 205 | return linux_proc_pid_has_state (pid, PROC_STATE_TRACING_STOP, 1); |
23f238d3 PA |
206 | } |
207 | ||
8784d563 PA |
208 | /* Return non-zero if PID is a zombie. If WARN, warn on failure to |
209 | open the /proc file. */ | |
210 | ||
211 | static int | |
212 | linux_proc_pid_is_zombie_maybe_warn (pid_t pid, int warn) | |
213 | { | |
d617208b | 214 | return linux_proc_pid_has_state (pid, PROC_STATE_ZOMBIE, warn); |
8784d563 PA |
215 | } |
216 | ||
217 | /* See linux-procfs.h declaration. */ | |
218 | ||
219 | int | |
220 | linux_proc_pid_is_zombie_nowarn (pid_t pid) | |
221 | { | |
222 | return linux_proc_pid_is_zombie_maybe_warn (pid, 0); | |
87b0bb13 JK |
223 | } |
224 | ||
225 | /* See linux-procfs.h declaration. */ | |
226 | ||
227 | int | |
228 | linux_proc_pid_is_zombie (pid_t pid) | |
229 | { | |
8784d563 | 230 | return linux_proc_pid_is_zombie_maybe_warn (pid, 1); |
87b0bb13 | 231 | } |
015de688 | 232 | |
8784d563 PA |
233 | /* See linux-procfs.h. */ |
234 | ||
16a447be TJB |
235 | std::optional<std::string> |
236 | linux_proc_get_stat_field (ptid_t ptid, int field) | |
237 | { | |
238 | /* We never need to read PID from the stat file, and there's | |
239 | command_from_pid to read the comm field. */ | |
240 | gdb_assert (field >= LINUX_PROC_STAT_STATE); | |
241 | ||
242 | std::string filename = string_printf ("/proc/%ld/task/%ld/stat", | |
243 | (long) ptid.pid (), (long) ptid.lwp ()); | |
244 | ||
245 | std::optional<std::string> content | |
246 | = read_text_file_to_string (filename.c_str ()); | |
247 | if (!content.has_value ()) | |
248 | return {}; | |
249 | ||
250 | /* ps command also relies on no trailing fields ever containing ')'. */ | |
251 | std::string::size_type pos = content->find_last_of (')'); | |
252 | if (pos == std::string::npos) | |
253 | return {}; | |
254 | ||
255 | /* The first field after program name is LINUX_PROC_STAT_STATE. */ | |
256 | for (int i = LINUX_PROC_STAT_STATE; i <= field; ++i) | |
257 | { | |
258 | /* Find separator. */ | |
259 | pos = content->find_first_of (' ', pos); | |
260 | if (pos == std::string::npos) | |
261 | return {}; | |
262 | ||
263 | /* Find beginning of field. */ | |
264 | pos = content->find_first_not_of (' ', pos); | |
265 | if (pos == std::string::npos) | |
266 | return {}; | |
267 | } | |
268 | ||
269 | /* Find end of field. */ | |
270 | std::string::size_type end_pos = content->find_first_of (' ', pos); | |
271 | if (end_pos == std::string::npos) | |
272 | return content->substr (pos); | |
273 | else | |
274 | return content->substr (pos, end_pos - pos); | |
275 | } | |
276 | ||
c930a077 TJB |
277 | /* Get the start time of thread PTID. */ |
278 | ||
279 | static std::optional<ULONGEST> | |
280 | linux_proc_get_starttime (ptid_t ptid) | |
281 | { | |
282 | std::optional<std::string> field | |
283 | = linux_proc_get_stat_field (ptid, LINUX_PROC_STAT_STARTTIME); | |
284 | ||
285 | if (!field.has_value ()) | |
286 | return {}; | |
287 | ||
288 | errno = 0; | |
289 | const char *trailer; | |
290 | ULONGEST starttime = strtoulst (field->c_str (), &trailer, 10); | |
291 | if (starttime == ULONGEST_MAX && errno == ERANGE) | |
292 | return {}; | |
293 | else if (*trailer != '\0') | |
294 | /* There were unexpected characters. */ | |
295 | return {}; | |
296 | ||
297 | return starttime; | |
298 | } | |
299 | ||
16a447be TJB |
300 | /* See linux-procfs.h. */ |
301 | ||
79efa585 SM |
302 | const char * |
303 | linux_proc_tid_get_name (ptid_t ptid) | |
304 | { | |
305 | #define TASK_COMM_LEN 16 /* As defined in the kernel's sched.h. */ | |
306 | ||
307 | static char comm_buf[TASK_COMM_LEN]; | |
308 | char comm_path[100]; | |
79efa585 | 309 | const char *comm_val; |
e99b03dc | 310 | pid_t pid = ptid.pid (); |
15a9e13e | 311 | pid_t tid = ptid.lwp_p () ? ptid.lwp () : ptid.pid (); |
79efa585 SM |
312 | |
313 | xsnprintf (comm_path, sizeof (comm_path), | |
314 | "/proc/%ld/task/%ld/comm", (long) pid, (long) tid); | |
315 | ||
d419f42d | 316 | gdb_file_up comm_file = gdb_fopen_cloexec (comm_path, "r"); |
79efa585 SM |
317 | if (comm_file == NULL) |
318 | return NULL; | |
319 | ||
d419f42d | 320 | comm_val = fgets (comm_buf, sizeof (comm_buf), comm_file.get ()); |
79efa585 SM |
321 | |
322 | if (comm_val != NULL) | |
323 | { | |
324 | int i; | |
325 | ||
326 | /* Make sure there is no newline at the end. */ | |
327 | for (i = 0; i < sizeof (comm_buf); i++) | |
328 | { | |
329 | if (comm_buf[i] == '\n') | |
330 | { | |
331 | comm_buf[i] = '\0'; | |
332 | break; | |
333 | } | |
334 | } | |
335 | } | |
336 | ||
337 | return comm_val; | |
338 | } | |
339 | ||
340 | /* See linux-procfs.h. */ | |
341 | ||
8784d563 PA |
342 | void |
343 | linux_proc_attach_tgid_threads (pid_t pid, | |
344 | linux_proc_attach_lwp_func attach_lwp) | |
345 | { | |
8784d563 PA |
346 | char pathname[128]; |
347 | int new_threads_found; | |
348 | int iterations; | |
349 | ||
350 | if (linux_proc_get_tgid (pid) != pid) | |
351 | return; | |
352 | ||
353 | xsnprintf (pathname, sizeof (pathname), "/proc/%ld/task", (long) pid); | |
596cd22c | 354 | gdb_dir_up dir (opendir (pathname)); |
8784d563 PA |
355 | if (dir == NULL) |
356 | { | |
31141b55 | 357 | warning (_("Could not open %s."), pathname); |
8784d563 PA |
358 | return; |
359 | } | |
360 | ||
c930a077 TJB |
361 | /* Callable object to hash elements in visited_lpws. */ |
362 | struct pair_hash | |
363 | { | |
364 | std::size_t operator() (const std::pair<unsigned long, ULONGEST> &v) const | |
365 | { | |
366 | return (std::hash<unsigned long>() (v.first) | |
367 | ^ std::hash<ULONGEST>() (v.second)); | |
368 | } | |
369 | }; | |
370 | ||
371 | /* Keeps track of the LWPs we have already visited in /proc, | |
372 | identified by their PID and starttime to detect PID reuse. */ | |
373 | std::unordered_set<std::pair<unsigned long, ULONGEST>, | |
374 | pair_hash> visited_lwps; | |
375 | ||
8784d563 PA |
376 | /* Scan the task list for existing threads. While we go through the |
377 | threads, new threads may be spawned. Cycle through the list of | |
378 | threads until we have done two iterations without finding new | |
379 | threads. */ | |
380 | for (iterations = 0; iterations < 2; iterations++) | |
381 | { | |
382 | struct dirent *dp; | |
383 | ||
384 | new_threads_found = 0; | |
596cd22c | 385 | while ((dp = readdir (dir.get ())) != NULL) |
8784d563 PA |
386 | { |
387 | unsigned long lwp; | |
388 | ||
389 | /* Fetch one lwp. */ | |
390 | lwp = strtoul (dp->d_name, NULL, 10); | |
391 | if (lwp != 0) | |
392 | { | |
184ea2f7 | 393 | ptid_t ptid = ptid_t (pid, lwp); |
c930a077 TJB |
394 | std::optional<ULONGEST> starttime |
395 | = linux_proc_get_starttime (ptid); | |
396 | ||
397 | if (starttime.has_value ()) | |
398 | { | |
399 | std::pair<unsigned long, ULONGEST> key (lwp, *starttime); | |
400 | ||
401 | /* If we already visited this LWP, skip it this time. */ | |
402 | if (visited_lwps.find (key) != visited_lwps.cend ()) | |
403 | continue; | |
404 | ||
405 | visited_lwps.insert (key); | |
406 | } | |
8784d563 PA |
407 | |
408 | if (attach_lwp (ptid)) | |
409 | new_threads_found = 1; | |
410 | } | |
411 | } | |
412 | ||
413 | if (new_threads_found) | |
414 | { | |
415 | /* Start over. */ | |
416 | iterations = -1; | |
417 | } | |
418 | ||
596cd22c | 419 | rewinddir (dir.get ()); |
8784d563 | 420 | } |
8784d563 | 421 | } |
2db9a427 PA |
422 | |
423 | /* See linux-procfs.h. */ | |
424 | ||
425 | int | |
426 | linux_proc_task_list_dir_exists (pid_t pid) | |
427 | { | |
428 | char pathname[128]; | |
429 | struct stat buf; | |
430 | ||
431 | xsnprintf (pathname, sizeof (pathname), "/proc/%ld/task", (long) pid); | |
432 | return (stat (pathname, &buf) == 0); | |
433 | } | |
e0d86d2c GB |
434 | |
435 | /* See linux-procfs.h. */ | |
436 | ||
0e90c441 | 437 | const char * |
e0d86d2c GB |
438 | linux_proc_pid_to_exec_file (int pid) |
439 | { | |
440 | static char buf[PATH_MAX]; | |
441 | char name[PATH_MAX]; | |
442 | ssize_t len; | |
443 | ||
444 | xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid); | |
445 | len = readlink (name, buf, PATH_MAX - 1); | |
446 | if (len <= 0) | |
447 | strcpy (buf, name); | |
448 | else | |
449 | buf[len] = '\0'; | |
450 | ||
e58beedf AB |
451 | /* Use /proc/PID/exe if the actual file can't be read, but /proc/PID/exe |
452 | can be. */ | |
453 | if (access (buf, R_OK) != 0 && access (name, R_OK) == 0) | |
454 | strcpy (buf, name); | |
455 | ||
e0d86d2c GB |
456 | return buf; |
457 | } | |
1b919490 VB |
458 | |
459 | /* See linux-procfs.h. */ | |
460 | ||
461 | void | |
462 | linux_proc_init_warnings () | |
463 | { | |
464 | static bool warned = false; | |
465 | ||
466 | if (warned) | |
467 | return; | |
468 | warned = true; | |
469 | ||
470 | struct stat st; | |
471 | ||
472 | if (stat ("/proc/self", &st) != 0) | |
473 | warning (_("/proc is not accessible.")); | |
474 | } |