Imported Upstream version 0.5.3
[platform/upstream/ltrace.git] / sysdeps / linux-gnu / events.c
1 #include "config.h"
2
3 #define _GNU_SOURCE     1
4 #include <stdlib.h>
5 #include <sys/types.h>
6 #include <sys/wait.h>
7 #include <errno.h>
8 #include <signal.h>
9 #include <string.h>
10 #include <sys/ptrace.h>
11
12 #include "common.h"
13
14 static Event event;
15
16 Event *
17 next_event(void) {
18         pid_t pid;
19         int status;
20         int tmp;
21         int stop_signal;
22
23         debug(DEBUG_FUNCTION, "next_event()");
24         if (!list_of_processes) {
25                 debug(DEBUG_EVENT, "event: No more traced programs: exiting");
26                 exit(0);
27         }
28         pid = waitpid(-1, &status, __WALL);
29         if (pid == -1) {
30                 if (errno == ECHILD) {
31                         debug(DEBUG_EVENT, "event: No more traced programs: exiting");
32                         exit(0);
33                 } else if (errno == EINTR) {
34                         debug(DEBUG_EVENT, "event: none (wait received EINTR?)");
35                         event.type = EVENT_NONE;
36                         return &event;
37                 }
38                 perror("wait");
39                 exit(1);
40         }
41         event.proc = pid2proc(pid);
42         if (!event.proc || event.proc->state == STATE_BEING_CREATED) {
43                 event.type = EVENT_NEW;
44                 event.e_un.newpid = pid;
45                 debug(DEBUG_EVENT, "event: NEW: pid=%d", pid);
46                 return &event;
47         }
48         get_arch_dep(event.proc);
49         event.proc->instruction_pointer = NULL;
50         debug(3, "event from pid %u", pid);
51         if (event.proc->breakpoints_enabled == -1) {
52                 enable_all_breakpoints(event.proc);
53                 event.type = EVENT_NONE;
54                 trace_set_options(event.proc, event.proc->pid);
55                 continue_process(event.proc->pid);
56                 debug(DEBUG_EVENT, "event: NONE: pid=%d (enabling breakpoints)", pid);
57                 return &event;
58         }
59         if (opt_i) {
60                 event.proc->instruction_pointer =
61                         get_instruction_pointer(event.proc);
62         }
63         switch (syscall_p(event.proc, status, &tmp)) {
64                 case 1:
65                         event.type = EVENT_SYSCALL;
66                         event.e_un.sysnum = tmp;
67                         debug(DEBUG_EVENT, "event: SYSCALL: pid=%d, sysnum=%d", pid, tmp);
68                         return &event;
69                 case 2:
70                         event.type = EVENT_SYSRET;
71                         event.e_un.sysnum = tmp;
72                         debug(DEBUG_EVENT, "event: SYSRET: pid=%d, sysnum=%d", pid, tmp);
73                         return &event;
74                 case 3:
75                         event.type = EVENT_ARCH_SYSCALL;
76                         event.e_un.sysnum = tmp;
77                         debug(DEBUG_EVENT, "event: ARCH_SYSCALL: pid=%d, sysnum=%d", pid, tmp);
78                         return &event;
79                 case 4:
80                         event.type = EVENT_ARCH_SYSRET;
81                         event.e_un.sysnum = tmp;
82                         debug(DEBUG_EVENT, "event: ARCH_SYSRET: pid=%d, sysnum=%d", pid, tmp);
83                         return &event;
84                 case -1:
85                         event.type = EVENT_NONE;
86                         continue_process(event.proc->pid);
87                         debug(DEBUG_EVENT, "event: NONE: pid=%d (syscall_p returned -1)", pid);
88                         return &event;
89         }
90         if (WIFSTOPPED(status) && ((status>>16 == PTRACE_EVENT_FORK) || (status>>16 == PTRACE_EVENT_VFORK) || (status>>16 == PTRACE_EVENT_CLONE))) {
91                 unsigned long data;
92                 ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data);
93                 event.type = EVENT_CLONE;
94                 event.e_un.newpid = data;
95                 debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d", pid, (int)data);
96                 return &event;
97         }
98         if (WIFSTOPPED(status) && (status>>16 == PTRACE_EVENT_EXEC)) {
99                 event.type = EVENT_EXEC;
100                 debug(DEBUG_EVENT, "event: EXEC: pid=%d", pid);
101                 return &event;
102         }
103         if (WIFEXITED(status)) {
104                 event.type = EVENT_EXIT;
105                 event.e_un.ret_val = WEXITSTATUS(status);
106                 debug(DEBUG_EVENT, "event: EXIT: pid=%d, status=%d", pid, event.e_un.ret_val);
107                 return &event;
108         }
109         if (WIFSIGNALED(status)) {
110                 event.type = EVENT_EXIT_SIGNAL;
111                 event.e_un.signum = WTERMSIG(status);
112                 debug(DEBUG_EVENT, "event: EXIT_SIGNAL: pid=%d, signum=%d", pid, event.e_un.signum);
113                 return &event;
114         }
115         if (!WIFSTOPPED(status)) {
116                 /* should never happen */
117                 event.type = EVENT_NONE;
118                 debug(DEBUG_EVENT, "event: NONE: pid=%d (wait error?)", pid);
119                 return &event;
120         }
121
122         stop_signal = WSTOPSIG(status);
123
124         /* On some targets, breakpoints are signalled not using
125            SIGTRAP, but also with SIGILL, SIGSEGV or SIGEMT.  Check
126            for these. (TODO: is this true?) */
127         if (stop_signal == SIGSEGV
128                         || stop_signal == SIGILL
129 #ifdef SIGEMT
130                         || stop_signal == SIGEMT
131 #endif
132            ) {
133                 if (!event.proc->instruction_pointer) {
134                         event.proc->instruction_pointer =
135                                 get_instruction_pointer(event.proc);
136                 }
137
138                 if (address2bpstruct(event.proc, event.proc->instruction_pointer))
139                         stop_signal = SIGTRAP;
140         }
141
142         if (stop_signal != (SIGTRAP | event.proc->tracesysgood)
143                         && stop_signal != SIGTRAP) {
144                 event.type = EVENT_SIGNAL;
145                 event.e_un.signum = stop_signal;
146                 debug(DEBUG_EVENT, "event: SIGNAL: pid=%d, signum=%d", pid, stop_signal);
147                 return &event;
148         }
149
150         /* last case [by exhaustion] */
151         event.type = EVENT_BREAKPOINT;
152
153         if (!event.proc->instruction_pointer) {
154                 event.proc->instruction_pointer =
155                         get_instruction_pointer(event.proc);
156         }
157         event.e_un.brk_addr =
158                 event.proc->instruction_pointer - DECR_PC_AFTER_BREAK;
159         debug(DEBUG_EVENT, "event: BREAKPOINT: pid=%d, addr=%p", pid, event.e_un.brk_addr);
160         return &event;
161 }