source: mainline/uspace/app/taskdump/taskdump.c@ 19f24fd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 19f24fd was 1ccafee, checked in by Jiri Svoboda <jiri@…>, 15 years ago

Add type formatting macros for HelenOS system types (sys/typefmt.h) and use in appropriate places.

  • Property mode set to 100644
File size: 10.9 KB
RevLine 
[336db295]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 <stdio.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <ipc/ipc.h>
39#include <errno.h>
40#include <udebug.h>
41#include <task.h>
42#include <kernel/mm/as.h>
[1ccafee]43#include <sys/types.h>
44#include <sys/typefmt.h>
[80487bc5]45#include <libarch/istate.h>
[336db295]46#include <macros.h>
47#include <assert.h>
48#include <bool.h>
49
[3698e44]50#include <symtab.h>
[e515b21a]51#include <stacktrace.h>
52
[336db295]53#define LINE_BYTES 16
54
55#define DBUF_SIZE 4096
56static uint8_t data_buf[DBUF_SIZE];
57
58static int phoneid;
59static task_id_t task_id;
60static bool dump_memory;
[3698e44]61static char *app_name;
62static symtab_t *app_symtab;
[336db295]63
64static int connect_task(task_id_t task_id);
65static int parse_args(int argc, char *argv[]);
[e515b21a]66static void print_syntax(void);
[336db295]67static int threads_dump(void);
[80487bc5]68static int thread_dump(uintptr_t thash);
[336db295]69static int areas_dump(void);
70static int area_dump(as_area_info_t *area);
71static void hex_dump(uintptr_t addr, void *buffer, size_t size);
[e515b21a]72static int td_read_uintptr(void *arg, uintptr_t addr, uintptr_t *value);
[336db295]73
[3698e44]74static void autoload_syms(void);
75static char *get_app_task_name(void);
76static char *fmt_sym_address(uintptr_t addr);
77
[336db295]78int main(int argc, char *argv[])
79{
80 int rc;
81
[bc310a05]82 /*
83 * FIXME: The stdio module cannot currently detect whether we are
84 * writing to a console or file. This workaround make file output
85 * faster.
86 */
87 setvbuf(stdout, NULL, _IOFBF, 32768);
88
[336db295]89 printf("Task Dump Utility\n");
90 dump_memory = false;
91
92 if (parse_args(argc, argv) < 0)
93 return 1;
94
95 rc = connect_task(task_id);
96 if (rc < 0) {
[1ccafee]97 printf("Failed connecting to task %" PRIdTASKID ".\n", task_id);
[336db295]98 return 1;
99 }
100
[3698e44]101 app_name = get_app_task_name();
102 app_symtab = NULL;
103
[1ccafee]104 printf("Dumping task '%s' (task ID %" PRIdTASKID ").\n", app_name, task_id);
[3698e44]105 autoload_syms();
106 putchar('\n');
[336db295]107
108 rc = threads_dump();
109 if (rc < 0)
110 printf("Failed dumping threads.\n");
111
112 rc = areas_dump();
113 if (rc < 0)
114 printf("Failed dumping address space areas.\n");
115
116 udebug_end(phoneid);
117 ipc_hangup(phoneid);
118
119 return 0;
120}
121
122static int connect_task(task_id_t task_id)
123{
124 int rc;
125
126 rc = ipc_connect_kbox(task_id);
127
128 if (rc == ENOTSUP) {
129 printf("You do not have userspace debugging support "
130 "compiled in the kernel.\n");
131 printf("Compile kernel with 'Support for userspace debuggers' "
132 "(CONFIG_UDEBUG) enabled.\n");
133 return rc;
134 }
135
136 if (rc < 0) {
137 printf("Error connecting\n");
[1ccafee]138 printf("ipc_connect_task(%" PRIdTASKID ") -> %d ", task_id, rc);
[336db295]139 return rc;
140 }
141
142 phoneid = rc;
143
144 rc = udebug_begin(phoneid);
145 if (rc < 0) {
146 printf("udebug_begin() -> %d\n", rc);
147 return rc;
148 }
149
150 return 0;
151}
152
153static int parse_args(int argc, char *argv[])
154{
155 char *arg;
156 char *err_p;
157
158 task_id = 0;
159
160 --argc; ++argv;
161
162 while (argc > 0) {
163 arg = *argv;
164 if (arg[0] == '-') {
165 if (arg[1] == 't' && arg[2] == '\0') {
166 /* Task ID */
167 --argc; ++argv;
168 task_id = strtol(*argv, &err_p, 10);
169 if (*err_p) {
170 printf("Task ID syntax error\n");
171 print_syntax();
172 return -1;
173 }
174 } else if (arg[1] == 'm' && arg[2] == '\0') {
175 dump_memory = true;
176 } else {
177 printf("Uknown option '%s'\n", arg[0]);
178 print_syntax();
179 return -1;
180 }
181 } else {
182 break;
183 }
184
185 --argc; ++argv;
186 }
187
188 if (task_id == 0) {
189 printf("Missing task ID argument\n");
190 print_syntax();
191 return -1;
192 }
193
194 if (argc != 0) {
195 printf("Extra arguments\n");
196 print_syntax();
197 return -1;
198 }
199
200 return 0;
201}
202
[e515b21a]203static void print_syntax(void)
[336db295]204{
205 printf("Syntax: taskdump [-m] -t <task_id>\n");
206 printf("\t-m\tDump memory area contents.\n");
207 printf("\t-t <task_id>\tWhich task to dump.\n");
208}
209
210static int threads_dump(void)
211{
212 uintptr_t *thash_buf;
213 uintptr_t dummy_buf;
214 size_t buf_size, n_threads;
215
216 size_t copied;
217 size_t needed;
218 size_t i;
219 int rc;
220
221 /* TODO: See why NULL does not work. */
222 rc = udebug_thread_read(phoneid, &dummy_buf, 0, &copied, &needed);
223 if (rc < 0) {
224 printf("udebug_thread_read() -> %d\n", rc);
225 return rc;
226 }
227
228 if (needed == 0) {
229 printf("No threads.\n\n");
230 return 0;
231 }
232
233 buf_size = needed;
234 thash_buf = malloc(buf_size);
235
236 rc = udebug_thread_read(phoneid, thash_buf, buf_size, &copied, &needed);
237 if (rc < 0) {
238 printf("udebug_thread_read() -> %d\n", rc);
239 return rc;
240 }
241
242 assert(copied == buf_size);
243 assert(needed == buf_size);
244
245 n_threads = copied / sizeof(uintptr_t);
246
247 printf("Threads:\n");
248 for (i = 0; i < n_threads; i++) {
[fb6f1a5]249 printf(" [%d] hash: %p\n", 1+i, thash_buf[i]);
[80487bc5]250
251 thread_dump(thash_buf[i]);
[336db295]252 }
253 putchar('\n');
254
255 free(thash_buf);
256
257 return 0;
258}
259
260static int areas_dump(void)
261{
262 as_area_info_t *ainfo_buf;
263 as_area_info_t dummy_buf;
264 size_t buf_size, n_areas;
265
266 size_t copied;
267 size_t needed;
268 size_t i;
269 int rc;
270
271 rc = udebug_areas_read(phoneid, &dummy_buf, 0, &copied, &needed);
272 if (rc < 0) {
273 printf("udebug_areas_read() -> %d\n", rc);
274 return rc;
275 }
276
277 buf_size = needed;
278 ainfo_buf = malloc(buf_size);
279
280 rc = udebug_areas_read(phoneid, ainfo_buf, buf_size, &copied, &needed);
281 if (rc < 0) {
282 printf("udebug_areas_read() -> %d\n", rc);
283 return rc;
284 }
285
286 assert(copied == buf_size);
287 assert(needed == buf_size);
288
289 n_areas = copied / sizeof(as_area_info_t);
290
291 printf("Address space areas:\n");
292 for (i = 0; i < n_areas; i++) {
[fb6f1a5]293 printf(" [%d] flags: %c%c%c%c base: %p size: %p\n", 1+i,
[336db295]294 (ainfo_buf[i].flags & AS_AREA_READ) ? 'R' : '-',
295 (ainfo_buf[i].flags & AS_AREA_WRITE) ? 'W' : '-',
296 (ainfo_buf[i].flags & AS_AREA_EXEC) ? 'X' : '-',
297 (ainfo_buf[i].flags & AS_AREA_CACHEABLE) ? 'C' : '-',
298 ainfo_buf[i].start_addr, ainfo_buf[i].size);
299
300 if (dump_memory) {
301 putchar('\n');
302 area_dump(&ainfo_buf[i]);
303 putchar('\n');
304 }
305 }
306
307 putchar('\n');
308
309 free(ainfo_buf);
310
311 return 0;
312}
313
[80487bc5]314static int thread_dump(uintptr_t thash)
315{
316 istate_t istate;
[e515b21a]317 uintptr_t pc, fp, nfp;
318 stacktrace_t st;
[3698e44]319 char *sym_pc;
[80487bc5]320 int rc;
321
322 rc = udebug_regs_read(phoneid, thash, &istate);
323 if (rc < 0) {
324 printf("Failed reading registers (%d).\n", rc);
325 return EIO;
326 }
327
328 pc = istate_get_pc(&istate);
329 fp = istate_get_fp(&istate);
330
[196a1439]331 sym_pc = fmt_sym_address(pc);
[fb6f1a5]332 printf("Thread %p crashed at %s. FP = %p\n", thash, sym_pc, fp);
[196a1439]333 free(sym_pc);
[e515b21a]334
335 st.op_arg = NULL;
336 st.read_uintptr = td_read_uintptr;
337
338 while (stacktrace_fp_valid(&st, fp)) {
[3698e44]339 sym_pc = fmt_sym_address(pc);
[196a1439]340 printf(" %p: %s\n", fp, sym_pc);
[3698e44]341 free(sym_pc);
[e515b21a]342
343 rc = stacktrace_ra_get(&st, fp, &pc);
344 if (rc != EOK)
345 return rc;
346
347 rc = stacktrace_fp_prev(&st, fp, &nfp);
348 if (rc != EOK)
349 return rc;
350
351 fp = nfp;
352 }
[80487bc5]353
354 return EOK;
355}
356
[336db295]357static int area_dump(as_area_info_t *area)
358{
359 size_t to_copy;
360 size_t total;
361 uintptr_t addr;
362 int rc;
363
364 addr = area->start_addr;
365 total = 0;
366
367 while (total < area->size) {
368 to_copy = min(area->size - total, DBUF_SIZE);
369 rc = udebug_mem_read(phoneid, data_buf, addr, to_copy);
370 if (rc < 0) {
371 printf("udebug_mem_read() failed.\n");
372 return rc;
373 }
374
375 hex_dump(addr, data_buf, to_copy);
376
377 addr += to_copy;
378 total += to_copy;
379 }
380
381 return EOK;
382}
383
384static void hex_dump(uintptr_t addr, void *buffer, size_t size)
385{
386 uint8_t *data = (uint8_t *) buffer;
387 uint8_t b;
388 size_t pos, i;
389
390 assert(addr % LINE_BYTES == 0);
391 assert(size % LINE_BYTES == 0);
392
393 pos = 0;
394
395 while (pos < size) {
[196a1439]396 printf("%08lx:", addr + pos);
[336db295]397 for (i = 0; i < LINE_BYTES; ++i) {
398 if (i % 4 == 0) putchar(' ');
399 printf(" %02x", data[pos + i]);
400 }
401 putchar('\t');
402
403 for (i = 0; i < LINE_BYTES; ++i) {
404 b = data[pos + i];
405 if (b >= 32 && b < 127) {
406 putchar(b);
407 } else {
408 putchar(' ');
409 }
410 }
411 putchar('\n');
412 pos += LINE_BYTES;
413 }
414}
415
[e515b21a]416static int td_read_uintptr(void *arg, uintptr_t addr, uintptr_t *value)
417{
418 uintptr_t data;
419 int rc;
420
421 (void) arg;
422
423 rc = udebug_mem_read(phoneid, &data, addr, sizeof(data));
424 if (rc < 0) {
425 printf("Warning: udebug_mem_read() failed.\n");
426 return rc;
427 }
428
429 *value = data;
430 return EOK;
431}
432
[3698e44]433/** Attempt to find the right executable file and load the symbol table. */
434static void autoload_syms(void)
435{
436 char *file_name;
437 int rc;
438
439 assert(app_name != NULL);
440 assert(app_symtab == NULL);
441
442 rc = asprintf(&file_name, "/app/%s", app_name);
443 if (rc < 0) {
444 printf("Memory allocation failure.\n");
445 exit(1);
446 }
447
448 rc = symtab_load(file_name, &app_symtab);
449 if (rc == EOK) {
450 printf("Loaded symbol table from %s\n", file_name);
451 free(file_name);
452 return;
453 }
454
455 free(file_name);
456
457 rc = asprintf(&file_name, "/srv/%s", app_name);
458 if (rc < 0) {
459 printf("Memory allocation failure.\n");
460 exit(1);
461 }
462
[196a1439]463 rc = symtab_load(file_name, &app_symtab);
[3698e44]464 if (rc == EOK) {
465 printf("Loaded symbol table from %s\n", file_name);
466 free(file_name);
467 return;
468 }
469
470 free(file_name);
471 printf("Failed autoloading symbol table.\n");
472}
473
474static char *get_app_task_name(void)
475{
476 char dummy_buf;
477 size_t copied, needed, name_size;
478 char *name;
479 int rc;
480
481 rc = udebug_name_read(phoneid, &dummy_buf, 0, &copied, &needed);
482 if (rc < 0)
483 return NULL;
484
485 name_size = needed;
486 name = malloc(name_size + 1);
487 rc = udebug_name_read(phoneid, name, name_size, &copied, &needed);
488 if (rc < 0) {
489 free(name);
490 return NULL;
491 }
492
493 assert(copied == name_size);
494 assert(copied == needed);
495 name[copied] = '\0';
496
497 return name;
498}
499
500/** Format address in symbolic form.
501 *
502 * Formats address as <symbol_name>+<offset> (<address>), if possible,
503 * otherwise as <address>.
504 *
505 * @param addr Address to format.
506 * @return Newly allocated string, address in symbolic form.
507 */
508static char *fmt_sym_address(uintptr_t addr)
509{
510 char *name;
511 size_t offs;
512 int rc;
513 char *str;
514
515 if (app_symtab != NULL) {
516 rc = symtab_addr_to_name(app_symtab, addr, &name, &offs);
517 } else {
518 rc = ENOTSUP;
519 }
520
521 if (rc == EOK) {
[196a1439]522 rc = asprintf(&str, "%p (%s+%p)", addr, name, offs);
[3698e44]523 } else {
524 rc = asprintf(&str, "%p", addr);
525 }
526
527 if (rc < 0) {
528 printf("Memory allocation error.\n");
529 exit(1);
530 }
531
532 return str;
533}
534
[336db295]535/** @}
536 */
Note: See TracBrowser for help on using the repository browser.