source: mainline/uspace/app/taskdump/taskdump.c@ b8b64a8

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b8b64a8 was 01900b6, checked in by Martin Decky <martin@…>, 5 years ago

Use an optional output argument instead of errno to propagate the error

The use of errno is troublesome in all other than top-level library
functions since the value in errno might get overwritten by subsequent
inner calls on the error path (e.g. cleanup, deallocation, etc.). The
optional output argument makes it possible to explicitly ignore the
error code if it is not needed, but still to pass it reliably back to
the original caller.

This change affecs async_connect_me_to(),
async_connect_me_to_blocking(), async_connect_kbox(), service_connect(),
service_connect_blocking() and loader_connect().

  • 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 errno_t rc;
129 async_sess_t *ksess = async_connect_kbox(task_id, &rc);
130
131 if (!ksess) {
132 if (rc == ENOTSUP) {
133 printf("You do not have userspace debugging support "
134 "compiled in the kernel.\n");
135 printf("Compile kernel with 'Support for userspace debuggers' "
136 "(CONFIG_UDEBUG) enabled.\n");
137 return rc;
138 }
139
140 printf("Error connecting\n");
141 printf("async_connect_kbox(%" PRIu64 ") -> %s", task_id, str_error_name(errno));
142 return rc;
143 }
144
145 rc = udebug_begin(ksess);
146 if (rc != EOK) {
147 printf("udebug_begin() -> %s\n", str_error_name(rc));
148 return rc;
149 }
150
151 sess = ksess;
152 return 0;
153}
154
155static int parse_args(int argc, char *argv[])
156{
157 char *arg;
158 char *err_p;
159
160 task_id = 0;
161
162 --argc;
163 ++argv;
164
165 while (argc > 0) {
166 arg = *argv;
167 if (arg[0] == '-') {
168 if (arg[1] == 't' && arg[2] == '\0') {
169 /* Task ID */
170 --argc;
171 ++argv;
172 task_id = strtol(*argv, &err_p, 10);
173 if (*err_p) {
174 printf("Task ID syntax error\n");
175 print_syntax();
176 return -1;
177 }
178 } else if (arg[1] == 'c' && arg[2] == '\0') {
179 write_core_file = true;
180
181 --argc;
182 ++argv;
183 core_file_name = *argv;
184 } else {
185 printf("Uknown option '%c'\n", arg[0]);
186 print_syntax();
187 return -1;
188 }
189 } else {
190 break;
191 }
192
193 --argc;
194 ++argv;
195 }
196
197 if (task_id == 0) {
198 printf("Missing task ID argument\n");
199 print_syntax();
200 return -1;
201 }
202
203 if (argc != 0) {
204 printf("Extra arguments\n");
205 print_syntax();
206 return -1;
207 }
208
209 return 0;
210}
211
212static void print_syntax(void)
213{
214 printf("Syntax: taskdump [-c <core_file>] -t <task_id>\n");
215 printf("\t-c <core_file_id>\tName of core file to write.\n");
216 printf("\t-t <task_id>\tWhich task to dump.\n");
217}
218
219static errno_t threads_dump(void)
220{
221 uintptr_t *thash_buf;
222 uintptr_t dummy_buf;
223 size_t buf_size, n_threads;
224
225 size_t copied;
226 size_t needed;
227 size_t i;
228 errno_t rc;
229
230 /* TODO: See why NULL does not work. */
231 rc = udebug_thread_read(sess, &dummy_buf, 0, &copied, &needed);
232 if (rc != EOK) {
233 printf("udebug_thread_read() -> %s\n", str_error_name(rc));
234 return rc;
235 }
236
237 if (needed == 0) {
238 printf("No threads.\n\n");
239 return 0;
240 }
241
242 buf_size = needed;
243 thash_buf = malloc(buf_size);
244
245 rc = udebug_thread_read(sess, thash_buf, buf_size, &copied, &needed);
246 if (rc != EOK) {
247 printf("udebug_thread_read() -> %s\n", str_error_name(rc));
248 return rc;
249 }
250
251 assert(copied == buf_size);
252 assert(needed == buf_size);
253
254 n_threads = copied / sizeof(uintptr_t);
255
256 printf("Threads:\n");
257 for (i = 0; i < n_threads; i++) {
258 printf(" [%zu] hash: %p\n", 1 + i, (void *) thash_buf[i]);
259
260 thread_dump(thash_buf[i]);
261 }
262 putchar('\n');
263
264 free(thash_buf);
265
266 return 0;
267}
268
269static errno_t areas_dump(void)
270{
271 as_area_info_t *ainfo_buf;
272 as_area_info_t dummy_buf;
273 size_t buf_size, n_areas;
274
275 size_t copied;
276 size_t needed;
277 size_t i;
278 errno_t rc;
279
280 rc = udebug_areas_read(sess, &dummy_buf, 0, &copied, &needed);
281 if (rc != EOK) {
282 printf("udebug_areas_read() -> %s\n", str_error_name(rc));
283 return rc;
284 }
285
286 buf_size = needed;
287 ainfo_buf = malloc(buf_size);
288
289 rc = udebug_areas_read(sess, ainfo_buf, buf_size, &copied, &needed);
290 if (rc != EOK) {
291 printf("udebug_areas_read() -> %s\n", str_error_name(rc));
292 return rc;
293 }
294
295 assert(copied == buf_size);
296 assert(needed == buf_size);
297
298 n_areas = copied / sizeof(as_area_info_t);
299
300 printf("Address space areas:\n");
301 for (i = 0; i < n_areas; i++) {
302 printf(" [%zu] flags: %c%c%c%c base: %p size: %zu\n", 1 + i,
303 (ainfo_buf[i].flags & AS_AREA_READ) ? 'R' : '-',
304 (ainfo_buf[i].flags & AS_AREA_WRITE) ? 'W' : '-',
305 (ainfo_buf[i].flags & AS_AREA_EXEC) ? 'X' : '-',
306 (ainfo_buf[i].flags & AS_AREA_CACHEABLE) ? 'C' : '-',
307 (void *) ainfo_buf[i].start_addr, ainfo_buf[i].size);
308 }
309
310 putchar('\n');
311
312 if (write_core_file) {
313 printf("Writing core file '%s'\n", core_file_name);
314
315 rc = elf_core_save(core_file_name, ainfo_buf, n_areas, sess,
316 &reg_state);
317
318 if (rc != EOK) {
319 printf("Failed writing core file.\n");
320 return EIO;
321 }
322 }
323
324 free(ainfo_buf);
325
326 return 0;
327}
328
329errno_t td_stacktrace(uintptr_t fp, uintptr_t pc)
330{
331 int cnt = 0;
332 uintptr_t nfp;
333 stacktrace_t st;
334 char *sym_pc;
335 errno_t rc;
336
337 st.op_arg = NULL;
338 st.ops = &td_stacktrace_ops;
339
340 while (cnt++ < STACK_FRAMES_MAX && stacktrace_fp_valid(&st, fp)) {
341 sym_pc = fmt_sym_address(pc);
342 printf(" %p: %s\n", (void *) fp, sym_pc);
343 free(sym_pc);
344
345 rc = stacktrace_ra_get(&st, fp, &pc);
346 if (rc != EOK)
347 return rc;
348
349 rc = stacktrace_fp_prev(&st, fp, &nfp);
350 if (rc != EOK)
351 return rc;
352
353 fp = nfp;
354 }
355
356 return EOK;
357}
358
359static errno_t thread_dump(uintptr_t thash)
360{
361 istate_t istate;
362 uintptr_t pc, fp;
363 char *sym_pc;
364 errno_t rc;
365
366 rc = udebug_regs_read(sess, thash, &istate);
367 if (rc != EOK) {
368 printf("Failed reading registers: %s.\n", str_error_name(rc));
369 return EIO;
370 }
371
372 pc = istate_get_pc(&istate);
373 fp = istate_get_fp(&istate);
374
375 /* Save register state for dumping to core file later. */
376 reg_state = istate;
377
378 sym_pc = fmt_sym_address(pc);
379 printf("Thread %p: PC = %s. FP = %p\n", (void *) thash,
380 sym_pc, (void *) fp);
381 free(sym_pc);
382
383 (void) td_stacktrace(fp, pc);
384
385 return EOK;
386}
387
388static errno_t td_read_uintptr(void *arg, uintptr_t addr, uintptr_t *value)
389{
390 uintptr_t data;
391 errno_t rc;
392
393 (void) arg;
394
395 rc = udebug_mem_read(sess, &data, addr, sizeof(data));
396 if (rc != EOK) {
397 printf("Warning: udebug_mem_read() failed.\n");
398 return rc;
399 }
400
401 *value = data;
402 return EOK;
403}
404
405/** Attempt to find the right executable file and load the symbol table. */
406static void autoload_syms(void)
407{
408 assert(app_name != NULL);
409 assert(app_symtab == NULL);
410
411 if (app_name[0] != '/') {
412 printf("Task name is not path. Can't autoload symbol table.\n");
413 return;
414 }
415
416 errno_t rc = symtab_load(app_name, &app_symtab);
417 if (rc != EOK) {
418 printf("Failed autoloading symbol table: %s\n",
419 str_error_name(rc));
420 return;
421 }
422
423 printf("Loaded symbol table from %s\n", app_name);
424}
425
426static char *get_app_task_name(void)
427{
428 char dummy_buf;
429 size_t copied, needed, name_size;
430 char *name;
431 errno_t rc;
432
433 rc = udebug_name_read(sess, &dummy_buf, 0, &copied, &needed);
434 if (rc != EOK)
435 return NULL;
436
437 name_size = needed;
438 name = malloc(name_size + 1);
439 rc = udebug_name_read(sess, name, name_size, &copied, &needed);
440 if (rc != EOK) {
441 free(name);
442 return NULL;
443 }
444
445 assert(copied == name_size);
446 assert(copied == needed);
447 name[copied] = '\0';
448
449 return name;
450}
451
452/** Format address in symbolic form.
453 *
454 * Formats address as <symbol_name>+<offset> (<address>), if possible,
455 * otherwise as <address>.
456 *
457 * @param addr Address to format.
458 * @return Newly allocated string, address in symbolic form.
459 */
460static char *fmt_sym_address(uintptr_t addr)
461{
462 char *name;
463 size_t offs;
464 errno_t rc;
465 int ret;
466 char *str;
467
468 if (app_symtab != NULL) {
469 rc = symtab_addr_to_name(app_symtab, addr, &name, &offs);
470 } else {
471 rc = ENOTSUP;
472 }
473
474 if (rc == EOK) {
475 ret = asprintf(&str, "%p (%s+%zu)", (void *) addr, name, offs);
476 } else {
477 ret = asprintf(&str, "%p", (void *) addr);
478 }
479
480 if (ret < 0) {
481 printf("Memory allocation error.\n");
482 exit(1);
483 }
484
485 return str;
486}
487
488/** @}
489 */
Note: See TracBrowser for help on using the repository browser.