2 * Copyright (c) 2010 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
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
15 * derived from this software without specific prior written permission.
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
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
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
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
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup taskdump
36 #include <elf/elf_linux.h>
37 #include <fibrildump.h>
42 #include <str_error.h>
47 #include <libarch/istate.h>
54 #include <stacktrace.h>
58 #define STACK_FRAMES_MAX 20
60 static async_sess_t
*sess
;
61 static task_id_t task_id
;
62 static bool write_core_file
;
63 static char *core_file_name
;
64 static char *app_name
;
65 static symtab_t
*app_symtab
;
67 static errno_t
connect_task(task_id_t task_id
);
68 static int parse_args(int argc
, char *argv
[]);
69 static void print_syntax(void);
70 static errno_t
threads_dump(void);
71 static errno_t
thread_dump(uintptr_t thash
);
72 static errno_t
areas_dump(void);
73 static errno_t
td_read_uintptr(void *arg
, uintptr_t addr
, uintptr_t *value
);
75 static void autoload_syms(void);
76 static char *get_app_task_name(void);
77 static char *fmt_sym_address(uintptr_t addr
);
79 static istate_t reg_state
;
81 static stacktrace_ops_t td_stacktrace_ops
= {
82 .read_uintptr
= td_read_uintptr
85 int main(int argc
, char *argv
[])
89 printf("Task Dump Utility\n");
90 write_core_file
= false;
92 if (parse_args(argc
, argv
) < 0)
95 rc
= connect_task(task_id
);
97 printf("Failed connecting to task %" PRIu64
".\n", task_id
);
101 app_name
= get_app_task_name();
104 printf("Dumping task '%s' (task ID %" PRIu64
").\n", app_name
, task_id
);
110 printf("Failed dumping threads.\n");
114 printf("Failed dumping address space areas.\n");
116 rc
= fibrils_dump(app_symtab
, sess
);
118 printf("Failed dumping fibrils.\n");
126 static errno_t
connect_task(task_id_t task_id
)
128 async_sess_t
*ksess
= async_connect_kbox(task_id
);
131 if (errno
== ENOTSUP
) {
132 printf("You do not have userspace debugging support "
133 "compiled in the kernel.\n");
134 printf("Compile kernel with 'Support for userspace debuggers' "
135 "(CONFIG_UDEBUG) enabled.\n");
139 printf("Error connecting\n");
140 printf("async_connect_kbox(%" PRIu64
") -> %s", task_id
, str_error_name(errno
));
144 errno_t rc
= udebug_begin(ksess
);
146 printf("udebug_begin() -> %s\n", str_error_name(rc
));
154 static int parse_args(int argc
, char *argv
[])
167 if (arg
[1] == 't' && arg
[2] == '\0') {
171 task_id
= strtol(*argv
, &err_p
, 10);
173 printf("Task ID syntax error\n");
177 } else if (arg
[1] == 'c' && arg
[2] == '\0') {
178 write_core_file
= true;
182 core_file_name
= *argv
;
184 printf("Uknown option '%c'\n", arg
[0]);
197 printf("Missing task ID argument\n");
203 printf("Extra arguments\n");
211 static void print_syntax(void)
213 printf("Syntax: taskdump [-c <core_file>] -t <task_id>\n");
214 printf("\t-c <core_file_id>\tName of core file to write.\n");
215 printf("\t-t <task_id>\tWhich task to dump.\n");
218 static errno_t
threads_dump(void)
220 uintptr_t *thash_buf
;
222 size_t buf_size
, n_threads
;
229 /* TODO: See why NULL does not work. */
230 rc
= udebug_thread_read(sess
, &dummy_buf
, 0, &copied
, &needed
);
232 printf("udebug_thread_read() -> %s\n", str_error_name(rc
));
237 printf("No threads.\n\n");
242 thash_buf
= malloc(buf_size
);
244 rc
= udebug_thread_read(sess
, thash_buf
, buf_size
, &copied
, &needed
);
246 printf("udebug_thread_read() -> %s\n", str_error_name(rc
));
250 assert(copied
== buf_size
);
251 assert(needed
== buf_size
);
253 n_threads
= copied
/ sizeof(uintptr_t);
255 printf("Threads:\n");
256 for (i
= 0; i
< n_threads
; i
++) {
257 printf(" [%zu] hash: %p\n", 1 + i
, (void *) thash_buf
[i
]);
259 thread_dump(thash_buf
[i
]);
268 static errno_t
areas_dump(void)
270 as_area_info_t
*ainfo_buf
;
271 as_area_info_t dummy_buf
;
272 size_t buf_size
, n_areas
;
279 rc
= udebug_areas_read(sess
, &dummy_buf
, 0, &copied
, &needed
);
281 printf("udebug_areas_read() -> %s\n", str_error_name(rc
));
286 ainfo_buf
= malloc(buf_size
);
288 rc
= udebug_areas_read(sess
, ainfo_buf
, buf_size
, &copied
, &needed
);
290 printf("udebug_areas_read() -> %s\n", str_error_name(rc
));
294 assert(copied
== buf_size
);
295 assert(needed
== buf_size
);
297 n_areas
= copied
/ sizeof(as_area_info_t
);
299 printf("Address space areas:\n");
300 for (i
= 0; i
< n_areas
; i
++) {
301 printf(" [%zu] flags: %c%c%c%c base: %p size: %zu\n", 1 + i
,
302 (ainfo_buf
[i
].flags
& AS_AREA_READ
) ? 'R' : '-',
303 (ainfo_buf
[i
].flags
& AS_AREA_WRITE
) ? 'W' : '-',
304 (ainfo_buf
[i
].flags
& AS_AREA_EXEC
) ? 'X' : '-',
305 (ainfo_buf
[i
].flags
& AS_AREA_CACHEABLE
) ? 'C' : '-',
306 (void *) ainfo_buf
[i
].start_addr
, ainfo_buf
[i
].size
);
311 if (write_core_file
) {
312 printf("Writing core file '%s'\n", core_file_name
);
314 rc
= elf_core_save(core_file_name
, ainfo_buf
, n_areas
, sess
,
318 printf("Failed writing core file.\n");
328 errno_t
td_stacktrace(uintptr_t fp
, uintptr_t pc
)
337 st
.ops
= &td_stacktrace_ops
;
339 while (cnt
++ < STACK_FRAMES_MAX
&& stacktrace_fp_valid(&st
, fp
)) {
340 sym_pc
= fmt_sym_address(pc
);
341 printf(" %p: %s\n", (void *) fp
, sym_pc
);
344 rc
= stacktrace_ra_get(&st
, fp
, &pc
);
348 rc
= stacktrace_fp_prev(&st
, fp
, &nfp
);
358 static errno_t
thread_dump(uintptr_t thash
)
365 rc
= udebug_regs_read(sess
, thash
, &istate
);
367 printf("Failed reading registers: %s.\n", str_error_name(rc
));
371 pc
= istate_get_pc(&istate
);
372 fp
= istate_get_fp(&istate
);
374 /* Save register state for dumping to core file later. */
377 sym_pc
= fmt_sym_address(pc
);
378 printf("Thread %p: PC = %s. FP = %p\n", (void *) thash
,
379 sym_pc
, (void *) fp
);
382 (void) td_stacktrace(fp
, pc
);
387 static errno_t
td_read_uintptr(void *arg
, uintptr_t addr
, uintptr_t *value
)
394 rc
= udebug_mem_read(sess
, &data
, addr
, sizeof(data
));
396 printf("Warning: udebug_mem_read() failed.\n");
404 /** Attempt to find the right executable file and load the symbol table. */
405 static void autoload_syms(void)
411 assert(app_name
!= NULL
);
412 assert(app_symtab
== NULL
);
414 ret
= asprintf(&file_name
, "/app/%s", app_name
);
416 printf("Memory allocation failure.\n");
420 rc
= symtab_load(file_name
, &app_symtab
);
422 printf("Loaded symbol table from %s\n", file_name
);
429 ret
= asprintf(&file_name
, "/srv/%s", app_name
);
431 printf("Memory allocation failure.\n");
435 rc
= symtab_load(file_name
, &app_symtab
);
437 printf("Loaded symbol table from %s\n", file_name
);
442 ret
= asprintf(&file_name
, "/drv/%s/%s", app_name
, app_name
);
444 printf("Memory allocation failure.\n");
448 rc
= symtab_load(file_name
, &app_symtab
);
450 printf("Loaded symbol table from %s\n", file_name
);
456 printf("Failed autoloading symbol table.\n");
459 static char *get_app_task_name(void)
462 size_t copied
, needed
, name_size
;
466 rc
= udebug_name_read(sess
, &dummy_buf
, 0, &copied
, &needed
);
471 name
= malloc(name_size
+ 1);
472 rc
= udebug_name_read(sess
, name
, name_size
, &copied
, &needed
);
478 assert(copied
== name_size
);
479 assert(copied
== needed
);
485 /** Format address in symbolic form.
487 * Formats address as <symbol_name>+<offset> (<address>), if possible,
488 * otherwise as <address>.
490 * @param addr Address to format.
491 * @return Newly allocated string, address in symbolic form.
493 static char *fmt_sym_address(uintptr_t addr
)
501 if (app_symtab
!= NULL
) {
502 rc
= symtab_addr_to_name(app_symtab
, addr
, &name
, &offs
);
508 ret
= asprintf(&str
, "%p (%s+%zu)", (void *) addr
, name
, offs
);
510 ret
= asprintf(&str
, "%p", (void *) addr
);
514 printf("Memory allocation error.\n");