source: mainline/uspace/app/taskdump/taskdump.c@ 57977e4

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 57977e4 was 9286475, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Set task name to full path instead of just the base name

This eliminates the guesswork it takes to determine path to the binary.
It is a stopgap measure. Eventually we want a better solution.

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/*
2 * Copyright (c) 2010 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
16 *
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.
27 */
28
29/** @addtogroup taskdump
30 * @{
31 */
32/** @file
33 */
34
35#include <async.h>
36#include <elf/elf_linux.h>
37#include <fibrildump.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <stddef.h>
41#include <stdbool.h>
42#include <str_error.h>
43#include <errno.h>
44#include <udebug.h>
45#include <task.h>
46#include <as.h>
47#include <libarch/istate.h>
48#include <macros.h>
49#include <assert.h>
50#include <str.h>
51
52#include <symtab.h>
53#include <elf_core.h>
54#include <stacktrace.h>
55#include <taskdump.h>
56
57#define LINE_BYTES 16
58#define STACK_FRAMES_MAX 20
59
60static async_sess_t *sess;
61static task_id_t task_id;
62static bool write_core_file;
63static char *core_file_name;
64static char *app_name;
65static symtab_t *app_symtab;
66
67static errno_t connect_task(task_id_t task_id);
68static int parse_args(int argc, char *argv[]);
69static void print_syntax(void);
70static errno_t threads_dump(void);
71static errno_t thread_dump(uintptr_t thash);
72static errno_t areas_dump(void);
73static errno_t td_read_uintptr(void *arg, uintptr_t addr, uintptr_t *value);
74
75static void autoload_syms(void);
76static char *get_app_task_name(void);
77static char *fmt_sym_address(uintptr_t addr);
78
79static istate_t reg_state;
80
81static stacktrace_ops_t td_stacktrace_ops = {
82 .read_uintptr = td_read_uintptr
83};
84
85int main(int argc, char *argv[])
86{
87 errno_t rc;
88
89 printf("Task Dump Utility\n");
90 write_core_file = false;
91
92 if (parse_args(argc, argv) < 0)
93 return 1;
94
95 rc = connect_task(task_id);
96 if (rc != EOK) {
97 printf("Failed connecting to task %" PRIu64 ".\n", task_id);
98 return 1;
99 }
100
101 app_name = get_app_task_name();
102 app_symtab = NULL;
103
104 printf("Dumping task '%s' (task ID %" PRIu64 ").\n", app_name, task_id);
105 autoload_syms();
106 putchar('\n');
107
108 rc = threads_dump();
109 if (rc != EOK)
110 printf("Failed dumping threads.\n");
111
112 rc = areas_dump();
113 if (rc != EOK)
114 printf("Failed dumping address space areas.\n");
115
116 rc = fibrils_dump(app_symtab, sess);
117 if (rc != EOK)
118 printf("Failed dumping fibrils.\n");
119
120 udebug_end(sess);
121 async_hangup(sess);
122
123 return 0;
124}
125
126static errno_t connect_task(task_id_t task_id)
127{
128 async_sess_t *ksess = async_connect_kbox(task_id);
129
130 if (!ksess) {
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");
136 return errno;
137 }
138
139 printf("Error connecting\n");
140 printf("async_connect_kbox(%" PRIu64 ") -> %s", task_id, str_error_name(errno));
141 return errno;
142 }
143
144 errno_t rc = udebug_begin(ksess);
145 if (rc != EOK) {
146 printf("udebug_begin() -> %s\n", str_error_name(rc));
147 return rc;
148 }
149
150 sess = ksess;
151 return 0;
152}
153
154static int parse_args(int argc, char *argv[])
155{
156 char *arg;
157 char *err_p;
158
159 task_id = 0;
160
161 --argc;
162 ++argv;
163
164 while (argc > 0) {
165 arg = *argv;
166 if (arg[0] == '-') {
167 if (arg[1] == 't' && arg[2] == '\0') {
168 /* Task ID */
169 --argc;
170 ++argv;
171 task_id = strtol(*argv, &err_p, 10);
172 if (*err_p) {
173 printf("Task ID syntax error\n");
174 print_syntax();
175 return -1;
176 }
177 } else if (arg[1] == 'c' && arg[2] == '\0') {
178 write_core_file = true;
179
180 --argc;
181 ++argv;
182 core_file_name = *argv;
183 } else {
184 printf("Uknown option '%c'\n", arg[0]);
185 print_syntax();
186 return -1;
187 }
188 } else {
189 break;
190 }
191
192 --argc;
193 ++argv;
194 }
195
196 if (task_id == 0) {
197 printf("Missing task ID argument\n");
198 print_syntax();
199 return -1;
200 }
201
202 if (argc != 0) {
203 printf("Extra arguments\n");
204 print_syntax();
205 return -1;
206 }
207
208 return 0;
209}
210
211static void print_syntax(void)
212{
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");
216}
217
218static errno_t threads_dump(void)
219{
220 uintptr_t *thash_buf;
221 uintptr_t dummy_buf;
222 size_t buf_size, n_threads;
223
224 size_t copied;
225 size_t needed;
226 size_t i;
227 errno_t rc;
228
229 /* TODO: See why NULL does not work. */
230 rc = udebug_thread_read(sess, &dummy_buf, 0, &copied, &needed);
231 if (rc != EOK) {
232 printf("udebug_thread_read() -> %s\n", str_error_name(rc));
233 return rc;
234 }
235
236 if (needed == 0) {
237 printf("No threads.\n\n");
238 return 0;
239 }
240
241 buf_size = needed;
242 thash_buf = malloc(buf_size);
243
244 rc = udebug_thread_read(sess, thash_buf, buf_size, &copied, &needed);
245 if (rc != EOK) {
246 printf("udebug_thread_read() -> %s\n", str_error_name(rc));
247 return rc;
248 }
249
250 assert(copied == buf_size);
251 assert(needed == buf_size);
252
253 n_threads = copied / sizeof(uintptr_t);
254
255 printf("Threads:\n");
256 for (i = 0; i < n_threads; i++) {
257 printf(" [%zu] hash: %p\n", 1 + i, (void *) thash_buf[i]);
258
259 thread_dump(thash_buf[i]);
260 }
261 putchar('\n');
262
263 free(thash_buf);
264
265 return 0;
266}
267
268static errno_t areas_dump(void)
269{
270 as_area_info_t *ainfo_buf;
271 as_area_info_t dummy_buf;
272 size_t buf_size, n_areas;
273
274 size_t copied;
275 size_t needed;
276 size_t i;
277 errno_t rc;
278
279 rc = udebug_areas_read(sess, &dummy_buf, 0, &copied, &needed);
280 if (rc != EOK) {
281 printf("udebug_areas_read() -> %s\n", str_error_name(rc));
282 return rc;
283 }
284
285 buf_size = needed;
286 ainfo_buf = malloc(buf_size);
287
288 rc = udebug_areas_read(sess, ainfo_buf, buf_size, &copied, &needed);
289 if (rc != EOK) {
290 printf("udebug_areas_read() -> %s\n", str_error_name(rc));
291 return rc;
292 }
293
294 assert(copied == buf_size);
295 assert(needed == buf_size);
296
297 n_areas = copied / sizeof(as_area_info_t);
298
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);
307 }
308
309 putchar('\n');
310
311 if (write_core_file) {
312 printf("Writing core file '%s'\n", core_file_name);
313
314 rc = elf_core_save(core_file_name, ainfo_buf, n_areas, sess,
315 &reg_state);
316
317 if (rc != EOK) {
318 printf("Failed writing core file.\n");
319 return EIO;
320 }
321 }
322
323 free(ainfo_buf);
324
325 return 0;
326}
327
328errno_t td_stacktrace(uintptr_t fp, uintptr_t pc)
329{
330 int cnt = 0;
331 uintptr_t nfp;
332 stacktrace_t st;
333 char *sym_pc;
334 errno_t rc;
335
336 st.op_arg = NULL;
337 st.ops = &td_stacktrace_ops;
338
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);
342 free(sym_pc);
343
344 rc = stacktrace_ra_get(&st, fp, &pc);
345 if (rc != EOK)
346 return rc;
347
348 rc = stacktrace_fp_prev(&st, fp, &nfp);
349 if (rc != EOK)
350 return rc;
351
352 fp = nfp;
353 }
354
355 return EOK;
356}
357
358static errno_t thread_dump(uintptr_t thash)
359{
360 istate_t istate;
361 uintptr_t pc, fp;
362 char *sym_pc;
363 errno_t rc;
364
365 rc = udebug_regs_read(sess, thash, &istate);
366 if (rc != EOK) {
367 printf("Failed reading registers: %s.\n", str_error_name(rc));
368 return EIO;
369 }
370
371 pc = istate_get_pc(&istate);
372 fp = istate_get_fp(&istate);
373
374 /* Save register state for dumping to core file later. */
375 reg_state = istate;
376
377 sym_pc = fmt_sym_address(pc);
378 printf("Thread %p: PC = %s. FP = %p\n", (void *) thash,
379 sym_pc, (void *) fp);
380 free(sym_pc);
381
382 (void) td_stacktrace(fp, pc);
383
384 return EOK;
385}
386
387static errno_t td_read_uintptr(void *arg, uintptr_t addr, uintptr_t *value)
388{
389 uintptr_t data;
390 errno_t rc;
391
392 (void) arg;
393
394 rc = udebug_mem_read(sess, &data, addr, sizeof(data));
395 if (rc != EOK) {
396 printf("Warning: udebug_mem_read() failed.\n");
397 return rc;
398 }
399
400 *value = data;
401 return EOK;
402}
403
404/** Attempt to find the right executable file and load the symbol table. */
405static void autoload_syms(void)
406{
407 assert(app_name != NULL);
408 assert(app_symtab == NULL);
409
410 if (app_name[0] != '/') {
411 printf("Task name is not path. Can't autoload symbol table.\n");
412 return;
413 }
414
415 errno_t rc = symtab_load(app_name, &app_symtab);
416 if (rc != EOK) {
417 printf("Failed autoloading symbol table: %s\n",
418 str_error_name(rc));
419 return;
420 }
421
422 printf("Loaded symbol table from %s\n", app_name);
423}
424
425static char *get_app_task_name(void)
426{
427 char dummy_buf;
428 size_t copied, needed, name_size;
429 char *name;
430 errno_t rc;
431
432 rc = udebug_name_read(sess, &dummy_buf, 0, &copied, &needed);
433 if (rc != EOK)
434 return NULL;
435
436 name_size = needed;
437 name = malloc(name_size + 1);
438 rc = udebug_name_read(sess, name, name_size, &copied, &needed);
439 if (rc != EOK) {
440 free(name);
441 return NULL;
442 }
443
444 assert(copied == name_size);
445 assert(copied == needed);
446 name[copied] = '\0';
447
448 return name;
449}
450
451/** Format address in symbolic form.
452 *
453 * Formats address as <symbol_name>+<offset> (<address>), if possible,
454 * otherwise as <address>.
455 *
456 * @param addr Address to format.
457 * @return Newly allocated string, address in symbolic form.
458 */
459static char *fmt_sym_address(uintptr_t addr)
460{
461 char *name;
462 size_t offs;
463 errno_t rc;
464 int ret;
465 char *str;
466
467 if (app_symtab != NULL) {
468 rc = symtab_addr_to_name(app_symtab, addr, &name, &offs);
469 } else {
470 rc = ENOTSUP;
471 }
472
473 if (rc == EOK) {
474 ret = asprintf(&str, "%p (%s+%zu)", (void *) addr, name, offs);
475 } else {
476 ret = asprintf(&str, "%p", (void *) addr);
477 }
478
479 if (ret < 0) {
480 printf("Memory allocation error.\n");
481 exit(1);
482 }
483
484 return str;
485}
486
487/** @}
488 */
Note: See TracBrowser for help on using the repository browser.