xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/nat/linux-procfs.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 /* Linux-specific PROCFS manipulation routines.
2    Copyright (C) 2009-2015 Free Software Foundation, Inc.
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 
19 #include "common-defs.h"
20 #include "linux-procfs.h"
21 #include "filestuff.h"
22 #include <dirent.h>
23 
24 /* Return the TGID of LWPID from /proc/pid/status.  Returns -1 if not
25    found.  */
26 
27 static int
28 linux_proc_get_int (pid_t lwpid, const char *field, int warn)
29 {
30   size_t field_len = strlen (field);
31   FILE *status_file;
32   char buf[100];
33   int retval = -1;
34 
35   snprintf (buf, sizeof (buf), "/proc/%d/status", (int) lwpid);
36   status_file = gdb_fopen_cloexec (buf, "r");
37   if (status_file == NULL)
38     {
39       if (warn)
40 	warning (_("unable to open /proc file '%s'"), buf);
41       return -1;
42     }
43 
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;
53 }
54 
55 /* Return the TGID of LWPID from /proc/pid/status.  Returns -1 if not
56    found.  */
57 
58 int
59 linux_proc_get_tgid (pid_t lwpid)
60 {
61   return linux_proc_get_int (lwpid, "Tgid", 1);
62 }
63 
64 /* See linux-procfs.h.  */
65 
66 pid_t
67 linux_proc_get_tracerpid_nowarn (pid_t lwpid)
68 {
69   return linux_proc_get_int (lwpid, "TracerPid", 0);
70 }
71 
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.  */
76 
77 static int
78 linux_proc_pid_get_state (pid_t pid, char *buffer, size_t buffer_size,
79 			  int warn)
80 {
81   FILE *procfile;
82   int have_state;
83 
84   xsnprintf (buffer, buffer_size, "/proc/%d/status", (int) pid);
85   procfile = gdb_fopen_cloexec (buffer, "r");
86   if (procfile == NULL)
87     {
88       if (warn)
89 	warning (_("unable to open /proc file '%s'"), buffer);
90       return -1;
91     }
92 
93   have_state = 0;
94   while (fgets (buffer, buffer_size, procfile) != NULL)
95     if (strncmp (buffer, "State:", 6) == 0)
96       {
97 	have_state = 1;
98 	break;
99       }
100   fclose (procfile);
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);
142 }
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 {
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);
168 }
169 
170 /* See linux-procfs.h declaration.  */
171 
172 int
173 linux_proc_pid_is_zombie (pid_t pid)
174 {
175   return linux_proc_pid_is_zombie_maybe_warn (pid, 1);
176 }
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 }
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."), (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 }
254