Commit | Line | Data |
---|---|---|
13da1c97 | 1 | /* Linux-specific PROCFS manipulation routines. |
32d0add0 | 2 | Copyright (C) 2009-2015 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 | ||
727605ca | 19 | #include "common-defs.h" |
13da1c97 | 20 | #include "linux-procfs.h" |
614c279d | 21 | #include "filestuff.h" |
8784d563 | 22 | #include <dirent.h> |
13da1c97 LM |
23 | |
24 | /* Return the TGID of LWPID from /proc/pid/status. Returns -1 if not | |
25 | found. */ | |
26 | ||
87b0bb13 | 27 | static int |
8784d563 | 28 | linux_proc_get_int (pid_t lwpid, const char *field, int warn) |
13da1c97 | 29 | { |
87b0bb13 | 30 | size_t field_len = strlen (field); |
13da1c97 LM |
31 | FILE *status_file; |
32 | char buf[100]; | |
87b0bb13 | 33 | int retval = -1; |
13da1c97 LM |
34 | |
35 | snprintf (buf, sizeof (buf), "/proc/%d/status", (int) lwpid); | |
614c279d | 36 | status_file = gdb_fopen_cloexec (buf, "r"); |
87b0bb13 | 37 | if (status_file == NULL) |
13da1c97 | 38 | { |
8784d563 PA |
39 | if (warn) |
40 | warning (_("unable to open /proc file '%s'"), buf); | |
87b0bb13 | 41 | return -1; |
13da1c97 LM |
42 | } |
43 | ||
87b0bb13 JK |
44 | while (fgets (buf, sizeof (buf), status_file)) |
45 | if (strncmp (buf, field, field_len) == 0 && buf[field_len] == ':') | |
46 | { | |
47 | retval = strtol (&buf[field_len + 1], NULL, 10); | |
48 | break; | |
49 | } | |
50 | ||
51 | fclose (status_file); | |
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 | |
8784d563 PA |
72 | /* Fill in BUFFER, a buffer with BUFFER_SIZE bytes with the 'State' |
73 | line of /proc/PID/status. Returns -1 on failure to open the /proc | |
74 | file, 1 if the line is found, and 0 if not found. If WARN, warn on | |
75 | failure to open the /proc file. */ | |
5f572dec | 76 | |
87b0bb13 | 77 | static int |
8784d563 PA |
78 | linux_proc_pid_get_state (pid_t pid, char *buffer, size_t buffer_size, |
79 | int warn) | |
5f572dec | 80 | { |
5f572dec | 81 | FILE *procfile; |
5f572dec JK |
82 | int have_state; |
83 | ||
8784d563 | 84 | xsnprintf (buffer, buffer_size, "/proc/%d/status", (int) pid); |
614c279d | 85 | procfile = gdb_fopen_cloexec (buffer, "r"); |
5f572dec JK |
86 | if (procfile == NULL) |
87 | { | |
8784d563 PA |
88 | if (warn) |
89 | warning (_("unable to open /proc file '%s'"), buffer); | |
90 | return -1; | |
5f572dec JK |
91 | } |
92 | ||
93 | have_state = 0; | |
8784d563 | 94 | while (fgets (buffer, buffer_size, procfile) != NULL) |
5f572dec JK |
95 | if (strncmp (buffer, "State:", 6) == 0) |
96 | { | |
97 | have_state = 1; | |
98 | break; | |
99 | } | |
5f572dec | 100 | fclose (procfile); |
8784d563 PA |
101 | return have_state; |
102 | } | |
103 | ||
104 | /* See linux-procfs.h declaration. */ | |
105 | ||
106 | int | |
107 | linux_proc_pid_is_gone (pid_t pid) | |
108 | { | |
109 | char buffer[100]; | |
110 | int have_state; | |
111 | ||
112 | have_state = linux_proc_pid_get_state (pid, buffer, sizeof buffer, 0); | |
113 | if (have_state < 0) | |
114 | { | |
115 | /* If we can't open the status file, assume the thread has | |
116 | disappeared. */ | |
117 | return 1; | |
118 | } | |
119 | else if (have_state == 0) | |
120 | { | |
121 | /* No "State:" line, assume thread is alive. */ | |
122 | return 0; | |
123 | } | |
124 | else | |
125 | { | |
126 | return (strstr (buffer, "Z (") != NULL | |
127 | || strstr (buffer, "X (") != NULL); | |
128 | } | |
129 | } | |
130 | ||
131 | /* Return non-zero if 'State' of /proc/PID/status contains STATE. If | |
132 | WARN, warn on failure to open the /proc file. */ | |
133 | ||
134 | static int | |
135 | linux_proc_pid_has_state (pid_t pid, const char *state, int warn) | |
136 | { | |
137 | char buffer[100]; | |
138 | int have_state; | |
139 | ||
140 | have_state = linux_proc_pid_get_state (pid, buffer, sizeof buffer, warn); | |
141 | return (have_state > 0 && strstr (buffer, state) != NULL); | |
5f572dec | 142 | } |
87b0bb13 JK |
143 | |
144 | /* Detect `T (stopped)' in `/proc/PID/status'. | |
145 | Other states including `T (tracing stop)' are reported as false. */ | |
146 | ||
147 | int | |
148 | linux_proc_pid_is_stopped (pid_t pid) | |
149 | { | |
8784d563 PA |
150 | return linux_proc_pid_has_state (pid, "T (stopped)", 1); |
151 | } | |
152 | ||
153 | /* Return non-zero if PID is a zombie. If WARN, warn on failure to | |
154 | open the /proc file. */ | |
155 | ||
156 | static int | |
157 | linux_proc_pid_is_zombie_maybe_warn (pid_t pid, int warn) | |
158 | { | |
159 | return linux_proc_pid_has_state (pid, "Z (zombie)", warn); | |
160 | } | |
161 | ||
162 | /* See linux-procfs.h declaration. */ | |
163 | ||
164 | int | |
165 | linux_proc_pid_is_zombie_nowarn (pid_t pid) | |
166 | { | |
167 | return linux_proc_pid_is_zombie_maybe_warn (pid, 0); | |
87b0bb13 JK |
168 | } |
169 | ||
170 | /* See linux-procfs.h declaration. */ | |
171 | ||
172 | int | |
173 | linux_proc_pid_is_zombie (pid_t pid) | |
174 | { | |
8784d563 | 175 | return linux_proc_pid_is_zombie_maybe_warn (pid, 1); |
87b0bb13 | 176 | } |
015de688 DC |
177 | |
178 | /* See linux-procfs.h declaration. */ | |
179 | ||
180 | char * | |
181 | linux_proc_pid_get_ns (pid_t pid, const char *ns) | |
182 | { | |
183 | char buf[100]; | |
184 | char nsval[64]; | |
185 | int ret; | |
186 | xsnprintf (buf, sizeof (buf), "/proc/%d/ns/%s", (int) pid, ns); | |
187 | ret = readlink (buf, nsval, sizeof (nsval)); | |
188 | if (0 < ret && ret < sizeof (nsval)) | |
189 | { | |
190 | nsval[ret] = '\0'; | |
191 | return xstrdup (nsval); | |
192 | } | |
193 | ||
194 | return NULL; | |
195 | } | |
8784d563 PA |
196 | |
197 | /* See linux-procfs.h. */ | |
198 | ||
199 | void | |
200 | linux_proc_attach_tgid_threads (pid_t pid, | |
201 | linux_proc_attach_lwp_func attach_lwp) | |
202 | { | |
203 | DIR *dir; | |
204 | char pathname[128]; | |
205 | int new_threads_found; | |
206 | int iterations; | |
207 | ||
208 | if (linux_proc_get_tgid (pid) != pid) | |
209 | return; | |
210 | ||
211 | xsnprintf (pathname, sizeof (pathname), "/proc/%ld/task", (long) pid); | |
212 | dir = opendir (pathname); | |
213 | if (dir == NULL) | |
214 | { | |
215 | warning (_("Could not open /proc/%ld/task.\n"), (long) pid); | |
216 | return; | |
217 | } | |
218 | ||
219 | /* Scan the task list for existing threads. While we go through the | |
220 | threads, new threads may be spawned. Cycle through the list of | |
221 | threads until we have done two iterations without finding new | |
222 | threads. */ | |
223 | for (iterations = 0; iterations < 2; iterations++) | |
224 | { | |
225 | struct dirent *dp; | |
226 | ||
227 | new_threads_found = 0; | |
228 | while ((dp = readdir (dir)) != NULL) | |
229 | { | |
230 | unsigned long lwp; | |
231 | ||
232 | /* Fetch one lwp. */ | |
233 | lwp = strtoul (dp->d_name, NULL, 10); | |
234 | if (lwp != 0) | |
235 | { | |
236 | ptid_t ptid = ptid_build (pid, lwp, 0); | |
237 | ||
238 | if (attach_lwp (ptid)) | |
239 | new_threads_found = 1; | |
240 | } | |
241 | } | |
242 | ||
243 | if (new_threads_found) | |
244 | { | |
245 | /* Start over. */ | |
246 | iterations = -1; | |
247 | } | |
248 | ||
249 | rewinddir (dir); | |
250 | } | |
251 | ||
252 | closedir (dir); | |
253 | } |