taskdump.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00035 #include <async.h>
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 #include <errno.h>
00040 #include <udebug.h>
00041 #include <task.h>
00042 #include <as.h>
00043 #include <sys/types.h>
00044 #include <sys/typefmt.h>
00045 #include <libarch/istate.h>
00046 #include <macros.h>
00047 #include <assert.h>
00048 #include <bool.h>
00049 
00050 #include <symtab.h>
00051 #include <elf_core.h>
00052 #include <stacktrace.h>
00053 
00054 #define LINE_BYTES 16
00055 
00056 static int phoneid;
00057 static task_id_t task_id;
00058 static bool write_core_file;
00059 static char *core_file_name;
00060 static char *app_name;
00061 static symtab_t *app_symtab;
00062 
00063 static int connect_task(task_id_t task_id);
00064 static int parse_args(int argc, char *argv[]);
00065 static void print_syntax(void);
00066 static int threads_dump(void);
00067 static int thread_dump(uintptr_t thash);
00068 static int areas_dump(void);
00069 static int td_read_uintptr(void *arg, uintptr_t addr, uintptr_t *value);
00070 
00071 static void autoload_syms(void);
00072 static char *get_app_task_name(void);
00073 static char *fmt_sym_address(uintptr_t addr);
00074 
00075 int main(int argc, char *argv[])
00076 {
00077         int rc;
00078 
00079         printf("Task Dump Utility\n");
00080         write_core_file = false;
00081 
00082         if (parse_args(argc, argv) < 0)
00083                 return 1;
00084 
00085         rc = connect_task(task_id);
00086         if (rc < 0) {
00087                 printf("Failed connecting to task %" PRIu64 ".\n", task_id);
00088                 return 1;
00089         }
00090 
00091         app_name = get_app_task_name();
00092         app_symtab = NULL;
00093 
00094         printf("Dumping task '%s' (task ID %" PRIu64 ").\n", app_name, task_id);
00095         autoload_syms();
00096         putchar('\n');
00097 
00098         rc = threads_dump();
00099         if (rc < 0)
00100                 printf("Failed dumping threads.\n");
00101 
00102         rc = areas_dump();
00103         if (rc < 0)
00104                 printf("Failed dumping address space areas.\n");
00105 
00106         udebug_end(phoneid);
00107         async_hangup(phoneid);
00108 
00109         return 0;
00110 }
00111 
00112 static int connect_task(task_id_t task_id)
00113 {
00114         int rc;
00115 
00116         rc = async_connect_kbox(task_id);
00117 
00118         if (rc == ENOTSUP) {
00119                 printf("You do not have userspace debugging support "
00120                     "compiled in the kernel.\n");
00121                 printf("Compile kernel with 'Support for userspace debuggers' "
00122                     "(CONFIG_UDEBUG) enabled.\n");
00123                 return rc;
00124         }
00125 
00126         if (rc < 0) {
00127                 printf("Error connecting\n");
00128                 printf("async_connect_kbox(%" PRIu64 ") -> %d ", task_id, rc);
00129                 return rc;
00130         }
00131 
00132         phoneid = rc;
00133 
00134         rc = udebug_begin(phoneid);
00135         if (rc < 0) {
00136                 printf("udebug_begin() -> %d\n", rc);
00137                 return rc;
00138         }
00139 
00140         return 0;
00141 }
00142 
00143 static int parse_args(int argc, char *argv[])
00144 {
00145         char *arg;
00146         char *err_p;
00147 
00148         task_id = 0;
00149 
00150         --argc; ++argv;
00151 
00152         while (argc > 0) {
00153                 arg = *argv;
00154                 if (arg[0] == '-') {
00155                         if (arg[1] == 't' && arg[2] == '\0') {
00156                                 /* Task ID */
00157                                 --argc; ++argv;
00158                                 task_id = strtol(*argv, &err_p, 10);
00159                                 if (*err_p) {
00160                                         printf("Task ID syntax error\n");
00161                                         print_syntax();
00162                                         return -1;
00163                                 }
00164                         } else if (arg[1] == 'c' && arg[2] == '\0') {
00165                                 write_core_file = true;
00166 
00167                                 --argc; ++argv;
00168                                 core_file_name = *argv;
00169                         } else {
00170                                 printf("Uknown option '%c'\n", arg[0]);
00171                                 print_syntax();
00172                                 return -1;
00173                         }
00174                 } else {
00175                         break;
00176                 }
00177 
00178                 --argc; ++argv;
00179         }
00180 
00181         if (task_id == 0) {
00182                 printf("Missing task ID argument\n");
00183                 print_syntax();
00184                 return -1;
00185         }
00186 
00187         if (argc != 0) {
00188                 printf("Extra arguments\n");
00189                 print_syntax();
00190                 return -1;
00191         }
00192 
00193         return 0;
00194 }
00195 
00196 static void print_syntax(void)
00197 {
00198         printf("Syntax: taskdump [-c <core_file>] -t <task_id>\n");
00199         printf("\t-c <core_file_id>\tName of core file to write.\n");
00200         printf("\t-t <task_id>\tWhich task to dump.\n");
00201 }
00202 
00203 static int threads_dump(void)
00204 {
00205         uintptr_t *thash_buf;
00206         uintptr_t dummy_buf;
00207         size_t buf_size, n_threads;
00208 
00209         size_t copied;
00210         size_t needed;
00211         size_t i;
00212         int rc;
00213 
00214         /* TODO: See why NULL does not work. */
00215         rc = udebug_thread_read(phoneid, &dummy_buf, 0, &copied, &needed);
00216         if (rc < 0) {
00217                 printf("udebug_thread_read() -> %d\n", rc);
00218                 return rc;
00219         }
00220 
00221         if (needed == 0) {
00222                 printf("No threads.\n\n");
00223                 return 0;
00224         }
00225 
00226         buf_size = needed;
00227         thash_buf = malloc(buf_size);
00228 
00229         rc = udebug_thread_read(phoneid, thash_buf, buf_size, &copied, &needed);
00230         if (rc < 0) {
00231                 printf("udebug_thread_read() -> %d\n", rc);
00232                 return rc;
00233         }
00234 
00235         assert(copied == buf_size);
00236         assert(needed == buf_size);
00237 
00238         n_threads = copied / sizeof(uintptr_t);
00239 
00240         printf("Threads:\n");
00241         for (i = 0; i < n_threads; i++) {
00242                 printf(" [%zu] hash: %p\n", 1 + i, (void *) thash_buf[i]);
00243 
00244                 thread_dump(thash_buf[i]);
00245         }
00246         putchar('\n');
00247 
00248         free(thash_buf);
00249 
00250         return 0;
00251 }
00252 
00253 static int areas_dump(void)
00254 {
00255         as_area_info_t *ainfo_buf;
00256         as_area_info_t dummy_buf;
00257         size_t buf_size, n_areas;
00258 
00259         size_t copied;
00260         size_t needed;
00261         size_t i;
00262         int rc;
00263 
00264         rc = udebug_areas_read(phoneid, &dummy_buf, 0, &copied, &needed);
00265         if (rc < 0) {
00266                 printf("udebug_areas_read() -> %d\n", rc);
00267                 return rc;
00268         }
00269 
00270         buf_size = needed;
00271         ainfo_buf = malloc(buf_size);
00272 
00273         rc = udebug_areas_read(phoneid, ainfo_buf, buf_size, &copied, &needed);
00274         if (rc < 0) {
00275                 printf("udebug_areas_read() -> %d\n", rc);
00276                 return rc;
00277         }
00278 
00279         assert(copied == buf_size);
00280         assert(needed == buf_size);
00281 
00282         n_areas = copied / sizeof(as_area_info_t);
00283 
00284         printf("Address space areas:\n");
00285         for (i = 0; i < n_areas; i++) {
00286                 printf(" [%zu] flags: %c%c%c%c base: %p size: %zu\n", 1 + i,
00287                     (ainfo_buf[i].flags & AS_AREA_READ) ? 'R' : '-',
00288                     (ainfo_buf[i].flags & AS_AREA_WRITE) ? 'W' : '-',
00289                     (ainfo_buf[i].flags & AS_AREA_EXEC) ? 'X' : '-',
00290                     (ainfo_buf[i].flags & AS_AREA_CACHEABLE) ? 'C' : '-',
00291                     (void *) ainfo_buf[i].start_addr, ainfo_buf[i].size);
00292         }
00293 
00294         putchar('\n');
00295 
00296         if (write_core_file) {
00297                 printf("Writing core file '%s'\n", core_file_name);
00298                 rc = elf_core_save(core_file_name, ainfo_buf, n_areas, phoneid);
00299                 if (rc != EOK) {
00300                         printf("Failed writing core file.\n");
00301                         return EIO;
00302                 }
00303         }
00304 
00305         free(ainfo_buf);
00306 
00307         return 0;
00308 }
00309 
00310 static int thread_dump(uintptr_t thash)
00311 {
00312         istate_t istate;
00313         uintptr_t pc, fp, nfp;
00314         stacktrace_t st;
00315         char *sym_pc;
00316         int rc;
00317 
00318         rc = udebug_regs_read(phoneid, thash, &istate);
00319         if (rc < 0) {
00320                 printf("Failed reading registers (%d).\n", rc);
00321                 return EIO;
00322         }
00323 
00324         pc = istate_get_pc(&istate);
00325         fp = istate_get_fp(&istate);
00326 
00327         sym_pc = fmt_sym_address(pc);
00328         printf("Thread %p: PC = %s. FP = %p\n", (void *) thash,
00329             sym_pc, (void *) fp);
00330         free(sym_pc);
00331 
00332         st.op_arg = NULL;
00333         st.read_uintptr = td_read_uintptr;
00334 
00335         while (stacktrace_fp_valid(&st, fp)) {
00336                 sym_pc = fmt_sym_address(pc);
00337                 printf("  %p: %s\n", (void *) fp, sym_pc);
00338                 free(sym_pc);
00339 
00340                 rc = stacktrace_ra_get(&st, fp, &pc);
00341                 if (rc != EOK)
00342                         return rc;
00343 
00344                 rc = stacktrace_fp_prev(&st, fp, &nfp);
00345                 if (rc != EOK)
00346                         return rc;
00347 
00348                 fp = nfp;
00349         }
00350 
00351         return EOK;
00352 }
00353 
00354 static int td_read_uintptr(void *arg, uintptr_t addr, uintptr_t *value)
00355 {
00356         uintptr_t data;
00357         int rc;
00358 
00359         (void) arg;
00360 
00361         rc = udebug_mem_read(phoneid, &data, addr, sizeof(data));
00362         if (rc < 0) {
00363                 printf("Warning: udebug_mem_read() failed.\n");
00364                 return rc;
00365         }
00366 
00367         *value = data;
00368         return EOK;
00369 }
00370 
00372 static void autoload_syms(void)
00373 {
00374         char *file_name;
00375         int rc;
00376 
00377         assert(app_name != NULL);
00378         assert(app_symtab == NULL);
00379 
00380         rc = asprintf(&file_name, "/app/%s", app_name);
00381         if (rc < 0) {
00382                 printf("Memory allocation failure.\n");
00383                 exit(1);
00384         }
00385 
00386         rc = symtab_load(file_name, &app_symtab);
00387         if (rc == EOK) {
00388                 printf("Loaded symbol table from %s\n", file_name);
00389                 free(file_name);
00390                 return;
00391         }
00392 
00393         free(file_name);
00394 
00395         rc = asprintf(&file_name, "/srv/%s", app_name);
00396         if (rc < 0) {
00397                 printf("Memory allocation failure.\n");
00398                 exit(1);
00399         }
00400 
00401         rc = symtab_load(file_name, &app_symtab);
00402         if (rc == EOK) {
00403                 printf("Loaded symbol table from %s\n", file_name);
00404                 free(file_name);
00405                 return;
00406         }
00407 
00408         rc = asprintf(&file_name, "/drv/%s/%s", app_name, app_name);
00409         if (rc < 0) {
00410                 printf("Memory allocation failure.\n");
00411                 exit(1);
00412         }
00413 
00414         rc = symtab_load(file_name, &app_symtab);
00415         if (rc == EOK) {
00416                 printf("Loaded symbol table from %s\n", file_name);
00417                 free(file_name);
00418                 return;
00419         }
00420 
00421         free(file_name);
00422         printf("Failed autoloading symbol table.\n");
00423 }
00424 
00425 static char *get_app_task_name(void)
00426 {
00427         char dummy_buf;
00428         size_t copied, needed, name_size;
00429         char *name;
00430         int rc;
00431 
00432         rc = udebug_name_read(phoneid, &dummy_buf, 0, &copied, &needed);
00433         if (rc < 0)
00434                 return NULL;
00435 
00436         name_size = needed;
00437         name = malloc(name_size + 1);
00438         rc = udebug_name_read(phoneid, name, name_size, &copied, &needed);
00439         if (rc < 0) {
00440                 free(name);
00441                 return NULL;
00442         }
00443 
00444         assert(copied == name_size);
00445         assert(copied == needed);
00446         name[copied] = '\0';
00447 
00448         return name;
00449 }
00450 
00459 static char *fmt_sym_address(uintptr_t addr)
00460 {
00461         char *name;
00462         size_t offs;
00463         int rc;
00464         char *str;
00465 
00466         if (app_symtab != NULL) {
00467                 rc = symtab_addr_to_name(app_symtab, addr, &name, &offs);
00468         } else {
00469                 rc = ENOTSUP;
00470         }
00471 
00472         if (rc == EOK) {
00473                 rc = asprintf(&str, "%p (%s+%zu)", (void *) addr, name, offs);
00474         } else {
00475                 rc = asprintf(&str, "%p", (void *) addr);
00476         }
00477 
00478         if (rc < 0) {
00479                 printf("Memory allocation error.\n");
00480                 exit(1);
00481         }
00482 
00483         return str;
00484 }
00485 

Generated on Thu Jun 2 07:45:43 2011 for HelenOS/USB by  doxygen 1.4.7