this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Many improvements for xtrace

The biggest improvement is the ability to log xtrace's output to a separate file for each thread. Jumbled output has been a big reason why xtrace is hard to use with multi-threaded programs, but this provides a nice optional solution to that problem. It doesn't come without its drawbacks, however: because xtrace has to open descriptors for the logfiles, it can affect program behavior, especially if the program sees the descriptors and decides to do something with them (like close them, which some programs do).

xtrace no longer buffers output on the stack (which could lead to stack overflows or truncated output). Actually, it doesn't buffer output at all anymore, which might be an issue (it means more potentially jumbled output when using the normal output method). If turns out to be a signifcant issue, we can re-add buffering in xtrace_log using a per-thread buffer rather than an on-stack one.

The kevent family of syscalls is now properly described! This means that those calls will now print much more useful output.

Also, to work around a stack overflow issue when running within signal handlers, xtrace now overrides the default sigstack with its own larger one. It's not apparent why xtrace is using so much stack space, but it seems like 16KiB is enough for now (rather than the default of 8KiB).

Executing the xtrace command with no arguments now produces a help message describing the various environment variables that can be used to modify xtrace's behavior.

Also, the simple printf family of functions in libsystem_kernel now support various argument sizes.

Finally, there's no reason to call the wrapper for `semaphore_signal_trap` in `bsdthread_terminate` (causing unnecessary xtrace output), so call our implementation directly instead.

+1156 -313
+1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/darling/emulation/signal/sigaltstack.h
··· 1 + ../../../../../../../../../../../src/kernel/emulation/linux/signal/sigaltstack.h
+2 -2
src/kernel/emulation/linux/bsdthread/bsdthread_terminate.c
··· 14 14 unsigned long freesize, 15 15 int thread, 16 16 int sem); 17 - int semaphore_signal_trap(int signal_name); 17 + int semaphore_signal_trap_impl(int signal_name); 18 18 19 19 long sys_bsdthread_terminate(void* stackaddr, unsigned long freesize, int port, 20 20 int join_sem) ··· 26 26 unsigned long freesize, unsigned long pthobj_size); 27 27 extern int lkm_call(int, void*); 28 28 29 - semaphore_signal_trap(join_sem); 29 + semaphore_signal_trap_impl(join_sem); 30 30 lkm_call(NR_thread_death_announce, 0); 31 31 32 32 return __darling_thread_terminate(stackaddr, freesize, pthread_obj_size);
+23
src/kernel/emulation/linux/ext/for-xtrace.c
··· 1 1 #include "for-xtrace.h" 2 2 #include "../mman/mman.h" 3 3 #include "../misc/abort_with_payload.h" 4 + #include "../fcntl/open.h" 5 + #include "../unistd/close.h" 4 6 5 7 VISIBLE 6 8 void* _mmap_for_xtrace(void* start, unsigned long len, int prot, int flags, int fd, long pos) { ··· 16 18 long _abort_with_payload_for_xtrace(unsigned int reason_namespace, unsigned long long reason_code, void *payload, unsigned int payload_size, const char *reason_string, unsigned long long reason_flags) { 17 19 return sys_abort_with_payload(reason_namespace, reason_code, payload, payload_size, reason_string, reason_flags); 18 20 }; 21 + 22 + // __write_for_xtrace is in unistd/write.c 23 + 24 + long _open_for_xtrace(const char* filename, int flags, unsigned int mode) { 25 + return sys_open_nocancel(filename, flags, mode); 26 + }; 27 + 28 + long _close_for_xtrace(int fd) { 29 + return sys_close_nocancel(fd); 30 + }; 31 + 32 + extern size_t default_sigaltstack_size; 33 + 34 + long _sigaltstack_set_default_size_for_xtrace(size_t new_size) { 35 + default_sigaltstack_size = new_size; 36 + return 0; 37 + }; 38 + 39 + long _sigaltstack_for_xtrace(const struct bsd_stack* ss, struct bsd_stack* oss) { 40 + return sys_sigaltstack(ss, oss); 41 + };
+17
src/kernel/emulation/linux/ext/for-xtrace.h
··· 3 3 4 4 #include <darling/emulation/base.h> 5 5 #include <darling/emulation/mman/duct_mman.h> 6 + #include <darling/emulation/signal/sigaltstack.h> 6 7 7 8 VISIBLE 8 9 void* _mmap_for_xtrace(void* start, unsigned long len, int prot, int flags, int fd, long pos); ··· 12 13 13 14 VISIBLE 14 15 long _abort_with_payload_for_xtrace(unsigned int reason_namespace, unsigned long long reason_code, void *payload, unsigned int payload_size, const char *reason_string, unsigned long long reason_flags); 16 + 17 + VISIBLE 18 + long __write_for_xtrace(int fd, const void* mem, int len); 19 + 20 + VISIBLE 21 + long _open_for_xtrace(const char* filename, int flags, unsigned int mode); 22 + 23 + VISIBLE 24 + long _close_for_xtrace(int fd); 25 + 26 + VISIBLE 27 + long _sigaltstack_set_default_size_for_xtrace(size_t new_size); 28 + 29 + // used to bypass the sigaltstack size requirements imposed by libc 30 + VISIBLE 31 + long _sigaltstack_for_xtrace(const struct bsd_stack* ss, struct bsd_stack* oss); 15 32 16 33 #endif // _DARLING_EMULATION_FOR_XTRACE_H_
+18 -6
src/kernel/emulation/linux/signal/sigexc.c
··· 30 30 void sigrt_handler(int signum, struct linux_siginfo* info, void* ctxt); 31 31 32 32 static char sigexc_altstack[8*1024]; 33 + size_t default_sigaltstack_size = sizeof(sigexc_altstack); 33 34 34 35 #if defined(__x86_64__) 35 36 static void mcontext_to_thread_state(const struct linux_gregset* regs, x86_thread_state64_t* s); ··· 73 74 unsigned long long* p = (unsigned long long*) regs; 74 75 for (int i = 0; i < 23; i++) 75 76 { 76 - kern_printf("sigexc: gregs 0x%x\n", p[i]); 77 + kern_printf("sigexc: gregs 0x%llx\n", p[i]); 77 78 } 78 79 } 79 80 ··· 226 227 memcpy(&sigprocess.linux_siginfo, info, sizeof(*info)); 227 228 228 229 #ifdef __x86_64__ 229 - kern_printf("sigexc: have RIP 0x%x\n", ctxt->uc_mcontext.gregs.rip); 230 + kern_printf("sigexc: have RIP 0x%llx\n", ctxt->uc_mcontext.gregs.rip); 230 231 #endif 231 232 232 233 thread_t thread = mach_thread_self(); ··· 302 303 kern_printf("sigexc: handler (%d) returning\n", linux_signum); 303 304 } 304 305 305 - #define DUMPREG(regname) kern_printf("sigexc: " #regname ": 0x%x\n", regs->regname); 306 + #define DUMPREG(regname) kern_printf("sigexc: " #regname ": 0x%llx\n", regs->regname); 306 307 307 308 #if defined(__x86_64__) 308 309 void mcontext_to_thread_state(const struct linux_gregset* regs, x86_thread_state64_t* s) ··· 493 494 void sigexc_thread_setup(void) 494 495 { 495 496 struct bsd_stack newstack = { 496 - .ss_size = sizeof(sigexc_altstack), 497 + .ss_size = default_sigaltstack_size, 497 498 .ss_flags = 0 498 499 }; 499 500 500 - newstack.ss_sp = (void*) sys_mmap(NULL, sizeof(sigexc_altstack), PROT_READ | PROT_WRITE, 501 + #if SIGALTSTACK_GUARD 502 + newstack.ss_sp = (void*) sys_mmap(NULL, newstack.ss_size + 4096, PROT_READ | PROT_WRITE, 501 503 MAP_ANON | MAP_PRIVATE, -1, 0); 504 + sys_mprotect(newstack.ss_sp, 4096, PROT_NONE); 505 + newstack.ss_sp = (char*)newstack.ss_sp + 4096; 506 + #else 507 + newstack.ss_sp = (void*) sys_mmap(NULL, newstack.ss_size, PROT_READ | PROT_WRITE, 508 + MAP_ANON | MAP_PRIVATE, -1, 0); 509 + #endif 502 510 sys_sigaltstack(&newstack, NULL); 503 511 } 504 512 ··· 507 515 struct bsd_stack oldstack; 508 516 sys_sigaltstack(NULL, &oldstack); 509 517 510 - sys_munmap(oldstack.ss_sp, oldstack.ss_flags); 518 + #if SIGALTSTACK_GUARD 519 + sys_munmap((char*)oldstack.ss_sp - 4096, oldstack.ss_size + 4096); 520 + #else 521 + sys_munmap(oldstack.ss_sp, oldstack.ss_size); 522 + #endif 511 523 } 512 524
+186 -9
src/kernel/emulation/linux/simple.c
··· 30 30 { 31 31 if (*format == '%') 32 32 { 33 + enum format_size { 34 + format_size_default, 35 + format_size_short_short, 36 + format_size_short, 37 + format_size_long, 38 + format_size_long_long, 39 + format_size_max, 40 + format_size_size_type, 41 + format_size_ptrdiff, 42 + }; 43 + 44 + enum format_size format_size = format_size_default; 45 + 33 46 format++; 34 47 if (!*format) 35 48 break; 36 - if (*format == 'l') 37 - format++; 38 - 49 + 50 + switch (*format) { 51 + case 'l': 52 + format++; 53 + if (*format == 'l') { 54 + format++; 55 + format_size = format_size_long_long; 56 + } else { 57 + format_size = format_size_long; 58 + } 59 + break; 60 + 61 + case 'h': 62 + format++; 63 + if (*format == 'h') { 64 + format++; 65 + format_size = format_size_short; 66 + } else { 67 + format_size = format_size_short_short; 68 + } 69 + break; 70 + 71 + case 'j': 72 + format++; 73 + format_size = format_size_max; 74 + break; 75 + 76 + case 'z': 77 + format++; 78 + format_size = format_size_size_type; 79 + break; 80 + 81 + case 't': 82 + format++; 83 + format_size = format_size_ptrdiff; 84 + break; 85 + } 86 + 39 87 switch (*format) 40 88 { 41 89 case '%': ··· 60 108 } 61 109 case 'd': 62 110 { 63 - int num = va_arg(vl, int); 64 - char temp[16]; 111 + intmax_t num = 0; 112 + char temp[20]; 65 113 int count = 0; 66 114 115 + switch (format_size) { 116 + case format_size_short: 117 + case format_size_short_short: 118 + case format_size_default: 119 + num = va_arg(vl, int); 120 + break; 121 + case format_size_long: 122 + num = va_arg(vl, long int); 123 + break; 124 + case format_size_long_long: 125 + num = va_arg(vl, long long int); 126 + break; 127 + case format_size_max: 128 + num = va_arg(vl, intmax_t); 129 + break; 130 + case format_size_size_type: 131 + num = va_arg(vl, size_t); 132 + break; 133 + case format_size_ptrdiff: 134 + num = va_arg(vl, ptrdiff_t); 135 + break; 136 + } 137 + 67 138 if (num < 0) 68 139 { 69 140 if (offset < max_length) ··· 90 161 } 91 162 case 'u': 92 163 { 93 - unsigned int num = va_arg(vl, unsigned int); 94 - char temp[16]; 164 + uintmax_t num = 0; 165 + char temp[20]; 95 166 int count = 0; 96 167 168 + switch (format_size) { 169 + case format_size_short: 170 + case format_size_short_short: 171 + case format_size_default: 172 + num = va_arg(vl, unsigned int); 173 + break; 174 + case format_size_long: 175 + num = va_arg(vl, unsigned long int); 176 + break; 177 + case format_size_long_long: 178 + num = va_arg(vl, unsigned long long int); 179 + break; 180 + case format_size_max: 181 + num = va_arg(vl, uintmax_t); 182 + break; 183 + case format_size_size_type: 184 + num = va_arg(vl, size_t); 185 + break; 186 + case format_size_ptrdiff: 187 + num = va_arg(vl, ptrdiff_t); 188 + break; 189 + } 190 + 97 191 do 98 192 { 99 193 temp[count++] = '0' + (num % 10); ··· 113 207 case 'p': 114 208 case 'x': 115 209 { 116 - unsigned long num = va_arg(vl, unsigned long); 210 + uintmax_t num = 0; 117 211 char temp[40]; 118 212 int count = 0; 119 213 214 + if (*format == 'p') { 215 + num = va_arg(vl, void*); 216 + } else { 217 + switch (format_size) { 218 + case format_size_short: 219 + case format_size_short_short: 220 + case format_size_default: 221 + num = va_arg(vl, unsigned int); 222 + break; 223 + case format_size_long: 224 + num = va_arg(vl, unsigned long int); 225 + break; 226 + case format_size_long_long: 227 + num = va_arg(vl, unsigned long long int); 228 + break; 229 + case format_size_max: 230 + num = va_arg(vl, uintmax_t); 231 + break; 232 + case format_size_size_type: 233 + num = va_arg(vl, size_t); 234 + break; 235 + case format_size_ptrdiff: 236 + num = va_arg(vl, ptrdiff_t); 237 + break; 238 + } 239 + } 240 + 120 241 if (*format == 'p') 121 242 { 122 243 if (offset < max_length) ··· 152 273 153 274 case 'o': 154 275 { 155 - unsigned int num = va_arg(vl, unsigned int); 276 + uintmax_t num = 0; 156 277 char temp[16]; 157 278 int count = 0; 158 279 280 + switch (format_size) { 281 + case format_size_short: 282 + case format_size_short_short: 283 + case format_size_default: 284 + num = va_arg(vl, unsigned int); 285 + break; 286 + case format_size_long: 287 + num = va_arg(vl, unsigned long int); 288 + break; 289 + case format_size_long_long: 290 + num = va_arg(vl, unsigned long long int); 291 + break; 292 + case format_size_max: 293 + num = va_arg(vl, uintmax_t); 294 + break; 295 + case format_size_size_type: 296 + num = va_arg(vl, size_t); 297 + break; 298 + case format_size_ptrdiff: 299 + num = va_arg(vl, ptrdiff_t); 300 + break; 301 + } 302 + 159 303 do 160 304 { 161 305 temp[count++] = '0' + (num % 8); ··· 170 314 offset++; 171 315 } 172 316 317 + break; 318 + } 319 + 320 + case 'c': 321 + { 322 + int num = va_arg(vl, int); 323 + if (offset < max_length) 324 + buf[offset] = (char)num; 325 + offset++; 173 326 break; 174 327 } 175 328 } ··· 239 392 __simple_vsnprintf(buffer, sizeof(buffer), format, vl); 240 393 va_end(vl); 241 394 395 + LINUX_SYSCALL3(__NR_write, fd, buffer, __simple_strlen(buffer)); 396 + } 397 + 398 + __attribute__ ((visibility ("default"))) 399 + void __simple_vprintf(const char* format, va_list args) 400 + { 401 + char buffer[512]; 402 + __simple_vsnprintf(buffer, sizeof(buffer), format, args); 403 + LINUX_SYSCALL3(__NR_write, 1, buffer, __simple_strlen(buffer)); 404 + } 405 + 406 + __attribute__ ((visibility ("default"))) 407 + void __simple_vkprintf(const char* format, va_list args) 408 + { 409 + char buffer[512]; 410 + __simple_vsnprintf(buffer, sizeof(buffer), format, args); 411 + lkm_call(NR_kernel_printk, buffer); 412 + } 413 + 414 + __attribute__ ((visibility ("default"))) 415 + void __simple_vfprintf(int fd, const char* format, va_list args) 416 + { 417 + char buffer[512]; 418 + __simple_vsnprintf(buffer, sizeof(buffer), format, args); 242 419 LINUX_SYSCALL3(__NR_write, fd, buffer, __simple_strlen(buffer)); 243 420 } 244 421
+3
src/kernel/emulation/linux/simple.h
··· 18 18 int __simple_vsprintf(char* buf, const char* format, va_list vl) __attribute__((format(printf, 2, 0))); 19 19 int __simple_vsnprintf(char* buffer, size_t max_length, const char* format, va_list args) __attribute__((format(printf, 3, 0))); 20 20 int __simple_snprintf(char* buffer, size_t max_length, const char* format, ...) __attribute__((format(printf, 3, 4))); 21 + void __simple_vprintf(const char* format, va_list args) __attribute__((format(printf, 1, 0))); 22 + void __simple_vkprintf(const char* format, va_list args) __attribute__((format(printf, 1, 0))); 23 + void __simple_vfprintf(int fd, const char* format, va_list args) __attribute__((format(printf, 2, 0))); 21 24 22 25 unsigned long long __simple_atoi(const char* str, const char** endp); 23 26 unsigned long long __simple_atoi16(const char* str, const char** endp);
+546 -93
src/xtrace/bsd_trace.cpp
··· 6 6 7 7 #define PRIVATE 1 8 8 #include <spawn_private.h> 9 + #include <sys/event.h> 9 10 10 11 #include "xtracelib.h" 11 12 #include "bsd_trace.h" 13 + #include "tls.h" 12 14 13 - static void print_errno(char* buf, int nr, uintptr_t rv); 14 - static void print_errno_num(char* buf, int nr, uintptr_t rv); 15 - static void print_errno_ptr(char* buf, int nr, uintptr_t rv); 15 + static void print_errno(int nr, uintptr_t rv); 16 + static void print_errno_num(int nr, uintptr_t rv); 17 + static void print_errno_ptr(int nr, uintptr_t rv); 18 + 19 + static void print_args(int nr, void* args[]); 20 + 21 + static void print_kevent_return(int nr, uintptr_t rv); 22 + static void print_kevent_args(int nr, void* args[]); 23 + static void print_kevent64_return(int nr, uintptr_t rv); 24 + static void print_kevent64_args(int nr, void* args[]); 25 + static void print_kevent_qos_return(int nr, uintptr_t rv); 26 + static void print_kevent_qos_args(int nr, void* args[]); 16 27 17 - static void print_args(char* buf, int nr, void* args[]); 28 + static void print_timespec(const struct timespec* timespec); 18 29 19 30 // awk '/^[0-9]/ { if ($6 !~ "nosys") { split($6, a, "("); print "[" $1 "] = { \"" a[1] "\", print_args, print_errno }," } }' 20 31 ··· 289 300 [360] = { "bsdthread_create", print_args, print_errno_num }, 290 301 [361] = { "bsdthread_terminate", print_args, print_errno_num }, 291 302 [362] = { "kqueue", print_args, print_errno_num }, 292 - [363] = { "kevent", print_args, print_errno_num }, 303 + 304 + // kevent is special 305 + [363] = { "kevent", print_kevent_args, print_kevent_return }, 306 + 293 307 [364] = { "lchown", print_args, print_errno_num }, 294 308 [366] = { "bsdthread_register", print_args, print_errno_num }, 295 309 [367] = { "workq_open", print_args, print_errno_num }, 296 310 [368] = { "workq_kernreturn", print_args, print_errno_num }, 297 - [369] = { "kevent64", print_args, print_errno_num }, 311 + 312 + // kevent64 is special 313 + [369] = { "kevent64", print_kevent64_args, print_kevent64_return }, 314 + 298 315 [370] = { "__old_semwait_signal", print_args, print_errno_num }, 299 316 [371] = { "__old_semwait_signal_nocancel", print_args, print_errno_num }, 300 317 [372] = { "thread_selfid", print_args, print_errno_num }, 301 318 [373] = { "ledger", print_args, print_errno_num }, 302 - [374] = { "kevent_qos", print_args, print_errno_num }, 319 + 320 + // kevent_qos is special 321 + [374] = { "kevent_qos", print_kevent_qos_args, print_kevent_qos_return }, 322 + 303 323 [380] = { "__mac_execve", print_args, print_errno_num }, 304 324 [381] = { "__mac_syscall", print_args, print_errno_num }, 305 325 [382] = { "__mac_get_file", print_args, print_errno_num }, ··· 421 441 [521] = { "abort_with_payload", print_args, print_errno_num }, 422 442 }; 423 443 424 - static int print_arg_int(char* buf, void* arg) 444 + static void print_arg_int(void* arg) 425 445 { 426 - return __simple_sprintf(buf, "%d", (int) (long) arg); 446 + xtrace_log("%d", (int) (long) arg); 427 447 } 428 448 429 - static int print_arg_ptr(char* buf, void* arg) 449 + static void print_arg_ptr(void* arg) 430 450 { 431 451 if (arg == NULL) 432 - return __simple_sprintf(buf, "NULL"); 433 - return __simple_sprintf(buf, "%p", arg); 452 + xtrace_log("NULL"); 453 + else 454 + xtrace_log("%p", arg); 434 455 } 435 456 436 457 extern "C" 437 - int xtrace_format_string_literal(char* buf, const char* str) 438 - { 439 - const char* initial_buf = buf; 440 - 441 - if (str == NULL) 442 - { 443 - return __simple_sprintf(buf, "NULL"); 458 + void xtrace_print_string_literal(const char* str) { 459 + if (str == NULL) { 460 + xtrace_log("NULL"); 461 + return; 444 462 } 445 463 446 464 if (!xtrace_no_color) 447 - buf += __simple_sprintf(buf, "\033[;1m"); // bold 465 + xtrace_log("\033[;1m"); // bold 448 466 449 - *buf++ = '"'; 467 + xtrace_log("\""); 450 468 451 469 for (; *str; str++) 452 470 { 453 471 switch (*str) 454 472 { 455 473 case '\\': 456 - *buf++ = '\\'; 457 - *buf++ = '\\'; 474 + xtrace_log("\\\\"); 458 475 break; 459 476 case '\n': 460 - *buf++ = '\\'; 461 - *buf++ = 'n'; 477 + xtrace_log("\\n"); 462 478 break; 463 479 case '\t': 464 - *buf++ = '\\'; 465 - *buf++ = 't'; 480 + xtrace_log("\\t"); 466 481 break; 467 482 default: 468 - *buf++ = *str; 483 + xtrace_log("%c", *str); 469 484 break; 470 485 } 471 486 } 472 487 473 - *buf++ = '"'; 488 + xtrace_log("\""); 474 489 475 490 if (!xtrace_no_color) 476 - buf += __simple_sprintf(buf, "\033[0m"); // reset 477 - 478 - return buf - initial_buf; 491 + xtrace_log("\033[0m"); // reset 479 492 } 480 493 481 - static int print_arg_str(char* buf, void* arg) 494 + static void print_arg_str(void* arg) 482 495 { 483 496 const char* str = (const char*) arg; 484 - return xtrace_format_string_literal(buf, str); 497 + xtrace_print_string_literal(str); 485 498 } 486 499 487 - static int print_arg_prot(char* buf, void* arg) 500 + static void print_arg_prot(void* arg) 488 501 { 489 - const char* initial_buf = buf; 490 502 int cnt = 0; 491 - int prot = (int) (long) arg; 503 + int prot = (int)(long)arg; 492 504 493 505 if (prot & PROT_READ) 494 506 { 495 - buf += __simple_sprintf(buf, "PROT_READ"); 507 + xtrace_log("PROT_READ"); 496 508 cnt++; 497 509 } 498 510 if (prot & PROT_WRITE) 499 511 { 500 512 if (cnt > 0) 501 - *buf++ = '|'; 502 - buf += __simple_sprintf(buf, "PROT_WRITE"); 513 + xtrace_log("|"); 514 + xtrace_log("PROT_WRITE"); 503 515 cnt++; 504 516 } 505 517 if (prot & PROT_EXEC) 506 518 { 507 519 if (cnt > 0) 508 - *buf++ = '|'; 509 - buf += __simple_sprintf(buf, "PROT_EXEC"); 520 + xtrace_log("|"); 521 + xtrace_log("PROT_EXEC"); 510 522 cnt++; 511 523 } 512 524 if (cnt == 0) 513 525 { 514 - buf += __simple_sprintf(buf, "PROT_NONE"); 526 + xtrace_log("PROT_NONE"); 515 527 } 516 - 517 - return buf - initial_buf; 518 528 } 519 529 520 - static int print_mmap_flags(char* buf, void* arg) 530 + static void print_mmap_flags(void* arg) 521 531 { 522 - const char* initial_buf = buf; 523 532 int cnt = 0; 524 533 int flags = (int) (long) arg; 525 534 ··· 541 550 if (flags & all_flags[i].flag) 542 551 { 543 552 if (cnt > 0) 544 - *buf++ = '|'; 545 - buf += __simple_sprintf(buf, "%s", all_flags[i].name); 553 + xtrace_log("|"); 554 + xtrace_log("%s", all_flags[i].name); 546 555 cnt++; 547 556 } 548 557 } 549 558 550 559 if (cnt == 0) 551 560 { 552 - buf += __simple_sprintf(buf, "MAP_FILE"); 561 + xtrace_log("MAP_FILE"); 553 562 } 554 - 555 - return buf - initial_buf; 556 563 } 557 564 558 565 extern "C" 559 - int print_open_flags(char* buf, void* arg) 566 + void print_open_flags(void* arg) 560 567 { 561 - const char* initial_buf = buf; 562 568 int cnt = 0; 563 569 int flags = (int) (long) arg; 564 570 ··· 589 595 if (flags & all_flags[i].flag) 590 596 { 591 597 if (cnt > 0) 592 - *buf++ = '|'; 593 - buf += __simple_sprintf(buf, "%s", all_flags[i].name); 598 + xtrace_log("|"); 599 + xtrace_log("%s", all_flags[i].name); 594 600 cnt++; 595 601 } 596 602 } 597 603 598 604 if (cnt == 0) 599 605 { 600 - buf += __simple_sprintf(buf, "O_RDONLY"); 606 + xtrace_log("O_RDONLY"); 601 607 } 602 - 603 - return buf - initial_buf; 604 608 } 605 609 606 - extern "C" int print_arg_posix_spawn_args(char* buf, void* arg); 610 + extern "C" void print_arg_posix_spawn_args(void* arg); 607 611 608 - static int print_arg_string_array(char* buf, void* arg) { 609 - const char* initial_buf = buf; 610 - const char* const* argv = (const char* const*)arg; 612 + static void print_arg_string_array(void* arg) { 613 + const char* const* array = (const char* const*)arg; 611 614 bool is_first = true; 612 615 613 - *buf++ = '{'; 616 + xtrace_log("{"); 617 + 618 + if (array) { 619 + for (const char* const* ptr = array; *ptr != NULL; ++ptr) { 620 + if (is_first) { 621 + is_first = false; 622 + } else { 623 + xtrace_log(", "); 624 + } 614 625 615 - for (const char* const* ptr = argv; *ptr != NULL; ++ptr) { 616 - if (is_first) { 617 - is_first = false; 618 - } else { 619 - *buf++ = ','; 620 - *buf++ = ' '; 626 + xtrace_print_string_literal(*ptr); 621 627 } 622 - 623 - buf += xtrace_format_string_literal(buf, *ptr); 624 628 } 625 629 626 - *buf++ = '}'; 627 - 628 - return buf - initial_buf; 630 + xtrace_log("}"); 629 631 }; 630 632 631 633 // TODO: output more specific information for certain calls ··· 634 636 635 637 static const struct { 636 638 int args_cnt; 637 - int (*print_arg[8])(char* buf, void* arg); 639 + void (*print_arg[8])(void* arg); 638 640 } args_info[] = { 639 641 [1] = { 1, { print_arg_int } }, // exit 640 642 [2] = { 0, { } }, // fork ··· 906 908 [360] = { 5, { print_arg_ptr, print_arg_ptr, print_arg_ptr, print_arg_ptr, print_arg_int } }, // bsdthread_create 907 909 [361] = { 4, { print_arg_ptr, print_arg_int, print_arg_int, print_arg_int } }, // bsdthread_terminate 908 910 [362] = { 0, { } }, // kqueue 909 - [363] = { 6, { print_arg_int, print_arg_ptr, print_arg_int, print_arg_ptr, print_arg_int, print_arg_ptr } }, // kevent 911 + // kevent is special 910 912 [364] = { 3, { print_arg_str, print_arg_int, print_arg_int } }, // lchown 911 913 [366] = { 7, { print_arg_ptr, print_arg_ptr, print_arg_int, print_arg_ptr, print_arg_ptr, print_arg_int, print_arg_int } }, // bsdthread_register 912 914 [367] = { 0, { } }, // workq_open 913 915 [368] = { 4, { print_arg_int, print_arg_ptr, print_arg_int, print_arg_int } }, // workq_kernreturn 914 - [369] = { 7, { print_arg_int, print_arg_ptr, print_arg_int, print_arg_ptr, print_arg_int, print_arg_int, print_arg_ptr } }, // kevent64 916 + // kevent64 is special 915 917 [370] = { 5, { print_arg_int, print_arg_int, print_arg_int, print_arg_int, print_arg_ptr } }, // __old_semwait_signal 916 918 [371] = { 5, { print_arg_int, print_arg_int, print_arg_int, print_arg_int, print_arg_ptr } }, // __old_semwait_signal_nocancel 917 919 [372] = { 0, { } }, // thread_selfid 918 920 [373] = { 4, { print_arg_int, print_arg_ptr, print_arg_ptr, print_arg_ptr } }, // ledger 919 - [374] = { 8, { print_arg_int, print_arg_ptr, print_arg_int, print_arg_ptr, print_arg_int, print_arg_ptr, print_arg_ptr, print_arg_int } }, // kevent_qos 921 + // kevent_qos is special 920 922 [380] = { 4, { print_arg_ptr, print_arg_ptr, print_arg_ptr, print_arg_ptr } }, // __mac_execve 921 923 [381] = { 3, { print_arg_ptr, print_arg_int, print_arg_ptr } }, // __mac_syscall 922 924 [382] = { 2, { print_arg_ptr, print_arg_ptr } }, // __mac_get_file ··· 1038 1040 [521] = { 6, { print_arg_int, print_arg_int, print_arg_ptr, print_arg_int, print_arg_str, print_arg_int } }, // abort_with_payload 1039 1041 }; 1040 1042 1041 - static void print_args(char* buf, int nr, void* args[]) 1043 + static void print_args(int nr, void* args[]) 1042 1044 { 1043 1045 int cnt = args_info[nr].args_cnt; 1044 1046 for (int i = 0; i < cnt; i++) 1045 1047 { 1046 1048 if (i > 0) 1047 - buf += __simple_sprintf(buf, ", "); 1048 - buf += (*args_info[nr].print_arg[i])(buf, args[i]); 1049 + xtrace_log(", "); 1050 + (*args_info[nr].print_arg[i])(args[i]); 1049 1051 } 1050 - *buf = 0; 1051 1052 } 1052 1053 1053 1054 ··· 1060 1061 { 1061 1062 // For exit() or execve(), print an extra newline, 1062 1063 // as we're likely not going to see the return. 1063 - xtrace_printf("\n"); 1064 + xtrace_log("\n"); 1064 1065 } 1065 1066 } 1066 1067 ··· 1179 1180 [106] = "EQFULL", 1180 1181 }; 1181 1182 1182 - static void print_errno_num(char* buf, int nr, uintptr_t rv) 1183 + static void print_errno_num(int nr, uintptr_t rv) 1183 1184 { 1184 1185 intptr_t v = (intptr_t)rv; 1185 1186 if (v >= 0 || v < -4095) 1186 1187 { 1187 - __simple_sprintf(buf, "%ld", rv); 1188 + xtrace_log("%ld", rv); 1188 1189 } 1189 1190 else 1190 - print_errno(buf, nr, rv); 1191 + print_errno(nr, rv); 1191 1192 } 1192 1193 1193 - static void print_errno_ptr(char* buf, int nr, uintptr_t rv) 1194 + static void print_errno_ptr(int nr, uintptr_t rv) 1194 1195 { 1195 1196 intptr_t v = (intptr_t)rv; 1196 1197 if (v >= 0 || v < -4095) 1197 1198 { 1198 - __simple_sprintf(buf, "%p", (void*) rv); 1199 + xtrace_log("%p", (void*) rv); 1199 1200 } 1200 1201 else 1201 - print_errno(buf, nr, rv); 1202 + print_errno(nr, rv); 1202 1203 } 1203 1204 1204 - static void print_errno(char* buf, int nr, uintptr_t rv) 1205 + static void print_errno(int nr, uintptr_t rv) 1205 1206 { 1206 1207 const char* error = NULL; 1207 1208 intptr_t v = (intptr_t) rv; 1208 1209 if (-v < 128) 1209 1210 error = error_strings[-v]; 1210 1211 if (error != NULL) 1211 - __simple_sprintf(buf, "%s", error); 1212 + xtrace_log("%s", error); 1212 1213 else 1213 - __simple_sprintf(buf, "%ld", v); 1214 + xtrace_log("%ld", v); 1214 1215 } 1215 1216 1217 + static const char* const filter_names[] = { 1218 + "EVFILT_READ", 1219 + "EVFILT_WRITE", 1220 + "EVFILT_AIO", 1221 + "EVFILT_VNODE", 1222 + "EVFILT_PROC", 1223 + "EVFILT_SIGNAL", 1224 + "EVFILT_TIMER", 1225 + "EVFILT_MACHPORT", 1226 + "EVFILT_FS", 1227 + "EVFILT_USER", 1228 + NULL, 1229 + "EVFILT_VM", 1230 + "EVFILT_SOCK", 1231 + "EVFILT_MEMORYSTATUS", 1232 + "EVFILT_EXCEPT", 1233 + NULL, 1234 + "EVFILT_WORKLOOP", 1235 + }; 1236 + 1237 + static const char* const signal_names[] = { 1238 + "SIGHUP", 1239 + "SIGINT", 1240 + "SIGQUIT", 1241 + "SIGILL", 1242 + "SIGTRAP", 1243 + "SIGABRT", 1244 + "SIGEMT", 1245 + "SIGFPE", 1246 + "SIGKILL", 1247 + "SIGBUS", 1248 + "SIGSEGV", 1249 + "SIGSYS", 1250 + "SIGPIPE", 1251 + "SIGALRM", 1252 + "SIGTERM", 1253 + "SIGURG", 1254 + "SIGSTOP", 1255 + "SIGTSTP", 1256 + "SIGCONT", 1257 + "SIGCHLD", 1258 + "SIGTTIN", 1259 + "SIGTTOU", 1260 + "SIGIO", 1261 + "SIGXCPU", 1262 + "SIGXFSZ", 1263 + "SIGVTALR", 1264 + "SIGPROF", 1265 + "SIGWINCH", 1266 + "SIGINFO", 1267 + "SIGUSR1", 1268 + "SIGUSR2", 1269 + }; 1270 + 1271 + static const struct { 1272 + uint16_t flag; 1273 + const char* name; 1274 + } kevent_flag_names[] = { 1275 + #define FLAG(_name) { _name, #_name } 1276 + FLAG(EV_ADD), 1277 + FLAG(EV_ENABLE), 1278 + FLAG(EV_DISABLE), 1279 + FLAG(EV_DELETE), 1280 + FLAG(EV_RECEIPT), 1281 + FLAG(EV_ONESHOT), 1282 + FLAG(EV_CLEAR), 1283 + FLAG(EV_DISPATCH), 1284 + FLAG(EV_UDATA_SPECIFIC), 1285 + FLAG(EV_FLAG0), 1286 + FLAG(EV_FLAG1), 1287 + FLAG(EV_EOF), 1288 + FLAG(EV_ERROR), 1289 + #undef KEVENT_FLAG 1290 + }; 1291 + 1292 + static const struct { 1293 + uint32_t flag; 1294 + const char* name; 1295 + } kevent_filter_flag_names[][16] = { 1296 + #define FLAG(_name) { _name, #_name } 1297 + [~EVFILT_READ] = { 1298 + FLAG(NOTE_LOWAT), 1299 + }, 1300 + [~EVFILT_EXCEPT] = { 1301 + FLAG(NOTE_OOB), 1302 + }, 1303 + [~EVFILT_VNODE] = { 1304 + FLAG(NOTE_DELETE), 1305 + FLAG(NOTE_WRITE), 1306 + FLAG(NOTE_EXTEND), 1307 + FLAG(NOTE_ATTRIB), 1308 + FLAG(NOTE_LINK), 1309 + FLAG(NOTE_RENAME), 1310 + FLAG(NOTE_REVOKE), 1311 + FLAG(NOTE_NONE), 1312 + FLAG(NOTE_FUNLOCK), 1313 + }, 1314 + [~EVFILT_PROC] = { 1315 + FLAG(NOTE_EXIT), 1316 + FLAG(NOTE_FORK), 1317 + FLAG(NOTE_EXEC), 1318 + FLAG(NOTE_REAP), 1319 + FLAG(NOTE_SIGNAL), 1320 + FLAG(NOTE_EXITSTATUS), 1321 + FLAG(NOTE_EXIT_DETAIL), 1322 + }, 1323 + [~EVFILT_TIMER] = { 1324 + FLAG(NOTE_SECONDS), 1325 + FLAG(NOTE_USECONDS), 1326 + FLAG(NOTE_NSECONDS), 1327 + FLAG(NOTE_MACHTIME), 1328 + FLAG(NOTE_ABSOLUTE), 1329 + FLAG(NOTE_MACH_CONTINUOUS_TIME), 1330 + FLAG(NOTE_CRITICAL), 1331 + FLAG(NOTE_BACKGROUND), 1332 + FLAG(NOTE_LEEWAY), 1333 + FLAG(NOTE_TRACK), 1334 + FLAG(NOTE_TRACKERR), 1335 + FLAG(NOTE_CHILD), 1336 + }, 1337 + [~EVFILT_SOCK] = { 1338 + FLAG(NOTE_CONNRESET), 1339 + FLAG(NOTE_READCLOSED), 1340 + FLAG(NOTE_WRITECLOSED), 1341 + FLAG(NOTE_TIMEOUT), 1342 + FLAG(NOTE_NOSRCADDR), 1343 + FLAG(NOTE_IFDENIED), 1344 + FLAG(NOTE_SUSPEND), 1345 + FLAG(NOTE_RESUME), 1346 + FLAG(NOTE_KEEPALIVE), 1347 + FLAG(NOTE_ADAPTIVE_WTIMO), 1348 + FLAG(NOTE_ADAPTIVE_RTIMO), 1349 + FLAG(NOTE_CONNECTED), 1350 + FLAG(NOTE_DISCONNECTED), 1351 + FLAG(NOTE_CONNINFO_UPDATED), 1352 + FLAG(NOTE_NOTIFY_ACK), 1353 + }, 1354 + [~EVFILT_MACHPORT] = { 1355 + FLAG(MACH_RCV_MSG), 1356 + }, 1357 + #undef FLAG 1358 + }; 1359 + 1360 + static void print_kevent_common(int16_t filter, uintptr_t ident, uint16_t flags, uint32_t fflags, intptr_t data, void* udata) { 1361 + int filt_index = ~filter; 1362 + bool printed_something = false; 1363 + 1364 + xtrace_log("%s { ident = ", (filt_index < 0 || filt_index >= sizeof(filter_names) / sizeof(*filter_names)) ? "EVFILT_UNKNOWN" : filter_names[filt_index]); 1365 + 1366 + switch (filter) { 1367 + case EVFILT_READ: 1368 + case EVFILT_WRITE: 1369 + case EVFILT_EXCEPT: 1370 + case EVFILT_VNODE: 1371 + case EVFILT_SOCK: 1372 + xtrace_log("fd %lu", ident); 1373 + break; 1374 + 1375 + case EVFILT_PROC: 1376 + xtrace_log("pid %lu", ident); 1377 + break; 1378 + 1379 + case EVFILT_SIGNAL: 1380 + xtrace_log("signal %s (%lu)", (ident < sizeof(signal_names) / sizeof(*signal_names)) ? signal_names[ident] : "SIGUNKNOWN", ident); 1381 + break; 1382 + 1383 + case EVFILT_TIMER: 1384 + xtrace_log("timer %lu", ident); 1385 + break; 1386 + 1387 + case EVFILT_MACHPORT: 1388 + // officially, only portsets can be used with EVFILT_MACHPORT. however, Apple introduced support for single ports in 10.13 or something around that time. 1389 + xtrace_log("port/portset %lu", ident); 1390 + break; 1391 + 1392 + case EVFILT_FS: 1393 + case EVFILT_USER: 1394 + case EVFILT_VM: 1395 + case EVFILT_MEMORYSTATUS: 1396 + case EVFILT_WORKLOOP: 1397 + // notes: 1398 + // * EVFILT_VM is unsupported on macOS. 1399 + // * do EVFILT_FS, EVFILT_MEMORYSTATUS, and EVFILT_WORKLOOP even use `ident`? 1400 + xtrace_log("%lu", ident); 1401 + break; 1402 + 1403 + default: 1404 + xtrace_log("%lu", ident); 1405 + break; 1406 + } 1407 + 1408 + xtrace_log(", flags = "); 1409 + 1410 + for (size_t i = 0; i < sizeof(kevent_flag_names) / sizeof(*kevent_flag_names); ++i) { 1411 + if ((flags & kevent_flag_names[i].flag) == 0) { 1412 + continue; 1413 + } 1414 + 1415 + if (!printed_something) { 1416 + printed_something = true; 1417 + } else { 1418 + xtrace_log("|"); 1419 + } 1420 + 1421 + xtrace_log("%s", kevent_flag_names[i].name); 1422 + } 1423 + 1424 + xtrace_log("%s(0x%x), fflags = ", printed_something ? " " : "", flags); 1425 + 1426 + printed_something = false; 1427 + 1428 + if (filt_index < sizeof(kevent_filter_flag_names) / sizeof(*kevent_filter_flag_names)) { 1429 + for (size_t i = 0; i < sizeof(*kevent_filter_flag_names) / sizeof(**kevent_filter_flag_names); ++i) { 1430 + if (!kevent_filter_flag_names[filt_index][i].name) { 1431 + break; 1432 + } 1433 + 1434 + if ((fflags & kevent_filter_flag_names[filt_index][i].flag) == 0) { 1435 + continue; 1436 + } 1437 + 1438 + if (!printed_something) { 1439 + printed_something = true; 1440 + } else { 1441 + xtrace_log("|"); 1442 + } 1443 + 1444 + xtrace_log("%s", kevent_filter_flag_names[filt_index][i].name); 1445 + } 1446 + } 1447 + 1448 + xtrace_log("%s(0x%x), udata = %p, data = 0x%lx", printed_something ? " " : "", fflags, udata, data); 1449 + }; 1450 + 1451 + static void print_kevent_structure(const struct kevent* event) { 1452 + print_kevent_common(event->filter, event->ident, event->flags, event->fflags, event->data, event->udata); 1453 + xtrace_log(" }"); 1454 + }; 1455 + 1456 + static void print_kevent64_structure(const struct kevent64_s* event) { 1457 + print_kevent_common(event->filter, event->ident, event->flags, event->fflags, event->data, (void*)(uintptr_t)event->udata); 1458 + xtrace_log(", ext[0] = 0x%llx, ext[1] = 0x%llx }", event->ext[0], event->ext[1]); 1459 + }; 1460 + 1461 + static void print_kevent_qos_structure(const struct kevent_qos_s* event) { 1462 + print_kevent_common(event->filter, event->ident, event->flags, event->fflags, event->data, (void*)(uintptr_t)event->udata); 1463 + xtrace_log(", ext[0] = 0x%llx, ext[1] = 0x%llx, ext[2] = 0x%llx, ext[3] = 0x%llx, qos = %d, xflags = 0x%x }", event->ext[0], event->ext[1], event->ext[2], event->ext[3], event->qos, event->xflags); 1464 + }; 1465 + 1466 + DEFINE_XTRACE_TLS_VAR(void*, kevent_stored_list, NULL); 1467 + 1468 + enum class kevent_type { 1469 + kevent, 1470 + kevent64, 1471 + kevent_qos, 1472 + }; 1473 + 1474 + static void print_kevent_return_common(int nr, uintptr_t rv, kevent_type type) { 1475 + void* event_list = get_kevent_stored_list(); 1476 + int ret = (intptr_t)rv; 1477 + 1478 + set_kevent_stored_list(NULL); 1479 + 1480 + if (ret < 0) { 1481 + print_errno(nr, rv); 1482 + return; 1483 + } 1484 + 1485 + xtrace_log("%d events {", ret); 1486 + 1487 + for (int i = 0; i < ret; ++i) { 1488 + if (i == 0) { 1489 + xtrace_log(" "); 1490 + } else { 1491 + xtrace_log(", "); 1492 + } 1493 + 1494 + switch (type) { 1495 + case kevent_type::kevent: 1496 + print_kevent_structure(&((struct kevent*)event_list)[i]); 1497 + break; 1498 + case kevent_type::kevent64: 1499 + print_kevent64_structure(&((struct kevent64_s*)event_list)[i]); 1500 + break; 1501 + case kevent_type::kevent_qos: 1502 + print_kevent_qos_structure(&((struct kevent_qos_s*)event_list)[i]); 1503 + break; 1504 + } 1505 + } 1506 + 1507 + xtrace_log("%s}", ret > 0 ? " " : ""); 1508 + }; 1509 + 1510 + static void print_kevent_return(int nr, uintptr_t rv) { 1511 + print_kevent_return_common(nr, rv, kevent_type::kevent); 1512 + }; 1513 + 1514 + static void print_kevent_args(int nr, void* args[]) { 1515 + int kq = (intptr_t)args[0]; 1516 + const struct kevent* change_list = (const struct kevent*)args[1]; 1517 + int nchanges = (intptr_t)args[2]; 1518 + struct kevent* event_list = (struct kevent*)args[3]; 1519 + int nevents = (intptr_t)args[4]; 1520 + const struct timespec* timeout = (const struct timespec*)args[5]; 1521 + 1522 + set_kevent_stored_list(event_list); 1523 + 1524 + xtrace_log("%d, change_list = {", kq); 1525 + 1526 + for (int i = 0; i < nchanges; ++i) { 1527 + if (i == 0) { 1528 + xtrace_log(" "); 1529 + } else { 1530 + xtrace_log(", "); 1531 + } 1532 + print_kevent_structure(&change_list[i]); 1533 + } 1534 + 1535 + xtrace_log("%s}, nchanges = %d, event_list = %p, nevents = %d, timeout = ", nchanges > 0 ? " " : "", nchanges, event_list, nevents); 1536 + 1537 + print_timespec(timeout); 1538 + }; 1539 + 1540 + static void print_kevent64_return(int nr, uintptr_t rv) { 1541 + print_kevent_return_common(nr, rv, kevent_type::kevent64); 1542 + }; 1543 + 1544 + static struct { 1545 + unsigned int flag; 1546 + const char* name; 1547 + } kevent_call_flags[] = { 1548 + #define FLAG(_name) { _name, #_name } 1549 + FLAG(KEVENT_FLAG_NONE), 1550 + FLAG(KEVENT_FLAG_IMMEDIATE), 1551 + FLAG(KEVENT_FLAG_ERROR_EVENTS), 1552 + FLAG(KEVENT_FLAG_STACK_DATA), 1553 + FLAG(KEVENT_FLAG_WORKQ), 1554 + FLAG(KEVENT_FLAG_WORKQ_MANAGER), 1555 + FLAG(KEVENT_FLAG_WORKLOOP), 1556 + FLAG(KEVENT_FLAG_PARKING), 1557 + FLAG(KEVENT_FLAG_WORKLOOP_SERVICER_ATTACH), 1558 + FLAG(KEVENT_FLAG_WORKLOOP_SERVICER_DETACH), 1559 + FLAG(KEVENT_FLAG_DYNAMIC_KQ_MUST_EXIST), 1560 + FLAG(KEVENT_FLAG_DYNAMIC_KQ_MUST_NOT_EXIST), 1561 + FLAG(KEVENT_FLAG_WORKLOOP_NO_WQ_THREAD), 1562 + #undef FLAG 1563 + }; 1564 + 1565 + static void print_kevent64_args(int nr, void* args[]) { 1566 + int kq = (intptr_t)args[0]; 1567 + const struct kevent64_s* change_list = (const struct kevent64_s*)args[1]; 1568 + int nchanges = (intptr_t)args[2]; 1569 + struct kevent64_s* event_list = (struct kevent64_s*)args[3]; 1570 + int nevents = (intptr_t)args[4]; 1571 + unsigned int flags = (uintptr_t)args[5]; 1572 + const struct timespec* timeout = (const struct timespec*)args[6]; 1573 + bool printed_something = false; 1574 + 1575 + set_kevent_stored_list(event_list); 1576 + 1577 + xtrace_log("%d, change_list = {", kq); 1578 + 1579 + for (int i = 0; i < nchanges; ++i) { 1580 + if (i == 0) { 1581 + xtrace_log(" "); 1582 + } else { 1583 + xtrace_log(", "); 1584 + } 1585 + print_kevent64_structure(&change_list[i]); 1586 + } 1587 + 1588 + xtrace_log("%s}, nchanges = %d, event_list = %p, nevents = %d, flags = ", nchanges > 0 ? " " : "", nchanges, event_list, nevents); 1589 + 1590 + for (size_t i = 0; i < sizeof(kevent_call_flags) / sizeof(*kevent_call_flags); ++i) { 1591 + if ((flags & kevent_call_flags[i].flag) == 0) { 1592 + continue; 1593 + } 1594 + 1595 + if (!printed_something) { 1596 + printed_something = true; 1597 + } else { 1598 + xtrace_log("|"); 1599 + } 1600 + 1601 + xtrace_log("%s", kevent_call_flags[i].name); 1602 + } 1603 + 1604 + if (!printed_something) { 1605 + xtrace_log("0"); 1606 + } 1607 + 1608 + xtrace_log(", timeout = "); 1609 + 1610 + print_timespec(timeout); 1611 + }; 1612 + 1613 + static void print_kevent_qos_return(int nr, uintptr_t rv) { 1614 + print_kevent_return_common(nr, rv, kevent_type::kevent_qos); 1615 + }; 1616 + 1617 + static void print_kevent_qos_args(int nr, void* args[]) { 1618 + int kq = (intptr_t)args[0]; 1619 + const struct kevent_qos_s* change_list = (const struct kevent_qos_s*)args[1]; 1620 + int nchanges = (intptr_t)args[2]; 1621 + struct kevent_qos_s* event_list = (struct kevent_qos_s*)args[3]; 1622 + int nevents = (intptr_t)args[4]; 1623 + void* data_out = args[5]; 1624 + size_t* data_available = (size_t*)args[6]; 1625 + unsigned int flags = (uintptr_t)args[7]; 1626 + bool printed_something = false; 1627 + 1628 + set_kevent_stored_list(event_list); 1629 + 1630 + xtrace_log("%d, change_list = {", kq); 1631 + 1632 + for (int i = 0; i < nchanges; ++i) { 1633 + if (i == 0) { 1634 + xtrace_log(" "); 1635 + } else { 1636 + xtrace_log(", "); 1637 + } 1638 + print_kevent_qos_structure(&change_list[i]); 1639 + } 1640 + 1641 + xtrace_log("%s}, nchanges = %d, event_list = %p, nevents = %d, data_out = %p, data_available = %p (%ld), flags = ", nchanges > 0 ? " " : "", nchanges, event_list, nevents, data_out, data_available, data_available ? *data_available : 0); 1642 + 1643 + for (size_t i = 0; i < sizeof(kevent_call_flags) / sizeof(*kevent_call_flags); ++i) { 1644 + if ((flags & kevent_call_flags[i].flag) == 0) { 1645 + continue; 1646 + } 1647 + 1648 + if (!printed_something) { 1649 + printed_something = true; 1650 + } else { 1651 + xtrace_log("|"); 1652 + } 1653 + 1654 + xtrace_log("%s", kevent_call_flags[i].name); 1655 + } 1656 + 1657 + if (!printed_something) { 1658 + xtrace_log("0"); 1659 + } 1660 + }; 1661 + 1662 + static void print_timespec(const struct timespec* timespec) { 1663 + if (timespec) { 1664 + xtrace_log("(%ld s, %ld ns)", timespec->tv_sec, timespec->tv_nsec); 1665 + } else { 1666 + xtrace_log("NULL"); 1667 + } 1668 + };
+1 -1
src/xtrace/bsd_trace.h
··· 2 2 extern "C" { 3 3 #endif 4 4 5 - extern int xtrace_format_string_literal(char* buf, const char* str); 5 + extern void xtrace_print_string_literal(const char* str); 6 6 7 7 #ifdef __cplusplus 8 8 }
+1 -1
src/xtrace/lock.c
··· 10 10 #endif 11 11 12 12 #if XTRACE_LOCK_DEBUG 13 - #define xtrace_lock_debug_internal(x, ...) xtrace_printf(x "\n", ## __VA_ARGS__) 13 + #define xtrace_lock_debug_internal(x, ...) xtrace_log(x "\n", ## __VA_ARGS__) 14 14 #undef XTRACE_INLINE 15 15 #define XTRACE_INLINE 16 16 #else
+75 -82
src/xtrace/mach_trace.cpp
··· 13 13 #include "mig_trace.h" 14 14 #include "tls.h" 15 15 16 - DEFINE_XTRACE_TLS_VAR(int, mach_call_nr); 17 - DEFINE_XTRACE_TLS_VAR(void*, argument_ptr); 18 - DEFINE_XTRACE_TLS_VAR(mach_port_name_t, request_port); 16 + DEFINE_XTRACE_TLS_VAR(int, mach_call_nr, -1); 17 + DEFINE_XTRACE_TLS_VAR(void*, argument_ptr, NULL); 18 + DEFINE_XTRACE_TLS_VAR(mach_port_name_t, request_port, MACH_PORT_NULL); 19 19 20 - static void print_kern_return(char* buf, int nr, uintptr_t rv); 21 - static void print_port_return(char* buf, int nr, uintptr_t rv); 22 - static void print_int_return(char* buf, int nr, uintptr_t rv); 23 - static void print_empty(char* buf, int nr, void* args[]); 24 - static void print_port_ptr_return(char* buf, int nr, uintptr_t rv); 20 + static void print_kern_return(int nr, uintptr_t rv); 21 + static void print_port_return(int nr, uintptr_t rv); 22 + static void print_int_return(int nr, uintptr_t rv); 23 + static void print_empty(int nr, void* args[]); 24 + static void print_port_ptr_return(int nr, uintptr_t rv); 25 25 26 - static void print_mach_msg_args(char* buf, int nr, void* args[]); 27 - static void print_mach_port_insert_right_args(char* buf, int nr, void* args[]); 26 + static void print_mach_msg_args(int nr, void* args[]); 27 + static void print_mach_port_insert_right_args(int nr, void* args[]); 28 28 29 - static void print_mach_port_member_args(char* buf, int nr, void* args[]); 29 + static void print_mach_port_member_args(int nr, void* args[]); 30 30 31 - static void print_mach_timebase_info_args(char* buf, int nr, void* args[]); 32 - static void print_mach_timebase_info_res(char* buf, int nr, uintptr_t rv); 31 + static void print_mach_timebase_info_args(int nr, void* args[]); 32 + static void print_mach_timebase_info_res(int nr, uintptr_t rv); 33 33 34 - static void print_mach_port_allocate_args(char* buf, int nr, void* args[]); 35 - static void print_task_for_pid_args(char* buf, int nr, void* args[]); 34 + static void print_mach_port_allocate_args(int nr, void* args[]); 35 + static void print_task_for_pid_args(int nr, void* args[]); 36 36 37 - static void print_pid_for_task_args(char* buf, int nr, void* args[]); 38 - static void print_pid_for_task_res(char* buf, int nr, uintptr_t rv); 37 + static void print_pid_for_task_args(int nr, void* args[]); 38 + static void print_pid_for_task_res(int nr, uintptr_t rv); 39 39 40 40 static void print_mach_msg_entry(void* args[]); 41 41 static void print_mach_msg_exit(void); ··· 47 47 [15] = { "_kernelrpc_mach_vm_map_trap", NULL, print_kern_return }, 48 48 [16] = { "_kernelrpc_mach_port_allocate_trap", print_mach_port_allocate_args, print_port_ptr_return }, 49 49 [17] = { "_kernelrpc_mach_port_destroy_trap", NULL, print_kern_return }, 50 - [18] = { "_kernelrpc_mach_port_deallocate_trap", [](char* buf, int nr, void* args[]) { __simple_sprintf(buf, "task %u, port name %u", (unsigned int) (unsigned long) args[0], (unsigned int) (unsigned long) args[1]); }, print_kern_return }, 50 + [18] = { "_kernelrpc_mach_port_deallocate_trap", [](int nr, void* args[]) { xtrace_log("task %u, port name %u", (unsigned int) (unsigned long) args[0], (unsigned int) (unsigned long) args[1]); }, print_kern_return }, 51 51 [19] = { "_kernelrpc_mach_port_mod_refs_trap", NULL, print_kern_return }, 52 52 [20] = { "_kernelrpc_mach_port_move_member_trap", print_mach_port_member_args, print_kern_return }, 53 53 [21] = { "_kernelrpc_mach_port_insert_right_trap", print_mach_port_insert_right_args, print_kern_return }, ··· 226 226 set_mach_call_nr(-1); 227 227 } 228 228 229 - int xtrace_kern_return_to_str(char* buf, kern_return_t kr) 229 + void xtrace_print_kern_return(kern_return_t kr) 230 230 { 231 231 if (kr >= MACH_RCV_IN_PROGRESS && kr <= MACH_RCV_INVALID_TRAILER) 232 - return __simple_sprintf(buf, "%s", mach_rcv_errors[kr - MACH_RCV_IN_PROGRESS]); 232 + xtrace_log("%s", mach_rcv_errors[kr - MACH_RCV_IN_PROGRESS]); 233 233 else if (kr >= MACH_SEND_IN_PROGRESS && kr <= MACH_SEND_INVALID_TRAILER) 234 - return __simple_sprintf(buf, "%s", mach_send_errors[kr - MACH_SEND_IN_PROGRESS]); 234 + xtrace_log("%s", mach_send_errors[kr - MACH_SEND_IN_PROGRESS]); 235 235 else if (kr >= KERN_SUCCESS && kr <= KERN_INSUFFICIENT_BUFFER_SIZE) 236 - return __simple_sprintf(buf, "%s", kern_return_values[kr]); 236 + xtrace_log("%s", kern_return_values[kr]); 237 237 else if (kr >= BOOTSTRAP_NOT_PRIVILEGED && kr <= BOOTSTRAP_NO_CHILDREN) 238 - return __simple_sprintf(buf, "%s", bootstrap_errors[kr - BOOTSTRAP_NOT_PRIVILEGED]); 238 + xtrace_log("%s", bootstrap_errors[kr - BOOTSTRAP_NOT_PRIVILEGED]); 239 239 else if (kr <= MIG_TYPE_ERROR && kr >= MIG_TRAILER_ERROR) 240 - return __simple_sprintf(buf, "%s", mig_errors[MIG_TYPE_ERROR - kr]); 240 + xtrace_log("%s", mig_errors[MIG_TYPE_ERROR - kr]); 241 241 else 242 - return __simple_sprintf(buf, "(kern_return_t) %x", kr); 242 + xtrace_log("(kern_return_t) %x", kr); 243 243 } 244 244 245 - static void print_kern_return(char* buf, int nr, uintptr_t rv) 245 + static void print_kern_return(int nr, uintptr_t rv) 246 246 { 247 - xtrace_kern_return_to_str(buf, (kern_return_t) rv); 247 + xtrace_print_kern_return((kern_return_t) rv); 248 248 } 249 249 250 - static void print_port_return(char* buf, int nr, uintptr_t rv) 250 + static void print_port_return(int nr, uintptr_t rv) 251 251 { 252 - __simple_sprintf(buf, "port right %d", (unsigned int) (unsigned long) rv); 252 + xtrace_log("port right %d", (unsigned int) (unsigned long) rv); 253 253 } 254 254 255 255 static void print_int_return(char* buf, int nr, uintptr_t rv) 256 256 { 257 - __simple_sprintf(buf, "%d", (int) (long) rv); 257 + xtrace_log("%d", (int) (long) rv); 258 258 } 259 259 260 - static void print_empty(char* buf, int nr, void* args[]) 260 + static void print_empty(int nr, void* args[]) 261 261 { 262 - *buf = 0; 262 + 263 263 } 264 264 265 - static void print_port_ptr_return(char* buf, int nr, uintptr_t rv) 265 + static void print_port_ptr_return(int nr, uintptr_t rv) 266 266 { 267 267 if (rv != KERN_SUCCESS) 268 268 { 269 - print_kern_return(buf, nr, rv); 269 + print_kern_return(nr, rv); 270 270 set_argument_ptr(NULL); 271 271 return; 272 272 } 273 273 if (get_argument_ptr() == NULL) 274 274 { 275 - *buf = 0; 276 275 return; 277 276 } 278 - __simple_sprintf(buf, "port right %d", *(mach_port_name_t*)get_argument_ptr()); 277 + xtrace_log("port right %d", *(mach_port_name_t*)get_argument_ptr()); 279 278 set_argument_ptr(NULL); 280 279 } 281 280 ··· 289 288 "MACH_PORT_RIGHT_NUMBER" 290 289 }; 291 290 292 - static void print_mach_port_allocate_args(char* buf, int nr, void* args[]) 291 + static void print_mach_port_allocate_args(int nr, void* args[]) 293 292 { 294 293 mach_port_name_t target = (mach_port_name_t) (long) args[0]; 295 294 mach_port_right_t right = (mach_port_right_t) (long) args[1]; ··· 301 300 else 302 301 right_name = port_right_names[right]; 303 302 304 - __simple_sprintf(buf, "task %d, %s", target, right_name); 303 + xtrace_log("task %d, %s", target, right_name); 305 304 } 306 305 307 306 static const char* const port_dispositions[] = { ··· 318 317 "MACH_MSG_TYPE_DISPOSE_SEND_ONCE" 319 318 }; 320 319 321 - static void print_mach_port_insert_right_args(char* buf, int nr, void* args[]) 320 + static void print_mach_port_insert_right_args(int nr, void* args[]) 322 321 { 323 322 mach_port_name_t target = (mach_port_name_t) (long) args[0]; 324 323 mach_port_name_t name = (mach_port_name_t) (long) args[1]; ··· 331 330 else 332 331 disp = port_dispositions[disposition - MACH_MSG_TYPE_PORT_NAME]; 333 332 334 - __simple_sprintf(buf, "task %d, new name %d, port right %d, %s", target, name, right, disp); 333 + xtrace_log("task %d, new name %d, port right %d, %s", target, name, right, disp); 335 334 } 336 335 337 - static void print_mach_port_member_args(char* buf, int nr, void* args[]) 336 + static void print_mach_port_member_args(int nr, void* args[]) 338 337 { 339 338 mach_port_name_t target = (mach_port_name_t) (long) args[0]; 340 339 mach_port_name_t name = (mach_port_name_t) (long) args[1]; 341 340 mach_port_name_t pset = (mach_port_name_t) (long) args[2]; 342 341 343 - __simple_sprintf(buf, "task %d, port right %d, port set %d", target, name, pset); 342 + xtrace_log("task %d, port right %d, port set %d", target, name, pset); 344 343 } 345 344 346 - static void print_mach_timebase_info_args(char* buf, int nr, void* args[]) 345 + static void print_mach_timebase_info_args(int nr, void* args[]) 347 346 { 348 347 set_argument_ptr(args[0]); 349 348 if (get_argument_ptr() == NULL) 350 - __simple_sprintf(buf, "NULL"); 351 - else 352 - *buf = 0; 349 + xtrace_log("NULL"); 353 350 } 354 351 355 - static void print_mach_timebase_info_res(char* buf, int nr, uintptr_t rv) 352 + static void print_mach_timebase_info_res(int nr, uintptr_t rv) 356 353 { 357 354 if (rv != KERN_SUCCESS) 358 355 { 359 - print_kern_return(buf, nr, rv); 356 + print_kern_return(nr, rv); 360 357 set_argument_ptr(NULL); 361 358 return; 362 359 } 363 360 if (get_argument_ptr() != NULL) 364 361 { 365 362 mach_timebase_info_t timebase = (mach_timebase_info_t)get_argument_ptr(); 366 - __simple_sprintf(buf, "numer = %d, denom = %d", timebase->numer, timebase->denom); 363 + xtrace_log("numer = %d, denom = %d", timebase->numer, timebase->denom); 367 364 } 368 - else 369 - *buf = 0; 370 365 371 366 set_argument_ptr(NULL); 372 367 } 373 368 374 - static void print_task_for_pid_args(char* buf, int nr, void* args[]) 369 + static void print_task_for_pid_args(int nr, void* args[]) 375 370 { 376 371 mach_port_name_t target = (mach_port_name_t) (long) args[0]; 377 372 int pid = (int) (long) args[1]; 378 373 set_argument_ptr(args[2]); 379 374 380 - __simple_sprintf(buf, "task %d, pid %d", target, pid); 375 + xtrace_log("task %d, pid %d", target, pid); 381 376 } 382 377 383 - static void print_pid_for_task_args(char* buf, int nr, void* args[]) 378 + static void print_pid_for_task_args(int nr, void* args[]) 384 379 { 385 380 mach_port_name_t task = (mach_port_name_t) (long) args[0]; 386 381 set_argument_ptr(args[1]); 387 382 388 - __simple_sprintf(buf, "task %d", task); 383 + xtrace_log("task %d", task); 389 384 } 390 385 391 - static void print_pid_for_task_res(char* buf, int nr, uintptr_t rv) 386 + static void print_pid_for_task_res(int nr, uintptr_t rv) 392 387 { 393 388 if (rv != KERN_SUCCESS) 394 389 { 395 - print_kern_return(buf, nr, rv); 390 + print_kern_return(nr, rv); 396 391 set_argument_ptr(NULL); 397 392 return; 398 393 } 399 394 if (get_argument_ptr() != NULL) 400 - __simple_sprintf(buf, "pid %d", * (int*)get_argument_ptr()); 401 - else 402 - *buf = 0; 395 + xtrace_log("pid %d", * (int*)get_argument_ptr()); 403 396 404 397 set_argument_ptr(NULL); 405 398 } ··· 427 420 428 421 static void print_mach_msg(const mach_msg_header_t* msg, mach_msg_size_t size) 429 422 { 430 - xtrace_printf("{"); 423 + xtrace_log("{"); 431 424 432 425 mach_msg_bits_t bits = msg->msgh_bits; 433 426 if (MACH_MSGH_BITS_HAS_REMOTE(bits)) 434 - xtrace_printf("remote = %s %u, ", xtrace_msg_type_to_str(MACH_MSGH_BITS_REMOTE(bits), 0), msg->msgh_remote_port); 427 + xtrace_log("remote = %s %u, ", xtrace_msg_type_to_str(MACH_MSGH_BITS_REMOTE(bits), 0), msg->msgh_remote_port); 435 428 if (MACH_MSGH_BITS_HAS_LOCAL(bits)) 436 - xtrace_printf("local = %s %u, ", xtrace_msg_type_to_str(MACH_MSGH_BITS_LOCAL(bits), 0), msg->msgh_local_port); 429 + xtrace_log("local = %s %u, ", xtrace_msg_type_to_str(MACH_MSGH_BITS_LOCAL(bits), 0), msg->msgh_local_port); 437 430 if (MACH_MSGH_BITS_HAS_VOUCHER(bits)) 438 - xtrace_printf("voucher = %s %u, ", xtrace_msg_type_to_str(MACH_MSGH_BITS_VOUCHER(bits), 0), msg->msgh_voucher_port); 431 + xtrace_log("voucher = %s %u, ", xtrace_msg_type_to_str(MACH_MSGH_BITS_VOUCHER(bits), 0), msg->msgh_voucher_port); 439 432 if (MACH_MSGH_BITS_IS_COMPLEX(bits)) 440 - xtrace_printf("complex, "); 433 + xtrace_log("complex, "); 441 434 442 - xtrace_printf("id = %d}", msg->msgh_id); 435 + xtrace_log("id = %d}", msg->msgh_id); 443 436 444 437 if (!MACH_MSGH_BITS_IS_COMPLEX(bits)) 445 438 { 446 - xtrace_printf(", %lu bytes of inline data\n", size - sizeof(mach_msg_header_t)); 439 + xtrace_log(", %lu bytes of inline data\n", size - sizeof(mach_msg_header_t)); 447 440 return; 448 441 } 449 442 ··· 456 449 if (type == MACH_MSG_PORT_DESCRIPTOR) 457 450 { 458 451 mach_msg_port_descriptor_t* port = (mach_msg_port_descriptor_t*) ptr; 459 - xtrace_printf(", %s %u", xtrace_msg_type_to_str(port->disposition, 0), port->name); 452 + xtrace_log(", %s %u", xtrace_msg_type_to_str(port->disposition, 0), port->name); 460 453 ptr = (mach_msg_descriptor_t*) (port + 1); 461 454 } 462 455 else if (type == MACH_MSG_OOL_DESCRIPTOR || type == MACH_MSG_OOL_VOLATILE_DESCRIPTOR) 463 456 { 464 457 mach_msg_ool_descriptor_t* ool = (mach_msg_ool_descriptor_t*) ptr; 465 - xtrace_printf(", ool [%p; %u]", ool->address, ool->size); 458 + xtrace_log(", ool [%p; %u]", ool->address, ool->size); 466 459 ptr = (mach_msg_descriptor_t*) (ool + 1); 467 460 } 468 461 else if (type == MACH_MSG_OOL_PORTS_DESCRIPTOR) 469 462 { 470 463 mach_msg_ool_ports_descriptor_t* ool_ports = (mach_msg_ool_ports_descriptor_t*) ptr; 471 - xtrace_printf(", ool ports %s [%p; x%u]", 464 + xtrace_log(", ool ports %s [%p; x%u]", 472 465 xtrace_msg_type_to_str(ool_ports->disposition, 0), 473 466 ool_ports->address, ool_ports->count); 474 467 ptr = (mach_msg_descriptor_t*) (ool_ports + 1); 475 468 } 476 469 else 477 470 { 478 - xtrace_printf(", ???"); 471 + xtrace_log(", ???"); 479 472 ptr++; 480 473 } 481 474 } 482 475 483 - xtrace_printf(", %lu bytes of inline data\n", size - ((const char*) ptr - (const char*) msg)); 476 + xtrace_log(", %lu bytes of inline data\n", size - ((const char*) ptr - (const char*) msg)); 484 477 } 485 478 486 479 static void print_mach_msg_entry(void* args[]) ··· 492 485 if (options & MACH_SEND_MSG) 493 486 { 494 487 set_request_port(message->msgh_remote_port); 495 - xtrace_printf("\n"); 488 + xtrace_log("\n"); 496 489 xtrace_start_line(8); 497 490 print_mach_msg(message, send_size); 498 491 xtrace_start_line(8); 499 492 xtrace_print_mig_message(message, get_request_port()); 500 - xtrace_printf("\n"); 493 + xtrace_log("\n"); 501 494 } 502 495 503 496 if (options & MACH_RCV_MSG) ··· 515 508 set_argument_ptr(args[0]); 516 509 break; 517 510 default: 518 - xtrace_printf("Unexpected mach_call_nr"); 511 + xtrace_log("Unexpected mach_call_nr"); 519 512 return; 520 513 } 521 514 } ··· 531 524 print_mach_msg(message, message->msgh_size); 532 525 xtrace_start_line(8); 533 526 xtrace_print_mig_message(message, get_request_port()); 534 - xtrace_printf("\n"); 527 + xtrace_log("\n"); 535 528 set_argument_ptr(NULL); 536 529 set_request_port(MACH_PORT_NULL); 537 530 } 538 531 539 - static void print_mach_msg_args(char* buf, int nr, void* args[]) 532 + static void print_mach_msg_args(int nr, void* args[]) 540 533 { 541 534 mach_msg_header_t* msg = (mach_msg_header_t*) args[0]; 542 535 mach_msg_option_t options = (mach_msg_option_t) (unsigned long) args[1]; ··· 546 539 mach_msg_timeout_t timeout = (mach_msg_timeout_t) (unsigned long) args[5]; 547 540 mach_port_name_t notify = (mach_port_name_t) (unsigned long) args[6]; 548 541 549 - buf += __simple_sprintf(buf, "%p, ", msg); 542 + xtrace_log("%p, ", msg); 550 543 551 544 int options_cnt = 0; 552 545 553 546 #define OPTION(OPT) if (options & OPT) \ 554 547 { \ 555 548 if (options_cnt > 0) \ 556 - *buf++ = '|'; \ 557 - buf += __simple_sprintf(buf, #OPT); \ 549 + xtrace_log("|"); \ 550 + xtrace_log(#OPT); \ 558 551 options_cnt++; \ 559 552 } 560 553 ··· 579 572 #undef OPTION 580 573 581 574 if (options_cnt == 0) 582 - buf += __simple_sprintf(buf, "MACH_MSG_OPTION_NONE"); 575 + xtrace_log("MACH_MSG_OPTION_NONE"); 583 576 584 - __simple_sprintf(buf, ", %d, %d, port %d, %d, port %d", send_size, rcv_size, rcv_name, timeout, notify); 577 + xtrace_log(", %d, %d, port %d, %d, port %d", send_size, rcv_size, rcv_name, timeout, notify); 585 578 }
+1 -1
src/xtrace/mach_trace.h
··· 3 3 #endif 4 4 5 5 const char* xtrace_msg_type_to_str(mach_msg_type_name_t type_name, int full); 6 - int xtrace_kern_return_to_str(char* buf, kern_return_t kr); 6 + void xtrace_print_kern_return(kern_return_t kr); 7 7 8 8 #ifdef __cplusplus 9 9 }
+1 -1
src/xtrace/malloc.c
··· 16 16 #endif 17 17 18 18 #if XTRACE_MALLOC_DEBUG 19 - #define xtrace_malloc_debug(x, ...) xtrace_printf(x "\n", ## __VA_ARGS__) 19 + #define xtrace_malloc_debug(x, ...) xtrace_log(x "\n", ## __VA_ARGS__) 20 20 #undef XTRACE_INLINE 21 21 #define XTRACE_INLINE 22 22 #else
+26 -32
src/xtrace/mig_trace.c
··· 61 61 void* dylib_handle = dlopen(path, RTLD_LOCAL); 62 62 if (dylib_handle == NULL) 63 63 { 64 - xtrace_fprintf(fileno(stderr), "xtrace: failed to dlopen %s: %s\n", path, dlerror()); 64 + xtrace_error("xtrace: failed to dlopen %s: %s\n", path, dlerror()); 65 65 subsystems[i] = NULL; 66 66 continue; 67 67 } 68 68 subsystems[i] = (struct xtrace_mig_subsystem*) dlsym(dylib_handle, "xtrace_mig_subsystem"); 69 69 if (subsystems[i] == NULL) 70 70 { 71 - xtrace_fprintf(fileno(stderr), "xtrace: failed to dlsym(%s, \"xtrace_mig_subsystem\"): %s\n", path, dlerror()); 71 + xtrace_error("xtrace: failed to dlsym(%s, \"xtrace_mig_subsystem\"): %s\n", path, dlerror()); 72 72 // Leave NULL subsystem in place and continue. 73 73 } 74 74 } ··· 76 76 closedir(xtrace_mig_dir); 77 77 } 78 78 79 - DEFINE_XTRACE_TLS_VAR(bool, is_first_arg); 79 + DEFINE_XTRACE_TLS_VAR(bool, is_first_arg, false); 80 80 81 - #define BEFORE if (!get_is_first_arg()) xtrace_printf(", ") 81 + #define BEFORE if (!get_is_first_arg()) xtrace_log(", ") 82 82 #define AFTER set_is_first_arg(false) 83 83 84 84 static void add_raw_arg(const char* format, ...) ··· 97 97 static void add_num_arg(unsigned long long n) 98 98 { 99 99 BEFORE; 100 - xtrace_printf("%llu", n); 100 + xtrace_log("%llu", n); 101 101 AFTER; 102 102 } 103 103 104 104 static void add_ptr_arg(void* ptr) 105 105 { 106 106 BEFORE; 107 - xtrace_printf("%p", ptr); 107 + xtrace_log("%p", ptr); 108 108 AFTER; 109 109 } 110 110 111 111 static void add_string_arg(const char* s) 112 112 { 113 113 BEFORE; 114 - char buf[1024]; 115 - xtrace_format_string_literal(buf, s); 116 - xtrace_printf("%s", buf); 114 + xtrace_print_string_literal(s); 117 115 AFTER; 118 116 } 119 117 ··· 121 119 { 122 120 BEFORE; 123 121 const unsigned char* b = (const unsigned char*) bytes; 124 - xtrace_printf("bytes "); 122 + xtrace_log("bytes "); 125 123 for (int i = 0; i < cnt; i++) 126 - xtrace_printf("%x", b[i]); 124 + xtrace_log("%x", b[i]); 127 125 AFTER; 128 126 } 129 127 130 128 static void add_return_code_arg(kern_return_t code) 131 129 { 132 130 BEFORE; 133 - char buf[100]; 134 - xtrace_kern_return_to_str(buf, code); 135 - xtrace_printf("return %s", buf); 131 + xtrace_print_kern_return(code); 136 132 AFTER; 137 133 } 138 134 139 135 static void add_port_arg(mach_port_name_t port_name, mach_msg_type_name_t disposition) 140 136 { 141 137 BEFORE; 142 - xtrace_printf("%s %u", xtrace_msg_type_to_str(disposition, 0), port_name); 138 + xtrace_log("%s %u", xtrace_msg_type_to_str(disposition, 0), port_name); 143 139 AFTER; 144 140 } 145 141 146 142 static void add_ool_mem_arg(const void* ptr, unsigned long size) 147 143 { 148 144 BEFORE; 149 - xtrace_printf("mem [%p; %lu]", ptr, size); 145 + xtrace_log("mem [%p; %lu]", ptr, size); 150 146 AFTER; 151 147 } 152 148 153 149 static void add_ool_ports_arg(const void* ptr, unsigned long cnt, mach_msg_type_name_t disposition) 154 150 { 155 151 BEFORE; 156 - xtrace_printf("%s [%p; x%lu]", xtrace_msg_type_to_str(disposition, 0), ptr, cnt); 152 + xtrace_log("%s [%p; x%lu]", xtrace_msg_type_to_str(disposition, 0), ptr, cnt); 157 153 AFTER; 158 154 } 159 155 ··· 182 178 { 183 179 BEFORE; 184 180 unsigned char* p = (unsigned char*) ptr; 185 - xtrace_printf("{"); 181 + xtrace_log("{"); 186 182 for (unsigned long i = 0; i < cnt; i++) 187 183 { 188 184 if (i != 0) 189 - xtrace_printf(", "); 190 - xtrace_printf("%llu", read_integer((void*) p, item_size)); 185 + xtrace_log(", "); 186 + xtrace_log("%llu", read_integer((void*) p, item_size)); 191 187 p += item_size; 192 188 } 193 - xtrace_printf("}"); 189 + xtrace_log("}"); 194 190 AFTER; 195 191 } 196 192 ··· 198 194 { 199 195 BEFORE; 200 196 unsigned char* p = (unsigned char*) ptr; 201 - xtrace_printf("["); 197 + xtrace_log("["); 202 198 for (unsigned long i = 0; i < cnt; i++) 203 199 { 204 200 if (i != 0) 205 - xtrace_printf(", "); 206 - xtrace_printf("%llu", read_integer((void*) p, item_size)); 201 + xtrace_log(", "); 202 + xtrace_log("%llu", read_integer((void*) p, item_size)); 207 203 p += item_size; 208 204 } 209 - xtrace_printf("]"); 205 + xtrace_log("]"); 210 206 AFTER; 211 207 } 212 208 213 209 static void set_return_code(kern_return_t code) 214 210 { 215 211 BEFORE; 216 - char buf[100]; 217 - xtrace_kern_return_to_str(buf, code); 218 - xtrace_printf("%s", buf); 212 + xtrace_print_kern_return(code); 219 213 AFTER; 220 214 } 221 215 ··· 343 337 return; 344 338 345 339 if (!is_reply) 346 - xtrace_printf("%s::%s(", s->name, r->name); 340 + xtrace_log("%s::%s(", s->name, r->name); 347 341 else 348 342 { 349 343 xtrace_set_gray_color(); 350 - xtrace_printf("%s::%s() -> ", s->name, r->name); 344 + xtrace_log("%s::%s() -> ", s->name, r->name); 351 345 xtrace_reset_color(); 352 346 } 353 347 ··· 355 349 r->routine(message, is_reply, &callbacks); 356 350 357 351 if (!is_reply) 358 - xtrace_printf(")"); 352 + xtrace_log(")"); 359 353 else 360 - xtrace_printf(" "); 354 + xtrace_log(" "); 361 355 }
+26 -29
src/xtrace/posix_spawn_args.c
··· 8 8 #include <spawn_private.h> 9 9 10 10 #include <darling/emulation/simple.h> 11 + #include "bsd_trace.h" 11 12 12 - extern int xtrace_format_string_literal(char* buf, const char* str); 13 - extern int print_open_flags(char* buf, void* arg); 13 + #include "xtracelib.h" 14 + 15 + extern void print_open_flags(void* arg); 14 16 15 17 static struct { 16 18 unsigned short flag; ··· 33 35 #undef POSIX_SPAWN_ATTR_ENTRY 34 36 }; 35 37 36 - int print_arg_posix_spawn_args(char* buf, void* arg) { 37 - const char* initial_buf = buf; 38 + void print_arg_posix_spawn_args(void* arg) { 38 39 const struct _posix_spawn_args_desc* args = (const struct _posix_spawn_args_desc*)(arg); 39 40 bool is_first = true; 40 41 41 - buf += __simple_sprintf(buf, "{ attributes = "); 42 + xtrace_log("{ attributes = "); 42 43 43 44 if (args && args->attrp) { 44 45 for (size_t i = 0; all_posix_spawn_attrs[i].name != NULL; i++) { ··· 46 47 if (is_first) { 47 48 is_first = false; 48 49 } else { 49 - *buf++ = '|'; 50 + xtrace_log("|"); 50 51 } 51 52 52 - buf += __simple_sprintf(buf, "%s", all_posix_spawn_attrs[i].name); 53 + xtrace_log("%s", all_posix_spawn_attrs[i].name); 53 54 } 54 55 } 55 56 } 56 57 57 58 if (is_first) { 58 - *buf++ = '0'; 59 + xtrace_log("0"); 59 60 } 60 61 61 - buf += __simple_sprintf(buf, ", file_actions = {"); 62 + xtrace_log(", file_actions = {"); 62 63 63 64 if (args && args->file_actions) { 64 65 for (size_t i = 0; i < args->file_actions->psfa_act_count; ++i) { 65 66 const struct _psfa_action* action = &args->file_actions->psfa_act_acts[i]; 66 67 67 68 if (i != 0) { 68 - *buf++ = ','; 69 - *buf++ = ' '; 69 + xtrace_log(", "); 70 70 } 71 71 72 72 switch (action->psfaa_type) { 73 73 case PSFA_OPEN: 74 - buf += __simple_sprintf(buf, "open("); 75 - buf += xtrace_format_string_literal(buf, action->psfaa_openargs.psfao_path); 76 - *buf++ = ','; 77 - *buf++ = ' '; 78 - buf += print_open_flags(buf, (void*)(intptr_t)(action->psfaa_openargs.psfao_oflag)); 79 - buf += __simple_sprintf(buf, ", 0%o) to %d", action->psfaa_openargs.psfao_mode, action->psfaa_filedes); 74 + xtrace_log("open("); 75 + xtrace_print_string_literal(action->psfaa_openargs.psfao_path); 76 + xtrace_log(", "); 77 + print_open_flags((void*)(intptr_t)(action->psfaa_openargs.psfao_oflag)); 78 + xtrace_log(", 0%o) to %d", action->psfaa_openargs.psfao_mode, action->psfaa_filedes); 80 79 break; 81 80 82 81 case PSFA_CLOSE: 83 - buf += __simple_sprintf(buf, "close(%d)", action->psfaa_filedes); 82 + xtrace_log("close(%d)", action->psfaa_filedes); 84 83 break; 85 84 86 85 case PSFA_DUP2: 87 - buf += __simple_sprintf(buf, "dup2(%d, %d)", action->psfaa_filedes, action->psfaa_dup2args.psfad_newfiledes); 86 + xtrace_log("dup2(%d, %d)", action->psfaa_filedes, action->psfaa_dup2args.psfad_newfiledes); 88 87 break; 89 88 90 89 case PSFA_INHERIT: 91 - buf += __simple_sprintf(buf, "inherit(%d)", action->psfaa_filedes); 90 + xtrace_log("inherit(%d)", action->psfaa_filedes); 92 91 break; 93 92 94 93 case PSFA_FILEPORT_DUP2: 95 94 // NOTE: if we see this in the output, that's an issue; 96 95 // we don't have this implemented right now 97 - buf += __simple_sprintf(buf, "dup2_fileport(port right %d, %d)", action->psfaa_fileport, action->psfaa_dup2args.psfad_newfiledes); 96 + xtrace_log("dup2_fileport(port right %d, %d)", action->psfaa_fileport, action->psfaa_dup2args.psfad_newfiledes); 98 97 break; 99 98 100 99 case PSFA_CHDIR: 101 - buf += __simple_sprintf(buf, "chdir("); 102 - buf += xtrace_format_string_literal(buf, action->psfaa_chdirargs.psfac_path); 103 - *buf++ = ')'; 100 + xtrace_log("chdir("); 101 + xtrace_print_string_literal(action->psfaa_chdirargs.psfac_path); 102 + xtrace_log(")"); 104 103 break; 105 104 106 105 case PSFA_FCHDIR: 107 - buf += __simple_sprintf(buf, "fchdir(%d)", action->psfaa_filedes); 106 + xtrace_log("fchdir(%d)", action->psfaa_filedes); 108 107 break; 109 108 110 109 default: 111 - buf += __simple_sprintf(buf, "???"); 110 + xtrace_log("???"); 112 111 break; 113 112 } 114 113 } 115 114 } 116 115 117 - buf += __simple_sprintf(buf, "} }"); 118 - 119 - return buf - initial_buf; 116 + xtrace_log("} }"); 120 117 };
+11 -4
src/xtrace/tls.c
··· 5 5 #include "lock.h" 6 6 #include <darling/emulation/simple.h> 7 7 #include <pthread/tsd_private.h> 8 + #include "xtracelib.h" 8 9 9 10 #ifndef XTRACE_TLS_DEBUG 10 11 #define XTRACE_TLS_DEBUG 0 11 12 #endif 12 13 13 14 #if XTRACE_TLS_DEBUG 14 - #define xtrace_tls_debug(x, ...) xtrace_printf(x "\n", ## __VA_ARGS__) 15 + #define xtrace_tls_debug(x, ...) xtrace_log(x "\n", ## __VA_ARGS__) 15 16 #else 16 17 #define xtrace_tls_debug(x, ...) 17 18 #endif ··· 50 51 }; 51 52 #endif 52 53 53 - void* xtrace_tls(void* key, size_t size) { 54 + void* xtrace_tls(void* key, size_t size, bool* created) { 54 55 xtrace_tls_debug("looking up tls variable for key %p", key); 55 56 56 57 tls_table_t table = _pthread_getspecific_direct(__PTK_XTRACE_TLS); ··· 62 63 xtrace_tls_debug("table is NULL, creating now..."); 63 64 table = xtrace_malloc(sizeof(struct tls_table)); 64 65 if (table == NULL) { 65 - _abort_with_payload_for_xtrace(0, 0, NULL, 0, "xtrace: failed TLS table memory allocation", 0); 66 + xtrace_abort("xtrace: failed TLS table memory allocation"); 66 67 } 67 68 table->size = 0; 68 69 _pthread_setspecific_direct(__PTK_XTRACE_TLS, table); ··· 72 73 for (size_t i = 0; i < table->size; ++i) { 73 74 if (table->table[i][0] == key) { 74 75 xtrace_tls_debug("found entry in table for key %p with value %p", key, table->table[i][1]); 76 + if (created) { 77 + *created = false; 78 + } 75 79 return table->table[i][1]; 76 80 } 77 81 } ··· 82 86 table->table[index][0] = key; 83 87 table->table[index][1] = xtrace_malloc(size); 84 88 if (table->table[index][1] == NULL) { 85 - _abort_with_payload_for_xtrace(0, 0, NULL, 0, "xtrace: failed TLS variable memory allocation", 0); 89 + xtrace_abort("xtrace: failed TLS variable memory allocation"); 86 90 } 87 91 xtrace_tls_debug("new table entry created for key %p with value %p", key, table->table[index][1]); 92 + if (created) { 93 + *created = true; 94 + } 88 95 return table->table[index][1]; 89 96 };
+23 -6
src/xtrace/tls.h
··· 1 1 #ifndef _XTRACE_TLS_H_ 2 2 #define _XTRACE_TLS_H_ 3 3 4 + #include <stddef.h> 5 + #include <stdbool.h> 4 6 #include <mach/port.h> 5 7 #include "base.h" 6 8 7 9 XTRACE_DECLARATIONS_BEGIN; 8 10 9 - #define DEFINE_XTRACE_TLS_VAR(type, name) \ 10 - char name ## _key = '\0'; \ 11 + #define DEFINE_XTRACE_TLS_VAR(type, name, default_value) \ 12 + static char name ## _key = '\0'; \ 11 13 XTRACE_INLINE \ 12 14 type get_ ## name(void) { \ 13 - return *(type*)xtrace_tls(&(name ## _key), sizeof(type)); \ 15 + bool created = false; \ 16 + type* var = (type*)xtrace_tls(&(name ## _key), sizeof(type), &created); \ 17 + if (created) { \ 18 + *var = default_value; \ 19 + } \ 20 + return *var; \ 14 21 }; \ 15 22 XTRACE_INLINE \ 16 23 void set_ ## name(type value) { \ 17 - *(type*)xtrace_tls(&(name ## _key), sizeof(type)) = value; \ 24 + bool created = false; \ 25 + type* var = (type*)xtrace_tls(&(name ## _key), sizeof(type), NULL); \ 26 + if (created) { \ 27 + *var = default_value; \ 28 + } \ 29 + *var = value; \ 18 30 }; \ 19 31 XTRACE_INLINE \ 20 32 type* get_ptr_ ## name(void) { \ 21 - return (type*)xtrace_tls(&(name ## _key), sizeof(type)); \ 33 + bool created = false; \ 34 + type* var = (type*)xtrace_tls(&(name ## _key), sizeof(type), &created); \ 35 + if (created) { \ 36 + *var = default_value; \ 37 + } \ 38 + return var; \ 22 39 }; 23 40 24 - void* xtrace_tls(void* key, size_t size); 41 + void* xtrace_tls(void* key, size_t size, bool* created); 25 42 26 43 XTRACE_DECLARATIONS_END; 27 44
+20
src/xtrace/xtrace
··· 1 1 #!/bin/sh 2 2 3 + if [ "$#" -eq 0 ]; then 4 + cat <<-'EOF' 5 + Usage: xtrace <command-to-trace> [arguments]... 6 + 7 + Useful environment variables: 8 + 9 + XTRACE_SPLIT_ENTRY_AND_EXIT - boolean - Used to print function entry (e.g. arguments) separately from function exit (e.g. return value). The normal format is "foo(arg1, arg2, etc) -> return_value". With this option, calls are formatted instead as "foo(arg1, arg2)" and then later (when they return) "foo() -> return_value". 10 + 11 + XTRACE_NO_COLOR - boolean - By default, xtrace produces colored output using ANSI escape codes; this option disables that. 12 + 13 + XTRACE_KPRINTF - boolean - xtrace normally prints to the standard console output. However, sometimes you would like to trace background processes, ones that don't have a console output. In that case, you can use XTRACE_KPRINTF to tell xtrace to print to the kernel console instead. All of xtrace's messages will be prefixed with `xtrace: `; that way, you can easily search for them in the kernel log. 14 + 15 + XTRACE_LOG_FILE - string (path) - This option serves the same purpose as XTRACE_KPRINTF: to log messages for background processes. However, instead of logging to the kernel console, it instead logs to the file at the path specified by this variable. NOTE: this option may affect program behavior, as a descriptor must be kept in-use for the logfile. It may also fail to log (and therefore abort) if the process is using all of its descriptors. There is a chance that it will affect program behavior if another thread sees the descriptor or tries to open a new one that would exceed the limit of descriptors only when the logfile is open. 16 + 17 + XTRACE_LOG_FILE_PER_THREAD - boolean - By default, xtrace outputs all of its messages into a single log stream (whether that's the standard console output, kernel console, or a log file). However, when this option is given and XTRACE_LOG_FILE is also specified, each thread will have a separate log file, each one named like "${XTRACE_LOG_FILE}.${THREAD_ID}". Note that without XTRACE_LOG_FILE, this option has no effect. 18 + EOF 19 + 20 + exit 0 21 + fi 22 + 3 23 export DYLD_INSERT_LIBRARIES="/usr/lib/darling/libxtrace.dylib" 4 24 exec "$@" 5 25
+168 -27
src/xtrace/xtracelib.c
··· 8 8 #include "xtracelib.h" 9 9 #include "mig_trace.h" 10 10 #include "tls.h" 11 + #include "lock.h" 12 + #include <limits.h> 13 + 14 + #include <darling/emulation/ext/for-xtrace.h> 15 + #include <fcntl.h> 16 + #include <signal.h> 11 17 12 18 // Defined in assembly 13 19 extern void darling_mach_syscall_entry_trampoline(void); ··· 39 45 40 46 static int xtrace_ignore = 1; 41 47 48 + // whether to use a sigaltstack guard page below the stack 49 + // (this should probably be left on) 50 + #define SIGALTSTACK_GUARD 1 51 + 42 52 __attribute__((constructor)) 43 53 void xtrace_setup() 44 54 { ··· 47 57 xtrace_setup_mach(); 48 58 xtrace_setup_bsd(); 49 59 60 + // override the default sigaltstack used by libsystem_kernel for the main thread 61 + // (we need more than the default 8KiB; testing has shown that 16KiB seems to be enough) 62 + struct bsd_stack custom_altstack = { 63 + .ss_size = 16 * 1024, 64 + .ss_flags = 0, 65 + }; 66 + 67 + #if SIGALTSTACK_GUARD 68 + custom_altstack.ss_sp = (void*)mmap(NULL, custom_altstack.ss_size + 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 69 + if (custom_altstack.ss_sp == MAP_FAILED) { 70 + xtrace_abort("xtrace: failed to allocate larger sigstack for main thread"); 71 + } 72 + 73 + mprotect(custom_altstack.ss_sp, 4096, PROT_NONE); 74 + custom_altstack.ss_sp = (char*)custom_altstack.ss_sp + 4096; 75 + #else 76 + custom_altstack.ss_sp = (void*)mmap(NULL, custom_altstack.ss_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 77 + if (custom_altstack.ss_sp == MAP_FAILED) { 78 + xtrace_abort("xtrace: failed to allocate larger sigstack for main thread"); 79 + } 80 + #endif 81 + 82 + if (_sigaltstack_for_xtrace(&custom_altstack, NULL) < 0) { 83 + xtrace_abort("failed to override sigaltstack"); 84 + } 85 + 86 + // and set the size to allocate for future threads 87 + _sigaltstack_set_default_size_for_xtrace(custom_altstack.ss_size); 88 + 50 89 xtrace_ignore = 0; 51 90 } 52 91 53 92 static int xtrace_split_entry_and_exit = 0; 54 93 int xtrace_no_color = 0; 55 94 int xtrace_kprintf = 0; 95 + 96 + static int xtrace_use_logfile = 0; 97 + static int xtrace_use_per_thread_logfile = 0; 98 + 99 + static char xtrace_logfile_base[PATH_MAX] = {0}; 100 + 101 + static xtrace_once_t xtrace_common_logfile_once = XTRACE_ONCE_INITIALIZER; 102 + int xtrace_common_logfile = -1; 103 + 104 + DEFINE_XTRACE_TLS_VAR(int, xtrace_per_thread_logfile, -1); 56 105 57 106 static void xtrace_setup_options(void) 58 107 { 108 + const char* xtrace_log_file = getenv("XTRACE_LOG_FILE"); 109 + 59 110 if (getenv("XTRACE_SPLIT_ENTRY_AND_EXIT") != NULL) 60 111 xtrace_split_entry_and_exit = 1; 61 112 if (getenv("XTRACE_NO_COLOR") != NULL) 62 113 xtrace_no_color = 1; 63 114 if (getenv("XTRACE_KPRINTF") != NULL) 64 115 xtrace_kprintf = 1; 116 + 117 + if (getenv("XTRACE_LOG_FILE_PER_THREAD") != NULL) 118 + xtrace_use_per_thread_logfile = 1; 119 + 120 + if (xtrace_log_file != NULL) { 121 + xtrace_use_logfile = 1; 122 + strlcpy(xtrace_logfile_base, xtrace_log_file, sizeof(xtrace_logfile_base)); 123 + } 65 124 } 66 125 67 126 ··· 121 180 if (xtrace_no_color) 122 181 return; 123 182 124 - xtrace_printf("\033[37m"); 183 + xtrace_log("\033[37m"); 125 184 } 126 185 127 186 void xtrace_reset_color(void) ··· 129 188 if (xtrace_no_color) 130 189 return; 131 190 132 - xtrace_printf("\033[0m"); 191 + xtrace_log("\033[0m"); 133 192 } 134 193 135 194 void xtrace_start_line(int indent) 136 195 { 137 196 xtrace_set_gray_color(); 138 197 139 - xtrace_printf("[%d]", sys_thread_selfid()); 198 + xtrace_log("[%d]", sys_thread_selfid()); 140 199 for (int i = 0; i < indent + 1; i++) 141 - xtrace_printf(" "); 200 + xtrace_log(" "); 142 201 143 202 xtrace_reset_color(); 144 203 } ··· 151 210 xtrace_set_gray_color(); 152 211 153 212 if (defs[nr].name != NULL) 154 - xtrace_printf("%s", defs[nr].name); 213 + xtrace_log("%s", defs[nr].name); 155 214 else 156 - xtrace_printf("%s %d", type, nr); 215 + xtrace_log("%s %d", type, nr); 157 216 158 217 // Leaves gray color on! 159 218 } ··· 170 229 int nrs[64]; 171 230 }; 172 231 173 - DEFINE_XTRACE_TLS_VAR(struct nested_call_struct, nested_call); 232 + DEFINE_XTRACE_TLS_VAR(struct nested_call_struct, nested_call, (struct nested_call_struct) {0}); 174 233 175 234 void handle_generic_entry(const struct calldef* defs, const char* type, int nr, void* args[]) 176 235 { ··· 180 239 if (get_ptr_nested_call()->previous_level < get_ptr_nested_call()->current_level && !xtrace_split_entry_and_exit) 181 240 { 182 241 // We are after an earlier entry without an exit. 183 - xtrace_printf("\n"); 242 + xtrace_log("\n"); 184 243 } 185 244 186 245 int indent = 4 * get_ptr_nested_call()->current_level; ··· 188 247 189 248 print_call(defs, type, nr, indent, 0); 190 249 191 - if (defs[nr].name != NULL) 250 + if (defs[nr].name != NULL && defs[nr].print_args != NULL) 192 251 { 193 - char args_buf[4096]; 194 - if (defs[nr].print_args != NULL) 195 - defs[nr].print_args(args_buf, nr, args); 196 - else 197 - strcpy(args_buf, "..."); 198 - xtrace_printf("(%s)", args_buf); 252 + xtrace_log("("); 253 + defs[nr].print_args(nr, args); 254 + xtrace_log(")"); 199 255 } 200 256 else 201 - xtrace_printf("(...)"); 257 + xtrace_log("(...)"); 202 258 203 259 if (xtrace_split_entry_and_exit) 204 - xtrace_printf("\n"); 260 + xtrace_log("\n"); 205 261 206 262 get_ptr_nested_call()->previous_level = get_ptr_nested_call()->current_level++; 207 263 } ··· 224 280 { 225 281 int indent = 4 * get_ptr_nested_call()->current_level; 226 282 print_call(defs, type, nr, indent, 1); 227 - xtrace_printf("()"); 283 + xtrace_log("()"); 228 284 } 229 285 230 286 xtrace_set_gray_color(); 231 - xtrace_printf(" -> "); 287 + xtrace_log(" -> "); 232 288 xtrace_reset_color(); 233 289 234 - if (defs[nr].name != NULL) 290 + if (defs[nr].name != NULL && defs[nr].print_retval != NULL) 235 291 { 236 - char args_buf[4096]; 237 - if (defs[nr].print_retval != NULL) 238 - defs[nr].print_retval(args_buf, nr, retval); 239 - else 240 - __simple_sprintf(args_buf, "0x%lx\n", retval); 241 - xtrace_printf("%s\n", args_buf); 292 + defs[nr].print_retval(nr, retval); 293 + xtrace_log("\n"); 242 294 } 243 295 else 244 - xtrace_printf("0x%lx\n", retval); 296 + xtrace_log("0x%lx\n", retval); 245 297 } 246 298 299 + void xtrace_log(const char* format, ...) { 300 + va_list args; 301 + va_start(args, format); 302 + xtrace_log_v(format, args); 303 + va_end(args); 304 + }; 305 + 306 + // TODO: we should add guarded FD support so that we can make FDs like these logfile descriptors guarded. 307 + // it would also be very useful to guard the special LKM descriptor. 308 + 309 + static void xtrace_common_logfile_init(void) { 310 + xtrace_common_logfile = _open_for_xtrace(xtrace_logfile_base, O_WRONLY | O_APPEND | O_CLOEXEC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 311 + }; 312 + 313 + static void ensure_logfile(void) { 314 + bool created = false; 315 + int fd = -1; 316 + 317 + if (!xtrace_use_logfile) { 318 + xtrace_abort("xtrace: tried to use logfile when not enabled"); 319 + } 320 + 321 + if (get_xtrace_per_thread_logfile() != -1) { 322 + return; 323 + } 324 + 325 + if (xtrace_use_per_thread_logfile) { 326 + char filename[sizeof(xtrace_logfile_base)]; 327 + char append[32] = {0}; 328 + 329 + strlcpy(filename, xtrace_logfile_base, PATH_MAX); 330 + 331 + __simple_snprintf(append, sizeof(append), ".%d", sys_thread_selfid()); 332 + strlcat(filename, append, PATH_MAX); 333 + 334 + fd = _open_for_xtrace(filename, O_WRONLY | O_APPEND | O_CLOEXEC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 335 + } else { 336 + xtrace_once(&xtrace_common_logfile_once, xtrace_common_logfile_init); 337 + fd = xtrace_common_logfile; 338 + } 339 + 340 + if (fd < 0) { 341 + xtrace_abort("xtrace: failed to open logfile"); 342 + } 343 + 344 + set_xtrace_per_thread_logfile(fd); 345 + }; 346 + 347 + void xtrace_log_v(const char* format, va_list args) { 348 + if (xtrace_kprintf) { 349 + char real_format[512] = "xtrace: "; 350 + strlcpy(&real_format[0] + (sizeof("xtrace: ") - 1), format, sizeof(real_format) - (sizeof("xtrace: ") - 1)); 351 + __simple_vkprintf(real_format, args); 352 + } else if (xtrace_use_logfile) { 353 + char output[512]; 354 + ensure_logfile(); 355 + __simple_vsnprintf(output, sizeof(output), format, args); 356 + __write_for_xtrace(get_xtrace_per_thread_logfile(), output, __simple_strlen(output)); 357 + } else { 358 + __simple_vprintf(format, args); 359 + } 360 + }; 361 + 362 + void xtrace_error(const char* format, ...) { 363 + va_list args; 364 + va_start(args, format); 365 + xtrace_error_v(format, args); 366 + va_end(args); 367 + }; 368 + 369 + void xtrace_error_v(const char* format, va_list args) { 370 + if (xtrace_kprintf) { 371 + char real_format[512] = "xtrace: "; 372 + strlcpy(real_format + (sizeof("xtrace: ") - 1), format, sizeof(real_format) - (sizeof("xtrace: ") - 1)); 373 + __simple_vkprintf(real_format, args); 374 + } else if (xtrace_use_logfile) { 375 + char output[512]; 376 + ensure_logfile(); 377 + __simple_vsnprintf(output, sizeof(output), format, args); 378 + __write_for_xtrace(get_xtrace_per_thread_logfile(), output, __simple_strlen(output)); 379 + } else { 380 + __simple_vfprintf(STDERR_FILENO, format, args); 381 + } 382 + }; 383 + 384 + void xtrace_abort(const char* message) { 385 + _abort_with_payload_for_xtrace(0, 0, NULL, 0, message, 0); 386 + __builtin_unreachable(); 387 + };
+7 -19
src/xtrace/xtracelib.h
··· 6 6 struct calldef 7 7 { 8 8 const char* name; 9 - void (*print_args)(char*, int nr, void* args[]); 10 - void (*print_retval)(char*, int nr, uintptr_t rv); 9 + void (*print_args)(int nr, void* args[]); 10 + void (*print_retval)(int nr, uintptr_t rv); 11 11 }; 12 12 13 13 #ifdef __cplusplus ··· 18 18 void handle_generic_exit(const struct calldef* defs, const char* type, uintptr_t retval, int force_split); 19 19 20 20 extern int xtrace_no_color; 21 - extern int xtrace_kprintf; 22 21 void xtrace_set_gray_color(void); 23 22 void xtrace_reset_color(void); 24 23 25 24 void xtrace_start_line(int indent); 26 25 27 - // the kprintf output is prefixed with "xtrace: " for easy grepping on dmesg 28 - 29 - #define xtrace_printf(format, ...) ({ \ 30 - if (xtrace_kprintf) { \ 31 - __simple_kprintf("xtrace: " format, ##__VA_ARGS__); \ 32 - } else { \ 33 - __simple_printf(format, ##__VA_ARGS__); \ 34 - } \ 35 - }) 26 + void xtrace_log(const char* format, ...) __attribute__((format(printf, 1, 2))); 27 + void xtrace_log_v(const char* format, va_list args) __attribute__((format(printf, 1, 0))); 28 + void xtrace_error(const char* format, ...) __attribute__((format(printf, 1, 2))); 29 + void xtrace_error_v(const char* format, va_list args) __attribute__((format(printf, 1, 0))); 36 30 37 - #define xtrace_fprintf(fd, format, ...) ({ \ 38 - if (xtrace_kprintf) { \ 39 - __simple_kprintf("xtrace: " format, ##__VA_ARGS__); \ 40 - } else { \ 41 - __simple_fprintf(fd, format, ##__VA_ARGS__); \ 42 - } \ 43 - }) 31 + void xtrace_abort(const char* message) __attribute__((noreturn)); 44 32 45 33 #ifdef __cplusplus 46 34 }