1 /* 2 * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2013, Rene Gollent, rene@gollent.com. 4 * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 #include <ctype.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <strings.h> 14 #include <errno.h> 15 #include <signal.h> 16 17 #include <algorithm> 18 #include <map> 19 #include <string> 20 #include <vector> 21 22 #include <debugger.h> 23 #include <image.h> 24 #include <syscalls.h> 25 26 #include "debug_utils.h" 27 28 #include "Context.h" 29 #include "MemoryReader.h" 30 #include "Syscall.h" 31 #include "TypeHandler.h" 32 33 34 using std::map; 35 using std::string; 36 using std::vector; 37 38 39 struct syscall_stats { 40 bigtime_t time; 41 uint32 count; 42 }; 43 44 45 extern void get_syscalls0(vector<Syscall*> &syscalls); 46 extern void get_syscalls1(vector<Syscall*> &syscalls); 47 extern void get_syscalls2(vector<Syscall*> &syscalls); 48 extern void get_syscalls3(vector<Syscall*> &syscalls); 49 extern void get_syscalls4(vector<Syscall*> &syscalls); 50 extern void get_syscalls5(vector<Syscall*> &syscalls); 51 extern void get_syscalls6(vector<Syscall*> &syscalls); 52 extern void get_syscalls7(vector<Syscall*> &syscalls); 53 extern void get_syscalls8(vector<Syscall*> &syscalls); 54 extern void get_syscalls9(vector<Syscall*> &syscalls); 55 extern void get_syscalls10(vector<Syscall*> &syscalls); 56 extern void get_syscalls11(vector<Syscall*> &syscalls); 57 extern void get_syscalls12(vector<Syscall*> &syscalls); 58 extern void get_syscalls13(vector<Syscall*> &syscalls); 59 extern void get_syscalls14(vector<Syscall*> &syscalls); 60 extern void get_syscalls15(vector<Syscall*> &syscalls); 61 extern void get_syscalls16(vector<Syscall*> &syscalls); 62 extern void get_syscalls17(vector<Syscall*> &syscalls); 63 extern void get_syscalls18(vector<Syscall*> &syscalls); 64 extern void get_syscalls19(vector<Syscall*> &syscalls); 65 66 67 extern const char *__progname; 68 static const char *kCommandName = __progname; 69 70 71 // usage 72 static const char *kUsage = 73 "Usage: %s [ <options> ] [ <thread or team ID> | <executable with args> ]\n" 74 "\n" 75 "Traces the syscalls of a thread or a team. If an executable with\n" 76 "arguments is supplied, it is loaded and it's main thread traced.\n" 77 "\n" 78 "Options:\n" 79 " -a - Don't print syscall arguments.\n" 80 " -c - Record and dump syscall usage statistics.\n" 81 " -C - Same as -c, but also print syscalls as usual.\n" 82 " -d <name> - Filter the types that have their contents retrieved.\n" 83 " <name> is one of: strings, enums, simple, complex or\n" 84 " pointer_values\n" 85 " -f - Fast mode. Syscall arguments contents aren't retrieved.\n" 86 " -h, --help - Print this text.\n" 87 " -i - Print integers in decimal format instead of hexadecimal.\n" 88 " -l - Also trace loading the executable. Only considered when\n" 89 " an executable is provided.\n" 90 " --no-color - Don't colorize output.\n" 91 " -r - Don't print syscall return values.\n" 92 " -s - Also trace all threads spawned by the supplied thread,\n" 93 " respectively the loaded executable's main thread.\n" 94 " -t - Also recursively trace all teams created by a traced\n" 95 " thread or team.\n" 96 " -T - Trace all threads of the supplied or loaded executable's\n" 97 " team. If an ID is supplied, it is interpreted as a team\n" 98 " ID.\n" 99 " -o <file> - directs output into the specified file.\n" 100 " -S - prints output to serial debug line.\n" 101 " -g - turns off signal tracing.\n" 102 ; 103 104 105 // terminal color escape sequences 106 // (http://www.dee.ufcg.edu.br/~rrbrandt/tools/ansi.html) 107 static const char *kTerminalTextNormal = "\33[0m"; 108 static const char *kTerminalTextRed = "\33[31m"; 109 static const char *kTerminalTextMagenta = "\33[35m"; 110 static const char *kTerminalTextBlue = "\33[34m"; 111 112 113 // signal names 114 static const char *kSignalName[] = { 115 /* 0 */ "SIG0", 116 /* 1 */ "SIGHUP", 117 /* 2 */ "SIGINT", 118 /* 3 */ "SIGQUIT", 119 /* 4 */ "SIGILL", 120 /* 5 */ "SIGCHLD", 121 /* 6 */ "SIGABRT", 122 /* 7 */ "SIGPIPE", 123 /* 8 */ "SIGFPE", 124 /* 9 */ "SIGKILL", 125 /* 10 */ "SIGSTOP", 126 /* 11 */ "SIGSEGV", 127 /* 12 */ "SIGCONT", 128 /* 13 */ "SIGTSTP", 129 /* 14 */ "SIGALRM", 130 /* 15 */ "SIGTERM", 131 /* 16 */ "SIGTTIN", 132 /* 17 */ "SIGTTOU", 133 /* 18 */ "SIGUSR1", 134 /* 19 */ "SIGUSR2", 135 /* 20 */ "SIGWINCH", 136 /* 21 */ "SIGKILLTHR", 137 /* 22 */ "SIGTRAP", 138 /* 23 */ "SIGPOLL", 139 /* 24 */ "SIGPROF", 140 /* 25 */ "SIGSYS", 141 /* 26 */ "SIGURG", 142 /* 27 */ "SIGVTALRM", 143 /* 28 */ "SIGXCPU", 144 /* 29 */ "SIGXFSZ", 145 /* 30 */ "SIGBUS", 146 /* 31 */ "SIGRESERVED1", 147 /* 32 */ "SIGRESERVED2", 148 }; 149 150 151 // command line args 152 static int sArgc; 153 static const char *const *sArgv; 154 155 // syscalls 156 static vector<Syscall*> sSyscallVector; 157 static map<string, Syscall*> sSyscallMap; 158 159 // statistics 160 typedef map<string, syscall_stats> StatsMap; 161 static StatsMap sSyscallStats; 162 static bigtime_t sSyscallTime; 163 164 165 struct Team { 166 Team(team_id id) 167 : 168 fID(id), 169 fNubPort(-1) 170 { 171 } 172 173 team_id ID() const 174 { 175 return fID; 176 } 177 178 port_id NubPort() const 179 { 180 return fNubPort; 181 } 182 183 MemoryReader& GetMemoryReader() 184 { 185 return fMemoryReader; 186 } 187 188 status_t InstallDebugger(port_id debuggerPort, bool traceTeam, 189 bool traceChildTeams, bool traceSignal) 190 { 191 fNubPort = install_team_debugger(fID, debuggerPort); 192 if (fNubPort < 0) { 193 fprintf(stderr, "%s: Failed to install team debugger: %s\n", 194 kCommandName, strerror(fNubPort)); 195 return fNubPort; 196 } 197 198 // set team debugging flags 199 int32 teamDebugFlags = 200 (traceTeam ? B_TEAM_DEBUG_PRE_SYSCALL | B_TEAM_DEBUG_POST_SYSCALL : 0) 201 | (traceChildTeams ? B_TEAM_DEBUG_TEAM_CREATION : 0) 202 | (traceSignal ? B_TEAM_DEBUG_SIGNALS : 0); 203 if (set_team_debugging_flags(fNubPort, teamDebugFlags) != B_OK) 204 exit(1); 205 206 return fMemoryReader.Init(fNubPort); 207 } 208 209 private: 210 team_id fID; 211 port_id fNubPort; 212 MemoryReader fMemoryReader; 213 }; 214 215 216 static void 217 print_usage(bool error) 218 { 219 // print usage 220 fprintf((error ? stderr : stdout), kUsage, kCommandName); 221 } 222 223 224 static void 225 print_usage_and_exit(bool error) 226 { 227 print_usage(error); 228 exit(error ? 1 : 0); 229 } 230 231 232 static bool 233 get_id(const char *str, int32 &id) 234 { 235 int32 len = strlen(str); 236 for (int32 i = 0; i < len; i++) { 237 if (!isdigit(str[i])) 238 return false; 239 } 240 241 id = atol(str); 242 return true; 243 } 244 245 246 Syscall * 247 get_syscall(const char *name) 248 { 249 map<string, Syscall *>::const_iterator i = sSyscallMap.find(name); 250 if (i == sSyscallMap.end()) 251 return NULL; 252 253 return i->second; 254 } 255 256 257 static void 258 patch_syscalls() 259 { 260 // instead of having this done here manually we should either add the 261 // patching step to gensyscalls also manually or add metadata to 262 // kernel/syscalls.h and have it parsed automatically 263 extern void patch_fcntl(); 264 extern void patch_ioctl(); 265 266 patch_fcntl(); 267 patch_ioctl(); 268 269 Syscall *poll = get_syscall("_kern_poll"); 270 poll->ParameterAt(0)->SetInOut(true); 271 272 Syscall *select = get_syscall("_kern_select"); 273 select->ParameterAt(1)->SetInOut(true); 274 select->ParameterAt(2)->SetInOut(true); 275 select->ParameterAt(3)->SetInOut(true); 276 277 Syscall *wait = get_syscall("_kern_wait_for_child"); 278 wait->ParameterAt(2)->SetOut(true); 279 wait->ParameterAt(3)->SetOut(true); 280 } 281 282 283 static void 284 init_syscalls() 285 { 286 // init the syscall vector 287 get_syscalls0(sSyscallVector); 288 get_syscalls1(sSyscallVector); 289 get_syscalls2(sSyscallVector); 290 get_syscalls3(sSyscallVector); 291 get_syscalls4(sSyscallVector); 292 get_syscalls5(sSyscallVector); 293 get_syscalls6(sSyscallVector); 294 get_syscalls7(sSyscallVector); 295 get_syscalls8(sSyscallVector); 296 get_syscalls9(sSyscallVector); 297 get_syscalls10(sSyscallVector); 298 get_syscalls11(sSyscallVector); 299 get_syscalls12(sSyscallVector); 300 get_syscalls13(sSyscallVector); 301 get_syscalls14(sSyscallVector); 302 get_syscalls15(sSyscallVector); 303 get_syscalls16(sSyscallVector); 304 get_syscalls17(sSyscallVector); 305 get_syscalls18(sSyscallVector); 306 get_syscalls19(sSyscallVector); 307 308 // init the syscall map 309 int32 count = sSyscallVector.size(); 310 for (int32 i = 0; i < count; i++) { 311 Syscall *syscall = sSyscallVector[i]; 312 sSyscallMap[syscall->Name()] = syscall; 313 } 314 315 patch_syscalls(); 316 } 317 318 319 static void 320 record_syscall_stats(const Syscall& syscall, debug_post_syscall& message) 321 { 322 syscall_stats& stats = sSyscallStats[syscall.Name()]; 323 stats.count++; 324 325 bigtime_t time = message.end_time - message.start_time; 326 stats.time += time; 327 sSyscallTime += time; 328 } 329 330 331 static void 332 print_buffer(FILE *outputFile, char* buffer, int32 length) 333 { 334 // output either to file or serial debug line 335 if (outputFile != NULL) 336 fwrite(buffer, length, 1, outputFile); 337 else 338 _kern_debug_output(buffer); 339 } 340 341 342 static void 343 print_to_string(char **_buffer, int32 *_length, const char *format, ...) 344 { 345 va_list list; 346 va_start(list, format); 347 ssize_t length = vsnprintf(*_buffer, *_length, format, list); 348 va_end(list); 349 350 *_buffer += length; 351 *_length -= length; 352 } 353 354 355 static void 356 print_syscall(FILE *outputFile, Syscall* syscall, debug_pre_syscall &message, 357 MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags, 358 bool colorize, bool decimal, thread_id ¤tThreadID) 359 { 360 char buffer[4096], *string = buffer; 361 int32 length = (int32)sizeof(buffer); 362 363 Context ctx(syscall, (char *)message.args, memoryReader, 364 contentsFlags | Context::INPUT_VALUES, decimal); 365 366 if (currentThreadID != message.origin.thread) { 367 if (currentThreadID != -1) 368 print_to_string(&string, &length, " <unfinished ...>\n"); 369 currentThreadID = message.origin.thread; 370 } 371 372 // print syscall name, without the "_kern_" 373 if (colorize) { 374 print_to_string(&string, &length, "[%6" B_PRId32 "] %s%s%s(", 375 message.origin.thread, kTerminalTextBlue, 376 syscall->Name().c_str() + 6, kTerminalTextNormal); 377 } else { 378 print_to_string(&string, &length, "[%6" B_PRId32 "] %s(", 379 message.origin.thread, syscall->Name().c_str() + 6); 380 } 381 382 // print arguments 383 if (printArguments) { 384 int32 count = syscall->CountParameters(); 385 for (int32 i = 0; i < count; i++) { 386 // get the value 387 Parameter *parameter = syscall->ParameterAt(i); 388 if (parameter->Out()) 389 continue; 390 TypeHandler *handler = parameter->Handler(); 391 ::string value = 392 handler->GetParameterValue(ctx, parameter, 393 ctx.GetValue(parameter)); 394 395 print_to_string(&string, &length, (i > 0 ? ", %s" : "%s"), 396 value.c_str()); 397 } 398 } 399 400 print_to_string(&string, &length, ")"); 401 402 print_buffer(outputFile, buffer, sizeof(buffer) - length); 403 } 404 405 406 static void 407 print_syscall(FILE *outputFile, Syscall* syscall, debug_post_syscall &message, 408 MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags, 409 bool printReturnValue, bool colorize, bool decimal, 410 thread_id ¤tThreadID) 411 { 412 char buffer[4096], *string = buffer; 413 int32 length = (int32)sizeof(buffer); 414 bool threadChanged = false; 415 416 Context ctx(syscall, (char *)message.args, memoryReader, 417 contentsFlags | Context::OUTPUT_VALUES, decimal, message.return_value); 418 419 if (currentThreadID != message.origin.thread) { 420 if (currentThreadID != -1) { 421 print_to_string(&string, &length, " <unfinished ...>\n"); 422 } 423 threadChanged = true; 424 } 425 currentThreadID = -1; 426 427 // print return value 428 if (printReturnValue) { 429 if (threadChanged) { 430 // print syscall name, without the "_kern_" 431 if (colorize) { 432 print_to_string(&string, &length, "[%6" B_PRId32 "] <... " 433 "%s%s%s resumed> ", message.origin.thread, kTerminalTextBlue, 434 syscall->Name().c_str() + 6, kTerminalTextNormal); 435 } else { 436 print_to_string(&string, &length, "[%6" B_PRId32 "] <... %s" 437 " resumed> ", message.origin.thread, 438 syscall->Name().c_str() + 6); 439 } 440 } 441 Type *returnType = syscall->ReturnType(); 442 TypeHandler *handler = returnType->Handler(); 443 ::string value = handler->GetReturnValue(ctx, message.return_value); 444 if (value.length() > 0) { 445 print_to_string(&string, &length, " = %s", value.c_str()); 446 447 // if the return type is status_t or ssize_t, print human-readable 448 // error codes 449 if (returnType->TypeName() == "status_t" 450 || ((returnType->TypeName() == "ssize_t" 451 || returnType->TypeName() == "int") 452 && message.return_value < 0)) { 453 print_to_string(&string, &length, " %s", strerror(message.return_value)); 454 } 455 } 456 } 457 458 // print arguments 459 if (printArguments) { 460 int32 count = syscall->CountParameters(); 461 int added = 0; 462 print_to_string(&string, &length, " ("); 463 for (int32 i = 0; i < count; i++) { 464 // get the value 465 Parameter *parameter = syscall->ParameterAt(i); 466 if (!parameter->InOut() && !parameter->Out()) 467 continue; 468 TypeHandler *handler = parameter->Handler(); 469 ::string value = 470 handler->GetParameterValue(ctx, parameter, 471 ctx.GetValue(parameter)); 472 473 print_to_string(&string, &length, (added > 0 ? ", %s" : "%s"), 474 value.c_str()); 475 added++; 476 } 477 print_to_string(&string, &length, ")"); 478 } 479 480 if (colorize) { 481 print_to_string(&string, &length, " %s(%lld us)%s\n", kTerminalTextMagenta, 482 message.end_time - message.start_time, kTerminalTextNormal); 483 } else { 484 print_to_string(&string, &length, " (%lld us)\n", 485 message.end_time - message.start_time); 486 } 487 488 //for (int32 i = 0; i < 16; i++) { 489 // if (i % 4 == 0) { 490 // if (i > 0) 491 // printf("\n"); 492 // printf(" "); 493 // } else 494 // printf(" "); 495 // printf("%08lx", message.args[i]); 496 //} 497 //printf("\n"); 498 print_buffer(outputFile, buffer, sizeof(buffer) - length); 499 } 500 501 502 static const char * 503 signal_name(int signal) 504 { 505 if (signal >= 0 && signal <= SIGRESERVED2) 506 return kSignalName[signal]; 507 508 static char buffer[32]; 509 sprintf(buffer, "%d", signal); 510 return buffer; 511 } 512 513 514 static void 515 print_signal(FILE *outputFile, debug_signal_received &message, 516 bool colorize) 517 { 518 char buffer[4096], *string = buffer; 519 int32 length = (int32)sizeof(buffer); 520 int signalNumber = message.signal; 521 522 // print signal name 523 if (colorize) { 524 print_to_string(&string, &length, "[%6" B_PRId32 "] --- %s%s (%s) %s---\n", 525 message.origin.thread, kTerminalTextRed, signal_name(signalNumber), 526 strsignal(signalNumber), kTerminalTextNormal); 527 } else { 528 print_to_string(&string, &length, "[%6" B_PRId32 "] --- %s (%s) ---\n", 529 message.origin.thread, signal_name(signalNumber), 530 strsignal(signalNumber)); 531 } 532 533 print_buffer(outputFile, buffer, sizeof(buffer) - length); 534 } 535 536 537 static bool 538 compare_stats_by_time( 539 const std::pair<const std::string*, const syscall_stats*>& a, 540 const std::pair<const std::string*, const syscall_stats*>& b) 541 { 542 return a.second->time > b.second->time; 543 } 544 545 546 static void 547 print_stats(FILE* outputFile) 548 { 549 char buffer[4096], *string = buffer; 550 int32 length = (int32)sizeof(buffer); 551 552 typedef std::vector<std::pair<const std::string*, const syscall_stats*> > 553 StatsRefVector; 554 StatsRefVector calls; 555 StatsMap::const_iterator iterator = sSyscallStats.begin(); 556 for (; iterator != sSyscallStats.end(); iterator++) 557 calls.push_back(std::make_pair(&iterator->first, &iterator->second)); 558 559 // Sort calls by time spent 560 std::sort(calls.begin(), calls.end(), compare_stats_by_time); 561 562 print_to_string(&string, &length, "\n%-6s %-10s %-7s %-10s Syscall\n", 563 "Time %", "Usecs", "Calls", "Usecs/call"); 564 print_to_string(&string, &length, "------ ---------- ------- ---------- " 565 "--------------------\n"); 566 567 StatsRefVector::const_iterator callIterator = calls.begin(); 568 for (; callIterator != calls.end(); callIterator++) { 569 const syscall_stats& stats = *callIterator->second; 570 double percent = stats.time * 100.0 / sSyscallTime; 571 bigtime_t perCall = stats.time / stats.count; 572 573 print_to_string(&string, &length, "%6.2f %10" B_PRIu64 " %7" B_PRIu32 574 " %10" B_PRIu64 " %s\n", percent, stats.time, stats.count, perCall, 575 callIterator->first->c_str()); 576 } 577 578 print_buffer(outputFile, buffer, sizeof(buffer) - length); 579 } 580 581 582 int 583 main(int argc, const char *const *argv) 584 { 585 sArgc = argc; 586 sArgv = argv; 587 588 // parameters 589 const char *const *programArgs = NULL; 590 int32 programArgCount = 0; 591 bool printArguments = true; 592 bool colorize = true; 593 bool stats = false; 594 bool trace = true; 595 uint32 contentsFlags = 0; 596 bool decimalFormat = false; 597 bool fastMode = false; 598 bool traceLoading = false; 599 bool printReturnValues = true; 600 bool traceChildThreads = false; 601 bool traceTeam = false; 602 bool traceChildTeams = false; 603 bool traceSignal = true; 604 bool serialOutput = false; 605 FILE *outputFile = stdout; 606 607 // parse arguments 608 for (int argi = 1; argi < argc; argi++) { 609 const char *arg = argv[argi]; 610 if (arg[0] == '-') { 611 // ToDo: improve option parsing so that ie. "-rsf" would also work 612 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { 613 print_usage_and_exit(false); 614 } else if (strcmp(arg, "-a") == 0) { 615 printArguments = false; 616 } else if (strcmp(arg, "-c") == 0) { 617 stats = true; 618 trace = false; 619 } else if (strcmp(arg, "-C") == 0) { 620 stats = true; 621 } else if (strcmp(arg, "--no-color") == 0) { 622 colorize = false; 623 } else if (strcmp(arg, "-d") == 0) { 624 const char *what = NULL; 625 626 if (arg[2] == '\0' 627 && argi + 1 < argc && argv[argi + 1][0] != '-') { 628 // next arg is what 629 what = argv[++argi]; 630 } else 631 print_usage_and_exit(true); 632 633 if (strcasecmp(what, "strings") == 0) 634 contentsFlags |= Context::STRINGS; 635 else if (strcasecmp(what, "enums") == 0) 636 contentsFlags |= Context::ENUMERATIONS; 637 else if (strcasecmp(what, "simple") == 0) 638 contentsFlags |= Context::SIMPLE_STRUCTS; 639 else if (strcasecmp(what, "complex") == 0) 640 contentsFlags |= Context::COMPLEX_STRUCTS; 641 else if (strcasecmp(what, "pointer_values") == 0) 642 contentsFlags |= Context::POINTER_VALUES; 643 else { 644 fprintf(stderr, "%s: Unknown content filter `%s'\n", 645 kCommandName, what); 646 exit(1); 647 } 648 } else if (strcmp(arg, "-f") == 0) { 649 fastMode = true; 650 } else if (strcmp(arg, "-i") == 0) { 651 decimalFormat = true; 652 } else if (strcmp(arg, "-l") == 0) { 653 traceLoading = true; 654 } else if (strcmp(arg, "-r") == 0) { 655 printReturnValues = false; 656 } else if (strcmp(arg, "-s") == 0) { 657 traceChildThreads = true; 658 } else if (strcmp(arg, "-t") == 0) { 659 traceChildTeams = true; 660 } else if (strcmp(arg, "-T") == 0) { 661 traceTeam = true; 662 } else if (strcmp(arg, "-g") == 0) { 663 traceSignal = false; 664 } else if (strcmp(arg, "-S") == 0) { 665 serialOutput = true; 666 outputFile = NULL; 667 } else if (strncmp(arg, "-o", 2) == 0) { 668 // read filename 669 const char *filename = NULL; 670 if (arg[2] == '=') { 671 // name follows 672 filename = arg + 3; 673 } else if (arg[2] == '\0' 674 && argi + 1 < argc && argv[argi + 1][0] != '-') { 675 // next arg is name 676 filename = argv[++argi]; 677 } else 678 print_usage_and_exit(true); 679 680 outputFile = fopen(filename, "w+"); 681 if (outputFile == NULL) { 682 fprintf(stderr, "%s: Could not open `%s': %s\n", 683 kCommandName, filename, strerror(errno)); 684 exit(1); 685 } 686 } else { 687 print_usage_and_exit(true); 688 } 689 } else { 690 programArgs = argv + argi; 691 programArgCount = argc - argi; 692 break; 693 } 694 } 695 696 // check parameters 697 if (!programArgs) 698 print_usage_and_exit(true); 699 700 if (fastMode) 701 contentsFlags = 0; 702 else if (contentsFlags == 0) 703 contentsFlags = Context::ALL; 704 705 // initialize our syscalls vector and map 706 init_syscalls(); 707 708 // don't colorize the output, if we don't have a terminal 709 if (outputFile == stdout) 710 colorize = colorize && isatty(STDOUT_FILENO); 711 else if (outputFile) 712 colorize = false; 713 714 // get thread/team to be debugged 715 thread_id threadID = -1; 716 team_id teamID = -1; 717 if (programArgCount > 1 718 || !get_id(*programArgs, (traceTeam ? teamID : threadID))) { 719 // we've been given an executable and need to load it 720 threadID = load_program(programArgs, programArgCount, traceLoading); 721 if (threadID < 0) { 722 fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, 723 programArgs[0], strerror(threadID)); 724 exit(1); 725 } 726 } 727 728 // get the team ID, if we have none yet 729 if (teamID < 0) { 730 thread_info threadInfo; 731 status_t error = get_thread_info(threadID, &threadInfo); 732 if (error != B_OK) { 733 fprintf(stderr, "%s: Failed to get info for thread %" B_PRId32 734 ": %s\n", kCommandName, threadID, strerror(error)); 735 exit(1); 736 } 737 teamID = threadInfo.team; 738 } 739 740 // create a debugger port 741 port_id debuggerPort = create_port(10, "debugger port"); 742 if (debuggerPort < 0) { 743 fprintf(stderr, "%s: Failed to create debugger port: %s\n", 744 kCommandName, strerror(debuggerPort)); 745 exit(1); 746 } 747 748 // install ourselves as the team debugger 749 typedef map<team_id, Team*> TeamMap; 750 TeamMap debuggedTeams; 751 port_id nubPort; 752 753 { 754 Team* team = new Team(teamID); 755 status_t error = team->InstallDebugger(debuggerPort, traceTeam, 756 traceChildTeams, traceSignal); 757 if (error != B_OK) 758 exit(1); 759 760 debuggedTeams[team->ID()] = team; 761 762 nubPort = team->NubPort(); 763 } 764 765 // set thread debugging flags 766 if (threadID >= 0) { 767 int32 threadDebugFlags = 0; 768 if (!traceTeam) { 769 threadDebugFlags = B_THREAD_DEBUG_PRE_SYSCALL | B_THREAD_DEBUG_POST_SYSCALL 770 | (traceChildThreads 771 ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0); 772 } 773 if (set_thread_debugging_flags(nubPort, threadID, threadDebugFlags) 774 != B_OK) { 775 exit(1); 776 } 777 778 // resume the target thread to be sure, it's running 779 resume_thread(threadID); 780 } 781 782 thread_id currentThreadID = -1; 783 784 // debug loop 785 while (true) { 786 bool quitLoop = false; 787 int32 code; 788 debug_debugger_message_data message; 789 ssize_t messageSize = read_port(debuggerPort, &code, &message, 790 sizeof(message)); 791 792 if (messageSize < 0) { 793 if (messageSize == B_INTERRUPTED) 794 continue; 795 796 fprintf(stderr, "%s: Reading from debugger port failed: %s\n", 797 kCommandName, strerror(messageSize)); 798 exit(1); 799 } 800 801 switch (code) { 802 case B_DEBUGGER_MESSAGE_PRE_SYSCALL: 803 { 804 TeamMap::iterator it = debuggedTeams.find(message.origin.team); 805 if (it == debuggedTeams.end()) 806 break; 807 808 Team* team = it->second; 809 MemoryReader& memoryReader = team->GetMemoryReader(); 810 811 uint32 syscallNumber = message.pre_syscall.syscall; 812 if (syscallNumber >= sSyscallVector.size()) { 813 fprintf(stderr, "%s: invalid syscall %" B_PRIu32 " attempted\n", 814 kCommandName, syscallNumber); 815 break; 816 } 817 Syscall* syscall = sSyscallVector[syscallNumber]; 818 819 if (trace) { 820 print_syscall(outputFile, syscall, message.pre_syscall, 821 memoryReader, printArguments, contentsFlags, 822 colorize, decimalFormat, currentThreadID); 823 } 824 break; 825 } 826 827 case B_DEBUGGER_MESSAGE_POST_SYSCALL: 828 { 829 TeamMap::iterator it = debuggedTeams.find(message.origin.team); 830 if (it == debuggedTeams.end()) 831 break; 832 833 Team* team = it->second; 834 MemoryReader& memoryReader = team->GetMemoryReader(); 835 836 uint32 syscallNumber = message.post_syscall.syscall; 837 if (syscallNumber >= sSyscallVector.size()) { 838 fprintf(stderr, "%s: invalid syscall %" B_PRIu32 " attempted\n", 839 kCommandName, syscallNumber); 840 break; 841 } 842 Syscall* syscall = sSyscallVector[syscallNumber]; 843 844 if (stats) 845 record_syscall_stats(*syscall, message.post_syscall); 846 847 if (trace) { 848 print_syscall(outputFile, syscall, message.post_syscall, 849 memoryReader, printArguments, contentsFlags, 850 printReturnValues, colorize, decimalFormat, 851 currentThreadID); 852 } 853 break; 854 } 855 856 case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: 857 { 858 if (traceSignal && trace) 859 print_signal(outputFile, message.signal_received, colorize); 860 break; 861 } 862 863 case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED: 864 case B_DEBUGGER_MESSAGE_DEBUGGER_CALL: 865 case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT: 866 case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT: 867 case B_DEBUGGER_MESSAGE_SINGLE_STEP: 868 case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: 869 case B_DEBUGGER_MESSAGE_THREAD_CREATED: 870 case B_DEBUGGER_MESSAGE_THREAD_DELETED: 871 case B_DEBUGGER_MESSAGE_IMAGE_CREATED: 872 case B_DEBUGGER_MESSAGE_IMAGE_DELETED: 873 break; 874 875 case B_DEBUGGER_MESSAGE_TEAM_CREATED: 876 { 877 if (!traceChildTeams) 878 break; 879 880 Team* team = new(std::nothrow) Team( 881 message.team_created.new_team); 882 if (team == NULL) { 883 fprintf(stderr, "%s: Out of memory!\n", kCommandName); 884 break; 885 } 886 887 status_t error = team->InstallDebugger(debuggerPort, true, true, 888 traceSignal); 889 if (error != B_OK) { 890 delete team; 891 break; 892 } 893 894 debuggedTeams[team->ID()] = team; 895 break; 896 } 897 898 case B_DEBUGGER_MESSAGE_TEAM_DELETED: 899 { 900 // a debugged team is gone 901 TeamMap::iterator it = debuggedTeams.find(message.origin.team); 902 if (it == debuggedTeams.end()) 903 break; 904 905 Team* team = it->second; 906 debuggedTeams.erase(it); 907 delete team; 908 909 // if all debugged teams are gone, we're done 910 quitLoop = debuggedTeams.empty(); 911 break; 912 } 913 } 914 915 if (quitLoop) 916 break; 917 918 // tell the thread to continue (only when there is a thread and the 919 // message was synchronous) 920 if (message.origin.thread >= 0 && message.origin.nub_port >= 0) { 921 if (continue_thread(message.origin.nub_port, 922 message.origin.thread) != B_OK) { 923 // the team can already be gone 924 } 925 } 926 } 927 928 if (stats) { 929 // Dump recorded statistics 930 print_stats(outputFile); 931 } 932 933 if (outputFile != NULL && outputFile != stdout) 934 fclose(outputFile); 935 936 return 0; 937 } 938