X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/34e8f8296870d0e8695f90e1a54240a589d41312..a28bf75d63c6a64e4c3b417c6052e45f42c6cedd:/gen/stack_logging_disk.c diff --git a/gen/stack_logging_disk.c b/gen/stack_logging_disk.c index 83c882f..00a0cae 100644 --- a/gen/stack_logging_disk.c +++ b/gen/stack_logging_disk.c @@ -170,6 +170,7 @@ static OSSpinLock stack_logging_lock = OS_SPINLOCK_INIT; extern void __stack_logging_fork_prepare(); extern void __stack_logging_fork_parent(); extern void __stack_logging_fork_child(); +extern void __stack_logging_early_finished(); // support for gdb and others checking for stack_logging locks __private_extern__ boolean_t __stack_logging_locked(); @@ -187,9 +188,10 @@ static const char *stack_log_file_base_name = "stack-logs."; static const char *stack_log_file_suffix = ".index"; static const char *stack_log_link_suffix = ".link"; -static char stack_log_location[PATH_MAX]; -static char stack_log_reference_file[PATH_MAX]; -static char index_file_path[PATH_MAX]; +static void *stack_log_path_buffers = NULL; +static char *stack_log_location = NULL; +static char *stack_log_reference_file = NULL; +char *__stack_log_file_path__ = NULL; static int index_file_descriptor = -1; // for accessing remote log files @@ -248,6 +250,13 @@ __create_uniquing_table(void) return uniquing_table; } +static void +__destroy_uniquing_table(backtrace_uniquing_table* table) +{ + deallocate_pages(table->table, table->tableSize); + deallocate_pages(table, sizeof(backtrace_uniquing_table)); +} + static void __expand_uniquing_table(backtrace_uniquing_table *uniquing_table) { @@ -285,10 +294,13 @@ __expand_uniquing_table(backtrace_uniquing_table *uniquing_table) static int __enter_frames_in_table(backtrace_uniquing_table *uniquing_table, uint64_t *foundIndex, mach_vm_address_t *frames, int32_t count) { + // The hash values need to be the same size as the addresses (because we use the value -1), for clarity, define a new type + typedef mach_vm_address_t hash_index_t; + mach_vm_address_t thisPC; - uint64_t hash, uParent = (uint64_t)(-1ll), modulus = (uniquing_table->numNodes-uniquing_table->untouchableNodes-1); + hash_index_t hash, uParent = (hash_index_t)(-1ll), modulus = (uniquing_table->numNodes-uniquing_table->untouchableNodes-1); int32_t collisions, lcopy = count, returnVal = 1; - uint64_t hash_multiplier = ((uniquing_table->numNodes - uniquing_table->untouchableNodes)/(uniquing_table->max_collide*2+1)); + hash_index_t hash_multiplier = ((uniquing_table->numNodes - uniquing_table->untouchableNodes)/(uniquing_table->max_collide*2+1)); mach_vm_address_t *node; while (--lcopy >= 0) { thisPC = frames[lcopy]; @@ -384,7 +396,63 @@ append_int(char * filename, pid_t pid, size_t maxLength) } } -// If successful, returns path to log file that was created. Otherwise returns NULL. +/* + * if we needed to call confstr during init then setting this + * flag will postpone stack logging until after Libsystem's initialiser has run. + */ +static void +postpone_stack_logging(void) +{ + _malloc_printf(ASL_LEVEL_INFO, "stack logging postponed until after initialization.\n"); + stack_logging_postponed = 1; +} + +/* + * Check various temporary directory options starting with _PATH_TMP and use confstr. + * Allocating and releasing target buffer is the caller's responsibility. + */ +static bool +get_writeable_temp_dir(char* target) +{ + if (!target) return false; + if (-1 != access(_PATH_TMP, W_OK)) { + strlcpy(target, _PATH_TMP, (size_t)PATH_MAX); + return true; + } + if (getenv("TMPDIR") && (-1 != access(getenv("TMPDIR"), W_OK))) { + strlcpy(target, getenv("TMPDIR"), (size_t)PATH_MAX); + return true; + } + if (stack_logging_finished_init) { + size_t n = confstr(_CS_DARWIN_USER_TEMP_DIR, target, (size_t) PATH_MAX); + if ((n > 0) && (n < PATH_MAX)) return true; + n = confstr(_CS_DARWIN_USER_CACHE_DIR, target, (size_t) PATH_MAX); + if ((n > 0) && (n < PATH_MAX)) return true; + } else { + /* Can't call confstr during init, so postpone + logging till after */ + postpone_stack_logging(); + } + /* No writeable tmp directory found. Maybe shd try /private/var/tmp for device here ... */ + *target = '\0'; + return false; +} + +/* + * If successful, returns path to log file that was created, otherwise NULL. + * + * The log could be in one of 3 places (in decreasing order of preference) + * + * 1) value of environment variable MallocStackLoggingDirectory + * 2) the temp directory /tmp for desktop apps and internal apps on devices, or + * 3) the sandbox location + tmp/ in case of 3rd party apps on the device. + * + * For 1 and 3, we create a .link file with the path of the file. We prefer to + * create this file in /tmp, but if we are unable to (device 3rd party case), + * we create it in the same location as the .index file and issue a message + * in syslog asking for it to be copied to /tmp to enable tools. + * + */ static char * create_log_file(void) { @@ -392,69 +460,112 @@ create_log_file(void) const char *progname = getprogname(); char *created_log_location = NULL; + if (stack_log_path_buffers == NULL) { + /* + * on first use, allocate buffers directly from the OS without + * using malloc + */ + + stack_log_path_buffers = allocate_pages((uint64_t)round_page(3*PATH_MAX)); + if (stack_log_path_buffers == NULL) { + _malloc_printf(ASL_LEVEL_INFO, "unable to allocate memory for path buffers\n"); + return NULL; + } + + stack_log_location = &((char *)stack_log_path_buffers)[0*PATH_MAX]; + stack_log_reference_file = &((char *)stack_log_path_buffers)[1*PATH_MAX]; + __stack_log_file_path__ = &((char *)stack_log_path_buffers)[2*PATH_MAX]; + } + // WARNING! use of snprintf can induce malloc() calls bool use_alternate_location = false; char *evn_log_directory = getenv("MallocStackLoggingDirectory"); + size_t stack_log_len; if (evn_log_directory && *evn_log_directory) { use_alternate_location = true; strlcpy(stack_log_location, evn_log_directory, (size_t)PATH_MAX); - size_t evn_log_len = strlen(stack_log_location); - // add the '/' only if it's not already there. - if (evn_log_directory[evn_log_len-1] != '/') { - strlcat(stack_log_location, "/", (size_t)PATH_MAX); + } + if (!use_alternate_location || (access(stack_log_location, W_OK) == -1)) { + if (!get_writeable_temp_dir(stack_log_location)) { + if (!stack_logging_postponed) { + _malloc_printf(ASL_LEVEL_INFO, "No writeable tmp dir\n"); + } + return NULL; } - } else { - strlcpy(stack_log_location, _PATH_TMP, (size_t)PATH_MAX); + if (0 != strcmp(stack_log_location, _PATH_TMP)) + use_alternate_location = true; + } + stack_log_len = strlen(stack_log_location); + // add the '/' only if it's not already there. + if (stack_log_location[stack_log_len-1] != '/') { + strlcat(stack_log_location, "/", (size_t)PATH_MAX); + ++stack_log_len; } - strlcat(stack_log_location, stack_log_file_base_name, (size_t)PATH_MAX); - append_int(stack_log_location, pid, (size_t)PATH_MAX); + strlcpy(__stack_log_file_path__, stack_log_location, (size_t)PATH_MAX); + + strlcat(__stack_log_file_path__, stack_log_file_base_name, (size_t)PATH_MAX); + append_int(__stack_log_file_path__, pid, (size_t)PATH_MAX); if (progname && progname[0] != '\0') { - strlcat(stack_log_location, ".", (size_t)PATH_MAX); - strlcat(stack_log_location, progname, (size_t)PATH_MAX); + strlcat(__stack_log_file_path__, ".", (size_t)PATH_MAX); + strlcat(__stack_log_file_path__, progname, (size_t)PATH_MAX); + } + if (!use_alternate_location) strlcat(__stack_log_file_path__, ".XXXXXX", (size_t)PATH_MAX); + strlcat(__stack_log_file_path__, stack_log_file_suffix, (size_t)PATH_MAX); + + // Securely create the log file. + if ((index_file_descriptor = mkstemps(__stack_log_file_path__, (int)strlen(stack_log_file_suffix))) != -1) { + _malloc_printf(ASL_LEVEL_INFO, "stack logs being written into %s\n", __stack_log_file_path__); + created_log_location = __stack_log_file_path__; + } else { + _malloc_printf(ASL_LEVEL_INFO, "unable to create stack logs at %s\n", stack_log_location); + if (use_alternate_location) delete_logging_file(stack_log_reference_file); + stack_log_reference_file[0] = '\0'; + stack_log_location[0] = '\0'; + __stack_log_file_path__[0] = '\0'; + created_log_location = NULL; + return created_log_location; } - if (!use_alternate_location) strlcat(stack_log_location, ".XXXXXX", (size_t)PATH_MAX); - strlcat(stack_log_location, stack_log_file_suffix, (size_t)PATH_MAX); // in the case where the user has specified an alternate location, drop a reference file // in /tmp with the suffix 'stack_log_link_suffix' (".link") and save the path of the // stack logging file there. + bool use_alternate_link_location = false; if (use_alternate_location) { strlcpy(stack_log_reference_file, _PATH_TMP, (size_t)PATH_MAX); + if (access(stack_log_reference_file, W_OK) == -1) { + strlcpy(stack_log_reference_file, stack_log_location, (size_t)PATH_MAX); + use_alternate_link_location = true; + } strlcat(stack_log_reference_file, stack_log_file_base_name, (size_t)PATH_MAX); append_int(stack_log_reference_file, pid, (size_t)PATH_MAX); if (progname && progname[0] != '\0') { strlcat(stack_log_reference_file, ".", (size_t)PATH_MAX); strlcat(stack_log_reference_file, progname, (size_t)PATH_MAX); } + if (!use_alternate_link_location) + strlcat(stack_log_reference_file, ".XXXXXX", (size_t)PATH_MAX); strlcat(stack_log_reference_file, ".XXXXXX", (size_t)PATH_MAX); strlcat(stack_log_reference_file, stack_log_link_suffix, (size_t)PATH_MAX); int link_file_descriptor = mkstemps(stack_log_reference_file, (int)strlen(stack_log_link_suffix)); if (link_file_descriptor == -1) { - _malloc_printf(ASL_LEVEL_INFO, "unable to create stack reference file at %s\n", stack_log_location); - return NULL; + _malloc_printf(ASL_LEVEL_INFO, "unable to create stack reference file %s at %s\n", + stack_log_reference_file, stack_log_location); + } else { + ssize_t written = write(link_file_descriptor, __stack_log_file_path__, strlen(__stack_log_file_path__)); + if (written < (ssize_t)strlen(__stack_log_file_path__)) { + _malloc_printf(ASL_LEVEL_INFO, "unable to write to stack reference file %s at %s\n", + stack_log_reference_file, stack_log_location); + } else { + const char *description_string = "\n(This is a reference file to the stack logs at the path above.)\n"; + write(link_file_descriptor, description_string, strlen(description_string)); } - ssize_t written = write(link_file_descriptor, stack_log_location, strlen(stack_log_location)); - if (written < (ssize_t)strlen(stack_log_location)) { - _malloc_printf(ASL_LEVEL_INFO, "unable to write to stack reference file at %s\n", stack_log_location); - return NULL; } - const char *description_string = "\n(This is a reference file to the stack logs at the path above.)\n"; - write(link_file_descriptor, description_string, strlen(description_string)); close(link_file_descriptor); } - - // Securely create the log file. - if ((index_file_descriptor = mkstemps(stack_log_location, (int)strlen(stack_log_file_suffix))) != -1) { - _malloc_printf(ASL_LEVEL_INFO, "stack logs being written into %s\n", stack_log_location); - created_log_location = stack_log_location; - } else { - _malloc_printf(ASL_LEVEL_INFO, "unable to create stack logs at %s\n", stack_log_location); - if (use_alternate_location) delete_logging_file(stack_log_reference_file); - stack_log_reference_file[0] = '\0'; - stack_log_location[0] = '\0'; - created_log_location = NULL; + if (use_alternate_link_location) { + _malloc_printf(ASL_LEVEL_INFO, "Please issue: cp %s %s\n", stack_log_reference_file, _PATH_TMP); } return created_log_location; } @@ -517,12 +628,12 @@ delete_logging_file(char *log_location) static void delete_log_files(void) { - if (stack_log_location && stack_log_location[0]) { - if (delete_logging_file(stack_log_location) == 0) { - _malloc_printf(ASL_LEVEL_INFO, "stack logs deleted from %s\n", stack_log_location); - index_file_path[0] = '\0'; + if (__stack_log_file_path__ && __stack_log_file_path__[0]) { + if (delete_logging_file(__stack_log_file_path__) == 0) { + _malloc_printf(ASL_LEVEL_INFO, "stack logs deleted from %s\n", __stack_log_file_path__); + __stack_log_file_path__[0] = '\0'; } else { - _malloc_printf(ASL_LEVEL_INFO, "unable to delete stack logs from %s\n", stack_log_location); + _malloc_printf(ASL_LEVEL_INFO, "unable to delete stack logs from %s\n", __stack_log_file_path__); } } if (stack_log_reference_file && stack_log_reference_file[0]) { @@ -629,7 +740,7 @@ robust_write(int fd, const void *buf, size_t nbyte) { // descriptor was closed on us. We need to reopen it if (fd == index_file_descriptor) { - file_to_reopen = index_file_path; + file_to_reopen = __stack_log_file_path__; fd_to_reset = &index_file_descriptor; } else { // We don't know about this file. Return (and abort()). @@ -684,7 +795,8 @@ flush_data(void) while (remaining > 0) { written = robust_write(index_file_descriptor, p, remaining); if (written == -1) { - _malloc_printf(ASL_LEVEL_INFO, "Unable to write to stack logging file %s (%s)\n", index_file_path, strerror(errno)); + _malloc_printf(ASL_LEVEL_INFO, "Unable to write to stack logging file %s (%s)\n", + __stack_log_file_path__, strerror(errno)); disable_stack_logging(); return; } @@ -723,12 +835,12 @@ prepare_to_log_stacks(void) pre_write_buffers = (stack_buffer_shared_memory*)mmap(0, full_shared_mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, (off_t)0); close(shmid); - if (!pre_write_buffers) { + if (MAP_FAILED == pre_write_buffers) { _malloc_printf(ASL_LEVEL_INFO, "error mapping in shared memory for disk-based stack logging output buffers\n"); disable_stack_logging(); return; } - + // Store and use the buffer offsets in shared memory so that they can be accessed remotely pre_write_buffers->start_index_offset = 0ull; pre_write_buffers->next_free_index_buffer_offset = 0; @@ -741,8 +853,9 @@ prepare_to_log_stacks(void) disable_stack_logging(); return; } - - stack_buffer = (vm_address_t*)allocate_pages((uint64_t)round_page(sizeof(vm_address_t) * STACK_LOGGING_MAX_STACK_SIZE)); + + uint64_t stack_buffer_sz = (uint64_t)round_page(sizeof(vm_address_t) * STACK_LOGGING_MAX_STACK_SIZE); + stack_buffer = (vm_address_t*)allocate_pages(stack_buffer_sz); if (!stack_buffer) { _malloc_printf(ASL_LEVEL_INFO, "error while allocating stack trace buffer\n"); disable_stack_logging(); @@ -752,10 +865,20 @@ prepare_to_log_stacks(void) // malloc() can be called by the following, so these need to be done outside the stack_logging_lock but after the buffers have been set up. atexit(delete_log_files); // atexit() can call malloc() reap_orphaned_log_files(true); // this calls opendir() which calls malloc() - + // this call ensures that the log files exist; analyzing processes will rely on this assumption. if (create_log_file() == NULL) { - disable_stack_logging(); + /* postponement support requires cleaning up these structures now */ + __destroy_uniquing_table(pre_write_buffers->uniquing_table); + deallocate_pages(stack_buffer, stack_buffer_sz); + stack_buffer = NULL; + + munmap(pre_write_buffers, full_shared_mem_size); + pre_write_buffers = NULL; + + if (!stack_logging_postponed) { + disable_stack_logging(); + } return; } } @@ -764,7 +887,7 @@ prepare_to_log_stacks(void) void __disk_stack_logging_log_stack(uint32_t type_flags, uintptr_t zone_ptr, uintptr_t size, uintptr_t ptr_arg, uintptr_t return_val, uint32_t num_hot_to_skip) { - if (!stack_logging_enable_logging) return; + if (!stack_logging_enable_logging || stack_logging_postponed) return; // check incoming data if (type_flags & stack_logging_type_alloc && type_flags & stack_logging_type_dealloc) { @@ -796,7 +919,8 @@ __disk_stack_logging_log_stack(uint32_t type_flags, uintptr_t zone_ptr, uintptr_ prepare_to_log_stacks(); // since there could have been a fatal (to stack logging) error such as the log files not being created, check this variable before continuing - if (!stack_logging_enable_logging) return; + if (!stack_logging_enable_logging || stack_logging_postponed) return; + vm_address_t self_thread = (vm_address_t)pthread_self(); // use pthread_self() rather than mach_thread_self() to avoid system call // lock and enter @@ -891,8 +1015,14 @@ __stack_logging_fork_child() { OSSpinLockUnlock(&stack_logging_lock); } +void +__stack_logging_early_finished() { + stack_logging_finished_init = 1; + stack_logging_postponed = 0; +} + boolean_t -__stack_logging_locked() +__stack_logging_locked() { bool acquired_lock = OSSpinLockTry(&stack_logging_lock); if (acquired_lock) OSSpinLockUnlock(&stack_logging_lock); @@ -1042,7 +1172,7 @@ update_cache_for_file_streams(remote_task_file_streams *descriptors) close(shmid); } - if (shmid < 0 || cache->shmem == NULL) { + if (shmid < 0 || cache->shmem == MAP_FAILED) { // failed to connect to the shared memory region; warn and continue. _malloc_printf(ASL_LEVEL_INFO, "warning: unable to connect to remote process' shared memory; allocation histories may not be up-to-date.\n"); } @@ -1084,7 +1214,7 @@ update_cache_for_file_streams(remote_task_file_streams *descriptors) } // if a snapshot is necessary, memcpy from remote frozen process' memory - // note: there were two ways to do this – spin lock or suspend. suspend allows us to + // note: there were two ways to do this - spin lock or suspend. suspend allows us to // analyze processes even if they were artificially suspended. with a lock, there'd be // worry that the target was suspended with the lock taken. if (update_snapshot) { @@ -1203,8 +1333,10 @@ destroy_cache_for_file_streams(remote_task_file_streams *descriptors) // In the stack log analysis process, find the stack logging files for target process // by scanning the temporary directory for directory entries with names of the form "stack-logs.." // If we find such a directory then open the stack logging files in there. +// We might also have been passed the file path if the client first read it from __stack_log_file_path__ +// global variable in the target task, as will be needed if the .link cannot be put in /tmp. static void -open_log_files(pid_t pid, remote_task_file_streams *this_task_streams) +open_log_files(pid_t pid, char* file_path, remote_task_file_streams *this_task_streams) { DIR *dp; struct dirent *entry; @@ -1213,6 +1345,11 @@ open_log_files(pid_t pid, remote_task_file_streams *this_task_streams) reap_orphaned_log_files(false); // reap any left-over log files (for non-existant processes, but not for this analysis process) + if (file_path != NULL) { + this_task_streams->index_file_stream = fopen(file_path, "r"); + return; + } + if ((dp = opendir(_PATH_TMP)) == NULL) { return; } @@ -1239,7 +1376,7 @@ open_log_files(pid_t pid, remote_task_file_streams *this_task_streams) } static remote_task_file_streams* -retain_file_streams_for_task(task_t task) +retain_file_streams_for_task(task_t task, char* file_path) { if (task == MACH_PORT_NULL) return NULL; @@ -1280,7 +1417,7 @@ retain_file_streams_for_task(task_t task) remote_task_file_streams *this_task_streams = &remote_fds[next_remote_task_fd]; - open_log_files(pid, this_task_streams); + open_log_files(pid, file_path, this_task_streams); // check if opens failed if (this_task_streams->index_file_stream == NULL) { @@ -1330,10 +1467,25 @@ release_file_streams_for_task(task_t task) #pragma mark - extern +// +// The following is used by client tools like malloc_history and Instruments to pass along the path +// of the index file as read from the target task's __stack_log_file_path__ variable (set in this file) +// Eventually, at a suitable point, this additional argument should just be added to the other APIs below. +// +kern_return_t +__mach_stack_logging_set_file_path(task_t task, char* file_path) +{ + remote_task_file_streams *remote_fd = retain_file_streams_for_task(task, file_path); + if (remote_fd == NULL) { + return KERN_FAILURE; + } + return KERN_SUCCESS; +} + kern_return_t __mach_stack_logging_get_frames(task_t task, mach_vm_address_t address, mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames, uint32_t *count) { - remote_task_file_streams *remote_fd = retain_file_streams_for_task(task); + remote_task_file_streams *remote_fd = retain_file_streams_for_task(task, NULL); if (remote_fd == NULL) { return KERN_FAILURE; } @@ -1413,7 +1565,7 @@ __mach_stack_logging_get_frames(task_t task, mach_vm_address_t address, mach_vm_ kern_return_t __mach_stack_logging_enumerate_records(task_t task, mach_vm_address_t address, void enumerator(mach_stack_logging_record_t, void *), void *context) { - remote_task_file_streams *remote_fd = retain_file_streams_for_task(task); + remote_task_file_streams *remote_fd = retain_file_streams_for_task(task, NULL); if (remote_fd == NULL) { return KERN_FAILURE; } @@ -1496,7 +1648,7 @@ __mach_stack_logging_enumerate_records(task_t task, mach_vm_address_t address, v kern_return_t __mach_stack_logging_frames_for_uniqued_stack(task_t task, uint64_t stack_identifier, mach_vm_address_t *stack_frames_buffer, uint32_t max_stack_frames, uint32_t *count) { - remote_task_file_streams *remote_fd = retain_file_streams_for_task(task); + remote_task_file_streams *remote_fd = retain_file_streams_for_task(task, NULL); if (remote_fd == NULL) return KERN_FAILURE; __unwind_stack_from_table_index(&remote_fd->cache->uniquing_table, stack_identifier, stack_frames_buffer, count, max_stack_frames); @@ -1528,9 +1680,9 @@ main() fprintf(stderr, "sizeof stack_log_file_base_name: %lu\n", sizeof(stack_log_file_base_name)); total_globals += sizeof(stack_log_file_base_name); fprintf(stderr, "sizeof stack_log_file_suffix: %lu\n", sizeof(stack_log_file_suffix)); total_globals += sizeof(stack_log_file_suffix); fprintf(stderr, "sizeof stack_log_link_suffix: %lu\n", sizeof(stack_log_link_suffix)); total_globals += sizeof(stack_log_link_suffix); - fprintf(stderr, "sizeof stack_log_location: %lu\n", sizeof(stack_log_location)); total_globals += sizeof(stack_log_location); - fprintf(stderr, "sizeof stack_log_reference_file: %lu\n", sizeof(stack_log_reference_file)); total_globals += sizeof(stack_log_reference_file); - fprintf(stderr, "sizeof index_file_path: %lu\n", sizeof(index_file_path)); total_globals += sizeof(index_file_path); + fprintf(stderr, "sizeof stack_log_location: %lu\n", (size_t)PATH_MAX); total_globals += (size_t)PATH_MAX; + fprintf(stderr, "sizeof stack_log_reference_file: %lu\n", (size_t)PATH_MAX); total_globals += (size_t)PATH_MAX; + fprintf(stderr, "sizeof __stack_log_file_path__ (index_file_path): %lu\n", (size_t)PATH_MAX); total_globals += (size_t)PATH_MAX; fprintf(stderr, "sizeof index_file_descriptor: %lu\n", sizeof(index_file_descriptor)); total_globals += sizeof(index_file_descriptor); fprintf(stderr, "sizeof remote_fds: %lu\n", sizeof(remote_fds)); total_globals += sizeof(remote_fds); fprintf(stderr, "sizeof next_remote_task_fd: %lu\n", sizeof(next_remote_task_fd)); total_globals += sizeof(next_remote_task_fd);