1 /* 2 * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <ctype.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <errno.h> 12 #include <signal.h> 13 14 #include <map> 15 #include <string> 16 #include <vector> 17 18 #include <debugger.h> 19 #include <image.h> 20 #include <syscalls.h> 21 22 #include "debug_utils.h" 23 24 #include "Context.h" 25 #include "MemoryReader.h" 26 #include "Syscall.h" 27 #include "TypeHandler.h" 28 29 30 using std::map; 31 using std::string; 32 using std::vector; 33 34 35 extern void get_syscalls0(vector<Syscall*> &syscalls); 36 extern void get_syscalls1(vector<Syscall*> &syscalls); 37 extern void get_syscalls2(vector<Syscall*> &syscalls); 38 extern void get_syscalls3(vector<Syscall*> &syscalls); 39 extern void get_syscalls4(vector<Syscall*> &syscalls); 40 extern void get_syscalls5(vector<Syscall*> &syscalls); 41 extern void get_syscalls6(vector<Syscall*> &syscalls); 42 extern void get_syscalls7(vector<Syscall*> &syscalls); 43 extern void get_syscalls8(vector<Syscall*> &syscalls); 44 extern void get_syscalls9(vector<Syscall*> &syscalls); 45 extern void get_syscalls10(vector<Syscall*> &syscalls); 46 extern void get_syscalls11(vector<Syscall*> &syscalls); 47 extern void get_syscalls12(vector<Syscall*> &syscalls); 48 extern void get_syscalls13(vector<Syscall*> &syscalls); 49 extern void get_syscalls14(vector<Syscall*> &syscalls); 50 extern void get_syscalls15(vector<Syscall*> &syscalls); 51 extern void get_syscalls16(vector<Syscall*> &syscalls); 52 extern void get_syscalls17(vector<Syscall*> &syscalls); 53 extern void get_syscalls18(vector<Syscall*> &syscalls); 54 extern void get_syscalls19(vector<Syscall*> &syscalls); 55 56 57 extern const char *__progname; 58 static const char *kCommandName = __progname; 59 60 61 // usage 62 static const char *kUsage = 63 "Usage: %s [ <options> ] [ <thread or team ID> | <executable with args> ]\n" 64 "\n" 65 "Traces the the syscalls of a thread or a team. If an executable with\n" 66 "arguments is supplied, it is loaded and it's main thread traced.\n" 67 "\n" 68 "Options:\n" 69 " -a - Don't print syscall arguments.\n" 70 " -c - Don't colorize output.\n" 71 " -d <name> - Filter the types that have their contents retrieved.\n" 72 " <name> is one of: strings, enums, simple, complex or\n" 73 " pointer_values\n" 74 " -f - Fast mode. Syscall arguments contents aren't retrieved.\n" 75 " -h, --help - Print this text.\n" 76 " -i - Print integers in decimal format instead of hexadecimal.\n" 77 " -l - Also trace loading the executable. Only considered when\n" 78 " an executable is provided.\n" 79 " -r - Don't print syscall return values.\n" 80 " -s - Also trace all threads spawned by the supplied thread,\n" 81 " respectively the loaded executable's main thread.\n" 82 " -t - Also recursively trace all teams created by a traced\n" 83 " thread or team.\n" 84 " -T - Trace all threads of the supplied or loaded executable's\n" 85 " team. If an ID is supplied, it is interpreted as a team\n" 86 " ID.\n" 87 " -o <file> - directs output into the specified file.\n" 88 " -S - prints output to serial debug line.\n" 89 " -g - turns off signal tracing.\n" 90 ; 91 92 93 // terminal color escape sequences 94 // (http://www.dee.ufcg.edu.br/~rrbrandt/tools/ansi.html) 95 static const char *kTerminalTextNormal = "\33[0m"; 96 static const char *kTerminalTextRed = "\33[31m"; 97 static const char *kTerminalTextMagenta = "\33[35m"; 98 static const char *kTerminalTextBlue = "\33[34m"; 99 100 101 // command line args 102 static int sArgc; 103 static const char *const *sArgv; 104 105 // syscalls 106 static vector<Syscall*> sSyscallVector; 107 static map<string, Syscall*> sSyscallMap; 108 109 110 struct Team { 111 Team(team_id id) 112 : 113 fID(id), 114 fNubPort(-1) 115 { 116 } 117 118 team_id ID() const 119 { 120 return fID; 121 } 122 123 port_id NubPort() const 124 { 125 return fNubPort; 126 } 127 128 MemoryReader& GetMemoryReader() 129 { 130 return fMemoryReader; 131 } 132 133 status_t InstallDebugger(port_id debuggerPort, bool traceTeam, 134 bool traceChildTeams, bool traceSignal) 135 { 136 fNubPort = install_team_debugger(fID, debuggerPort); 137 if (fNubPort < 0) { 138 fprintf(stderr, "%s: Failed to install team debugger: %s\n", 139 kCommandName, strerror(fNubPort)); 140 return fNubPort; 141 } 142 143 // set team debugging flags 144 int32 teamDebugFlags = (traceTeam ? B_TEAM_DEBUG_POST_SYSCALL : 0) 145 | (traceChildTeams ? B_TEAM_DEBUG_TEAM_CREATION : 0) 146 | (traceSignal ? B_TEAM_DEBUG_SIGNALS : 0); 147 set_team_debugging_flags(fNubPort, teamDebugFlags); 148 149 return fMemoryReader.Init(fNubPort); 150 } 151 152 private: 153 team_id fID; 154 port_id fNubPort; 155 MemoryReader fMemoryReader; 156 }; 157 158 159 static void 160 print_usage(bool error) 161 { 162 // print usage 163 fprintf((error ? stderr : stdout), kUsage, kCommandName); 164 } 165 166 167 static void 168 print_usage_and_exit(bool error) 169 { 170 print_usage(error); 171 exit(error ? 1 : 0); 172 } 173 174 175 static bool 176 get_id(const char *str, int32 &id) 177 { 178 int32 len = strlen(str); 179 for (int32 i = 0; i < len; i++) { 180 if (!isdigit(str[i])) 181 return false; 182 } 183 184 id = atol(str); 185 return true; 186 } 187 188 189 Syscall * 190 get_syscall(const char *name) 191 { 192 map<string, Syscall *>::const_iterator i = sSyscallMap.find(name); 193 if (i == sSyscallMap.end()) 194 return NULL; 195 196 return i->second; 197 } 198 199 200 static void 201 patch_syscalls() 202 { 203 // instead of having this done here manually we should either add the 204 // patching step to gensyscalls also manually or add metadata to 205 // kernel/syscalls.h and have it parsed automatically 206 extern void patch_ioctl(); 207 208 patch_ioctl(); 209 } 210 211 212 static void 213 init_syscalls() 214 { 215 // init the syscall vector 216 get_syscalls0(sSyscallVector); 217 get_syscalls1(sSyscallVector); 218 get_syscalls2(sSyscallVector); 219 get_syscalls3(sSyscallVector); 220 get_syscalls4(sSyscallVector); 221 get_syscalls5(sSyscallVector); 222 get_syscalls6(sSyscallVector); 223 get_syscalls7(sSyscallVector); 224 get_syscalls8(sSyscallVector); 225 get_syscalls9(sSyscallVector); 226 get_syscalls10(sSyscallVector); 227 get_syscalls11(sSyscallVector); 228 get_syscalls12(sSyscallVector); 229 get_syscalls13(sSyscallVector); 230 get_syscalls14(sSyscallVector); 231 get_syscalls15(sSyscallVector); 232 get_syscalls16(sSyscallVector); 233 get_syscalls17(sSyscallVector); 234 get_syscalls18(sSyscallVector); 235 get_syscalls19(sSyscallVector); 236 237 // init the syscall map 238 int32 count = sSyscallVector.size(); 239 for (int32 i = 0; i < count; i++) { 240 Syscall *syscall = sSyscallVector[i]; 241 sSyscallMap[syscall->Name()] = syscall; 242 } 243 244 patch_syscalls(); 245 } 246 247 248 static void 249 print_to_string(char **_buffer, int32 *_length, const char *format, ...) 250 { 251 va_list list; 252 va_start(list, format); 253 ssize_t length = vsnprintf(*_buffer, *_length, format, list); 254 va_end(list); 255 256 *_buffer += length; 257 *_length -= length; 258 } 259 260 261 static void 262 print_syscall(FILE *outputFile, debug_post_syscall &message, 263 MemoryReader &memoryReader, bool printArguments, uint32 contentsFlags, 264 bool printReturnValue, bool colorize, bool decimal) 265 { 266 char buffer[4096], *string = buffer; 267 int32 length = (int32)sizeof(buffer); 268 int32 syscallNumber = message.syscall; 269 Syscall *syscall = sSyscallVector[syscallNumber]; 270 271 Context ctx(syscall, (char *)message.args, memoryReader, 272 contentsFlags, decimal); 273 274 // print syscall name 275 if (colorize) { 276 print_to_string(&string, &length, "[%6ld] %s%s%s(", 277 message.origin.thread, kTerminalTextBlue, syscall->Name().c_str(), 278 kTerminalTextNormal); 279 } else { 280 print_to_string(&string, &length, "[%6ld] %s(", 281 message.origin.thread, syscall->Name().c_str()); 282 } 283 284 // print arguments 285 if (printArguments) { 286 int32 count = syscall->CountParameters(); 287 for (int32 i = 0; i < count; i++) { 288 // get the value 289 Parameter *parameter = syscall->ParameterAt(i); 290 TypeHandler *handler = parameter->Handler(); 291 ::string value = 292 handler->GetParameterValue(ctx, parameter, 293 ctx.GetValue(parameter)); 294 295 print_to_string(&string, &length, (i > 0 ? ", %s" : "%s"), 296 value.c_str()); 297 } 298 } 299 300 print_to_string(&string, &length, ")"); 301 302 // print return value 303 if (printReturnValue) { 304 Type *returnType = syscall->ReturnType(); 305 TypeHandler *handler = returnType->Handler(); 306 ::string value = handler->GetReturnValue(ctx, message.return_value); 307 if (value.length() > 0) { 308 print_to_string(&string, &length, " = %s", value.c_str()); 309 310 // if the return type is status_t or ssize_t, print human-readable 311 // error codes 312 if (returnType->TypeName() == "status_t" 313 || ((returnType->TypeName() == "ssize_t" 314 || returnType->TypeName() == "int") 315 && message.return_value < 0)) { 316 print_to_string(&string, &length, " %s", strerror(message.return_value)); 317 } 318 } 319 } 320 321 if (colorize) { 322 print_to_string(&string, &length, " %s(%lld us)%s\n", kTerminalTextMagenta, 323 message.end_time - message.start_time, kTerminalTextNormal); 324 } else { 325 print_to_string(&string, &length, " (%lld us)\n", 326 message.end_time - message.start_time); 327 } 328 329 //for (int32 i = 0; i < 16; i++) { 330 // if (i % 4 == 0) { 331 // if (i > 0) 332 // printf("\n"); 333 // printf(" "); 334 // } else 335 // printf(" "); 336 // printf("%08lx", message.args[i]); 337 //} 338 //printf("\n"); 339 340 // output either to file or serial debug line 341 if (outputFile != NULL) 342 fwrite(buffer, sizeof(buffer) - length, 1, outputFile); 343 else 344 _kern_debug_output(buffer); 345 } 346 347 348 static const char * 349 signal_name(int signal) 350 { 351 if (signal >= 0 && signal < NSIG) 352 return strsignal(signal); 353 354 static char buffer[32]; 355 sprintf(buffer, "%d", signal); 356 return buffer; 357 } 358 359 360 static void 361 print_signal(FILE *outputFile, debug_signal_received &message, 362 bool colorize) 363 { 364 char buffer[4096], *string = buffer; 365 int32 length = (int32)sizeof(buffer); 366 int signalNumber = message.signal; 367 368 // print signal name 369 if (colorize) { 370 print_to_string(&string, &length, "[%6ld] --- %s%s (%s) %s---\n", 371 message.origin.thread, kTerminalTextRed, signal_name(signalNumber), 372 strsignal(signalNumber), kTerminalTextNormal); 373 } else { 374 print_to_string(&string, &length, "[%6ld] --- %s (%s) ---\n", 375 message.origin.thread, signal_name(signalNumber), 376 strsignal(signalNumber)); 377 } 378 379 // output either to file or serial debug line 380 if (outputFile != NULL) 381 fwrite(buffer, sizeof(buffer) - length, 1, outputFile); 382 else 383 _kern_debug_output(buffer); 384 } 385 386 387 int 388 main(int argc, const char *const *argv) 389 { 390 sArgc = argc; 391 sArgv = argv; 392 393 // parameters 394 const char *const *programArgs = NULL; 395 int32 programArgCount = 0; 396 bool printArguments = true; 397 bool colorize = true; 398 uint32 contentsFlags = 0; 399 bool decimalFormat = false; 400 bool fastMode = false; 401 bool traceLoading = false; 402 bool printReturnValues = true; 403 bool traceChildThreads = false; 404 bool traceTeam = false; 405 bool traceChildTeams = false; 406 bool traceSignal = true; 407 bool serialOutput = false; 408 FILE *outputFile = stdout; 409 410 // parse arguments 411 for (int argi = 1; argi < argc; argi++) { 412 const char *arg = argv[argi]; 413 if (arg[0] == '-') { 414 // ToDo: improve option parsing so that ie. "-rsf" would also work 415 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { 416 print_usage_and_exit(false); 417 } else if (strcmp(arg, "-a") == 0) { 418 printArguments = false; 419 } else if (strcmp(arg, "-c") == 0) { 420 colorize = false; 421 } else if (strcmp(arg, "-d") == 0) { 422 const char *what = NULL; 423 424 if (arg[2] == '\0' 425 && argi + 1 < argc && argv[argi + 1][0] != '-') { 426 // next arg is what 427 what = argv[++argi]; 428 } else 429 print_usage_and_exit(true); 430 431 if (strcasecmp(what, "strings") == 0) 432 contentsFlags |= Context::STRINGS; 433 else if (strcasecmp(what, "enums") == 0) 434 contentsFlags |= Context::ENUMERATIONS; 435 else if (strcasecmp(what, "simple") == 0) 436 contentsFlags |= Context::SIMPLE_STRUCTS; 437 else if (strcasecmp(what, "complex") == 0) 438 contentsFlags |= Context::COMPLEX_STRUCTS; 439 else if (strcasecmp(what, "pointer_values") == 0) 440 contentsFlags |= Context::POINTER_VALUES; 441 else { 442 fprintf(stderr, "%s: Unknown content filter `%s'\n", 443 kCommandName, what); 444 exit(1); 445 } 446 } else if (strcmp(arg, "-f") == 0) { 447 fastMode = true; 448 } else if (strcmp(arg, "-i") == 0) { 449 decimalFormat = true; 450 } else if (strcmp(arg, "-l") == 0) { 451 traceLoading = true; 452 } else if (strcmp(arg, "-r") == 0) { 453 printReturnValues = false; 454 } else if (strcmp(arg, "-s") == 0) { 455 traceChildThreads = true; 456 } else if (strcmp(arg, "-t") == 0) { 457 traceChildTeams = true; 458 } else if (strcmp(arg, "-T") == 0) { 459 traceTeam = true; 460 } else if (strcmp(arg, "-g") == 0) { 461 traceSignal = false; 462 } else if (strcmp(arg, "-S") == 0) { 463 serialOutput = true; 464 outputFile = NULL; 465 } else if (strncmp(arg, "-o", 2) == 0) { 466 // read filename 467 const char *filename = NULL; 468 if (arg[2] == '=') { 469 // name follows 470 filename = arg + 3; 471 } else if (arg[2] == '\0' 472 && argi + 1 < argc && argv[argi + 1][0] != '-') { 473 // next arg is name 474 filename = argv[++argi]; 475 } else 476 print_usage_and_exit(true); 477 478 outputFile = fopen(filename, "w+"); 479 if (outputFile == NULL) { 480 fprintf(stderr, "%s: Could not open `%s': %s\n", 481 kCommandName, filename, strerror(errno)); 482 exit(1); 483 } 484 } else { 485 print_usage_and_exit(true); 486 } 487 } else { 488 programArgs = argv + argi; 489 programArgCount = argc - argi; 490 break; 491 } 492 } 493 494 // check parameters 495 if (!programArgs) 496 print_usage_and_exit(true); 497 498 if (fastMode) 499 contentsFlags = 0; 500 else if (contentsFlags == 0) 501 contentsFlags = Context::ALL; 502 503 // initialize our syscalls vector and map 504 init_syscalls(); 505 506 // don't colorize the output, if we don't have a terminal 507 if (outputFile == stdout) 508 colorize = colorize && isatty(STDOUT_FILENO); 509 else if (outputFile) 510 colorize = false; 511 512 // get thread/team to be debugged 513 thread_id threadID = -1; 514 team_id teamID = -1; 515 if (programArgCount > 1 516 || !get_id(*programArgs, (traceTeam ? teamID : threadID))) { 517 // we've been given an executable and need to load it 518 threadID = load_program(programArgs, programArgCount, traceLoading); 519 if (threadID < 0) { 520 fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, 521 programArgs[0], strerror(threadID)); 522 exit(1); 523 } 524 } 525 526 // get the team ID, if we have none yet 527 if (teamID < 0) { 528 thread_info threadInfo; 529 status_t error = get_thread_info(threadID, &threadInfo); 530 if (error != B_OK) { 531 fprintf(stderr, "%s: Failed to get info for thread %ld: %s\n", 532 kCommandName, threadID, strerror(error)); 533 exit(1); 534 } 535 teamID = threadInfo.team; 536 } 537 538 // create a debugger port 539 port_id debuggerPort = create_port(10, "debugger port"); 540 if (debuggerPort < 0) { 541 fprintf(stderr, "%s: Failed to create debugger port: %s\n", 542 kCommandName, strerror(debuggerPort)); 543 exit(1); 544 } 545 546 // install ourselves as the team debugger 547 typedef map<team_id, Team*> TeamMap; 548 TeamMap debuggedTeams; 549 port_id nubPort; 550 551 { 552 Team* team = new Team(teamID); 553 status_t error = team->InstallDebugger(debuggerPort, traceTeam, 554 traceChildTeams, traceSignal); 555 if (error != B_OK) 556 exit(1); 557 558 debuggedTeams[team->ID()] = team; 559 560 nubPort = team->NubPort(); 561 } 562 563 // set thread debugging flags 564 if (threadID >= 0) { 565 int32 threadDebugFlags = 0; 566 if (!traceTeam) { 567 threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL 568 | (traceChildThreads 569 ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0); 570 } 571 set_thread_debugging_flags(nubPort, threadID, threadDebugFlags); 572 573 // resume the target thread to be sure, it's running 574 resume_thread(threadID); 575 } 576 577 // debug loop 578 while (true) { 579 bool quitLoop = false; 580 int32 code; 581 debug_debugger_message_data message; 582 ssize_t messageSize = read_port(debuggerPort, &code, &message, 583 sizeof(message)); 584 585 if (messageSize < 0) { 586 if (messageSize == B_INTERRUPTED) 587 continue; 588 589 fprintf(stderr, "%s: Reading from debugger port failed: %s\n", 590 kCommandName, strerror(messageSize)); 591 exit(1); 592 } 593 594 switch (code) { 595 case B_DEBUGGER_MESSAGE_POST_SYSCALL: 596 { 597 TeamMap::iterator it = debuggedTeams.find(message.origin.team); 598 if (it == debuggedTeams.end()) 599 break; 600 601 Team* team = it->second; 602 MemoryReader& memoryReader = team->GetMemoryReader(); 603 604 print_syscall(outputFile, message.post_syscall, memoryReader, 605 printArguments, contentsFlags, printReturnValues, 606 colorize, decimalFormat); 607 break; 608 } 609 610 case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: 611 { 612 if (traceSignal) 613 print_signal(outputFile, message.signal_received, colorize); 614 break; 615 } 616 617 case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED: 618 case B_DEBUGGER_MESSAGE_DEBUGGER_CALL: 619 case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT: 620 case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT: 621 case B_DEBUGGER_MESSAGE_SINGLE_STEP: 622 case B_DEBUGGER_MESSAGE_PRE_SYSCALL: 623 case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: 624 case B_DEBUGGER_MESSAGE_THREAD_CREATED: 625 case B_DEBUGGER_MESSAGE_THREAD_DELETED: 626 case B_DEBUGGER_MESSAGE_IMAGE_CREATED: 627 case B_DEBUGGER_MESSAGE_IMAGE_DELETED: 628 break; 629 630 case B_DEBUGGER_MESSAGE_TEAM_CREATED: 631 { 632 if (!traceChildTeams) 633 break; 634 635 Team* team = new(std::nothrow) Team( 636 message.team_created.new_team); 637 if (team == NULL) { 638 fprintf(stderr, "%s: Out of memory!\n", kCommandName); 639 break; 640 } 641 642 status_t error = team->InstallDebugger(debuggerPort, true, true, 643 traceSignal); 644 if (error != B_OK) { 645 delete team; 646 break; 647 } 648 649 debuggedTeams[team->ID()] = team; 650 break; 651 } 652 653 case B_DEBUGGER_MESSAGE_TEAM_DELETED: 654 { 655 // a debugged team is gone 656 TeamMap::iterator it = debuggedTeams.find(message.origin.team); 657 if (it == debuggedTeams.end()) 658 break; 659 660 Team* team = it->second; 661 debuggedTeams.erase(it); 662 delete team; 663 664 // if all debugged teams are gone, we're done 665 quitLoop = debuggedTeams.empty(); 666 break; 667 } 668 } 669 670 if (quitLoop) 671 break; 672 673 // tell the thread to continue (only when there is a thread and the 674 // message was synchronous) 675 if (message.origin.thread >= 0 && message.origin.nub_port >= 0) 676 continue_thread(message.origin.nub_port, message.origin.thread); 677 } 678 679 if (outputFile != NULL && outputFile != stdout) 680 fclose(outputFile); 681 682 return 0; 683 } 684