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