1 /* 2 * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8 9 10 #include <arch/debug.h> 11 12 #include <stdio.h> 13 #include <stdlib.h> 14 15 #include <debug.h> 16 #include <elf.h> 17 #include <kernel.h> 18 #include <kimage.h> 19 #include <thread.h> 20 #include <vm.h> 21 #include <vm_types.h> 22 23 #include <arch_cpu.h> 24 25 26 struct stack_frame { 27 struct stack_frame *previous; 28 addr_t return_address; 29 }; 30 31 #define NUM_PREVIOUS_LOCATIONS 32 32 33 34 static bool 35 already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 ebp) 36 { 37 int32 last = *_last; 38 int32 num = *_num; 39 int32 i; 40 41 for (i = 0; i < num; i++) { 42 if (visited[(NUM_PREVIOUS_LOCATIONS + last - i) % NUM_PREVIOUS_LOCATIONS] == ebp) 43 return true; 44 } 45 46 *_last = last = (last + 1) % NUM_PREVIOUS_LOCATIONS; 47 visited[last] = ebp; 48 49 if (num < NUM_PREVIOUS_LOCATIONS) 50 *_num = num + 1; 51 52 return false; 53 } 54 55 56 static status_t 57 get_next_frame(addr_t ebp, addr_t *_next, addr_t *_eip) 58 { 59 // set fault handler, so that we can safely access user stacks 60 addr_t oldFaultHandler = thread_get_current_thread()->fault_handler; 61 thread_get_current_thread()->fault_handler = (addr_t)&&error; 62 // Fake goto to trick the compiler not to optimize the code at the label 63 // away. 64 if (ebp == 0) 65 goto error; 66 67 *_eip = ((struct stack_frame *)ebp)->return_address; 68 *_next = (addr_t)((struct stack_frame *)ebp)->previous; 69 70 thread_get_current_thread()->fault_handler = oldFaultHandler; 71 return B_OK; 72 73 error: 74 thread_get_current_thread()->fault_handler = oldFaultHandler; 75 return B_BAD_ADDRESS; 76 } 77 78 79 static status_t 80 lookup_symbol(struct thread* thread, addr_t address, addr_t *_baseAddress, 81 const char **_symbolName, const char **_imageName, bool *_exactMatch) 82 { 83 status_t status = B_ENTRY_NOT_FOUND; 84 85 if (IS_KERNEL_ADDRESS(address)) { 86 // a kernel symbol 87 status = elf_debug_lookup_symbol_address(address, _baseAddress, 88 _symbolName, _imageName, _exactMatch); 89 } else if (thread != NULL && thread->team != NULL) { 90 // try a lookup using the userland runtime loader structures 91 status = elf_debug_lookup_user_symbol_address(thread->team, address, 92 _baseAddress, _symbolName, _imageName, _exactMatch); 93 94 if (status != B_OK) { 95 // try to locate the image in the images loaded into user space 96 status = image_debug_lookup_user_symbol_address(thread->team, 97 address, _baseAddress, _symbolName, _imageName, _exactMatch); 98 } 99 } 100 101 return status; 102 } 103 104 105 static void 106 print_stack_frame(struct thread *thread, addr_t eip, addr_t ebp, addr_t nextEbp) 107 { 108 const char *symbol, *image; 109 addr_t baseAddress; 110 bool exactMatch; 111 status_t status; 112 addr_t diff; 113 114 diff = nextEbp - ebp; 115 116 // kernel space/user space switch 117 if (diff & 0x80000000) 118 diff = 0; 119 120 status = lookup_symbol(thread, eip, &baseAddress, &symbol, &image, 121 &exactMatch); 122 123 kprintf("%08lx (+%4ld) %08lx", ebp, diff, eip); 124 125 if (status == B_OK) { 126 if (symbol != NULL) { 127 kprintf(" <%s>:%s + 0x%04lx%s\n", image, symbol, 128 eip - baseAddress, exactMatch ? "" : " (nearest)"); 129 } else { 130 kprintf(" <%s@%p>:unknown + 0x%04lx\n", image, 131 (void *)baseAddress, eip - baseAddress); 132 } 133 } else { 134 vm_area *area = NULL; 135 if (thread->team->address_space != NULL) 136 area = vm_area_lookup(thread->team->address_space, eip); 137 if (area != NULL) { 138 kprintf(" %ld:%s@%p + %#lx\n", area->id, area->name, (void *)area->base, 139 eip - area->base); 140 } else 141 kprintf("\n"); 142 } 143 } 144 145 146 static void 147 print_iframe(struct iframe *frame) 148 { 149 kprintf("iframe at %p (end = %p)\n", frame, frame + 1); 150 151 kprintf(" eax 0x%-9lx ebx 0x%-9lx ecx 0x%-9lx edx 0x%lx\n", 152 frame->eax, frame->ebx, frame->ecx, frame->edx); 153 kprintf(" esi 0x%-9lx edi 0x%-9lx ebp 0x%-9lx esp 0x%lx\n", 154 frame->esi, frame->edi, frame->ebp, frame->esp); 155 kprintf(" eip 0x%-9lx eflags 0x%-9lx", frame->eip, frame->flags); 156 if ((frame->error_code & 0x4) != 0) { 157 // from user space 158 kprintf("user esp 0x%lx", frame->user_esp); 159 } 160 kprintf("\n"); 161 kprintf(" vector: 0x%lx, error code: 0x%lx\n", frame->vector, 162 frame->error_code); 163 } 164 165 166 static void 167 setup_for_thread(char *arg, struct thread **_thread, uint32 *_ebp, 168 uint32 *_oldPageDirectory) 169 { 170 struct thread *thread = NULL; 171 172 if (arg != NULL) { 173 thread_id id = strtoul(arg, NULL, 0); 174 thread = thread_get_thread_struct_locked(id); 175 if (thread == NULL) { 176 kprintf("could not find thread %ld\n", id); 177 return; 178 } 179 180 if (id != thread_get_current_thread_id()) { 181 // switch to the page directory of the new thread to be 182 // able to follow the stack trace into userland 183 addr_t newPageDirectory = (addr_t)x86_next_page_directory( 184 thread_get_current_thread(), thread); 185 186 if (newPageDirectory != 0) { 187 read_cr3(*_oldPageDirectory); 188 write_cr3(newPageDirectory); 189 } 190 191 // read %ebp from the thread's stack stored by a pushad 192 *_ebp = thread->arch_info.current_stack.esp[2]; 193 } else 194 thread = NULL; 195 } 196 197 if (thread == NULL) { 198 // if we don't have a thread yet, we want the current one 199 // (ebp has been set by the caller for this case already) 200 thread = thread_get_current_thread(); 201 } 202 203 *_thread = thread; 204 } 205 206 207 static bool 208 is_kernel_stack_address(struct thread* thread, addr_t address) 209 { 210 // We don't have a thread pointer in the early boot process, but then we are 211 // on the kernel stack for sure. 212 if (thread == NULL) 213 return IS_KERNEL_ADDRESS(address); 214 215 return address >= thread->kernel_stack_base 216 && address < thread->kernel_stack_top; 217 } 218 219 220 static bool 221 is_iframe(struct thread* thread, addr_t frame) 222 { 223 if (!is_kernel_stack_address(thread, frame)) 224 return false; 225 226 addr_t previousFrame = *(addr_t*)frame; 227 return ((previousFrame & ~IFRAME_TYPE_MASK) == 0 && previousFrame != 0); 228 } 229 230 231 static struct iframe * 232 find_previous_iframe(struct thread *thread, addr_t frame) 233 { 234 // iterate backwards through the stack frames, until we hit an iframe 235 while (is_kernel_stack_address(thread, frame)) { 236 if (is_iframe(thread, frame)) 237 return (struct iframe*)frame; 238 239 frame = *(addr_t*)frame; 240 } 241 242 return NULL; 243 } 244 245 246 static struct iframe* 247 get_previous_iframe(struct thread* thread, struct iframe* frame) 248 { 249 if (frame == NULL) 250 return NULL; 251 252 return find_previous_iframe(thread, frame->ebp); 253 } 254 255 256 static int 257 stack_trace(int argc, char **argv) 258 { 259 static const char* usage = "usage: %s [ <thread id> ]\n" 260 "Prints a stack trace for the current, respectively the specified\n" 261 "thread.\n" 262 " <thread id> - The ID of the thread for which to print the stack\n" 263 " trace.\n"; 264 if (argc > 2 || argc == 2 && strcmp(argv[1], "--help") == 0) { 265 kprintf(usage, argv[0]); 266 return 0; 267 } 268 269 uint32 previousLocations[NUM_PREVIOUS_LOCATIONS]; 270 struct thread *thread = NULL; 271 addr_t oldPageDirectory = 0; 272 uint32 ebp = x86_read_ebp(); 273 int32 i, num = 0, last = 0; 274 275 setup_for_thread(argc == 2 ? argv[1] : NULL, &thread, &ebp, 276 &oldPageDirectory); 277 278 if (thread != NULL) { 279 kprintf("stack trace for thread %ld \"%s\"\n", thread->id, 280 thread->name); 281 282 kprintf(" kernel stack: %p to %p\n", 283 (void *)thread->kernel_stack_base, 284 (void *)(thread->kernel_stack_top)); 285 if (thread->user_stack_base != 0) { 286 kprintf(" user stack: %p to %p\n", 287 (void *)thread->user_stack_base, 288 (void *)(thread->user_stack_base + thread->user_stack_size)); 289 } 290 } 291 292 kprintf("frame caller <image>:function + offset\n"); 293 294 bool onKernelStack = true; 295 296 for (;;) { 297 onKernelStack = onKernelStack 298 && is_kernel_stack_address(thread, ebp); 299 300 if (onKernelStack && is_iframe(thread, ebp)) { 301 struct iframe *frame = (struct iframe *)ebp; 302 303 print_iframe(frame); 304 print_stack_frame(thread, frame->eip, ebp, frame->ebp); 305 306 ebp = frame->ebp; 307 } else { 308 addr_t eip, nextEbp; 309 310 if (get_next_frame(ebp, &nextEbp, &eip) != B_OK) { 311 kprintf("%08lx -- read fault\n", ebp); 312 break; 313 } 314 315 if (eip == 0 || ebp == 0) 316 break; 317 318 print_stack_frame(thread, eip, ebp, nextEbp); 319 ebp = nextEbp; 320 } 321 322 if (already_visited(previousLocations, &last, &num, ebp)) { 323 kprintf("circular stack frame: %p!\n", (void *)ebp); 324 break; 325 } 326 if (ebp == 0) 327 break; 328 } 329 330 if (oldPageDirectory != 0) { 331 // switch back to the previous page directory to no cause any troubles 332 write_cr3(oldPageDirectory); 333 } 334 335 return 0; 336 } 337 338 339 static void 340 print_call(struct thread *thread, addr_t eip, addr_t ebp, addr_t nextEbp, 341 int32 argCount) 342 { 343 const char *symbol, *image; 344 addr_t baseAddress; 345 bool exactMatch; 346 status_t status; 347 348 status = lookup_symbol(thread, eip, &baseAddress, &symbol, &image, 349 &exactMatch); 350 351 kprintf("%08lx %08lx", ebp, eip); 352 353 if (status == B_OK) { 354 if (symbol != NULL) { 355 kprintf(" <%s>:%s%s", image, symbol, 356 exactMatch ? "" : " (nearest)"); 357 } else { 358 kprintf(" <%s@%p>:unknown + 0x%04lx", image, 359 (void *)baseAddress, eip - baseAddress); 360 } 361 } else { 362 vm_area *area = NULL; 363 if (thread->team->address_space != NULL) 364 area = vm_area_lookup(thread->team->address_space, eip); 365 if (area != NULL) { 366 kprintf(" %ld:%s@%p + %#lx", area->id, area->name, 367 (void *)area->base, eip - area->base); 368 } 369 } 370 371 int32 *arg = (int32 *)(nextEbp + 8); 372 kprintf("("); 373 374 for (int32 i = 0; i < argCount; i++) { 375 if (i > 0) 376 kprintf(", "); 377 kprintf("%#lx", *arg); 378 if (*arg > -0x10000 && *arg < 0x10000) 379 kprintf(" (%ld)", *arg); 380 381 char name[8]; 382 snprintf(name, sizeof(name), "_arg%ld", i + 1); 383 set_debug_variable(name, *(uint32 *)arg); 384 385 arg++; 386 } 387 388 kprintf(")\n"); 389 390 set_debug_variable("_frame", nextEbp); 391 } 392 393 394 static int 395 show_call(int argc, char **argv) 396 { 397 static const char* usage 398 = "usage: %s [ <thread id> ] <call index> [ -<arg count> ]\n" 399 "Prints a function call with parameters of the current, respectively\n" 400 "the specified thread.\n" 401 " <thread id> - The ID of the thread for which to print the call.\n" 402 " <call index> - The index of the call in the stack trace.\n" 403 " <arg count> - The number of call arguments to print.\n"; 404 if (argc == 2 && strcmp(argv[1], "--help") == 0) { 405 kprintf(usage, argv[0]); 406 return 0; 407 } 408 409 struct thread *thread = NULL; 410 addr_t oldPageDirectory = 0; 411 addr_t ebp = x86_read_ebp(); 412 int32 argCount = 0; 413 414 if (argc >= 2 && argv[argc - 1][0] == '-') { 415 argCount = strtoul(argv[argc - 1] + 1, NULL, 0); 416 if (argCount < 0 || argCount > 16) { 417 kprintf("Invalid argument count \"%ld\".\n", argCount); 418 return 0; 419 } 420 argc--; 421 } 422 423 if (argc < 2 || argc > 3) { 424 kprintf(usage, argv[0]); 425 return 0; 426 } 427 428 setup_for_thread(argc == 3 ? argv[1] : NULL, &thread, &ebp, 429 &oldPageDirectory); 430 431 int32 callIndex = strtoul(argv[argc == 3 ? 2 : 1], NULL, 0); 432 433 if (thread != NULL) 434 kprintf("thread %ld, %s\n", thread->id, thread->name); 435 436 bool onKernelStack = true; 437 438 for (int32 index = 0; index <= callIndex; index++) { 439 onKernelStack = onKernelStack 440 && is_kernel_stack_address(thread, ebp); 441 442 if (onKernelStack && is_iframe(thread, ebp)) { 443 struct iframe *frame = (struct iframe *)ebp; 444 445 if (index == callIndex) 446 print_call(thread, frame->eip, ebp, frame->ebp, argCount); 447 448 ebp = frame->ebp; 449 } else { 450 addr_t eip, nextEbp; 451 452 if (get_next_frame(ebp, &nextEbp, &eip) != B_OK) { 453 kprintf("%08lx -- read fault\n", ebp); 454 break; 455 } 456 457 if (eip == 0 || ebp == 0) 458 break; 459 460 if (index == callIndex) 461 print_call(thread, eip, ebp, nextEbp, argCount); 462 463 ebp = nextEbp; 464 } 465 466 if (ebp == 0) 467 break; 468 } 469 470 if (oldPageDirectory != 0) { 471 // switch back to the previous page directory to not cause any troubles 472 write_cr3(oldPageDirectory); 473 } 474 475 return 0; 476 } 477 478 479 static int 480 dump_iframes(int argc, char **argv) 481 { 482 static const char* usage = "usage: %s [ <thread id> ]\n" 483 "Prints the iframe stack for the current, respectively the specified\n" 484 "thread.\n" 485 " <thread id> - The ID of the thread for which to print the iframe\n" 486 " stack.\n"; 487 if (argc == 2 && strcmp(argv[1], "--help") == 0) { 488 kprintf(usage, argv[0]); 489 return 0; 490 } 491 492 struct thread *thread = NULL; 493 int32 i; 494 495 if (argc < 2) { 496 thread = thread_get_current_thread(); 497 } else if (argc == 2) { 498 thread_id id = strtoul(argv[1], NULL, 0); 499 thread = thread_get_thread_struct_locked(id); 500 if (thread == NULL) { 501 kprintf("could not find thread %ld\n", id); 502 return 0; 503 } 504 } else if (argc > 2) { 505 kprintf(usage, argv[0]); 506 return 0; 507 } 508 509 if (thread != NULL) 510 kprintf("iframes for thread %ld \"%s\"\n", thread->id, thread->name); 511 512 struct iframe* frame = find_previous_iframe(thread, x86_read_ebp()); 513 while (frame != NULL) { 514 print_iframe(frame); 515 frame = get_previous_iframe(thread, frame); 516 } 517 518 return 0; 519 } 520 521 522 static bool 523 is_calling(struct thread *thread, addr_t eip, const char *pattern, 524 addr_t start, addr_t end) 525 { 526 if (pattern == NULL) 527 return eip >= start && eip < end; 528 529 const char *symbol; 530 if (lookup_symbol(thread, eip, NULL, &symbol, NULL, NULL) != B_OK) 531 return false; 532 533 return strstr(symbol, pattern); 534 } 535 536 537 // #pragma mark - 538 539 540 bool 541 arch_debug_contains_call(struct thread *thread, const char *symbol, 542 addr_t start, addr_t end) 543 { 544 addr_t ebp; 545 if (thread == thread_get_current_thread()) 546 ebp = x86_read_ebp(); 547 else 548 ebp = thread->arch_info.current_stack.esp[2]; 549 550 for (;;) { 551 if (!is_kernel_stack_address(thread, ebp)) 552 break; 553 554 if (is_iframe(thread, ebp)) { 555 struct iframe *frame = (struct iframe *)ebp; 556 557 if (is_calling(thread, frame->eip, symbol, start, end)) 558 return true; 559 560 ebp = frame->ebp; 561 } else { 562 addr_t eip, nextEbp; 563 564 if (get_next_frame(ebp, &nextEbp, &eip) != B_OK 565 || eip == 0 || ebp == 0) 566 break; 567 568 if (is_calling(thread, eip, symbol, start, end)) 569 return true; 570 571 ebp = nextEbp; 572 } 573 574 if (ebp == 0) 575 break; 576 } 577 578 return false; 579 } 580 581 582 void * 583 arch_debug_get_caller(void) 584 { 585 struct stack_frame *frame = (struct stack_frame *)x86_read_ebp(); 586 return (void *)frame->previous->return_address; 587 } 588 589 590 int32 591 arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount, 592 int32 skipFrames, bool userOnly) 593 { 594 // always skip our own frame 595 skipFrames++; 596 597 struct thread* thread = thread_get_current_thread(); 598 int32 count = 0; 599 addr_t ebp = x86_read_ebp(); 600 bool onKernelStack = true; 601 602 while (ebp != 0 && count < maxCount) { 603 onKernelStack = onKernelStack 604 && is_kernel_stack_address(thread, ebp); 605 606 addr_t eip; 607 addr_t nextEbp; 608 609 if (onKernelStack && is_iframe(thread, ebp)) { 610 struct iframe *frame = (struct iframe*)ebp; 611 eip = frame->eip; 612 nextEbp = frame->ebp; 613 } else { 614 if (get_next_frame(ebp, &nextEbp, &eip) != B_OK) 615 break; 616 } 617 618 if (skipFrames <= 0 && (!userOnly || IS_USER_ADDRESS(ebp))) 619 returnAddresses[count++] = eip; 620 else 621 skipFrames--; 622 623 ebp = nextEbp; 624 } 625 626 return count; 627 } 628 629 630 status_t 631 arch_debug_init(kernel_args *args) 632 { 633 // at this stage, the debugger command system is alive 634 635 add_debugger_command("where", &stack_trace, "Same as \"sc\""); 636 add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)"); 637 add_debugger_command("sc", &stack_trace, 638 "Stack crawl for current thread (or any other)"); 639 add_debugger_command("iframe", &dump_iframes, 640 "Dump iframes for the specified thread"); 641 add_debugger_command("call", &show_call, "Show call with arguments"); 642 643 return B_NO_ERROR; 644 } 645 646