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