summaryrefslogtreecommitdiffstats
path: root/arch/um/kernel/tt/gdb.c
blob: 030e4658f36bc5a0d8a39a80724cfba6fd1b3c1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/* 
 * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
 * Licensed under the GPL
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include "ptrace_user.h"
#include "uml-config.h"
#include "kern_constants.h"
#include "chan_user.h"
#include "init.h"
#include "user.h"
#include "debug.h"
#include "kern_util.h"
#include "tt.h"
#include "sysdep/thread.h"
#include "os.h"

extern int debugger_pid;
extern int debugger_fd;
extern int debugger_parent;

int detach(int pid, int sig)
{
	return(ptrace(PTRACE_DETACH, pid, 0, sig));
}

int attach(int pid)
{
	int err;

	err = ptrace(PTRACE_ATTACH, pid, 0, 0);
	if(err < 0) return(-errno);
	else return(err);
}

int cont(int pid)
{
	return(ptrace(PTRACE_CONT, pid, 0, 0));
}

#ifdef UML_CONFIG_PT_PROXY

int debugger_signal(int status, pid_t pid)
{
	return(debugger_proxy(status, pid));
}

void child_signal(pid_t pid, int status)
{
	child_proxy(pid, status);
}

static void gdb_announce(char *dev_name, int dev)
{
	printf("gdb assigned device '%s'\n", dev_name);
}

static struct chan_opts opts = {
	.announce  	= gdb_announce,
	.xterm_title 	= "UML kernel debugger",
	.raw 		= 0,
	.tramp_stack 	= 0,
	.in_kernel  	= 0,
};

/* Accessed by the tracing thread, which automatically serializes access */
static void *xterm_data;
static int xterm_fd;

extern void *xterm_init(char *, int, struct chan_opts *);
extern int xterm_open(int, int, int, void *, char **);
extern void xterm_close(int, void *);

int open_gdb_chan(void)
{
	char stack[UM_KERN_PAGE_SIZE], *dummy;

	opts.tramp_stack = (unsigned long) stack;
	xterm_data = xterm_init("", 0, &opts);
	xterm_fd = xterm_open(1, 1, 1, xterm_data, &dummy);
	return(xterm_fd);
}

static void exit_debugger_cb(void *unused)
{
	if(debugger_pid != -1){
		if(gdb_pid != -1){
			fake_child_exit();
			gdb_pid = -1;
		}
		else kill_child_dead(debugger_pid);
		debugger_pid = -1;
		if(debugger_parent != -1)
			detach(debugger_parent, SIGINT);
	}
	if(xterm_data != NULL) xterm_close(xterm_fd, xterm_data);
}

static void exit_debugger(void)
{
	initial_thread_cb(exit_debugger_cb, NULL);
}

__uml_exitcall(exit_debugger);

struct gdb_data {
	char *str;
	int err;
};

extern char *linux_prog;

static void config_gdb_cb(void *arg)
{
	struct gdb_data *data = arg;
	void *task;
	int pid;

	data->err = -1;
	if(debugger_pid != -1) exit_debugger_cb(NULL);
	if(!strncmp(data->str, "pid,", strlen("pid,"))){
		data->str += strlen("pid,");
		pid = strtoul(data->str, NULL, 0);
		task = cpu_tasks[0].task;
		debugger_pid = attach_debugger(TASK_EXTERN_PID(task), pid, 0);
		if(debugger_pid != -1){
			data->err = 0;
			gdb_pid = pid;
		}
		return;
	}
	data->err = 0;
	debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
	init_proxy(debugger_pid, 0, 0);
}

int gdb_config(char *str, char **error_out)
{
	struct gdb_data data;

	if(*str++ != '=') return(-1);
	data.str = str;
	initial_thread_cb(config_gdb_cb, &data);
	return(data.err);
}

void remove_gdb_cb(void *unused)
{
	exit_debugger_cb(NULL);
}

int gdb_remove(int unused, char **error_out)
{
	initial_thread_cb(remove_gdb_cb, NULL);
        return 0;
}

void signal_usr1(int sig)
{
	if(debugger_pid != -1){
		printf("The debugger is already running\n");
		return;
	}
	debugger_pid = start_debugger(linux_prog, 0, 0, &debugger_fd);
	init_proxy(debugger_pid, 0, 0);
}

int init_ptrace_proxy(int idle_pid, int startup, int stop)
{
	int pid, status;

	pid = start_debugger(linux_prog, startup, stop, &debugger_fd);
	status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
 	if(pid < 0){
		cont(idle_pid);
		return(-1);
	}
	init_proxy(pid, 1, status);
	return(pid);
}

int attach_debugger(int idle_pid, int pid, int stop)
{
	int status = 0, err;

	err = attach(pid);
	if(err < 0){
		printf("Failed to attach pid %d, errno = %d\n", pid, -err);
		return(-1);
	}
	if(stop) status = wait_for_stop(idle_pid, SIGSTOP, PTRACE_CONT, NULL);
	init_proxy(pid, 1, status);
	return(pid);
}

#ifdef notdef /* Put this back in when it does something useful */
static int __init uml_gdb_init_setup(char *line, int *add)
{
	gdb_init = uml_strdup(line);
	return 0;
}

__uml_setup("gdb=", uml_gdb_init_setup, 
"gdb=<channel description>\n\n"
);
#endif

static int __init uml_gdb_pid_setup(char *line, int *add)
{
	gdb_pid = strtoul(line, NULL, 0);
	*add = 0;
	return 0;
}

__uml_setup("gdb-pid=", uml_gdb_pid_setup, 
"gdb-pid=<pid>\n"
"    gdb-pid is used to attach an external debugger to UML.  This may be\n"
"    an already-running gdb or a debugger-like process like strace.\n\n"
);

#else

int debugger_signal(int status, pid_t pid){ return(0); }
void child_signal(pid_t pid, int status){ }
int init_ptrace_proxy(int idle_pid, int startup, int stop)
{
	printf("debug requested when CONFIG_PT_PROXY is off\n");
	kill_child_dead(idle_pid);
	exit(1);
}

void signal_usr1(int sig)
{
	printf("debug requested when CONFIG_PT_PROXY is off\n");
}

int attach_debugger(int idle_pid, int pid, int stop)
{
	printf("attach_debugger called when CONFIG_PT_PROXY "
	       "is off\n");
	return(-1);
}

int config_gdb(char *str)
{
	return(-1);
}

int remove_gdb(void)
{
	return(-1);
}

int init_parent_proxy(int pid)
{
	return(-1);
}

void debugger_parent_signal(int status, int pid)
{
}

#endif

/*
 * Overrides for Emacs so that we follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-file-style: "linux"
 * End:
 */