blob: 9263184c06151dc2395ab0c7c24e3994cd4ee915 [file] [log] [blame]
Juan Cespedesd44c6b81998-09-25 14:48:42 +02001#if HAVE_CONFIG_H
2#include "config.h"
3#endif
4
Juan Cespedes5e01f651998-03-08 22:31:44 +01005#define _GNU_SOURCE
6#include <stdio.h>
7#include <string.h>
Juan Cespedes1fe93d51998-03-13 00:29:21 +01008#include <stdlib.h>
Juan Cespedes28f60191998-04-12 00:04:39 +02009#include <signal.h>
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +020010#include <assert.h>
Juan Cespedesd65efa32003-02-03 00:22:30 +010011#include <sys/time.h>
Juan Cespedes5e01f651998-03-08 22:31:44 +010012
13#include "ltrace.h"
14#include "output.h"
15#include "options.h"
Juan Cespedes81690ef1998-03-13 19:31:29 +010016#include "elf.h"
Juan Cespedescac15c32003-01-31 18:58:58 +010017#include "debug.h"
Juan Cespedes5e01f651998-03-08 22:31:44 +010018
Juan Cespedesf1bfe202002-03-27 00:22:23 +010019#ifdef __powerpc__
20#include <sys/ptrace.h>
21#endif
22
Ian Wienand9a2ad352006-02-20 22:44:45 +010023static void process_signal(struct event * event);
24static void process_exit(struct event * event);
25static void process_exit_signal(struct event * event);
26static void process_syscall(struct event * event);
27static void process_sysret(struct event * event);
28static void process_breakpoint(struct event * event);
29static void remove_proc(struct process * proc);
Juan Cespedes5e01f651998-03-08 22:31:44 +010030
Ian Wienand9a2ad352006-02-20 22:44:45 +010031static void callstack_push_syscall(struct process * proc, int sysnum);
32static void callstack_push_symfunc(struct process * proc, struct library_symbol * sym);
33static void callstack_pop(struct process * proc);
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +020034
Ian Wienand9a2ad352006-02-20 22:44:45 +010035static char *
36shortsignal(struct process *proc, int signum) {
37 static char * signalent0[] = {
38 #include "signalent.h"
Juan Cespedes5e01f651998-03-08 22:31:44 +010039 };
Ian Wienand9a2ad352006-02-20 22:44:45 +010040 static char * signalent1[] = {
41 #include "signalent1.h"
42 };
43 static char **signalents[] = { signalent0, signalent1 };
44 int nsignals[] = { sizeof signalent0 / sizeof signalent0[0],
45 sizeof signalent1 / sizeof signalent1[0] };
Juan Cespedes5e01f651998-03-08 22:31:44 +010046
Ian Wienand9a2ad352006-02-20 22:44:45 +010047 if (proc->personality > sizeof signalents / sizeof signalents[0])
48 abort ();
49 if (signum < 0 || signum >= nsignals[proc->personality]) {
Juan Cespedes5e01f651998-03-08 22:31:44 +010050 return "UNKNOWN_SIGNAL";
51 } else {
Ian Wienand9a2ad352006-02-20 22:44:45 +010052 return signalents[proc->personality][signum];
Juan Cespedes5e01f651998-03-08 22:31:44 +010053 }
54}
55
Ian Wienand9a2ad352006-02-20 22:44:45 +010056static char *
57sysname(struct process *proc, int sysnum) {
Juan Cespedes5e01f651998-03-08 22:31:44 +010058 static char result[128];
Ian Wienand9a2ad352006-02-20 22:44:45 +010059 static char * syscalent0[] = {
60 #include "syscallent.h"
Juan Cespedes5e01f651998-03-08 22:31:44 +010061 };
Ian Wienand9a2ad352006-02-20 22:44:45 +010062 static char * syscalent1[] = {
63 #include "syscallent1.h"
64 };
65 static char **syscalents[] = { syscalent0, syscalent1 };
66 int nsyscals[] = { sizeof syscalent0 / sizeof syscalent0[0],
67 sizeof syscalent1 / sizeof syscalent1[0] };
Juan Cespedes5e01f651998-03-08 22:31:44 +010068
Ian Wienand9a2ad352006-02-20 22:44:45 +010069 if (proc->personality > sizeof syscalents / sizeof syscalents[0])
70 abort ();
71 if (sysnum < 0 || sysnum >= nsyscals[proc->personality]) {
Juan Cespedes5e01f651998-03-08 22:31:44 +010072 sprintf(result, "SYS_%d", sysnum);
73 return result;
74 } else {
Ian Wienand9a2ad352006-02-20 22:44:45 +010075 sprintf(result, "SYS_%s", syscalents[proc->personality][sysnum]);
Juan Cespedes5e01f651998-03-08 22:31:44 +010076 return result;
77 }
78}
79
Ian Wienand9a2ad352006-02-20 22:44:45 +010080void
81process_event(struct event * event) {
Juan Cespedesefe85f02004-04-04 01:31:38 +020082 switch (event->thing) {
Ian Wienand9a2ad352006-02-20 22:44:45 +010083 case LT_EV_NONE:
84 debug(1, "event: none");
85 return;
86 case LT_EV_SIGNAL:
87 debug(1, "event: signal (%s [%d])", shortsignal(event->proc, event->e_un.signum), event->e_un.signum);
88 process_signal(event);
89 return;
90 case LT_EV_EXIT:
91 debug(1, "event: exit (%d)", event->e_un.ret_val);
92 process_exit(event);
93 return;
94 case LT_EV_EXIT_SIGNAL:
95 debug(1, "event: exit signal (%s [%d])", shortsignal(event->proc, event->e_un.signum), event->e_un.signum);
96 process_exit_signal(event);
97 return;
98 case LT_EV_SYSCALL:
99 debug(1, "event: syscall (%s [%d])", sysname (event->proc, event->e_un.sysnum), event->e_un.sysnum);
100 process_syscall(event);
101 return;
102 case LT_EV_SYSRET:
103 debug(1, "event: sysret (%s [%d])", sysname (event->proc, event->e_un.sysnum), event->e_un.sysnum);
104 process_sysret(event);
105 return;
106 case LT_EV_BREAKPOINT:
107 debug(1, "event: breakpoint");
108 process_breakpoint(event);
109 return;
110 default:
111 fprintf(stderr, "Error! unknown event?\n");
112 exit(1);
Juan Cespedesefe85f02004-04-04 01:31:38 +0200113 }
114}
115
Ian Wienand9a2ad352006-02-20 22:44:45 +0100116static void
117process_signal(struct event * event) {
Juan Cespedes28f60191998-04-12 00:04:39 +0200118 if (exiting && event->e_un.signum == SIGSTOP) {
119 pid_t pid = event->proc->pid;
120 disable_all_breakpoints(event->proc);
121 untrace_pid(pid);
122 remove_proc(event->proc);
123 continue_after_signal(pid, event->e_un.signum);
124 return;
125 }
Juan Cespedes5e01f651998-03-08 22:31:44 +0100126 output_line(event->proc, "--- %s (%s) ---",
Ian Wienand9a2ad352006-02-20 22:44:45 +0100127 shortsignal(event->proc, event->e_un.signum), strsignal(event->e_un.signum));
Juan Cespedes5e01f651998-03-08 22:31:44 +0100128 continue_after_signal(event->proc->pid, event->e_un.signum);
129}
130
Ian Wienand9a2ad352006-02-20 22:44:45 +0100131static void
132process_exit(struct event * event) {
Juan Cespedes5e01f651998-03-08 22:31:44 +0100133 output_line(event->proc, "+++ exited (status %d) +++",
Ian Wienand9a2ad352006-02-20 22:44:45 +0100134 event->e_un.ret_val);
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100135 remove_proc(event->proc);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100136}
137
Ian Wienand9a2ad352006-02-20 22:44:45 +0100138static void
139process_exit_signal(struct event * event) {
Juan Cespedes5e01f651998-03-08 22:31:44 +0100140 output_line(event->proc, "+++ killed by %s +++",
Ian Wienand9a2ad352006-02-20 22:44:45 +0100141 shortsignal(event->proc, event->e_un.signum));
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100142 remove_proc(event->proc);
143}
144
Ian Wienand9a2ad352006-02-20 22:44:45 +0100145static void
146remove_proc(struct process * proc) {
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100147 struct process *tmp, *tmp2;
148
Juan Cespedescac15c32003-01-31 18:58:58 +0100149 debug(1, "Removing pid %u\n", proc->pid);
Juan Cespedes28f60191998-04-12 00:04:39 +0200150
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100151 if (list_of_processes == proc) {
152 tmp = list_of_processes;
153 list_of_processes = list_of_processes->next;
154 free(tmp);
155 return;
156 }
157 tmp = list_of_processes;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100158 while(tmp->next) {
159 if (tmp->next==proc) {
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100160 tmp2 = tmp->next;
161 tmp->next = tmp->next->next;
162 free(tmp2);
Juan Cespedes28f60191998-04-12 00:04:39 +0200163 continue;
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100164 }
Juan Cespedes35d70631998-03-15 14:05:40 +0100165 tmp = tmp->next;
Juan Cespedes1fe93d51998-03-13 00:29:21 +0100166 }
Juan Cespedes5e01f651998-03-08 22:31:44 +0100167}
168
Ian Wienand9a2ad352006-02-20 22:44:45 +0100169static void
170process_syscall(struct event * event) {
Juan Cespedes5e01f651998-03-08 22:31:44 +0100171 if (opt_S) {
Ian Wienand9a2ad352006-02-20 22:44:45 +0100172 output_left(LT_TOF_SYSCALL, event->proc, sysname(event->proc, event->e_un.sysnum));
Juan Cespedes5e01f651998-03-08 22:31:44 +0100173 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100174 if (fork_p(event->proc, event->e_un.sysnum)
175 || exec_p(event->proc, event->e_un.sysnum)) {
Juan Cespedes5e01f651998-03-08 22:31:44 +0100176 disable_all_breakpoints(event->proc);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100177 } else if (event->proc->breakpoints_enabled == 0) {
Juan Cespedes81690ef1998-03-13 19:31:29 +0100178 enable_all_breakpoints(event->proc);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100179 }
Juan Cespedesd65efa32003-02-03 00:22:30 +0100180 callstack_push_syscall(event->proc, event->e_un.sysnum);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100181 continue_process(event->proc->pid);
182}
183
Juan Cespedesd65efa32003-02-03 00:22:30 +0100184struct timeval current_time_spent;
185
Ian Wienand9a2ad352006-02-20 22:44:45 +0100186static void
187calc_time_spent(struct process * proc) {
Juan Cespedesd65efa32003-02-03 00:22:30 +0100188 struct timeval tv;
189 struct timezone tz;
190 struct timeval diff;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100191 struct callstack_element * elem;
Juan Cespedesd65efa32003-02-03 00:22:30 +0100192
Ian Wienand9a2ad352006-02-20 22:44:45 +0100193 elem = & proc->callstack[proc->callstack_depth-1];
Juan Cespedesd65efa32003-02-03 00:22:30 +0100194
195 gettimeofday(&tv, &tz);
196
197 diff.tv_sec = tv.tv_sec - elem->time_spent.tv_sec;
198 if (tv.tv_usec >= elem->time_spent.tv_usec) {
199 diff.tv_usec = tv.tv_usec - elem->time_spent.tv_usec;
200 } else {
201 diff.tv_sec++;
202 diff.tv_usec = 1000000 + tv.tv_usec - elem->time_spent.tv_usec;
203 }
204 current_time_spent = diff;
205}
206
Ian Wienand9a2ad352006-02-20 22:44:45 +0100207static void
208process_sysret(struct event * event) {
Juan Cespedesd65efa32003-02-03 00:22:30 +0100209 if (opt_T || opt_c) {
210 calc_time_spent(event->proc);
211 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100212 if (fork_p(event->proc, event->e_un.sysnum)) {
Juan Cespedes5e01f651998-03-08 22:31:44 +0100213 if (opt_f) {
Ian Wienand9a2ad352006-02-20 22:44:45 +0100214 pid_t child = gimme_arg(LT_TOF_SYSCALLR,event->proc,-1);
215 if (child>0) {
Juan Cespedes273ea6d1998-03-14 23:02:40 +0100216 open_pid(child, 0);
217 }
Juan Cespedes5e01f651998-03-08 22:31:44 +0100218 }
Juan Cespedes35d70631998-03-15 14:05:40 +0100219 enable_all_breakpoints(event->proc);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100220 }
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200221 callstack_pop(event->proc);
Juan Cespedes21c63a12001-07-07 20:56:56 +0200222 if (opt_S) {
Ian Wienand9a2ad352006-02-20 22:44:45 +0100223 output_right(LT_TOF_SYSCALLR, event->proc, sysname(event->proc, event->e_un.sysnum));
Juan Cespedes21c63a12001-07-07 20:56:56 +0200224 }
Ian Wienand9a2ad352006-02-20 22:44:45 +0100225 if (exec_p(event->proc, event->e_un.sysnum)) {
226 if (gimme_arg(LT_TOF_SYSCALLR,event->proc,-1)==0) {
227 pid_t saved_pid;
228 event->proc->mask_32bit = 0;
229 event->proc->personality = 0;
230 /* FIXME: Leak, should have arch_dep_free.
231 But we are leaking here much more than that. */
232 event->proc->arch_ptr = NULL;
Juan Cespedes7186e2a2003-01-31 19:56:34 +0100233 event->proc->filename = pid2name(event->proc->pid);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100234 saved_pid = event->proc->pid;
235 event->proc->pid = 0;
Juan Cespedes7186e2a2003-01-31 19:56:34 +0100236 breakpoints_init(event->proc);
Ian Wienand9a2ad352006-02-20 22:44:45 +0100237 event->proc->pid = saved_pid;
238 } else
239 enable_all_breakpoints(event->proc);
Juan Cespedes7186e2a2003-01-31 19:56:34 +0100240 }
Juan Cespedes5e01f651998-03-08 22:31:44 +0100241 continue_process(event->proc->pid);
242}
243
Ian Wienand9a2ad352006-02-20 22:44:45 +0100244static void
245process_breakpoint(struct event * event) {
246 int i,j;
247 struct breakpoint *sbp;
Juan Cespedes5e01f651998-03-08 22:31:44 +0100248
Juan Cespedesefe85f02004-04-04 01:31:38 +0200249 debug(2, "event: breakpoint (%p)", event->e_un.brk_addr);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100250 if (event->proc->breakpoint_being_enabled) {
Juan Cespedesb1dd77d2002-03-03 00:22:06 +0100251 /* Reinsert breakpoint */
Ian Wienand9a2ad352006-02-20 22:44:45 +0100252 continue_enabling_breakpoint(event->proc->pid, event->proc->breakpoint_being_enabled);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100253 event->proc->breakpoint_being_enabled = NULL;
254 return;
255 }
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200256
Ian Wienand9a2ad352006-02-20 22:44:45 +0100257 for(i=event->proc->callstack_depth-1; i>=0; i--) {
258 if (event->e_un.brk_addr == event->proc->callstack[i].return_addr) {
Juan Cespedes5bfb0612002-03-31 20:01:28 +0200259#ifdef __powerpc__
Ian Wienand3219f322006-02-16 06:00:00 +0100260 /*
261 * PPC HACK! (XXX FIXME TODO)
262 * The PLT gets modified during the first call,
263 * so be sure to re-enable the breakpoint.
Ian Wienand9a2ad352006-02-20 22:44:45 +0100264 */
265 unsigned long a;
266 struct library_symbol *libsym =
267 event->proc->callstack[i].c_un.libfunc;
268 void *addr = plt2addr(event->proc, libsym->enter_addr);
Juan Cespedes5bfb0612002-03-31 20:01:28 +0200269
Ian Wienand9a2ad352006-02-20 22:44:45 +0100270 if (event->proc->e_machine == EM_PPC) {
271 unsigned char break_insn[] = BREAKPOINT_VALUE;
272
273 sbp = address2bpstruct(event->proc, addr);
274 assert(sbp);
275 a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, addr);
276
277 if (memcmp(&a, break_insn, 4)) {
278 sbp->enabled--;
279 insert_breakpoint(event->proc, addr, libsym);
280 }
281 } else {
282 sbp = libsym->brkpnt;
283 assert(sbp);
284 if (addr != sbp->addr)
285 insert_breakpoint(event->proc, addr, libsym);
Ian Wienand3219f322006-02-16 06:00:00 +0100286 }
Juan Cespedes5bfb0612002-03-31 20:01:28 +0200287#endif
Ian Wienand9a2ad352006-02-20 22:44:45 +0100288 for(j=event->proc->callstack_depth-1; j>i; j--) {
Juan Cespedes5916fda2002-02-25 00:19:21 +0100289 callstack_pop(event->proc);
290 }
Juan Cespedesd65efa32003-02-03 00:22:30 +0100291 if (opt_T || opt_c) {
292 calc_time_spent(event->proc);
293 }
294 callstack_pop(event->proc);
Juan Cespedes5916fda2002-02-25 00:19:21 +0100295 event->proc->return_addr = event->e_un.brk_addr;
Juan Cespedes5c3fe062004-06-14 18:08:37 +0200296 output_right(LT_TOF_FUNCTIONR, event->proc,
Ian Wienand9a2ad352006-02-20 22:44:45 +0100297 event->proc->callstack[i].c_un.libfunc->name);
Juan Cespedes5916fda2002-02-25 00:19:21 +0100298 continue_after_breakpoint(event->proc,
Ian Wienand9a2ad352006-02-20 22:44:45 +0100299 address2bpstruct(event->proc, event->e_un.brk_addr));
Juan Cespedes5916fda2002-02-25 00:19:21 +0100300 return;
301 }
Juan Cespedes5e01f651998-03-08 22:31:44 +0100302 }
303
Ian Wienand9a2ad352006-02-20 22:44:45 +0100304 if ((sbp = address2bpstruct(event->proc, event->e_un.brk_addr)))
305 {
306 event->proc->stack_pointer = get_stack_pointer(event->proc);
307 event->proc->return_addr = get_return_addr(event->proc, event->proc->stack_pointer);
308 output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name);
309 callstack_push_symfunc(event->proc, sbp->libsym);
310 if (PLTs_initialized_by_here
311 && event->proc->need_to_reinitialize_breakpoints
312 && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == 0))
313 reinitialize_breakpoints(event->proc);
314
315 continue_after_breakpoint(event->proc, sbp);
316 return;
317 }
318
319 output_line(event->proc, "unexpected breakpoint at %p",
320 (void *)event->e_un.brk_addr);
Juan Cespedes5e01f651998-03-08 22:31:44 +0100321 continue_process(event->proc->pid);
322}
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200323
Ian Wienand9a2ad352006-02-20 22:44:45 +0100324static void
325callstack_push_syscall(struct process * proc, int sysnum) {
326 struct callstack_element * elem;
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200327
328 /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */
Ian Wienand9a2ad352006-02-20 22:44:45 +0100329 if (proc->callstack_depth == MAX_CALLDEPTH-1) {
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200330 fprintf(stderr, "Error: call nesting too deep!\n");
331 return;
332 }
333
Ian Wienand9a2ad352006-02-20 22:44:45 +0100334 elem = & proc->callstack[proc->callstack_depth];
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200335 elem->is_syscall = 1;
336 elem->c_un.syscall = sysnum;
337 elem->return_addr = NULL;
338
339 proc->callstack_depth++;
Juan Cespedesd65efa32003-02-03 00:22:30 +0100340 if (opt_T || opt_c) {
341 struct timezone tz;
342 gettimeofday(&elem->time_spent, &tz);
343 }
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200344}
345
Juan Cespedes21c63a12001-07-07 20:56:56 +0200346static void
Ian Wienand9a2ad352006-02-20 22:44:45 +0100347callstack_push_symfunc(struct process * proc, struct library_symbol * sym) {
348 struct callstack_element * elem;
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200349
350 /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */
Ian Wienand9a2ad352006-02-20 22:44:45 +0100351 if (proc->callstack_depth == MAX_CALLDEPTH-1) {
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200352 fprintf(stderr, "Error: call nesting too deep!\n");
353 return;
354 }
355
Ian Wienand9a2ad352006-02-20 22:44:45 +0100356 elem = & proc->callstack[proc->callstack_depth];
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200357 elem->is_syscall = 0;
358 elem->c_un.libfunc = sym;
359
Juan Cespedes3f0b62e2001-07-09 01:02:52 +0200360 elem->return_addr = proc->return_addr;
Ian Wienand9a2ad352006-02-20 22:44:45 +0100361 insert_breakpoint(proc, elem->return_addr, 0);
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200362
363 proc->callstack_depth++;
Juan Cespedesd65efa32003-02-03 00:22:30 +0100364 if (opt_T || opt_c) {
365 struct timezone tz;
366 gettimeofday(&elem->time_spent, &tz);
367 }
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200368}
369
Ian Wienand9a2ad352006-02-20 22:44:45 +0100370static void
371callstack_pop(struct process * proc) {
372 struct callstack_element * elem;
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200373 assert(proc->callstack_depth > 0);
374
Ian Wienand9a2ad352006-02-20 22:44:45 +0100375 elem = & proc->callstack[proc->callstack_depth-1];
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200376 if (!elem->is_syscall) {
377 delete_breakpoint(proc, elem->return_addr);
378 }
379 proc->callstack_depth--;
Juan Cespedes5b3ffdf2001-07-02 00:52:45 +0200380}