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 {
TeamTeam134 Team(team_id id)
135 :
136 fID(id),
137 fNubPort(-1)
138 {
139 }
140
IDTeam141 team_id ID() const
142 {
143 return fID;
144 }
145
NubPortTeam146 port_id NubPort() const
147 {
148 return fNubPort;
149 }
150
GetMemoryReaderTeam151 MemoryReader& GetMemoryReader()
152 {
153 return fMemoryReader;
154 }
155
InstallDebuggerTeam156 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
print_usage(bool error)185 print_usage(bool error)
186 {
187 // print usage
188 fprintf((error ? stderr : stdout), kUsage, kCommandName);
189 }
190
191
192 static void
print_usage_and_exit(bool error)193 print_usage_and_exit(bool error)
194 {
195 print_usage(error);
196 exit(error ? 1 : 0);
197 }
198
199
200 static bool
get_id(const char * str,int32 & id)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 *
get_syscall(const char * name)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
patch_syscalls()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
init_syscalls()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
record_syscall_stats(const Syscall & syscall,debug_post_syscall & message)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
print_buffer(FILE * outputFile,char * buffer,int32 length)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
print_to_string(char ** _buffer,int32 * _length,const char * format,...)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
print_syscall(FILE * outputFile,Syscall * syscall,debug_pre_syscall & message,MemoryReader & memoryReader,bool printArguments,uint32 contentsFlags,bool colorize,bool decimal,thread_id & currentThreadID)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
print_syscall(FILE * outputFile,Syscall * syscall,debug_post_syscall & message,MemoryReader & memoryReader,bool printArguments,uint32 contentsFlags,bool printReturnValue,bool colorize,bool decimal,thread_id & currentThreadID)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
print_signal(FILE * outputFile,debug_signal_received & message,bool colorize)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
compare_stats_by_time(const std::pair<const std::string *,const syscall_stats * > & a,const std::pair<const std::string *,const syscall_stats * > & b)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
print_stats(FILE * outputFile)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
main(int argc,const char * const * argv)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