Pilot nptl_db application.
[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 *object_name;
175     Dwfl_Module *module_main;   /* first */
176     Dwfl_Module *module_libpthread;
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   const char *basename;
186
187   if (getmodules_callback_arg->module_main == NULL)
188     getmodules_callback_arg->module_main = module;
189
190   basename = strrchr (module_name, '/');
191   if (basename != NULL)
192     basename++;
193   else
194     basename = module_name;
195
196   /* FIXME */
197   if (strcmp (basename, "libpthread-2.5.so") == 0)
198     basename = "libpthread.so.0";
199
200   if (strcmp (basename, getmodules_callback_arg->object_name) != 0)
201     return DWARF_CB_OK;
202
203   getmodules_callback_arg->module_libpthread = module;
204   return DWARF_CB_ABORT;
205 }
206
207 /* Look up the named symbol in the named DSO in the symbol tables
208    associated with the process being debugged, filling in *SYM_ADDR
209    with the corresponding run-time address.  */
210 ps_err_e ps_pglobal_lookup (struct ps_prochandle *proc_handle,
211                             const char *object_name, const char *sym_name,
212                             psaddr_t *sym_addr)
213 {
214   Dwfl *dwfl = get_dwfl (proc_handle);
215   struct getmodules_callback_arg getmodules_callback_arg;
216   int sym_count, ndx;
217   GElf_Sym sym;
218   Dwfl_Module *module;
219
220   getmodules_callback_arg.object_name = object_name;
221   getmodules_callback_arg.module_main = NULL;
222   getmodules_callback_arg.module_libpthread = NULL;
223   dwfl_getmodules (dwfl, getmodules_callback, &getmodules_callback_arg, 0);
224   if (getmodules_callback_arg.module_libpthread != NULL)
225     module = getmodules_callback_arg.module_libpthread;
226   else if (getmodules_callback_arg.module_main != NULL)
227     module = getmodules_callback_arg.module_main;
228   else
229     return PS_NOSYM;
230
231   sym_count = dwfl_module_getsymtab (module);
232   assert (sym_count >= 0);
233   for (ndx = 0; ndx < sym_count; ndx++)
234     {
235       const char *name_got;
236
237       name_got = dwfl_module_getsym (module, ndx, &sym,
238                                      NULL);
239       assert (name_got != NULL);
240       if (strcmp (name_got, sym_name) == 0)
241         break;
242     }
243   if (ndx == sym_count)
244     return PS_NOSYM;
245
246   *sym_addr = (psaddr_t) sym.st_value;
247   return PS_OK;
248 }
249
250 #ifdef __x86_64__
251 ps_err_e ps_get_thread_area (const struct ps_prochandle *ph, lwpid_t lwpid,
252                              int idx, void **base)
253 {
254   long val;
255
256   assert (idx == FS || idx == GS);
257
258   if (ptrace (PTRACE_ATTACH, lwpid, NULL, NULL) != 0)
259     abort ();
260   if (waitpid (lwpid, NULL, 0) != lwpid)
261     abort ();
262
263   val = ptrace (PTRACE_ARCH_PRCTL, lwpid, base, (idx == FS ? ARCH_GET_FS
264                                                            : ARCH_GET_GS));
265
266   if (ptrace (PTRACE_DETACH, lwpid, NULL, NULL) != 0)
267     abort ();
268
269   if (val != 0)
270     {
271       fprintf (stderr, "PTRACE_ARCH_PRCTL (%s): %m\n", (idx == FS ? "FS"
272                                                                   : "GS"));
273       return PS_ERR;
274     }
275
276   return PS_OK;
277 }
278 #else
279 #error "Unsupported ps_get_thread_area ()!"
280 #endif
281
282 static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
283 {
284   td_err_e err;
285   td_thrinfo_t info;
286
287   err = td_thr_get_info (th_p, &info);
288   assert (err == TD_OK);
289   printf ("LWP = %ld TID = 0x%lx\n", (long) info.ti_lid, info.ti_tid);
290
291   return 0;
292 }
293
294 static void thread_test (td_thragent_t *thread_agent)
295 {
296   td_err_e err;
297
298   err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL,
299                         TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
300                         TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
301   assert (err == TD_OK);
302 }
303
304 static void attach (pid_t pid)
305 {
306   td_err_e err;
307   td_thragent_t *thread_agent;
308   struct ps_prochandle proc_handle_local;
309
310   err = td_init ();
311   assert (err == TD_OK);
312
313   proc_handle_local.pid = pid;
314   proc_handle_local.dwfl = NULL;
315   err = td_ta_new (&proc_handle_local, &thread_agent);
316   assert (err == TD_OK || err == TD_NOLIBTHREAD);
317   if (err == TD_OK)
318     puts ("multithreaded");
319   else
320     puts ("singlethreaded");
321   thread_test (thread_agent);
322 }
323
324 static void *start (void *arg)
325 {
326   sleep (1);
327
328   return arg;
329 }
330
331 static __attribute__((__noreturn__)) void syntax (void)
332 {
333   puts ("threadtest {<pid>|multi|single}");
334   exit (EXIT_FAILURE);
335 }
336
337 int main (int argc, char **argv)
338 {
339   if (argc != 2)
340     syntax ();
341   if (strcmp (argv[1], "single") == 0)
342     attach (getpid ());
343   else if (strcmp (argv[1], "multi") == 0)
344     {
345       void *handle;
346       int (*pthread_create_pointer) (pthread_t *thread,
347                                      const pthread_attr_t *attr,
348                                      void *(*start_routine) (void *),
349                                      void *arg);
350       pthread_t thread;
351       int i;
352
353       /* Do not access pthread_create () directly as we would need to link
354          `-lpthread' and we could never provide the `single' option.  */
355       handle = dlopen ("libpthread.so.0", RTLD_LAZY);
356       assert (handle != NULL);
357       pthread_create_pointer = dlsym (handle, "pthread_create");
358       assert (pthread_create_pointer != NULL);
359
360       i = (*pthread_create_pointer) (&thread, NULL, start, NULL);
361       assert (i == 0);
362       i = (*pthread_create_pointer) (&thread, NULL, start, NULL);
363       assert (i == 0);
364       
365       attach (getpid ());
366     }
367   else
368     {
369       char *endptr;
370       long pidl = strtol (argv[1], &endptr, 0);
371
372       if (pidl < 0 || (pid_t) pidl != pidl || (endptr != NULL && *endptr != 0))
373         syntax ();
374       attach (pidl);
375     }
376
377   return EXIT_SUCCESS;
378 }