Rev 4377 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4377 | Rev 4380 | ||
---|---|---|---|
1 | /* |
1 | /* |
2 | * Copyright (c) 2008 Jiri Svoboda |
2 | * Copyright (c) 2008 Jiri Svoboda |
3 | * All rights reserved. |
3 | * All rights reserved. |
4 | * |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions |
6 | * modification, are permitted provided that the following conditions |
7 | * are met: |
7 | * are met: |
8 | * |
8 | * |
9 | * - Redistributions of source code must retain the above copyright |
9 | * - Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * - Redistributions in binary form must reproduce the above copyright |
11 | * - Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
13 | * documentation and/or other materials provided with the distribution. |
14 | * - The name of the author may not be used to endorse or promote products |
14 | * - The name of the author may not be used to endorse or promote products |
15 | * derived from this software without specific prior written permission. |
15 | * derived from this software without specific prior written permission. |
16 | * |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
27 | */ |
28 | 28 | ||
29 | /** @addtogroup debug |
29 | /** @addtogroup debug |
30 | * @{ |
30 | * @{ |
31 | */ |
31 | */ |
32 | /** @file |
32 | /** @file |
33 | */ |
33 | */ |
34 | 34 | ||
35 | #include <stdio.h> |
35 | #include <stdio.h> |
36 | #include <stdlib.h> |
36 | #include <stdlib.h> |
37 | #include <unistd.h> |
37 | #include <unistd.h> |
38 | #include <libarch/syscall.h> |
38 | #include <libarch/syscall.h> |
39 | #include <ipc/ipc.h> |
39 | #include <ipc/ipc.h> |
40 | #include <fibril.h> |
40 | #include <fibril.h> |
41 | #include <loader/loader.h> |
41 | #include <loader/loader.h> |
42 | #include <errno.h> |
42 | #include <errno.h> |
43 | #include <udebug.h> |
43 | #include <udebug.h> |
44 | #include <async.h> |
44 | #include <async.h> |
45 | #include <string.h> |
45 | #include <string.h> |
46 | 46 | ||
47 | #include "cmd.h" |
47 | #include "cmd.h" |
48 | #include "cons.h" |
48 | #include "cons.h" |
49 | #include "dthread.h" |
49 | #include "dthread.h" |
50 | #include "breakpoint.h" |
50 | #include "breakpoint.h" |
51 | #include "include/arch.h" |
51 | #include "include/arch.h" |
52 | #include "main.h" |
52 | #include "main.h" |
53 | 53 | ||
54 | void thread_debug_start(unsigned thread_hash); |
54 | void thread_debug_start(unsigned thread_hash); |
55 | 55 | ||
56 | #define IN_BUF_SIZE 64 |
56 | #define IN_BUF_SIZE 64 |
57 | static char in_buf[IN_BUF_SIZE]; |
57 | static char in_buf[IN_BUF_SIZE]; |
58 | 58 | ||
59 | #define MAX_ARGC 10 |
59 | #define MAX_ARGC 10 |
60 | int cmd_argc; |
60 | int cmd_argc; |
61 | char *cmd_argv[MAX_ARGC + 1]; /* need one spare field for cmd_split() */ |
61 | char *cmd_argv[MAX_ARGC + 1]; /* need one spare field for cmd_split() */ |
62 | 62 | ||
63 | 63 | ||
64 | int next_thread_id; |
64 | int next_thread_id; |
65 | 65 | ||
66 | int app_phone; |
66 | int app_phone; |
67 | volatile bool abort_debug; |
67 | volatile bool abort_debug; |
68 | 68 | ||
69 | volatile int paused; |
69 | volatile int paused; |
70 | 70 | ||
71 | static task_id_t task_id; |
71 | static task_id_t task_id; |
72 | static loader_t *task_ldr; |
72 | static loader_t *task_ldr; |
73 | 73 | ||
74 | static int program_run_fibril(void *arg); |
74 | static int program_run_fibril(void *arg); |
75 | 75 | ||
76 | static void program_run(void) |
76 | static void program_run(void) |
77 | { |
77 | { |
78 | fid_t fid; |
78 | fid_t fid; |
79 | 79 | ||
80 | fid = fibril_create(program_run_fibril, NULL); |
80 | fid = fibril_create(program_run_fibril, NULL); |
81 | if (fid == 0) { |
81 | if (fid == 0) { |
82 | printf("Error creating fibril\n"); |
82 | printf("Error creating fibril\n"); |
83 | exit(1); |
83 | exit(1); |
84 | } |
84 | } |
85 | 85 | ||
86 | fibril_add_ready(fid); |
86 | fibril_add_ready(fid); |
87 | } |
87 | } |
88 | 88 | ||
89 | static int program_run_fibril(void *arg) |
89 | static int program_run_fibril(void *arg) |
90 | { |
90 | { |
91 | int rc; |
91 | int rc; |
92 | 92 | ||
93 | /* |
93 | /* |
94 | * This must be done in background as it will block until |
94 | * This must be done in background as it will block until |
95 | * we let the task reply to this call. |
95 | * we let the task reply to this call. |
96 | */ |
96 | */ |
97 | rc = loader_run(task_ldr); |
97 | rc = loader_run(task_ldr); |
98 | if (rc != 0) { |
98 | if (rc != 0) { |
99 | printf("Error running program\n"); |
99 | printf("Error running program\n"); |
100 | exit(1); |
100 | exit(1); |
101 | } |
101 | } |
102 | 102 | ||
103 | free(task_ldr); |
103 | free(task_ldr); |
104 | task_ldr = NULL; |
104 | task_ldr = NULL; |
105 | 105 | ||
106 | return 0; |
106 | return 0; |
107 | } |
107 | } |
108 | 108 | ||
109 | static loader_t *preload_task(const char *path, char *const argv[], |
109 | static loader_t *preload_task(const char *path, char *const argv[], |
110 | task_id_t *task_id) |
110 | task_id_t *task_id) |
111 | { |
111 | { |
112 | loader_t *ldr; |
112 | loader_t *ldr; |
113 | int rc; |
113 | int rc; |
114 | 114 | ||
115 | /* Spawn a program loader */ |
115 | /* Spawn a program loader */ |
116 | ldr = loader_spawn(path); |
116 | ldr = loader_connect(); |
117 | if (ldr == NULL) |
117 | if (ldr == NULL) |
118 | return 0; |
118 | return 0; |
119 | 119 | ||
120 | /* Get task ID. */ |
120 | /* Get task ID. */ |
121 | rc = loader_get_task_id(ldr, task_id); |
121 | rc = loader_get_task_id(ldr, task_id); |
122 | if (rc != EOK) |
122 | if (rc != EOK) |
123 | goto error; |
123 | goto error; |
124 | 124 | ||
125 | /* Send program pathname */ |
125 | /* Send program pathname */ |
126 | rc = loader_set_pathname(ldr, path); |
126 | rc = loader_set_pathname(ldr, path); |
127 | if (rc != EOK) |
127 | if (rc != EOK) |
128 | goto error; |
128 | goto error; |
129 | 129 | ||
130 | /* Send arguments */ |
130 | /* Send arguments */ |
131 | rc = loader_set_args(ldr, argv); |
131 | rc = loader_set_args(ldr, argv); |
132 | if (rc != EOK) |
132 | if (rc != EOK) |
133 | goto error; |
133 | goto error; |
134 | 134 | ||
135 | /* Load the program. */ |
135 | /* Load the program. */ |
136 | rc = loader_load_program(ldr); |
136 | rc = loader_load_program(ldr); |
137 | if (rc != EOK) |
137 | if (rc != EOK) |
138 | goto error; |
138 | goto error; |
139 | 139 | ||
140 | /* Success */ |
140 | /* Success */ |
141 | return ldr; |
141 | return ldr; |
142 | 142 | ||
143 | /* Error exit */ |
143 | /* Error exit */ |
144 | error: |
144 | error: |
145 | loader_abort(ldr); |
145 | loader_abort(ldr); |
146 | free(ldr); |
146 | free(ldr); |
147 | return NULL; |
147 | return NULL; |
148 | } |
148 | } |
149 | 149 | ||
150 | 150 | ||
151 | static void command_split(char *cmd_str) |
151 | static void command_split(char *cmd_str) |
152 | { |
152 | { |
153 | char *p = cmd_str; |
153 | char *p = cmd_str; |
154 | 154 | ||
155 | if (*p == '\0') { |
155 | if (*p == '\0') { |
156 | cmd_argc = 0; |
156 | cmd_argc = 0; |
157 | return; |
157 | return; |
158 | } |
158 | } |
159 | 159 | ||
160 | cmd_argc = 1; |
160 | cmd_argc = 1; |
161 | cmd_argv[0] = p; |
161 | cmd_argv[0] = p; |
162 | 162 | ||
163 | while (*p != '\0') { |
163 | while (*p != '\0') { |
164 | if (*p == ' ') { |
164 | if (*p == ' ') { |
165 | cmd_argv[cmd_argc++] = p + 1; |
165 | cmd_argv[cmd_argc++] = p + 1; |
166 | *p = '\0'; |
166 | *p = '\0'; |
167 | } |
167 | } |
168 | ++p; |
168 | ++p; |
169 | } |
169 | } |
170 | } |
170 | } |
171 | 171 | ||
172 | static void command_run(void) |
172 | static void command_run(void) |
173 | { |
173 | { |
174 | int i; |
174 | int i; |
175 | int cmp_len; |
175 | int cmp_len; |
176 | int len; |
176 | int len; |
177 | 177 | ||
178 | int idx_found; |
178 | int idx_found; |
179 | int num_found; |
179 | int num_found; |
180 | 180 | ||
181 | len = str_length(cmd_argv[0]); |
181 | len = str_length(cmd_argv[0]); |
182 | cmp_len = 1; |
182 | cmp_len = 1; |
183 | 183 | ||
184 | /* Silence warnings */ |
184 | /* Silence warnings */ |
185 | num_found = 0; |
185 | num_found = 0; |
186 | idx_found = 0; |
186 | idx_found = 0; |
187 | 187 | ||
188 | while (cmp_len <= len + 1) { |
188 | while (cmp_len <= len + 1) { |
189 | 189 | ||
190 | num_found = 0; |
190 | num_found = 0; |
191 | i = 0; |
191 | i = 0; |
192 | while (cmd_table[i].name != NULL) { |
192 | while (cmd_table[i].name != NULL) { |
193 | if (str_lcmp(cmd_table[i].name, cmd_argv[0], cmp_len) == 0) { |
193 | if (str_lcmp(cmd_table[i].name, cmd_argv[0], cmp_len) == 0) { |
194 | idx_found = i; |
194 | idx_found = i; |
195 | ++num_found; |
195 | ++num_found; |
196 | } |
196 | } |
197 | ++i; |
197 | ++i; |
198 | } |
198 | } |
199 | 199 | ||
200 | if (num_found < 2) break; |
200 | if (num_found < 2) break; |
201 | 201 | ||
202 | ++cmp_len; |
202 | ++cmp_len; |
203 | } |
203 | } |
204 | 204 | ||
205 | if (num_found == 0) { |
205 | if (num_found == 0) { |
206 | cons_printf("Unknown command. Try one of:\n"); |
206 | cons_printf("Unknown command. Try one of:\n"); |
207 | cmd_help(0, NULL); |
207 | cmd_help(0, NULL); |
208 | return; |
208 | return; |
209 | } |
209 | } |
210 | 210 | ||
211 | if (cmd_argc - 1 != cmd_table[idx_found].argc) { |
211 | if (cmd_argc - 1 != cmd_table[idx_found].argc) { |
212 | cons_printf("Command '%s' expects %d arguments\n", |
212 | cons_printf("Command '%s' expects %d arguments\n", |
213 | cmd_table[idx_found].name, cmd_table[idx_found].argc); |
213 | cmd_table[idx_found].name, cmd_table[idx_found].argc); |
214 | return; |
214 | return; |
215 | } |
215 | } |
216 | 216 | ||
217 | (*cmd_table[idx_found].proc)(cmd_argc, cmd_argv); |
217 | (*cmd_table[idx_found].proc)(cmd_argc, cmd_argv); |
218 | } |
218 | } |
219 | 219 | ||
220 | static void thread_stop(void) |
220 | static void thread_stop(void) |
221 | { |
221 | { |
222 | dthread_t *dt; |
222 | dthread_t *dt; |
223 | uintptr_t pc; |
223 | uintptr_t pc; |
224 | 224 | ||
225 | dt = dthread_get(); |
225 | dt = dthread_get(); |
226 | pc = dthread_get_pc(dt); |
226 | pc = dthread_get_pc(dt); |
227 | cons_printf("[thread %d] stopped at 0x%lx\n", dt->id, pc); |
227 | cons_printf("[thread %d] stopped at 0x%lx\n", dt->id, pc); |
228 | dthread_stop_me(); |
228 | dthread_stop_me(); |
229 | cons_printf("[thread %d] go\n", dt->id); |
229 | cons_printf("[thread %d] go\n", dt->id); |
230 | } |
230 | } |
231 | 231 | ||
232 | /* |
232 | /* |
233 | * Called by a fibril (from arch code) when a breakpoint is hit. |
233 | * Called by a fibril (from arch code) when a breakpoint is hit. |
234 | */ |
234 | */ |
235 | void breakpoint_hit(breakpoint_t *b) |
235 | void breakpoint_hit(breakpoint_t *b) |
236 | { |
236 | { |
237 | dthread_t *dt; |
237 | dthread_t *dt; |
238 | 238 | ||
239 | dt = dthread_get(); |
239 | dt = dthread_get(); |
240 | cons_printf("Thread %d hit breakpoint %d at 0x%lx\n", |
240 | cons_printf("Thread %d hit breakpoint %d at 0x%lx\n", |
241 | dt->id, b->id, b->addr); |
241 | dt->id, b->id, b->addr); |
242 | thread_stop(); |
242 | thread_stop(); |
243 | } |
243 | } |
244 | 244 | ||
245 | /* |
245 | /* |
246 | * Called by a fibril (from arch code) when a single instruction |
246 | * Called by a fibril (from arch code) when a single instruction |
247 | * in singlestep is executed |
247 | * in singlestep is executed |
248 | */ |
248 | */ |
249 | void singlestep_hit(void) |
249 | void singlestep_hit(void) |
250 | { |
250 | { |
251 | cons_printf("singlestep hit\n"); |
251 | cons_printf("singlestep hit\n"); |
252 | thread_stop(); |
252 | thread_stop(); |
253 | } |
253 | } |
254 | 254 | ||
255 | static int connect_task(task_id_t task_id) |
255 | static int connect_task(task_id_t task_id) |
256 | { |
256 | { |
257 | int rc; |
257 | int rc; |
258 | unsigned evmask; |
258 | unsigned evmask; |
259 | 259 | ||
260 | cons_printf("ipc_connect_kbox(%;;d)... ", task_id); |
260 | cons_printf("ipc_connect_kbox(%;;d)... ", task_id); |
261 | rc = ipc_connect_kbox(task_id); |
261 | rc = ipc_connect_kbox(task_id); |
262 | cons_printf("-> %d\n", rc); |
262 | cons_printf("-> %d\n", rc); |
263 | app_phone = rc; |
263 | app_phone = rc; |
264 | if (rc < 0) return rc; |
264 | if (rc < 0) return rc; |
265 | 265 | ||
266 | cons_printf("udebug_begin()... "); |
266 | cons_printf("udebug_begin()... "); |
267 | rc = udebug_begin(app_phone); |
267 | rc = udebug_begin(app_phone); |
268 | cons_printf("-> %d\n", rc); |
268 | cons_printf("-> %d\n", rc); |
269 | if (rc < 0) return rc; |
269 | if (rc < 0) return rc; |
270 | 270 | ||
271 | evmask = UDEBUG_EM_ALL & ~(UDEBUG_EM_SYSCALL_B | UDEBUG_EM_SYSCALL_E); |
271 | evmask = UDEBUG_EM_ALL & ~(UDEBUG_EM_SYSCALL_B | UDEBUG_EM_SYSCALL_E); |
272 | cons_printf("udebug_set_evmask(0x%x)... ", evmask); |
272 | cons_printf("udebug_set_evmask(0x%x)... ", evmask); |
273 | rc = udebug_set_evmask(app_phone, evmask); |
273 | rc = udebug_set_evmask(app_phone, evmask); |
274 | cons_printf("-> %d\n", rc); |
274 | cons_printf("-> %d\n", rc); |
275 | if (rc < 0) return rc; |
275 | if (rc < 0) return rc; |
276 | 276 | ||
277 | return 0; |
277 | return 0; |
278 | } |
278 | } |
279 | 279 | ||
280 | #define THASH_BUF_INIT_LENGTH 32 |
280 | #define THASH_BUF_INIT_LENGTH 32 |
281 | 281 | ||
282 | static int get_thread_list(thash_t **thash_buf_ptr, int *n) |
282 | static int get_thread_list(thash_t **thash_buf_ptr, int *n) |
283 | { |
283 | { |
284 | int rc; |
284 | int rc; |
285 | size_t tb_copied; |
285 | size_t tb_copied; |
286 | size_t tb_needed; |
286 | size_t tb_needed; |
287 | int i; |
287 | int i; |
288 | size_t tb_size; |
288 | size_t tb_size; |
289 | thash_t *thash_buf; |
289 | thash_t *thash_buf; |
290 | 290 | ||
291 | tb_size = THASH_BUF_INIT_LENGTH * sizeof(thash_t); |
291 | tb_size = THASH_BUF_INIT_LENGTH * sizeof(thash_t); |
292 | thash_buf = malloc(tb_size); |
292 | thash_buf = malloc(tb_size); |
293 | 293 | ||
294 | rc = udebug_thread_read(app_phone, thash_buf, |
294 | rc = udebug_thread_read(app_phone, thash_buf, |
295 | tb_size, &tb_copied, &tb_needed); |
295 | tb_size, &tb_copied, &tb_needed); |
296 | if (rc < 0) return rc; |
296 | if (rc < 0) return rc; |
297 | 297 | ||
298 | if (tb_needed > tb_size) { |
298 | if (tb_needed > tb_size) { |
299 | /* Larger buffer needed */ |
299 | /* Larger buffer needed */ |
300 | 300 | ||
301 | free(thash_buf); |
301 | free(thash_buf); |
302 | 302 | ||
303 | tb_size = tb_needed; |
303 | tb_size = tb_needed; |
304 | thash_buf = malloc(tb_size); |
304 | thash_buf = malloc(tb_size); |
305 | 305 | ||
306 | if (!thash_buf) { |
306 | if (!thash_buf) { |
307 | printf("malloc failed\n"); |
307 | printf("malloc failed\n"); |
308 | exit(1); |
308 | exit(1); |
309 | } |
309 | } |
310 | 310 | ||
311 | /* Try again */ |
311 | /* Try again */ |
312 | 312 | ||
313 | rc = udebug_thread_read(app_phone, thash_buf, |
313 | rc = udebug_thread_read(app_phone, thash_buf, |
314 | tb_size, &tb_copied, &tb_needed); |
314 | tb_size, &tb_copied, &tb_needed); |
315 | 315 | ||
316 | if (rc < 0) return rc; |
316 | if (rc < 0) return rc; |
317 | } |
317 | } |
318 | 318 | ||
319 | *n = tb_copied / sizeof(thash_t); |
319 | *n = tb_copied / sizeof(thash_t); |
320 | 320 | ||
321 | cons_printf("thread hashes:"); |
321 | cons_printf("thread hashes:"); |
322 | 322 | ||
323 | for (i = 0; i < *n; ++i) { |
323 | for (i = 0; i < *n; ++i) { |
324 | cons_printf("0x%x\n", thash_buf[i]); |
324 | cons_printf("0x%x\n", thash_buf[i]); |
325 | } |
325 | } |
326 | 326 | ||
327 | cons_printf("Total of %u threads\n", *n); |
327 | cons_printf("Total of %u threads\n", *n); |
328 | 328 | ||
329 | *thash_buf_ptr = thash_buf; |
329 | *thash_buf_ptr = thash_buf; |
330 | 330 | ||
331 | return 0; |
331 | return 0; |
332 | } |
332 | } |
333 | 333 | ||
334 | static void event_thread_b(unsigned hash) |
334 | static void event_thread_b(unsigned hash) |
335 | { |
335 | { |
336 | async_serialize_start(); |
336 | async_serialize_start(); |
337 | cons_printf("new thread, hash 0x%x\n", hash); |
337 | cons_printf("new thread, hash 0x%x\n", hash); |
338 | async_serialize_end(); |
338 | async_serialize_end(); |
339 | 339 | ||
340 | thread_debug_start(hash); |
340 | thread_debug_start(hash); |
341 | } |
341 | } |
342 | 342 | ||
343 | static void debug_event(thash_t thash, udebug_event_t ev_type, sysarg_t val0) |
343 | static void debug_event(thash_t thash, udebug_event_t ev_type, sysarg_t val0) |
344 | { |
344 | { |
345 | switch (ev_type) { |
345 | switch (ev_type) { |
346 | case UDEBUG_EVENT_STOP: |
346 | case UDEBUG_EVENT_STOP: |
347 | cons_printf("stop event\n"); |
347 | cons_printf("stop event\n"); |
348 | thread_stop(); |
348 | thread_stop(); |
349 | break; |
349 | break; |
350 | case UDEBUG_EVENT_THREAD_B: |
350 | case UDEBUG_EVENT_THREAD_B: |
351 | event_thread_b(val0); |
351 | event_thread_b(val0); |
352 | break; |
352 | break; |
353 | case UDEBUG_EVENT_THREAD_E: |
353 | case UDEBUG_EVENT_THREAD_E: |
354 | cons_printf("thread 0x%x exited\n", val0); |
354 | cons_printf("thread 0x%x exited\n", val0); |
355 | abort_debug = true; |
355 | abort_debug = true; |
356 | break; |
356 | break; |
357 | case UDEBUG_EVENT_BREAKPOINT: |
357 | case UDEBUG_EVENT_BREAKPOINT: |
358 | arch_event_breakpoint(thash); |
358 | arch_event_breakpoint(thash); |
359 | break; |
359 | break; |
360 | case UDEBUG_EVENT_TRAP: |
360 | case UDEBUG_EVENT_TRAP: |
361 | arch_event_trap(dthread_get()); |
361 | arch_event_trap(dthread_get()); |
362 | break; |
362 | break; |
363 | default: |
363 | default: |
364 | cons_printf("unknown event type %d\n", ev_type); |
364 | cons_printf("unknown event type %d\n", ev_type); |
365 | break; |
365 | break; |
366 | } |
366 | } |
367 | } |
367 | } |
368 | 368 | ||
369 | static int debug_loop(void *dt_arg) |
369 | static int debug_loop(void *dt_arg) |
370 | { |
370 | { |
371 | int rc; |
371 | int rc; |
372 | udebug_event_t ev_type; |
372 | udebug_event_t ev_type; |
373 | unsigned val0, val1; |
373 | unsigned val0, val1; |
374 | dthread_t *dt; |
374 | dthread_t *dt; |
375 | 375 | ||
376 | dt = (dthread_t *)dt_arg; |
376 | dt = (dthread_t *)dt_arg; |
377 | 377 | ||
378 | cons_printf("debug_loop(%d)\n", dt->id); |
378 | cons_printf("debug_loop(%d)\n", dt->id); |
379 | 379 | ||
380 | while (!abort_debug) { |
380 | while (!abort_debug) { |
381 | 381 | ||
382 | /* Run thread until an event occurs */ |
382 | /* Run thread until an event occurs */ |
383 | rc = udebug_go(app_phone, dt->hash, &ev_type, &val0, &val1); |
383 | rc = udebug_go(app_phone, dt->hash, &ev_type, &val0, &val1); |
384 | 384 | ||
385 | if (ev_type == UDEBUG_EVENT_FINISHED) { |
385 | if (ev_type == UDEBUG_EVENT_FINISHED) { |
386 | cons_printf("thread %u debugging finished\n", dt->id); |
386 | cons_printf("thread %u debugging finished\n", dt->id); |
387 | break; |
387 | break; |
388 | } |
388 | } |
389 | if (rc >= 0) debug_event(dt->hash, ev_type, val0); |
389 | if (rc >= 0) debug_event(dt->hash, ev_type, val0); |
390 | } |
390 | } |
391 | 391 | ||
392 | cons_printf("debug_loop(%d) exiting\n", dt->id); |
392 | cons_printf("debug_loop(%d) exiting\n", dt->id); |
393 | return 0; |
393 | return 0; |
394 | } |
394 | } |
395 | 395 | ||
396 | void thread_debug_start(unsigned thash) |
396 | void thread_debug_start(unsigned thash) |
397 | { |
397 | { |
398 | fid_t fid; |
398 | fid_t fid; |
399 | dthread_t *dt; |
399 | dthread_t *dt; |
400 | 400 | ||
401 | dt = dthread_new(thash); |
401 | dt = dthread_new(thash); |
402 | 402 | ||
403 | fid = fibril_create(debug_loop, (void *)dt); |
403 | fid = fibril_create(debug_loop, (void *)dt); |
404 | if (fid == 0) { |
404 | if (fid == 0) { |
405 | cons_printf("Warning: Failed creating fibril\n"); |
405 | cons_printf("Warning: Failed creating fibril\n"); |
406 | } |
406 | } |
407 | dt->fid = fid; |
407 | dt->fid = fid; |
408 | 408 | ||
409 | fibril_add_ready(fid); |
409 | fibril_add_ready(fid); |
410 | } |
410 | } |
411 | 411 | ||
412 | static void debug_task(task_id_t task_id) |
412 | static void debug_task(task_id_t task_id) |
413 | { |
413 | { |
414 | int i; |
414 | int i; |
415 | int rc; |
415 | int rc; |
416 | 416 | ||
417 | thash_t *thash_buffer; |
417 | thash_t *thash_buffer; |
418 | int n_threads; |
418 | int n_threads; |
419 | 419 | ||
420 | rc = get_thread_list(&thash_buffer, &n_threads); |
420 | rc = get_thread_list(&thash_buffer, &n_threads); |
421 | if (rc < 0) { |
421 | if (rc < 0) { |
422 | cons_printf("Failed to get thread list\n", rc); |
422 | cons_printf("Failed to get thread list\n", rc); |
423 | return; |
423 | return; |
424 | } |
424 | } |
425 | 425 | ||
426 | abort_debug = false; |
426 | abort_debug = false; |
427 | 427 | ||
428 | for (i = 0; i < n_threads; i++) { |
428 | for (i = 0; i < n_threads; i++) { |
429 | thread_debug_start(thash_buffer[i]); |
429 | thread_debug_start(thash_buffer[i]); |
430 | } |
430 | } |
431 | 431 | ||
432 | while (!quit) { |
432 | while (!quit) { |
433 | cons_read_line(in_buf, IN_BUF_SIZE); |
433 | cons_read_line(in_buf, IN_BUF_SIZE); |
434 | command_split(in_buf); |
434 | command_split(in_buf); |
435 | if (cmd_argc == 0) continue; |
435 | if (cmd_argc == 0) continue; |
436 | 436 | ||
437 | command_run(); |
437 | command_run(); |
438 | } |
438 | } |
439 | 439 | ||
440 | cons_printf("terminate debugging session...\n"); |
440 | cons_printf("terminate debugging session...\n"); |
441 | abort_debug = true; |
441 | abort_debug = true; |
442 | udebug_end(app_phone); |
442 | udebug_end(app_phone); |
443 | ipc_hangup(app_phone); |
443 | ipc_hangup(app_phone); |
444 | 444 | ||
445 | cons_printf("done\n"); |
445 | cons_printf("done\n"); |
446 | return; |
446 | return; |
447 | } |
447 | } |
448 | 448 | ||
449 | static void main_init(void) |
449 | static void main_init(void) |
450 | { |
450 | { |
451 | next_thread_id = 1; |
451 | next_thread_id = 1; |
452 | paused = 0; |
452 | paused = 0; |
453 | 453 | ||
454 | list_initialize(&dthreads); |
454 | list_initialize(&dthreads); |
455 | breakpoint_init(); |
455 | breakpoint_init(); |
456 | cwt = NULL; |
456 | cwt = NULL; |
457 | } |
457 | } |
458 | 458 | ||
459 | static void print_syntax() |
459 | static void print_syntax() |
460 | { |
460 | { |
461 | printf("Syntax:\n"); |
461 | printf("Syntax:\n"); |
462 | printf("\tdebug <executable> [<arg1> [...]]\n"); |
462 | printf("\tdebug <executable> [<arg1> [...]]\n"); |
463 | printf("or\tdebug -t <task_id>\n"); |
463 | printf("or\tdebug -t <task_id>\n"); |
464 | } |
464 | } |
465 | 465 | ||
466 | static int parse_args(int argc, char *argv[]) |
466 | static int parse_args(int argc, char *argv[]) |
467 | { |
467 | { |
468 | char *arg; |
468 | char *arg; |
469 | char *err_p; |
469 | char *err_p; |
470 | 470 | ||
471 | task_id = 0; |
471 | task_id = 0; |
472 | 472 | ||
473 | --argc; ++argv; |
473 | --argc; ++argv; |
474 | 474 | ||
475 | while (argc > 0) { |
475 | while (argc > 0) { |
476 | arg = *argv; |
476 | arg = *argv; |
477 | if (arg[0] == '-') { |
477 | if (arg[0] == '-') { |
478 | if (arg[1] == 't') { |
478 | if (arg[1] == 't') { |
479 | /* Trace an already running task */ |
479 | /* Trace an already running task */ |
480 | --argc; ++argv; |
480 | --argc; ++argv; |
481 | task_id = strtol(*argv, &err_p, 10); |
481 | task_id = strtol(*argv, &err_p, 10); |
482 | task_ldr = NULL; |
482 | task_ldr = NULL; |
483 | if (*err_p) { |
483 | if (*err_p) { |
484 | printf("Task ID syntax error\n"); |
484 | printf("Task ID syntax error\n"); |
485 | print_syntax(); |
485 | print_syntax(); |
486 | return -1; |
486 | return -1; |
487 | } |
487 | } |
488 | } else { |
488 | } else { |
489 | printf("Uknown option '%s'\n", arg[0]); |
489 | printf("Uknown option '%s'\n", arg[0]); |
490 | print_syntax(); |
490 | print_syntax(); |
491 | return -1; |
491 | return -1; |
492 | } |
492 | } |
493 | } else { |
493 | } else { |
494 | break; |
494 | break; |
495 | } |
495 | } |
496 | 496 | ||
497 | --argc; ++argv; |
497 | --argc; ++argv; |
498 | } |
498 | } |
499 | 499 | ||
500 | if (task_id != 0) { |
500 | if (task_id != 0) { |
501 | if (argc == 0) return 0; |
501 | if (argc == 0) return 0; |
502 | printf("Extra arguments\n"); |
502 | printf("Extra arguments\n"); |
503 | print_syntax(); |
503 | print_syntax(); |
504 | return -1; |
504 | return -1; |
505 | } |
505 | } |
506 | 506 | ||
507 | if (argc < 1) { |
507 | if (argc < 1) { |
508 | printf("Missing argument\n"); |
508 | printf("Missing argument\n"); |
509 | print_syntax(); |
509 | print_syntax(); |
510 | return -1; |
510 | return -1; |
511 | } |
511 | } |
512 | 512 | ||
513 | /* Preload the specified program file. */ |
513 | /* Preload the specified program file. */ |
514 | printf("Spawning '%s' with arguments:\n", *argv); |
514 | printf("Spawning '%s' with arguments:\n", *argv); |
515 | { |
515 | { |
516 | char **cp = argv; |
516 | char **cp = argv; |
517 | while (*cp) printf("'%s'\n", *cp++); |
517 | while (*cp) printf("'%s'\n", *cp++); |
518 | } |
518 | } |
519 | task_ldr = preload_task(*argv, argv, &task_id); |
519 | task_ldr = preload_task(*argv, argv, &task_id); |
520 | 520 | ||
521 | return 0; |
521 | return 0; |
522 | } |
522 | } |
523 | 523 | ||
524 | int main(int argc, char *argv[]) |
524 | int main(int argc, char *argv[]) |
525 | { |
525 | { |
526 | int rc; |
526 | int rc; |
527 | 527 | ||
528 | cons_printf("Breakpoint Debugger\n"); |
528 | cons_printf("Breakpoint Debugger\n"); |
529 | 529 | ||
530 | main_init(); |
530 | main_init(); |
531 | 531 | ||
532 | if (parse_args(argc, argv) < 0) |
532 | if (parse_args(argc, argv) < 0) |
533 | return 1; |
533 | return 1; |
534 | 534 | ||
535 | rc = connect_task(task_id); |
535 | rc = connect_task(task_id); |
536 | if (rc < 0) { |
536 | if (rc < 0) { |
537 | printf("Failed connecting to task %lld\n", task_id); |
537 | printf("Failed connecting to task %lld\n", task_id); |
538 | return 1; |
538 | return 1; |
539 | } |
539 | } |
540 | 540 | ||
541 | cons_printf("Connected to task %lld\n", task_id); |
541 | cons_printf("Connected to task %lld\n", task_id); |
542 | 542 | ||
543 | if (task_ldr != NULL) { |
543 | if (task_ldr != NULL) { |
544 | program_run(); |
544 | program_run(); |
545 | } |
545 | } |
546 | 546 | ||
547 | debug_task(task_id); |
547 | debug_task(task_id); |
548 | 548 | ||
549 | return 0; |
549 | return 0; |
550 | } |
550 | } |
551 | 551 | ||
552 | /** @} |
552 | /** @} |
553 | */ |
553 | */ |
554 | 554 |