New state: STATE_DISK_SLEEP
[debugger.git] / threadtest.c
1 /* Copyright 2007, Red Hat Inc.  */
2
3 #define _GNU_SOURCE 1
4
5 #include <unistd.h>
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <signal.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <sys/ptrace.h>
12 #include <sys/types.h>
13 #include <sys/wait.h>
14 #include <limits.h>
15 #include <string.h>
16 #include <execinfo.h>
17 #include <thread_db.h>
18 #include <link.h>
19 #include <elfutils/libdwfl.h>
20 #include <fcntl.h>
21 #include <sys/reg.h>
22 #include <linux/ptrace.h>
23 #include <asm/prctl.h>
24
25 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
26
27 static __attribute__((__noreturn__)) void crash (void)
28 {
29 #if 0
30   void *array[0x100];
31   int count;
32 #endif
33   char command[256];
34
35   fputs (">>> CRASH START\n", stderr);
36 #if 0
37   count = backtrace (array, ARRAY_SIZE (array));
38   backtrace_symbols_fd (array, count, STDERR_FILENO);
39 #endif
40   snprintf (command, sizeof (command), "echo -e \"bt\\nquit\""
41             " >/tmp/debugger.%d; gdb --batch -nx --command=/tmp/debugger.%d"
42             " /proc/%d/exe %d </dev/null;rm -f /tmp/debugger.%d",
43             (int) getpid(), (int) getpid(), (int) getpid(), (int) getpid(),
44             (int) getpid());
45   system (command);
46   fputs (">>> CRASH FINISH\n", stderr);
47   abort ();
48 }
49
50 struct ps_prochandle
51   {
52     pid_t pid;
53     Dwfl *dwfl;
54   };
55
56 static Dwfl *get_dwfl (struct ps_prochandle *proc_handle)
57 {
58   static char *debuginfo_path;
59
60   static const Dwfl_Callbacks proc_callbacks =
61    {
62      .find_debuginfo = dwfl_standard_find_debuginfo,
63      .debuginfo_path = &debuginfo_path,
64
65      .find_elf = dwfl_linux_proc_find_elf,
66    };
67
68   if (proc_handle->dwfl == NULL)
69     {
70       proc_handle->dwfl = dwfl_begin (&proc_callbacks);
71       assert (proc_handle->dwfl != NULL);
72
73       errno = 0;
74       if (dwfl_linux_proc_report (proc_handle->dwfl, proc_handle->pid) != 0
75          || dwfl_report_end (proc_handle->dwfl, NULL, NULL) != 0)
76         {
77           fprintf (stderr, "dwfl reporting: %m\n");
78           dwfl_end (proc_handle->dwfl);
79           proc_handle->dwfl = NULL;
80           abort ();
81         }
82     }
83   return proc_handle->dwfl;
84 }
85
86 /* Functions in this interface return one of these status codes.  */
87 typedef enum
88 {
89   PS_OK,                /* Generic "call succeeded".  */
90   PS_ERR,               /* Generic error. */
91   PS_BADPID,            /* Bad process handle.  */
92   PS_BADLID,            /* Bad LWP identifier.  */ 
93   PS_BADADDR,           /* Bad address.  */
94   PS_NOSYM,             /* Could not find given symbol.  */
95   PS_NOFREGS            /* FPU register set not available for given LWP.  */
96 } ps_err_e;
97
98 ps_err_e ps_pdread (struct ps_prochandle *proc_handle, psaddr_t addr,
99                     void *buffer, size_t length)
100 {
101   pid_t pid = proc_handle->pid;
102   char filename[64];
103   int fd;
104
105   if (pid == getpid ())
106     {
107       memcpy (buffer, addr, length);
108       return PS_OK;
109     }
110
111   if (ptrace (PTRACE_ATTACH, pid, NULL, NULL) != 0)
112     abort ();
113   if (waitpid (pid, NULL, 0) != pid)
114     abort ();
115
116   snprintf (filename, sizeof (filename), "/proc/%ld/mem", (long) pid);
117   fd = open (filename, O_RDONLY);
118   assert (fd != -1);
119   if (lseek64 (fd, (off64_t) addr, SEEK_SET) != (off64_t) addr)
120     abort ();
121   if (read (fd, buffer, length) != length)
122     {
123       fprintf (stderr, "read() error @0x%lx length %lu: %m\n",
124                (unsigned long) addr, (unsigned long) length);
125       abort ();
126     }
127   if (close (fd) != 0)
128     abort ();
129
130   if (ptrace (PTRACE_DETACH, pid, NULL, NULL) != 0)
131     abort ();
132
133   return PS_OK;
134 }
135
136 ps_err_e ps_pdwrite (struct ps_prochandle *proc_handle, psaddr_t addr,
137                      const void *buffer, size_t length)
138 {
139   crash ();
140 }
141
142 ps_err_e ps_lgetregs (struct ps_prochandle *proc_handle, lwpid_t lwp,
143                       prgregset_t regs)
144 {
145   crash ();
146 }
147
148 ps_err_e ps_lsetregs (struct ps_prochandle *proc_handle, lwpid_t lwp,
149                       const prgregset_t regs)
150 {
151   crash ();
152 }
153
154 ps_err_e ps_lgetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp,
155                         prfpregset_t *fpregs)
156 {
157   crash ();
158 }
159
160 ps_err_e ps_lsetfpregs (struct ps_prochandle *proc_handle, lwpid_t lwp,
161                         const prfpregset_t *fpregs)
162 {
163   crash ();
164 }
165
166 /* Return the PID of the process.  */
167 pid_t ps_getpid (struct ps_prochandle *proc_handle)
168 {
169   return proc_handle->pid;
170 }
171
172 struct getmodules_callback_arg
173   {
174     const char *sym_name;
175     psaddr_t *sym_addr;
176     ps_err_e retval;
177   };
178
179 static int getmodules_callback (Dwfl_Module *module,
180                                 void **module_userdata_pointer,
181                                 const char *module_name,
182                                 Dwarf_Addr module_low_addr, void *arg)
183 {
184   struct getmodules_callback_arg *getmodules_callback_arg = arg;
185   int sym_count, ndx;
186   GElf_Sym sym;
187
188   sym_count = dwfl_module_getsymtab (module);
189   assert (sym_count >= 0);
190   for (ndx = 0; ndx < sym_count; ndx++)
191     {
192       const char *name_got;
193
194       name_got = dwfl_module_getsym (module, ndx, &sym,
195                                      NULL);
196       assert (name_got != NULL);
197       if (strcmp (name_got, getmodules_callback_arg->sym_name) == 0)
198         break;
199     }
200   if (ndx == sym_count)
201     return DWARF_CB_OK;
202
203   *getmodules_callback_arg->sym_addr = (psaddr_t) sym.st_value;
204   getmodules_callback_arg->retval = PS_OK;
205
206   return DWARF_CB_OK;
207 }
208
209 /* Look up the named symbol in the named DSO in the symbol tables
210    associated with the process being debugged, filling in *SYM_ADDR
211    with the corresponding run-time address.  */
212 ps_err_e ps_pglobal_lookup (struct ps_prochandle *proc_handle,
213                             const char *object_name, const char *sym_name,
214                             psaddr_t *sym_addr)
215 {
216   Dwfl *dwfl = get_dwfl (proc_handle);
217   struct getmodules_callback_arg getmodules_callback_arg;
218   ptrdiff_t err_ptrdiff;
219
220   /* FIXME: `object_name' ignored due to missing unresolving of shared
221      libraries symbolic links.  */
222   getmodules_callback_arg.sym_name = sym_name;
223   getmodules_callback_arg.sym_addr = sym_addr;
224   getmodules_callback_arg.retval = PS_NOSYM;
225   err_ptrdiff = dwfl_getmodules (dwfl, getmodules_callback,
226                                  &getmodules_callback_arg, 0);
227   assert (err_ptrdiff == 0);
228   return getmodules_callback_arg.retval;
229 }
230
231 #ifdef __x86_64__
232 ps_err_e ps_get_thread_area (const struct ps_prochandle *ph, lwpid_t lwpid,
233                              int idx, void **base)
234 {
235   long val;
236
237   assert (idx == FS || idx == GS);
238
239   if (ptrace (PTRACE_ATTACH, lwpid, NULL, NULL) != 0)
240     abort ();
241   if (waitpid (lwpid, NULL, 0) != lwpid)
242     abort ();
243
244   val = ptrace (PTRACE_ARCH_PRCTL, lwpid, base, (idx == FS ? ARCH_GET_FS
245                                                            : ARCH_GET_GS));
246
247   if (ptrace (PTRACE_DETACH, lwpid, NULL, NULL) != 0)
248     abort ();
249
250   if (val != 0)
251     {
252       fprintf (stderr, "PTRACE_ARCH_PRCTL (%s): %m\n", (idx == FS ? "FS"
253                                                                   : "GS"));
254       return PS_ERR;
255     }
256
257   return PS_OK;
258 }
259 #else
260 #error "Unsupported ps_get_thread_area ()!"
261 #endif
262
263 static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
264 {
265   td_err_e err;
266   td_thrinfo_t info;
267
268   err = td_thr_get_info (th_p, &info);
269   assert (err == TD_OK);
270   printf ("LWP = %ld TID = 0x%lx\n", (long) info.ti_lid, info.ti_tid);
271
272   return 0;
273 }
274
275 static void thread_test (td_thragent_t *thread_agent)
276 {
277   td_err_e err;
278
279   err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL,
280                         TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
281                         TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
282   if (err != TD_OK)
283     {
284       fprintf (stderr, "err = %d\n", err);
285       abort ();
286     }
287 }
288
289 static void attach (pid_t pid)
290 {
291   td_err_e err;
292   td_thragent_t *thread_agent;
293   struct ps_prochandle proc_handle_local;
294
295   err = td_init ();
296   assert (err == TD_OK);
297
298   proc_handle_local.pid = pid;
299   proc_handle_local.dwfl = NULL;
300   err = td_ta_new (&proc_handle_local, &thread_agent);
301   assert (err == TD_OK || err == TD_NOLIBTHREAD);
302   if (err == TD_NOLIBTHREAD)
303     puts ("singlethreaded");
304   else
305     {
306       puts ("multithreaded");
307       thread_test (thread_agent);
308       err = td_ta_delete (thread_agent);
309       assert (err == TD_OK);
310     }
311   if (proc_handle_local.dwfl != NULL);
312     dwfl_end (proc_handle_local.dwfl);
313 }
314
315 static void *start (void *arg)
316 {
317   sleep (1);
318
319   return arg;
320 }
321
322 static __attribute__((__noreturn__)) void syntax (void)
323 {
324   puts ("threadtest {<pid>|multi|single}");
325   exit (EXIT_FAILURE);
326 }
327
328 int main (int argc, char **argv)
329 {
330   if (argc != 2)
331     syntax ();
332   if (strcmp (argv[1], "single") == 0)
333     attach (getpid ());
334   else if (strcmp (argv[1], "multi") == 0)
335     {
336       void *handle;
337       int (*pthread_create_pointer) (pthread_t *thread,
338                                      const pthread_attr_t *attr,
339                                      void *(*start_routine) (void *),
340                                      void *arg);
341       pthread_t thread;
342       int i;
343
344       /* Do not access pthread_create () directly as we would need to link
345          `-lpthread' and we could never provide the `single' option.  */
346       handle = dlopen ("libpthread.so.0", RTLD_LAZY);
347       assert (handle != NULL);
348       pthread_create_pointer = dlsym (handle, "pthread_create");
349       assert (pthread_create_pointer != NULL);
350
351       i = (*pthread_create_pointer) (&thread, NULL, start, NULL);
352       assert (i == 0);
353       i = (*pthread_create_pointer) (&thread, NULL, start, NULL);
354       assert (i == 0);
355       
356       attach (getpid ());
357     }
358   else
359     {
360       char *endptr;
361       long pidl = strtol (argv[1], &endptr, 0);
362
363       if (pidl < 0 || (pid_t) pidl != pidl || (endptr != NULL && *endptr != 0))
364         syntax ();
365       attach (pidl);
366     }
367
368   return EXIT_SUCCESS;
369 }