diff options
Diffstat (limited to 'arch/um/kernel/tt/gdb.c')
-rw-r--r-- | arch/um/kernel/tt/gdb.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/arch/um/kernel/tt/gdb.c b/arch/um/kernel/tt/gdb.c new file mode 100644 index 000000000000..19a0ad7b35b3 --- /dev/null +++ b/arch/um/kernel/tt/gdb.c | |||
@@ -0,0 +1,278 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <errno.h> | ||
9 | #include <string.h> | ||
10 | #include <signal.h> | ||
11 | #include <sys/types.h> | ||
12 | #include "ptrace_user.h" | ||
13 | #include "uml-config.h" | ||
14 | #include "kern_constants.h" | ||
15 | #include "chan_user.h" | ||
16 | #include "init.h" | ||
17 | #include "user.h" | ||
18 | #include "debug.h" | ||
19 | #include "kern_util.h" | ||
20 | #include "user_util.h" | ||
21 | #include "tt.h" | ||
22 | #include "sysdep/thread.h" | ||
23 | |||
24 | extern int debugger_pid; | ||
25 | extern int debugger_fd; | ||
26 | extern int debugger_parent; | ||
27 | |||
28 | int detach(int pid, int sig) | ||
29 | { | ||
30 | return(ptrace(PTRACE_DETACH, pid, 0, sig)); | ||
31 | } | ||
32 | |||
33 | int attach(int pid) | ||
34 | { | ||
35 | int err; | ||
36 | |||
37 | err = ptrace(PTRACE_ATTACH, pid, 0, 0); | ||
38 | if(err < 0) return(-errno); | ||
39 | else return(err); | ||
40 | } | ||
41 | |||
42 | int cont(int pid) | ||
43 | { | ||
44 | return(ptrace(PTRACE_CONT, pid, 0, 0)); | ||
45 | } | ||
46 | |||
47 | #ifdef UML_CONFIG_PT_PROXY | ||
48 | |||
49 | int debugger_signal(int status, pid_t pid) | ||
50 | { | ||
51 | return(debugger_proxy(status, pid)); | ||
52 | } | ||
53 | |||
54 | void child_signal(pid_t pid, int status) | ||
55 | { | ||
56 | child_proxy(pid, status); | ||
57 | } | ||
58 | |||
59 | static void gdb_announce(char *dev_name, int dev) | ||
60 | { | ||
61 | printf("gdb assigned device '%s'\n", dev_name); | ||
62 | } | ||
63 | |||
64 | static struct chan_opts opts = { | ||
65 | .announce = gdb_announce, | ||
66 | .xterm_title = "UML kernel debugger", | ||
67 | .raw = 0, | ||
68 | .tramp_stack = 0, | ||
69 | .in_kernel = 0, | ||
70 | }; | ||
71 | |||
72 | /* Accessed by the tracing thread, which automatically serializes access */ | ||
73 | static void *xterm_data; | ||
74 | static int xterm_fd; | ||
75 | |||
76 | extern void *xterm_init(char *, int, struct chan_opts *); | ||
77 | extern int xterm_open(int, int, int, void *, char **); | ||
78 | extern void xterm_close(int, void *); | ||
79 | |||
80 | int open_gdb_chan(void) | ||
81 | { | ||
82 | char stack[UM_KERN_PAGE_SIZE], *dummy; | ||
83 | |||
84 | opts.tramp_stack = (unsigned long) stack; | ||
85 | xterm_data = xterm_init("", 0, &opts); | ||
86 | xterm_fd = xterm_open(1, 1, 1, xterm_data, &dummy); | ||
87 | return(xterm_fd); | ||
88 | } | ||
89 | |||
90 | static void exit_debugger_cb(void *unused) | ||
91 | { | ||
92 | if(debugger_pid != -1){ | ||
93 | if(gdb_pid != -1){ | ||
94 | fake_child_exit(); | ||
95 | gdb_pid = -1; | ||
96 | } | ||
97 | else kill_child_dead(debugger_pid); | ||
98 | debugger_pid = -1; | ||
99 | if(debugger_parent != -1) | ||
100 | detach(debugger_parent, SIGINT); | ||
101 | } | ||
102 | if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data); | ||
103 | } | ||
104 | |||
105 | static void exit_debugger(void) | ||
106 | { | ||
107 | initial_thread_cb(exit_debugger_cb, NULL); | ||
108 | } | ||
109 | |||
110 | __uml_exitcall(exit_debugger); | ||
111 | |||
112 | struct gdb_data { | ||
113 | char *str; | ||
114 | int err; | ||
115 | }; | ||
116 | |||
117 | static void config_gdb_cb(void *arg) | ||
118 | { | ||
119 | struct gdb_data *data = arg; | ||
120 | void *task; | ||
121 | int pid; | ||
122 | |||
123 | data->err = -1; | ||
124 | if(debugger_pid != -1) exit_debugger_cb(NULL); | ||
125 | if(!strncmp(data->str, "pid,", strlen("pid,"))){ | ||
126 | data->str += strlen("pid,"); | ||
127 | pid = strtoul(data->str, NULL, 0); | ||
128 | task = cpu_tasks[0].task; | ||
129 | debugger_pid = attach_debugger(TASK_EXTERN_PID(task), pid, 0); | ||
130 | if(debugger_pid != -1){ | ||
131 | data->err = 0; | ||
132 | gdb_pid = pid; | ||
133 | } | ||
134 | return; | ||
135 | } | ||
136 | data->err = 0; | ||
137 | debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); | ||
138 | init_proxy(debugger_pid, 0, 0); | ||
139 | } | ||
140 | |||
141 | int gdb_config(char *str) | ||
142 | { | ||
143 | struct gdb_data data; | ||
144 | |||
145 | if(*str++ != '=') return(-1); | ||
146 | data.str = str; | ||
147 | initial_thread_cb(config_gdb_cb, &data); | ||
148 | return(data.err); | ||
149 | } | ||
150 | |||
151 | void remove_gdb_cb(void *unused) | ||
152 | { | ||
153 | exit_debugger_cb(NULL); | ||
154 | } | ||
155 | |||
156 | int gdb_remove(char *unused) | ||
157 | { | ||
158 | initial_thread_cb(remove_gdb_cb, NULL); | ||
159 | return(0); | ||
160 | } | ||
161 | |||
162 | void signal_usr1(int sig) | ||
163 | { | ||
164 | if(debugger_pid != -1){ | ||
165 | printf("The debugger is already running\n"); | ||
166 | return; | ||
167 | } | ||
168 | debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd); | ||
169 | init_proxy(debugger_pid, 0, 0); | ||
170 | } | ||
171 | |||
172 | int init_ptrace_proxy(int idle_pid, int startup, int stop) | ||
173 | { | ||
174 | int pid, status; | ||
175 | |||
176 | pid = start_debugger(linux_prog, startup, stop, &debugger_fd); | ||
177 | status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL); | ||
178 | if(pid < 0){ | ||
179 | cont(idle_pid); | ||
180 | return(-1); | ||
181 | } | ||
182 | init_proxy(pid, 1, status); | ||
183 | return(pid); | ||
184 | } | ||
185 | |||
186 | int attach_debugger(int idle_pid, int pid, int stop) | ||
187 | { | ||
188 | int status = 0, err; | ||
189 | |||
190 | err = attach(pid); | ||
191 | if(err < 0){ | ||
192 | printf("Failed to attach pid %d, errno = %d\n", pid, -err); | ||
193 | return(-1); | ||
194 | } | ||
195 | if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL); | ||
196 | init_proxy(pid, 1, status); | ||
197 | return(pid); | ||
198 | } | ||
199 | |||
200 | #ifdef notdef /* Put this back in when it does something useful */ | ||
201 | static int __init uml_gdb_init_setup(char *line, int *add) | ||
202 | { | ||
203 | gdb_init = uml_strdup(line); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | __uml_setup("gdb=", uml_gdb_init_setup, | ||
208 | "gdb=<channel description>\n\n" | ||
209 | ); | ||
210 | #endif | ||
211 | |||
212 | static int __init uml_gdb_pid_setup(char *line, int *add) | ||
213 | { | ||
214 | gdb_pid = strtoul(line, NULL, 0); | ||
215 | *add = 0; | ||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | __uml_setup("gdb-pid=", uml_gdb_pid_setup, | ||
220 | "gdb-pid=<pid>\n" | ||
221 | " gdb-pid is used to attach an external debugger to UML. This may be\n" | ||
222 | " an already-running gdb or a debugger-like process like strace.\n\n" | ||
223 | ); | ||
224 | |||
225 | #else | ||
226 | |||
227 | int debugger_signal(int status, pid_t pid){ return(0); } | ||
228 | void child_signal(pid_t pid, int status){ } | ||
229 | int init_ptrace_proxy(int idle_pid, int startup, int stop) | ||
230 | { | ||
231 | printf("debug requested when CONFIG_PT_PROXY is off\n"); | ||
232 | kill_child_dead(idle_pid); | ||
233 | exit(1); | ||
234 | } | ||
235 | |||
236 | void signal_usr1(int sig) | ||
237 | { | ||
238 | printf("debug requested when CONFIG_PT_PROXY is off\n"); | ||
239 | } | ||
240 | |||
241 | int attach_debugger(int idle_pid, int pid, int stop) | ||
242 | { | ||
243 | printf("attach_debugger called when CONFIG_PT_PROXY " | ||
244 | "is off\n"); | ||
245 | return(-1); | ||
246 | } | ||
247 | |||
248 | int config_gdb(char *str) | ||
249 | { | ||
250 | return(-1); | ||
251 | } | ||
252 | |||
253 | int remove_gdb(void) | ||
254 | { | ||
255 | return(-1); | ||
256 | } | ||
257 | |||
258 | int init_parent_proxy(int pid) | ||
259 | { | ||
260 | return(-1); | ||
261 | } | ||
262 | |||
263 | void debugger_parent_signal(int status, int pid) | ||
264 | { | ||
265 | } | ||
266 | |||
267 | #endif | ||
268 | |||
269 | /* | ||
270 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
271 | * Emacs will notice this stuff at the end of the file and automatically | ||
272 | * adjust the settings for this buffer only. This must remain at the end | ||
273 | * of the file. | ||
274 | * --------------------------------------------------------------------------- | ||
275 | * Local variables: | ||
276 | * c-file-style: "linux" | ||
277 | * End: | ||
278 | */ | ||