Brian Silverman | 8649792 | 2018-02-10 19:28:39 -0500 | [diff] [blame] | 1 | /* Unwinding of frames like gstack/pstack. |
| 2 | Copyright (C) 2013-2014 Red Hat, Inc. |
| 3 | This file is part of elfutils. |
| 4 | |
| 5 | This file is free software; you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by |
| 7 | the Free Software Foundation; either version 3 of the License, or |
| 8 | (at your option) any later version. |
| 9 | |
| 10 | elfutils is distributed in the hope that it will be useful, but |
| 11 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | GNU General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU General Public License |
| 16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| 17 | |
| 18 | #include <config.h> |
| 19 | #include <assert.h> |
| 20 | #include <argp.h> |
| 21 | #include <error.h> |
| 22 | #include <stdlib.h> |
| 23 | #include <inttypes.h> |
| 24 | #include <stdio.h> |
| 25 | #include <stdio_ext.h> |
| 26 | #include <string.h> |
| 27 | #include <locale.h> |
| 28 | #include <fcntl.h> |
| 29 | #include ELFUTILS_HEADER(dwfl) |
| 30 | |
| 31 | #include <dwarf.h> |
| 32 | #include <system.h> |
| 33 | #include <printversion.h> |
| 34 | |
| 35 | /* Name and version of program. */ |
| 36 | ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; |
| 37 | |
| 38 | /* Bug report address. */ |
| 39 | ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; |
| 40 | |
| 41 | /* non-printable argp options. */ |
| 42 | #define OPT_DEBUGINFO 0x100 |
| 43 | #define OPT_COREFILE 0x101 |
| 44 | |
| 45 | static bool show_activation = false; |
| 46 | static bool show_module = false; |
| 47 | static bool show_build_id = false; |
| 48 | static bool show_source = false; |
| 49 | static bool show_one_tid = false; |
| 50 | static bool show_quiet = false; |
| 51 | static bool show_raw = false; |
| 52 | static bool show_modules = false; |
| 53 | static bool show_debugname = false; |
| 54 | static bool show_inlines = false; |
| 55 | |
| 56 | static int maxframes = 256; |
| 57 | |
| 58 | struct frame |
| 59 | { |
| 60 | Dwarf_Addr pc; |
| 61 | bool isactivation; |
| 62 | }; |
| 63 | |
| 64 | struct frames |
| 65 | { |
| 66 | int frames; |
| 67 | int allocated; |
| 68 | struct frame *frame; |
| 69 | }; |
| 70 | |
| 71 | static Dwfl *dwfl = NULL; |
| 72 | static pid_t pid = 0; |
| 73 | static int core_fd = -1; |
| 74 | static Elf *core = NULL; |
| 75 | static const char *exec = NULL; |
| 76 | static char *debuginfo_path = NULL; |
| 77 | |
| 78 | static const Dwfl_Callbacks proc_callbacks = |
| 79 | { |
| 80 | .find_elf = dwfl_linux_proc_find_elf, |
| 81 | .find_debuginfo = dwfl_standard_find_debuginfo, |
| 82 | .debuginfo_path = &debuginfo_path, |
| 83 | }; |
| 84 | |
| 85 | static const Dwfl_Callbacks core_callbacks = |
| 86 | { |
| 87 | .find_elf = dwfl_build_id_find_elf, |
| 88 | .find_debuginfo = dwfl_standard_find_debuginfo, |
| 89 | .debuginfo_path = &debuginfo_path, |
| 90 | }; |
| 91 | |
| 92 | #ifdef USE_DEMANGLE |
| 93 | static size_t demangle_buffer_len = 0; |
| 94 | static char *demangle_buffer = NULL; |
| 95 | #endif |
| 96 | |
| 97 | /* Whether any frames have been shown at all. Determines exit status. */ |
| 98 | static bool frames_shown = false; |
| 99 | |
| 100 | /* Program exit codes. All frames shown without any errors is GOOD. |
| 101 | Some frames shown with some non-fatal errors is an ERROR. A fatal |
| 102 | error or no frames shown at all is BAD. A command line USAGE exit |
| 103 | is generated by argp_error. */ |
| 104 | #define EXIT_OK 0 |
| 105 | #define EXIT_ERROR 1 |
| 106 | #define EXIT_BAD 2 |
| 107 | #define EXIT_USAGE 64 |
| 108 | |
| 109 | static int |
| 110 | get_addr_width (Dwfl_Module *mod) |
| 111 | { |
| 112 | // Try to find the address wide if possible. |
| 113 | static int width = 0; |
| 114 | if (width == 0 && mod) |
| 115 | { |
| 116 | Dwarf_Addr bias; |
| 117 | Elf *elf = dwfl_module_getelf (mod, &bias); |
| 118 | if (elf) |
| 119 | { |
| 120 | GElf_Ehdr ehdr_mem; |
| 121 | GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); |
| 122 | if (ehdr) |
| 123 | width = ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16; |
| 124 | } |
| 125 | } |
| 126 | if (width == 0) |
| 127 | width = 16; |
| 128 | |
| 129 | return width; |
| 130 | } |
| 131 | |
| 132 | static int |
| 133 | module_callback (Dwfl_Module *mod, void **userdata __attribute__((unused)), |
| 134 | const char *name, Dwarf_Addr start, |
| 135 | void *arg __attribute__((unused))) |
| 136 | { |
| 137 | /* Forces resolving of main elf and debug files. */ |
| 138 | Dwarf_Addr bias; |
| 139 | Elf *elf = dwfl_module_getelf (mod, &bias); |
| 140 | Dwarf *dwarf = dwfl_module_getdwarf (mod, &bias); |
| 141 | |
| 142 | Dwarf_Addr end; |
| 143 | const char *mainfile; |
| 144 | const char *debugfile; |
| 145 | const char *modname = dwfl_module_info (mod, NULL, NULL, &end, NULL, |
| 146 | NULL, &mainfile, &debugfile); |
| 147 | assert (strcmp (modname, name) == 0); |
| 148 | |
| 149 | int width = get_addr_width (mod); |
| 150 | printf ("0x%0*" PRIx64 "-0x%0*" PRIx64 " %s\n", |
| 151 | width, start, width, end, basename (name)); |
| 152 | |
| 153 | const unsigned char *id; |
| 154 | GElf_Addr id_vaddr; |
| 155 | int id_len = dwfl_module_build_id (mod, &id, &id_vaddr); |
| 156 | if (id_len > 0) |
| 157 | { |
| 158 | printf (" ["); |
| 159 | do |
| 160 | printf ("%02" PRIx8, *id++); |
| 161 | while (--id_len > 0); |
| 162 | printf ("]\n"); |
| 163 | } |
| 164 | |
| 165 | if (elf != NULL) |
| 166 | printf (" %s\n", mainfile != NULL ? mainfile : "-"); |
| 167 | if (dwarf != NULL) |
| 168 | printf (" %s\n", debugfile != NULL ? debugfile : "-"); |
| 169 | |
| 170 | return DWARF_CB_OK; |
| 171 | } |
| 172 | |
| 173 | static int |
| 174 | frame_callback (Dwfl_Frame *state, void *arg) |
| 175 | { |
| 176 | struct frames *frames = (struct frames *) arg; |
| 177 | int nr = frames->frames; |
| 178 | if (! dwfl_frame_pc (state, &frames->frame[nr].pc, |
| 179 | &frames->frame[nr].isactivation)) |
| 180 | return -1; |
| 181 | |
| 182 | frames->frames++; |
| 183 | if (frames->frames == maxframes) |
| 184 | return DWARF_CB_ABORT; |
| 185 | |
| 186 | if (frames->frames == frames->allocated) |
| 187 | { |
| 188 | frames->allocated *= 2; |
| 189 | frames->frame = realloc (frames->frame, |
| 190 | sizeof (struct frame) * frames->allocated); |
| 191 | if (frames->frame == NULL) |
| 192 | error (EXIT_BAD, errno, "realloc frames.frame"); |
| 193 | } |
| 194 | |
| 195 | return DWARF_CB_OK; |
| 196 | } |
| 197 | |
| 198 | static const char* |
| 199 | die_name (Dwarf_Die *die) |
| 200 | { |
| 201 | Dwarf_Attribute attr; |
| 202 | const char *name; |
| 203 | name = dwarf_formstring (dwarf_attr_integrate (die, |
| 204 | DW_AT_MIPS_linkage_name, |
| 205 | &attr) |
| 206 | ?: dwarf_attr_integrate (die, |
| 207 | DW_AT_linkage_name, |
| 208 | &attr)); |
| 209 | if (name == NULL) |
| 210 | name = dwarf_diename (die); |
| 211 | |
| 212 | return name; |
| 213 | } |
| 214 | |
| 215 | static void |
| 216 | print_frame (int nr, Dwarf_Addr pc, bool isactivation, |
| 217 | Dwarf_Addr pc_adjusted, Dwfl_Module *mod, |
| 218 | const char *symname, Dwarf_Die *cudie, |
| 219 | Dwarf_Die *die) |
| 220 | { |
| 221 | int width = get_addr_width (mod); |
| 222 | printf ("#%-2u 0x%0*" PRIx64, nr, width, (uint64_t) pc); |
| 223 | |
| 224 | if (show_activation) |
| 225 | printf ("%4s", ! isactivation ? "- 1" : ""); |
| 226 | |
| 227 | if (symname != NULL) |
| 228 | { |
| 229 | #ifdef USE_DEMANGLE |
| 230 | // Require GNU v3 ABI by the "_Z" prefix. |
| 231 | if (! show_raw && symname[0] == '_' && symname[1] == 'Z') |
| 232 | { |
| 233 | int status = -1; |
| 234 | char *dsymname = __cxa_demangle (symname, demangle_buffer, |
| 235 | &demangle_buffer_len, &status); |
| 236 | if (status == 0) |
| 237 | symname = demangle_buffer = dsymname; |
| 238 | } |
| 239 | #endif |
| 240 | printf (" %s", symname); |
| 241 | } |
| 242 | |
| 243 | const char* fname; |
| 244 | Dwarf_Addr start; |
| 245 | fname = dwfl_module_info(mod, NULL, &start, |
| 246 | NULL, NULL, NULL, NULL, NULL); |
| 247 | if (show_module) |
| 248 | { |
| 249 | if (fname != NULL) |
| 250 | printf (" - %s", fname); |
| 251 | } |
| 252 | |
| 253 | if (show_build_id) |
| 254 | { |
| 255 | const unsigned char *id; |
| 256 | GElf_Addr id_vaddr; |
| 257 | int id_len = dwfl_module_build_id (mod, &id, &id_vaddr); |
| 258 | if (id_len > 0) |
| 259 | { |
| 260 | printf ("\n ["); |
| 261 | do |
| 262 | printf ("%02" PRIx8, *id++); |
| 263 | while (--id_len > 0); |
| 264 | printf ("]@0x%0" PRIx64 "+0x%" PRIx64, |
| 265 | start, pc_adjusted - start); |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | if (show_source) |
| 270 | { |
| 271 | int line, col; |
| 272 | const char* sname; |
| 273 | line = col = -1; |
| 274 | sname = NULL; |
| 275 | if (die != NULL) |
| 276 | { |
| 277 | Dwarf_Files *files; |
| 278 | if (dwarf_getsrcfiles (cudie, &files, NULL) == 0) |
| 279 | { |
| 280 | Dwarf_Attribute attr; |
| 281 | Dwarf_Word val; |
| 282 | if (dwarf_formudata (dwarf_attr (die, DW_AT_call_file, &attr), |
| 283 | &val) == 0) |
| 284 | { |
| 285 | sname = dwarf_filesrc (files, val, NULL, NULL); |
| 286 | if (dwarf_formudata (dwarf_attr (die, DW_AT_call_line, |
| 287 | &attr), &val) == 0) |
| 288 | { |
| 289 | line = val; |
| 290 | if (dwarf_formudata (dwarf_attr (die, DW_AT_call_column, |
| 291 | &attr), &val) == 0) |
| 292 | col = val; |
| 293 | } |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | else |
| 298 | { |
| 299 | Dwfl_Line *lineobj = dwfl_module_getsrc(mod, pc_adjusted); |
| 300 | if (lineobj) |
| 301 | sname = dwfl_lineinfo (lineobj, NULL, &line, &col, NULL, NULL); |
| 302 | } |
| 303 | |
| 304 | if (sname != NULL) |
| 305 | { |
| 306 | printf ("\n %s", sname); |
| 307 | if (line > 0) |
| 308 | { |
| 309 | printf (":%d", line); |
| 310 | if (col > 0) |
| 311 | printf (":%d", col); |
| 312 | } |
| 313 | } |
| 314 | } |
| 315 | printf ("\n"); |
| 316 | } |
| 317 | |
| 318 | static void |
| 319 | print_inline_frames (int *nr, Dwarf_Addr pc, bool isactivation, |
| 320 | Dwarf_Addr pc_adjusted, Dwfl_Module *mod, |
| 321 | const char *symname, Dwarf_Die *cudie, Dwarf_Die *die) |
| 322 | { |
| 323 | Dwarf_Die *scopes = NULL; |
| 324 | int nscopes = dwarf_getscopes_die (die, &scopes); |
| 325 | if (nscopes > 0) |
| 326 | { |
| 327 | /* scopes[0] == die, the lowest level, for which we already have |
| 328 | the name. This is the actual source location where it |
| 329 | happened. */ |
| 330 | print_frame ((*nr)++, pc, isactivation, pc_adjusted, mod, symname, |
| 331 | NULL, NULL); |
| 332 | |
| 333 | /* last_scope is the source location where the next frame/function |
| 334 | call was done. */ |
| 335 | Dwarf_Die *last_scope = &scopes[0]; |
| 336 | for (int i = 1; i < nscopes && (maxframes == 0 || *nr < maxframes); i++) |
| 337 | { |
| 338 | Dwarf_Die *scope = &scopes[i]; |
| 339 | int tag = dwarf_tag (scope); |
| 340 | if (tag != DW_TAG_inlined_subroutine |
| 341 | && tag != DW_TAG_entry_point |
| 342 | && tag != DW_TAG_subprogram) |
| 343 | continue; |
| 344 | |
| 345 | symname = die_name (scope); |
| 346 | print_frame ((*nr)++, pc, isactivation, pc_adjusted, mod, symname, |
| 347 | cudie, last_scope); |
| 348 | |
| 349 | /* Found the "top-level" in which everything was inlined? */ |
| 350 | if (tag == DW_TAG_subprogram) |
| 351 | break; |
| 352 | |
| 353 | last_scope = scope; |
| 354 | } |
| 355 | } |
| 356 | free (scopes); |
| 357 | } |
| 358 | |
| 359 | static void |
| 360 | print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what) |
| 361 | { |
| 362 | if (frames->frames > 0) |
| 363 | frames_shown = true; |
| 364 | |
| 365 | printf ("TID %lld:\n", (long long) tid); |
| 366 | int frame_nr = 0; |
| 367 | for (int nr = 0; nr < frames->frames && (maxframes == 0 |
| 368 | || frame_nr < maxframes); nr++) |
| 369 | { |
| 370 | Dwarf_Addr pc = frames->frame[nr].pc; |
| 371 | bool isactivation = frames->frame[nr].isactivation; |
| 372 | Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1); |
| 373 | |
| 374 | /* Get PC->SYMNAME. */ |
| 375 | Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted); |
| 376 | const char *symname = NULL; |
| 377 | Dwarf_Die die_mem; |
| 378 | Dwarf_Die *die = NULL; |
| 379 | Dwarf_Die *cudie = NULL; |
| 380 | if (mod && ! show_quiet) |
| 381 | { |
| 382 | if (show_debugname) |
| 383 | { |
| 384 | Dwarf_Addr bias = 0; |
| 385 | Dwarf_Die *scopes = NULL; |
| 386 | cudie = dwfl_module_addrdie (mod, pc_adjusted, &bias); |
| 387 | int nscopes = dwarf_getscopes (cudie, pc_adjusted - bias, |
| 388 | &scopes); |
| 389 | |
| 390 | /* Find the first function-like DIE with a name in scope. */ |
| 391 | for (int i = 0; symname == NULL && i < nscopes; i++) |
| 392 | { |
| 393 | Dwarf_Die *scope = &scopes[i]; |
| 394 | int tag = dwarf_tag (scope); |
| 395 | if (tag == DW_TAG_subprogram |
| 396 | || tag == DW_TAG_inlined_subroutine |
| 397 | || tag == DW_TAG_entry_point) |
| 398 | symname = die_name (scope); |
| 399 | |
| 400 | if (symname != NULL) |
| 401 | { |
| 402 | die_mem = *scope; |
| 403 | die = &die_mem; |
| 404 | } |
| 405 | } |
| 406 | free (scopes); |
| 407 | } |
| 408 | |
| 409 | if (symname == NULL) |
| 410 | symname = dwfl_module_addrname (mod, pc_adjusted); |
| 411 | } |
| 412 | |
| 413 | if (show_inlines && die != NULL) |
| 414 | print_inline_frames (&frame_nr, pc, isactivation, pc_adjusted, mod, |
| 415 | symname, cudie, die); |
| 416 | else |
| 417 | print_frame (frame_nr++, pc, isactivation, pc_adjusted, mod, symname, |
| 418 | NULL, NULL); |
| 419 | } |
| 420 | |
| 421 | if (frames->frames > 0 && frame_nr == maxframes) |
| 422 | error (0, 0, "tid %lld: shown max number of frames " |
| 423 | "(%d, use -n 0 for unlimited)", (long long) tid, maxframes); |
| 424 | else if (dwflerr != 0) |
| 425 | { |
| 426 | if (frames->frames > 0) |
| 427 | { |
| 428 | unsigned nr = frames->frames - 1; |
| 429 | Dwarf_Addr pc = frames->frame[nr].pc; |
| 430 | bool isactivation = frames->frame[nr].isactivation; |
| 431 | Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1); |
| 432 | Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted); |
| 433 | const char *mainfile = NULL; |
| 434 | const char *modname = dwfl_module_info (mod, NULL, NULL, NULL, NULL, |
| 435 | NULL, &mainfile, NULL); |
| 436 | if (modname == NULL || modname[0] == '\0') |
| 437 | { |
| 438 | if (mainfile != NULL) |
| 439 | modname = mainfile; |
| 440 | else |
| 441 | modname = "<unknown>"; |
| 442 | } |
| 443 | error (0, 0, "%s tid %lld at 0x%" PRIx64 " in %s: %s", what, |
| 444 | (long long) tid, pc_adjusted, modname, dwfl_errmsg (dwflerr)); |
| 445 | } |
| 446 | else |
| 447 | error (0, 0, "%s tid %lld: %s", what, (long long) tid, |
| 448 | dwfl_errmsg (dwflerr)); |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | static int |
| 453 | thread_callback (Dwfl_Thread *thread, void *thread_arg) |
| 454 | { |
| 455 | struct frames *frames = (struct frames *) thread_arg; |
| 456 | pid_t tid = dwfl_thread_tid (thread); |
| 457 | int err = 0; |
| 458 | frames->frames = 0; |
| 459 | switch (dwfl_thread_getframes (thread, frame_callback, thread_arg)) |
| 460 | { |
| 461 | case DWARF_CB_OK: |
| 462 | case DWARF_CB_ABORT: |
| 463 | break; |
| 464 | case -1: |
| 465 | err = dwfl_errno (); |
| 466 | break; |
| 467 | default: |
| 468 | abort (); |
| 469 | } |
| 470 | print_frames (frames, tid, err, "dwfl_thread_getframes"); |
| 471 | return DWARF_CB_OK; |
| 472 | } |
| 473 | |
| 474 | static error_t |
| 475 | parse_opt (int key, char *arg __attribute__ ((unused)), |
| 476 | struct argp_state *state) |
| 477 | { |
| 478 | switch (key) |
| 479 | { |
| 480 | case 'p': |
| 481 | pid = atoi (arg); |
| 482 | if (pid == 0) |
| 483 | argp_error (state, N_("-p PID should be a positive process id.")); |
| 484 | break; |
| 485 | |
| 486 | case OPT_COREFILE: |
| 487 | core_fd = open (arg, O_RDONLY); |
| 488 | if (core_fd < 0) |
| 489 | error (EXIT_BAD, errno, N_("Cannot open core file '%s'"), arg); |
| 490 | elf_version (EV_CURRENT); |
| 491 | core = elf_begin (core_fd, ELF_C_READ_MMAP, NULL); |
| 492 | if (core == NULL) |
| 493 | error (EXIT_BAD, 0, "core '%s' elf_begin: %s", arg, elf_errmsg(-1)); |
| 494 | break; |
| 495 | |
| 496 | case 'e': |
| 497 | exec = arg; |
| 498 | break; |
| 499 | |
| 500 | case OPT_DEBUGINFO: |
| 501 | debuginfo_path = arg; |
| 502 | break; |
| 503 | |
| 504 | case 'm': |
| 505 | show_module = true; |
| 506 | break; |
| 507 | |
| 508 | case 's': |
| 509 | show_source = true; |
| 510 | break; |
| 511 | |
| 512 | case 'a': |
| 513 | show_activation = true; |
| 514 | break; |
| 515 | |
| 516 | case 'd': |
| 517 | show_debugname = true; |
| 518 | break; |
| 519 | |
| 520 | case 'i': |
| 521 | show_inlines = show_debugname = true; |
| 522 | break; |
| 523 | |
| 524 | case 'v': |
| 525 | show_activation = show_source = show_module = show_debugname = true; |
| 526 | show_inlines = true; |
| 527 | break; |
| 528 | |
| 529 | case 'b': |
| 530 | show_build_id = true; |
| 531 | break; |
| 532 | |
| 533 | case 'q': |
| 534 | show_quiet = true; |
| 535 | break; |
| 536 | |
| 537 | case 'r': |
| 538 | show_raw = true; |
| 539 | break; |
| 540 | |
| 541 | case '1': |
| 542 | show_one_tid = true; |
| 543 | break; |
| 544 | |
| 545 | case 'n': |
| 546 | maxframes = atoi (arg); |
| 547 | if (maxframes < 0) |
| 548 | { |
| 549 | argp_error (state, N_("-n MAXFRAMES should be 0 or higher.")); |
| 550 | return EINVAL; |
| 551 | } |
| 552 | break; |
| 553 | |
| 554 | case 'l': |
| 555 | show_modules = true; |
| 556 | break; |
| 557 | |
| 558 | case ARGP_KEY_END: |
| 559 | if (core == NULL && exec != NULL) |
| 560 | argp_error (state, |
| 561 | N_("-e EXEC needs a core given by --core.")); |
| 562 | |
| 563 | if (pid == 0 && show_one_tid == true) |
| 564 | argp_error (state, |
| 565 | N_("-1 needs a thread id given by -p.")); |
| 566 | |
| 567 | if ((pid == 0 && core == NULL) || (pid != 0 && core != NULL)) |
| 568 | argp_error (state, |
| 569 | N_("One of -p PID or --core COREFILE should be given.")); |
| 570 | |
| 571 | if (pid != 0) |
| 572 | { |
| 573 | dwfl = dwfl_begin (&proc_callbacks); |
| 574 | if (dwfl == NULL) |
| 575 | error (EXIT_BAD, 0, "dwfl_begin: %s", dwfl_errmsg (-1)); |
| 576 | |
| 577 | int err = dwfl_linux_proc_report (dwfl, pid); |
| 578 | if (err < 0) |
| 579 | error (EXIT_BAD, 0, "dwfl_linux_proc_report pid %lld: %s", |
| 580 | (long long) pid, dwfl_errmsg (-1)); |
| 581 | else if (err > 0) |
| 582 | error (EXIT_BAD, err, "dwfl_linux_proc_report pid %lld", |
| 583 | (long long) pid); |
| 584 | } |
| 585 | |
| 586 | if (core != NULL) |
| 587 | { |
| 588 | dwfl = dwfl_begin (&core_callbacks); |
| 589 | if (dwfl == NULL) |
| 590 | error (EXIT_BAD, 0, "dwfl_begin: %s", dwfl_errmsg (-1)); |
| 591 | if (dwfl_core_file_report (dwfl, core, exec) < 0) |
| 592 | error (EXIT_BAD, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1)); |
| 593 | } |
| 594 | |
| 595 | if (dwfl_report_end (dwfl, NULL, NULL) != 0) |
| 596 | error (EXIT_BAD, 0, "dwfl_report_end: %s", dwfl_errmsg (-1)); |
| 597 | |
| 598 | if (pid != 0) |
| 599 | { |
| 600 | int err = dwfl_linux_proc_attach (dwfl, pid, false); |
| 601 | if (err < 0) |
| 602 | error (EXIT_BAD, 0, "dwfl_linux_proc_attach pid %lld: %s", |
| 603 | (long long) pid, dwfl_errmsg (-1)); |
| 604 | else if (err > 0) |
| 605 | error (EXIT_BAD, err, "dwfl_linux_proc_attach pid %lld", |
| 606 | (long long) pid); |
| 607 | } |
| 608 | |
| 609 | if (core != NULL) |
| 610 | { |
| 611 | if (dwfl_core_file_attach (dwfl, core) < 0) |
| 612 | error (EXIT_BAD, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1)); |
| 613 | } |
| 614 | |
| 615 | /* Makes sure we are properly attached. */ |
| 616 | if (dwfl_pid (dwfl) < 0) |
| 617 | error (EXIT_BAD, 0, "dwfl_pid: %s\n", dwfl_errmsg (-1)); |
| 618 | break; |
| 619 | |
| 620 | default: |
| 621 | return ARGP_ERR_UNKNOWN; |
| 622 | } |
| 623 | return 0; |
| 624 | } |
| 625 | |
| 626 | int |
| 627 | main (int argc, char **argv) |
| 628 | { |
| 629 | /* We use no threads here which can interfere with handling a stream. */ |
| 630 | __fsetlocking (stdin, FSETLOCKING_BYCALLER); |
| 631 | __fsetlocking (stdout, FSETLOCKING_BYCALLER); |
| 632 | __fsetlocking (stderr, FSETLOCKING_BYCALLER); |
| 633 | |
| 634 | /* Set locale. */ |
| 635 | (void) setlocale (LC_ALL, ""); |
| 636 | |
| 637 | const struct argp_option options[] = |
| 638 | { |
| 639 | { NULL, 0, NULL, 0, N_("Input selection options:"), 0 }, |
| 640 | { "pid", 'p', "PID", 0, |
| 641 | N_("Show stack of process PID"), 0 }, |
| 642 | { "core", OPT_COREFILE, "COREFILE", 0, |
| 643 | N_("Show stack found in COREFILE"), 0 }, |
| 644 | { "executable", 'e', "EXEC", 0, N_("(optional) EXECUTABLE that produced COREFILE"), 0 }, |
| 645 | { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0, |
| 646 | N_("Search path for separate debuginfo files"), 0 }, |
| 647 | |
| 648 | { NULL, 0, NULL, 0, N_("Output selection options:"), 0 }, |
| 649 | { "activation", 'a', NULL, 0, |
| 650 | N_("Additionally show frame activation"), 0 }, |
| 651 | { "debugname", 'd', NULL, 0, |
| 652 | N_("Additionally try to lookup DWARF debuginfo name for frame address"), |
| 653 | 0 }, |
| 654 | { "inlines", 'i', NULL, 0, |
| 655 | N_("Additionally show inlined function frames using DWARF debuginfo if available (implies -d)"), 0 }, |
| 656 | { "module", 'm', NULL, 0, |
| 657 | N_("Additionally show module file information"), 0 }, |
| 658 | { "source", 's', NULL, 0, |
| 659 | N_("Additionally show source file information"), 0 }, |
| 660 | { "verbose", 'v', NULL, 0, |
| 661 | N_("Show all additional information (activation, debugname, inlines, module and source)"), 0 }, |
| 662 | { "quiet", 'q', NULL, 0, |
| 663 | N_("Do not resolve address to function symbol name"), 0 }, |
| 664 | { "raw", 'r', NULL, 0, |
| 665 | N_("Show raw function symbol names, do not try to demangle names"), 0 }, |
| 666 | { "build-id", 'b', NULL, 0, |
| 667 | N_("Show module build-id, load address and pc offset"), 0 }, |
| 668 | { NULL, '1', NULL, 0, |
| 669 | N_("Show the backtrace of only one thread"), 0 }, |
| 670 | { NULL, 'n', "MAXFRAMES", 0, |
| 671 | N_("Show at most MAXFRAMES per thread (default 256, use 0 for unlimited)"), 0 }, |
| 672 | { "list-modules", 'l', NULL, 0, |
| 673 | N_("Show module memory map with build-id, elf and debug files detected"), 0 }, |
| 674 | { NULL, 0, NULL, 0, NULL, 0 } |
| 675 | }; |
| 676 | |
| 677 | const struct argp argp = |
| 678 | { |
| 679 | .options = options, |
| 680 | .parser = parse_opt, |
| 681 | .doc = N_("Print a stack for each thread in a process or core file.\n\ |
| 682 | \n\ |
| 683 | Program exits with return code 0 if all frames were shown without \ |
| 684 | any errors. If some frames were shown, but there were some non-fatal \ |
| 685 | errors, possibly causing an incomplete backtrace, the program exits \ |
| 686 | with return code 1. If no frames could be shown, or a fatal error \ |
| 687 | occured the program exits with return code 2. If the program was \ |
| 688 | invoked with bad or missing arguments it will exit with return code 64.") |
| 689 | }; |
| 690 | |
| 691 | argp_parse (&argp, argc, argv, 0, NULL, NULL); |
| 692 | |
| 693 | if (show_modules) |
| 694 | { |
| 695 | printf ("PID %lld - %s module memory map\n", (long long) dwfl_pid (dwfl), |
| 696 | pid != 0 ? "process" : "core"); |
| 697 | if (dwfl_getmodules (dwfl, module_callback, NULL, 0) != 0) |
| 698 | error (EXIT_BAD, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1)); |
| 699 | } |
| 700 | |
| 701 | struct frames frames; |
| 702 | /* When maxframes is zero, then 2048 is just the initial allocation |
| 703 | that will be increased using realloc in framecallback (). */ |
| 704 | frames.allocated = maxframes == 0 ? 2048 : maxframes; |
| 705 | frames.frames = 0; |
| 706 | frames.frame = malloc (sizeof (struct frame) * frames.allocated); |
| 707 | if (frames.frame == NULL) |
| 708 | error (EXIT_BAD, errno, "malloc frames.frame"); |
| 709 | |
| 710 | if (show_one_tid) |
| 711 | { |
| 712 | int err = 0; |
| 713 | switch (dwfl_getthread_frames (dwfl, pid, frame_callback, &frames)) |
| 714 | { |
| 715 | case DWARF_CB_OK: |
| 716 | case DWARF_CB_ABORT: |
| 717 | break; |
| 718 | case -1: |
| 719 | err = dwfl_errno (); |
| 720 | break; |
| 721 | default: |
| 722 | abort (); |
| 723 | } |
| 724 | print_frames (&frames, pid, err, "dwfl_getthread_frames"); |
| 725 | } |
| 726 | else |
| 727 | { |
| 728 | printf ("PID %lld - %s\n", (long long) dwfl_pid (dwfl), |
| 729 | pid != 0 ? "process" : "core"); |
| 730 | switch (dwfl_getthreads (dwfl, thread_callback, &frames)) |
| 731 | { |
| 732 | case DWARF_CB_OK: |
| 733 | case DWARF_CB_ABORT: |
| 734 | break; |
| 735 | case -1: |
| 736 | error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1)); |
| 737 | break; |
| 738 | default: |
| 739 | abort (); |
| 740 | } |
| 741 | } |
| 742 | free (frames.frame); |
| 743 | dwfl_end (dwfl); |
| 744 | |
| 745 | if (core != NULL) |
| 746 | elf_end (core); |
| 747 | |
| 748 | if (core_fd != -1) |
| 749 | close (core_fd); |
| 750 | |
| 751 | #ifdef USE_DEMANGLE |
| 752 | free (demangle_buffer); |
| 753 | #endif |
| 754 | |
| 755 | if (! frames_shown) |
| 756 | error (EXIT_BAD, 0, N_("Couldn't show any frames.")); |
| 757 | |
| 758 | return error_message_count != 0 ? EXIT_ERROR : EXIT_OK; |
| 759 | } |