this repo has no description
1
fork

Configure Feed

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

Get xtrace working again (for multithreaded programs)

We basically have to reimplement locking, memory allocation, and TLS for xtrace directly on top of Linux syscalls because it can't use almost anything external (it can pretty much only use libsystem_kernel).

The one exception is that we use `pthread_key`s from libpthread to make TLS easier to implement. The only side effect that has is that it's one less available key for the application we're tracing to use, but we shouldn't ever see that cause problems (and if we do see it cause problems later on, then we can revise that).

+888 -51
+1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/darling/emulation/base.h
··· 1 + ../../../../../../../../../../src/kernel/emulation/linux/base.h
+1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/darling/emulation/ext/for-xtrace.h
··· 1 + ../../../../../../../../../../../src/kernel/emulation/linux/ext/for-xtrace.h
+1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/darling/emulation/ext/futex.h
··· 1 + ../../../../../../../../../../../src/kernel/emulation/linux/ext/futex.h
+1
Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/darling/emulation/mman/duct_mman.h
··· 1 + ../../../../../../../../../../../src/kernel/emulation/linux/mman/duct_mman.h
+1
src/kernel/emulation/linux/CMakeLists.txt
··· 263 263 ext/mremap.c 264 264 ext/file_handle.c 265 265 ext/fanotify.c 266 + ext/for-xtrace.c 266 267 bsdthread/bsdthread_register.c 267 268 bsdthread/bsdthread_create.c 268 269 bsdthread/bsdthread_terminate.c
+18
src/kernel/emulation/linux/ext/for-xtrace.c
··· 1 + #include "for-xtrace.h" 2 + #include "../mman/mman.h" 3 + #include "../misc/abort_with_payload.h" 4 + 5 + VISIBLE 6 + void* _mmap_for_xtrace(void* start, unsigned long len, int prot, int flags, int fd, long pos) { 7 + return sys_mmap(start, len, prot, flags, fd, pos); 8 + }; 9 + 10 + VISIBLE 11 + long _munmap_for_xtrace(void* addr, unsigned long len) { 12 + return sys_munmap(addr, len); 13 + }; 14 + 15 + VISIBLE 16 + 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 + return sys_abort_with_payload(reason_namespace, reason_code, payload, payload_size, reason_string, reason_flags); 18 + };
+16
src/kernel/emulation/linux/ext/for-xtrace.h
··· 1 + #ifndef _DARLING_EMULATION_FOR_XTRACE_H_ 2 + #define _DARLING_EMULATION_FOR_XTRACE_H_ 3 + 4 + #include <darling/emulation/base.h> 5 + #include <darling/emulation/mman/duct_mman.h> 6 + 7 + VISIBLE 8 + void* _mmap_for_xtrace(void* start, unsigned long len, int prot, int flags, int fd, long pos); 9 + 10 + VISIBLE 11 + long _munmap_for_xtrace(void* addr, unsigned long len); 12 + 13 + VISIBLE 14 + 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); 15 + 16 + #endif // _DARLING_EMULATION_FOR_XTRACE_H_
+3
src/xtrace/CMakeLists.txt
··· 15 15 mach_trace.cpp 16 16 bsd_trace.cpp 17 17 mig_trace.c 18 + tls.c 19 + malloc.c 20 + lock.c 18 21 ) 19 22 20 23 if (TARGET_x86_64)
+20
src/xtrace/base.h
··· 1 + #ifndef _XTRACE_BASE_H_ 2 + #define _XTRACE_BASE_H_ 3 + 4 + // common utilities for xtrace code 5 + 6 + #define XTRACE_INLINE static __attribute__((always_inline)) 7 + 8 + #ifdef __cplusplus 9 + #define XTRACE_DECLARATIONS_BEGIN extern "C" { 10 + #else 11 + #define XTRACE_DECLARATIONS_BEGIN 12 + #endif 13 + 14 + #ifdef __cplusplus 15 + #define XTRACE_DECLARATIONS_END } 16 + #else 17 + #define XTRACE_DECLARATIONS_END 18 + #endif 19 + 20 + #endif // _XTRACE_BASE_H_
+105
src/xtrace/lock.c
··· 1 + #include <stdatomic.h> 2 + 3 + #include "lock.h" 4 + #include "simple.h" 5 + 6 + #ifndef XTRACE_LOCK_DEBUG 7 + #define XTRACE_LOCK_DEBUG 0 8 + #endif 9 + 10 + #if XTRACE_LOCK_DEBUG 11 + #define xtrace_lock_debug(x, ...) __simple_printf(x "\n", ## __VA_ARGS__) 12 + #undef XTRACE_INLINE 13 + #define XTRACE_INLINE 14 + #else 15 + #define xtrace_lock_debug(x, ...) 16 + #endif 17 + 18 + // simple lock implementation on top of futexes, based on https://github.com/eliben/code-for-blog/blob/master/2018/futex-basics/mutex-using-futex.cpp 19 + 20 + // used to hide the use of C11 atomics from external code (e.g. C++ code, which can't use C11 atomics). 21 + // it's *technically* undefined behavior to cast the external one to this one, but that's okay; 22 + // we know our exact environment and it works in it 23 + typedef struct xtrace_lock_internal { 24 + _Atomic uint32_t state; 25 + } xtrace_lock_internal_t; 26 + 27 + typedef struct xtrace_once_internal { 28 + _Atomic uint8_t state; 29 + } xtrace_once_internal_t; 30 + 31 + enum xtrace_lock_state { 32 + // the lock is unlocked (duh) 33 + xtrace_lock_state_unlocked = 0, 34 + 35 + // the lock is locked, but no one is waiting for it 36 + xtrace_lock_state_locked_uncontended = 1, 37 + 38 + // the locked is locked and there are waiters 39 + xtrace_lock_state_locked_contended = 2, 40 + }; 41 + 42 + enum xtrace_once_state { 43 + // the once hasn't been run yet 44 + xtrace_once_state_untriggered = 0, 45 + 46 + // the once has already been run at least once 47 + xtrace_once_state_triggered = 1, 48 + }; 49 + 50 + void xtrace_lock_lock(xtrace_lock_t* _lock) { 51 + xtrace_lock_internal_t* lock = (xtrace_lock_internal_t*)_lock; 52 + 53 + xtrace_lock_debug("lock %p: locking", lock); 54 + 55 + // we do the initial exchange using `xtrace_lock_state_locked_uncontended` because 56 + // if it was previously unlocked, it is now locked but nobody is waiting for it 57 + uint32_t prev = atomic_exchange(&lock->state, xtrace_lock_state_locked_uncontended); 58 + 59 + xtrace_lock_debug("lock %p: previous state was %u", lock, prev); 60 + 61 + // if it was not unlocked, we need to do some waiting 62 + if (prev != xtrace_lock_state_unlocked) { 63 + do { 64 + // we can skip the atomic_exchange if the lock was already contended 65 + // also, when we do the atomic_exchange, we need to make sure it's still locked 66 + // we use `xtrace_lock_state_locked_contended` because it was either uncontended (but it is now) or it was already contended 67 + if (prev == xtrace_lock_state_locked_contended || atomic_exchange(&lock->state, xtrace_lock_state_locked_contended) != xtrace_lock_state_unlocked) { 68 + xtrace_lock_debug("lock %p: going to wait", lock); 69 + // do the actual sleeping 70 + // we expect `xtrace_lock_state_locked_contended` because we don't want to sleep if it's not contended 71 + __linux_futex(&lock->state, FUTEX_WAIT, xtrace_lock_state_locked_contended, NULL, 0, 0); 72 + xtrace_lock_debug("lock %p: awoken", lock); 73 + } 74 + 75 + // loop as long as the lock was not unlocked 76 + // we use `xtrace_lock_state_locked_contended` for the same reason as before 77 + } while ((prev = atomic_exchange(&lock->state, xtrace_lock_state_locked_contended)) != xtrace_lock_state_unlocked); 78 + xtrace_lock_debug("lock %p: lock acquired after sleeping", lock); 79 + } 80 + 81 + xtrace_lock_debug("lock %p: lock acquired", lock); 82 + }; 83 + 84 + void xtrace_lock_unlock(xtrace_lock_t* _lock) { 85 + xtrace_lock_internal_t* lock = (xtrace_lock_internal_t*)_lock; 86 + 87 + xtrace_lock_debug("lock %p: unlocking", lock); 88 + 89 + uint32_t prev = atomic_exchange(&lock->state, xtrace_lock_state_unlocked); 90 + 91 + xtrace_lock_debug("lock %p: previous state was %u", prev); 92 + 93 + // if it was previously contended, then we need to wake someone up 94 + if (prev == xtrace_lock_state_locked_contended) { 95 + xtrace_lock_debug("lock %p: waking someone up", lock); 96 + __linux_futex(&lock->state, FUTEX_WAKE, 1, NULL, 0, 0); 97 + } 98 + }; 99 + 100 + void xtrace_once(xtrace_once_t* _once, xtrace_once_callback callback) { 101 + xtrace_lock_internal_t* once = (xtrace_lock_internal_t*)_once; 102 + if (atomic_exchange(&once->state, xtrace_once_state_triggered) == xtrace_once_state_untriggered) { 103 + callback(); 104 + } 105 + };
+53
src/xtrace/lock.h
··· 1 + #ifndef _XTRACE_LOCK_H_ 2 + #define _XTRACE_LOCK_H_ 3 + 4 + #include <darling/emulation/ext/futex.h> 5 + #include <stdint.h> 6 + 7 + #include "base.h" 8 + 9 + // we also have to implement our own locks, because we can't rely on libplatform's or libpthread's locks 10 + // so we implement our own on top of Linux futexes 11 + 12 + XTRACE_DECLARATIONS_BEGIN; 13 + 14 + // 15 + // lock 16 + // 17 + 18 + typedef struct xtrace_lock { 19 + uint32_t state; 20 + } xtrace_lock_t; 21 + 22 + #define XTRACE_LOCK_INITIALIZER {0} 23 + 24 + XTRACE_INLINE 25 + void xtrace_lock_init(xtrace_lock_t* lock) { 26 + lock->state = 0; 27 + }; 28 + 29 + void xtrace_lock_lock(xtrace_lock_t* lock); 30 + void xtrace_lock_unlock(xtrace_lock_t* lock); 31 + 32 + // 33 + // once 34 + // 35 + 36 + typedef struct xtrace_once { 37 + uint8_t state; 38 + } xtrace_once_t; 39 + 40 + typedef void (*xtrace_once_callback)(void); 41 + 42 + #define XTRACE_ONCE_INITIALIZER {0} 43 + 44 + XTRACE_INLINE 45 + void xtrace_once_init(xtrace_once_t* once) { 46 + once->state = 0; 47 + }; 48 + 49 + void xtrace_once(xtrace_once_t* once, xtrace_once_callback callback); 50 + 51 + XTRACE_DECLARATIONS_END; 52 + 53 + #endif // _XTRACE_LOCK_H_
+38 -37
src/xtrace/mach_trace.cpp
··· 11 11 #include "xtracelib.h" 12 12 #include "mach_trace.h" 13 13 #include "mig_trace.h" 14 + #include "tls.h" 14 15 15 - _Thread_local int mach_call_nr = -1; 16 - _Thread_local void* argument_ptr = NULL; 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); 17 19 18 20 static void print_kern_return(char* buf, int nr, uintptr_t rv); 19 21 static void print_port_return(char* buf, int nr, uintptr_t rv); ··· 207 209 extern "C" 208 210 void darling_mach_syscall_entry_print(int nr, void* args[]) 209 211 { 210 - mach_call_nr = nr; 211 - handle_generic_entry(mach_defs, "mach", mach_call_nr, args); 212 + set_mach_call_nr(nr); 213 + handle_generic_entry(mach_defs, "mach", nr, args); 212 214 if (nr == 31 || nr == 32) 213 215 print_mach_msg_entry(args); 214 216 } ··· 216 218 extern "C" 217 219 void darling_mach_syscall_exit_print(uintptr_t retval) 218 220 { 219 - int is_msg = mach_call_nr == 31 || mach_call_nr == 32; 221 + int nr = get_mach_call_nr(); 222 + int is_msg = nr == 31 || nr == 32; 220 223 handle_generic_exit(mach_defs, "mach", retval, is_msg); 221 224 if (retval == KERN_SUCCESS && is_msg) 222 225 print_mach_msg_exit(); 223 - mach_call_nr = -1; 226 + set_mach_call_nr(-1); 224 227 } 225 228 226 229 int xtrace_kern_return_to_str(char* buf, kern_return_t kr) ··· 264 267 if (rv != KERN_SUCCESS) 265 268 { 266 269 print_kern_return(buf, nr, rv); 267 - argument_ptr = NULL; 270 + set_argument_ptr(NULL); 268 271 return; 269 272 } 270 - if (argument_ptr == NULL) 273 + if (get_argument_ptr() == NULL) 271 274 { 272 275 *buf = 0; 273 276 return; 274 277 } 275 - __simple_sprintf(buf, "port right %d", *(mach_port_name_t*) argument_ptr); 276 - argument_ptr = NULL; 278 + __simple_sprintf(buf, "port right %d", *(mach_port_name_t*)get_argument_ptr()); 279 + set_argument_ptr(NULL); 277 280 } 278 281 279 282 ··· 290 293 { 291 294 mach_port_name_t target = (mach_port_name_t) (long) args[0]; 292 295 mach_port_right_t right = (mach_port_right_t) (long) args[1]; 293 - argument_ptr = args[2]; 296 + set_argument_ptr(args[2]); 294 297 295 298 const char* right_name; 296 299 if (right > MACH_PORT_RIGHT_NUMBER) ··· 342 345 343 346 static void print_mach_timebase_info_args(char* buf, int nr, void* args[]) 344 347 { 345 - argument_ptr = args[0]; 346 - if (argument_ptr == NULL) 348 + set_argument_ptr(args[0]); 349 + if (get_argument_ptr() == NULL) 347 350 __simple_sprintf(buf, "NULL"); 348 351 else 349 352 *buf = 0; ··· 354 357 if (rv != KERN_SUCCESS) 355 358 { 356 359 print_kern_return(buf, nr, rv); 357 - argument_ptr = NULL; 360 + set_argument_ptr(NULL); 358 361 return; 359 362 } 360 - if (argument_ptr != NULL) 363 + if (get_argument_ptr() != NULL) 361 364 { 362 - mach_timebase_info_t timebase = (mach_timebase_info_t) argument_ptr; 365 + mach_timebase_info_t timebase = (mach_timebase_info_t)get_argument_ptr(); 363 366 __simple_sprintf(buf, "numer = %d, denom = %d", timebase->numer, timebase->denom); 364 367 } 365 368 else 366 369 *buf = 0; 367 370 368 - argument_ptr = NULL; 371 + set_argument_ptr(NULL); 369 372 } 370 373 371 374 static void print_task_for_pid_args(char* buf, int nr, void* args[]) 372 375 { 373 376 mach_port_name_t target = (mach_port_name_t) (long) args[0]; 374 377 int pid = (int) (long) args[1]; 375 - argument_ptr = args[2]; 378 + set_argument_ptr(args[2]); 376 379 377 380 __simple_sprintf(buf, "task %d, pid %d", target, pid); 378 381 } ··· 380 383 static void print_pid_for_task_args(char* buf, int nr, void* args[]) 381 384 { 382 385 mach_port_name_t task = (mach_port_name_t) (long) args[0]; 383 - argument_ptr = args[1]; 386 + set_argument_ptr(args[1]); 384 387 385 388 __simple_sprintf(buf, "task %d", task); 386 389 } ··· 390 393 if (rv != KERN_SUCCESS) 391 394 { 392 395 print_kern_return(buf, nr, rv); 393 - argument_ptr = NULL; 396 + set_argument_ptr(NULL); 394 397 return; 395 398 } 396 - if (argument_ptr != NULL) 397 - __simple_sprintf(buf, "pid %d", * (int*) argument_ptr); 399 + if (get_argument_ptr() != NULL) 400 + __simple_sprintf(buf, "pid %d", * (int*)get_argument_ptr()); 398 401 else 399 402 *buf = 0; 400 403 401 - argument_ptr = NULL; 404 + set_argument_ptr(NULL); 402 405 } 403 406 404 407 const char* xtrace_msg_type_to_str(mach_msg_type_name_t type_name, int full) ··· 480 483 __simple_printf(", %lu bytes of inline data\n", size - ((const char*) ptr - (const char*) msg)); 481 484 } 482 485 483 - _Thread_local mach_port_name_t request_port; 484 - 485 486 static void print_mach_msg_entry(void* args[]) 486 487 { 487 488 const mach_msg_header_t* message = (const mach_msg_header_t*) args[0]; ··· 490 491 491 492 if (options & MACH_SEND_MSG) 492 493 { 493 - request_port = message->msgh_remote_port; 494 + set_request_port(message->msgh_remote_port); 494 495 __simple_printf("\n"); 495 496 xtrace_start_line(8); 496 497 print_mach_msg(message, send_size); 497 498 xtrace_start_line(8); 498 - xtrace_print_mig_message(message, request_port); 499 + xtrace_print_mig_message(message, get_request_port()); 499 500 __simple_printf("\n"); 500 501 } 501 502 502 503 if (options & MACH_RCV_MSG) 503 504 { 504 - switch (mach_call_nr) 505 + switch (get_mach_call_nr()) 505 506 { 506 507 case 31: 507 508 // mach_msg_trap 508 - argument_ptr = args[0]; 509 + set_argument_ptr(args[0]); 509 510 break; 510 511 case 32: 511 512 // mach_msg_overwrite_trap 512 - argument_ptr = args[7]; 513 - if (argument_ptr == NULL) 514 - argument_ptr = args[0]; 513 + set_argument_ptr(args[7]); 514 + if (get_argument_ptr() == NULL) 515 + set_argument_ptr(args[0]); 515 516 break; 516 517 default: 517 518 __simple_printf("Unexpected mach_call_nr"); ··· 522 523 523 524 static void print_mach_msg_exit() 524 525 { 525 - if (argument_ptr == NULL) 526 + if (get_argument_ptr() == NULL) 526 527 return; 527 528 528 - const mach_msg_header_t* message = (const mach_msg_header_t*) argument_ptr; 529 + const mach_msg_header_t* message = (const mach_msg_header_t*)get_argument_ptr(); 529 530 xtrace_start_line(8); 530 531 print_mach_msg(message, message->msgh_size); 531 532 xtrace_start_line(8); 532 - xtrace_print_mig_message(message, request_port); 533 + xtrace_print_mig_message(message, get_request_port()); 533 534 __simple_printf("\n"); 534 - argument_ptr = NULL; 535 - request_port = MACH_PORT_NULL; 535 + set_argument_ptr(NULL); 536 + set_request_port(MACH_PORT_NULL); 536 537 } 537 538 538 539 static void print_mach_msg_args(char* buf, int nr, void* args[])
+482
src/xtrace/malloc.c
··· 1 + #define PRIVATE 1 2 + #include <sys/cdefs.h> 3 + 4 + #include <darling/emulation/ext/for-xtrace.h> 5 + #include <stdint.h> 6 + #include <stdbool.h> 7 + #include <string.h> 8 + #include <sys/queue.h> 9 + 10 + #include "malloc.h" 11 + #include "lock.h" 12 + #include "simple.h" 13 + 14 + #ifndef XTRACE_MALLOC_DEBUG 15 + #define XTRACE_MALLOC_DEBUG 0 16 + #endif 17 + 18 + #if XTRACE_MALLOC_DEBUG 19 + #define xtrace_malloc_debug(x, ...) __simple_printf(x "\n", ## __VA_ARGS__) 20 + #undef XTRACE_INLINE 21 + #define XTRACE_INLINE 22 + #else 23 + #define xtrace_malloc_debug(x, ...) 24 + #endif 25 + 26 + // we can't rely on libmalloc because: 27 + // 1. we should be invisible to everyone but libkernel 28 + // 2. libmalloc might need to `thread_switch(2)`, which will recurse back into xtrace, blowing up the stack 29 + // so let's roll our own malloc! (using Linux's mmap) 30 + 31 + // all blocks will be allocated in multiples of this size 32 + #define BLOCK_SIZE_MULTIPLE (4096ULL) 33 + 34 + #define LIST_PREV(elm, link) ({ \ 35 + __typeof(elm) elm_cache = elm; \ 36 + __typeof((elm_cache)->link)* as_link = __container_of(elm_cache->link.le_prev, __typeof((elm_cache)->link), le_next); \ 37 + elm_cache = __container_of(as_link, __typeof(*elm_cache), link); \ 38 + elm_cache; \ 39 + }) 40 + 41 + typedef LIST_HEAD(xtrace_memory_fragment_head, xtrace_memory_fragment) xtrace_memory_fragment_head_t; 42 + typedef LIST_ENTRY(xtrace_memory_fragment) xtrace_memory_fragment_entry_t; 43 + 44 + // 45 + // xtrace_memory_fragment 46 + // 47 + 48 + #define XTRACE_MEMORY_FRAGMENT_FLAG_IN_USE (1ULL << 63) 49 + #define XTRACE_MEMORY_FRAGMENT_FLAG_IS_MMAP_BASE (1ULL << 62) 50 + #define XTRACE_MEMORY_FRAGMENT_FLAG_BIT_COUNT (2) 51 + #define XTRACE_MEMORY_FRAGMENT_SIZE_MASK (0xffffffffffffffffULL >> XTRACE_MEMORY_FRAGMENT_FLAG_BIT_COUNT) 52 + 53 + typedef struct xtrace_memory_fragment* xtrace_memory_fragment_t; 54 + struct xtrace_memory_fragment { 55 + uint64_t flags; 56 + xtrace_memory_fragment_entry_t block_link; 57 + xtrace_memory_fragment_entry_t free_link; 58 + char memory[]; 59 + }; 60 + 61 + XTRACE_INLINE 62 + bool xtrace_memory_fragment_in_use(xtrace_memory_fragment_t fragment) { 63 + return fragment->flags & XTRACE_MEMORY_FRAGMENT_FLAG_IN_USE; 64 + }; 65 + 66 + XTRACE_INLINE 67 + void xtrace_memory_fragment_set_in_use(xtrace_memory_fragment_t fragment, bool in_use) { 68 + if (in_use) { 69 + fragment->flags |= XTRACE_MEMORY_FRAGMENT_FLAG_IN_USE; 70 + } else { 71 + fragment->flags &= ~XTRACE_MEMORY_FRAGMENT_FLAG_IN_USE; 72 + } 73 + }; 74 + 75 + XTRACE_INLINE 76 + bool xtrace_memory_fragment_is_mmap_base(xtrace_memory_fragment_t fragment) { 77 + return fragment->flags & XTRACE_MEMORY_FRAGMENT_FLAG_IS_MMAP_BASE; 78 + }; 79 + 80 + XTRACE_INLINE 81 + void xtrace_memory_fragment_set_is_mmap_base(xtrace_memory_fragment_t fragment, bool is_mmap_base) { 82 + if (is_mmap_base) { 83 + fragment->flags |= XTRACE_MEMORY_FRAGMENT_FLAG_IS_MMAP_BASE; 84 + } else { 85 + fragment->flags &= ~XTRACE_MEMORY_FRAGMENT_FLAG_IS_MMAP_BASE; 86 + } 87 + }; 88 + 89 + XTRACE_INLINE 90 + size_t xtrace_memory_fragment_size(xtrace_memory_fragment_t fragment) { 91 + return fragment->flags & XTRACE_MEMORY_FRAGMENT_SIZE_MASK; 92 + }; 93 + 94 + XTRACE_INLINE 95 + void xtrace_memory_fragment_set_size(xtrace_memory_fragment_t fragment, size_t new_size) { 96 + fragment->flags = (fragment->flags & ~XTRACE_MEMORY_FRAGMENT_SIZE_MASK) | (new_size & XTRACE_MEMORY_FRAGMENT_SIZE_MASK); 97 + }; 98 + 99 + XTRACE_INLINE 100 + void xtrace_memory_fragment_init(xtrace_memory_fragment_t fragment, size_t size) { 101 + xtrace_malloc_debug("initializing fragment of %llu bytes at %p", size, fragment); 102 + fragment->flags = size & XTRACE_MEMORY_FRAGMENT_SIZE_MASK; 103 + fragment->block_link.le_next = NULL; 104 + fragment->block_link.le_prev = NULL; 105 + fragment->free_link.le_next = NULL; 106 + fragment->free_link.le_prev = NULL; 107 + }; 108 + 109 + // 110 + // xtrace_memory_block 111 + // 112 + 113 + typedef struct xtrace_memory_block* xtrace_memory_block_t; 114 + struct xtrace_memory_block { 115 + uint64_t size; 116 + xtrace_memory_fragment_head_t fragment_list; 117 + struct xtrace_memory_fragment fragment; 118 + }; 119 + 120 + XTRACE_INLINE 121 + bool xtrace_memory_block_in_use(xtrace_memory_block_t block) { 122 + // the block is in use if either: 123 + // * the first fragment is in use, or 124 + // * the block has been fragmented 125 + return xtrace_memory_fragment_in_use(&block->fragment) || (block->size != xtrace_memory_fragment_size(&block->fragment)); 126 + }; 127 + 128 + XTRACE_INLINE 129 + void xtrace_memory_block_init(xtrace_memory_block_t block, size_t size) { 130 + block->size = size; 131 + LIST_INIT(&block->fragment_list); 132 + xtrace_memory_fragment_init(&block->fragment, block->size); 133 + xtrace_memory_fragment_set_is_mmap_base(&block->fragment, true); 134 + LIST_INSERT_HEAD(&block->fragment_list, &block->fragment, block_link); 135 + }; 136 + 137 + // 138 + // global variables 139 + // 140 + 141 + // list of available fragments 142 + static xtrace_memory_fragment_head_t free_list = LIST_HEAD_INITIALIZER(free_list); 143 + // lock for free_list 144 + static xtrace_lock_t free_lock = XTRACE_LOCK_INITIALIZER; 145 + 146 + // 147 + // internal functions 148 + // 149 + 150 + static xtrace_memory_fragment_t allocate_fragment(size_t required_size); 151 + static void release_fragment(xtrace_memory_fragment_t fragment); 152 + static bool shrink_fragment(xtrace_memory_fragment_t fragment, size_t new_size); 153 + static bool expand_fragment(xtrace_memory_fragment_t fragment, size_t new_size); 154 + 155 + // borrowed from libgmalloc 156 + XTRACE_INLINE 157 + size_t round_up(size_t size, size_t increment) { 158 + if ((size & (increment - 1)) == 0) { 159 + return size; 160 + } 161 + return (size | (increment - 1)) + 1; 162 + } 163 + 164 + static xtrace_memory_fragment_t allocate_fragment(size_t required_size) { 165 + xtrace_memory_fragment_t viable_fragment = NULL; 166 + size_t viable_fragment_size = 0; 167 + xtrace_memory_fragment_t loop_var = NULL; 168 + xtrace_lock_lock(&free_lock); 169 + 170 + // look for the smallest free fragment 171 + LIST_FOREACH(loop_var, &free_list, free_link) { 172 + size_t loop_var_size = xtrace_memory_fragment_size(loop_var); 173 + 174 + // 1. always make sure the current fragment is large enough 175 + // 2. we'll prefer the current fragment over the previous candidate if either: 176 + // * we don't currently have a candidate fragment, or 177 + // * the current fragment is smaller than the previous candidate 178 + if (loop_var_size > required_size && (viable_fragment == NULL || loop_var_size < viable_fragment_size)) { 179 + viable_fragment = loop_var; 180 + viable_fragment_size = loop_var_size; 181 + xtrace_malloc_debug("found viable fragment with address %p and size %llu", viable_fragment->memory, viable_fragment_size); 182 + } 183 + } 184 + 185 + // if we didn't find a viable candidate... 186 + if (viable_fragment == NULL) { 187 + // ...allocate some memory 188 + size_t allocation_size = round_up(required_size, BLOCK_SIZE_MULTIPLE); 189 + xtrace_memory_block_t block = _mmap_for_xtrace(NULL, allocation_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 190 + 191 + xtrace_malloc_debug("mmap for %llu bytes returned %p", allocation_size, block); 192 + 193 + // check if return value is an error code 194 + // see similar check in libkernel's `mman.c` 195 + if ((unsigned long)block > (unsigned long)-4096) { 196 + // we failed to allocate the memory, we can't do anything else 197 + xtrace_malloc_debug("mmap result was failure"); 198 + goto out; 199 + } 200 + 201 + // initialize it 202 + xtrace_memory_block_init(block, allocation_size - sizeof(struct xtrace_memory_block)); 203 + 204 + // add it to the free list 205 + LIST_INSERT_HEAD(&free_list, &block->fragment, free_link); 206 + 207 + // and set `viable_fragment` so our code below can handle it the same way as the no-mmap case 208 + viable_fragment = &block->fragment; 209 + } 210 + 211 + // at this point, `viable_fragment` is guaranteed to have a value 212 + xtrace_malloc_debug("final fragment for allocation: %llu bytes at %p", xtrace_memory_fragment_size(viable_fragment), viable_fragment->memory); 213 + 214 + // shrink the fragment (if possible) 215 + shrink_fragment(viable_fragment, required_size); 216 + 217 + // set the in_use bit 218 + xtrace_memory_fragment_set_in_use(viable_fragment, true); 219 + 220 + // finally, remove the viable fragment from the free list 221 + LIST_REMOVE(viable_fragment, free_link); 222 + 223 + out: 224 + xtrace_lock_unlock(&free_lock); 225 + 226 + return viable_fragment; 227 + }; 228 + 229 + static void release_fragment(xtrace_memory_fragment_t fragment) { 230 + xtrace_lock_lock(&free_lock); 231 + 232 + xtrace_malloc_debug("releasing fragment of %llu bytes at %p", xtrace_memory_fragment_size(fragment), fragment->memory); 233 + 234 + // clear the in_use bit 235 + xtrace_memory_fragment_set_in_use(fragment, false); 236 + 237 + // add it to the free list 238 + LIST_INSERT_HEAD(&free_list, fragment, free_link); 239 + 240 + // try to defragment the block 241 + while (true) { 242 + xtrace_memory_fragment_t prev_frag = xtrace_memory_fragment_is_mmap_base(fragment) ? NULL : LIST_PREV(fragment, block_link); 243 + xtrace_memory_fragment_t next_frag = LIST_NEXT(fragment, block_link); 244 + bool defragged = false; 245 + 246 + if (prev_frag && !xtrace_memory_fragment_in_use(prev_frag)) { 247 + xtrace_malloc_debug("defragmenting with previous fragment (%llu bytes at %p)", xtrace_memory_fragment_size(prev_frag), prev_frag->memory); 248 + defragged = true; 249 + 250 + // remove the current fragment from the free list 251 + LIST_REMOVE(fragment, free_link); 252 + 253 + // remove the current fragment from the block list 254 + LIST_REMOVE(fragment, block_link); 255 + 256 + // expand the previous fragment 257 + xtrace_memory_fragment_set_size(prev_frag, xtrace_memory_fragment_size(prev_frag) + sizeof(struct xtrace_memory_fragment) + xtrace_memory_fragment_size(fragment)); 258 + 259 + // `fragment` is now gone; set it to `prev_frag` 260 + fragment = prev_frag; 261 + } 262 + 263 + if (next_frag && !xtrace_memory_fragment_in_use(next_frag)) { 264 + xtrace_malloc_debug("defragmenting with next fragment (%llu bytes at %p)", xtrace_memory_fragment_size(fragment), fragment->memory); 265 + defragged = true; 266 + 267 + // remove the next fragment from the free list 268 + LIST_REMOVE(next_frag, free_link); 269 + 270 + // remove the next fragment from the block list 271 + LIST_REMOVE(next_frag, block_link); 272 + 273 + // expand the current fragment 274 + xtrace_memory_fragment_set_size(fragment, xtrace_memory_fragment_size(fragment) + sizeof(struct xtrace_memory_fragment) + xtrace_memory_fragment_size(next_frag)); 275 + 276 + // `next_frag` is now gone 277 + } 278 + 279 + // if we didn't defragment anything on this loop, we can't defragment any more 280 + if (!defragged) { 281 + xtrace_malloc_debug("cannot defragment any further"); 282 + break; 283 + } else { 284 + // otherwise, keep looping to try to defragment more 285 + xtrace_malloc_debug("doing another defragmentation iteration"); 286 + } 287 + } 288 + 289 + xtrace_malloc_debug("final releasing fragment is %llu bytes at %p", xtrace_memory_fragment_size(fragment), fragment->memory); 290 + 291 + // if `fragment` is the mmap base fragment... 292 + if (xtrace_memory_fragment_is_mmap_base(fragment)) { 293 + xtrace_memory_block_t block = __container_of(fragment, struct xtrace_memory_block, fragment); 294 + xtrace_malloc_debug("releasing fragment is base fragment"); 295 + 296 + // ...and we managed to defragment the entire block... 297 + if (!xtrace_memory_block_in_use(block)) { 298 + // ...unmap it! 299 + xtrace_malloc_debug("unmapping completely freed block (%llu bytes at %p)", block->size, block->fragment.memory); 300 + 301 + // start off by removing all traces of this block in the free list. 302 + // at this point, only the base fragment should be in the free list, so remove it 303 + LIST_REMOVE(fragment, free_link); 304 + 305 + // and now, just unmap it 306 + _munmap_for_xtrace(block, block->size + sizeof(struct xtrace_memory_block)); 307 + // and we ignore errors 308 + } 309 + } 310 + 311 + out: 312 + xtrace_lock_unlock(&free_lock); 313 + }; 314 + 315 + // must be called with free_lock held 316 + static bool shrink_fragment(xtrace_memory_fragment_t fragment, size_t new_size) { 317 + xtrace_malloc_debug("shrink to %llu bytes requested for fragment of %llu bytes at %p", new_size, xtrace_memory_fragment_size(fragment), fragment->memory); 318 + 319 + // calculate the remaining size if we were to shrink this fragment 320 + size_t remaining_size = xtrace_memory_fragment_size(fragment) - new_size; 321 + 322 + // if we would have enough space to create another fragment after shrinking this one... 323 + if (remaining_size > sizeof(struct xtrace_memory_fragment)) { 324 + xtrace_malloc_debug("fragment eligible; shrinking..."); 325 + 326 + // ...shrink it... 327 + xtrace_memory_fragment_set_size(fragment, new_size); 328 + 329 + // ...and create the new fragment 330 + xtrace_memory_fragment_t new_fragment = (xtrace_memory_fragment_t)(fragment->memory + new_size); 331 + xtrace_memory_fragment_init(new_fragment, remaining_size - sizeof(struct xtrace_memory_fragment)); 332 + 333 + // add it to the block list 334 + LIST_INSERT_AFTER(fragment, new_fragment, block_link); 335 + 336 + xtrace_malloc_debug("old fragment shrunk to %llu bytes at %p", xtrace_memory_fragment_size(fragment), fragment->memory); 337 + xtrace_malloc_debug("new fragment of %llu bytes created at %p", xtrace_memory_fragment_size(new_fragment), new_fragment->memory); 338 + 339 + // insert it into the free list 340 + LIST_INSERT_HEAD(&free_list, new_fragment, free_link); 341 + 342 + return true; 343 + } else { 344 + // otherwise, we don't want to shrink this one to avoid holes in our block 345 + xtrace_malloc_debug("fragment ineligible for shrinking (not enough space after it)"); 346 + } 347 + 348 + return false; 349 + }; 350 + 351 + // must be called with free_lock held 352 + static bool expand_fragment(xtrace_memory_fragment_t fragment, size_t new_size) { 353 + xtrace_malloc_debug("expansion to %llu bytes requested for fragment of %llu bytes at %p", new_size, xtrace_memory_fragment_size(fragment), fragment->memory); 354 + 355 + size_t available_size = xtrace_memory_fragment_size(fragment); 356 + xtrace_memory_fragment_t loop_var = fragment; 357 + xtrace_memory_fragment_t end_frag = NULL; 358 + 359 + while ((end_frag = LIST_NEXT(loop_var, block_link)) != NULL) { 360 + // if the fragment is in use... 361 + if (xtrace_memory_fragment_in_use(end_frag)) { 362 + // ...we've reached a limit 363 + xtrace_malloc_debug("insufficient free fragments for expansion"); 364 + break; 365 + } 366 + 367 + available_size += xtrace_memory_fragment_size(end_frag) + sizeof(struct xtrace_memory_fragment); 368 + loop_var = end_frag; 369 + 370 + // if we've got enough... 371 + if (available_size >= new_size) { 372 + xtrace_malloc_debug("found enough free fragments for expansion"); 373 + // ...we're done; we don't need to keep looking for more 374 + break; 375 + } 376 + } 377 + 378 + if (end_frag && available_size >= new_size) { 379 + xtrace_memory_fragment_t terminating_value = LIST_NEXT(end_frag, block_link); 380 + 381 + while (LIST_NEXT(fragment, block_link) != terminating_value) { 382 + xtrace_memory_fragment_t dying_fragment = LIST_NEXT(fragment, block_link); 383 + 384 + xtrace_malloc_debug("absorbing fragment of %llu bytes at %p", xtrace_memory_fragment_size(dying_fragment), dying_fragment->memory); 385 + 386 + // remove the dying fragment from the free list 387 + LIST_REMOVE(dying_fragment, free_link); 388 + 389 + // remove the dying fragment from the block list 390 + LIST_REMOVE(dying_fragment, block_link); 391 + 392 + // expand the surviving fragment 393 + xtrace_memory_fragment_set_size(fragment, xtrace_memory_fragment_size(fragment) + sizeof(struct xtrace_memory_fragment) + xtrace_memory_fragment_size(dying_fragment)); 394 + 395 + // `dying_fragment` is now gone 396 + } 397 + 398 + xtrace_malloc_debug("expanded fragment is %llu bytes at %p; trying to shrink...", xtrace_memory_fragment_size(fragment), fragment->memory); 399 + 400 + // try to shrink it so that we leave space for other possible fragments 401 + shrink_fragment(fragment, new_size); 402 + 403 + xtrace_malloc_debug("final fragment is %llu bytes at %p", xtrace_memory_fragment_size(fragment), fragment->memory); 404 + 405 + return true; 406 + } 407 + 408 + xtrace_malloc_debug("ineligible for expansion"); 409 + 410 + return false; 411 + }; 412 + 413 + // 414 + // api functions 415 + // 416 + 417 + void* xtrace_malloc(size_t size) { 418 + if (size == 0) { 419 + return NULL; 420 + } 421 + xtrace_memory_fragment_t fragment = allocate_fragment(size); 422 + if (fragment == NULL) { 423 + return NULL; 424 + } 425 + return fragment->memory; 426 + }; 427 + 428 + void xtrace_free(void* pointer) { 429 + if (pointer == NULL) { 430 + return; 431 + } 432 + xtrace_memory_fragment_t fragment = __container_of(pointer, struct xtrace_memory_fragment, memory); 433 + release_fragment(fragment); 434 + }; 435 + 436 + void* xtrace_realloc(void* old_pointer, size_t new_size) { 437 + xtrace_memory_fragment_t fragment = __container_of(old_pointer, struct xtrace_memory_fragment, memory); 438 + size_t old_size = xtrace_memory_fragment_size(fragment); 439 + 440 + xtrace_lock_lock(&free_lock); 441 + 442 + // if we're shrinking, we can always do that 443 + if (new_size < old_size) { 444 + // we don't really care if it succeeds or not 445 + shrink_fragment(fragment, new_size); 446 + goto out; 447 + } else if (new_size == old_size) { 448 + // likewise, we can always keep it the same 449 + goto out; 450 + } 451 + 452 + // but otherwise, we're expanding 453 + 454 + // try to see if we can expand the fragment first 455 + if (expand_fragment(fragment, new_size)) { 456 + // if we expanded it successfully, we're done! 457 + goto out; 458 + } else { 459 + // otherwise, we need to allocate a new fragment 460 + xtrace_lock_unlock(&free_lock); // `allocate_fragment` takes the lock, so we need to drop it 461 + xtrace_memory_fragment_t new_fragment = allocate_fragment(new_size); 462 + if (new_fragment == NULL) { 463 + return NULL; 464 + } 465 + 466 + // copy the old contents to the new fragment 467 + memcpy(new_fragment->memory, fragment->memory, old_size); 468 + 469 + // and release the old fragment 470 + release_fragment(fragment); 471 + 472 + // assign the new pointer to the return variable and we're done! 473 + old_pointer = new_fragment->memory; 474 + goto out_unlocked; 475 + } 476 + 477 + out: 478 + xtrace_lock_unlock(&free_lock); 479 + 480 + out_unlocked: 481 + return old_pointer; 482 + };
+15
src/xtrace/malloc.h
··· 1 + #ifndef _XTRACE_MALLOC_H_ 2 + #define _XTRACE_MALLOC_H_ 3 + 4 + #include <stddef.h> 5 + #include "base.h" 6 + 7 + XTRACE_DECLARATIONS_BEGIN; 8 + 9 + void* xtrace_malloc(size_t size); 10 + void xtrace_free(void* pointer); 11 + void* xtrace_realloc(void* old_pointer, size_t new_size); 12 + 13 + XTRACE_DECLARATIONS_END; 14 + 15 + #endif // _XTRACE_MALLOC_H_
+6 -4
src/xtrace/mig_trace.c
··· 3 3 #include <sys/types.h> 4 4 #include <dirent.h> 5 5 #include <dlfcn.h> 6 + #include <stdbool.h> 6 7 7 8 #include <mach/mach.h> 8 9 ··· 11 12 #include "mach_trace.h" 12 13 #include "bsd_trace.h" 13 14 #include "xtrace/xtrace-mig-types.h" 15 + #include "tls.h" 14 16 15 17 #define XTRACE_MIG_DIR_PATH "/usr/lib/darling/xtrace-mig" 16 18 ··· 74 76 closedir(xtrace_mig_dir); 75 77 } 76 78 77 - _Thread_local int is_first_arg; 79 + DEFINE_XTRACE_TLS_VAR(bool, is_first_arg); 78 80 79 - #define BEFORE if (!is_first_arg) __simple_printf(", ") 80 - #define AFTER is_first_arg = 0 81 + #define BEFORE if (!get_is_first_arg()) __simple_printf(", ") 82 + #define AFTER set_is_first_arg(false) 81 83 82 84 static void add_raw_arg(const char* format, ...) 83 85 { ··· 349 351 xtrace_reset_color(); 350 352 } 351 353 352 - is_first_arg = 1; 354 + set_is_first_arg(true); 353 355 r->routine(message, is_reply, &callbacks); 354 356 355 357 if (!is_reply)
+86
src/xtrace/tls.c
··· 1 + #include <pthread/pthread.h> 2 + #include <stdlib.h> 3 + #include <darling/emulation/ext/for-xtrace.h> 4 + #include "tls.h" 5 + #include "malloc.h" 6 + #include "lock.h" 7 + #include "simple.h" 8 + 9 + #ifndef XTRACE_TLS_DEBUG 10 + #define XTRACE_TLS_DEBUG 0 11 + #endif 12 + 13 + #if XTRACE_TLS_DEBUG 14 + #define xtrace_tls_debug(x, ...) __simple_printf(x "\n", ## __VA_ARGS__) 15 + #else 16 + #define xtrace_tls_debug(x, ...) 17 + #endif 18 + 19 + // 100 TLS vars should be enough, right? 20 + #define TLS_TABLE_MAX_SIZE 100 21 + 22 + typedef struct tls_table* tls_table_t; 23 + struct tls_table { 24 + size_t size; 25 + void* table[TLS_TABLE_MAX_SIZE][2]; 26 + }; 27 + 28 + static xtrace_once_t tls_key_initialized = XTRACE_ONCE_INITIALIZER; 29 + static pthread_key_t tls_key; 30 + 31 + static void tls_table_destructor(void* _table) { 32 + tls_table_t table = _table; 33 + xtrace_tls_debug("destroying table %p", table); 34 + for (size_t i = 0; i < table->size; ++i) { 35 + xtrace_tls_debug("freeing value %p for key %p", table->table[i][1], table->table[i][0]); 36 + xtrace_free(table->table[i][1]); 37 + } 38 + xtrace_tls_debug("freeing table %p", table); 39 + xtrace_free(table); 40 + }; 41 + 42 + static void tls_key_initializer(void) { 43 + xtrace_tls_debug("initializing tls key"); 44 + if (pthread_key_create(&tls_key, tls_table_destructor) != 0) { 45 + _abort_with_payload_for_xtrace(0, 0, NULL, 0, "xtrace: failed pthread key creation", 0); 46 + } 47 + }; 48 + 49 + void* xtrace_tls(void* key, size_t size) { 50 + xtrace_tls_debug("looking up tls variable for key %p", key); 51 + 52 + xtrace_once(&tls_key_initialized, tls_key_initializer); 53 + tls_table_t table = pthread_getspecific(tls_key); 54 + 55 + xtrace_tls_debug("got %p as table pointer from pthread", table); 56 + 57 + // if the table doesn't exist yet, create it 58 + if (table == NULL) { 59 + xtrace_tls_debug("table is NULL, creating now..."); 60 + table = xtrace_malloc(sizeof(struct tls_table)); 61 + if (table == NULL) { 62 + _abort_with_payload_for_xtrace(0, 0, NULL, 0, "xtrace: failed TLS table memory allocation", 0); 63 + } 64 + table->size = 0; 65 + pthread_setspecific(tls_key, table); 66 + } 67 + 68 + // check if the key is already present 69 + for (size_t i = 0; i < table->size; ++i) { 70 + if (table->table[i][0] == key) { 71 + xtrace_tls_debug("found entry in table for key %p with value %p", key, table->table[i][1]); 72 + return table->table[i][1]; 73 + } 74 + } 75 + 76 + // otherwise, create it 77 + xtrace_tls_debug("creating new entry in table for key %p", key); 78 + size_t index = table->size++; 79 + table->table[index][0] = key; 80 + table->table[index][1] = xtrace_malloc(size); 81 + if (table->table[index][1] == NULL) { 82 + _abort_with_payload_for_xtrace(0, 0, NULL, 0, "xtrace: failed TLS variable memory allocation", 0); 83 + } 84 + xtrace_tls_debug("new table entry created for key %p with value %p", key, table->table[index][1]); 85 + return table->table[index][1]; 86 + };
+28
src/xtrace/tls.h
··· 1 + #ifndef _XTRACE_TLS_H_ 2 + #define _XTRACE_TLS_H_ 3 + 4 + #include <mach/port.h> 5 + #include "base.h" 6 + 7 + XTRACE_DECLARATIONS_BEGIN; 8 + 9 + #define DEFINE_XTRACE_TLS_VAR(type, name) \ 10 + char name ## _key = '\0'; \ 11 + XTRACE_INLINE \ 12 + type get_ ## name(void) { \ 13 + return *(type*)xtrace_tls(&(name ## _key), sizeof(type)); \ 14 + }; \ 15 + XTRACE_INLINE \ 16 + void set_ ## name(type value) { \ 17 + *(type*)xtrace_tls(&(name ## _key), sizeof(type)) = value; \ 18 + }; \ 19 + XTRACE_INLINE \ 20 + type* get_ptr_ ## name(void) { \ 21 + return (type*)xtrace_tls(&(name ## _key), sizeof(type)); \ 22 + }; 23 + 24 + void* xtrace_tls(void* key, size_t size); 25 + 26 + XTRACE_DECLARATIONS_END; 27 + 28 + #endif // _XTRACE_TLS_H_
+13 -10
src/xtrace/xtracelib.c
··· 7 7 #include "simple.h" 8 8 #include "xtracelib.h" 9 9 #include "mig_trace.h" 10 + #include "tls.h" 10 11 11 12 // Defined in assembly 12 13 extern void darling_mach_syscall_entry_trampoline(void); ··· 152 153 } 153 154 154 155 155 - _Thread_local struct { 156 + struct nested_call_struct { 156 157 // We're inside this many calls. In other words, we have printed this many 157 158 // call entries without matching exits. 158 159 int current_level; ··· 161 162 int previous_level; 162 163 // Call numbers, indexed by current level. 163 164 int nrs[64]; 164 - } nested_call; 165 + }; 166 + 167 + DEFINE_XTRACE_TLS_VAR(struct nested_call_struct, nested_call); 165 168 166 169 void handle_generic_entry(const struct calldef* defs, const char* type, int nr, void* args[]) 167 170 { 168 171 if (xtrace_ignore) 169 172 return; 170 173 171 - if (nested_call.previous_level < nested_call.current_level && !xtrace_split_entry_and_exit) 174 + if (get_ptr_nested_call()->previous_level < get_ptr_nested_call()->current_level && !xtrace_split_entry_and_exit) 172 175 { 173 176 // We are after an earlier entry without an exit. 174 177 __simple_printf("\n"); 175 178 } 176 179 177 - int indent = 4 * nested_call.current_level; 178 - nested_call.nrs[nested_call.current_level] = nr; 180 + int indent = 4 * get_ptr_nested_call()->current_level; 181 + get_ptr_nested_call()->nrs[get_ptr_nested_call()->current_level] = nr; 179 182 180 183 print_call(defs, type, nr, indent, 0); 181 184 ··· 194 197 if (xtrace_split_entry_and_exit) 195 198 __simple_printf("\n"); 196 199 197 - nested_call.previous_level = nested_call.current_level++; 200 + get_ptr_nested_call()->previous_level = get_ptr_nested_call()->current_level++; 198 201 } 199 202 200 203 ··· 203 206 if (xtrace_ignore) 204 207 return; 205 208 206 - if (nested_call.previous_level > nested_call.current_level) 209 + if (get_ptr_nested_call()->previous_level > get_ptr_nested_call()->current_level) 207 210 { 208 211 // We are after an exit, so our call has been split up. 209 212 force_split = 1; 210 213 } 211 - nested_call.previous_level = nested_call.current_level--; 212 - int nr = nested_call.nrs[nested_call.current_level]; 214 + get_ptr_nested_call()->previous_level = get_ptr_nested_call()->current_level--; 215 + int nr = get_ptr_nested_call()->nrs[get_ptr_nested_call()->current_level]; 213 216 214 217 if (xtrace_split_entry_and_exit || force_split) 215 218 { 216 - int indent = 4 * nested_call.current_level; 219 + int indent = 4 * get_ptr_nested_call()->current_level; 217 220 print_call(defs, type, nr, indent, 1); 218 221 __simple_printf("()"); 219 222 }