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