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