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