diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c0c95fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/system_cmds.xcodeproj/project.xcworkspace +/system_cmds.xcodeproj/xcuserdata +.DS_Store +build/ +*~ +*.swp diff --git a/.upstream_base_commits b/.upstream_base_commits new file mode 100644 index 0000000..886f2e2 --- /dev/null +++ b/.upstream_base_commits @@ -0,0 +1,2 @@ +#freebsd = https://github.com/freebsd/freebsd.git +iostat.tproj/iostat.8 freebsd usr.sbin/iostat/iostat.8 4724d1448f7e7698308a1e05a7bb9b2069d68234 diff --git a/arch.tproj/arch.1 b/arch.tproj/arch.1 index 909bac5..afc984c 100644 --- a/arch.tproj/arch.1 +++ b/arch.tproj/arch.1 @@ -58,7 +58,7 @@ command with no arguments, displays the machine's architecture type. .Pp The other use of the .Nm arch -command it to run a selected architecture of a universal binary. +command is to run a selected architecture of a universal binary. A universal binary contains code that can run on different architectures. By default, the operating system will select the architecture that most closely matches the processor type. diff --git a/arch.tproj/arch.c b/arch.tproj/arch.c index 38db40d..86df70a 100644 --- a/arch.tproj/arch.c +++ b/arch.tproj/arch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * Copyright (c) 1999-2018 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -76,9 +76,10 @@ static const CPUTypes knownArchs[] = { #if defined(__i386__) || defined(__x86_64__) {"i386", CPU_TYPE_I386}, {"x86_64", CPU_TYPE_X86_64}, -#elif defined(__arm__) || defined(__arm64__) - {"arm", CPU_TYPE_ARM}, +#elif defined(__arm64__) {"arm64", CPU_TYPE_ARM64}, +#elif defined(__arm__) + {"arm", CPU_TYPE_ARM}, #else #error "Unsupported architecture" #endif @@ -98,14 +99,18 @@ extern char **environ; * the arch command is running. NULL means unsupported. */ #if defined(__i386__) || defined(__x86_64__) -#define NATIVE_32 "i386" -#define NATIVE_64 "x86_64" -#elif defined(__arm__) || defined(__arm64__) -#define NATIVE_32 "arm" -#define NATIVE_64 "arm64" + #define NATIVE_32 "i386" + #define NATIVE_64 "x86_64" +#elif defined(__arm64__) + #define NATIVE_64 "arm64" + #define NATIVE_32 NULL +#elif defined(__arm__) + #define NATIVE_32 "arm" + #define NATIVE_64 NULL #else -#error "Unsupported architecture" + #error "Unsupported architecture" #endif + bool unrecognizednative32seen = false; bool unrecognizednative64seen = false; diff --git a/base.xcconfig b/base.xcconfig new file mode 100644 index 0000000..5cd52d0 --- /dev/null +++ b/base.xcconfig @@ -0,0 +1,9 @@ +CODE_SIGN_IDENTITY = -; +CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion); +DEAD_CODE_STRIPPING = YES; +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym; +PREBINDING = NO; +// Current macOS +SDKROOT = macosx.internal; +VERSION_INFO_PREFIX = __attribute__((visibility("hidden"))) __ +VERSIONING_SYSTEM = apple-generic; diff --git a/chpass.tproj/pw_copy.c b/chpass.tproj/pw_copy.c index a9b9c70..8aa74a6 100644 --- a/chpass.tproj/pw_copy.c +++ b/chpass.tproj/pw_copy.c @@ -63,7 +63,7 @@ #include #include -#include +#include "pw_util.h" #include "pw_copy.h" extern char *tempname; diff --git a/cpuctl.tproj/cpuctl.8 b/cpuctl.tproj/cpuctl.8 new file mode 100644 index 0000000..f52f514 --- /dev/null +++ b/cpuctl.tproj/cpuctl.8 @@ -0,0 +1,67 @@ +.\" Darwin cpuctl man page adapted from NetBSD (credits below): +.\" +.\" $NetBSD: cpuctl.8,v 1.18 2018/01/14 00:45:54 mrg Exp $ +.\" +.\" Copyright (c) 2007, 2008, 2012, 2015 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Andrew Doran. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd March 18, 2019 +.Dt CPUCTL 8 +.Os Darwin +.Sh NAME +.Nm cpuctl +.Nd program to control CPUs +.Sh SYNOPSIS +.Nm cpuctl +.Ar command +.Op Ar arguments +.Sh DESCRIPTION +The +.Nm +command can be used to control and inspect the state of CPUs in the system. +.Pp +The first argument, +.Ar command , +specifies the action to take. +Valid commands are: +.Bl -tag -width offline +.It list +For each CPU in the system, display the current state and time of the last +state change. +.It offline Ar cpu Op Ar cpu ... +Set the specified CPUs off line. +.Pp +At least one CPU in the system must remain on line. +.It online Ar cpu Op Ar cpu ... +Set the specified CPUs on line. +.El +.Sh EXAMPLES +Run +.Dl cpuctl offline 2 +and then +.Dl cpuctl list +The output should reflect the fact that CPU#2 was taken offline. diff --git a/cpuctl.tproj/cpuctl.c b/cpuctl.tproj/cpuctl.c new file mode 100644 index 0000000..4821878 --- /dev/null +++ b/cpuctl.tproj/cpuctl.c @@ -0,0 +1,145 @@ +// +// cpuctl.c +// system_cmds +// +// Copyright (c) 2019 Apple Inc. All rights reserved. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void usage() +{ + printf("usage: cpuctl [ list ]\n"); + printf(" cpuctl { offline | online } [ ... ]\n"); + exit(EX_USAGE); +} + +static void fetch_cpu_info(host_t *priv_port, + processor_port_array_t proc_ports, + mach_msg_type_number_t proc_count, + processor_basic_info_data_t *cpus) +{ + for (int i = 0; i < proc_count; i++) { + mach_msg_type_number_t info_count = PROCESSOR_BASIC_INFO_COUNT; + + if (processor_info(proc_ports[i], PROCESSOR_BASIC_INFO, priv_port, + (processor_info_t)&cpus[i], &info_count) != KERN_SUCCESS) { + errx(EX_OSERR, "processor_info(%d) failed", i); + } + } +} + +static int do_cmd_list(mach_msg_type_number_t proc_count, processor_basic_info_data_t *cpus) +{ + int prev_lowest = -1; + for (int i = 0; i < proc_count; i++) { + int lowest_slot = INT_MAX; + int lowest_idx = -1; + for (int j = 0; j < proc_count; j++) { + int slot = cpus[j].slot_num; + if (slot > prev_lowest && slot < lowest_slot) { + lowest_slot = slot; + lowest_idx = j; + } + } + if (lowest_idx == -1) + errx(EX_OSERR, "slot numbers are out of range"); + + processor_basic_info_data_t *cpu = &cpus[lowest_idx]; + printf("CPU%d: %-7s type=%x,%x master=%d\n", + cpu->slot_num, + cpu->running ? "online" : "offline", + cpu->cpu_type, + cpu->cpu_subtype, + cpu->is_master); + + prev_lowest = lowest_slot; + } + return 0; +} + +static int find_cpu_by_slot(mach_msg_type_number_t proc_count, + processor_basic_info_data_t *cpus, + int slot) +{ + for (int i = 0; i < proc_count; i++) { + if (cpus[i].slot_num == slot) + return i; + } + return -1; +} + +int main(int argc, char **argv) +{ + int opt; + + while ((opt = getopt(argc, argv, "h")) != -1) { + switch (opt) { + case 'h': + usage(); + } + } + + host_t priv_port; + if (host_get_host_priv_port(mach_host_self(), &priv_port) != KERN_SUCCESS) + errx(EX_OSERR, "host_get_host_priv_port() failed"); + + processor_port_array_t proc_ports; + mach_msg_type_number_t proc_count; + if (host_processors(priv_port, &proc_ports, &proc_count) != KERN_SUCCESS) + errx(EX_OSERR, "host_processors() failed"); + + processor_basic_info_data_t *cpus = calloc(proc_count, sizeof(*cpus)); + if (!cpus) + errx(EX_OSERR, "calloc() failed"); + fetch_cpu_info(&priv_port, proc_ports, proc_count, cpus); + + if (optind == argc) + return do_cmd_list(proc_count, cpus); + + const char *cmd = argv[optind]; + optind++; + + if (!strcmp(cmd, "list")) + return do_cmd_list(proc_count, cpus); + + bool up = true; + if (!strncmp(cmd, "off", 3)) + up = false; + else if (strncmp(cmd, "on", 2)) + usage(); + + if (optind == argc) + usage(); + + int ret = 0; + for (; optind < argc; optind++) { + char *endp = NULL; + int slot = (int)strtoul(argv[optind], &endp, 0); + if (*endp != 0) + usage(); + + int cpu = find_cpu_by_slot(proc_count, cpus, slot); + if (cpu == -1) + errx(EX_USAGE, "Invalid CPU ID %d", slot); + + if (up) { + if (processor_start(proc_ports[cpu]) != KERN_SUCCESS) + errx(EX_OSERR, "processor_start(%u) failed", cpu); + } else { + if (processor_exit(proc_ports[cpu]) != KERN_SUCCESS) + errx(EX_OSERR, "processor_exit(%u) failed", cpu); + } + } + + return ret; +} diff --git a/dynamic_pager.tproj/com.apple.dynamic_pager.plist b/dynamic_pager.tproj/com.apple.dynamic_pager.plist index bf95905..4214037 100644 --- a/dynamic_pager.tproj/com.apple.dynamic_pager.plist +++ b/dynamic_pager.tproj/com.apple.dynamic_pager.plist @@ -16,8 +16,6 @@ ProgramArguments /sbin/dynamic_pager - -F - /private/var/vm/swapfile diff --git a/dynamic_pager.tproj/dynamic_pager.c b/dynamic_pager.tproj/dynamic_pager.c index 9c0f435..deb9379 100644 --- a/dynamic_pager.tproj/dynamic_pager.c +++ b/dynamic_pager.tproj/dynamic_pager.c @@ -43,16 +43,14 @@ clean_swap_directory(const char *path) int main(int argc, char **argv) { - char default_filename[] = "/private/var/vm/swapfile"; int ch; - int err=0; static char tmp[1024]; struct statfs sfs; char *q; char fileroot[512]; seteuid(getuid()); - strcpy(fileroot, default_filename); + fileroot[0] = '\0'; while ((ch = getopt(argc, argv, "F:")) != EOF) { switch((char)ch) { @@ -68,6 +66,25 @@ main(int argc, char **argv) } } + /* + * set vm.swapfileprefix if a fileroot was passed from the command + * line, otherwise get the value from the kernel + */ + if (fileroot[0] != '\0') { + if (sysctlbyname("vm.swapfileprefix", NULL, 0, fileroot, sizeof(fileroot)) == -1) { + perror("Failed to set swapfile name prefix"); + } + } else { + size_t fileroot_len = sizeof(fileroot); + if (sysctlbyname("vm.swapfileprefix", fileroot, &fileroot_len, NULL, 0) == -1) { + perror("Failed to get swapfile name prefix"); + /* + * can't continue without a fileroot + */ + return (0); + } + } + /* * get rid of the filename at the end of the swap file specification * we only want the portion of the pathname that should already exist @@ -93,10 +110,5 @@ main(int argc, char **argv) chown(tmp, 0, 0); - err = sysctlbyname("vm.swapfileprefix", NULL, 0, fileroot, sizeof(fileroot)); - if (err) { - (void)fprintf(stderr, "Failed to set swapfile name prefix with error: %d\n", err); - } - return (0); } diff --git a/dynamic_pager.tproj/entitlements.plist b/dynamic_pager.tproj/entitlements.plist new file mode 100644 index 0000000..2e0c44d --- /dev/null +++ b/dynamic_pager.tproj/entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.rootless.volume.VM + + + diff --git a/dynamic_pager.tproj/generate_plist.sh b/dynamic_pager.tproj/generate_plist.sh index 288f045..290a550 100644 --- a/dynamic_pager.tproj/generate_plist.sh +++ b/dynamic_pager.tproj/generate_plist.sh @@ -4,7 +4,7 @@ set -x cp "${SCRIPT_INPUT_FILE_0}" "${SCRIPT_OUTPUT_FILE_0}" case "$PLATFORM_NAME" in -iphone*|appletv*|watch*) +iphone*|appletv*|watch*|bridge*) /usr/libexec/PlistBuddy -c "Add :LaunchOnlyOnce bool true" "${SCRIPT_OUTPUT_FILE_0}" ;; macosx) diff --git a/fs_usage.tproj/fs_usage.c b/fs_usage.tproj/fs_usage.c index 24ec958..8612ae1 100644 --- a/fs_usage.tproj/fs_usage.c +++ b/fs_usage.tproj/fs_usage.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * Copyright (c) 1999-2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -40,18 +40,20 @@ #include #include -#include -#include +#include +#include +#include -#include -#include -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #import #import @@ -66,7 +68,7 @@ typedef struct th_info { struct th_info *next; - uintptr_t thread; + uint64_t thread; /* this is needed for execve()/posix_spawn(), because the command name at the end probe is the new name, which we don't want */ char command[MAXCOMLEN + 1]; @@ -76,21 +78,21 @@ typedef struct th_info { * (e.g., one absolute, one relative). traditional fs_usage behavior was to display the * *first* lookup, so we need to save it off once we see it. */ - unsigned long vnodeid; /* the vp of the VFS_LOOKUP we're currently in, 0 if we are not in one */ + uint64_t vnodeid; /* the vp of the VFS_LOOKUP we're currently in, 0 if we are not in one */ char pathname[MAXPATHLEN]; char pathname2[MAXPATHLEN]; char *newest_pathname; /* points to pathname2 if it's filled, otherwise pathname if it's filled, otherwise NULL */ int pid; int type; - unsigned long arg1; - unsigned long arg2; - unsigned long arg3; - unsigned long arg4; - unsigned long arg5; - unsigned long arg6; - unsigned long arg7; - unsigned long arg8; + uint64_t arg1; + uint64_t arg2; + uint64_t arg3; + uint64_t arg4; + uint64_t arg5; + uint64_t arg6; + uint64_t arg7; + uint64_t arg8; int waited; uint64_t stime; } *th_info_t; @@ -98,17 +100,17 @@ typedef struct th_info { struct diskio { struct diskio *next; struct diskio *prev; - unsigned long type; - unsigned long bp; - unsigned long dev; - unsigned long blkno; - unsigned long iosize; - unsigned long io_errno; - unsigned long is_meta; + uint64_t type; + uint64_t bp; + uint64_t dev; + uint64_t blkno; + uint64_t iosize; + uint64_t io_errno; + uint64_t is_meta; uint64_t vnodeid; - uintptr_t issuing_thread; + uint64_t issuing_thread; pid_t issuing_pid; - uintptr_t completion_thread; + uint64_t completion_thread; char issuing_command[MAXCOMLEN + 1]; uint64_t issued_time; uint64_t completed_time; @@ -120,11 +122,12 @@ struct diskio { #define HASH_MASK (HASH_SIZE - 1) void setup_ktrace_callbacks(void); -void extend_syscall(uintptr_t thread, int type, ktrace_event_t event); +void extend_syscall(uint64_t thread, int type, ktrace_event_t event); /* printing routines */ -bool check_filter_mode(pid_t pid, th_info_t ti, unsigned long type, int error, int retval, char *sc_name); -void format_print(th_info_t ti, char *sc_name, ktrace_event_t event, unsigned long type, int format, uint64_t now, uint64_t stime, int waited, const char *pathname, struct diskio *dio); +bool check_filter_mode(pid_t pid, th_info_t ti, uint64_t type, int error, int retval, char *sc_name); +void format_print(th_info_t ti, char *sc_name, ktrace_event_t event, uint64_t type, int format, uint64_t now, uint64_t stime, int waited, const char *pathname, struct diskio *dio); +int print_open(ktrace_event_t event, uint64_t flags); /* metadata info hash routines */ void meta_add_name(uint64_t blockno, const char *pathname); @@ -134,14 +137,14 @@ void meta_delete_all(void); /* event ("thread info") routines */ void event_enter(int type, ktrace_event_t event); void event_exit(char *sc_name, int type, ktrace_event_t event, int format); -th_info_t event_find(uintptr_t thread, int type); +th_info_t event_find(uint64_t thread, int type); void event_delete(th_info_t ti_to_delete); void event_delete_all(void); -void event_mark_thread_waited(uintptr_t); +void event_mark_thread_waited(uint64_t); /* network fd set routines */ -void fd_set_is_network(pid_t pid, unsigned long fd, bool set); -bool fd_is_network(pid_t pid, unsigned long fd); +void fd_set_is_network(pid_t pid, uint64_t fd, bool set); +bool fd_is_network(pid_t pid, uint64_t fd); void fd_clear_pid(pid_t pid); void fd_clear_all(void); @@ -150,17 +153,17 @@ void init_shared_cache_mapping(void); void lookup_name(uint64_t user_addr, char **type, char **name); /* disk I/O tracking routines */ -struct diskio *diskio_start(unsigned long type, unsigned long bp, unsigned long dev, unsigned long blkno, unsigned long iosize, ktrace_event_t event); -struct diskio *diskio_find(unsigned long bp); -struct diskio *diskio_complete(unsigned long bp, unsigned long io_errno, unsigned long resid, uintptr_t thread, uint64_t curtime, struct timeval curtime_wall); +struct diskio *diskio_start(uint64_t type, uint64_t bp, uint64_t dev, uint64_t blkno, uint64_t iosize, ktrace_event_t event); +struct diskio *diskio_find(uint64_t bp); +struct diskio *diskio_complete(uint64_t bp, uint64_t io_errno, uint64_t resid, uint64_t thread, uint64_t curtime, struct timeval curtime_wall); void diskio_print(struct diskio *dio); void diskio_free(struct diskio *dio); /* disk name routines */ #define NFS_DEV -1 #define CS_DEV -2 -char *generate_cs_disk_name(unsigned long dev, char *s); -char *find_disk_name(unsigned long dev); +char *generate_cs_disk_name(uint64_t dev, char *s); +char *find_disk_name(uint64_t dev); void cache_disk_names(void); #define CLASS_MASK 0xff000000 @@ -383,7 +386,11 @@ void cache_disk_names(void); #define BSC_pwrite_nocancel 0x040c067c #define BSC_aio_suspend_nocancel 0x40c0694 #define BSC_guarded_open_np 0x040c06e4 +#define BSC_guarded_open_dprotected_np 0x040c0790 #define BSC_guarded_close_np 0x040c06e8 +#define BSC_guarded_write_np 0x040c0794 +#define BSC_guarded_pwrite_np 0x040c0798 +#define BSC_guarded_writev_np 0x040c079c #define BSC_fsgetpath 0x040c06ac @@ -406,6 +413,7 @@ void cache_disk_names(void); #define BSC_msync_extended 0x040e0104 #define BSC_pread_extended 0x040e0264 #define BSC_pwrite_extended 0x040e0268 +#define BSC_guarded_pwrite_extended 0x040e0798 #define BSC_mmap_extended 0x040e0314 #define BSC_mmap_extended2 0x040f0314 @@ -458,6 +466,7 @@ void cache_disk_names(void); #define FMT_OPENAT 45 #define FMT_RENAMEAT 46 #define FMT_IOCTL_SYNCCACHE 47 +#define FMT_GUARDED_OPEN 48 #define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END) @@ -540,14 +549,18 @@ const struct bsd_syscall bsd_syscalls[MAX_BSD_SYSCALL] = { NORMAL_SYSCALL(posix_spawn), SYSCALL_WITH_NOCANCEL(open, FMT_OPEN), SYSCALL(open_extended, FMT_OPEN), - SYSCALL(guarded_open_np, FMT_OPEN), + SYSCALL(guarded_open_np, FMT_GUARDED_OPEN), SYSCALL_NAMED(open_dprotected_np, open_dprotected, FMT_OPEN), + SYSCALL(guarded_open_dprotected_np, FMT_GUARDED_OPEN), SYSCALL(dup, FMT_FD_2), SYSCALL(dup2, FMT_FD_2), SYSCALL_WITH_NOCANCEL(close, FMT_FD), SYSCALL(guarded_close_np, FMT_FD), SYSCALL_WITH_NOCANCEL(read, FMT_FD_IO), SYSCALL_WITH_NOCANCEL(write, FMT_FD_IO), + SYSCALL(guarded_write_np, FMT_FD_IO), + SYSCALL(guarded_pwrite_np, FMT_PREAD), + SYSCALL(guarded_writev_np, FMT_FD_IO), SYSCALL(fgetxattr, FMT_FD), SYSCALL(fsetxattr, FMT_FD), SYSCALL(fremovexattr, FMT_FD), @@ -674,7 +687,7 @@ static uint64_t mach_to_nano(uint64_t mach) { uint64_t nanoseconds = 0; - assert(ktrace_convert_timestamp_to_nanoseconds(s, mach, &nanoseconds) == 0); + os_assert(ktrace_convert_timestamp_to_nanoseconds(s, mach, &nanoseconds) == 0); return nanoseconds; } @@ -710,18 +723,35 @@ exit_usage(void) exit(1); } +static void fs_usage_cleanup(const char *message) +{ + if (s){ + ktrace_session_destroy(s); + } + + fprintf(stderr, "Cleaning up tracing state because of %s\n", message); +} + int main(int argc, char *argv[]) { char ch; int rv; bool exclude_pids = false; - double time_limit = 0.0; + uint64_t time_limit_ns = 0; + + os_set_crash_callback(&fs_usage_cleanup); get_screenwidth(); s = ktrace_session_create(); - assert(s); + os_assert(s != NULL); + (void)ktrace_ignore_process_filter_for_event(s, P_WrData); + (void)ktrace_ignore_process_filter_for_event(s, P_RdData); + (void)ktrace_ignore_process_filter_for_event(s, P_WrMeta); + (void)ktrace_ignore_process_filter_for_event(s, P_RdMeta); + (void)ktrace_ignore_process_filter_for_event(s, P_PgOut); + (void)ktrace_ignore_process_filter_for_event(s, P_PgIn); while ((ch = getopt(argc, argv, "bewf:R:S:E:t:W")) != -1) { switch (ch) { @@ -759,8 +789,12 @@ main(int argc, char *argv[]) break; case 't': - time_limit = atof(optarg); - + time_limit_ns = (uint64_t)(NSEC_PER_SEC * atof(optarg)); + if (time_limit_ns == 0) { + fprintf(stderr, "ERROR: could not set time limit to %s\n", + optarg); + exit(1); + } break; case 'R': @@ -788,17 +822,9 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; - if (time_limit > 0.0) { - if (RAW_flag) { - fprintf(stderr, "NOTE: time limit ignored when a raw file is specified\n"); - } else { - stop_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - dispatch_source_set_timer(stop_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * time_limit), DISPATCH_TIME_FOREVER, 0); - dispatch_source_set_event_handler(stop_timer, ^{ - ktrace_end(s, 0); - }); - dispatch_resume(stop_timer); - } + if (time_limit_ns > 0 && RAW_flag) { + fprintf(stderr, "NOTE: time limit ignored when a raw file is specified\n"); + time_limit_ns = 0; } if (!RAW_flag) { @@ -826,9 +852,9 @@ main(int argc, char *argv[]) ktrace_exclude_process(s, "csh"); ktrace_exclude_process(s, "sh"); ktrace_exclude_process(s, "zsh"); -#if TARGET_OS_EMBEDDED +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) ktrace_exclude_process(s, "dropbear"); -#endif /* TARGET_OS_EMBEDDED */ +#endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */ } } @@ -881,7 +907,7 @@ main(int argc, char *argv[]) fprintf(stderr, "ERROR: cannot both include and exclude simultaneously\n"); exit(1); } else { - assert(!rv); + os_assert(!rv); } argc--; @@ -901,7 +927,7 @@ main(int argc, char *argv[]) if (!wideflag) get_screenwidth(); }); - dispatch_resume(sigwinch_source); + dispatch_activate(sigwinch_source); init_shared_cache_mapping(); @@ -922,6 +948,8 @@ main(int argc, char *argv[]) ktrace_set_default_event_names_enabled(KTRACE_FEATURE_DISABLED); ktrace_set_execnames_enabled(s, KTRACE_FEATURE_LAZY); ktrace_set_vnode_paths_enabled(s, true); + /* no need to symbolicate addresses */ + ktrace_set_uuid_map_enabled(s, KTRACE_FEATURE_DISABLED); rv = ktrace_start(s, dispatch_get_main_queue()); @@ -930,6 +958,14 @@ main(int argc, char *argv[]) exit(1); } + if (time_limit_ns > 0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, time_limit_ns), + dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), + ^{ + ktrace_end(s, 0); + }); + } + dispatch_main(); return 0; @@ -1257,6 +1293,15 @@ setup_ktrace_callbacks(void) }); } +static void +extend_syscall_rw(th_info_t ti, ktrace_event_t event) +{ + ti->arg1 = event->arg1; /* the fd */ + ti->arg2 = event->arg2; /* nbytes */ + ti->arg3 = event->arg3; /* top half offset */ + ti->arg4 = event->arg4; /* bottom half offset */ +} + /* * Handle system call extended trace data. * pread and pwrite: @@ -1265,14 +1310,15 @@ setup_ktrace_callbacks(void) * is all we really need. */ void -extend_syscall(uintptr_t thread, int type, ktrace_event_t event) +extend_syscall(uint64_t thread, int type, ktrace_event_t event) { th_info_t ti; switch (type) { case BSC_mmap_extended: - if ((ti = event_find(thread, BSC_mmap)) == NULL) + if ((ti = event_find(thread, BSC_mmap)) == NULL) { return; + } ti->arg8 = ti->arg3; /* save protection */ ti->arg1 = event->arg1; /* the fd */ @@ -1292,8 +1338,9 @@ extend_syscall(uintptr_t thread, int type, ktrace_event_t event) case BSC_msync_extended: if ((ti = event_find(thread, BSC_msync)) == NULL) { - if ((ti = event_find(thread, BSC_msync_nocancel)) == NULL) + if ((ti = event_find(thread, BSC_msync_nocancel)) == NULL) { return; + } } ti->arg4 = event->arg1; /* top half address */ @@ -1302,26 +1349,30 @@ extend_syscall(uintptr_t thread, int type, ktrace_event_t event) case BSC_pread_extended: if ((ti = event_find(thread, BSC_pread)) == NULL) { - if ((ti = event_find(thread, BSC_pread_nocancel)) == NULL) + if ((ti = event_find(thread, BSC_pread_nocancel)) == NULL) { return; + } } - ti->arg1 = event->arg1; /* the fd */ - ti->arg2 = event->arg2; /* nbytes */ - ti->arg3 = event->arg3; /* top half offset */ - ti->arg4 = event->arg4; /* bottom half offset */ + extend_syscall_rw(ti, event); break; case BSC_pwrite_extended: if ((ti = event_find(thread, BSC_pwrite)) == NULL) { - if ((ti = event_find(thread, BSC_pwrite_nocancel)) == NULL) + if ((ti = event_find(thread, BSC_pwrite_nocancel)) == NULL) { return; + } } - ti->arg1 = event->arg1; /* the fd */ - ti->arg2 = event->arg2; /* nbytes */ - ti->arg3 = event->arg3; /* top half offset */ - ti->arg4 = event->arg4; /* bottom half offset */ + extend_syscall_rw(ti, event); + break; + + case BSC_guarded_pwrite_extended: + if ((ti = event_find(thread, BSC_guarded_pwrite_np)) == NULL) { + return; + } + + extend_syscall_rw(ti, event); break; } } @@ -1329,7 +1380,7 @@ extend_syscall(uintptr_t thread, int type, ktrace_event_t event) #pragma mark printing routines static void -get_mode_nibble(char *buf, unsigned long smode, unsigned long special, char x_on, char x_off) +get_mode_nibble(char *buf, uint64_t smode, uint64_t special, char x_on, char x_off) { if (smode & 04) buf[0] = 'r'; @@ -1349,7 +1400,7 @@ get_mode_nibble(char *buf, unsigned long smode, unsigned long special, char x_on } static void -get_mode_string(unsigned long mode, char *buf) +get_mode_string(uint64_t mode, char *buf) { memset(buf, '-', 9); buf[9] = '\0'; @@ -1396,11 +1447,11 @@ clip_64bit(char *s, uint64_t value) * filters may be combined; default is all filters on (except cachehit) */ bool -check_filter_mode(pid_t pid, th_info_t ti, unsigned long type, int error, int retval, char *sc_name) +check_filter_mode(pid_t pid, th_info_t ti, uint64_t type, int error, int retval, char *sc_name) { bool ret = false; int network_fd_isset = 0; - unsigned long fd; + uint64_t fd; /* cachehit is special -- it's not on by default */ if (sc_name[0] == 'C' && !strcmp(sc_name, "CACHE_HIT")) { @@ -1550,6 +1601,41 @@ check_filter_mode(pid_t pid, th_info_t ti, unsigned long type, int error, int re return ret; } +int +print_open(ktrace_event_t event, uint64_t flags) +{ + char mode[] = { + '_', + '_', + (flags & O_CREAT) ? 'C' : '_', + (flags & O_APPEND) ? 'A' : '_', + (flags & O_TRUNC) ? 'T' : '_', + (flags & O_EXCL) ? 'E' : '_', + (flags & O_NONBLOCK) ? 'N' : '_', + (flags & O_SHLOCK) ? 'l' : (flags & O_EXLOCK) ? 'L' : '_', + (flags & O_NOFOLLOW) ? 'F' : '_', + (flags & O_SYMLINK) ? 'S' : '_', + (flags & O_EVTONLY) ? 'V' : '_', + (flags & O_CLOEXEC) ? 'X' : '_', + '\0', + }; + + if (flags & O_RDWR) { + mode[0] = 'R'; + mode[1] = 'W'; + } else if (flags & O_WRONLY) { + mode[1] = 'W'; + } else { + mode[0] = 'R'; + } + + if (event->arg1) { + return printf(" [%3d] (%s) ", (int)event->arg1, mode); + } else { + return printf(" F=%-3d (%s) ", (int)event->arg2, mode); + } +} + /* * called from: * @@ -1559,7 +1645,7 @@ check_filter_mode(pid_t pid, th_info_t ti, unsigned long type, int error, int re */ void format_print(th_info_t ti, char *sc_name, ktrace_event_t event, - unsigned long type, int format, uint64_t now, uint64_t stime, + uint64_t type, int format, uint64_t now, uint64_t stime, int waited, const char *pathname, struct diskio *dio) { uint64_t secs, usecs; @@ -1570,16 +1656,16 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, int len = 0; int clen = 0; size_t tlen = 0; - unsigned long class; + uint64_t class; uint64_t user_addr; uint64_t user_size; char *framework_name; char *framework_type; char *p1; char *p2; - char buf[MAXWIDTH]; + char buf[2 * PATH_MAX + 64]; char cs_diskname[32]; - unsigned long threadid; + uint64_t threadid; struct timeval now_walltime; static char timestamp[32]; @@ -1589,12 +1675,12 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, mach_time_of_first_event = now; if (format == FMT_DISKIO || format == FMT_DISKIO_CS) { - assert(dio); + os_assert(dio); } else { - assert(event); + os_assert(event); if (format != FMT_UNMAP_INFO) - assert(ti); + os_assert(ti); } /* Filter out WindowServer/xcpm ioctls in fs_usage */ @@ -1638,7 +1724,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, if (!command_name) command_name = ""; - assert(now_walltime.tv_sec || now_walltime.tv_usec); + os_assert(now_walltime.tv_sec || now_walltime.tv_usec); /* try and reuse the timestamp string */ if (last_walltime_secs != now_walltime.tv_sec) { @@ -1713,7 +1799,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, if (event->arg1) clen += printf(" F=%-3d[%3d]", (int)ti->arg1, (int)event->arg1); else - clen += printf(" F=%-3d B=0x%-6lx", (int)ti->arg1, event->arg2); + clen += printf(" F=%-3d B=0x%-6" PRIx64, (int)ti->arg1, (uint64_t)event->arg2); break; @@ -1741,7 +1827,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, /* * pageout */ - clen += printf(" B=0x%-8lx", event->arg1); + clen += printf(" B=0x%-8" PRIx64, (uint64_t)event->arg1); break; case FMT_HFS_update: @@ -1796,12 +1882,12 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, * physical disk I/O */ if (dio->io_errno) { - clen += printf(" D=0x%8.8lx [%3d]", dio->blkno, (int)dio->io_errno); + clen += printf(" D=0x%8.8" PRIx64 " [%3d]", dio->blkno, (int)dio->io_errno); } else { if (BC_flag) - clen += printf(" D=0x%8.8lx B=0x%-6lx BC:%s /dev/%s ", dio->blkno, dio->iosize, BC_STR(dio->bc_info), find_disk_name(dio->dev)); + clen += printf(" D=0x%8.8" PRIx64 " B=0x%-6" PRIx64 " BC:%s /dev/%s ", dio->blkno, dio->iosize, BC_STR(dio->bc_info), find_disk_name(dio->dev)); else - clen += printf(" D=0x%8.8lx B=0x%-6lx /dev/%s ", dio->blkno, dio->iosize, find_disk_name(dio->dev)); + clen += printf(" D=0x%8.8" PRIx64 " B=0x%-6" PRIx64 " /dev/%s ", dio->blkno, dio->iosize, find_disk_name(dio->dev)); if (dio->is_meta) { if (!(type & P_DISKIO_READ)) { @@ -1824,9 +1910,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, * physical disk I/O */ if (dio->io_errno) - clen += printf(" D=0x%8.8lx [%3lu]", dio->blkno, dio->io_errno); + clen += printf(" D=0x%8.8" PRIx64 " [%3" PRIu64 "]", dio->blkno, dio->io_errno); else - clen += printf(" D=0x%8.8lx B=0x%-6lx /dev/%s", dio->blkno, dio->iosize, generate_cs_disk_name(dio->dev, cs_diskname)); + clen += printf(" D=0x%8.8" PRIx64 " B=0x%-6" PRIx64 " /dev/%s", dio->blkno, dio->iosize, generate_cs_disk_name(dio->dev, cs_diskname)); break; @@ -1964,6 +2050,34 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, p = "SETLKW"; break; + case F_SETLKWTIMEOUT: + p = "SETLKWTIMEOUT"; + break; + + case F_GETLKPID: + p = "GETLKPID"; + break; + + case F_OFD_GETLK: + p = "OFD_GETLK"; + break; + + case F_OFD_SETLK: + p = "OFD_SETLK"; + break; + + case F_OFD_SETLKW: + p = "OFD_SETLKW"; + break; + + case F_OFD_SETLKWTIMEOUT: + p = "OFD_SETLKWTIMEOUT"; + break; + + case F_OFD_GETLKPID: + p = "OFD_GETLKPID"; + break; + case F_PREALLOCATE: p = "PREALLOCATE"; break; @@ -2051,7 +2165,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, /* * ioctl */ - clen += printf(" B=%lu /dev/%s", event->arg3, find_disk_name(event->arg1)); + clen += printf(" B=%" PRIu64 " /dev/%s", (uint64_t)event->arg3, find_disk_name(event->arg1)); break; } @@ -2078,7 +2192,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, case FMT_UNMAP_INFO: { - clen += printf(" D=0x%8.8lx B=0x%-6lx /dev/%s", event->arg2, event->arg3, find_disk_name(event->arg1)); + clen += printf(" D=0x%8.8" PRIx64 " B=0x%-6" PRIx64 " /dev/%s", (uint64_t)event->arg2, (uint64_t)event->arg3, find_disk_name(event->arg1)); break; } @@ -2105,7 +2219,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, clen += printf("[%3d] ", (int)event->arg1); } else { if (format == FMT_PREAD) - clen += printf(" B=0x%-8lx ", event->arg2); + clen += printf(" B=0x%-8" PRIx64 " ", (uint64_t)event->arg2); else clen += printf(" "); } @@ -2267,7 +2381,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, /* * fchmod, fchmod_extended, chmod, chmod_extended */ - unsigned long mode; + uint64_t mode; if (format == FMT_FCHMOD || format == FMT_FCHMOD_EXT) { if (event->arg1) @@ -2328,9 +2442,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, case FMT_MOUNT: { if (event->arg1) - clen += printf(" [%3d] ", (int)event->arg1, ti->arg3); + clen += printf(" [%3d] ", (int)event->arg1, ti->arg3); else - clen += printf(" ", ti->arg3); + clen += printf(" ", ti->arg3); nopadding = 1; break; @@ -2354,46 +2468,20 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, break; } - case FMT_OPENAT: case FMT_OPEN: - { - /* - * open - */ - char mode[7]; - - memset(mode, '_', 6); - mode[6] = '\0'; - - if (ti->arg2 & O_RDWR) { - mode[0] = 'R'; - mode[1] = 'W'; - } else if (ti->arg2 & O_WRONLY) { - mode[1] = 'W'; - } else { - mode[0] = 'R'; - } - - if (ti->arg2 & O_CREAT) - mode[2] = 'C'; - - if (ti->arg2 & O_APPEND) - mode[3] = 'A'; - - if (ti->arg2 & O_TRUNC) - mode[4] = 'T'; - - if (ti->arg2 & O_EXCL) - mode[5] = 'E'; - - if (event->arg1) - clen += printf(" [%3d] (%s) ", (int)event->arg1, mode); - else - clen += printf(" F=%-3d (%s) ", (int)event->arg2, mode); - + clen += print_open(event, ti->arg2); + nopadding = 1; + break; + + case FMT_OPENAT: + clen += print_open(event, ti->arg3); + nopadding = 1; + break; + + case FMT_GUARDED_OPEN: + clen += print_open(event, ti->arg4); nopadding = 1; break; - } case FMT_SOCKET: { @@ -2446,9 +2534,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, } if (event->arg1) - clen += printf(" [%3d] <%s, %s, 0x%lx>", (int)event->arg1, domain, type, ti->arg3); + clen += printf(" [%3d] <%s, %s, 0x%" PRIx64 ">", (int)event->arg1, domain, type, ti->arg3); else - clen += printf(" F=%-3d <%s, %s, 0x%lx>", (int)event->arg2, domain, type, ti->arg3); + clen += printf(" F=%-3d <%s, %s, 0x%" PRIx64 ">", (int)event->arg2, domain, type, ti->arg3); break; } @@ -2470,9 +2558,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, op = "UNKNOWN"; if (event->arg1) - clen += printf(" [%3d] P=0x%8.8lx <%s>", (int)event->arg1, ti->arg2, op); + clen += printf(" [%3d] P=0x%8.8" PRIx64 " <%s>", (int)event->arg1, ti->arg2, op); else - clen += printf(" P=0x%8.8lx <%s>", ti->arg2, op); + clen += printf(" P=0x%8.8" PRIx64 " <%s>", ti->arg2, op); break; } @@ -2482,9 +2570,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, * aio_return [errno] AIOCBP IOSIZE */ if (event->arg1) - clen += printf(" [%3d] P=0x%8.8lx", (int)event->arg1, ti->arg1); + clen += printf(" [%3d] P=0x%8.8" PRIx64, (int)event->arg1, ti->arg1); else - clen += printf(" P=0x%8.8lx B=0x%-8lx", ti->arg1, event->arg2); + clen += printf(" P=0x%8.8" PRIx64 " B=0x%-8" PRIx64, ti->arg1, (uint64_t)event->arg2); break; @@ -2505,9 +2593,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, */ if (ti->arg2) { if (event->arg1) - clen += printf(" [%3d] P=0x%8.8lx", (int)event->arg1, ti->arg2); + clen += printf(" [%3d] P=0x%8." PRIx64, (int)event->arg1, ti->arg2); else - clen += printf(" P=0x%8.8lx", ti->arg2); + clen += printf(" P=0x%8.8" PRIx64, ti->arg2); } else { if (event->arg1) clen += printf(" F=%-3d[%3d]", (int)ti->arg1, (int)event->arg1); @@ -2522,9 +2610,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, * aio_error, aio_read, aio_write [errno] AIOCBP */ if (event->arg1) - clen += printf(" [%3d] P=0x%8.8lx", (int)event->arg1, ti->arg1); + clen += printf(" [%3d] P=0x%8.8" PRIx64, (int)event->arg1, ti->arg1); else - clen += printf(" P=0x%8.8lx", ti->arg1); + clen += printf(" P=0x%8.8" PRIx64, ti->arg1); break; @@ -2633,7 +2721,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event, p2 = " "; if (columns > MAXCOLS || wideflag) - printf("%s%s %3llu.%06llu%s %s.%lu\n", p1, pathname, secs, usecs, p2, command_name, threadid); + printf("%s%s %3llu.%06llu%s %s.%" PRIu64 "\n", p1, pathname, secs, usecs, p2, command_name, threadid); else printf("%s%s %3llu.%06llu%s %-12.12s\n", p1, pathname, secs, usecs, p2, command_name); @@ -2721,7 +2809,7 @@ add_event(ktrace_event_t event, int type) { th_info_t ti; int hashid; - unsigned long eventid; + uint64_t eventid; if ((ti = th_info_freelist)) th_info_freelist = ti->next; @@ -2756,7 +2844,7 @@ add_event(ktrace_event_t event, int type) } th_info_t -event_find(uintptr_t thread, int type) +event_find(uint64_t thread, int type) { th_info_t ti; int hashid; @@ -2851,7 +2939,7 @@ event_enter(int type, ktrace_event_t event) found = true; } - assert(found); + os_assert(found); #endif /* DEBUG */ if ((ti = add_event(event, type)) == NULL) @@ -2894,7 +2982,7 @@ event_exit(char *sc_name, int type, ktrace_event_t event, int format) } void -event_mark_thread_waited(uintptr_t thread) +event_mark_thread_waited(uint64_t thread) { th_info_t ti; int hashid; @@ -2924,7 +3012,7 @@ pfs_get(pid_t pid) struct pid_fd_set *pfs; int hashid; - assert(pid >= 0); + os_assert(pid >= 0); hashid = pid & HASH_MASK; @@ -2997,12 +3085,14 @@ fd_clear_all(void) } void -fd_set_is_network(pid_t pid, unsigned long fd, bool set) +fd_set_is_network(pid_t pid, uint64_t fd, bool set) { struct pid_fd_set *pfs; if (pid < 0) return; + if (fd > OPEN_MAX) + return; pfs = pfs_get(pid); @@ -3011,9 +3101,9 @@ fd_set_is_network(pid_t pid, unsigned long fd, bool set) if (!set) return; - newsize = MAX((fd + CHAR_BIT) / CHAR_BIT, 2 * pfs->setsize); + newsize = MAX(((size_t)fd + CHAR_BIT) / CHAR_BIT, 2 * pfs->setsize); pfs->set = reallocf(pfs->set, newsize); - assert(pfs->set); + os_assert(pfs->set != NULL); bzero(pfs->set + pfs->setsize, newsize - pfs->setsize); pfs->setsize = newsize; @@ -3026,7 +3116,7 @@ fd_set_is_network(pid_t pid, unsigned long fd, bool set) } bool -fd_is_network(pid_t pid, unsigned long fd) +fd_is_network(pid_t pid, uint64_t fd) { struct pid_fd_set *pfs; @@ -3321,8 +3411,8 @@ struct diskio *free_diskios = NULL; struct diskio *busy_diskios = NULL; struct diskio * -diskio_start(unsigned long type, unsigned long bp, unsigned long dev, - unsigned long blkno, unsigned long iosize, ktrace_event_t event) +diskio_start(uint64_t type, uint64_t bp, uint64_t dev, + uint64_t blkno, uint64_t iosize, ktrace_event_t event) { const char *command; struct diskio *dio; @@ -3365,7 +3455,7 @@ diskio_start(unsigned long type, unsigned long bp, unsigned long dev, } struct diskio * -diskio_find(unsigned long bp) +diskio_find(uint64_t bp) { struct diskio *dio; @@ -3378,8 +3468,8 @@ diskio_find(unsigned long bp) } struct diskio * -diskio_complete(unsigned long bp, unsigned long io_errno, unsigned long resid, - uintptr_t thread, uint64_t curtime, struct timeval curtime_wall) +diskio_complete(uint64_t bp, uint64_t io_errno, uint64_t resid, + uint64_t thread, uint64_t curtime, struct timeval curtime_wall) { struct diskio *dio; @@ -3415,7 +3505,7 @@ diskio_print(struct diskio *dio) { char *p = NULL; int len = 0; - unsigned long type; + uint64_t type; int format = FMT_DISKIO; char buf[64]; @@ -3533,8 +3623,11 @@ diskio_print(struct diskio *dio) buf[len] = 0; - if (check_filter_mode(-1, NULL, type, 0, 0, buf)) - format_print(NULL, buf, NULL, type, format, dio->completed_time, dio->issued_time, 1, "", dio); + if (check_filter_mode(-1, NULL, type, 0, 0, buf)) { + const char *pathname = ktrace_get_path_for_vp(s, dio->vnodeid); + format_print(NULL, buf, NULL, type, format, dio->completed_time, + dio->issued_time, 1, pathname ? pathname : "", dio); + } } #pragma mark disk name routines @@ -3604,7 +3697,7 @@ recache_disk_names(void) } char * -find_disk_name(unsigned long dev) +find_disk_name(uint64_t dev) { struct diskrec *dnp; int i; @@ -3627,12 +3720,12 @@ find_disk_name(unsigned long dev) } char * -generate_cs_disk_name(unsigned long dev, char *s) +generate_cs_disk_name(uint64_t dev, char *s) { if (dev == -1) return "UNKNOWN"; - sprintf(s, "disk%lus%lu", (dev >> 16) & 0xffff, dev & 0xffff); + sprintf(s, "disk%" PRIu64 "s%" PRIu64, (dev >> 16) & 0xffff, dev & 0xffff); return (s); } diff --git a/gcore.tproj/convert.c b/gcore.tproj/convert.c new file mode 100644 index 0000000..b945733 --- /dev/null +++ b/gcore.tproj/convert.c @@ -0,0 +1,1117 @@ +/* + * Copyright (c) 2016 Apple Inc. All rights reserved. + */ + +#include "convert.h" +#include "corefile.h" +#include "vanilla.h" +#include "threads.h" +#include "vm.h" +#include "dyld_shared_cache.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_GCORE_MAP) || defined(CONFIG_GCORE_CONV) || defined(CONFIG_GCORE_FREF) + +static const void * +mmapfile(int fd, off_t off, off_t *filesize) +{ + struct stat st; + if (-1 == fstat(fd, &st)) + errc(EX_OSERR, errno, "can't stat input file"); + + const size_t size = (size_t)(st.st_size - off); + if ((off_t)size != (st.st_size - off)) + errc(EX_OSERR, EOVERFLOW, "input file too large?"); + + const void *addr = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, off); + if ((void *)-1 == addr) + errc(EX_OSERR, errno, "can't mmap input file"); + *filesize = st.st_size; + return addr; +} + +static void +walkcore( + const native_mach_header_t *mh, + void (^coreinfo)(const struct proto_coreinfo_command *), + void (^frefdata)(const struct proto_fileref_command *), + void (^coredata)(const struct proto_coredata_command *), + void (^segdata)(const native_segment_command_t *), + void (^thrdata)(const struct thread_command *)) +{ + const struct load_command *lc = (const void *)(mh + 1); + for (unsigned i = 0; i < mh->ncmds; i++) { + switch (lc->cmd) { + case proto_LC_COREINFO: + if (coreinfo) + coreinfo((const void *)lc); + break; + case proto_LC_FILEREF: + if (frefdata) + frefdata((const void *)lc); + break; + case proto_LC_COREDATA: + if (coredata) + coredata((const void *)lc); + break; + case NATIVE_LC_SEGMENT: + if (segdata) + segdata((const void *)lc); + break; + case LC_THREAD: + if (thrdata) + thrdata((const void *)lc); + break; + default: + break; + } + if (NULL == (lc = next_lc(lc))) + break; + } +} + +#endif + +#ifdef CONFIG_GCORE_FREF + +int +gcore_fref(int fd) +{ + off_t filesize; + const void *corebase = mmapfile(fd, 0, &filesize); + + close(fd); + struct flist { + STAILQ_ENTRY(flist) f_linkage; + const char *f_nm; + unsigned long f_nmhash; + }; + STAILQ_HEAD(flisthead, flist) __flh, *flh = &__flh; + STAILQ_INIT(flh); + + walkcore(corebase, NULL, ^(const struct proto_fileref_command *fc) { + const char *nm = fc->filename.offset + (const char *)fc; + const unsigned long nmhash = simple_namehash(nm); + struct flist *f; + STAILQ_FOREACH(f, flh, f_linkage) { + if (nmhash == f->f_nmhash && 0 == strcmp(f->f_nm, nm)) + return; /* skip duplicates */ + } + struct flist *nf = calloc(1, sizeof (*nf)); + nf->f_nm = nm; + nf->f_nmhash = nmhash; + STAILQ_INSERT_TAIL(flh, nf, f_linkage); + }, NULL, NULL, NULL); + + struct flist *f, *tf; + STAILQ_FOREACH_SAFE(f, flh, f_linkage, tf) { + printf("%s\n", f->f_nm); + free(f); + f = NULL; + } + + munmap((void *)corebase, (size_t)filesize); + return 0; +} + +#endif /* CONFIG_GCORE_FREF */ + +#ifdef CONFIG_GCORE_MAP + +/* + * A pale imitation of vmmap, but for core files + */ +int +gcore_map(int fd) +{ + off_t filesize; + const void *corebase = mmapfile(fd, 0, &filesize); + + __block int coreversion = 0; + + walkcore(corebase, ^(const struct proto_coreinfo_command *ci) { + coreversion = ci->version; + }, NULL, NULL, NULL, NULL); + + if (0 == coreversion) { + const char titlfmt[] = "%16s-%-16s [%7s] %3s/%3s\n"; + const char *segcfmt = "%016llx-%016llx [%7s] %3s/%3s\n"; + + printf(titlfmt, "start ", " end", "vsize", "prt", "max"); + walkcore(corebase, NULL, NULL, NULL, ^(const native_segment_command_t *sc) { + hsize_str_t vstr; + printf(segcfmt, (mach_vm_offset_t)sc->vmaddr, (mach_vm_offset_t)sc->vmaddr + sc->vmsize, str_hsize(vstr, sc->vmsize), str_prot(sc->initprot), str_prot(sc->maxprot)); + }, NULL); + } else { + const char titlfmt[] = "%-23s %16s-%-16s [%7s] %3s/%3s %6s %4s %-14s\n"; + const char *freffmt = "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s @%lld\n"; + const char *datafmt = "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s\n"; + + printf(titlfmt, "region type", "start ", " end", "vsize", "prt", "max", "shrmod", "purge", "region detail"); + walkcore(corebase, NULL, ^(const struct proto_fileref_command *fc) { + const char *nm = fc->filename.offset + (const char *)fc; + tag_str_t tstr; + hsize_str_t vstr; + printf(freffmt, str_tag(tstr, fc->tag, fc->share_mode, fc->prot, fc->extp), + fc->vmaddr, fc->vmaddr + fc->vmsize, + str_hsize(vstr, fc->vmsize), str_prot(fc->prot), + str_prot(fc->maxprot), str_shared(fc->share_mode), + str_purgable(fc->purgable, fc->share_mode), nm, fc->fileoff); + }, ^(const struct proto_coredata_command *cc) { + tag_str_t tstr; + hsize_str_t vstr; + printf(datafmt, str_tag(tstr, cc->tag, cc->share_mode, cc->prot, cc->extp), + cc->vmaddr, cc->vmaddr + cc->vmsize, + str_hsize(vstr, cc->vmsize), str_prot(cc->prot), + str_prot(cc->maxprot), str_shared(cc->share_mode), + str_purgable(cc->purgable, cc->share_mode), + cc->vmsize && 0 == cc->filesize ? "(zfod)" : ""); + }, ^(const native_segment_command_t *sc) { + hsize_str_t vstr; + printf(datafmt, "", (mach_vm_offset_t)sc->vmaddr, + (mach_vm_offset_t)sc->vmaddr + sc->vmsize, + str_hsize(vstr, sc->vmsize), str_prot(sc->initprot), + str_prot(sc->maxprot), "", "", + sc->vmsize && 0 == sc->filesize ? "(zfod)" : ""); + }, NULL); + } + close(fd); + munmap((void *)corebase, (size_t)filesize); + return 0; +} + +#endif + +#ifdef CONFIG_GCORE_CONV + +/* + * Convert an input core file into an "old" format core file + * (a) convert all fileref segments into regular segments + * (b) uncompress anything we find compressed. + * This should be equivalent to a copy for an "old" format core file. + */ + +static int +machocmp(const native_mach_header_t *tmh, const native_mach_header_t *mh, const struct proto_fileref_command *fr) +{ + if (tmh->magic == mh->magic) { + const struct load_command *lc = (const void *)(tmh + 1); + for (unsigned i = 0; i < tmh->ncmds; i++) { + if (LC_UUID == lc->cmd && lc->cmdsize >= sizeof (struct uuid_command)) { + const struct uuid_command *uc = (const void *)lc; + return uuid_compare(uc->uuid, fr->id); + } + if (NULL == (lc = next_lc(lc))) + break; + } + } + return -1; +} + +static int +fat_machocmp(const struct fat_header *fh, const native_mach_header_t *mh, const struct proto_fileref_command *fr, off_t *reloff) +{ + const uint32_t (^get32)(uint32_t); + + if (FAT_MAGIC == fh->magic) { + get32 = ^(uint32_t val) { + return val; + }; + } else { + get32 = ^(uint32_t val) { + uint32_t result = 0; + for (unsigned i = 0; i < sizeof (uint32_t); i++) + ((uint8_t *)&result)[i] = ((uint8_t *)&val)[3-i]; + return result; + }; + } + + assert(FAT_MAGIC == get32(fh->magic)); + assert(kFREF_ID_UUID == FREF_ID_TYPE(fr->flags) && !uuid_is_null(fr->id)); + + const struct fat_arch *fa = (const struct fat_arch *)(fh + 1); + uint32_t narch = get32(fh->nfat_arch); + for (unsigned n = 0; n < narch; n++, fa++) { + const native_mach_header_t *tmh = (const void *)(((const char *)fh) + get32(fa->offset)); + if (tmh->magic == mh->magic && 0 == machocmp(tmh, mh, fr)) { + *reloff = get32(fa->offset); + return 0; + } + } + return -1; +} + +struct output_info { + int oi_fd; + off_t oi_foffset; + bool oi_nocache; +}; + +static struct convstats { + int64_t copied; + int64_t added; + int64_t compressed; + int64_t uncompressed; +} cstat, *cstats = &cstat; + +/* + * A fileref segment references a read-only file that contains pages from + * the image. The file may be a Mach binary or dylib identified with a uuid. + */ +static int +convert_fileref_with_file(const char *filename, const native_mach_header_t *inmh, const struct proto_fileref_command *infr, const struct vm_range *invr, struct load_command *lc, struct output_info *oi) +{ + assert(invr->addr == infr->vmaddr && invr->size == infr->vmsize); + + struct stat st; + const int rfd = open(filename, O_RDONLY); + if (-1 == rfd || -1 == fstat(rfd, &st)) { + warnc(errno, "%s: open", filename); + return EX_IOERR; + } + const size_t rlen = (size_t)st.st_size; + void *raddr = mmap(NULL, rlen, PROT_READ, MAP_PRIVATE, rfd, 0); + if ((void *)-1 == raddr) { + warnc(errno, "%s: mmap", filename); + close(rfd); + return EX_IOERR; + } + close(rfd); + + off_t fatoff = 0; /* for FAT objects */ + int ecode = EX_DATAERR; + + switch (FREF_ID_TYPE(infr->flags)) { + case kFREF_ID_UUID: { + /* file should be a mach binary: check that uuid matches */ + const uint32_t magic = *(uint32_t *)raddr; + switch (magic) { + case FAT_MAGIC: + case FAT_CIGAM: + if (0 == fat_machocmp(raddr, inmh, infr, &fatoff)) + ecode = 0; + break; + case NATIVE_MH_MAGIC: + if (0 == machocmp(raddr, inmh, infr)) + ecode = 0; + break; + default: { + /* + * Maybe this is the shared cache? + */ + uuid_t uu; + if (get_uuid_from_shared_cache_mapping(raddr, rlen, uu) && uuid_compare(uu, infr->id) == 0) + ecode = 0; + break; + } + } + break; + } + case kFREF_ID_MTIMESPEC_LE: + /* file should have the same mtime */ + if (0 == memcmp(&st.st_mtimespec, infr->id, sizeof (infr->id))) + ecode = 0; + break; + case kFREF_ID_NONE: + /* file has no uniquifier, copy it anyway */ + break; + } + + if (0 != ecode) { + munmap(raddr, rlen); + warnx("%s doesn't match corefile content", filename); + return ecode; + } + + const off_t fileoff = fatoff + infr->fileoff; + const void *start = (const char *)raddr + fileoff; + const size_t len = (size_t)infr->filesize; + void *zaddr = NULL; + size_t zlen = 0; + + if (fileoff + (off_t)infr->filesize > (off_t)rlen) { + /* + * the file content needed (as described on machine with + * larger pagesize) extends beyond the end of the mapped + * file using our smaller pagesize. Zero pad it. + */ + const size_t pagesize_host = 1ul << pageshift_host; + void *endaddr = (caddr_t)raddr + roundup(rlen, pagesize_host); + zlen = (size_t)(fileoff + infr->filesize - rlen); + zaddr = mmap(endaddr, zlen, PROT_READ, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0); + if ((void *)-1 == zaddr) { + hsize_str_t hstr; + warnc(errno, "cannot zero-pad %s mapping for %s", str_hsize(hstr, zlen),filename); + munmap(raddr, rlen); + return EX_IOERR; + } + } + + if (-1 == madvise((void *)start, len, MADV_SEQUENTIAL)) + warnc(errno, "%s: madvise", filename); + + const int error = bounded_pwrite(oi->oi_fd, start, len, oi->oi_foffset, &oi->oi_nocache, NULL); + + if (zlen) { + if (-1 == munmap(zaddr, zlen)) + warnc(errno, "%s: munmap zero pad", filename); + } + if (-1 == munmap(raddr, rlen)) + warnc(errno, "%s: munmap", filename); + if (error) { + warnc(error, "while copying %s to core file", filename); + return EX_IOERR; + } + + const struct file_range fr = { + .off = oi->oi_foffset, + .size = infr->filesize, + }; + make_native_segment_command(lc, invr, &fr, infr->maxprot, infr->prot); + oi->oi_foffset += fr.size; + cstats->added += infr->filesize; + return 0; +} + +/* + * expanduser tries to expand the leading '~' (if there is any) in the given + * path and returns a copy of the expanded path; it returns NULL on failures. + * The caller is responsible for freeing the returned string. + */ +static char * +expanduser(const char *path) +{ + if (path == NULL) { + return NULL; + } + if (path[0] != '~') { + /* + * For consistency, still dup the string so that the caller always + * needs to free the string. + */ + return strdup(path); + } + + char *expanded = NULL; + glob_t globbuf = {}; + if (OPTIONS_DEBUG(opt, 1)) { + printf("Expanding %s\n", path); + } + int rc = glob(path, GLOB_TILDE, NULL, &globbuf); + if (rc == 0) { + if (OPTIONS_DEBUG(opt, 3)) { + printf("expanduser - gl_pathc: %zu\n", globbuf.gl_pathc); + for (size_t i = 0; i < globbuf.gl_pathc; i++) { + printf("expanduser - gl_pathv[%zu]: %s\n", i, globbuf.gl_pathv[i]); + } + } + if (globbuf.gl_pathc == 1) { + expanded = strdup(globbuf.gl_pathv[0]); + if (OPTIONS_DEBUG(opt, 1)) { + printf("Expanded path: %s\n", expanded); + } + } + globfree(&globbuf); + } + + return expanded; +} + +#define RESPONSE_BUFF_SIZE (2048) + +/* + * read_response dynamically allocates buffer for reading bytes from the given + * fd. Upon success, this function sets response to point to the buffer and + * returns bytes being read; otherwise, it returns -1. The caller is + * responsible for freeing the response buffer. + */ +static ssize_t +read_response(int fd, char **response) +{ + if (response == NULL || *response) { + warnx("Invalid response buffer pointer"); + return -1; + } + + ssize_t bytes_read = 0; + size_t buff_size = RESPONSE_BUFF_SIZE; + + if (OPTIONS_DEBUG(opt, 3)) { + printf("Allocating response buffer (%zu)\n", buff_size); + } + char *buff = malloc(buff_size); + if (buff == NULL) { + warn("Failed to allocate response buffer (%zu)", buff_size); + return -1; + } + + size_t total = 0; + bool failed = false; + + do { + bytes_read = read(fd, buff + total, buff_size - total); + if (bytes_read == -1) { + if (errno == EINTR) { + continue; + } + failed = true; + break; + } + + total += (size_t)bytes_read; + if (total == buff_size) { + size_t new_buff_size = buff_size * 2; + if (OPTIONS_DEBUG(opt, 3)) { + printf("Reallocating response buffer (%zu)\n", new_buff_size); + } + char *new_buff = realloc(buff, new_buff_size); + if (new_buff == NULL) { + warn("Failed to reallocate response buffer (%zu)", new_buff_size); + failed = true; + break; + } + buff_size = new_buff_size; + buff = new_buff; + } + } while (bytes_read != 0); + + if (failed) { + if (buff != NULL) { + free(buff); + } + return -1; + } + + assert(total < buff_size); + buff[total] = '\0'; + *response = buff; + + return (ssize_t)total; +} + +#define WAITPID_WTO_SIGALRM (100) /* alternative for SIGALRM for kevent timeout */ +#define WAITPID_WTO_SIGERR (101) /* sig for error when waiting for pid */ + +/* + * waitpid_with_timeout returns true if the process exits successfully within + * timeout; otherwise, it returns false along with setting exitstatus and + * signal_no if the pointers are given. + */ +static bool +waitpid_with_timeout(pid_t pid, int *exitstatus, int *signal_no, int timeout) +{ + int status; + int kq = -1; + + if (timeout > 0) { + kq = kqueue(); + struct kevent64_s event = { + .ident = (uint64_t)pid, + .filter = EVFILT_PROC, + .flags = EV_ADD | EV_ONESHOT, + .fflags = NOTE_EXIT + }; + struct timespec tmout = { + .tv_sec = timeout + }; + int ret = kevent64(kq, &event, 1, &event, 1, 0, &tmout); + int kevent64_errno = errno; + + close(kq); + if (ret == 0) { /* timeout */ + if (exitstatus) { + *exitstatus = 0; + } + if (signal_no) { + *signal_no = WAITPID_WTO_SIGALRM; + } + return false; + } + + if (ret == -1) { + warnx("kevent64(): errno=%d (%s)\n", kevent64_errno, strerror(kevent64_errno)); + goto waitpid_error; + } + + if (event.flags == EV_ERROR && event.data != ESRCH) { + warnx("event.data (%lld) is not ESRCH when event.flags is EV_ERROR\n", event.data); + goto waitpid_error; + } + + if (event.ident != (uint64_t)pid) { + warnx("event.ident is %lld (should be pid %d)\n", event.ident, pid); + goto waitpid_error; + } + + if (event.filter != EVFILT_PROC) { + warnx("event.filter (%d) is not EVFILT_PROC\n", event.filter); + goto waitpid_error; + } + } + + while (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) { + continue; + } + warnx("waitpid(): errno=%d (%s)\n", errno, strerror(errno)); + goto waitpid_error; + } + if (WIFEXITED(status)) { + if (exitstatus) { + *exitstatus = WEXITSTATUS(status); + } + if (signal_no) { + *signal_no = 0; + } + return WEXITSTATUS(status) == 0; + } + if (WIFSIGNALED(status)) { + if (exitstatus) { + *exitstatus = 0; + } + if (signal_no) { + *signal_no = WTERMSIG(status); + } + return false; + } + +waitpid_error: + if (exitstatus) *exitstatus = 0; + if (signal_no) *signal_no = WAITPID_WTO_SIGERR; + return false; +} + +#define DSYMFORUUID_PATH "/usr/local/bin/dsymForUUID" + +/* + * exec_dsymForUUID spawns dsymForUUID to query dsym UUID info and responds the + * result plist. Upon success, this function sets response point to the buffer + * and returns bytes being read; otherwise, it returns -1. The caller is + * responsible for freeing the response buffer. + */ +static ssize_t +exec_dsymForUUID(uuid_string_t id, char **response) +{ + int pipe_fds[2] = {-1, -1}; + bool file_actions_inited = false; + ssize_t bytes_read = -1; + int rc; + + rc = pipe(pipe_fds); + if (rc == -1) { + goto cleanup; + } + + posix_spawn_file_actions_t file_actions; + rc = posix_spawn_file_actions_init(&file_actions); + if (rc) { + goto cleanup; + } + file_actions_inited = true; + + rc = posix_spawn_file_actions_addclose(&file_actions, pipe_fds[0]); + if (rc) { + goto cleanup; + } + + rc = posix_spawn_file_actions_adddup2(&file_actions, pipe_fds[1], STDOUT_FILENO); + if (rc) { + goto cleanup; + } + + rc = posix_spawn_file_actions_addclose(&file_actions, pipe_fds[1]); + if (rc) { + goto cleanup; + } + + char *command[] = {DSYMFORUUID_PATH, id, NULL}; + pid_t child; + rc = posix_spawn(&child, command[0], &file_actions, NULL, command, NULL); + if (rc) { + goto cleanup; + } + + close(pipe_fds[1]); + pipe_fds[1] = -1; + + bytes_read = read_response(pipe_fds[0], response); + + waitpid_with_timeout(child, NULL, NULL, 3); + +cleanup: + if (pipe_fds[1] != -1) { + close(pipe_fds[1]); + } + if (pipe_fds[0] != -1) { + close(pipe_fds[0]); + } + if (file_actions_inited) { + posix_spawn_file_actions_destroy(&file_actions); + } + + return bytes_read; +} + +/* + * get_symbol_rich_executable_path_via_dsymForUUID spawns dsymForUUID to query + * dsym uuid info for the given uuid and returns the string of + * DBGSymbolRichExecutable; otherwise, it returns NULL on failures. The caller + * is responsible for freeing the returned string. + */ +static char * +get_symbol_rich_executable_path_via_dsymForUUID(const uuid_t uuid) +{ + char *response; + ssize_t size; + uuid_string_t uuid_str; + xpc_object_t plist = NULL; + xpc_object_t uuid_info = NULL; + xpc_object_t exec_path = NULL; + char *expanded_exec_path = NULL; + + uuid_unparse_upper(uuid, uuid_str); + + size = exec_dsymForUUID(uuid_str, &response); + if (size <= 0) { + goto cleanup; + } + + if (OPTIONS_DEBUG(opt, 3)) { + printf("dsymForUUID response:\n%s\n", response); + } + + plist = xpc_create_from_plist(response, (size_t)size); + if (plist == NULL) { + goto cleanup; + } + if (xpc_get_type(plist) != XPC_TYPE_DICTIONARY) { + goto cleanup; + } + + uuid_info = xpc_dictionary_get_value(plist, uuid_str); + if (uuid_info == NULL) { + goto cleanup; + } + if (xpc_get_type(uuid_info) != XPC_TYPE_DICTIONARY) { + goto cleanup; + } + + exec_path = xpc_dictionary_get_value(uuid_info, "DBGSymbolRichExecutable"); + if (exec_path == NULL) { + goto cleanup; + } + if (xpc_get_type(exec_path) != XPC_TYPE_STRING) { + goto cleanup; + } + + expanded_exec_path = expanduser(xpc_string_get_string_ptr(exec_path)); + +cleanup: + if (plist) { + xpc_release(plist); + } + if (response) { + free(response); + } + + return expanded_exec_path; +} + +/* + * bind the file reference into the output core file. + * filename optionally prefixed with names from a ':'-separated PATH variable + */ +static int +convert_fileref(const char *path, bool zf, const native_mach_header_t *inmh, const struct proto_fileref_command *infr, struct load_command *lc, struct output_info *oi) +{ + const char *nm = infr->filename.offset + (const char *)infr; + uuid_string_t uustr; + const struct vm_range invr = { + .addr = infr->vmaddr, + .size = infr->vmsize, + }; + + if (opt->verbose) { + hsize_str_t hstr; + printvr(&invr, "adding %s from '%s'", + str_hsize(hstr, (off_t)infr->filesize), nm); + switch (FREF_ID_TYPE(infr->flags)) { + case kFREF_ID_NONE: + break; + case kFREF_ID_UUID: + uuid_unparse_lower(infr->id, uustr); + printf(" (%s)", uustr); + break; + case kFREF_ID_MTIMESPEC_LE: { + struct timespec mts; + struct tm tm; + char tbuf[4 + 2 + 2 + 2 + 2 + 1 + 2 + 1]; /* touch -t */ + memcpy(&mts, &infr->id, sizeof (mts)); + localtime_r(&mts.tv_sec, &tm); + strftime(tbuf, sizeof (tbuf), "%Y%m%d%H%M.%S", &tm); + printf(" (%s)", tbuf); + } break; + } + printf("\n"); + } + + int ecode = 0; + if (opt->dsymforuuid && (FREF_ID_TYPE(infr->flags) == kFREF_ID_UUID)) { + /* Try to use dsymForUUID to get the symbol-rich executable */ + char *symrich_filepath = get_symbol_rich_executable_path_via_dsymForUUID(infr->id); + if (symrich_filepath) { + if (opt->verbose) { + printf("\tTrying %s from dsymForUUID\n", symrich_filepath); + } + ecode = convert_fileref_with_file(symrich_filepath, inmh, infr, &invr, lc, oi); + free(symrich_filepath); + if (ecode == 0) { + return (ecode); + } + warnx("Failed to convert fileref with dsymForUUID. Fall back to local paths"); + } + } + + const size_t pathsize = path ? strlen(path) : 0; + ecode = EX_DATAERR; + if (0 == pathsize) + ecode = convert_fileref_with_file(nm, inmh, infr, &invr, lc, oi); + else { + /* search the : separated path (-L) for possible matches */ + char *pathcopy = strdup(path); + char *searchpath = pathcopy; + const char *token; + + while ((token = strsep(&searchpath, ":")) != NULL) { + const size_t buflen = strlen(token) + 1 + strlen(nm) + 1; + char *buf = malloc(buflen); + snprintf(buf, buflen, "%s%s%s", token, '/' == nm[0] ? "" : "/", nm); + if (opt->verbose) + printf("\tTrying '%s'", buf); + if (0 == access(buf, R_OK)) { + if (opt->verbose) + printf("\n"); + ecode = convert_fileref_with_file(buf, inmh, infr, &invr, lc, oi); + if (0 == ecode) { + free(buf); + break; + } + } else if (opt->verbose) + printf(": %s.\n", + 0 == access(buf, F_OK) ? "Unreadable" : "Not present"); + free(buf); + } + free(pathcopy); + } + + if (0 != ecode && zf) { + /* + * Failed to find the file reference. If this was a fileref that uses + * a file metadata tagging method (e.g. mtime), allow the user to subsitute a + * zfod region: assumes that it's better to have something to debug + * vs. nothing. UUID-tagged filerefs are Mach-O tags, and are + * assumed to be never substitutable. + */ + switch (FREF_ID_TYPE(infr->flags)) { + case kFREF_ID_NONE: + case kFREF_ID_MTIMESPEC_LE: { // weak tagging, allow zfod substitution + const struct file_range outfr = { + .off = oi->oi_foffset, + .size = 0, + }; + if (opt->verbose) + printf("\tWARNING: no file matched. Missing content is now zfod\n"); + else + printvr(&invr, "WARNING: missing content (%s) now zfod\n", nm); + make_native_segment_command(lc, &invr, &outfr, infr->maxprot, infr->prot); + ecode = 0; + } break; + default: + break; + } + } + + return (ecode); +} + +static int +segment_uncompflags(unsigned algnum, compression_algorithm *ca) +{ + switch (algnum) { + case kCOMP_LZ4: + *ca = COMPRESSION_LZ4; + break; + case kCOMP_ZLIB: + *ca = COMPRESSION_ZLIB; + break; + case kCOMP_LZMA: + *ca = COMPRESSION_LZMA; + break; + case kCOMP_LZFSE: + *ca = COMPRESSION_LZFSE; + break; + default: + warnx("unknown compression flavor %d", algnum); + return EX_DATAERR; + } + return 0; +} + +static int +convert_region(const void *inbase, const struct vm_range *invr, const struct file_range *infr, const vm_prot_t prot, const vm_prot_t maxprot, const int flavor, struct load_command *lc, struct output_info *oi) +{ + int ecode = 0; + + if (F_SIZE(infr)) { + void *input = (const caddr_t)inbase + F_OFF(infr); + void *buf; + + if (0 == flavor) { + buf = input; + if (opt->verbose) { + hsize_str_t hstr; + printvr(invr, "copying %s\n", str_hsize(hstr, F_SIZE(infr))); + } + } else { + compression_algorithm ca; + + if (0 != (ecode = segment_uncompflags(flavor, &ca))) + return ecode; + if (opt->verbose) { + hsize_str_t hstr1, hstr2; + printvr(invr, "uncompressing %s to %s\n", + str_hsize(hstr1, F_SIZE(infr)), str_hsize(hstr2, V_SIZE(invr))); + } + const size_t buflen = V_SIZEOF(invr); + buf = malloc(buflen); + const size_t dstsize = compression_decode_buffer(buf, buflen, input, (size_t)F_SIZE(infr), NULL, ca); + if (buflen != dstsize) { + warnx("failed to uncompress segment"); + free(buf); + return EX_DATAERR; + } + cstats->compressed += F_SIZE(infr); + } + const int error = bounded_pwrite(oi->oi_fd, buf, V_SIZEOF(invr), oi->oi_foffset, &oi->oi_nocache, NULL); + if (error) { + warnc(error, "failed to write data to core file"); + ecode = EX_IOERR; + } + if (buf != input) + free(buf); + if (ecode) + return ecode; + + const struct file_range outfr = { + .off = oi->oi_foffset, + .size = V_SIZE(invr), + }; + make_native_segment_command(lc, invr, &outfr, maxprot, prot); + oi->oi_foffset += outfr.size; + + if (0 == flavor) + cstats->copied += outfr.size; + else + cstats->uncompressed += outfr.size; + } else { + if (opt->verbose) { + hsize_str_t hstr; + printvr(invr, "%s remains zfod\n", str_hsize(hstr, V_SIZE(invr))); + } + const struct file_range outfr = { + .off = oi->oi_foffset, + .size = 0, + }; + make_native_segment_command(lc, invr, &outfr, maxprot, prot); + } + return ecode; +} + +static int +convert_coredata(const void *inbase, const native_mach_header_t *__unused inmh, const struct proto_coredata_command *cc, struct load_command *lc, struct output_info *oi) +{ + const struct vm_range vr = { + .addr = cc->vmaddr, + .size = cc->vmsize, + }; + const struct file_range fr = { + .off = cc->fileoff, + .size = cc->filesize, + }; + return convert_region(inbase, &vr, &fr, cc->prot, cc->maxprot, COMP_ALG_TYPE(cc->flags), lc, oi); +} + +static int +convert_segment(const void *inbase, const native_mach_header_t *__unused inmh, const native_segment_command_t *sc, struct load_command *lc, struct output_info *oi) +{ + const struct vm_range vr = { + .addr = sc->vmaddr, + .size = sc->vmsize, + }; + const struct file_range fr = { + .off = sc->fileoff, + .size = sc->filesize, + }; + return convert_region(inbase, &vr, &fr, sc->initprot, sc->maxprot, 0, lc, oi); +} + +/* pass-through - content is all in the header */ + +static int +convert_thread(struct thread_command *dst, const struct thread_command *src) +{ + assert(LC_THREAD == src->cmd); + memcpy(dst, src, src->cmdsize); + cstats->copied += src->cmdsize; + return 0; +} + +int +gcore_conv(int infd, const char *searchpath, bool zf, int fd) +{ + off_t filesize; + const void *corebase = mmapfile(infd, 0, &filesize); + close(infd); + /* + * Check to see if the input file is "sane" as far as we're concerned. + * XXX Note that this -won't- necessarily work for other ISAs than + * our own! + */ + const native_mach_header_t *inmh = corebase; + validate_core_header(inmh, filesize); + + /* + * The sparse file may have created many more segments, but there's no + * attempt to change their numbers here. Just count all the segment + * types needed to figure out the size of the output file header. + * + * (Size assertions to be deleted once data structures stable!) + */ + __block size_t headersize = sizeof (native_mach_header_t); + __block unsigned pageshift_target = pageshift_host; + + walkcore(inmh, ^(const struct proto_coreinfo_command *ci) { + assert(sizeof (*ci) == ci->cmdsize); + if (opt->verbose) + printf("Converting version %d core file to pre-versioned format\n", ci->version); + if (0 < ci->pageshift && ci->pageshift < 31) + pageshift_target = ci->pageshift; + else if (CPU_TYPE_ARM64 == inmh->cputype) + pageshift_target = 14; // compatibility hack, should go soon + }, ^(const struct proto_fileref_command *__unused fc) { + const char *nm = fc->filename.offset + (const char *)fc; + size_t nmlen = strlen(nm) + 1; + size_t cmdsize = sizeof (*fc) + roundup(nmlen, sizeof (long)); + assert(cmdsize == fc->cmdsize); + + headersize += sizeof (native_segment_command_t); + }, ^(const struct proto_coredata_command *__unused cc) { + assert(sizeof (*cc) == cc->cmdsize); + headersize += sizeof (native_segment_command_t); + }, ^(const native_segment_command_t *sc) { + headersize += sc->cmdsize; + }, ^(const struct thread_command *tc) { + headersize += tc->cmdsize; + }); + + void *header = calloc(1, headersize); + if (NULL == header) + errx(EX_OSERR, "out of memory for header"); + + native_mach_header_t *mh = memcpy(header, inmh, sizeof (*mh)); + mh->ncmds = 0; + mh->sizeofcmds = 0; + + assert(0 < pageshift_target && pageshift_target < 31); + const vm_offset_t pagesize_target = ((vm_offset_t)1 << pageshift_target); + const vm_offset_t pagemask_target = pagesize_target - 1; + + const struct load_command *inlc = (const void *)(inmh + 1); + struct load_command *lc = (void *)(mh + 1); + int ecode = 0; + + struct output_info oi = { + .oi_fd = fd, + .oi_foffset = ((vm_offset_t)headersize + pagemask_target) & ~pagemask_target, + .oi_nocache = false, + }; + + for (unsigned i = 0; i < inmh->ncmds; i++) { + switch (inlc->cmd) { + case proto_LC_FILEREF: + ecode = convert_fileref(searchpath, zf, inmh, (const void *)inlc, lc, &oi); + break; + case proto_LC_COREDATA: + ecode = convert_coredata(corebase, inmh, (const void *)inlc, lc, &oi); + break; + case NATIVE_LC_SEGMENT: + ecode = convert_segment(corebase, inmh, (const void *)inlc, lc, &oi); + break; + case LC_THREAD: + ecode = convert_thread((void *)lc, (const void *)inlc); + break; + default: + if (OPTIONS_DEBUG(opt, 1)) + printf("discarding load command %d\n", inlc->cmd); + break; + } + if (0 != ecode) + break; + if (NATIVE_LC_SEGMENT == lc->cmd || LC_THREAD == lc->cmd) { + mach_header_inc_ncmds(mh, 1); + mach_header_inc_sizeofcmds(mh, lc->cmdsize); + lc = (void *)next_lc(lc); + } + if (NULL == (inlc = next_lc(inlc))) + break; + } + + /* + * Even if we've encountered an error, try and write out the header + */ + if (0 != bounded_pwrite(fd, header, headersize, 0, &oi.oi_nocache, NULL)) + ecode = EX_IOERR; + if (0 == ecode && sizeof (*mh) + mh->sizeofcmds != headersize) + ecode = EX_SOFTWARE; + validate_core_header(mh, oi.oi_foffset); + if (ecode) + warnx("failed to write new core file correctly"); + else if (opt->verbose) { + hsize_str_t hstr; + printf("Conversion complete: %s copied", str_hsize(hstr, cstats->copied)); + const int64_t delta = cstats->uncompressed - cstats->compressed; + if (delta > 0) + printf(", %s uncompressed", str_hsize(hstr, delta)); + const int64_t added = cstats->added + ((int)mh->sizeofcmds - (int)inmh->sizeofcmds); + if (added > 0) + printf(", %s added", str_hsize(hstr, added)); + printf("\n"); + } + free(header); + munmap((void *)corebase, (size_t)filesize); + return ecode; +} +#endif diff --git a/gcore.tproj/convert.h b/gcore.tproj/convert.h new file mode 100644 index 0000000..03854b8 --- /dev/null +++ b/gcore.tproj/convert.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016 Apple Inc. All rights reserved. + */ + +#include "options.h" + +#include + +#ifndef _CONVERT_H +#define _CONVERT_H + +#ifdef CONFIG_GCORE_FREF +extern int gcore_fref(int); +#endif + +#ifdef CONFIG_GCORE_MAP +extern int gcore_map(int); +#endif + +#ifdef CONFIG_GCORE_CONV +extern int gcore_conv(int, const char *, bool, int); +#endif + +#endif /* _CONVERT_H */ diff --git a/gcore.tproj/corefile.c b/gcore.tproj/corefile.c index addcda4..b1e4421 100644 --- a/gcore.tproj/corefile.c +++ b/gcore.tproj/corefile.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Apple Inc. All rights reserved. + * Copyright (c) 2016-2018 Apple Inc. All rights reserved. */ #include "options.h" @@ -18,6 +18,7 @@ #include #include #include +#include native_mach_header_t * make_corefile_mach_header(void *data) @@ -50,6 +51,7 @@ make_coreinfo_command(native_mach_header_t *mh, void *data, const uuid_t aoutid, cc->cmdsize = sizeof (*cc); cc->version = 1; cc->type = proto_CORETYPE_USER; + cc->pageshift = (uint16_t)pageshift_host; cc->address = address; uuid_copy(cc->uuid, aoutid); cc->dyninfo = dyninfo; @@ -58,30 +60,63 @@ make_coreinfo_command(native_mach_header_t *mh, void *data, const uuid_t aoutid, return cc; } -static native_segment_command_t * -make_native_segment_command(void *data, mach_vm_offset_t vmaddr, mach_vm_size_t vmsize, off_t fileoff, size_t filesize, unsigned maxprot, unsigned initprot, unsigned comptype) +native_segment_command_t * +make_native_segment_command(void *data, const struct vm_range *vr, const struct file_range *fr, vm_prot_t maxprot, vm_prot_t initprot) { native_segment_command_t *sc = data; sc->cmd = NATIVE_LC_SEGMENT; sc->cmdsize = sizeof (*sc); - assert(vmsize); -#if defined(__LP64__) - sc->vmaddr = vmaddr; - sc->vmsize = vmsize; - sc->fileoff = fileoff; -#else - sc->vmaddr = (uintptr_t)vmaddr; - sc->vmsize = (size_t)vmsize; - sc->fileoff = (long)fileoff; -#endif - sc->filesize = filesize; + assert(V_SIZE(vr)); + sc->vmaddr = (unsigned long)V_ADDR(vr); + sc->vmsize = (unsigned long)V_SIZE(vr); + sc->fileoff = (unsigned long)F_OFF(fr); + sc->filesize = (unsigned long)F_SIZE(fr); sc->maxprot = maxprot; sc->initprot = initprot; sc->nsects = 0; - sc->flags = proto_SG_COMP_MAKE_FLAGS(comptype); + sc->flags = 0; return sc; } +static struct proto_coredata_command * +make_coredata_command(void *data, const struct vm_range *vr, const struct file_range *fr, const vm_region_submap_info_data_64_t *info, unsigned comptype, unsigned purgable) +{ + struct proto_coredata_command *cc = data; + cc->cmd = proto_LC_COREDATA; + cc->cmdsize = sizeof (*cc); + assert(V_SIZE(vr)); + cc->vmaddr = V_ADDR(vr); + cc->vmsize = V_SIZE(vr); + cc->fileoff = F_OFF(fr); + cc->filesize = F_SIZE(fr); + cc->maxprot = info->max_protection; + cc->prot = info->protection; + cc->flags = COMP_MAKE_FLAGS(comptype); + cc->share_mode = info->share_mode; + assert(purgable <= UINT8_MAX); + cc->purgable = (uint8_t)purgable; + assert(info->user_tag <= UINT8_MAX); + cc->tag = (uint8_t)info->user_tag; + cc->extp = info->external_pager; + return cc; +} + +static size_t +sizeof_segment_command(void) { + return opt->extended ? + sizeof (struct proto_coredata_command) : sizeof (native_segment_command_t); +} + +static struct load_command * +make_segment_command(void *data, const struct vm_range *vr, const struct file_range *fr, const vm_region_submap_info_data_64_t *info, unsigned comptype, int purgable) +{ + if (opt->extended) + make_coredata_command(data, vr, fr, info, comptype, purgable); + else + make_native_segment_command(data, vr, fr, info->max_protection, info->protection); + return data; +} + /* * Increment the mach-o header data when we succeed */ @@ -119,53 +154,81 @@ size_fileref_subregion(const struct subregion *s, struct size_core *sc) sc->memsize += S_SIZE(s); } -#ifdef CONFIG_REFSC static void size_fileref_region(const struct region *r, struct size_core *sc) { assert(0 == r->r_nsubregions); assert(!r->r_inzfodregion); - size_t cmdsize = cmdsize_fileref_command(r->r_fileref->fr_libent->le_pathname); + size_t cmdsize = cmdsize_fileref_command(r->r_fileref->fr_pathname); sc->headersize += cmdsize; sc->count++; sc->memsize += R_SIZE(r); } -#endif static struct proto_fileref_command * -make_fileref_command(void *data, const struct libent *le, mach_vm_offset_t vmaddr, mach_vm_size_t vmsize, off_t fileoff, off_t filesize, unsigned maxprot, unsigned initprot) +make_fileref_command(void *data, const char *pathname, const uuid_t uuid, + const struct vm_range *vr, const struct file_range *fr, + const vm_region_submap_info_data_64_t *info, unsigned purgable) { - struct proto_fileref_command *fr = data; + struct proto_fileref_command *fc = data; size_t len; - fr->cmd = proto_LC_FILEREF; - fr->cmdsize = sizeof (*fr); - if (0 != (len = strlen(le->le_pathname))) { + fc->cmd = proto_LC_FILEREF; + fc->cmdsize = sizeof (*fc); + if (0 != (len = strlen(pathname))) { /* * Strings live immediately after the * command, and are included in the cmdsize */ - fr->filename.offset = sizeof (*fr); - void *s = fr + 1; - strlcpy(s, le->le_pathname, ++len); // NUL-terminated for mmap sanity - fr->cmdsize += roundup(len, sizeof (long)); - assert(cmdsize_fileref_command(le->le_pathname) == fr->cmdsize); + fc->filename.offset = sizeof (*fc); + void *s = fc + 1; + strlcpy(s, pathname, ++len); // NUL-terminated for mmap sanity + fc->cmdsize += roundup(len, sizeof (long)); + assert(cmdsize_fileref_command(pathname) == fc->cmdsize); } - uuid_copy(fr->uuid, le->le_uuid); - fr->vmaddr = vmaddr; + /* + * A file reference allows different kinds of identifiers for + * the reference to be reconstructed. + */ + assert(info->external_pager); - assert(vmsize); - fr->vmsize = vmsize; - assert(fileoff >= 0); - fr->fileoff = fileoff; - fr->filesize = filesize; + if (!uuid_is_null(uuid)) { + uuid_copy(fc->id, uuid); + fc->flags = FREF_MAKE_FLAGS(kFREF_ID_UUID); + } else { + struct stat st; + if (-1 != stat(pathname, &st) && 0 != st.st_mtimespec.tv_sec) { + /* "little-endian format timespec structure" */ + struct timespec ts = st.st_mtimespec; + ts.tv_nsec = 0; // allow touch(1) to fix things + memset(fc->id, 0, sizeof(fc->id)); + memcpy(fc->id, &ts, sizeof(ts)); + fc->flags = FREF_MAKE_FLAGS(kFREF_ID_MTIMESPEC_LE); + } else + fc->flags = FREF_MAKE_FLAGS(kFREF_ID_NONE); + } - assert(maxprot & VM_PROT_READ); - fr->maxprot = maxprot; - fr->initprot = initprot; - return fr; + fc->vmaddr = V_ADDR(vr); + assert(V_SIZE(vr)); + fc->vmsize = V_SIZE(vr); + + assert(F_OFF(fr) >= 0); + fc->fileoff = F_OFF(fr); + fc->filesize = F_SIZE(fr); + + assert(info->max_protection & VM_PROT_READ); + fc->maxprot = info->max_protection; + fc->prot = info->protection; + + fc->share_mode = info->share_mode; + assert(purgable <= UINT8_MAX); + fc->purgable = (uint8_t)purgable; + assert(info->user_tag <= UINT8_MAX); + fc->tag = (uint8_t)info->user_tag; + fc->extp = info->external_pager; + return fc; } /* @@ -176,23 +239,26 @@ static walk_return_t write_fileref_subregion(const struct region *r, const struct subregion *s, struct write_segment_data *wsd) { assert(S_LIBENT(s)); - if (opt->debug && !issubregiontype(s, SEG_TEXT) && !issubregiontype(s, SEG_LINKEDIT)) + if (OPTIONS_DEBUG(opt, 1) && !issubregiontype(s, SEG_TEXT) && !issubregiontype(s, SEG_LINKEDIT)) printf("%s: unusual segment type %s from %s\n", __func__, S_MACHO_TYPE(s), S_FILENAME(s)); assert((r->r_info.max_protection & VM_PROT_READ) == VM_PROT_READ); assert((r->r_info.protection & VM_PROT_WRITE) == 0); const struct libent *le = S_LIBENT(s); - const struct proto_fileref_command *fc = make_fileref_command(wsd->wsd_lc, le, S_ADDR(s), S_SIZE(s), S_MACHO_FILEOFF(s), S_SIZE(s), r->r_info.max_protection, r->r_info.protection); + const struct file_range fr = { + .off = S_MACHO_FILEOFF(s), + .size = S_SIZE(s), + }; + const struct proto_fileref_command *fc = make_fileref_command(wsd->wsd_lc, le->le_pathname, le->le_uuid, S_RANGE(s), &fr, &r->r_info, r->r_purgable); + commit_load_command(wsd, (const void *)fc); - if (opt->debug > 1) { + if (OPTIONS_DEBUG(opt, 3)) { hsize_str_t hstr; printr(r, "ref '%s' %s (vm %llx-%llx, file offset %lld for %s)\n", S_FILENAME(s), S_MACHO_TYPE(s), (uint64_t)fc->vmaddr, (uint64_t)fc->vmaddr + fc->vmsize, (int64_t)fc->fileoff, str_hsize(hstr, fc->filesize)); } return WALK_CONTINUE; } -#ifdef CONFIG_REFSC - /* * Note that we may be asked to write reference segments whose protections * are rw- -- this -should- be ok as we don't convert the region to a file @@ -206,12 +272,18 @@ write_fileref_region(const struct region *r, struct write_segment_data *wsd) assert((r->r_info.max_protection & VM_PROT_READ) == VM_PROT_READ); assert(!r->r_inzfodregion); - const struct libent *le = r->r_fileref->fr_libent; - const struct proto_fileref_command *fc = make_fileref_command(wsd->wsd_lc, le, R_ADDR(r), R_SIZE(r), r->r_fileref->fr_offset, (size_t)R_SIZE(r), r->r_info.max_protection, r->r_info.protection); + const struct libent *le = r->r_fileref->fr_libent; + const char *pathname = r->r_fileref->fr_pathname; + const struct file_range fr = { + .off = r->r_fileref->fr_offset, + .size = R_SIZE(r), + }; + const struct proto_fileref_command *fc = make_fileref_command(wsd->wsd_lc, pathname, le ? le->le_uuid : UUID_NULL, R_RANGE(r), &fr, &r->r_info, r->r_purgable); + commit_load_command(wsd, (const void *)fc); - if (opt->debug > 1) { + if (OPTIONS_DEBUG(opt, 3)) { hsize_str_t hstr; - printr(r, "ref '%s' %s (vm %llx-%llx, file offset %lld for %s)\n", le->le_filename, "(type?)", (uint64_t)fc->vmaddr, (uint64_t)fc->vmaddr + fc->vmsize, (int64_t)fc->fileoff, str_hsize(hstr, fc->filesize)); + printr(r, "ref '%s' %s (vm %llx-%llx, file offset %lld for %s)\n", pathname, "(type?)", (uint64_t)fc->vmaddr, (uint64_t)fc->vmaddr + fc->vmsize, (int64_t)fc->fileoff, str_hsize(hstr, fc->filesize)); } return WALK_CONTINUE; } @@ -222,7 +294,6 @@ const struct regionop fileref_ops = { del_fileref_region, }; -#endif /* CONFIG_REFSC */ #pragma mark -- ZFOD segments written only to the header -- @@ -231,7 +302,7 @@ size_zfod_region(const struct region *r, struct size_core *sc) { assert(0 == r->r_nsubregions); assert(r->r_inzfodregion); - sc->headersize += sizeof (native_segment_command_t); + sc->headersize += sizeof_segment_command(); sc->count++; sc->memsize += R_SIZE(r); } @@ -242,8 +313,12 @@ write_zfod_region(const struct region *r, struct write_segment_data *wsd) assert(r->r_info.user_tag != VM_MEMORY_IOKIT); assert((r->r_info.max_protection & VM_PROT_READ) == VM_PROT_READ); - const void *sc = make_native_segment_command(wsd->wsd_lc, R_ADDR(r), R_SIZE(r), wsd->wsd_foffset, 0, r->r_info.max_protection, r->r_info.protection, 0); - commit_load_command(wsd, sc); + const struct file_range fr = { + .off = wsd->wsd_foffset, + .size = 0, + }; + make_segment_command(wsd->wsd_lc, R_RANGE(r), &fr, &r->r_info, 0, VM_PURGABLE_EMPTY); + commit_load_command(wsd, wsd->wsd_lc); return WALK_CONTINUE; } @@ -256,35 +331,25 @@ const struct regionop zfod_ops = { #pragma mark -- Regions containing data -- static walk_return_t -pwrite_memory(struct write_segment_data *wsd, const void *addr, size_t size, mach_vm_offset_t memaddr, size_t memsize) +pwrite_memory(struct write_segment_data *wsd, const void *addr, size_t size, const struct vm_range *vr) { assert(size); - int error = 0; - ssize_t nwritten = 0; + ssize_t nwritten; + const int error = bounded_pwrite(wsd->wsd_fd, addr, size, wsd->wsd_foffset, &wsd->wsd_nocache, &nwritten); - if (opt->sizebound > 0 && - wsd->wsd_foffset + (off_t)size > opt->sizebound) { - error = EFBIG; - } else { - nwritten = pwrite(wsd->wsd_fd, addr, size, wsd->wsd_foffset); - if (nwritten < 0) - error = errno; - } - - if (error || opt->debug > 1) { + if (error || OPTIONS_DEBUG(opt, 3)) { hsize_str_t hsz; - printf("%llx-%llx writing %ld bytes at offset %lld -> ", - memaddr, memaddr+memsize, size, wsd->wsd_foffset); + printvr(vr, "writing %ld bytes at offset %lld -> ", size, wsd->wsd_foffset); if (error) printf("err #%d - %s ", error, strerror(error)); else { printf("%s ", str_hsize(hsz, nwritten)); if (size != (size_t)nwritten) printf("[%zd - incomplete write!] ", nwritten); - else if (size != memsize) + else if (size != V_SIZE(vr)) printf("(%s in memory) ", - str_hsize(hsz, memsize)); + str_hsize(hsz, V_SIZE(vr))); } printf("\n"); } @@ -318,16 +383,16 @@ segment_compflags(compression_algorithm ca, unsigned *algnum) { switch (ca) { case COMPRESSION_LZ4: - *algnum = proto_SG_COMP_LZ4; + *algnum = kCOMP_LZ4; break; case COMPRESSION_ZLIB: - *algnum = proto_SG_COMP_ZLIB; + *algnum = kCOMP_ZLIB; break; case COMPRESSION_LZMA: - *algnum = proto_SG_COMP_LZMA; + *algnum = kCOMP_LZMA; break; case COMPRESSION_LZFSE: - *algnum = proto_SG_COMP_LZFSE; + *algnum = kCOMP_LZFSE; break; default: err(EX_SOFTWARE, "unsupported compression algorithm %x", ca); @@ -335,6 +400,190 @@ segment_compflags(compression_algorithm ca, unsigned *algnum) return 0; } +static bool +is_file_mapped_shared(const struct region *r) +{ + if (r->r_info.external_pager) + switch (r->r_info.share_mode) { + case SM_TRUESHARED: // sm=shm + case SM_SHARED: // sm=ali + case SM_SHARED_ALIASED: // sm=s/a + return true; + default: + break; + } + return false; +} + +static walk_return_t +map_memory_range(struct write_segment_data *wsd, const struct region *r, const struct vm_range *vr, struct vm_range *dp) +{ + if (r->r_incommregion) { + /* + * Special case: for commpage access, copy from our own address space. + */ + V_SETADDR(dp, 0); + V_SETSIZE(dp, V_SIZE(vr)); + + kern_return_t kr = mach_vm_allocate(mach_task_self(), &dp->addr, dp->size, VM_FLAGS_ANYWHERE); + if (KERN_SUCCESS != kr || 0 == dp->addr) { + err_mach(kr, r, "mach_vm_allocate c %llx-%llx", V_ADDR(vr), V_ENDADDR(vr)); + print_one_memory_region(r); + return WALK_ERROR; + } + if (OPTIONS_DEBUG(opt, 3)) + printr(r, "copying from self %llx-%llx\n", V_ADDR(vr), V_ENDADDR(vr)); + memcpy((void *)dp->addr, (const void *)V_ADDR(vr), V_SIZE(vr)); + return WALK_CONTINUE; + } + + if (!r->r_insharedregion && 0 == (r->r_info.protection & VM_PROT_READ)) { + assert(0 != (r->r_info.max_protection & VM_PROT_READ)); // simple_region_optimization() + + /* + * Special case: region that doesn't currently have read permission. + * (e.g. --x/r-x permissions with tag 64 - JS JIT generated code + * from com.apple.WebKit.WebContent) + */ + const mach_vm_offset_t pagesize_host = 1u << pageshift_host; + if (OPTIONS_DEBUG(opt, 3)) + printr(r, "unreadable (%s/%s), remap with read permission\n", + str_prot(r->r_info.protection), str_prot(r->r_info.max_protection)); + V_SETADDR(dp, 0); + V_SETSIZE(dp, V_SIZE(vr)); + vm_prot_t cprot, mprot; + kern_return_t kr = mach_vm_remap(mach_task_self(), &dp->addr, V_SIZE(dp), pagesize_host - 1, true, wsd->wsd_task, V_ADDR(vr), true, &cprot, &mprot, VM_INHERIT_NONE); + if (KERN_SUCCESS != kr) { + err_mach(kr, r, "mach_vm_remap() %llx-%llx", V_ADDR(vr), V_ENDADDR(vr)); + return WALK_ERROR; + } + assert(r->r_info.protection == cprot && r->r_info.max_protection == mprot); + kr = mach_vm_protect(mach_task_self(), V_ADDR(dp), V_SIZE(dp), false, VM_PROT_READ); + if (KERN_SUCCESS != kr) { + err_mach(kr, r, "mach_vm_protect() %llx-%llx", V_ADDR(vr), V_ENDADDR(vr)); + mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp)); + return WALK_ERROR; + } + return WALK_CONTINUE; + } + + /* + * Most segments with data are read here + */ + vm_offset_t data32 = 0; + mach_msg_type_number_t data32_count; + kern_return_t kr = mach_vm_read(wsd->wsd_task, V_ADDR(vr), V_SIZE(vr), &data32, &data32_count); + switch (kr) { + case KERN_SUCCESS: + V_SETADDR(dp, data32); + V_SETSIZE(dp, data32_count); + break; + case KERN_INVALID_ADDRESS: + if (!r->r_insharedregion && + (VM_MEMORY_SKYWALK == r->r_info.user_tag || is_file_mapped_shared(r))) { + if (OPTIONS_DEBUG(opt, 1)) { + /* not necessarily an error: mitigation below */ + tag_str_t tstr; + printr(r, "mach_vm_read() failed (%s) -- substituting zeroed region\n", str_tagr(tstr, r)); + if (OPTIONS_DEBUG(opt, 2)) + print_one_memory_region(r); + } + V_SETSIZE(dp, V_SIZE(vr)); + kr = mach_vm_allocate(mach_task_self(), &dp->addr, V_SIZE(dp), VM_FLAGS_ANYWHERE); + if (KERN_SUCCESS != kr || 0 == V_ADDR(dp)) + err_mach(kr, r, "mach_vm_allocate() z %llx-%llx", V_ADDR(vr), V_ENDADDR(vr)); + break; + } + /*FALLTHROUGH*/ + default: + err_mach(kr, r, "mach_vm_read() %llx-%llx", V_ADDR(vr), V_SIZE(vr)); + if (OPTIONS_DEBUG(opt, 1)) + print_one_memory_region(r); + break; + } + if (kr != KERN_SUCCESS) { + V_SETADDR(dp, 0); + return WALK_ERROR; + } + + /* + * Sometimes (e.g. searchd) we may not be able to fetch all the pages + * from the underlying mapped file, in which case replace those pages + * with zfod pages (at least they compress efficiently) rather than + * taking a SIGBUS when compressing them. + * + * XXX Perhaps we should just catch the SIGBUS, and if the faulting address + * is in the right range, substitute zfod pages and rerun region compression? + * Complex though, because the compression code may be multithreaded. + */ + if (!r->r_insharedregion && is_file_mapped_shared(r)) { + const mach_vm_offset_t pagesize_host = 1u << pageshift_host; + + if (r->r_info.pages_resident * pagesize_host == V_SIZE(dp)) + return WALK_CONTINUE; // all pages resident, so skip .. + + if (OPTIONS_DEBUG(opt, 2)) + printr(r, "probing %llu pages in mapped-shared file\n", V_SIZE(dp) / pagesize_host); + + kr = KERN_SUCCESS; + for (mach_vm_offset_t a = V_ADDR(dp); a < V_ENDADDR(dp); a += pagesize_host) { + + mach_msg_type_number_t pCount = VM_PAGE_INFO_BASIC_COUNT; + vm_page_info_basic_data_t pInfo; + + kr = mach_vm_page_info(mach_task_self(), a, VM_PAGE_INFO_BASIC, (vm_page_info_t)&pInfo, &pCount); + if (KERN_SUCCESS != kr) { + err_mach(kr, NULL, "mach_vm_page_info() at %llx", a); + break; + } + /* If the VM has the page somewhere, assume we can bring it back */ + if (pInfo.disposition & (VM_PAGE_QUERY_PAGE_PRESENT | VM_PAGE_QUERY_PAGE_REF | VM_PAGE_QUERY_PAGE_DIRTY)) + continue; + + /* Force the page to be fetched to see if it faults */ + mach_vm_size_t tsize = pagesize_host; + void *tmp = valloc((size_t)tsize); + const mach_vm_address_t vtmp = (mach_vm_address_t)tmp; + + switch (kr = mach_vm_read_overwrite(mach_task_self(), a, tsize, vtmp, &tsize)) { + case KERN_SUCCESS: + break; + case KERN_INVALID_ADDRESS: { + /* Content can't be found: replace it and the rest of the region with zero-fill pages */ + if (OPTIONS_DEBUG(opt, 2)) { + printr(r, "mach_vm_read_overwrite() failed after %llu pages -- substituting zfod\n", (a - V_ADDR(dp)) / pagesize_host); + print_one_memory_region(r); + } + mach_vm_address_t va = a; + kr = mach_vm_allocate(mach_task_self(), &va, V_ENDADDR(dp) - va, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE); + if (KERN_SUCCESS != kr) { + err_mach(kr, r, "mach_vm_allocate() %llx", a); + } else { + assert(a == va); + a = V_ENDADDR(dp); // no need to look any further + } + break; + } + default: + err_mach(kr, r, "mach_vm_overwrite() %llx", a); + break; + } + free(tmp); + if (KERN_SUCCESS != kr) + break; + } + if (KERN_SUCCESS != kr) { + kr = mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp)); + if (KERN_SUCCESS != kr && OPTIONS_DEBUG(opt, 1)) + err_mach(kr, r, "mach_vm_deallocate() pre %llx-%llx", V_ADDR(dp), V_ENDADDR(dp)); + V_SETADDR(dp, 0); + return WALK_ERROR; + } + } + + return WALK_CONTINUE; +} + static walk_return_t write_memory_range(struct write_segment_data *wsd, const struct region *r, mach_vm_offset_t vmaddr, mach_vm_offset_t vmsize) { @@ -344,10 +593,6 @@ write_memory_range(struct write_segment_data *wsd, const struct region *r, mach_ walk_return_t step = WALK_CONTINUE; do { - unsigned algorithm = 0; - void *dstbuf = NULL; - size_t filesize; - vmsize = resid; /* @@ -360,93 +605,72 @@ write_memory_range(struct write_segment_data *wsd, const struct region *r, mach_ vmsize = opt->chunksize; assert(vmsize <= INT32_MAX); - mach_vm_offset_t data; - mach_vm_offset_t data_count; + const struct vm_range vr = { + .addr = vmaddr, + .size = vmsize, + }; + struct vm_range d, *dp = &d; - kern_return_t kr; - const void *srcaddr; + step = map_memory_range(wsd, r, &vr, dp); + if (WALK_CONTINUE != step) + break; + assert(0 != V_ADDR(dp) && 0 != V_SIZE(dp)); + const void *srcaddr = (const void *)V_ADDR(dp); - if (r->r_incommregion) { - /* - * For commpage access, we just copy from our own address space. - */ - data = 0; - data_count = vmsize; - kr = mach_vm_allocate(mach_task_self(), &data, data_count, VM_FLAGS_ANYWHERE); - if (KERN_SUCCESS != kr || data == 0) { - err_mach(kr, r, "subregion %llx-%llx, mach_vm_allocate()", vmaddr, vmaddr + vmsize); - if (opt->debug) { - print_memory_region_header(); - ROP_PRINT(r); - } - break; - } - if (opt->debug) - printr(r, "subregion %llx-%llx, copying from self\n", vmaddr, vmaddr+vmsize); - srcaddr = (const void *)memcpy((void *)data, (void *)vmaddr, vmsize); - } else { - /* - * Most segments with data are mapped here - */ - vm_offset_t data32 = 0; - mach_msg_type_number_t data32_count; - kr = mach_vm_read(wsd->wsd_task, vmaddr, vmsize, &data32, &data32_count); - if (KERN_SUCCESS != kr || data32 == 0 || data32_count < vmsize) { - err_mach(kr, r, "subregion %llx-%llx, mach_vm_read()", vmaddr, vmaddr + vmsize); - if (opt->debug) { - print_memory_region_header(); - ROP_PRINT(r); - } - break; - } - data = data32; - data_count = data32_count; - mach_vm_behavior_set(mach_task_self(), data, data_count, VM_BEHAVIOR_SEQUENTIAL); - srcaddr = (const void *)data; - } + mach_vm_behavior_set(mach_task_self(), V_ADDR(dp), V_SIZE(dp), VM_BEHAVIOR_SEQUENTIAL); - assert(vmsize); + void *dstbuf = NULL; + unsigned algorithm = 0; + size_t filesize; - if (opt->compress) { - dstbuf = malloc((size_t)vmsize); - if (dstbuf) { - - filesize = compression_encode_buffer(dstbuf, (size_t)vmsize, srcaddr, (size_t)vmsize, NULL, opt->calgorithm); - - if (filesize > 0 && filesize < vmsize) { - srcaddr = dstbuf; - if (segment_compflags(opt->calgorithm, &algorithm) != 0) { - free(dstbuf); - mach_vm_deallocate(mach_task_self(), data, data_count); - return WALK_ERROR; - } - } else { - free(dstbuf); - dstbuf = NULL; - filesize = (size_t)vmsize; - } - } else - filesize = (size_t)vmsize; - } else - filesize = (size_t)vmsize; + if (opt->extended) { + dstbuf = malloc(V_SIZEOF(dp)); + if (dstbuf) { + filesize = compression_encode_buffer(dstbuf, V_SIZEOF(dp), srcaddr, V_SIZEOF(dp), NULL, opt->calgorithm); + if (filesize > 0 && filesize < V_SIZEOF(dp)) { + srcaddr = dstbuf; /* the data source is now heap, compressed */ + mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp)); + V_SETADDR(dp, 0); + if (segment_compflags(opt->calgorithm, &algorithm) != 0) { + free(dstbuf); + mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp)); + V_SETADDR(dp, 0); + step = WALK_ERROR; + break; + } + } else { + free(dstbuf); + dstbuf = NULL; + filesize = V_SIZEOF(dp); + } + } else + filesize = V_SIZEOF(dp); + assert(filesize <= V_SIZEOF(dp)); + } else + filesize = V_SIZEOF(dp); assert(filesize); - native_segment_command_t *sc = make_native_segment_command(wsd->wsd_lc, vmaddr, vmsize, wsd->wsd_foffset, filesize, r->r_info.max_protection, r->r_info.protection, algorithm); + const struct file_range fr = { + .off = wsd->wsd_foffset, + .size = filesize, + }; + make_segment_command(wsd->wsd_lc, &vr, &fr, &r->r_info, algorithm, r->r_purgable); + step = pwrite_memory(wsd, srcaddr, filesize, &vr); + if (dstbuf) + free(dstbuf); + if (V_ADDR(dp)) { + kern_return_t kr = mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp)); + if (KERN_SUCCESS != kr && OPTIONS_DEBUG(opt, 1)) + err_mach(kr, r, "mach_vm_deallocate() post %llx-%llx", V_ADDR(dp), V_SIZE(dp)); + } - assert((sc->flags == 0) ^ (sc->filesize < sc->vmsize)); - - step = pwrite_memory(wsd, srcaddr, sc->filesize, vmaddr, sc->vmsize); - if (dstbuf) - free(dstbuf); - mach_vm_deallocate(mach_task_self(), data, data_count); - - if (WALK_ERROR == step) - break; - commit_load_command(wsd, (const void *)sc); - resid -= vmsize; - vmaddr += vmsize; - } while (resid); + if (WALK_ERROR == step) + break; + commit_load_command(wsd, wsd->wsd_lc); + resid -= vmsize; + vmaddr += vmsize; + } while (resid); return step; } @@ -464,7 +688,7 @@ getvmsize_host(const task_t task, const struct region *r) if (pageshift_host != pageshift_app) { is_actual_size(task, r, &vmsize_host); - if (opt->debug && R_SIZE(r) != vmsize_host) + if (OPTIONS_DEBUG(opt, 1) && R_SIZE(r) != vmsize_host) printr(r, "(region size tweak: was %llx, is %llx)\n", R_SIZE(r), vmsize_host); } return vmsize_host; @@ -482,9 +706,7 @@ write_sparse_region(const struct region *r, struct write_segment_data *wsd) { assert(r->r_nsubregions); assert(!r->r_inzfodregion); -#ifdef CONFIG_REFSC assert(NULL == r->r_fileref); -#endif const mach_vm_size_t vmsize_host = getvmsize_host(wsd->wsd_task, r); walk_return_t step = WALK_CONTINUE; @@ -492,7 +714,7 @@ write_sparse_region(const struct region *r, struct write_segment_data *wsd) for (unsigned i = 0; i < r->r_nsubregions; i++) { const struct subregion *s = r->r_subregions[i]; - if (s->s_isfileref) + if (s->s_isuuidref) step = write_fileref_subregion(r, s, wsd); else { /* Write this one out as real data */ @@ -500,7 +722,7 @@ write_sparse_region(const struct region *r, struct write_segment_data *wsd) if (R_SIZE(r) != vmsize_host) { if (S_ADDR(s) + vmsize > R_ADDR(r) + vmsize_host) { vmsize = R_ADDR(r) + vmsize_host - S_ADDR(s); - if (opt->debug) + if (OPTIONS_DEBUG(opt, 3)) printr(r, "(subregion size tweak: was %llx, is %llx)\n", S_SIZE(s), vmsize); } @@ -518,9 +740,7 @@ write_vanilla_region(const struct region *r, struct write_segment_data *wsd) { assert(0 == r->r_nsubregions); assert(!r->r_inzfodregion); -#ifdef CONFIG_REFSC assert(NULL == r->r_fileref); -#endif const mach_vm_size_t vmsize_host = getvmsize_host(wsd->wsd_task, r); return write_memory_range(wsd, r, R_ADDR(r), vmsize_host); @@ -542,7 +762,7 @@ static unsigned long count_memory_range(mach_vm_offset_t vmsize) { unsigned long count; - if (opt->compress && opt->chunksize > 0) { + if (opt->chunksize) { count = (size_t)vmsize / opt->chunksize; if (vmsize != (mach_vm_offset_t)count * opt->chunksize) count++; @@ -559,7 +779,7 @@ static void size_sparse_subregion(const struct subregion *s, struct size_core *sc) { const unsigned long count = count_memory_range(S_SIZE(s)); - sc->headersize += sizeof (native_segment_command_t) * count; + sc->headersize += sizeof_segment_command() * count; sc->count += count; sc->memsize += S_SIZE(s); } @@ -572,12 +792,12 @@ size_sparse_region(const struct region *r, struct size_core *sc_sparse, struct s unsigned long entry_total = sc_sparse->count + sc_fileref->count; for (unsigned i = 0; i < r->r_nsubregions; i++) { const struct subregion *s = r->r_subregions[i]; - if (s->s_isfileref) + if (s->s_isuuidref) size_fileref_subregion(s, sc_fileref); else size_sparse_subregion(s, sc_sparse); } - if (opt->debug) { + if (OPTIONS_DEBUG(opt, 3)) { /* caused by compression breaking a large region into chunks */ entry_total = (sc_fileref->count + sc_sparse->count) - entry_total; if (entry_total > r->r_nsubregions) @@ -598,11 +818,11 @@ size_vanilla_region(const struct region *r, struct size_core *sc) assert(0 == r->r_nsubregions); const unsigned long count = count_memory_range(R_SIZE(r)); - sc->headersize += sizeof (native_segment_command_t) * count; + sc->headersize += sizeof_segment_command() * count; sc->count += count; sc->memsize += R_SIZE(r); - if (opt->debug && count > 1) + if (OPTIONS_DEBUG(opt, 3) && count != 1) printr(r, "range with 1 region, but requires %lu segment commands\n", count); } @@ -619,10 +839,8 @@ region_size_memory(struct region *r, void *arg) if (&zfod_ops == r->r_op) size_zfod_region(r, &ssd->ssd_zfod); -#ifdef CONFIG_REFSC else if (&fileref_ops == r->r_op) size_fileref_region(r, &ssd->ssd_fileref); -#endif else if (&sparse_ops == r->r_op) size_sparse_region(r, &ssd->ssd_sparse, &ssd->ssd_fileref); else if (&vanilla_ops == r->r_op) diff --git a/gcore.tproj/corefile.h b/gcore.tproj/corefile.h index d455d7e..2bdc43e 100644 --- a/gcore.tproj/corefile.h +++ b/gcore.tproj/corefile.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Apple Inc. All rights reserved. + * Copyright (c) 2016 Apple Inc. All rights reserved. */ #include "loader_additions.h" @@ -26,16 +26,22 @@ typedef struct segment_command native_segment_command_t; #define NATIVE_LC_SEGMENT LC_SEGMENT #endif +static __inline const struct load_command *next_lc(const struct load_command *lc) { + if (lc->cmdsize && (lc->cmdsize & 3) == 0) + return (const void *)((caddr_t)lc + lc->cmdsize); + return NULL; +} + +extern native_segment_command_t *make_native_segment_command(void *, const struct vm_range *, const struct file_range *, vm_prot_t, vm_prot_t); + extern native_mach_header_t *make_corefile_mach_header(void *); extern struct proto_coreinfo_command *make_coreinfo_command(native_mach_header_t *, void *, const uuid_t, uint64_t, uint64_t); -static __inline void -mach_header_inc_ncmds(native_mach_header_t *mh, uint32_t inc) { +static __inline void mach_header_inc_ncmds(native_mach_header_t *mh, uint32_t inc) { mh->ncmds += inc; } -static __inline void -mach_header_inc_sizeofcmds(native_mach_header_t *mh, uint32_t inc) { +static __inline void mach_header_inc_sizeofcmds(native_mach_header_t *mh, uint32_t inc) { mh->sizeofcmds += inc; } @@ -48,7 +54,7 @@ struct size_core { struct size_segment_data { struct size_core ssd_vanilla; /* full segments with data */ struct size_core ssd_sparse; /* sparse segments with data */ - struct size_core ssd_fileref; /* full & sparse segments with file references */ + struct size_core ssd_fileref; /* full & sparse segments with uuid file references */ struct size_core ssd_zfod; /* full segments with zfod pages */ }; @@ -57,6 +63,7 @@ struct write_segment_data { native_mach_header_t *wsd_mh; void *wsd_lc; int wsd_fd; + bool wsd_nocache; off_t wsd_foffset; off_t wsd_nwritten; }; diff --git a/gcore.tproj/dyld.c b/gcore.tproj/dyld.c index c8235b5..0ff3958 100644 --- a/gcore.tproj/dyld.c +++ b/gcore.tproj/dyld.c @@ -129,16 +129,6 @@ struct liblist { }; static STAILQ_HEAD(, liblist) libhead = STAILQ_HEAD_INITIALIZER(libhead); -static unsigned long -namehash(const char *nm) -{ - unsigned long result = 5381; - int c; - while (0 != (c = *nm++)) - result = (result * 33) ^ c; - return result; /* modified djb2 */ -} - static const struct libent * libent_lookup_bypathname_withhash(const char *nm, const unsigned long hash) { @@ -178,25 +168,33 @@ libent_lookup_first_bytype(uint32_t mhtype) } const struct libent * -libent_insert(const char *nm, const uuid_t uuid, uint64_t mhaddr, const native_mach_header_t *mh) +libent_insert(const char *rawnm, const uuid_t uuid, uint64_t mhaddr, const native_mach_header_t *mh, const struct vm_range *vr, mach_vm_offset_t objoff) { const struct libent *le = libent_lookup_byuuid(uuid); if (NULL != le) return le; // disallow multiple names for the same uuid - unsigned long nmhash = namehash(nm); + char *nm = realpath(rawnm, NULL); + if (NULL == nm) + nm = strdup(rawnm); + const unsigned long nmhash = simple_namehash(nm); le = libent_lookup_bypathname_withhash(nm, nmhash); - if (NULL != le) + if (NULL != le) { + free(nm); return le; + } - if (opt->debug > 3) { + if (OPTIONS_DEBUG(opt, 3)) { uuid_string_t uustr; uuid_unparse_lower(uuid, uustr); - printf("[adding <'%s', %s, 0x%llx, %p>]\n", nm, uustr, mhaddr, mh); + printf("[adding <'%s', %s, 0x%llx, %p", nm, uustr, mhaddr, mh); + if (vr) + printf(" (%llx-%llx)", V_ADDR(vr), V_ENDADDR(vr)); + printf(">]\n"); } struct liblist *ll = malloc(sizeof (*ll)); ll->ll_namehash = nmhash; - ll->ll_entry.le_pathname = strdup(nm); + ll->ll_entry.le_pathname = nm; ll->ll_entry.le_filename = strrchr(ll->ll_entry.le_pathname, '/'); if (NULL == ll->ll_entry.le_filename) ll->ll_entry.le_filename = ll->ll_entry.le_pathname; @@ -205,7 +203,13 @@ libent_insert(const char *nm, const uuid_t uuid, uint64_t mhaddr, const native_m uuid_copy(ll->ll_entry.le_uuid, uuid); ll->ll_entry.le_mhaddr = mhaddr; ll->ll_entry.le_mh = mh; - + if (vr) + ll->ll_entry.le_vr = *vr; + else { + V_SETADDR(&ll->ll_entry.le_vr, MACH_VM_MAX_ADDRESS); + V_SETSIZE(&ll->ll_entry.le_vr, 0); + } + ll->ll_entry.le_objoff = objoff; STAILQ_INSERT_HEAD(&libhead, ll, ll_linkage); return &ll->ll_entry; @@ -216,7 +220,7 @@ libent_build_nametable(task_t task, dyld_process_info dpi) { __block bool valid = true; - _dyld_process_info_for_each_image(dpi, ^(uint64_t mhaddr, const uuid_t uuid, const char *path) { + _dyld_process_info_for_each_image(dpi, ^(uint64_t mhaddr, const uuid_t uuid, const char *path) { if (valid) { native_mach_header_t *mh = copy_dyld_image_mh(task, mhaddr, path); if (mh) { @@ -225,6 +229,11 @@ libent_build_nametable(task_t task, dyld_process_info dpi) */ const size_t mhlen = sizeof (*mh) + mh->sizeofcmds; const struct load_command *lc = (const void *)(mh + 1); + struct vm_range vr = { + .addr = MACH_VM_MAX_ADDRESS, + .size = 0 + }; + mach_vm_offset_t objoff = MACH_VM_MAX_ADDRESS; for (unsigned n = 0; n < mh->ncmds; n++) { if (((uintptr_t)lc & 0x3) != 0 || @@ -233,17 +242,73 @@ libent_build_nametable(task_t task, dyld_process_info dpi) valid = false; break; } - if (lc->cmdsize) - lc = (const void *)((caddr_t)lc + lc->cmdsize); - else + switch (lc->cmd) { + case NATIVE_LC_SEGMENT: { + const native_segment_command_t *sc = (const void *)lc; + + char scsegname[17]; + strlcpy(scsegname, sc->segname, sizeof (scsegname)); + + if (0 == sc->vmaddr && + strcmp(scsegname, SEG_PAGEZERO) == 0) + break; + + /* + * -Depends- on finding a __TEXT segment first + * which implicitly maps the mach header too + */ + + if (MACH_VM_MAX_ADDRESS == objoff) { + if (strcmp(scsegname, SEG_TEXT) == 0) { + objoff = mhaddr - sc->vmaddr; + V_SETADDR(&vr, mhaddr); + V_SETSIZE(&vr, sc->vmsize); + } else { + printf("%s: expected %s segment, found %s\n", path, SEG_TEXT, scsegname); + valid = false; + break; + } + } + + mach_vm_offset_t lo = sc->vmaddr + objoff; + mach_vm_offset_t hi = lo + sc->vmsize; + + if (V_SIZE(&vr)) { + if (lo < V_ADDR(&vr)) { + mach_vm_offset_t newsize = V_SIZE(&vr) + (V_ADDR(&vr) - lo); + V_SETSIZE(&vr, newsize); + V_SETADDR(&vr, lo); + } + if (hi > V_ENDADDR(&vr)) { + V_SETSIZE(&vr, (hi - V_ADDR(&vr))); + } + } else { + V_SETADDR(&vr, lo); + V_SETSIZE(&vr, hi - lo); + } + assert(lo >= V_ADDR(&vr) && hi <= V_ENDADDR(&vr)); + } break; +#if defined(RDAR_28040018) + case LC_ID_DYLINKER: + if (MH_DYLINKER == mh->filetype) { + /* workaround: the API doesn't always return the right name */ + const struct dylinker_command *dc = (const void *)lc; + path = dc->name.offset + (const char *)dc; + } + break; +#endif + default: + break; + } + if (NULL == (lc = next_lc(lc))) break; } - if (valid) - (void) libent_insert(path, uuid, mhaddr, mh); + if (valid) + (void) libent_insert(path, uuid, mhaddr, mh, &vr, objoff); } } }); - if (opt->debug) + if (OPTIONS_DEBUG(opt, 3)) printf("nametable %sconstructed\n", valid ? "" : "NOT "); return valid; } diff --git a/gcore.tproj/dyld.h b/gcore.tproj/dyld.h index cac1409..02ded2a 100644 --- a/gcore.tproj/dyld.h +++ b/gcore.tproj/dyld.h @@ -4,6 +4,7 @@ #include "options.h" #include "corefile.h" +#include "utils.h" #include #include @@ -17,17 +18,18 @@ struct libent { char *le_pathname; uuid_t le_uuid; uint64_t le_mhaddr; // address in target process - const native_mach_header_t *le_mh; // cached copy in this address space + const native_mach_header_t *le_mh; // copy mapped into this address space + struct vm_range le_vr; // vmaddr, vmsize bounds in target process + mach_vm_offset_t le_objoff; // offset from le_mhaddr to first __TEXT seg }; extern const struct libent *libent_lookup_byuuid(const uuid_t); extern const struct libent *libent_lookup_first_bytype(uint32_t); -extern const struct libent *libent_insert(const char *, const uuid_t, uint64_t, const native_mach_header_t *); +extern const struct libent *libent_insert(const char *, const uuid_t, uint64_t, const native_mach_header_t *, const struct vm_range *, mach_vm_offset_t); extern bool libent_build_nametable(task_t, dyld_process_info); extern dyld_process_info get_task_dyld_info(task_t); extern bool get_sc_uuid(dyld_process_info, uuid_t); extern void free_task_dyld_info(dyld_process_info); - #endif /* _DYLD_H */ diff --git a/gcore.tproj/dyld_shared_cache.c b/gcore.tproj/dyld_shared_cache.c index 91aefa5..c65ffca 100644 --- a/gcore.tproj/dyld_shared_cache.c +++ b/gcore.tproj/dyld_shared_cache.c @@ -24,6 +24,7 @@ static const size_t dyld_cache_header_size = sizeof (struct copied_dyld_cache_he /* * The shared cache must both contain the magic ID and * match the uuid we discovered via dyld's information. + * Assumes that the dyld_cache_header grows in a binary compatible fashion. */ bool get_uuid_from_shared_cache_mapping(const void *addr, size_t size, uuid_t out) @@ -31,7 +32,7 @@ get_uuid_from_shared_cache_mapping(const void *addr, size_t size, uuid_t out) const struct copied_dyld_cache_header *ch = addr; if (size < sizeof (*ch)) return false; - static const char prefix[] = "dyld_v1 "; + static const char prefix[] = "dyld_v"; if (strncmp(ch->magic, prefix, strlen(prefix)) != 0) return false; uuid_copy(out, ch->uuid); @@ -73,14 +74,14 @@ shared_cache_filename(const uuid_t uu) int d = open(fe->fts_accpath, O_RDONLY); if (-1 == d) { - if (opt->debug) + if (OPTIONS_DEBUG(opt, 1)) printf("%s: cannot open - %s\n", fe->fts_accpath, strerror(errno)); continue; } void *addr = mmap(NULL, dyld_cache_header_size, PROT_READ, MAP_PRIVATE, d, 0); close(d); if ((void *)-1 == addr) { - if (opt->debug) + if (OPTIONS_DEBUG(opt, 1)) printf("%s: cannot mmap - %s\n", fe->fts_accpath, strerror(errno)); continue; } @@ -89,7 +90,7 @@ shared_cache_filename(const uuid_t uu) if (get_uuid_from_shared_cache_mapping(addr, dyld_cache_header_size, scuuid)) { if (uuid_compare(uu, scuuid) == 0) nm = strdup(fe->fts_accpath); - else if (opt->debug) { + else if (OPTIONS_DEBUG(opt, 3)) { uuid_string_t scstr; uuid_unparse_lower(scuuid, scstr); printf("%s: shared cache mismatch (%s)\n", fe->fts_accpath, scstr); diff --git a/gcore.tproj/gcore-entitlements.plist b/gcore.tproj/gcore-entitlements.plist new file mode 100644 index 0000000..39c14ef --- /dev/null +++ b/gcore.tproj/gcore-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.debugger.root + + + diff --git a/gcore.tproj/gcore-internal.1 b/gcore.tproj/gcore-internal.1 new file mode 100644 index 0000000..923f3e9 --- /dev/null +++ b/gcore.tproj/gcore-internal.1 @@ -0,0 +1,201 @@ +.Dd 9/29/16 +.Dt gcore-internal 1 +.Os Darwin +.Sh NAME +.Nm gcore +.Nd get core images of running processes and corpses +.Sh SYNOPSIS +.Nm +.Op Fl x +.Op Fl F +.Op Fl C +.Op Fl Z Ar compopts +.Op Fl t Ar threshold +.Op Fl d +.Ar args ... +.Nm +.Sy conv +.Op Fl L Ar searchpath +.Op Fl z +.Op Fl s +.Op Fl v +.Op Fl d +.Ar incore outcore +.Nm +.Sy map +.Ar corefile +.Nm +.Sy fref +.Ar corefile +.Sh DESCRIPTION +For an introduction to this command and its options, see +.Xr gcore 1 . +This page describes various experimental capabilities +of the +.Nm +command intended, for the moment, for internal use only. +.Pp +The following set of additional flags are available: +.Bl -tag -width Fl +.It Fl x +Create extended (compact) core files. With this flag, +.Nm +elides empty and unmapped regions from the dump, and uses +metadata from the VM system and +.Xr dyld 1 +to minimize the size of the dump, writing compressed versions of +the active regions of the address space into the dump file. +.Nm +also records file references to the various files mapped into the +address space, potentially including the shared cache, to +avoid duplicating content already present on the filesystem. +Taken together, these techniques can lead to very significant +space savings for the core file, particularly for smaller programs. +.It Fl F +Normally when +.Fl x +is specified, +.Nm +makes conservative assumptions about which files should be +incorporated into the dump as file references so that the +full core file can be recreated later. This flag attempts to make +.Em every +mapped file into a file reference. While this can occasionally +be useful for applications that map many files into their address space, +it may be +.Em extremely +difficult to recreate the process image as a result. +Use cautiously! +.El +.Pp +The remaining options are more relevant to the +.Nm +maintainers: +.Bl -tag -width Fl +.It Fl C +Forcibly generate a corpse for the process, even if the process is suspended. +.It Fl Z Ar compopts +Specify compression options e.g. algorithm and chunksize. +.It Fl t Ar threshold +Set the threshold at which I/O caching is disabled to +.Ar threshold +KiBytes. +.It Fl d +Enable debugging of the +.Nm +command. +.El +.Pp +If the +.Ar pid +value is specified as 0, +.Nm +assumes it has been passed a corpse port by its parent; +if so it will generate a core dump for that corpse. The +.Fl c +flag may not be used in this case, as the process context may no longer exist. +.Pp +The +.Nm +command supports several sub-commands that can be +used with extended core files created using the +.Fl x +flag. These are: +.Bl -tag -width frefs +.\" -compact -offset indent +.Pp +.It Sy conv +Copy and convert a core file to the "pre-coreinfo" format +compatible with +.Xr lldb(1) . +This operation reads the input core file dereferencing any file +references it contains by copying the content +and decompressing any compressed data into the output core file. +This conversion usually makes the core file substantially larger. +.Pp +By default, files to be dereferenced must be accessible on the +local filesystem by the same relative paths as they were originally recorded +when the dump was taken. +Files that are Mach-O objects containing UUIDs are required to match +the UUIDs recorded at the time the core dump was taken. +Files are otherwise only checked for matching modification times, and +thus can easily be "forged" using +.Xr touch 1 . +.Pp +Several flags can be used with the conversion: +.Pp +.Bl -tag -width Fl +.It Fl L Ar searchpath +When processing file references, +look for the pathnames in the directories specified in +.Ar searchpath . +These should be specified as a colon-separated +list of base directories which will be prepended to each pathname in turn +for each file reference. +.It Fl z +During conversion, if any mapped file +identified by modification time +cannot be located, substitute zeroed memory. +.It Fl s +When processing file references, +try looking for the pathname through +.Xr dsymForUUID 1 +before searching locally. +.It Fl v +Report progress on the conversion as it proceeds. +.It Fl d +Enable debugging of the +.Sy conv +subcommand. +.El +.It Sy map +Print a representation of the address space contained in the core file. +.Pp +.It Sy frefs +Print a list of files corresponding to the file references +in the core file. +Can be used to capture the set of files needed to bind the file references +into the core file at a later time. +.El +.Sh BUGS +.Pp +When using the +.Fl x +flag, +.Nm +will likely incorporate a reference to the shared cache into +.Ar corefile +including the UUID of that cache. +On some platforms, the cache is created when the release is built +and since it resides on a read-only root filesystem it should +generally be easy to retrieve. +However on the desktop, the lifecycle of this cache is managed locally +e.g. with +.Xr update_dyld_shared_cache 1 . +When this cache is recreated it is given a new UUID, the directory +entry for the old cache is removed, and the same filename +is used for the new cache. +Thus when the last named copy of the shared cache is removed from the +filesystem, extended core files that contain references to that cache +can no longer be converted. +.Pp +When using the +.Fl x +flag, +.Nm +may be unable to locate the currently mapped shared cache in the filesystem. +In this case +.Nm +does not generate any file references to the content of the +shared cache; it just copies as much of the content +as is needed from the memory image into the core file. +This may lead to substantially larger core files than expected. +.Pp +It would be nice if +.Xr lldb 1 +could examine extended core files directly i.e. without the conversion step. +.Pp +.Sh SEE ALSO +.Xr dyld 1 , +.Xr update_dyld_shared_cache 1 , +.Xr dsymForUUID 1 diff --git a/gcore.tproj/loader_additions.h b/gcore.tproj/loader_additions.h index 47e3054..16cf5b5 100644 --- a/gcore.tproj/loader_additions.h +++ b/gcore.tproj/loader_additions.h @@ -10,56 +10,94 @@ /* * Something like this should end up in */ -#define proto_LC_COREINFO 0x40 /* unofficial value!! */ + +#define proto_LC_COREINFO 0x140 /* unofficial value!! */ #define proto_CORETYPE_KERNEL 1 -#define proto_CORETYPE_USER 2 +#define proto_CORETYPE_USER 2 #define proto_CORETYPE_IBOOT 3 struct proto_coreinfo_command { uint32_t cmd; /* LC_COREINFO */ uint32_t cmdsize; /* total size of this command */ uint32_t version; /* currently 1 */ - /* - * 'type' determines the content of the corefile; interpretation - * of the address and uuid fields are specific to the type. - */ - uint32_t type; /* CORETYPE_KERNEL, CORETYPE_USER etc. */ + uint16_t type; /* CORETYPE_KERNEL, CORETYPE_USER etc. */ + uint16_t pageshift; /* log2 host pagesize */ + /* content & interpretation depends on 'type' field */ uint64_t address; /* load address of "main binary" */ uint8_t uuid[16]; /* uuid of "main binary" */ uint64_t dyninfo; /* dynamic modules info */ }; +#define proto_LC_FILEREF 0x141 /* unofficial value! */ + +#define FREF_ID_SHIFT 0 +#define FREF_ID_MASK 0x7 /* up to 8 flavors */ + +typedef enum { + kFREF_ID_NONE = 0, /* file has no available verifying ID */ + kFREF_ID_UUID = 1, /* file has an associated UUID */ + kFREF_ID_MTIMESPEC_LE = 2, /* file has a specific mtime */ + /* one day: file has a computed hash? */ +} fref_type_t; + +#define FREF_ID_TYPE(f) ((fref_type_t)(((f) >> FREF_ID_SHIFT) & FREF_ID_MASK)) +#define FREF_MAKE_FLAGS(t) (((t) & FREF_ID_MASK) << FREF_ID_SHIFT) + +struct proto_fileref_command { + uint32_t cmd; /* LC_FILEREF */ + uint32_t cmdsize; + union lc_str filename; /* filename these bits come from */ + uint8_t id[16]; /* uuid, size or hash etc. */ + uint64_t vmaddr; /* memory address of this segment */ + uint64_t vmsize; /* memory size of this segment */ + uint64_t fileoff; /* file offset of this segment */ + uint64_t filesize; /* amount to map from the file */ + vm_prot_t maxprot; /* maximum VM protection */ + vm_prot_t prot; /* current VM protection */ + uint32_t flags; + uint8_t share_mode; /* SM_COW etc. */ + uint8_t purgable; /* VM_PURGABLE_NONVOLATILE etc. */ + uint8_t tag; /* VM_MEMORY_MALLOC etc. */ + uint8_t extp; /* external pager */ +}; + +#define proto_LC_COREDATA 0x142 /* unofficial value! */ + /* * These are flag bits for the segment_command 'flags' field. */ -#define proto_SG_COMP_ALG_MASK 0x7 +#define COMP_ALG_MASK 0x7 /* carve out 3 bits for an enum i.e. allow for 7 flavors */ -#define proto_SG_COMP_ALG_SHIFT 4 /* (bottom 4 bits taken) */ +#define COMP_ALG_SHIFT 4 /* (bottom 4 bits taken) */ /* zero -> no compression */ -#define proto_SG_COMP_LZ4 1 /* 0x100 */ -#define proto_SG_COMP_ZLIB 2 /* 0x205 */ -#define proto_SG_COMP_LZMA 3 /* 0x306 */ -#define proto_SG_COMP_LZFSE 4 /* 0x801 */ +typedef enum { + kCOMP_NONE = 0, + kCOMP_LZ4 = 1, /* 0x100 */ + kCOMP_ZLIB = 2, /* 0x205 */ + kCOMP_LZMA = 3, /* 0x306 */ + kCOMP_LZFSE = 4, /* 0x801 */ +} compression_flavor_t; -#define proto_SG_COMP_ALG_TYPE(flags) (((flags) >> proto_SG_COMP_ALG_SHIFT) & proto_SG_COMP_ALG_MASK) -#define proto_SG_COMP_MAKE_FLAGS(type) (((type) & proto_SG_COMP_ALG_MASK) << proto_SG_COMP_ALG_SHIFT) +#define COMP_ALG_TYPE(f) ((compression_flavor_t)((f) >> COMP_ALG_SHIFT) & COMP_ALG_MASK) +#define COMP_MAKE_FLAGS(t) (((t) & COMP_ALG_MASK) << COMP_ALG_SHIFT) -#define proto_LC_FILEREF 0x41 /* unofficial value! */ - -struct proto_fileref_command { - uint32_t cmd; /* LC_FILEREF */ +struct proto_coredata_command { + uint32_t cmd; /* LC_COREDATA */ uint32_t cmdsize; - union lc_str filename; /* filename these bits come from */ - uint8_t uuid[16]; /* uuid if known */ - uint64_t vmaddr; /* memory address of this segment */ - uint64_t vmsize; /* memory size of this segment */ - uint64_t fileoff; /* file offset of this segment */ - uint64_t filesize; /* amount to map from the file */ - vm_prot_t maxprot; /* maximum VM protection */ - vm_prot_t initprot; /* initial VM protection */ + uint64_t vmaddr; /* memory address of this segment */ + uint64_t vmsize; /* memory size of this segment */ + uint64_t fileoff; /* file offset of this segment */ + uint64_t filesize; /* amount to map from the file */ + vm_prot_t maxprot; /* maximum VM protection */ + vm_prot_t prot; /* current VM protection */ + uint32_t flags; + uint8_t share_mode; /* SM_COW etc. */ + uint8_t purgable; /* VM_PURGABLE_NONVOLATILE etc. */ + uint8_t tag; /* VM_MEMORY_MALLOC etc. */ + uint8_t extp; /* external pager */ }; #endif /* _LOADER_ADDITIONS_H */ diff --git a/gcore.tproj/main.c b/gcore.tproj/main.c index d434468..64be227 100644 --- a/gcore.tproj/main.c +++ b/gcore.tproj/main.c @@ -7,6 +7,7 @@ #include "corefile.h" #include "vanilla.h" #include "sparse.h" +#include "convert.h" #include #include @@ -71,7 +72,7 @@ get_bsdinfo(pid_t pid) } static char * -format_gcore_name(const char *fmt, const struct proc_bsdinfo *pbi) +format_gcore_name(const char *fmt, pid_t pid, uid_t uid, const char *nm) { __block size_t resid = MAXPATHLEN; __block char *p = calloc(1, resid); @@ -93,7 +94,7 @@ format_gcore_name(const char *fmt, const struct proc_bsdinfo *pbi) return s - str; }; - ptrdiff_t (^outint)(int value)= ^(int value) { + ptrdiff_t (^outint)(int value) = ^(int value) { char id[11]; snprintf(id, sizeof (id), "%u", value); return outstr(id); @@ -123,14 +124,13 @@ format_gcore_name(const char *fmt, const struct proc_bsdinfo *pbi) outchar(c); break; case 'P': - outint(pbi->pbi_pid); + outint(pid); break; case 'U': - outint(pbi->pbi_uid); + outint(uid); break; case 'N': - outstr(pbi->pbi_name[0] ? - pbi->pbi_name : pbi->pbi_comm); + outstr(nm); break; case 'T': outtstamp(); // ISO 8601 format @@ -152,17 +152,154 @@ done: return out; } +static char * +make_gcore_path(char **corefmtp, pid_t pid, uid_t uid, const char *nm) +{ + char *corefmt = *corefmtp; + if (NULL == corefmt) { + const char defcore[] = "%N-%P-%T"; + if (NULL == (corefmt = kern_corefile())) + corefmt = strdup(defcore); + else { + // use the same directory as kern.corefile + char *p = strrchr(corefmt, '/'); + if (NULL != p) { + *p = '\0'; + size_t len = strlen(corefmt) + strlen(defcore) + 2; + char *buf = malloc(len); + snprintf(buf, len, "%s/%s", corefmt, defcore); + free(corefmt); + corefmt = buf; + } + if (OPTIONS_DEBUG(opt, 3)) + printf("corefmt '%s'\n", corefmt); + } + } + char *path = format_gcore_name(corefmt, pid, uid, nm); + free(corefmt); + *corefmtp = NULL; + return path; +} + +static bool proc_same_data_model(const struct proc_bsdinfo *pbi) { +#if defined(__LP64__) + return (pbi->pbi_flags & PROC_FLAG_LP64) != 0; +#else + return (pbi->pbi_flags & PROC_FLAG_LP64) == 0; +#endif +} + +static bool task_same_data_model(const task_flags_info_data_t *tfid) { +#if defined(__LP64__) + return (tfid->flags & TF_LP64) != 0; +#else + return (tfid->flags & TF_LP64) == 0; +#endif +} + +/* + * Change credentials for writing out the file + */ +static void +change_credentials(gid_t uid, uid_t gid) +{ + if ((getgid() != gid && -1 == setgid(gid)) || + (getuid() != uid && -1 == setuid(uid))) + errc(EX_NOPERM, errno, "insufficient privilege"); + if (uid != getuid() || gid != getgid()) + err(EX_OSERR, "wrong credentials"); +} + +static int +openout(const char *corefname, char **coretname, struct stat *st) +{ + const int tfd = open(corefname, O_WRONLY); + if (-1 == tfd) { + if (ENOENT == errno) { + /* + * Arrange for a core file to appear "atomically": write the data + * to the file + ".tmp" suffix, then fchmod and rename it into + * place once the dump completes successfully. + */ + const size_t nmlen = strlen(corefname) + 4 + 1; + char *tnm = malloc(nmlen); + snprintf(tnm, nmlen, "%s.tmp", corefname); + const int fd = open(tnm, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (-1 == fd || -1 == fstat(fd, st)) + errc(EX_CANTCREAT, errno, "%s", tnm); + if (!S_ISREG(st->st_mode) || 1 != st->st_nlink) + errx(EX_CANTCREAT, "%s: invalid attributes", tnm); + *coretname = tnm; + return fd; + } else + errc(EX_CANTCREAT, errno, "%s", corefname); + } else if (-1 == fstat(tfd, st)) { + close(tfd); + errx(EX_CANTCREAT, "%s: fstat", corefname); + } else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { + /* + * Write dump to a device, no rename! + */ + *coretname = NULL; + return tfd; + } else { + close(tfd); + errc(EX_CANTCREAT, EEXIST, "%s", corefname); + } +} + +static int +closeout(int fd, int ecode, char *corefname, char *coretname, const struct stat *st) +{ + if (0 != ecode && !opt->preserve && S_ISREG(st->st_mode)) + ftruncate(fd, 0); // limit large file clutter + if (0 == ecode && S_ISREG(st->st_mode)) + fchmod(fd, 0400); // protect core files + if (-1 == close(fd)) { + warnc(errno, "%s: close", coretname ? coretname : corefname); + ecode = EX_OSERR; + } + if (NULL != coretname) { + if (0 == ecode && -1 == rename(coretname, corefname)) { + warnc(errno, "cannot rename %s to %s", coretname, corefname); + ecode = EX_NOPERM; + } + free(coretname); + } + if (corefname) + free(corefname); + return ecode; +} + const char *pgm; const struct options *opt; -int -main(int argc, char *const *argv) -{ - if (NULL == (pgm = strrchr(*argv, '/'))) - pgm = *argv; - else - pgm++; +static const size_t oneK = 1024; +static const size_t oneM = oneK * oneK; +#define LARGEST_CHUNKSIZE INT32_MAX +#define DEFAULT_COMPRESSION_CHUNKSIZE (16 * oneM) +#define DEFAULT_NC_THRESHOLD (17 * oneK) + +static struct options options = { + .corpsify = 0, + .suspend = 0, + .preserve = 0, + .verbose = 0, +#ifdef CONFIG_DEBUG + .debug = 0, +#endif + .extended = 0, + .sizebound = 0, + .chunksize = 0, + .calgorithm = COMPRESSION_LZFSE, + .ncthresh = DEFAULT_NC_THRESHOLD, + .dsymforuuid = 0, +}; + +static int +gcore_main(int argc, char *const *argv) +{ #define ZOPT_ALG (0) #define ZOPT_CHSIZE (ZOPT_ALG + 1) @@ -175,13 +312,15 @@ main(int argc, char *const *argv) err_set_exit_b(^(int eval) { if (EX_USAGE == eval) { fprintf(stderr, - "usage:\n\t%s [-s] [-v] [[-o file] | [-c pathfmt ]] [-b size] " + "usage:\t%s [-s] [-v] [[-o file] | [-c pathfmt ]] [-b size] " #if DEBUG - "[-d] [-n] [-i] [-p] [-S] [-z] [-C] " - "[-Z compression-options] " -#ifdef CONFIG_REFSC - "[-R] " +#ifdef CONFIG_DEBUG + "[-d] " #endif + "[-x] [-C] " + "[-Z compression-options] " + "[-t size] " + "[-F] " #endif "pid\n", pgm); #if DEBUG @@ -197,33 +336,11 @@ main(int argc, char *const *argv) char *corefmt = NULL; char *corefname = NULL; - const size_t oneM = 1024 * 1024; - -#define LARGEST_CHUNKSIZE INT32_MAX -#define DEFAULT_COMPRESSION_CHUNKSIZE (16 * oneM) - - struct options options = { - .corpse = 0, - .suspend = 0, - .preserve = 0, - .verbose = 0, - .debug = 0, - .dryrun = 0, - .sparse = 0, - .sizebound = 0, - .coreinfo = 0, -#ifdef OPTIONS_REFSC - .scfileref = 0, -#endif - .compress = 0, - .chunksize = LARGEST_CHUNKSIZE, - .calgorithm = COMPRESSION_LZFSE, - }; int c; char *sopts, *value; - while ((c = getopt(argc, argv, "inmvdszpCSRZ:o:c:b:")) != -1) { + while ((c = getopt(argc, argv, "vdsxCFZ:o:c:b:t:")) != -1) { switch (c) { /* @@ -257,44 +374,23 @@ main(int argc, char *const *argv) /* * dev and debugging help */ - case 'n': /* write the core file to /dev/null */ - options.dryrun++; - break; +#ifdef CONFIG_DEBUG case 'd': /* debugging */ options.debug++; options.verbose++; options.preserve++; break; - case 'p': /* preserve partial core file (even if errors) */ - options.preserve++; - break; - +#endif /* * Remaining options are experimental and/or * affect the content of the core file */ - case 'i': /* include LC_COREINFO data */ - options.coreinfo++; + case 'x': /* write extended format (small) core files */ + options.extended++; + options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE; break; - case 'C': /* corpsify rather than suspend */ - options.corpse++; - break; -#ifdef CONFIG_REFSC - case 'R': /* include the shared cache by reference */ - options.scfileref++; - options.coreinfo++; - break; -#endif - case 'S': /* use dyld info to control the content */ - options.sparse++; - options.coreinfo++; - break; - case 'z': /* create compressed LC_SEGMENT* segments */ - if (0 == options.compress) { - options.compress++; - options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE; - } - options.coreinfo++; + case 'C': /* forcibly corpsify rather than suspend */ + options.corpsify++; break; case 'Z': /* control compression options */ /* @@ -302,10 +398,8 @@ main(int argc, char *const *argv) * (Default to LZ4 compression when the * process is suspended, LZFSE when corpsed?) */ - if (0 == options.compress) { - options.compress++; - options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE; - } + if (0 == options.extended) + errx(EX_USAGE, "illegal flag combination"); sopts = optarg; while (*sopts) { size_t chsize; @@ -317,22 +411,17 @@ main(int argc, char *const *argv) "%s suboption", zoptkeys[ZOPT_ALG]); if (strcmp(value, "lz4") == 0) - options.calgorithm = - COMPRESSION_LZ4; + options.calgorithm = COMPRESSION_LZ4; else if (strcmp(value, "zlib") == 0) - options.calgorithm = - COMPRESSION_ZLIB; + options.calgorithm = COMPRESSION_ZLIB; else if (strcmp(value, "lzma") == 0) - options.calgorithm = - COMPRESSION_LZMA; + options.calgorithm = COMPRESSION_LZMA; else if (strcmp(value, "lzfse") == 0) - options.calgorithm = - COMPRESSION_LZFSE; + options.calgorithm = COMPRESSION_LZFSE; else errx(EX_USAGE, "unknown algorithm '%s'" " for %s suboption", - value, - zoptkeys[ZOPT_ALG]); + value, zoptkeys[ZOPT_ALG]); break; case ZOPT_CHSIZE: /* set the chunksize */ if (NULL == value) @@ -353,245 +442,422 @@ main(int argc, char *const *argv) errx(EX_USAGE, "missing suboption"); } } + break; + case 't': /* set the F_NOCACHE threshold */ + if (NULL != optarg) { + size_t tsize = atoi(optarg) * oneK; + if (tsize > 0) + options.ncthresh = tsize; + else + errx(EX_USAGE, "invalid nc threshold"); + } else + errx(EX_USAGE, "no threshold specified"); + break; + case 'F': /* maximize filerefs */ + options.allfilerefs++; break; default: errx(EX_USAGE, "unknown flag"); } } - if (optind == argc) - errx(EX_USAGE, "no pid specified"); - opt = &options; + if (optind == argc) + errx(EX_USAGE, "no pid specified"); + if (optind < argc-1) + errx(EX_USAGE, "too many arguments"); - if ((opt->dryrun ? 1 : 0) + - (NULL != corefname ? 1 : 0) + - (NULL != corefmt ? 1 : 0) > 1) - errx(EX_USAGE, "specify only one of -n, -o and -c"); + opt = &options; + if (NULL != corefname && NULL != corefmt) + errx(EX_USAGE, "specify only one of -o and -c"); + if (!opt->extended && opt->allfilerefs) + errx(EX_USAGE, "unknown flag"); - setpageshift(); + setpageshift(); - const pid_t pid = atoi(argv[optind]); - if (pid < 1 || getpid() == pid) - errx(EX_DATAERR, "invalid pid: %d", pid); - if (-1 == kill(pid, 0)) { - switch (errno) { - case ESRCH: - errc(EX_DATAERR, errno, "no process with pid %d", pid); - default: - errc(EX_DATAERR, errno, "pid %d", pid); - } - } + if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host)) + errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh); - const struct proc_bsdinfo *pbi = get_bsdinfo(pid); - if (NULL == pbi) - errx(EX_OSERR, "cannot get bsdinfo about %d", pid); + const pid_t apid = atoi(argv[optind]); + pid_t pid = apid; + mach_port_t corpse = MACH_PORT_NULL; + kern_return_t ret; - /* - * make our data model match the data model of the target - */ - if (-1 == reexec_to_match_lp64ness(pbi->pbi_flags & PROC_FLAG_LP64)) - errc(1, errno, "cannot match data model of %d", pid); + if (0 == apid) { + /* look for corpse - dead or alive */ + mach_port_array_t parray = NULL; + mach_msg_type_number_t pcount = 0; + ret = mach_ports_lookup(mach_task_self(), &parray, &pcount); + if (KERN_SUCCESS == ret && pcount > 0) { + task_t tcorpse = parray[0]; + mig_deallocate((vm_address_t)parray, pcount * sizeof (*parray)); + pid_t tpid = 0; + ret = pid_for_task(tcorpse, &tpid); + if (KERN_SUCCESS == ret && tpid != getpid()) { + corpse = tcorpse; + pid = tpid; + } + } + } -#if defined(__LP64__) - if ((pbi->pbi_flags & PROC_FLAG_LP64) == 0) -#else - if ((pbi->pbi_flags & PROC_FLAG_LP64) != 0) -#endif - errx(EX_OSERR, "cannot match data model of %d", pid); + if (pid < 1 || getpid() == pid) + errx(EX_DATAERR, "invalid pid: %d", pid); - /* - * These are experimental options for the moment. - * These will likely change. - * Some may become defaults, some may be removed altogether. - */ - if (opt->sparse || -#ifdef CONFIG_REFSC - opt->scfileref || -#endif - opt->compress || - opt->corpse || - opt->coreinfo) - warnx("experimental option(s) used, " - "resulting corefile may be unusable."); + if (0 == apid && MACH_PORT_NULL == corpse) + errx(EX_DATAERR, "missing or bad corpse from parent"); - if (pbi->pbi_ruid != pbi->pbi_svuid || - pbi->pbi_rgid != pbi->pbi_svgid) - errx(EX_NOPERM, "pid %d - not dumping a set-id process", pid); + task_t task = TASK_NULL; + const struct proc_bsdinfo *pbi = NULL; + const int rc = kill(pid, 0); - if (NULL == corefname) { - if (NULL == corefmt) { - const char defcore[] = "%N-%P-%T"; - if (NULL == (corefmt = kern_corefile())) - corefmt = strdup(defcore); - else { - // use the same directory as kern.corefile - char *p = strrchr(corefmt, '/'); - if (NULL != p) { - *p = '\0'; - size_t len = strlen(corefmt) + - strlen(defcore) + 2; - char *buf = malloc(len); - snprintf(buf, len, "%s/%s", corefmt, defcore); - free(corefmt); - corefmt = buf; - } - if (opt->debug) - printf("corefmt '%s'\n", corefmt); - } - } - corefname = format_gcore_name(corefmt, pbi); - free(corefmt); - } + if (rc == 0) { + /* process or corpse that may respond to signals */ + pbi = get_bsdinfo(pid); + } - task_t task; - kern_return_t ret = task_for_pid(mach_task_self(), pid, &task); - if (KERN_SUCCESS != ret) { - if (KERN_FAILURE == ret) - errx(EX_NOPERM, "insufficient privilege"); - else - errx(EX_NOPERM, "task_for_pid: %s", mach_error_string(ret)); - } + if (rc == 0 && pbi != NULL) { + /* process or corpse that responds to signals */ - /* - * Now that we have the task port, we adopt the credentials of - * the target process, *before* opening the core file, and - * analyzing the address space. - * - * If we are unable to match the target credentials, bail out. - */ - if (getgid() != pbi->pbi_gid && - setgid(pbi->pbi_gid) == -1) - errc(EX_NOPERM, errno, "insufficient privilege"); + /* make our data model match the data model of the target */ + if (-1 == reexec_to_match_lp64ness(pbi->pbi_flags & PROC_FLAG_LP64)) + errc(1, errno, "cannot match data model of %d", pid); - if (getuid() != pbi->pbi_uid && - setuid(pbi->pbi_uid) == -1) - errc(EX_NOPERM, errno, "insufficient privilege"); + if (!proc_same_data_model(pbi)) + errx(EX_OSERR, "cannot match data model of %d", pid); - int fd; + if (pbi->pbi_ruid != pbi->pbi_svuid || + pbi->pbi_rgid != pbi->pbi_svgid) + errx(EX_NOPERM, "pid %d - not dumping a set-id process", pid); - if (opt->dryrun) { - free(corefname); - corefname = strdup("/dev/null"); - fd = open(corefname, O_RDWR); - } else { - fd = open(corefname, O_RDWR | O_CREAT | O_EXCL, 0400); + if (NULL == corefname) + corefname = make_gcore_path(&corefmt, pbi->pbi_pid, pbi->pbi_uid, pbi->pbi_name[0] ? pbi->pbi_name : pbi->pbi_comm); - struct stat st; + if (MACH_PORT_NULL == corpse) { + ret = task_for_pid(mach_task_self(), pid, &task); + if (KERN_SUCCESS != ret) { + if (KERN_FAILURE == ret) + errx(EX_NOPERM, "insufficient privilege"); + else + errx(EX_NOPERM, "task_for_pid: %s", mach_error_string(ret)); + } + } - if (-1 == fd || -1 == fstat(fd, &st)) - errc(EX_CANTCREAT, errno, "%s", corefname); - if ((st.st_mode & S_IFMT) != S_IFREG || 1 != st.st_nlink) { - close(fd); - errx(EX_CANTCREAT, "%s: invalid file", corefname); - } - } + /* + * Have either the corpse port or the task port so adopt the + * credentials of the target process, *before* opening the + * core file, and analyzing the address space. + * + * If we are unable to match the target credentials, bail out. + */ + change_credentials(pbi->pbi_uid, pbi->pbi_gid); + } else { + if (MACH_PORT_NULL == corpse) { + if (rc == 0) { + errx(EX_OSERR, "cannot get process info for %d", pid); + } + switch (errno) { + case ESRCH: + errc(EX_DATAERR, errno, "no process with pid %d", pid); + default: + errc(EX_DATAERR, errno, "pid %d", pid); + } + } + /* a corpse with no live process backing it */ - if (opt->verbose) { + assert(0 == apid && TASK_NULL == task); + + task_flags_info_data_t tfid; + mach_msg_type_number_t count = TASK_FLAGS_INFO_COUNT; + ret = task_info(corpse, TASK_FLAGS_INFO, (task_info_t)&tfid, &count); + if (KERN_SUCCESS != ret) + err_mach(ret, NULL, "task_info"); + if (!task_same_data_model(&tfid)) + errx(EX_OSERR, "data model mismatch for target corpse"); + + if (opt->suspend || opt->corpsify) + errx(EX_USAGE, "cannot use -s or -C option with a corpse"); + if (NULL != corefmt) + errx(EX_USAGE, "cannot use -c with a corpse"); + if (NULL == corefname) + corefname = make_gcore_path(&corefmt, pid, -2, "corpse"); + + /* + * Only have a corpse, thus no process credentials. + * Switch to nobody. + */ + change_credentials(-2, -2); + } + + struct stat cst; + char *coretname = NULL; + const int fd = openout(corefname, &coretname, &cst); + + if (opt->verbose) { printf("Dumping core "); - if (opt->debug) { - printf("(%s%s%s", - opt->sparse ? "sparse" : "normal", - opt->compress ? ", compressed" : "", -#ifdef CONFIG_REFSC - opt->scfileref ? ", scfilerefs" : -#endif - ""); + if (OPTIONS_DEBUG(opt, 1)) { + printf("(%s", opt->extended ? "extended" : "vanilla"); if (0 != opt->sizebound) { hsize_str_t hstr; - printf(", %s", str_hsize(hstr, opt->sizebound)); + printf(", <= %s", str_hsize(hstr, opt->sizebound)); } printf(") "); } - printf("for pid %d to %s\n", pid, corefname); + printf("for pid %d to %s\n", pid, corefname); } + int ecode; - /* - * The traditional way to capture a consistent core dump is to - * suspend the process while processing it and writing it out. - * Yet suspending a large process for a long time can have - * unpleasant side-effects. Alternatively dumping from the live - * process can lead to an inconsistent state in the core file. - * - * Instead we can ask xnu to create a 'corpse' - the process is transiently - * suspended while a COW snapshot of the address space is constructed - * in the kernel and dump from that. This vastly reduces the suspend - * time, but it is more resource hungry and thus may fail. - * - * The -s flag (opt->suspend) causes a task_suspend/task_resume - * The -C flag (opt->corpse) causes a corpse be taken, exiting if that fails. - * Both flags can be specified, in which case corpse errors are ignored. - * - * With no flags, we imitate traditional behavior though more - * efficiently: we try to take a corpse-based dump, in the event that - * fails, dump the live process. - */ + if (MACH_PORT_NULL == corpse) { + assert(TASK_NULL != task); - int trycorpse = 1; /* default: use corpses */ - int badcorpse_is_fatal = 1; /* default: failure to create a corpse is an error */ + /* + * The "traditional" way to capture a consistent core dump is to + * suspend the process while examining it and writing it out. + * Yet suspending a large process for a long time can have + * unpleasant side-effects. Alternatively dumping from the live + * process can lead to an inconsistent state in the core file. + * + * Instead we can ask xnu to create a 'corpse' - the process is transiently + * suspended while a COW snapshot of the address space is constructed + * in the kernel and dump from that. This vastly reduces the suspend + * time, but it is more resource hungry and thus may fail. + * + * The -s flag (opt->suspend) causes a task_suspend/task_resume + * The -C flag (opt->corpse) causes a corpse be taken, exiting if that fails. + * Both flags can be specified, in which case corpse errors are ignored. + * + * With no flags, we imitate traditional behavior though more + * efficiently: we try to take a corpse-based dump, in the event that + * fails, dump the live process. + */ - if (!opt->suspend && !opt->corpse) { - /* try a corpse dump, else dump the live process */ - badcorpse_is_fatal = 0; - } else if (opt->suspend) { - trycorpse = opt->corpse; - /* if suspended anyway, ignore corpse-creation errors */ - badcorpse_is_fatal = 0; - } + int trycorpse = 1; /* default: use corpses */ + int badcorpse_is_fatal = 1; /* default: failure to create is an error */ - if (opt->suspend) - task_suspend(task); + if (!opt->suspend && !opt->corpsify) { + /* try a corpse dump, else dump the live process */ + badcorpse_is_fatal = 0; + } else if (opt->suspend) { + trycorpse = opt->corpsify; + /* if suspended anyway, ignore corpse-creation errors */ + badcorpse_is_fatal = 0; + } - if (trycorpse) { - /* - * Create a corpse from the image before dumping it - */ - mach_port_t corpse = MACH_PORT_NULL; - ret = task_generate_corpse(task, &corpse); - switch (ret) { - case KERN_SUCCESS: - if (opt->debug) - printf("corpse generated on port %x, task %x\n", - corpse, task); - ecode = coredump(corpse, fd); - mach_port_deallocate(mach_task_self(), corpse); - break; - default: - if (badcorpse_is_fatal || opt->debug) { - warnx("failed to snapshot pid %d: %s\n", - pid, mach_error_string(ret)); - if (badcorpse_is_fatal) { - ecode = KERN_RESOURCE_SHORTAGE == ret ? EX_TEMPFAIL : EX_OSERR; - goto out; - } - } - ecode = coredump(task, fd); - break; - } - } else { - /* - * Examine the task directly - */ - ecode = coredump(task, fd); - } + if (opt->suspend) + task_suspend(task); -out: - if (opt->suspend) - task_resume(task); + if (trycorpse) { + /* + * Create a corpse from the image before dumping it + */ + ret = task_generate_corpse(task, &corpse); + switch (ret) { + case KERN_SUCCESS: + if (OPTIONS_DEBUG(opt, 1)) + printf("Corpse generated on port %x, task %x\n", + corpse, task); + ecode = coredump(corpse, fd, pbi); + mach_port_deallocate(mach_task_self(), corpse); + break; + default: + if (badcorpse_is_fatal || opt->verbose) { + warnx("failed to snapshot pid %d: %s\n", + pid, mach_error_string(ret)); + if (badcorpse_is_fatal) { + ecode = KERN_RESOURCE_SHORTAGE == ret ? EX_TEMPFAIL : EX_OSERR; + goto out; + } + } + ecode = coredump(task, fd, pbi); + break; + } + } else { + /* + * Examine the task directly + */ + ecode = coredump(task, fd, pbi); + } - if (0 != ecode && !opt->preserve && !opt->dryrun) { - /* - * try not to leave a half-written mess occupying - * blocks on the filesystem - */ - ftruncate(fd, 0); - unlink(corefname); - } - if (-1 == close(fd)) - ecode = EX_OSERR; - if (ecode) - errx(ecode, "failed to dump core for pid %d", pid); - free(corefname); + out: + if (opt->suspend) + task_resume(task); + } else { + /* + * Handed a corpse by our parent. + */ + ecode = coredump(corpse, fd, pbi); + mach_port_deallocate(mach_task_self(), corpse); + } + ecode = closeout(fd, ecode, corefname, coretname, &cst); + if (ecode) + errx(ecode, "failed to dump core for pid %d", pid); return 0; } + +#if defined(CONFIG_GCORE_FREF) || defined(CONFIG_GCORE_MAP) || defined(GCONFIG_GCORE_CONV) + +static int +getcorefd(const char *infile) +{ + const int fd = open(infile, O_RDONLY | O_CLOEXEC); + if (-1 == fd) + errc(EX_DATAERR, errno, "cannot open %s", infile); + + struct mach_header mh; + if (-1 == pread(fd, &mh, sizeof (mh), 0)) + errc(EX_OSERR, errno, "cannot read mach header from %s", infile); + + static const char cant_match_data_model[] = "cannot match the data model of %s"; + + if (-1 == reexec_to_match_lp64ness(MH_MAGIC_64 == mh.magic)) + errc(1, errno, cant_match_data_model, infile); + + if (NATIVE_MH_MAGIC != mh.magic) + errx(EX_OSERR, cant_match_data_model, infile); + if (MH_CORE != mh.filetype) + errx(EX_DATAERR, "%s is not a mach core file", infile); + return fd; +} + +#endif + +#ifdef CONFIG_GCORE_FREF + +static int +gcore_fref_main(int argc, char *argv[]) +{ + err_set_exit_b(^(int eval) { + if (EX_USAGE == eval) { + fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]); + } + }); + if (2 == argc) + errx(EX_USAGE, "no input corefile"); + if (argc > 3) + errx(EX_USAGE, "too many arguments"); + opt = &options; + return gcore_fref(getcorefd(argv[2])); +} + +#endif /* CONFIG_GCORE_FREF */ + +#ifdef CONFIG_GCORE_MAP + +static int +gcore_map_main(int argc, char *argv[]) +{ + err_set_exit_b(^(int eval) { + if (EX_USAGE == eval) { + fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]); + } + }); + if (2 == argc) + errx(EX_USAGE, "no input corefile"); + if (argc > 3) + errx(EX_USAGE, "too many arguments"); + opt = &options; + return gcore_map(getcorefd(argv[2])); +} + +#endif + +#ifdef CONFIG_GCORE_CONV + +static int +gcore_conv_main(int argc, char *argv[]) +{ + err_set_exit_b(^(int eval) { + if (EX_USAGE == eval) + fprintf(stderr, + "usage:\t%s %s [-v] [-L searchpath] [-z] [-s] incore outcore\n", pgm, argv[1]); + }); + + char *searchpath = NULL; + bool zf = false; + + int c; + optind = 2; + while ((c = getopt(argc, argv, "dzvL:s")) != -1) { + switch (c) { + /* + * likely documented options + */ + case 'L': + searchpath = strdup(optarg); + break; + case 'z': + zf = true; + break; + case 'v': + options.verbose++; + break; + case 's': + options.dsymforuuid++; + break; + /* + * dev and debugging help + */ +#ifdef CONFIG_DEBUG + case 'd': + options.debug++; + options.verbose++; + options.preserve++; + break; +#endif + default: + errx(EX_USAGE, "unknown flag"); + } + } + if (optind == argc) + errx(EX_USAGE, "no input corefile"); + if (optind == argc - 1) + errx(EX_USAGE, "no output corefile"); + if (optind < argc - 2) + errx(EX_USAGE, "too many arguments"); + + const char *incore = argv[optind]; + char *corefname = strdup(argv[optind+1]); + + opt = &options; + + setpageshift(); + + if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host)) + errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh); + + const int infd = getcorefd(incore); + struct stat cst; + char *coretname = NULL; + const int fd = openout(corefname, &coretname, &cst); + int ecode = gcore_conv(infd, searchpath, zf, fd); + ecode = closeout(fd, ecode, corefname, coretname, &cst); + if (ecode) + errx(ecode, "failed to convert core file successfully"); + return 0; +} +#endif + +int +main(int argc, char *argv[]) +{ + if (NULL == (pgm = strrchr(*argv, '/'))) + pgm = *argv; + else + pgm++; +#ifdef CONFIG_GCORE_FREF + if (argc > 1 && 0 == strcmp(argv[1], "fref")) { + return gcore_fref_main(argc, argv); + } +#endif +#ifdef CONFIG_GCORE_MAP + if (argc > 1 && 0 == strcmp(argv[1], "map")) { + return gcore_map_main(argc, argv); + } +#endif +#ifdef CONFIG_GCORE_CONV + if (argc > 1 && 0 == strcmp(argv[1], "conv")) { + return gcore_conv_main(argc, argv); + } +#endif + return gcore_main(argc, argv); +} diff --git a/gcore.tproj/options.h b/gcore.tproj/options.h index 051be4a..71481fa 100644 --- a/gcore.tproj/options.h +++ b/gcore.tproj/options.h @@ -12,11 +12,14 @@ #if defined(__arm__) || defined(__arm64__) #define RDAR_23744374 1 /* 'true' while not fixed i.e. enable workarounds */ +#define RDAR_28040018 1 /* 'true' while not fixed i.e. enable workarounds */ #endif -#define CONFIG_REFSC 1 /* create shared cache reference segment (-R) */ -//#define CONFIG_PURGABLE 1 /* record purgability */ -//#define CONFIG_SUBMAP 1 /* include submaps (debugging) */ +//#define CONFIG_SUBMAP 1 /* include submaps (debugging output) */ +#define CONFIG_GCORE_MAP 1 /* support 'gcore map' */ +#define CONFIG_GCORE_CONV 1 /* support 'gcore conv' - new -> old core files */ +#define CONFIG_GCORE_FREF 1 /* support 'gcore fref' - referenced file list */ +#define CONFIG_DEBUG 1 /* support '-d' option */ #ifdef NDEBUG #define poison(a, p, s) /* do nothing */ @@ -25,23 +28,35 @@ #endif struct options { - int corpse; // dump from a corpse + int corpsify; // make a corpse to dump from int suspend; // suspend while dumping int preserve; // preserve the core file, even if there are errors int verbose; // be chatty - int debug; // internal debugging: options accumulate. very noisy. - int dryrun; // do all the work, but throw the dump away - int sparse; // use dyld's data about dylibs to reduce the size of the dump - off_t sizebound; // maximum size of the dump - int coreinfo; // create a (currently experimental) 'coreinfo' section -#ifdef CONFIG_REFSC - int scfileref; // create "reference" segments that point at the shared cache +#ifdef CONFIG_DEBUG + int debug; // internal debugging: options accumulate. noisy. #endif - int compress; // compress the dump - size_t chunksize; // max size of the compressed segment + int extended; // avoid writing out ro mapped files, compress regions + off_t sizebound; // maximum size of the dump + size_t chunksize; // max size of a compressed subregion compression_algorithm calgorithm; // algorithm in use + size_t ncthresh; // F_NOCACHE enabled *above* this value + int allfilerefs; // if set, every mapped file on the root fs is a fileref + int dsymforuuid; // Try dsysForUUID to retrieve symbol-rich executable }; extern const struct options *opt; +/* + * == 0 - not verbose + * >= 1 - verbose plus chatty + * >= 2 - tabular summaries + * >= 3 - all + */ + +#ifdef CONFIG_DEBUG +#define OPTIONS_DEBUG(opt, lvl) ((opt)->debug && (opt)->debug >= (lvl)) +#else +#define OPTIONS_DEBUG(opt, lvl) 0 +#endif + #endif /* _OPTIONS_H */ diff --git a/gcore.tproj/region.h b/gcore.tproj/region.h index a4f0afb..77853b2 100644 --- a/gcore.tproj/region.h +++ b/gcore.tproj/region.h @@ -10,36 +10,68 @@ #ifndef _REGION_H #define _REGION_H +/* + * A range of virtual memory + */ +struct vm_range { + mach_vm_offset_t addr; + mach_vm_offset_t size; +}; + +#define _V_ADDR(g) ((g)->addr) +#define _V_SIZE(g) ((g)->size) +#define V_SETADDR(g, a) ((g)->addr = (a)) +#define V_SETSIZE(g, z) ((g)->size = (z)) +#define V_ENDADDR(g) (_V_ADDR(g) + _V_SIZE(g)) + +static __inline const mach_vm_offset_t V_ADDR(const struct vm_range *vr) { + return _V_ADDR(vr); +} +static __inline const mach_vm_offset_t V_SIZE(const struct vm_range *vr) { + return _V_SIZE(vr); +} +static __inline const size_t V_SIZEOF(const struct vm_range *vr) { + assert((typeof (vr->size))(size_t)_V_SIZE(vr) == _V_SIZE(vr)); + return (size_t)_V_SIZE(vr); +} + +/* + * A range of offsets into a file + */ +struct file_range { + off_t off; + off_t size; +}; + +#define F_OFF(f) ((f)->off) +#define F_SIZE(f) ((f)->size) + struct regionop; struct subregion; struct region { STAILQ_ENTRY(region) r_linkage; - mach_vm_offset_t r_address; - mach_vm_offset_t r_size; + struct vm_range r_range; -#define _R_ADDR(r) ((r)->r_address) -#define _R_SIZE(r) ((r)->r_size) -#define R_SETADDR(r, a) ((r)->r_address = (a)) -#define R_SETSIZE(r, z) ((r)->r_size = (z)) +#define R_RANGE(r) (&(r)->r_range) +#define _R_ADDR(r) _V_ADDR(R_RANGE(r)) +#define _R_SIZE(r) _V_SIZE(R_RANGE(r)) +#define R_SIZEOF(r) V_SIZEOF(R_RANGE(r)) +#define R_SETADDR(r, a) V_SETADDR(R_RANGE(r), (a)) +#define R_SETSIZE(r, z) V_SETSIZE(R_RANGE(r), (z)) #define R_ENDADDR(r) (_R_ADDR(r) + _R_SIZE(r)) vm_region_submap_info_data_64_t r_info; vm_page_info_basic_data_t r_pageinfo; -#ifdef CONFIG_PURGABLE int r_purgable; -#endif + #ifdef CONFIG_SUBMAP int r_depth; #endif - boolean_t - r_insharedregion, - r_inzfodregion, - r_incommregion; + boolean_t r_insharedregion, r_inzfodregion, r_incommregion; -#ifdef CONFIG_REFSC /* * This field may be non-NULL if the region is a read-only part * of a mapped file (i.e. the shared cache) and thus @@ -47,9 +79,9 @@ struct region { */ struct { const struct libent *fr_libent; + const char *fr_pathname; off_t fr_offset; } *r_fileref; -#endif /* * These (optional) fields are filled in after we parse the information @@ -64,7 +96,6 @@ struct region { static __inline const mach_vm_offset_t R_ADDR(const struct region *r) { return _R_ADDR(r); } - static __inline const mach_vm_offset_t R_SIZE(const struct region *r) { return _R_SIZE(r); } @@ -95,9 +126,7 @@ struct regionop { #define ROP_DELETE(r) (((r)->r_op->rop_delete)(r)) extern const struct regionop vanilla_ops, sparse_ops, zfod_ops; -#ifdef CONFIG_REFSC extern const struct regionop fileref_ops; -#endif struct regionhead; diff --git a/gcore.tproj/sparse.c b/gcore.tproj/sparse.c index 26d4dc7..c62b9f3 100644 --- a/gcore.tproj/sparse.c +++ b/gcore.tproj/sparse.c @@ -49,7 +49,7 @@ new_subregion( S_SETSIZE(s, vmsize); s->s_libent = le; - s->s_isfileref = false; + s->s_isuuidref = false; return s; } @@ -63,11 +63,17 @@ del_subregion(struct subregion *s) static walk_return_t clean_subregions(struct region *r) { - for (unsigned i = 0; i < r->r_nsubregions; i++) - del_subregion(r->r_subregions[i]); - poison(r->r_subregions, 0xfac1fac1, sizeof (r->r_subregions[0]) * r->r_nsubregions); - free(r->r_subregions); - r->r_nsubregions = 0; + if (r->r_nsubregions) { + assert(r->r_subregions); + for (unsigned i = 0; i < r->r_nsubregions; i++) + del_subregion(r->r_subregions[i]); + poison(r->r_subregions, 0xfac1fac1, sizeof (r->r_subregions[0]) * r->r_nsubregions); + free(r->r_subregions); + r->r_nsubregions = 0; + r->r_subregions = NULL; + } else { + assert(NULL == r->r_subregions); + } return WALK_CONTINUE; } @@ -112,12 +118,11 @@ add_subregions_for_libent( subregionlisthead_t *srlh, const struct region *r, const native_mach_header_t *mh, - const mach_vm_offset_t mh_taddr, + const mach_vm_offset_t __unused mh_taddr, // address in target const struct libent *le) { const struct load_command *lc = (const void *)(mh + 1); - mach_vm_offset_t scoffset = MACH_VM_MAX_ADDRESS; - + mach_vm_offset_t objoff = le->le_objoff; for (unsigned n = 0; n < mh->ncmds; n++) { const native_segment_command_t *sc; @@ -126,60 +131,43 @@ add_subregions_for_libent( case NATIVE_LC_SEGMENT: sc = (const void *)lc; - char scsegname[17]; - strlcpy(scsegname, sc->segname, sizeof (scsegname)); - - if (0 == sc->vmaddr && - strcmp(scsegname, SEG_PAGEZERO) == 0) + if (0 == sc->vmaddr && strcmp(sc->segname, SEG_PAGEZERO) == 0) break; - - /* -Depends- on finding a __TEXT segment first! */ - - if (MACH_VM_MAX_ADDRESS == scoffset) { - if (strcmp(scsegname, SEG_TEXT) == 0) - scoffset = mh_taddr - sc->vmaddr; - else { - /* - * Treat as error - don't want a partial description - * to cause something to be omitted from the dump. - */ - printr(r, "expected %s segment, found %s segment\n", SEG_TEXT, scsegname); - return WALK_ERROR; - } - } + mach_vm_offset_t lo = sc->vmaddr + objoff; + mach_vm_offset_t hi = lo + sc->vmsize; /* Eliminate non-overlapping sections first */ - if (R_ENDADDR(r) - 1 < sc->vmaddr + scoffset) + if (R_ENDADDR(r) - 1 < lo) break; - if (sc->vmaddr + scoffset + sc->vmsize - 1 < R_ADDR(r)) + if (hi - 1 < R_ADDR(r)) break; + /* * Some part of this segment is in the region. * Trim the edges in the case where we span regions. */ - mach_vm_offset_t loaddr = sc->vmaddr + scoffset; - mach_vm_offset_t hiaddr = loaddr + sc->vmsize; - if (loaddr < R_ADDR(r)) - loaddr = R_ADDR(r); - if (hiaddr > R_ENDADDR(r)) - hiaddr = R_ENDADDR(r); + if (lo < R_ADDR(r)) + lo = R_ADDR(r); + if (hi > R_ENDADDR(r)) + hi = R_ENDADDR(r); struct subregionlist *srl = calloc(1, sizeof (*srl)); - struct subregion *s = new_subregion(loaddr, hiaddr - loaddr, sc, le); + struct subregion *s = new_subregion(lo, hi - lo, sc, le); assert(sc->fileoff >= 0); srl->srl_s = s; STAILQ_INSERT_HEAD(srlh, srl, srl_linkage); - if (opt->debug > 3) { + if (OPTIONS_DEBUG(opt, 2)) { hsize_str_t hstr; - printr(r, "subregion %llx-%llx %7s %12s\t%s [%x/%x off %zd for %zd nsects %u flags %x]\n", + printr(r, "subregion %llx-%llx %7s %12s\t%s [%s off %lu for %lu nsects %u flags %x]\n", S_ADDR(s), S_ENDADDR(s), str_hsize(hstr, S_SIZE(s)), - scsegname, + sc->segname, S_FILENAME(s), - sc->initprot, sc->maxprot, - sc->fileoff, sc->filesize, + str_prot(sc->initprot), + (unsigned long)sc->fileoff, + (unsigned long)sc->filesize, sc->nsects, sc->flags); } break; @@ -220,7 +208,7 @@ eliminate_duplicate_subregions(struct region *r) i++; continue; } - if (opt->debug) + if (OPTIONS_DEBUG(opt, 3)) printr(r, "eliding duplicate %s subregion (%llx-%llx) file %s\n", S_MACHO_TYPE(s1), S_ADDR(s1), S_ENDADDR(s1), S_FILENAME(s1)); /* If the duplicate subregions aren't mapping the same file (?), forget the name */ @@ -237,24 +225,24 @@ eliminate_duplicate_subregions(struct region *r) walk_return_t decorate_memory_region(struct region *r, void *arg) { + if (r->r_inzfodregion || r->r_incommregion) + return WALK_CONTINUE; + const dyld_process_info dpi = arg; __block walk_return_t retval = WALK_CONTINUE; __block subregionlisthead_t srlhead = STAILQ_HEAD_INITIALIZER(srlhead); - _dyld_process_info_for_each_image(dpi, ^(uint64_t mhaddr, const uuid_t uuid, __unused const char *path) { + _dyld_process_info_for_each_image(dpi, ^(uint64_t __unused mhaddr, const uuid_t uuid, __unused const char *path) { if (WALK_CONTINUE == retval) { const struct libent *le = libent_lookup_byuuid(uuid); assert(le->le_mhaddr == mhaddr); - /* - * Core dumps conventionally contain the whole executable, but we're trying - * to elide everything that can't be found in a file elsewhere. - */ -#if 0 - if (MH_EXECUTE == le->le_mh->filetype) - return; // cause the whole a.out to be emitted -#endif - retval = add_subregions_for_libent(&srlhead, r, le->le_mh, le->le_mhaddr, le); + bool shouldskip = false; + if (V_SIZE(&le->le_vr)) + shouldskip = (R_ENDADDR(r) < V_ADDR(&le->le_vr) || + R_ADDR(r) > V_ENDADDR(&le->le_vr)); + if (!shouldskip) + retval = add_subregions_for_libent(&srlhead, r, le->le_mh, le->le_mhaddr, le); } }); if (WALK_CONTINUE != retval) @@ -289,48 +277,67 @@ decorate_memory_region(struct region *r, void *arg) eliminate_duplicate_subregions(r); - const struct libent *lesc = NULL; /* libent ref for shared cache */ - if (r->r_insharedregion) { - uuid_t uusc; - if (get_sc_uuid(dpi, uusc)) { - lesc = libent_lookup_byuuid(uusc); - assert(NULL == lesc->le_mh && 0 == lesc->le_mhaddr); - } - } + if (r->r_info.external_pager) { + /* + * Only very specific segment types get to be filerefs + */ + for (i = 0; i < r->r_nsubregions; i++) { + struct subregion *s = r->r_subregions[i]; + /* + * Anything marked writable is trivially disqualified; we're + * going to copy it anyway. + */ + if (s->s_segcmd.initprot & VM_PROT_WRITE) + continue; - /* - * Only very specific segment types get to be filerefs - */ - for (i = 0; i < r->r_nsubregions; i++) { - struct subregion *s = r->r_subregions[i]; - /* - * Anything writable is trivially disqualified - */ - if (s->s_segcmd.initprot & VM_PROT_WRITE) - continue; - /* - * As long as there's a filename, __TEXT and __LINKEDIT - * end up as a file reference. - * - * __LINKEDIT is more complicated: the segment commands point - * at a unified segment in the shared cache mapping. - * Ditto for __UNICODE(?) - */ - if (issubregiontype(s, SEG_TEXT)) { - /* fall through */; - } else if (issubregiontype(s, SEG_LINKEDIT)) { - if (r->r_insharedregion) - s->s_libent = lesc; - } else if (issubregiontype(s, "__UNICODE")) { - if (r->r_insharedregion) - s->s_libent = lesc; - } else - continue; - - if (s->s_libent) - s->s_isfileref = true; - } - } + /* __TEXT and __LINKEDIT are our real targets */ + if (!issubregiontype(s, SEG_TEXT) && !issubregiontype(s, SEG_LINKEDIT) && !issubregiontype(s, "__UNICODE")) { + if (OPTIONS_DEBUG(opt, 3)) { + hsize_str_t hstr; + printvr(S_RANGE(s), "skipping read-only %s segment %s\n", S_MACHO_TYPE(s), str_hsize(hstr, S_SIZE(s))); + } + continue; + } + if (r->r_insharedregion) { + /* + * Part of the shared region: things get more complicated. + */ + if (r->r_fileref) { + /* + * There's a file reference here for the whole region. + * For __TEXT subregions, we could, in principle (though + * see below) generate references to the individual + * dylibs that dyld reports in the region. If the + * debugger could then use the __LINKEDIT info in the + * file, then we'd be done. But as long as the dump + * includes __LINKEDIT sections, we're going to + * end up generating a file reference to the combined + * __LINKEDIT section in the shared cache anyway, so + * we might as well do that for the __TEXT regions as + * well. + */ + s->s_libent = r->r_fileref->fr_libent; + s->s_isuuidref = true; + } else { + /* + * If we get here, it's likely that the shared cache + * name can't be found e.g. update_dyld_shared_cache(1). + * For __TEXT subregions, we could generate refs to + * the individual dylibs, but note that the mach header + * and segment commands in memory are still pointing + * into the shared cache so any act of reconstruction + * is fiendishly complex. So copy it. + */ + assert(!s->s_isuuidref); + } + } else { + /* Just a regular dylib? */ + if (s->s_libent) + s->s_isuuidref = true; + } + } + } + } assert(WALK_CONTINUE == retval); done: @@ -347,8 +354,7 @@ done: * Strip region of all decoration * * Invoked (on every region!) after an error during the initial - * 'decoration' phase to discard to discard potentially incomplete - * information. + * 'decoration' phase to discard potentially incomplete information. */ walk_return_t undecorate_memory_region(struct region *r, __unused void *arg) @@ -371,36 +377,44 @@ sparse_region_optimization(struct region *r, __unused void *arg) * Pure zfod region: almost certainly a more compact * representation - keep it that way. */ + if (OPTIONS_DEBUG(opt, 3)) + printr(r, "retaining zfod region\n"); assert(&zfod_ops == r->r_op); return clean_subregions(r); } -#ifdef CONFIG_REFSC - if (r->r_fileref) { - /* - * Already have a fileref for the whole region: almost - * certainly a more compact representation - keep - * it that way. - */ - assert(&fileref_ops == r->r_op); - return clean_subregions(r); - } -#endif + if (r->r_insharedregion && 0 == r->r_nsubregions) { + /* + * A segment in the shared region needs to be + * identified with an LC_SEGMENT that dyld claims, + * otherwise (we assert) it's not useful to the dump. + */ + if (OPTIONS_DEBUG(opt, 2)) { + hsize_str_t hstr; + printr(r, "not referenced in dyld info => " + "eliding %s range in shared region\n", + str_hsize(hstr, R_SIZE(r))); + } + if (0 == r->r_info.pages_dirtied && 0 == r->r_info.pages_swapped_out) + return WALK_DELETE_REGION; + if (OPTIONS_DEBUG(opt, 2)) { + hsize_str_t hstr; + printr(r, "dirty pages, but not referenced in dyld info => " + "NOT eliding %s range in shared region\n", + str_hsize(hstr, R_SIZE(r))); + } + } - if (r->r_insharedregion && 0 == r->r_nsubregions) { - /* - * A segment in the shared region needs to be - * identified with an LC_SEGMENT that dyld claims, - * otherwise (we assert) it's not useful to the dump. - */ - if (opt->debug) { - hsize_str_t hstr; - printr(r, "not referenced in dyld info => " - "eliding %s range in shared region\n", - str_hsize(hstr, R_SIZE(r))); - } - return WALK_DELETE_REGION; - } + if (r->r_fileref) { + /* + * Already have a fileref for the whole region: already + * a more compact representation - keep it that way. + */ + if (OPTIONS_DEBUG(opt, 3)) + printr(r, "retaining fileref region\n"); + assert(&fileref_ops == r->r_op); + return clean_subregions(r); + } if (r->r_nsubregions > 1) { /* @@ -413,7 +427,7 @@ sparse_region_optimization(struct region *r, __unused void *arg) struct subregion *s0 = r->r_subregions[i-1]; struct subregion *s1 = r->r_subregions[i]; - if (s0->s_isfileref) { + if (s0->s_isuuidref) { i++; continue; /* => destined to be a fileref */ } @@ -424,11 +438,9 @@ sparse_region_optimization(struct region *r, __unused void *arg) if (S_ENDADDR(s0) == S_ADDR(s1)) { /* directly adjacent subregions */ -#if 1 - if (opt->debug) + if (OPTIONS_DEBUG(opt, 2)) printr(r, "merging subregions (%llx-%llx + %llx-%llx) -- adjacent\n", S_ADDR(s0), S_ENDADDR(s0), S_ADDR(s1), S_ENDADDR(s1)); -#endif S_SETSIZE(s0, S_ENDADDR(s1) - S_ADDR(s0)); elide_subregion(r, i); continue; @@ -445,11 +457,9 @@ sparse_region_optimization(struct region *r, __unused void *arg) if (pfn[0] == pfn[1] && pfn[0] == endpfn[0] && pfn[0] == endpfn[1]) { /* two small subregions share a host page */ -#if 1 - if (opt->debug) + if (OPTIONS_DEBUG(opt, 2)) printr(r, "merging subregions (%llx-%llx + %llx-%llx) -- same page\n", S_ADDR(s0), S_ENDADDR(s0), S_ADDR(s1), S_ENDADDR(s1)); -#endif S_SETSIZE(s0, S_ENDADDR(s1) - S_ADDR(s0)); elide_subregion(r, i); continue; @@ -457,11 +467,9 @@ sparse_region_optimization(struct region *r, __unused void *arg) if (pfn[1] == 1 + endpfn[0]) { /* subregions are pagewise-adjacent: bigger chunks to compress */ -#if 1 - if (opt->debug) + if (OPTIONS_DEBUG(opt, 2)) printr(r, "merging subregions (%llx-%llx + %llx-%llx) -- adjacent pages\n", S_ADDR(s0), S_ENDADDR(s0), S_ADDR(s1), S_ENDADDR(s1)); -#endif S_SETSIZE(s0, S_ENDADDR(s1) - S_ADDR(s0)); elide_subregion(r, i); continue; @@ -471,6 +479,17 @@ sparse_region_optimization(struct region *r, __unused void *arg) } } + if (1 == r->r_nsubregions) { + struct subregion *s = r->r_subregions[0]; + if (!s->s_isuuidref && + R_ADDR(r) == S_ADDR(s) && R_ENDADDR(r) == S_ENDADDR(s)) { + if (OPTIONS_DEBUG(opt, 3)) + printr(r, "subregion (%llx-%llx) reverts to region\n", + S_ADDR(s), S_ENDADDR(s)); + return clean_subregions(r); + } + } + if (r->r_nsubregions) r->r_op = &sparse_ops; diff --git a/gcore.tproj/sparse.h b/gcore.tproj/sparse.h index 1880bc5..cf920ff 100644 --- a/gcore.tproj/sparse.h +++ b/gcore.tproj/sparse.h @@ -9,27 +9,28 @@ #define _SPARSE_H struct subregion { - mach_vm_offset_t s_address; - mach_vm_offset_t s_size; + struct vm_range s_range; native_segment_command_t s_segcmd; const struct libent *s_libent; - bool s_isfileref; + bool s_isuuidref; }; +#define S_RANGE(s) (&(s)->s_range) + static __inline void S_SETADDR(struct subregion *s, mach_vm_offset_t a) { - s->s_address = a; + V_SETADDR(S_RANGE(s), a); } static __inline void S_SETSIZE(struct subregion *s, mach_vm_offset_t sz) { - s->s_size = sz; + V_SETSIZE(S_RANGE(s), sz); } static __inline const mach_vm_offset_t S_ADDR(const struct subregion *s) { - return s->s_address; + return V_ADDR(S_RANGE(s)); } static __inline const mach_vm_offset_t S_SIZE(const struct subregion *s) { - return s->s_size; + return V_SIZE(S_RANGE(s)); } static __inline const mach_vm_offset_t S_ENDADDR(const struct subregion *s) { diff --git a/gcore.tproj/threads.c b/gcore.tproj/threads.c index 9903803..b1b3d6f 100644 --- a/gcore.tproj/threads.c +++ b/gcore.tproj/threads.c @@ -14,6 +14,7 @@ #include #include #include +#include typedef struct { int flavor; @@ -73,6 +74,8 @@ dump_thread_state(native_mach_header_t *mh, struct thread_command *tc, mach_port wbuf += thread_flavor[f].count; } + assert((ptrdiff_t)tc->cmdsize == ((caddr_t)wbuf - (caddr_t)tc)); + mach_header_inc_ncmds(mh, 1); mach_header_inc_sizeofcmds(mh, tc->cmdsize); } diff --git a/gcore.tproj/utils.c b/gcore.tproj/utils.c index 2c90967..f0edcf8 100644 --- a/gcore.tproj/utils.c +++ b/gcore.tproj/utils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Apple Inc. All rights reserved. + * Copyright (c) 2016 Apple Inc. All rights reserved. */ #include "options.h" @@ -13,6 +13,7 @@ #include #include #include +#include void err_mach(kern_return_t kr, const struct region *r, const char *fmt, ...) @@ -22,12 +23,12 @@ err_mach(kern_return_t kr, const struct region *r, const char *fmt, ...) if (0 != kr) printf("%s: ", pgm); if (NULL != r) - printf("%llx-%llx ", R_ADDR(r), R_ENDADDR(r)); + printf("%016llx-%016llx ", R_ADDR(r), R_ENDADDR(r)); vprintf(fmt, ap); va_end(ap); if (0 != kr) { - printf(": %s (%x)", mach_error_string(kr), kr); + printf(": failed: %s (0x%x)", mach_error_string(kr), kr); switch (err_get_system(kr)) { case err_get_system(err_mach_ipc): /* 0x10000000 == (4 << 26) */ @@ -41,14 +42,29 @@ err_mach(kern_return_t kr, const struct region *r, const char *fmt, ...) putchar('\n'); } +static void +vprintvr(const struct vm_range *vr, const char *restrict fmt, va_list ap) +{ + if (NULL != vr) + printf("%016llx-%016llx ", V_ADDR(vr), V_ENDADDR(vr)); + vprintf(fmt, ap); +} + +void +printvr(const struct vm_range *vr, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vprintvr(vr, fmt, ap); + va_end(ap); +} + void printr(const struct region *r, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - if (NULL != r) - printf("%llx-%llx ", R_ADDR(r), R_ENDADDR(r)); - vfprintf(stdout, fmt, ap); + vprintvr(R_RANGE(r), fmt, ap); va_end(ap); } @@ -63,9 +79,291 @@ str_hsize(hsize_str_t hstr, uint64_t size) return hstr; } +/* + * Print VM protections in human-readable form + */ +const char * +str_prot(const vm_prot_t prot) +{ + static const char *pstr[] = { + [0] = "---", + [VM_PROT_READ] = "r--", + [VM_PROT_WRITE] = "-w-", + [VM_PROT_READ|VM_PROT_WRITE] = "rw-", + [VM_PROT_EXECUTE] = "--x", + [VM_PROT_READ|VM_PROT_EXECUTE] = "r-x", + [VM_PROT_WRITE|VM_PROT_EXECUTE] = "-wx", + [VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE] = "rwx" + }; + return pstr[prot & 7]; +} + +// c.f. VMUVMRegion.m + +const char * +str_shared(int sm) +{ + static const char *sstr[] = { + [0] = " ", + [SM_COW] = "sm=cow", + [SM_PRIVATE] = "sm=prv", + [SM_EMPTY] = "sm=nul", + [SM_SHARED] = "sm=ali", + [SM_TRUESHARED] = "sm=shm", + [SM_PRIVATE_ALIASED] = "sm=zer", + [SM_SHARED_ALIASED] = "sm=s/a", + [SM_LARGE_PAGE] = "sm=lpg", + }; + if ((unsigned)sm < sizeof (sstr) / sizeof (sstr[0])) + return sstr[sm]; + return "sm=???"; +} + +const char * +str_purgable(int pu, int sm) +{ + if (SM_EMPTY == sm) + return " "; + static const char *pstr[] = { + [VM_PURGABLE_NONVOLATILE] = "p=n", + [VM_PURGABLE_VOLATILE] = "p=v", + [VM_PURGABLE_EMPTY] = "p=e", + [VM_PURGABLE_DENY] = " ", + }; + if ((unsigned)pu < sizeof (pstr) / sizeof (pstr[0])) + return pstr[pu]; + return "p=?"; +} + +/* + * c.f. VMURegionTypeDescriptionForTagShareProtAndPager. + */ +const char * +str_tag(tag_str_t tstr, int tag, int share_mode, vm_prot_t curprot, int external_pager) +{ + const char *rtype; + + switch (tag) { + case 0: + if (external_pager) + rtype = "mapped file"; + else if (SM_TRUESHARED == share_mode) + rtype = "shared memory"; + else + rtype = "VM_allocate"; + break; + case VM_MEMORY_MALLOC: + if (VM_PROT_NONE == curprot) + rtype = "MALLOC guard page"; + else if (SM_EMPTY == share_mode) + rtype = "MALLOC"; + else + rtype = "MALLOC metadata"; + break; + case VM_MEMORY_STACK: + if (VM_PROT_NONE == curprot) + rtype = "Stack guard"; + else + rtype = "Stack"; + break; +#if defined(CONFIG_DEBUG) || defined(CONFIG_GCORE_MAP) + case VM_MEMORY_MALLOC_SMALL: + rtype = "MALLOC_SMALL"; + break; + case VM_MEMORY_MALLOC_LARGE: + rtype = "MALLOC_LARGE"; + break; + case VM_MEMORY_MALLOC_HUGE: + rtype = "MALLOC_HUGE"; + break; + case VM_MEMORY_SBRK: + rtype = "SBRK"; + break; + case VM_MEMORY_REALLOC: + rtype = "MALLOC_REALLOC"; + break; + case VM_MEMORY_MALLOC_TINY: + rtype = "MALLOC_TINY"; + break; + case VM_MEMORY_MALLOC_LARGE_REUSABLE: + rtype = "MALLOC_LARGE_REUSABLE"; + break; + case VM_MEMORY_MALLOC_LARGE_REUSED: + rtype = "MALLOC_LARGE"; + break; + case VM_MEMORY_ANALYSIS_TOOL: + rtype = "Performance tool data"; + break; + case VM_MEMORY_MALLOC_NANO: + rtype = "MALLOC_NANO"; + break; + case VM_MEMORY_MACH_MSG: + rtype = "Mach message"; + break; + case VM_MEMORY_IOKIT: + rtype = "IOKit"; + break; + case VM_MEMORY_GUARD: + rtype = "Guard"; + break; + case VM_MEMORY_SHARED_PMAP: + rtype = "shared pmap"; + break; + case VM_MEMORY_DYLIB: + rtype = "dylib"; + break; + case VM_MEMORY_OBJC_DISPATCHERS: + rtype = "ObjC dispatching code"; + break; + case VM_MEMORY_UNSHARED_PMAP: + rtype = "unshared pmap"; + break; + case VM_MEMORY_APPKIT: + rtype = "AppKit"; + break; + case VM_MEMORY_FOUNDATION: + rtype = "Foundation"; + break; + case VM_MEMORY_COREGRAPHICS: + rtype = "CoreGraphics"; + break; + case VM_MEMORY_CORESERVICES: + rtype = "CoreServices"; + break; + case VM_MEMORY_JAVA: + rtype = "Java"; + break; + case VM_MEMORY_COREDATA: + rtype = "CoreData"; + break; + case VM_MEMORY_COREDATA_OBJECTIDS: + rtype = "CoreData Object IDs"; + break; + case VM_MEMORY_ATS: + rtype = "ATS (font support)"; + break; + case VM_MEMORY_LAYERKIT: + rtype = "CoreAnimation"; + break; + case VM_MEMORY_CGIMAGE: + rtype = "CG image"; + break; + case VM_MEMORY_TCMALLOC: + rtype = "WebKit Malloc"; + break; + case VM_MEMORY_COREGRAPHICS_DATA: + rtype = "CG raster data"; + break; + case VM_MEMORY_COREGRAPHICS_SHARED: + rtype = "CG shared images"; + break; + case VM_MEMORY_COREGRAPHICS_FRAMEBUFFERS: + rtype = "CG framebuffers"; + break; + case VM_MEMORY_COREGRAPHICS_BACKINGSTORES: + rtype = "CG backingstores"; + break; + case VM_MEMORY_DYLD: + rtype = "dyld private memory"; + break; + case VM_MEMORY_DYLD_MALLOC: + rtype = "dyld malloc memory"; + break; + case VM_MEMORY_SQLITE: + rtype = "SQlite page cache"; + break; + case VM_MEMORY_JAVASCRIPT_CORE: + rtype = "WebAssembly memory"; + break; + case VM_MEMORY_JAVASCRIPT_JIT_EXECUTABLE_ALLOCATOR: + rtype = "JS JIT generated code"; + break; + case VM_MEMORY_JAVASCRIPT_JIT_REGISTER_FILE: + rtype = "JS VM register file"; + break; + case VM_MEMORY_GLSL: + rtype = "OpenGL GLSL"; + break; + case VM_MEMORY_OPENCL: + rtype = "OpenCL"; + break; + case VM_MEMORY_COREIMAGE: + rtype = "CoreImage"; + break; + case VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS: + rtype = "WebCore purgable data"; + break; + case VM_MEMORY_IMAGEIO: + rtype = "Image IO"; + break; + case VM_MEMORY_COREPROFILE: + rtype = "CoreProfile"; + break; + case VM_MEMORY_ASSETSD: + rtype = "Assets Library"; + break; + case VM_MEMORY_OS_ALLOC_ONCE: + rtype = "OS Alloc Once"; + break; + case VM_MEMORY_LIBDISPATCH: + rtype = "Dispatch continuations"; + break; + case VM_MEMORY_ACCELERATE: + rtype = "Accelerate framework"; + break; + case VM_MEMORY_COREUI: + rtype = "CoreUI image data"; + break; + case VM_MEMORY_COREUIFILE: + rtype = "CoreUI image file"; + break; + case VM_MEMORY_GENEALOGY: + rtype = "Activity Tracing"; + break; + case VM_MEMORY_RAWCAMERA: + rtype = "RawCamera"; + break; + case VM_MEMORY_CORPSEINFO: + rtype = "Process Corpse Info"; + break; + case VM_MEMORY_ASL: + rtype = "Apple System Log"; + break; + case VM_MEMORY_SWIFT_RUNTIME: + rtype = "Swift runtime"; + break; + case VM_MEMORY_SWIFT_METADATA: + rtype = "Swift metadata"; + break; + case VM_MEMORY_DHMM: + rtype = "DHMM"; + break; + case VM_MEMORY_SCENEKIT: + rtype = "SceneKit"; + break; + case VM_MEMORY_SKYWALK: + rtype = "Skywalk Networking"; + break; +#endif + default: + rtype = NULL; + break; + } + if (rtype) + snprintf(tstr, sizeof (tag_str_t), "%s", rtype); + else + snprintf(tstr, sizeof (tag_str_t), "tag #%d", tag); + return tstr; +} + +const char * +str_tagr(tag_str_t tstr, const struct region *r) { + return str_tag(tstr, r->r_info.user_tag, r->r_info.share_mode, r->r_info.protection, r->r_info.external_pager); +} + /* * Put two strings together separated by a '+' sign - * If the string gets too long, then add an elipsis and + * If the string gets too long, then add an ellipsis and * stop concatenating it. */ char * @@ -89,3 +387,35 @@ strconcat(const char *s0, const char *s1, size_t maxlen) } return p; } + +unsigned long +simple_namehash(const char *nm) +{ + unsigned long result = 5381; + int c; + while (0 != (c = *nm++)) + result = (result * 33) ^ c; + return result; /* modified djb2 */ +} + +int +bounded_pwrite(int fd, const void *addr, size_t size, off_t off, bool *nocache, ssize_t *nwrittenp) +{ + if (opt->sizebound && off + (off_t)size > opt->sizebound) + return EFBIG; + + bool oldnocache = *nocache; + if (size >= opt->ncthresh && !oldnocache) + *nocache = 0 == fcntl(fd, F_NOCACHE, 1); + else if (size < opt->ncthresh && oldnocache) + *nocache = 0 != fcntl(fd, F_NOCACHE, 0); + if (OPTIONS_DEBUG(opt, 3) && oldnocache ^ *nocache) + printf("F_NOCACHE now %sabled on fd %d\n", *nocache ? "en" : "dis", fd); + + const ssize_t nwritten = pwrite(fd, addr, size, off); + if (-1 == nwritten) + return errno; + if (nwrittenp) + *nwrittenp = nwritten; + return 0; +} diff --git a/gcore.tproj/utils.h b/gcore.tproj/utils.h index 890f837..37eda58 100644 --- a/gcore.tproj/utils.h +++ b/gcore.tproj/utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Apple Inc. All rights reserved. + * Copyright (c) 2016 Apple Inc. All rights reserved. */ #include @@ -9,20 +9,34 @@ #include #include #include +#include #ifndef _UTILS_H #define _UTILS_H extern const char *pgm; +struct vm_range; struct region; -extern void err_mach(kern_return_t, const struct region *r, const char *fmt, ...) __printflike(3, 4); -extern void printr(const struct region *r, const char *fmt, ...) __printflike(2, 3); +extern void err_mach(kern_return_t, const struct region *, const char *, ...) __printflike(3, 4); +extern void printvr(const struct vm_range *, const char *, ...) __printflike(2, 3); +extern void printr(const struct region *, const char *, ...) __printflike(2, 3); typedef char hsize_str_t[7]; /* e.g. 1008Mib */ extern const char *str_hsize(hsize_str_t hstr, uint64_t); +extern const char *str_prot(vm_prot_t); +extern const char *str_shared(int); +extern const char *str_purgable(int, int); + +typedef char tag_str_t[24]; + +extern const char *str_tag(tag_str_t, int, int, vm_prot_t, int); +extern const char *str_tagr(tag_str_t, const struct region *); + extern char *strconcat(const char *, const char *, size_t); +extern unsigned long simple_namehash(const char *); +extern int bounded_pwrite(int, const void *, size_t, off_t, bool *, ssize_t *); #endif /* _UTILS_H */ diff --git a/gcore.tproj/vanilla.c b/gcore.tproj/vanilla.c index 61f9c09..2253bff 100644 --- a/gcore.tproj/vanilla.c +++ b/gcore.tproj/vanilla.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -32,8 +33,13 @@ #include -walk_return_t -vanilla_region_optimization(struct region *r, __unused void *arg) +/* + * (Another optimization to consider is merging adjacent regions with + * the same properties.) + */ + +static walk_return_t +simple_region_optimization(struct region *r, __unused void *arg) { assert(0 != R_SIZE(r)); @@ -41,7 +47,7 @@ vanilla_region_optimization(struct region *r, __unused void *arg) * Elide unreadable regions */ if ((r->r_info.max_protection & VM_PROT_READ) != VM_PROT_READ) { - if (opt->debug) + if (OPTIONS_DEBUG(opt, 2)) printr(r, "eliding unreadable region\n"); return WALK_DELETE_REGION; } @@ -50,7 +56,7 @@ vanilla_region_optimization(struct region *r, __unused void *arg) * Elide submaps (here for debugging purposes?) */ if (r->r_info.is_submap) { - if (opt->debug) + if (OPTIONS_DEBUG(opt)) printr(r, "eliding submap\n"); return WALK_DELETE_REGION; } @@ -61,26 +67,47 @@ vanilla_region_optimization(struct region *r, __unused void *arg) if (r->r_info.protection == VM_PROT_NONE && (VM_MEMORY_STACK == r->r_info.user_tag || VM_MEMORY_MALLOC == r->r_info.user_tag)) { - if (opt->debug) { + if (OPTIONS_DEBUG(opt, 2)) { hsize_str_t hstr; - printr(r, "eliding %s - guard\n", - str_hsize(hstr, R_SIZE(r))); + tag_str_t tstr; + printr(r, "elide %s - %s\n", str_hsize(hstr, R_SIZE(r)), str_tagr(tstr, r)); } return WALK_DELETE_REGION; } + + /* + * Regions full of clean zfod data e.g. VM_MEMORY_MALLOC_LARGE can be recorded as zfod + */ + if (r->r_info.share_mode == SM_PRIVATE && + 0 == r->r_info.external_pager && + 0 == r->r_info.pages_dirtied) { + if (OPTIONS_DEBUG(opt, 2)) { + hsize_str_t hstr; + tag_str_t tstr; + printr(r, "convert to zfod %s - %s\n", str_hsize(hstr, R_SIZE(r)), str_tagr(tstr, r)); + } + r->r_inzfodregion = true; + r->r_op = &zfod_ops; + } + return WALK_CONTINUE; } /* * (Paranoid validation + debugging assistance.) */ -static void +void validate_core_header(const native_mach_header_t *mh, off_t corefilesize) { - if (opt->debug) - printf("Core file: mh %p ncmds %u sizeofcmds %u\n", - mh, mh->ncmds, mh->sizeofcmds); + assert(NATIVE_MH_MAGIC == mh->magic); + assert(MH_CORE == mh->filetype); + if (OPTIONS_DEBUG(opt, 2)) + printf("%s: core file: mh %p ncmds %u sizeofcmds %u\n", + __func__, mh, mh->ncmds, mh->sizeofcmds); + + unsigned sizeofcmds = 0; + off_t corefilemaxoff = 0; const struct load_command *lc = (const void *)(mh + 1); for (unsigned i = 0; i < mh->ncmds; i++) { @@ -90,43 +117,41 @@ validate_core_header(const native_mach_header_t *mh, off_t corefilesize) lc, mh, (uintptr_t)mh + mh->sizeofcmds); abort(); } - if (opt->debug) - printf("lc %p cmd %u cmdsize %u ", lc, lc->cmd, lc->cmdsize); - - const native_segment_command_t *sc; - const struct proto_coreinfo_command *cic; - const struct proto_fileref_command *frc; - const struct thread_command *tc; + if (OPTIONS_DEBUG(opt, 2)) + printf("lc %p cmd %3u size %3u ", lc, lc->cmd, lc->cmdsize); + sizeofcmds += lc->cmdsize; switch (lc->cmd) { - case NATIVE_LC_SEGMENT: - sc = (const void *)lc; - if (opt->debug) { - printf("%8s: mem %llx-%llx file %lld-%lld %x/%x flags %x\n", + case NATIVE_LC_SEGMENT: { + const native_segment_command_t *sc = (const void *)lc; + if (OPTIONS_DEBUG(opt, 2)) { + printf("%8s: mem %llx-%llx file %lld-%lld %s/%s nsect %u flags %x\n", "SEGMENT", (mach_vm_offset_t)sc->vmaddr, (mach_vm_offset_t)sc->vmaddr + sc->vmsize, (off_t)sc->fileoff, (off_t)sc->fileoff + (off_t)sc->filesize, - sc->initprot, sc->maxprot, sc->flags); + str_prot(sc->initprot), str_prot(sc->maxprot), + sc->nsects, sc->flags); } if ((off_t)sc->fileoff < mh->sizeofcmds || (off_t)sc->filesize < 0) { warnx("bad segment command"); abort(); } - if ((off_t)sc->fileoff > corefilesize || - (off_t)sc->fileoff + (off_t)sc->filesize > corefilesize) { + const off_t endoff = (off_t)sc->fileoff + (off_t)sc->filesize; + if ((off_t)sc->fileoff > corefilesize || endoff > corefilesize) { /* * We may have run out of space to write the data */ warnx("segment command points beyond end of file"); } + corefilemaxoff = MAX(corefilemaxoff, endoff); break; - - case proto_LC_COREINFO: - cic = (const void *)lc; - if (opt->debug) { + } + case proto_LC_COREINFO: { + const struct proto_coreinfo_command *cic = (const void *)lc; + if (OPTIONS_DEBUG(opt, 2)) { uuid_string_t uustr; uuid_unparse_lower(cic->uuid, uustr); printf("%8s: version %d type %d uuid %s addr %llx dyninfo %llx\n", @@ -138,21 +163,27 @@ validate_core_header(const native_mach_header_t *mh, off_t corefilesize) abort(); } break; - - case proto_LC_FILEREF: - frc = (const void *)lc; + } + case proto_LC_FILEREF: { + const struct proto_fileref_command *frc = (const void *)lc; const char *nm = frc->filename.offset + (char *)lc; - if (opt->debug) { - uuid_string_t uustr; - uuid_unparse_lower(frc->uuid, uustr); - printf("%8s: mem %llx-%llx file %lld-%lld %x/%x '%s' %.12s..\n", - "FILEREF", - frc->vmaddr, - frc->vmaddr + frc->vmsize, + if (OPTIONS_DEBUG(opt, 2)) { + printf("%8s: mem %llx-%llx file %lld-%lld %s/%s '%s'\n", + "FREF", + frc->vmaddr, frc->vmaddr + frc->vmsize, (off_t)frc->fileoff, (off_t)frc->fileoff + (off_t)frc->filesize, - frc->initprot, frc->maxprot, nm, uustr); + str_prot(frc->prot), str_prot(frc->maxprot), nm); } + switch (FREF_ID_TYPE(frc->flags)) { + case kFREF_ID_UUID: + case kFREF_ID_MTIMESPEC_LE: + case kFREF_ID_NONE: + break; + default: + warnx("unknown fref id type: flags %x", frc->flags); + abort(); + } if (nm <= (caddr_t)lc || nm > (caddr_t)lc + lc->cmdsize || (off_t)frc->fileoff < 0 || (off_t)frc->filesize < 0) { @@ -160,20 +191,45 @@ validate_core_header(const native_mach_header_t *mh, off_t corefilesize) abort(); } break; - - case LC_THREAD: - tc = (const void *)lc; - if (opt->debug) + } + case proto_LC_COREDATA: { + const struct proto_coredata_command *cc = (const void *)lc; + if (OPTIONS_DEBUG(opt, 2)) { + printf("%8s: mem %llx-%llx file %lld-%lld %s/%s flags %x\n", + "COREDATA", + cc->vmaddr, cc->vmaddr + cc->vmsize, + (off_t)cc->fileoff, + (off_t)cc->fileoff + (off_t)cc->filesize, + str_prot(cc->prot), str_prot(cc->maxprot), cc->flags); + } + if ((off_t)cc->fileoff < mh->sizeofcmds || + (off_t)cc->filesize < 0) { + warnx("bad COREDATA command"); + abort(); + } + const off_t endoff = (off_t)cc->fileoff + (off_t)cc->filesize; + if ((off_t)cc->fileoff > corefilesize || endoff > corefilesize) { + /* + * We may have run out of space to write the data + */ + warnx("segment command points beyond end of file"); + } + corefilemaxoff = MAX(corefilemaxoff, endoff); + break; + } + case LC_THREAD: { + const struct thread_command *tc = (const void *)lc; + if (OPTIONS_DEBUG(opt, 2)) printf("%8s:\n", "THREAD"); uint32_t *wbuf = (void *)(tc + 1); do { const uint32_t flavor = *wbuf++; const uint32_t count = *wbuf++; - if (opt->debug) { + if (OPTIONS_DEBUG(opt, 2)) { printf(" flavor %u count %u\n", flavor, count); if (count) { - boolean_t nl = false; + bool nl = false; for (unsigned k = 0; k < count; k++) { if (0 == (k & 7)) printf(" [%3u] ", k); @@ -196,9 +252,9 @@ validate_core_header(const native_mach_header_t *mh, off_t corefilesize) } } while ((caddr_t) wbuf < (caddr_t)tc + tc->cmdsize); break; - + } default: - warnx("unknown cmd %u in header\n", lc->cmd); + warnx("unknown cmd %u in header", lc->cmd); abort(); } if (lc->cmdsize) @@ -206,6 +262,12 @@ validate_core_header(const native_mach_header_t *mh, off_t corefilesize) else break; } + if (corefilemaxoff < corefilesize) + warnx("unused data after corefile offset %lld", corefilemaxoff); + if (sizeofcmds != mh->sizeofcmds) { + warnx("inconsistent mach header %u vs. %u", sizeofcmds, mh->sizeofcmds); + abort(); + } } /* @@ -217,12 +279,16 @@ validate_core_header(const native_mach_header_t *mh, off_t corefilesize) * * - LC_SEGMENT{,_64} pointing at memory content in the file, * each chunk consisting of a contiguous region. Regions may be zfod + * (no file content present). + * + * - proto_LC_COREDATA pointing at memory content in the file, + * each chunk consisting of a contiguous region. Regions may be zfod * (no file content present) or content may be compressed (experimental) * - * - prototype_LC_COREINFO (experimental), pointing at dyld (10.12 onwards) + * - proto_LC_COREINFO (experimental), pointing at dyld (10.12 onwards) * - * - prototype_LC_FILEREF (experimental) pointing at memory - * content to be mapped in from another file at various offsets + * - proto_LC_FILEREF (experimental) pointing at memory + * content to be mapped in from another uuid-tagged file at various offsets * * - LC_THREAD commands with state for each thread * @@ -255,10 +321,16 @@ coredump_write( thread_count = 0; } - if (opt->debug) { - print_memory_region_header(); - walk_region_list(rhead, region_print_memory, NULL); - } + if (OPTIONS_DEBUG(opt, 3)) { + print_memory_region_header(); + walk_region_list(rhead, region_print_memory, NULL); + printf("\nmach header %lu\n", sizeof (native_mach_header_t)); + printf("threadcount %u threadsize %lu\n", thread_count, thread_count * sizeof_LC_THREAD()); + printf("fileref %lu %lu %llu\n", ssda.ssd_fileref.count, ssda.ssd_fileref.headersize, ssda.ssd_fileref.memsize); + printf("zfod %lu %lu %llu\n", ssda.ssd_zfod.count, ssda.ssd_zfod.headersize, ssda.ssd_zfod.memsize); + printf("vanilla %lu %lu %llu\n", ssda.ssd_vanilla.count, ssda.ssd_vanilla.headersize, ssda.ssd_vanilla.memsize); + printf("sparse %lu %lu %llu\n", ssda.ssd_sparse.count, ssda.ssd_sparse.headersize, ssda.ssd_sparse.memsize); + } size_t headersize = sizeof (native_mach_header_t) + thread_count * sizeof_LC_THREAD() + @@ -266,7 +338,7 @@ coredump_write( ssda.ssd_zfod.headersize + ssda.ssd_vanilla.headersize + ssda.ssd_sparse.headersize; - if (opt->coreinfo) + if (opt->extended) headersize += sizeof (struct proto_coreinfo_command); void *header = calloc(1, headersize); @@ -276,17 +348,17 @@ coredump_write( native_mach_header_t *mh = make_corefile_mach_header(header); struct load_command *lc = (void *)(mh + 1); - if (opt->coreinfo) { + if (opt->extended) { const struct proto_coreinfo_command *cc = make_coreinfo_command(mh, lc, aout_uuid, aout_load_addr, dyld_aii_addr); lc = (void *)((caddr_t)cc + cc->cmdsize); } - if (opt->debug) { + if (opt->verbose) { const unsigned long fileref_count = ssda.ssd_fileref.count; const unsigned long segment_count = fileref_count + ssda.ssd_zfod.count + ssda.ssd_vanilla.count + ssda.ssd_sparse.count; - printf("Dumping %lu memory segments", segment_count); + printf("Writing %lu segments", segment_count); if (0 != fileref_count) printf(" (including %lu file reference%s (%lu bytes))", fileref_count, 1 == fileref_count ? "" : "s", @@ -294,19 +366,20 @@ coredump_write( printf("\n"); } - vm_size_t pagesize = ((vm_offset_t)1 << pageshift_host); - vm_offset_t pagemask = (vm_offset_t)(pagesize - 1); + mach_vm_offset_t pagesize = ((mach_vm_offset_t)1 << pageshift_host); + mach_vm_offset_t pagemask = pagesize - 1; struct write_segment_data wsda = { .wsd_task = task, .wsd_mh = mh, .wsd_lc = lc, .wsd_fd = fd, - .wsd_foffset = ((vm_offset_t)headersize + pagemask) & ~pagemask, + .wsd_nocache = false, + .wsd_foffset = ((mach_vm_offset_t)headersize + pagemask) & ~pagemask, .wsd_nwritten = 0, }; - int ecode = 0; + int ecode = 0; if (0 != walk_region_list(rhead, region_write_memory, &wsda)) ecode = EX_IOERR; @@ -324,9 +397,11 @@ coredump_write( * Even if we've run out of space, try our best to * write out the header. */ - if (-1 == pwrite(fd, header, headersize, 0)) + if (0 != bounded_pwrite(fd, header, headersize, 0, &wsda.wsd_nocache, NULL)) ecode = EX_IOERR; - else + if (0 == ecode && headersize != sizeof (*mh) + mh->sizeofcmds) + ecode = EX_SOFTWARE; + if (0 == ecode) wsda.wsd_nwritten += headersize; validate_core_header(mh, wsda.wsd_foffset); @@ -349,17 +424,137 @@ coredump_write( return ecode; } +static void +addfileref(struct region *r, const struct libent *le, const char *nm) +{ + r->r_fileref = calloc(1, sizeof (*r->r_fileref)); + if (r->r_fileref) { + if (le) { + assert(NULL == nm); + r->r_fileref->fr_libent = le; + r->r_fileref->fr_pathname = le->le_pathname; + } else { + assert(NULL == le); + r->r_fileref->fr_pathname = strdup(nm); + } + r->r_fileref->fr_offset = r->r_pageinfo.offset; + r->r_op = &fileref_ops; + } +} + +/* + * Once all the info about the shared cache (filerefs) and the information from + * dyld (filerefs and subregions), take one last look for mappings + * of filesystem content to convert to additional filerefs. + * + * By default we are pessimistic: read-only mappings on read-only root. + */ +static walk_return_t +label_mapped_files(struct region *r, void *arg) +{ + const struct proc_bsdinfo *pbi = arg; + + if (r->r_fileref || r->r_insharedregion || r->r_incommregion || r->r_inzfodregion) + return WALK_CONTINUE; + if (r->r_nsubregions) + return WALK_CONTINUE; + if (!r->r_info.external_pager) + return WALK_CONTINUE; + if (!opt->allfilerefs) { + /* must be mapped without write permission */ + if (0 != (r->r_info.protection & VM_PROT_WRITE)) + return WALK_CONTINUE; + } + + char pathbuf[MAXPATHLEN+1]; + pathbuf[0] = '\0'; + int len = proc_regionfilename(pbi->pbi_pid, R_ADDR(r), pathbuf, sizeof (pathbuf)-1); + if (len <= 0 || len > MAXPATHLEN) + return WALK_CONTINUE; + pathbuf[len] = 0; + +#if 0 + /* + * On the desktop, only refer to files beginning with particular + * prefixes to increase the likelihood that we'll be able to + * find the content later on. + * + * XXX Not practical with a writable root, but helpful for testing. + */ + static const char *white[] = { + "/System", + "/Library", + "/usr", + }; + const unsigned nwhite = sizeof (white) / sizeof (white[0]); + bool skip = true; + for (unsigned i = 0; skip && i < nwhite; i++) + skip = 0 != strncmp(white[i], pathbuf, strlen(white[i])); + if (skip) { + if (OPTIONS_DEBUG(opt, 3)) + printf("\t(%s not included)\n", pathbuf); + return WALK_CONTINUE; + } + static const char *black[] = { + "/System/Library/Caches", + "/Library/Caches", + "/usr/local", + }; + const unsigned nblack = sizeof (black) / sizeof (black[0]); + for (unsigned i = 0; !skip && i < nblack; i++) + skip = 0 == strncmp(black[i], pathbuf, strlen(black[i])); + if (skip) { + if (OPTIONS_DEBUG(opt, 3)) + printf("\t(%s excluded)\n", pathbuf); + return WALK_CONTINUE; + } +#endif + + struct statfs stfs; + if (-1 == statfs(pathbuf, &stfs)) { + switch (errno) { + case EACCES: + case EPERM: + case ENOENT: + break; + default: + warnc(errno, "statfs: %s", pathbuf); + break; + } + return WALK_CONTINUE; + } + + do { + if (OPTIONS_DEBUG(opt, 2)) + printr(r, "found mapped file %s\n", pathbuf); + if (!opt->allfilerefs) { + if ((stfs.f_flags & MNT_ROOTFS) != MNT_ROOTFS) + break; // must be on the root filesystem + if ((stfs.f_flags & MNT_RDONLY) != MNT_RDONLY) + break; // must be on a read-only filesystem + } + if (OPTIONS_DEBUG(opt, 2)) + print_memory_region(r); + addfileref(r, NULL, pathbuf); + } while (0); + + return WALK_CONTINUE; +} + int -coredump(task_t task, int fd) +coredump(task_t task, int fd, const struct proc_bsdinfo *__unused pbi) { /* this is the shared cache id, if any */ uuid_t sc_uuid; uuid_clear(sc_uuid); - dyld_process_info dpi = get_task_dyld_info(task); - if (dpi) { - get_sc_uuid(dpi, sc_uuid); - } + dyld_process_info dpi = NULL; + if (opt->extended) { + dpi = get_task_dyld_info(task); + if (dpi) { + get_sc_uuid(dpi, sc_uuid); + } + } /* this group is for LC_COREINFO */ mach_vm_offset_t dyld_addr = 0; // all_image_infos -or- dyld mach header @@ -377,60 +572,74 @@ coredump(task_t task, int fd) goto done; } - if (opt->debug) + if (OPTIONS_DEBUG(opt, 1)) printf("Optimizing dump content\n"); - walk_region_list(rhead, vanilla_region_optimization, NULL); + walk_region_list(rhead, simple_region_optimization, NULL); - if (dpi) { - if (opt->coreinfo || opt->sparse) { - /* - * Snapshot dyld's info .. - */ - if (!libent_build_nametable(task, dpi)) - warnx("error parsing dyld data => ignored"); - else { - if (opt->coreinfo) { - /* - * Find the a.out load address and uuid, and the dyld mach header for the coreinfo - */ - const struct libent *le; - if (NULL != (le = libent_lookup_first_bytype(MH_EXECUTE))) { - aout_load_addr = le->le_mhaddr; - uuid_copy(aout_uuid, le->le_uuid); - } - if (NULL != (le = libent_lookup_first_bytype(MH_DYLINKER))) { - dyld_addr = le->le_mhaddr; - } - } - if (opt->sparse) { - /* - * Use dyld's view of what's being used in the address - * space to shrink the dump. - */ - if (0 == walk_region_list(rhead, decorate_memory_region, (void *)dpi)) { - if (opt->debug) - printf("Performing sparse dump optimization(s)\n"); - walk_region_list(rhead, sparse_region_optimization, NULL); - } else { - walk_region_list(rhead, undecorate_memory_region, NULL); - warnx("error parsing dyld data => ignored"); - } - } - } - } - free_task_dyld_info(dpi); - } + if (dpi) { + /* + * Snapshot dyld's info .. + */ + if (!libent_build_nametable(task, dpi)) + warnx("error parsing dyld data => ignored"); + else { + /* + * Find the a.out load address and uuid, and the dyld mach header for the coreinfo + */ + const struct libent *le; + if (NULL != (le = libent_lookup_first_bytype(MH_EXECUTE))) { + aout_load_addr = le->le_mhaddr; + uuid_copy(aout_uuid, le->le_uuid); + } + if (NULL != (le = libent_lookup_first_bytype(MH_DYLINKER))) { + dyld_addr = le->le_mhaddr; + } - if (opt->debug) + /* + * Use dyld's view of what's being used in the address + * space to shrink the dump. + */ + if (OPTIONS_DEBUG(opt, 1)) + printf("Decorating dump with dyld-derived data\n"); + if (0 == walk_region_list(rhead, decorate_memory_region, (void *)dpi)) { + if (OPTIONS_DEBUG(opt, 1)) + printf("Sparse dump optimization(s)\n"); + walk_region_list(rhead, sparse_region_optimization, NULL); + } else { + walk_region_list(rhead, undecorate_memory_region, NULL); + warnx("error parsing dyld data => ignored"); + } + } + free_task_dyld_info(dpi); + } + + /* + * Hunt for any memory mapped files that we can include by reference + * Depending on whether the bsd part of the task is still present + * we might be able to determine filenames of other regions mapping + * them here - this allows fonts, images, and other read-only content + * to be converted into file references, further reducing the size + * of the dump. + * + * NOTE: Even though the corpse snapshots the VM, the filesystem is + * not correspondingly snapshotted and thus may mutate while the dump + * proceeds - so be pessimistic about inclusion. + */ + if (opt->extended && NULL != pbi) { + if (OPTIONS_DEBUG(opt, 1)) + printf("Mapped file optimization\n"); + walk_region_list(rhead, label_mapped_files, (void *)pbi); + } + + if (OPTIONS_DEBUG(opt, 1)) printf("Optimization(s) done\n"); + done: if (0 == ecode) ecode = coredump_write(task, fd, rhead, aout_uuid, aout_load_addr, dyld_addr); return ecode; } -#ifdef CONFIG_REFSC - struct find_shared_cache_args { task_t fsc_task; vm_object_id_t fsc_object_id; @@ -458,10 +667,9 @@ find_shared_cache(struct region *r, void *arg) if (r->r_pageinfo.offset != 0) return WALK_CONTINUE; /* must map beginning of file */ - if (opt->debug) { + if (OPTIONS_DEBUG(opt, 1)) { hsize_str_t hstr; - printf("Examining shared cache candidate %llx-%llx (%s)\n", - R_ADDR(r), R_ENDADDR(r), str_hsize(hstr, R_SIZE(r))); + printr(r, "examining %s shared cache candidate\n", str_hsize(hstr, R_SIZE(r))); } struct copied_dyld_cache_header *ch; @@ -469,20 +677,20 @@ find_shared_cache(struct region *r, void *arg) kern_return_t ret = mach_vm_read(fsc->fsc_task, R_ADDR(r), sizeof (*ch), (vm_offset_t *)&ch, &chlen); if (KERN_SUCCESS != ret) { - err_mach(ret, NULL, "mapping candidate shared region"); + err_mach(ret, NULL, "mach_vm_read() candidate shared region header"); return WALK_CONTINUE; } uuid_t scuuid; if (get_uuid_from_shared_cache_mapping(ch, chlen, scuuid) && uuid_compare(scuuid, fsc->fsc_uuid) == 0) { - if (opt->debug > 2) { + if (OPTIONS_DEBUG(opt, 1)) { uuid_string_t uustr; uuid_unparse_lower(fsc->fsc_uuid, uustr); printr(r, "found shared cache %s here\n", uustr); } if (!r->r_info.external_pager) { - if (opt->debug) + if (OPTIONS_DEBUG(opt, 1)) printf("Hmm. Found shared cache magic# + uuid, but not externally paged?\n"); #if 0 return WALK_CONTINUE; /* should be "paged" from a file */ @@ -495,7 +703,7 @@ find_shared_cache(struct region *r, void *arg) } mach_vm_deallocate(mach_task_self(), (vm_offset_t)ch, chlen); if (fsc->fsc_object_id) { - if (opt->debug) { + if (OPTIONS_DEBUG(opt, 3)) { uuid_string_t uu; uuid_unparse_lower(fsc->fsc_uuid, uu); printf("Shared cache objid %llx uuid %s\n", @@ -506,23 +714,23 @@ find_shared_cache(struct region *r, void *arg) return WALK_CONTINUE; } -static boolean_t +static bool compare_region_with_shared_cache(const struct region *r, struct find_shared_cache_args *fsc) { struct stat st; if (-1 == fstat(fsc->fsc_fd, &st)) { - if (opt->debug) - printr(r, "%s - %s\n", + if (OPTIONS_DEBUG(opt, 1)) + printr(r, "cannot fstat %s - %s\n", fsc->fsc_le->le_filename, strerror(errno)); return false; } - void *file = mmap(NULL, (size_t)R_SIZE(r), PROT_READ, MAP_PRIVATE, fsc->fsc_fd, r->r_pageinfo.offset); + void *file = mmap(NULL, R_SIZEOF(r), PROT_READ, MAP_PRIVATE, fsc->fsc_fd, r->r_pageinfo.offset); if ((void *)-1L == file) { - if (opt->debug) + if (OPTIONS_DEBUG(opt, 1)) printr(r, "mmap %s - %s\n", fsc->fsc_le->le_filename, strerror(errno)); return false; } - madvise(file, (size_t)R_SIZE(r), MADV_SEQUENTIAL); + madvise(file, R_SIZEOF(r), MADV_SEQUENTIAL); vm_offset_t data = 0; mach_msg_type_number_t data_count; @@ -530,7 +738,7 @@ compare_region_with_shared_cache(const struct region *r, struct find_shared_cach if (KERN_SUCCESS != kr || data_count < R_SIZE(r)) { err_mach(kr, r, "mach_vm_read()"); - munmap(file, (size_t)R_SIZE(r)); + munmap(file, R_SIZEOF(r)); return false; } @@ -549,7 +757,7 @@ compare_region_with_shared_cache(const struct region *r, struct find_shared_cach * Check what's really mapped there and reduce the size accordingly. */ if (!is_actual_size(fsc->fsc_task, r, &cmpsize)) { - if (opt->debug) + if (OPTIONS_DEBUG(opt, 3)) printr(r, "narrowing the comparison (%llu " "-> %llu)\n", R_SIZE(r), cmpsize); } @@ -558,7 +766,7 @@ compare_region_with_shared_cache(const struct region *r, struct find_shared_cach mach_vm_behavior_set(mach_task_self(), data, data_count, VM_BEHAVIOR_SEQUENTIAL); - const boolean_t thesame = memcmp(file, (void *)data, (size_t)cmpsize) == 0; + const bool thesame = memcmp(file, (void *)data, (size_t)cmpsize) == 0; #if 0 if (!thesame) { int diffcount = 0; @@ -577,9 +785,9 @@ compare_region_with_shared_cache(const struct region *r, struct find_shared_cach } #endif mach_vm_deallocate(mach_task_self(), data, data_count); - munmap(file, (size_t)R_SIZE(r)); + munmap(file, R_SIZEOF(r)); - if (!thesame && opt->debug) + if (!thesame && OPTIONS_DEBUG(opt, 3)) printr(r, "mapped file (%s) region is modified\n", fsc->fsc_le->le_filename); return thesame; } @@ -598,47 +806,38 @@ label_shared_cache(struct region *r, void *arg) return WALK_CONTINUE; } if (((r->r_info.protection | r->r_info.max_protection) & VM_PROT_WRITE) != 0) { - /* writable, but was it written? */ - if (r->r_info.pages_dirtied + r->r_info.pages_swapped_out != 0) - return WALK_CONTINUE; // a heuristic .. + /* potentially writable, but was it written? */ + if (0 != r->r_info.pages_dirtied) + return WALK_CONTINUE; + if (0 != r->r_info.pages_swapped_out) + return WALK_CONTINUE; + if (0 != r->r_info.pages_resident && !r->r_info.external_pager) + return WALK_CONTINUE; + if (OPTIONS_DEBUG(opt, 1)) + printr(r, "verifying shared cache content against memory image\n"); if (!compare_region_with_shared_cache(r, fsc)) { /* bits don't match */ + if (OPTIONS_DEBUG(opt, 1)) + printr(r, "hmm .. mismatch: using memory image\n"); return WALK_CONTINUE; } - } - - if (opt->debug > 2) { - /* this validation is -really- expensive */ - if (!compare_region_with_shared_cache(r, fsc)) - printr(r, "WARNING: region should match, but doesn't\n"); - } + } /* * This mapped file segment will be represented as a reference - * to the file, rather than as a copy of the file. + * to the file, rather than as a copy of the mapped file. */ - const struct libent *le = libent_lookup_byuuid(fsc->fsc_uuid); - r->r_fileref = calloc(1, sizeof (*r->r_fileref)); - if (r->r_fileref) { - r->r_fileref->fr_libent = le; - if (r->r_fileref->fr_libent) { - r->r_fileref->fr_offset = r->r_pageinfo.offset; - r->r_op = &fileref_ops; - } else { - free(r->r_fileref); - r->r_fileref = NULL; - } - } - return WALK_CONTINUE; + addfileref(r, libent_lookup_byuuid(fsc->fsc_uuid), NULL); + return WALK_CONTINUE; } -#endif /* CONFIG_REFSC */ struct regionhead * coredump_prepare(task_t task, uuid_t sc_uuid) { struct regionhead *rhead = build_region_list(task); - if (opt->debug) { + if (OPTIONS_DEBUG(opt, 2)) { + printf("Region list built\n"); print_memory_region_header(); walk_region_list(rhead, region_print_memory, NULL); } @@ -653,24 +852,27 @@ coredump_prepare(task_t task, uuid_t sc_uuid) const struct libent *le; if (NULL != nm) - le = libent_insert(nm, sc_uuid, 0, NULL); + le = libent_insert(nm, sc_uuid, 0, NULL, NULL, 0); else { - le = libent_insert("(shared cache)", sc_uuid, 0, NULL); + libent_insert("(anonymous shared cache)", sc_uuid, 0, NULL, NULL, 0); if (opt->verbose){ - uuid_string_t uustr; - uuid_unparse_lower(sc_uuid, uustr); - printf("Shared cache UUID: %s, but no filename => ignored\n", uustr); - return rhead; + printf("Warning: cannot name the shared cache "); + if (OPTIONS_DEBUG(opt, 1)) { + uuid_string_t uustr; + uuid_unparse_lower(sc_uuid, uustr); + printf("(%s) ", uustr); + } + printf("- dump may be large!\n"); } + return rhead; } -#ifdef CONFIG_REFSC - if (opt->scfileref) { + if (opt->extended) { /* * See if we can replace entire regions with references to the shared cache * by looking at the VM meta-data about those regions. */ - if (opt->debug) { + if (OPTIONS_DEBUG(opt, 1)) { uuid_string_t uustr; uuid_unparse_lower(sc_uuid, uustr); printf("Searching for shared cache with uuid %s\n", uustr); @@ -694,15 +896,16 @@ coredump_prepare(task_t task, uuid_t sc_uuid) if (opt->verbose) printf("Referenced %s\n", nm); fsca.fsc_le = le; - fsca.fsc_fd = open(fsca.fsc_le->le_filename, O_RDONLY); - - walk_region_list(rhead, label_shared_cache, &fsca); - - close(fsca.fsc_fd); + fsca.fsc_fd = open(fsca.fsc_le->le_pathname, O_RDONLY); + if (-1 == fsca.fsc_fd) + errc(EX_SOFTWARE, errno, "open %s", fsca.fsc_le->le_pathname); + else { + walk_region_list(rhead, label_shared_cache, &fsca); + close(fsca.fsc_fd); + } free(nm); } } -#endif /* CONFIG_REFSC */ return rhead; } diff --git a/gcore.tproj/vanilla.h b/gcore.tproj/vanilla.h index ac29e85..721c653 100644 --- a/gcore.tproj/vanilla.h +++ b/gcore.tproj/vanilla.h @@ -7,9 +7,10 @@ #ifndef _VANILLA_H #define _VANILLA_H -extern walk_region_cbfn_t vanilla_region_optimization; +struct proc_bsdinfo; -extern int coredump(task_t, int); +extern void validate_core_header(const native_mach_header_t *, off_t); +extern int coredump(task_t, int, const struct proc_bsdinfo *); extern int coredump_write(task_t, int, struct regionhead *, const uuid_t, mach_vm_offset_t, mach_vm_offset_t); extern struct regionhead *coredump_prepare(task_t, uuid_t); diff --git a/gcore.tproj/vm.c b/gcore.tproj/vm.c index fdd6814..22b0efe 100644 --- a/gcore.tproj/vm.c +++ b/gcore.tproj/vm.c @@ -15,7 +15,6 @@ #include #include #include - #include /* @@ -70,9 +69,7 @@ new_region(mach_vm_offset_t vmaddr, mach_vm_size_t vmsize, const vm_region_subma R_SETADDR(r, vmaddr); R_SETSIZE(r, vmsize); r->r_info = *infop; -#ifdef CONFIG_PURGABLE r->r_purgable = VM_PURGABLE_DENY; -#endif r->r_insharedregion = in_shared_region(vmaddr); r->r_incommregion = in_comm_region(vmaddr, &r->r_info); r->r_inzfodregion = in_zfod_region(&r->r_info); @@ -84,7 +81,6 @@ new_region(mach_vm_offset_t vmaddr, mach_vm_size_t vmsize, const vm_region_subma return r; } -#ifdef CONFIG_REFSC void del_fileref_region(struct region *r) { @@ -95,16 +91,13 @@ del_fileref_region(struct region *r) poison(r, 0xdeadbeeb, sizeof (*r)); free(r); } -#endif /* CONFIG_REFSC */ void del_zfod_region(struct region *r) { assert(&zfod_ops == r->r_op); assert(r->r_inzfodregion && 0 == r->r_nsubregions); -#ifdef CONFIG_REFSC assert(NULL == r->r_fileref); -#endif poison(r, 0xdeadbeed, sizeof (*r)); free(r); } @@ -114,9 +107,7 @@ del_vanilla_region(struct region *r) { assert(&vanilla_ops == r->r_op); assert(!r->r_inzfodregion && 0 == r->r_nsubregions); -#ifdef CONFIG_REFSC assert(NULL == r->r_fileref); -#endif poison(r, 0xdeadbeef, sizeof (*r)); free(r); } @@ -174,9 +165,10 @@ walk_regions(task_t task, struct regionhead *rhead) mach_vm_offset_t vm_addr = MACH_VM_MIN_ADDRESS; natural_t depth = 0; - if (opt->debug > 3) + if (OPTIONS_DEBUG(opt, 3)) { + printf("Building raw region list\n"); print_memory_region_header(); - + } while (1) { vm_region_submap_info_data_64_t info; mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; @@ -194,7 +186,7 @@ walk_regions(task_t task, struct regionhead *rhead) goto bad; } - if (opt->debug > 3) { + if (OPTIONS_DEBUG(opt, 3)) { struct region *d = new_region(vm_addr, vm_size, &info); ROP_PRINT(d); ROP_DELETE(d); @@ -227,14 +219,13 @@ walk_regions(task_t task, struct regionhead *rhead) if (KERN_SUCCESS != ret) err_mach(ret, r, "getting pageinfo at %llx", R_ADDR(r)); -#ifdef CONFIG_PURGABLE /* record the purgability */ ret = mach_vm_purgable_control(task, vm_addr, VM_PURGABLE_GET_STATE, &r->r_purgable); if (KERN_SUCCESS != ret) r->r_purgable = VM_PURGABLE_DENY; -#endif - STAILQ_INSERT_TAIL(rhead, r, r_linkage); + + STAILQ_INSERT_TAIL(rhead, r, r_linkage); vm_addr += vm_size; } @@ -307,7 +298,7 @@ setpageshift(void) pshift++; pageshift_host = pshift; } - if (opt->debug) + if (OPTIONS_DEBUG(opt, 3)) printf("host page size: %lu\n", 1ul << pageshift_host); if (0 == pageshift_app) { @@ -317,58 +308,10 @@ setpageshift(void) pshift++; pageshift_app = pshift; } - if (opt->debug && pageshift_app != pageshift_host) + if (OPTIONS_DEBUG(opt, 3) && pageshift_app != pageshift_host) printf("app page size: %lu\n", 1ul << pageshift_app); } -static const char * -strshared(const int sm) -{ - switch (sm) { - case SM_COW: - return "cow"; - case SM_PRIVATE: - return "priv"; - case SM_EMPTY: - return "empty"; - case SM_SHARED: - return "shr"; - case SM_TRUESHARED: - return "true_shr"; - case SM_PRIVATE_ALIASED: - return "priv_alias"; - case SM_SHARED_ALIASED: - return "shr_alias"; - case SM_LARGE_PAGE: - return "large_pg"; - default: - return "share?"; - } -} - -typedef char prot_str_t[9]; /* rwxNCWT& */ - -static const char * -str_prot(prot_str_t pstr, const vm_prot_t prot) -{ - snprintf(pstr, sizeof (prot_str_t), "%c%c%c", - prot & VM_PROT_READ ? 'r' : '-', - prot & VM_PROT_WRITE ? 'w' : '-', - prot & VM_PROT_EXECUTE ? 'x' : '-'); - /* for completeness */ - if (prot & VM_PROT_NO_CHANGE) - strlcat(pstr, "N", sizeof (prot_str_t)); - if (prot & VM_PROT_COPY) - strlcat(pstr, "C", sizeof (prot_str_t)); - if (prot & VM_PROT_WANTS_COPY) - strlcat(pstr, "W", sizeof (prot_str_t)); - if (prot & 0x20) - strlcat(pstr, "T", sizeof (prot_str_t)); - if (prot & VM_PROT_IS_MASK) - strlcat(pstr, "&", sizeof (prot_str_t)); - return pstr; -} - void print_memory_region_header(void) { @@ -387,10 +330,8 @@ print_memory_region_header(void) static __inline char region_type(const struct region *r) { -#ifdef CONFIG_REFSC if (r->r_fileref) return 'f'; -#endif if (r->r_inzfodregion) return 'z'; if (r->r_incommregion) @@ -403,14 +344,14 @@ region_type(const struct region *r) void print_memory_region(const struct region *r) { - prot_str_t pstr, pstr_max; hsize_str_t hstr; + tag_str_t tstr; printf("%016llx-%016llx %c %-7s %s/%s %8x %16llx ", R_ADDR(r), R_ENDADDR(r), region_type(r), str_hsize(hstr, R_SIZE(r)), - str_prot(pstr, r->r_info.protection), - str_prot(pstr_max, r->r_info.max_protection), + str_prot(r->r_info.protection), + str_prot(r->r_info.max_protection), r->r_info.object_id, r->r_pageinfo.object_id ); @@ -418,7 +359,7 @@ print_memory_region(const struct region *r) r->r_info.external_pager ? r->r_pageinfo.offset : r->r_info.offset, r->r_info.user_tag, - strshared(r->r_info.share_mode), + str_shared(r->r_info.share_mode), r->r_info.ref_count ); #ifdef CONFIG_SUBMAP @@ -431,12 +372,12 @@ print_memory_region(const struct region *r) r->r_info.pages_shared_now_private, r->r_info.pages_dirtied, r->r_info.external_pager ? "ext" : ""); -#if CONFIG_REFSC - if (r->r_fileref) + if (r->r_fileref) printf("\n %s at %lld ", - r->r_fileref->fr_libent->le_filename, + r->r_fileref->fr_pathname, r->r_fileref->fr_offset); -#endif + else + printf("%s", str_tagr(tstr, r)); printf("\n"); if (r->r_nsubregions) { printf(" %-33s %7s %12s\t%s\n", @@ -450,19 +391,8 @@ print_memory_region(const struct region *r) S_FILENAME(s)); } } - } else { - switch (r->r_info.user_tag) { - case VM_MEMORY_SHARED_PMAP: - printf("// VM_MEMORY_SHARED_PMAP"); - break; - case VM_MEMORY_UNSHARED_PMAP: - printf("// VM_MEMORY_UNSHARED_PMAP"); - break; - default: - printf("// is a submap"); - break; - } - printf("\n"); + } else { + printf("%5s %5s %5s %3s %s\n", "", "", "", "", str_tagr(tstr, r)); } } @@ -473,6 +403,13 @@ region_print_memory(struct region *r, __unused void *arg) return WALK_CONTINUE; } +void +print_one_memory_region(const struct region *r) +{ + print_memory_region_header(); + ROP_PRINT(r); +} + #ifdef RDAR_23744374 /* * The reported size of a mapping to a file object gleaned from @@ -485,7 +422,7 @@ region_print_memory(struct region *r, __unused void *arg) * Figure out what the "non-faulting" size of the object is to * *host* page size resolution. */ -boolean_t +bool is_actual_size(const task_t task, const struct region *r, mach_vm_size_t *hostvmsize) { if (!r->r_info.external_pager || diff --git a/gcore.tproj/vm.h b/gcore.tproj/vm.h index b029bea..b912782 100644 --- a/gcore.tproj/vm.h +++ b/gcore.tproj/vm.h @@ -21,9 +21,7 @@ extern int pageshift_app; struct region; struct regionhead; -#ifdef CONFIG_REFSC extern void del_fileref_region(struct region *); -#endif extern void del_zfod_region(struct region *); extern void del_sparse_region(struct region *); extern void del_vanilla_region(struct region *); @@ -34,6 +32,7 @@ extern void del_region_list(struct regionhead *); extern void print_memory_region_header(void); extern void print_memory_region(const struct region *); +extern void print_one_memory_region(const struct region *); extern walk_region_cbfn_t region_print_memory; extern walk_region_cbfn_t region_write_memory; @@ -42,7 +41,7 @@ extern walk_region_cbfn_t region_size_memory; extern int is_tagged(task_t, mach_vm_offset_t, mach_vm_offset_t, unsigned); #ifdef RDAR_23744374 -extern boolean_t is_actual_size(const task_t, const struct region *, mach_vm_size_t *); +extern bool is_actual_size(const task_t, const struct region *, mach_vm_size_t *); #endif #endif /* _VM_H */ diff --git a/getconf.tproj/confstr.c b/getconf.tproj/confstr.c deleted file mode 100644 index 9a88ff5..0000000 --- a/getconf.tproj/confstr.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright is disclaimed as to the contents of this file. - * - * $FreeBSD: src/usr.bin/getconf/confstr.gperf,v 1.5 2003/08/22 17:32:07 markm Exp $ - */ - -#include - -#include -#include - -#include "getconf.h" - -/* - * Override gperf's built-in external scope. - */ -static const struct map *in_word_set(const char *str); - -/* - * The Standard seems a bit ambiguous over whether the POSIX_V6_* - * are specified with or without a leading underscore, so we just - * use both. - */ -struct map { const char *name; int key; int valid; }; -static const struct map wordlist[] = { -#ifdef _CS_PATH - { "PATH", _CS_PATH, 1 }, -#else - { "PATH", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFF32_CFLAGS - { "POSIX_V6_ILP32_OFF32_CFLAGS", _CS_POSIX_V6_ILP32_OFF32_CFLAGS, 1 }, -#else - { "POSIX_V6_ILP32_OFF32_CFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFF32_LDFLAGS - { "POSIX_V6_ILP32_OFF32_LDFLAGS", _CS_POSIX_V6_ILP32_OFF32_LDFLAGS, 1 }, -#else - { "POSIX_V6_ILP32_OFF32_LDFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFF32_LIBS - { "POSIX_V6_ILP32_OFF32_LIBS", _CS_POSIX_V6_ILP32_OFF32_LIBS, 1 }, -#else - { "POSIX_V6_ILP32_OFF32_LIBS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS - { "POSIX_V6_ILP32_OFFBIG_CFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS, 1 }, -#else - { "POSIX_V6_ILP32_OFFBIG_CFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS - { "POSIX_V6_ILP32_OFFBIG_LDFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS, 1 }, -#else - { "POSIX_V6_ILP32_OFFBIG_LDFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFFBIG_LIBS - { "POSIX_V6_ILP32_OFFBIG_LIBS", _CS_POSIX_V6_ILP32_OFFBIG_LIBS, 1 }, -#else - { "POSIX_V6_ILP32_OFFBIG_LIBS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LP64_OFF64_CFLAGS - { "POSIX_V6_LP64_OFF64_CFLAGS", _CS_POSIX_V6_LP64_OFF64_CFLAGS, 1 }, -#else - { "POSIX_V6_LP64_OFF64_CFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LP64_OFF64_LDFLAGS - { "POSIX_V6_LP64_OFF64_LDFLAGS", _CS_POSIX_V6_LP64_OFF64_LDFLAGS, 1 }, -#else - { "POSIX_V6_LP64_OFF64_LDFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LP64_OFF64_LIBS - { "POSIX_V6_LP64_OFF64_LIBS", _CS_POSIX_V6_LP64_OFF64_LIBS, 1 }, -#else - { "POSIX_V6_LP64_OFF64_LIBS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS - { "POSIX_V6_LPBIG_OFFBIG_CFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS, 1 }, -#else - { "POSIX_V6_LPBIG_OFFBIG_CFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS - { "POSIX_V6_LPBIG_OFFBIG_LDFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS, 1 }, -#else - { "POSIX_V6_LPBIG_OFFBIG_LDFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LIBS - { "POSIX_V6_LPBIG_OFFBIG_LIBS", _CS_POSIX_V6_LPBIG_OFFBIG_LIBS, 1 }, -#else - { "POSIX_V6_LPBIG_OFFBIG_LIBS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS - { "POSIX_V6_WIDTH_RESTRICTED_ENVS", _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, 1 }, -#else - { "POSIX_V6_WIDTH_RESTRICTED_ENVS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFF32_CFLAGS - { "_POSIX_V6_ILP32_OFF32_CFLAGS", _CS_POSIX_V6_ILP32_OFF32_CFLAGS, 1 }, -#else - { "_POSIX_V6_ILP32_OFF32_CFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFF32_LDFLAGS - { "_POSIX_V6_ILP32_OFF32_LDFLAGS", _CS_POSIX_V6_ILP32_OFF32_LDFLAGS, 1 }, -#else - { "_POSIX_V6_ILP32_OFF32_LDFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFF32_LIBS - { "_POSIX_V6_ILP32_OFF32_LIBS", _CS_POSIX_V6_ILP32_OFF32_LIBS, 1 }, -#else - { "_POSIX_V6_ILP32_OFF32_LIBS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS - { "_POSIX_V6_ILP32_OFFBIG_CFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS, 1 }, -#else - { "_POSIX_V6_ILP32_OFFBIG_CFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS - { "_POSIX_V6_ILP32_OFFBIG_LDFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS, 1 }, -#else - { "_POSIX_V6_ILP32_OFFBIG_LDFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_ILP32_OFFBIG_LIBS - { "_POSIX_V6_ILP32_OFFBIG_LIBS", _CS_POSIX_V6_ILP32_OFFBIG_LIBS, 1 }, -#else - { "_POSIX_V6_ILP32_OFFBIG_LIBS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LP64_OFF64_CFLAGS - { "_POSIX_V6_LP64_OFF64_CFLAGS", _CS_POSIX_V6_LP64_OFF64_CFLAGS, 1 }, -#else - { "_POSIX_V6_LP64_OFF64_CFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LP64_OFF64_LDFLAGS - { "_POSIX_V6_LP64_OFF64_LDFLAGS", _CS_POSIX_V6_LP64_OFF64_LDFLAGS, 1 }, -#else - { "_POSIX_V6_LP64_OFF64_LDFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LP64_OFF64_LIBS - { "_POSIX_V6_LP64_OFF64_LIBS", _CS_POSIX_V6_LP64_OFF64_LIBS, 1 }, -#else - { "_POSIX_V6_LP64_OFF64_LIBS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS - { "_POSIX_V6_LPBIG_OFFBIG_CFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS, 1 }, -#else - { "_POSIX_V6_LPBIG_OFFBIG_CFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS - { "_POSIX_V6_LPBIG_OFFBIG_LDFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS, 1 }, -#else - { "_POSIX_V6_LPBIG_OFFBIG_LDFLAGS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LIBS - { "_POSIX_V6_LPBIG_OFFBIG_LIBS", _CS_POSIX_V6_LPBIG_OFFBIG_LIBS, 1 }, -#else - { "_POSIX_V6_LPBIG_OFFBIG_LIBS", 0, 0 }, -#endif -#ifdef _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS - { "_POSIX_V6_WIDTH_RESTRICTED_ENVS", _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, 1 }, -#else - { "_POSIX_V6_WIDTH_RESTRICTED_ENVS", 0, 0 }, -#endif -#ifdef _CS_DARWIN_USER_DIR - { "DARWIN_USER_DIR", _CS_DARWIN_USER_DIR, 1 }, -#else - { "DARWIN_USER_DIR", 0, 0 }, -#endif -#ifdef _CS_DARWIN_USER_TEMP_DIR - { "DARWIN_USER_TEMP_DIR", _CS_DARWIN_USER_TEMP_DIR, 1 }, -#else - { "DARWIN_USER_TEMP_DIR", 0, 0 }, -#endif -#ifdef _CS_DARWIN_USER_CACHE_DIR - { "DARWIN_USER_CACHE_DIR", _CS_DARWIN_USER_CACHE_DIR, 1 }, -#else - { "DARWIN_USER_CACHE_DIR", 0, 0 }, -#endif - { NULL, 0, 0 } -}; -#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1) -static const struct map * -in_word_set(const char *word) -{ - const struct map *mp; - - for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) { - if (strcmp(word, mp->name) == 0) - return (mp); - } - return (NULL); -} - -int -find_confstr(const char *name, int *key) -{ - const struct map *rv; - - rv = in_word_set(name); - if (rv != NULL) { - if (rv->valid) { - *key = rv->key; - return 1; - } - return -1; - } - return 0; -} diff --git a/getconf.tproj/limits.c b/getconf.tproj/limits.c deleted file mode 100644 index 0033721..0000000 --- a/getconf.tproj/limits.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright is disclaimed as to the contents of this file. - * - * $FreeBSD: src/usr.bin/getconf/limits.gperf,v 1.2 2003/08/22 17:32:07 markm Exp $ - */ - -#include - -#include -#include -#ifdef APPLE_GETCONF_UNDERSCORE -#include -#endif /* APPLE_GETCONF_UNDERSCORE */ - -#include "getconf.h" - -/* - * Override gperf's built-in external scope. - */ -static const struct map *in_word_set(const char *str); - -struct map { const char *name; intmax_t value; int valid; }; -static const struct map wordlist[] = { -#ifdef _POSIX_AIO_LISTIO_MAX - { "_POSIX_AIO_LISTIO_MAX", _POSIX_AIO_LISTIO_MAX, 1 }, -#else - { "_POSIX_AIO_LISTIO_MAX", 0, 0 }, -#endif -#ifdef _POSIX_AIO_MAX - { "_POSIX_AIO_MAX", _POSIX_AIO_MAX, 1 }, -#else - { "_POSIX_AIO_MAX", 0, 0 }, -#endif -#ifdef _POSIX_ARG_MAX - { "_POSIX_ARG_MAX", _POSIX_ARG_MAX, 1 }, -#else - { "_POSIX_ARG_MAX", 0, 0 }, -#endif -#ifdef _POSIX_CHILD_MAX - { "_POSIX_CHILD_MAX", _POSIX_CHILD_MAX, 1 }, -#else - { "_POSIX_CHILD_MAX", 0, 0 }, -#endif -#ifdef _POSIX_CLOCKRES_MIN - { "_POSIX_CLOCKRES_MIN", _POSIX_CLOCKRES_MIN, 1 }, -#else - { "_POSIX_CLOCKRES_MIN", 0, 0 }, -#endif -#ifdef _POSIX_DELAYTIMER_MAX - { "_POSIX_DELAYTIMER_MAX", _POSIX_DELAYTIMER_MAX, 1 }, -#else - { "_POSIX_DELAYTIMER_MAX", 0, 0 }, -#endif -#ifdef _POSIX_HOST_NAME_MAX - { "_POSIX_HOST_NAME_MAX", _POSIX_HOST_NAME_MAX, 1 }, -#else - { "_POSIX_HOST_NAME_MAX", 0, 0 }, -#endif -#ifdef _POSIX_LINK_MAX - { "_POSIX_LINK_MAX", _POSIX_LINK_MAX, 1 }, -#else - { "_POSIX_LINK_MAX", 0, 0 }, -#endif -#ifdef _POSIX_LOGIN_NAME_MAX - { "_POSIX_LOGIN_NAME_MAX", _POSIX_LOGIN_NAME_MAX, 1 }, -#else - { "_POSIX_LOGIN_NAME_MAX", 0, 0 }, -#endif -#ifdef _POSIX_MAX_CANON - { "_POSIX_MAX_CANON", _POSIX_MAX_CANON, 1 }, -#else - { "_POSIX_MAX_CANON", 0, 0 }, -#endif -#ifdef _POSIX_MAX_INPUT - { "_POSIX_MAX_INPUT", _POSIX_MAX_INPUT, 1 }, -#else - { "_POSIX_MAX_INPUT", 0, 0 }, -#endif -#ifdef _POSIX_MQ_OPEN_MAX - { "_POSIX_MQ_OPEN_MAX", _POSIX_MQ_OPEN_MAX, 1 }, -#else - { "_POSIX_MQ_OPEN_MAX", 0, 0 }, -#endif -#ifdef _POSIX_MQ_PRIO_MAX - { "_POSIX_MQ_PRIO_MAX", _POSIX_MQ_PRIO_MAX, 1 }, -#else - { "_POSIX_MQ_PRIO_MAX", 0, 0 }, -#endif -#ifdef _POSIX_NAME_MAX - { "_POSIX_NAME_MAX", _POSIX_NAME_MAX, 1 }, -#else - { "_POSIX_NAME_MAX", 0, 0 }, -#endif -#ifdef _POSIX_NGROUPS_MAX - { "_POSIX_NGROUPS_MAX", _POSIX_NGROUPS_MAX, 1 }, -#else - { "_POSIX_NGROUPS_MAX", 0, 0 }, -#endif -#ifdef _POSIX_OPEN_MAX - { "_POSIX_OPEN_MAX", _POSIX_OPEN_MAX, 1 }, -#else - { "_POSIX_OPEN_MAX", 0, 0 }, -#endif -#ifdef _POSIX_PATH_MAX - { "_POSIX_PATH_MAX", _POSIX_PATH_MAX, 1 }, -#else - { "_POSIX_PATH_MAX", 0, 0 }, -#endif -#ifdef _POSIX_PIPE_BUF - { "_POSIX_PIPE_BUF", _POSIX_PIPE_BUF, 1 }, -#else - { "_POSIX_PIPE_BUF", 0, 0 }, -#endif -#ifdef _POSIX_RE_DUP_MAX - { "_POSIX_RE_DUP_MAX", _POSIX_RE_DUP_MAX, 1 }, -#else - { "_POSIX_RE_DUP_MAX", 0, 0 }, -#endif -#ifdef _POSIX_RTSIG_MAX - { "_POSIX_RTSIG_MAX", _POSIX_RTSIG_MAX, 1 }, -#else - { "_POSIX_RTSIG_MAX", 0, 0 }, -#endif -#ifdef _POSIX_SEM_NSEMS_MAX - { "_POSIX_SEM_NSEMS_MAX", _POSIX_SEM_NSEMS_MAX, 1 }, -#else - { "_POSIX_SEM_NSEMS_MAX", 0, 0 }, -#endif -#ifdef _POSIX_SEM_VALUE_MAX - { "_POSIX_SEM_VALUE_MAX", _POSIX_SEM_VALUE_MAX, 1 }, -#else - { "_POSIX_SEM_VALUE_MAX", 0, 0 }, -#endif -#ifdef _POSIX_SIGQUEUE_MAX - { "_POSIX_SIGQUEUE_MAX", _POSIX_SIGQUEUE_MAX, 1 }, -#else - { "_POSIX_SIGQUEUE_MAX", 0, 0 }, -#endif -#ifdef _POSIX_SSIZE_MAX - { "_POSIX_SSIZE_MAX", _POSIX_SSIZE_MAX, 1 }, -#else - { "_POSIX_SSIZE_MAX", 0, 0 }, -#endif -#ifdef _POSIX_STREAM_MAX - { "_POSIX_STREAM_MAX", _POSIX_STREAM_MAX, 1 }, -#else - { "_POSIX_STREAM_MAX", 0, 0 }, -#endif -#ifdef _POSIX_SS_REPL_MAX - { "_POSIX_SS_REPL_MAX", _POSIX_SS_REPL_MAX, 1 }, -#else - { "_POSIX_SS_REPL_MAX", 0, 0 }, -#endif -#ifdef _POSIX_SYMLINK_MAX - { "_POSIX_SYMLINK_MAX", _POSIX_SYMLINK_MAX, 1 }, -#else - { "_POSIX_SYMLINK_MAX", 0, 0 }, -#endif -#ifdef _POSIX_SYMLOOP_MAX - { "_POSIX_SYMLOOP_MAX", _POSIX_SYMLOOP_MAX, 1 }, -#else - { "_POSIX_SYMLOOP_MAX", 0, 0 }, -#endif -#ifdef _POSIX_THREAD_DESTRUCTOR_ITERATIONS - { "_POSIX_THREAD_DESTRUCTOR_ITERATIONS", _POSIX_THREAD_DESTRUCTOR_ITERATIONS, 1 }, -#else - { "_POSIX_THREAD_DESTRUCTOR_ITERATIONS", 0, 0 }, -#endif -#ifdef _POSIX_THREAD_KEYS_MAX - { "_POSIX_THREAD_KEYS_MAX", _POSIX_THREAD_KEYS_MAX, 1 }, -#else - { "_POSIX_THREAD_KEYS_MAX", 0, 0 }, -#endif -#ifdef _POSIX_THREAD_THREADS_MAX - { "_POSIX_THREAD_THREADS_MAX", _POSIX_THREAD_THREADS_MAX, 1 }, -#else - { "_POSIX_THREAD_THREADS_MAX", 0, 0 }, -#endif -#ifdef _POSIX_TIMER_MAX - { "_POSIX_TIMER_MAX", _POSIX_TIMER_MAX, 1 }, -#else - { "_POSIX_TIMER_MAX", 0, 0 }, -#endif -#ifdef _POSIX_TRACE_EVENT_NAME_MAX - { "_POSIX_TRACE_EVENT_NAME_MAX", _POSIX_TRACE_EVENT_NAME_MAX, 1 }, -#else - { "_POSIX_TRACE_EVENT_NAME_MAX", 0, 0 }, -#endif -#ifdef _POSIX_TRACE_NAME_MAX - { "_POSIX_TRACE_NAME_MAX", _POSIX_TRACE_NAME_MAX, 1 }, -#else - { "_POSIX_TRACE_NAME_MAX", 0, 0 }, -#endif -#ifdef _POSIX_TRACE_SYS_MAX - { "_POSIX_TRACE_SYS_MAX", _POSIX_TRACE_SYS_MAX, 1 }, -#else - { "_POSIX_TRACE_SYS_MAX", 0, 0 }, -#endif -#ifdef _POSIX_TRACE_USER_EVENT_MAX - { "_POSIX_TRACE_USER_EVENT_MAX", _POSIX_TRACE_USER_EVENT_MAX, 1 }, -#else - { "_POSIX_TRACE_USER_EVENT_MAX", 0, 0 }, -#endif -#ifdef _POSIX_TTY_NAME_MAX - { "_POSIX_TTY_NAME_MAX", _POSIX_TTY_NAME_MAX, 1 }, -#else - { "_POSIX_TTY_NAME_MAX", 0, 0 }, -#endif -#ifdef _POSIX_TZNAME_MAX - { "_POSIX_TZNAME_MAX", _POSIX_TZNAME_MAX, 1 }, -#else - { "_POSIX_TZNAME_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_BC_BASE_MAX - { "_POSIX2_BC_BASE_MAX", _POSIX2_BC_BASE_MAX, 1 }, -#else - { "_POSIX2_BC_BASE_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_BC_DIM_MAX - { "_POSIX2_BC_DIM_MAX", _POSIX2_BC_DIM_MAX, 1 }, -#else - { "_POSIX2_BC_DIM_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_BC_SCALE_MAX - { "_POSIX2_BC_SCALE_MAX", _POSIX2_BC_SCALE_MAX, 1 }, -#else - { "_POSIX2_BC_SCALE_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_BC_STRING_MAX - { "_POSIX2_BC_STRING_MAX", _POSIX2_BC_STRING_MAX, 1 }, -#else - { "_POSIX2_BC_STRING_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_CHARCLASS_NAME_MAX - { "_POSIX2_CHARCLASS_NAME_MAX", _POSIX2_CHARCLASS_NAME_MAX, 1 }, -#else - { "_POSIX2_CHARCLASS_NAME_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_COLL_WEIGHTS_MAX - { "_POSIX2_COLL_WEIGHTS_MAX", _POSIX2_COLL_WEIGHTS_MAX, 1 }, -#else - { "_POSIX2_COLL_WEIGHTS_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_EXPR_NEST_MAX - { "_POSIX2_EXPR_NEST_MAX", _POSIX2_EXPR_NEST_MAX, 1 }, -#else - { "_POSIX2_EXPR_NEST_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_LINE_MAX - { "_POSIX2_LINE_MAX", _POSIX2_LINE_MAX, 1 }, -#else - { "_POSIX2_LINE_MAX", 0, 0 }, -#endif -#ifdef _POSIX2_RE_DUP_MAX - { "_POSIX2_RE_DUP_MAX", _POSIX2_RE_DUP_MAX, 1 }, -#else - { "_POSIX2_RE_DUP_MAX", 0, 0 }, -#endif -#ifdef _XOPEN_IOV_MAX - { "_XOPEN_IOV_MAX", _XOPEN_IOV_MAX, 1 }, -#else - { "_XOPEN_IOV_MAX", 0, 0 }, -#endif -#ifdef _XOPEN_NAME_MAX - { "_XOPEN_NAME_MAX", _XOPEN_NAME_MAX, 1 }, -#else - { "_XOPEN_NAME_MAX", 0, 0 }, -#endif -#ifdef _XOPEN_PATH_MAX - { "_XOPEN_PATH_MAX", _XOPEN_PATH_MAX, 1 }, -#else - { "_XOPEN_PATH_MAX", 0, 0 }, -#endif -#ifdef CHAR_BIT - { "CHAR_BIT", CHAR_BIT, 1 }, -#else - { "CHAR_BIT", 0, 0 }, -#endif -#ifdef CHAR_MAX - { "CHAR_MAX", CHAR_MAX, 1 }, -#else - { "CHAR_MAX", 0, 0 }, -#endif -#ifdef CHAR_MIN - { "CHAR_MIN", CHAR_MIN, 1 }, -#else - { "CHAR_MIN", 0, 0 }, -#endif -#ifdef INT_MAX - { "INT_MAX", INT_MAX, 1 }, -#else - { "INT_MAX", 0, 0 }, -#endif -#ifdef INT_MIN - { "INT_MIN", INT_MIN, 1 }, -#else - { "INT_MIN", 0, 0 }, -#endif -#ifdef LLONG_MIN - { "LLONG_MIN", LLONG_MIN, 1 }, -#else - { "LLONG_MIN", 0, 0 }, -#endif -#ifdef LLONG_MAX - { "LLONG_MAX", LLONG_MAX, 1 }, -#else - { "LLONG_MAX", 0, 0 }, -#endif -#ifdef LONG_BIT - { "LONG_BIT", LONG_BIT, 1 }, -#else - { "LONG_BIT", 0, 0 }, -#endif -#ifdef LONG_MAX - { "LONG_MAX", LONG_MAX, 1 }, -#else - { "LONG_MAX", 0, 0 }, -#endif -#ifdef LONG_MIN - { "LONG_MIN", LONG_MIN, 1 }, -#else - { "LONG_MIN", 0, 0 }, -#endif -#ifdef MB_LEN_MAX - { "MB_LEN_MAX", MB_LEN_MAX, 1 }, -#else - { "MB_LEN_MAX", 0, 0 }, -#endif -#ifdef SCHAR_MAX - { "SCHAR_MAX", SCHAR_MAX, 1 }, -#else - { "SCHAR_MAX", 0, 0 }, -#endif -#ifdef SCHAR_MIN - { "SCHAR_MIN", SCHAR_MIN, 1 }, -#else - { "SCHAR_MIN", 0, 0 }, -#endif -#ifdef SHRT_MAX - { "SHRT_MAX", SHRT_MAX, 1 }, -#else - { "SHRT_MAX", 0, 0 }, -#endif -#ifdef SHRT_MIN - { "SHRT_MIN", SHRT_MIN, 1 }, -#else - { "SHRT_MIN", 0, 0 }, -#endif -#ifdef SSIZE_MAX - { "SSIZE_MAX", SSIZE_MAX, 1 }, -#else - { "SSIZE_MAX", 0, 0 }, -#endif -#ifdef UCHAR_MAX - { "UCHAR_MAX", UCHAR_MAX, 1 }, -#else - { "UCHAR_MAX", 0, 0 }, -#endif -#ifdef UINT_MAX - { "UINT_MAX", UINT_MAX, 1 }, -#else - { "UINT_MAX", 0, 0 }, -#endif -#ifdef ULLONG_MAX - { "ULLONG_MAX", ULLONG_MAX, 1 }, -#else - { "ULLONG_MAX", 0, 0 }, -#endif -#ifdef ULONG_MAX - { "ULONG_MAX", ULONG_MAX, 1 }, -#else - { "ULONG_MAX", 0, 0 }, -#endif -#ifdef USHRT_MAX - { "USHRT_MAX", USHRT_MAX, 1 }, -#else - { "USHRT_MAX", 0, 0 }, -#endif -#ifdef WORD_BIT - { "WORD_BIT", WORD_BIT, 1 }, -#else - { "WORD_BIT", 0, 0 }, -#endif -#ifdef CHARCLASS_NAME_MAX - { "CHARCLASS_NAME_MAX", CHARCLASS_NAME_MAX, 1 }, -#else - { "CHARCLASS_NAME_MAX", 0, 0 }, -#endif -#ifdef NL_ARGMAX - { "NL_ARGMAX", NL_ARGMAX, 1 }, -#else - { "NL_ARGMAX", 0, 0 }, -#endif -#ifdef NL_LANGMAX - { "ML_LANGMAX", NL_LANGMAX, 1 }, -#else - { "ML_LANGMAX", 0, 0 }, -#endif -#ifdef NL_MSGMAX - { "NL_MSGMAX", NL_MSGMAX, 1 }, -#else - { "NL_MSGMAX", 0, 0 }, -#endif -#ifdef NL_NMAX - { "NL_NMAX", NL_NMAX, 1 }, -#else - { "NL_NMAX", 0, 0 }, -#endif -#ifdef NL_SETMAX - { "NL_SETMAX", NL_SETMAX, 1 }, -#else - { "NL_SETMAX", 0, 0 }, -#endif -#ifdef NL_TEXTMAX - { "NL_TEXTMAX", NL_TEXTMAX, 1 }, -#else - { "NL_TEXTMAX", 0, 0 }, -#endif -#ifdef NZERO - { "NZERO", NZERO, 1 }, -#else - { "NZERO", 0, 0 }, -#endif - { NULL, 0, 0 } -}; -#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1) -static const struct map * -in_word_set(const char *word) -{ - const struct map *mp; - - for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) { - if (strcmp(word, mp->name) == 0) - return (mp); - } - return (NULL); -} - -int -find_limit(const char *name, intmax_t *value) -{ - const struct map *rv; -#ifdef APPLE_GETCONF_UNDERSCORE - char *alt; -#endif /* APPLE_GETCONF_UNDERSCORE */ - - rv = in_word_set(name); - if (rv != NULL) { - if (rv->valid) { - *value = rv->value; - return 1; - } - return -1; - } -#ifdef APPLE_GETCONF_UNDERSCORE - if(*name == '_') - alt = (char *)name + 1; - else { - if((alt = (char *)alloca(strlen(name) + 2)) == NULL) - return 0; - *alt = '_'; - strcpy(alt + 1, name); - } - rv = in_word_set(alt); - if (rv != NULL) { - if (rv->valid) { - *value = rv->value; - return 1; - } - return -1; - } -#endif /* APPLE_GETCONF_UNDERSCORE */ - return 0; -} diff --git a/getconf.tproj/pathconf.c b/getconf.tproj/pathconf.c deleted file mode 100644 index 57ed3c5..0000000 --- a/getconf.tproj/pathconf.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright is disclaimed as to the contents of this file. - * - * $FreeBSD: src/usr.bin/getconf/pathconf.gperf,v 1.4 2003/08/22 17:32:07 markm Exp $ - */ - -#include - -#include -#include -#ifdef APPLE_GETCONF_UNDERSCORE -#include -#endif /* APPLE_GETCONF_UNDERSCORE */ - -#include "getconf.h" - -/* - * Override gperf's built-in external scope. - */ -static const struct map *in_word_set(const char *str); - -struct map { const char *name; int key; int valid; }; -static const struct map wordlist[] = { -#ifdef _PC_FILESIZEBITS - { "FILESIZEBITS", _PC_FILESIZEBITS, 1 }, -#else - { "FILESIZEBITS", 0, 0 }, -#endif -#ifdef _PC_LINK_MAX - { "LINK_MAX", _PC_LINK_MAX, 1 }, -#else - { "LINK_MAX", 0, 0 }, -#endif -#ifdef _PC_MAX_CANON - { "MAX_CANON", _PC_MAX_CANON, 1 }, -#else - { "MAX_CANON", 0, 0 }, -#endif -#ifdef _PC_MAX_INPUT - { "MAX_INPUT", _PC_MAX_INPUT, 1 }, -#else - { "MAX_INPUT", 0, 0 }, -#endif -#ifdef _PC_NAME_MAX - { "NAME_MAX", _PC_NAME_MAX, 1 }, -#else - { "NAME_MAX", 0, 0 }, -#endif -#ifdef _PC_PATH_MAX - { "PATH_MAX", _PC_PATH_MAX, 1 }, -#else - { "PATH_MAX", 0, 0 }, -#endif -#ifdef _PC_PIPE_BUF - { "PIPE_BUF", _PC_PIPE_BUF, 1 }, -#else - { "PIPE_BUF", 0, 0 }, -#endif -#ifdef _PC_ALLOC_SIZE_MIN - { "POSIX_ALLOC_SIZE_MIN", _PC_ALLOC_SIZE_MIN, 1 }, -#else - { "POSIX_ALLOC_SIZE_MIN", 0, 0 }, -#endif -#ifdef _PC_REC_INCR_XFER_SIZE - { "POSIX_REC_INCR_XFER_SIZE", _PC_REC_INCR_XFER_SIZE, 1 }, -#else - { "POSIX_REC_INCR_XFER_SIZE", 0, 0 }, -#endif -#ifdef _PC_REC_MAX_XFER_SIZE - { "POSIX_REC_MAX_XFER_SIZE", _PC_REC_MAX_XFER_SIZE, 1 }, -#else - { "POSIX_REC_MAX_XFER_SIZE", 0, 0 }, -#endif -#ifdef _PC_REC_MIN_XFER_SIZE - { "POSIX_REC_MIN_XFER_SIZE", _PC_REC_MIN_XFER_SIZE, 1 }, -#else - { "POSIX_REC_MIN_XFER_SIZE", 0, 0 }, -#endif -#ifdef _PC_REC_XFER_ALIGN - { "POSIX_REC_XFER_ALIGN", _PC_REC_XFER_ALIGN, 1 }, -#else - { "POSIX_REC_XFER_ALIGN", 0, 0 }, -#endif -#ifdef _PC_2_SYMLINKS - { "POSIX2_SYMLINKS", _PC_2_SYMLINKS, 1 }, -#else - { "POSIX2_SYMLINKS", 0, 0 }, -#endif -#ifdef _PC_SYMLINK_MAX - { "SYMLINK_MAX", _PC_SYMLINK_MAX, 1 }, -#else - { "SYMLINK_MAX", 0, 0 }, -#endif -#ifdef _PC_ACL_EXTENDED - { "TRUSTEDBSD_ACL_EXTENDED", _PC_ACL_EXTENDED, 1 }, -#else - { "TRUSTEDBSD_ACL_EXTENDED", 0, 0 }, -#endif -#ifdef _PC_ACL_PATH_MAX - { "TRUSTEDBSD_ACL_PATH_MAX", _PC_ACL_PATH_MAX, 1 }, -#else - { "TRUSTEDBSD_ACL_PATH_MAX", 0, 0 }, -#endif -#ifdef _PC_CAP_PRESENT - { "TRUSTEDBSD_CAP_PRESENT", _PC_CAP_PRESENT, 1 }, -#else - { "TRUSTEDBSD_CAP_PRESENT", 0, 0 }, -#endif -#ifdef _PC_INF_PRESENT - { "TRUSTEDBSD_INF_PRESENT", _PC_INF_PRESENT, 1 }, -#else - { "TRUSTEDBSD_INF_PRESENT", 0, 0 }, -#endif -#ifdef _PC_MAC_PRESENT - { "TRUSTEDBSD_MAC_PRESENT", _PC_MAC_PRESENT, 1 }, -#else - { "TRUSTEDBSD_MAC_PRESENT", 0, 0 }, -#endif -#ifdef _PC_ASYNC_IO - { "_POSIX_ASYNC_IO", _PC_ASYNC_IO, 1 }, -#else - { "_POSIX_ASYNC_IO", 0, 0 }, -#endif -#ifdef _PC_CHOWN_RESTRICTED - { "_POSIX_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED, 1 }, -#else - { "_POSIX_CHOWN_RESTRICTED", 0, 0 }, -#endif -#ifdef _PC_NO_TRUNC - { "_POSIX_NO_TRUNC", _PC_NO_TRUNC, 1 }, -#else - { "_POSIX_NO_TRUNC", 0, 0 }, -#endif -#ifdef _PC_PATH_MAX - { "_POSIX_PATH_MAX", _PC_PATH_MAX, 1 }, -#else - { "_POSIX_PATH_MAX", 0, 0 }, -#endif -#ifdef _PC_PRIO_IO - { "_POSIX_PRIO_IO", _PC_PRIO_IO, 1 }, -#else - { "_POSIX_PRIO_IO", 0, 0 }, -#endif -#ifdef _PC_SYNC_IO - { "_POSIX_SYNC_IO", _PC_SYNC_IO, 1 }, -#else - { "_POSIX_SYNC_IO", 0, 0 }, -#endif -#ifdef _PC_VDISABLE - { "_POSIX_VDISABLE", _PC_VDISABLE, 1 }, -#else - { "_POSIX_VDISABLE", 0, 0 }, -#endif - { NULL, 0, 0 } -}; -#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1) -static const struct map * -in_word_set(const char *word) -{ - const struct map *mp; - - for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) { - if (strcmp(word, mp->name) == 0) - return (mp); - } - return (NULL); -} - -int -find_pathconf(const char *name, int *key) -{ - const struct map *rv; -#ifdef APPLE_GETCONF_UNDERSCORE - char *alt; -#endif /* APPLE_GETCONF_UNDERSCORE */ - - rv = in_word_set(name); - if (rv != NULL) { - if (rv->valid) { - *key = rv->key; - return 1; - } - return -1; - } -#ifdef APPLE_GETCONF_UNDERSCORE - if(*name == '_') - alt = (char *)name + 1; - else { - if((alt = (char *)alloca(strlen(name) + 2)) == NULL) - return 0; - *alt = '_'; - strcpy(alt + 1, name); - } - rv = in_word_set(alt); - if (rv != NULL) { - if (rv->valid) { - *key = rv->key; - return 1; - } - return -1; - } -#endif /* APPLE_GETCONF_UNDERSCORE */ - return 0; -} diff --git a/getconf.tproj/progenv.c b/getconf.tproj/progenv.c deleted file mode 100644 index 6dcf471..0000000 --- a/getconf.tproj/progenv.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright is disclaimed as to the contents of this file. - * - * $FreeBSD: src/usr.bin/getconf/progenv.gperf,v 1.2 2003/08/22 17:32:07 markm Exp $ - */ - -#include - -#include -#include - -#include "getconf.h" - -/* - * Override gperf's built-in external scope. - */ -static const struct map *in_word_set(const char *str); - -/* - * The Standard seems a bit ambiguous over whether the POSIX_V6_* - * are specified with or without a leading underscore, so we just - * use both. - */ -/* - * The alt_path member gives the path containing another `getconf' - * executable which was compiled using the specified programming - * environment. If it is NULL, the current executable is good enough. - * If we ever support multiple environments, this table will need to - * be updated. (We cheat here and define the supported environments - * statically.) - */ -#if defined(__alpha__) || defined(__sparc64__) -#define have_LP64_OFF64 NULL -#elif defined(__APPLE__) -#define have_LP64_OFF64 NULL -#define have_LPBIG_OFFBIG NULL -#endif - -#if defined(__i386__) || defined(__powerpc__) || defined(__x86_64__) -#define have_ILP32_OFFBIG NULL -#endif - -struct map { const char *name; const char *alt_path; int valid; }; -static const struct map wordlist[] = { -#ifdef notdef - { "POSIX_V6_ILP32_OFF32", notdef, 1 }, -#else - { "POSIX_V6_ILP32_OFF32", 0, 0 }, -#endif -#ifdef have_ILP32_OFFBIG - { "POSIX_V6_ILP32_OFFBIG", have_ILP32_OFFBIG, 1 }, -#else - { "POSIX_V6_ILP32_OFFBIG", 0, 0 }, -#endif -#ifdef have_LP64_OFF64 - { "POSIX_V6_LP64_OFF64", have_LP64_OFF64, 1 }, -#else - { "POSIX_V6_LP64_OFF64", 0, 0 }, -#endif -#ifdef have_LPBIG_OFFBIG - { "POSIX_V6_LPBIG_OFFBIG", have_LPBIG_OFFBIG, 1 }, -#else - { "POSIX_V6_LPBIG_OFFBIG", 0, 0 }, -#endif -#ifdef notdef - { "_POSIX_V6_ILP32_OFF32", notdef, 1 }, -#else - { "_POSIX_V6_ILP32_OFF32", 0, 0 }, -#endif -#ifdef have_ILP32_OFFBIG - { "_POSIX_V6_ILP32_OFFBIG", have_ILP32_OFFBIG, 1 }, -#else - { "_POSIX_V6_ILP32_OFFBIG", 0, 0 }, -#endif -#ifdef have_LP64_OFF64 - { "_POSIX_V6_LP64_OFF64", have_LP64_OFF64, 1 }, -#else - { "_POSIX_V6_LP64_OFF64", 0, 0 }, -#endif -#ifdef have_LPBIG_OFFBIG - { "_POSIX_V6_LPBIG_OFFBIG", have_LPBIG_OFFBIG, 1 }, -#else - { "_POSIX_V6_LPBIG_OFFBIG", 0, 0 }, -#endif - { NULL, 0, 0 } -}; -#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1) -static const struct map * -in_word_set(const char *word) -{ - const struct map *mp; - - for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) { - if (strcmp(word, mp->name) == 0) - return (mp); - } - return (NULL); -} - -int -find_progenv(const char *name, const char **alt_path) -{ - const struct map *rv; - - rv = in_word_set(name); - if (rv != NULL) { - if (rv->valid) { - *alt_path = rv->alt_path; - return 1; - } - return -1; - } - return 0; -} diff --git a/getconf.tproj/sysconf.c b/getconf.tproj/sysconf.c deleted file mode 100644 index 1c1ff61..0000000 --- a/getconf.tproj/sysconf.c +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Copyright is disclaimed as to the contents of this file. - * - * $FreeBSD: src/usr.bin/getconf/sysconf.gperf,v 1.5 2003/08/22 17:32:07 markm Exp $ - */ - -#include - -#include -#include -#ifdef APPLE_GETCONF_UNDERSCORE -#include -#endif /* APPLE_GETCONF_UNDERSCORE */ - -#include "getconf.h" - -/* - * Override gperf's built-in external scope. - */ -static const struct map *in_word_set(const char *str); - -struct map { const char *name; int key; int valid; }; -static const struct map wordlist[] = { -#ifdef _SC_AIO_LISTIO_MAX - { "AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX, 1 }, -#else - { "AIO_LISTIO_MAX", 0, 0 }, -#endif -#ifdef _SC_AIO_MAX - { "AIO_MAX", _SC_AIO_MAX, 1 }, -#else - { "AIO_MAX", 0, 0 }, -#endif -#ifdef _SC_AIO_PRIO_DELTA_MAX - { "AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX, 1 }, -#else - { "AIO_PRIO_DELTA_MAX", 0, 0 }, -#endif -#ifdef _SC_ARG_MAX - { "ARG_MAX", _SC_ARG_MAX, 1 }, -#else - { "ARG_MAX", 0, 0 }, -#endif -#ifdef _SC_ATEXIT_MAX - { "ATEXIT_MAX", _SC_ATEXIT_MAX, 1 }, -#else - { "ATEXIT_MAX", 0, 0 }, -#endif -#ifdef _SC_BC_BASE_MAX - { "BC_BASE_MAX", _SC_BC_BASE_MAX, 1 }, -#else - { "BC_BASE_MAX", 0, 0 }, -#endif -#ifdef _SC_BC_DIM_MAX - { "BC_DIM_MAX", _SC_BC_DIM_MAX, 1 }, -#else - { "BC_DIM_MAX", 0, 0 }, -#endif -#ifdef _SC_BC_SCALE_MAX - { "BC_SCALE_MAX", _SC_BC_SCALE_MAX, 1 }, -#else - { "BC_SCALE_MAX", 0, 0 }, -#endif -#ifdef _SC_BC_STRING_MAX - { "BC_STRING_MAX", _SC_BC_STRING_MAX, 1 }, -#else - { "BC_STRING_MAX", 0, 0 }, -#endif -#ifdef _SC_CHILD_MAX - { "CHILD_MAX", _SC_CHILD_MAX, 1 }, -#else - { "CHILD_MAX", 0, 0 }, -#endif -#ifdef _SC_CLK_TCK - { "CLK_TCK", _SC_CLK_TCK, 1 }, -#else - { "CLK_TCK", 0, 0 }, -#endif -#ifdef _SC_COLL_WEIGHTS_MAX - { "COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX, 1 }, -#else - { "COLL_WEIGHTS_MAX", 0, 0 }, -#endif -#ifdef _SC_DELAYTIMER_MAX - { "DELAYTIMER_MAX", _SC_DELAYTIMER_MAX, 1 }, -#else - { "DELAYTIMER_MAX", 0, 0 }, -#endif -#ifdef _SC_EXPR_NEST_MAX - { "EXPR_NEST_MAX", _SC_EXPR_NEST_MAX, 1 }, -#else - { "EXPR_NEST_MAX", 0, 0 }, -#endif -#ifdef _SC_GETGR_R_SIZE_MAX - { "GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX, 1 }, -#else - { "GETGR_R_SIZE_MAX", 0, 0 }, -#endif -#ifdef _SC_GETPW_R_SIZE_MAX - { "GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX, 1 }, -#else - { "GETPW_R_SIZE_MAX", 0, 0 }, -#endif -#ifdef _SC_HOST_NAME_MAX - { "HOST_NAME_MAX", _SC_HOST_NAME_MAX, 1 }, -#else - { "HOST_NAME_MAX", 0, 0 }, -#endif -#ifdef _SC_IOV_MAX - { "IOV_MAX", _SC_IOV_MAX, 1 }, -#else - { "IOV_MAX", 0, 0 }, -#endif -#ifdef _SC_LINE_MAX - { "LINE_MAX", _SC_LINE_MAX, 1 }, -#else - { "LINE_MAX", 0, 0 }, -#endif -#ifdef _SC_LOGIN_NAME_MAX - { "LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX, 1 }, -#else - { "LOGIN_NAME_MAX", 0, 0 }, -#endif -#ifdef _SC_MQ_OPEN_MAX - { "MQ_OPEN_MAX", _SC_MQ_OPEN_MAX, 1 }, -#else - { "MQ_OPEN_MAX", 0, 0 }, -#endif -#ifdef _SC_MQ_PRIO_MAX - { "MQ_PRIO_MAX", _SC_MQ_PRIO_MAX, 1 }, -#else - { "MQ_PRIO_MAX", 0, 0 }, -#endif -#ifdef _SC_NGROUPS_MAX - { "NGROUPS_MAX", _SC_NGROUPS_MAX, 1 }, -#else - { "NGROUPS_MAX", 0, 0 }, -#endif -#ifdef _SC_NPROCESSORS_CONF - { "NPROCESSORS_CONF", _SC_NPROCESSORS_CONF, 1 }, -#else - { "NPROCESSORS_CONF", 0, 0 }, -#endif -#ifdef _SC_NPROCESSORS_ONLN - { "NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN, 1 }, -#else - { "NPROCESSORS_ONLN", 0, 0 }, -#endif -#ifdef _SC_OPEN_MAX - { "OPEN_MAX", _SC_OPEN_MAX, 1 }, -#else - { "OPEN_MAX", 0, 0 }, -#endif -#ifdef _SC_PAGESIZE - { "PAGESIZE", _SC_PAGESIZE, 1 }, -#else - { "PAGESIZE", 0, 0 }, -#endif -#ifdef _SC_PAGESIZE - { "PAGE_SIZE", _SC_PAGESIZE, 1 }, -#else - { "PAGE_SIZE", 0, 0 }, -#endif -#ifdef _SC_PASS_MAX - { "PASS_MAX", _SC_PASS_MAX, 1 }, -#else - { "PASS_MAX", 0, 0 }, -#endif -#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS - { "PTHREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS, 1 }, -#else - { "PTHREAD_DESTRUCTOR_ITERATIONS", 0, 0 }, -#endif -#ifdef _SC_THREAD_KEYS_MAX - { "PTHREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX, 1 }, -#else - { "PTHREAD_KEYS_MAX", 0, 0 }, -#endif -#ifdef _SC_THREAD_STACK_MIN - { "PTHREAD_STACK_MIN", _SC_THREAD_STACK_MIN, 1 }, -#else - { "PTHREAD_STACK_MIN", 0, 0 }, -#endif -#ifdef _SC_THREAD_THREADS_MAX - { "PTHREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX, 1 }, -#else - { "PTHREAD_THREADS_MAX", 0, 0 }, -#endif -#ifdef _SC_RE_DUP_MAX - { "RE_DUP_MAX", _SC_RE_DUP_MAX, 1 }, -#else - { "RE_DUP_MAX", 0, 0 }, -#endif -#ifdef _SC_RTSIG_MAX - { "RTSIG_MAX", _SC_RTSIG_MAX, 1 }, -#else - { "RTSIG_MAX", 0, 0 }, -#endif -#ifdef _SC_SEM_NSEMS_MAX - { "SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX, 1 }, -#else - { "SEM_NSEMS_MAX", 0, 0 }, -#endif -#ifdef _SC_SEM_VALUE_MAX - { "SEM_VALUE_MAX", _SC_SEM_VALUE_MAX, 1 }, -#else - { "SEM_VALUE_MAX", 0, 0 }, -#endif -#ifdef _SC_SIGQUEUE_MAX - { "SIGQUEUE_MAX", _SC_SIGQUEUE_MAX, 1 }, -#else - { "SIGQUEUE_MAX", 0, 0 }, -#endif -#ifdef _SC_STREAM_MAX - { "STREAM_MAX", _SC_STREAM_MAX, 1 }, -#else - { "STREAM_MAX", 0, 0 }, -#endif -#ifdef _SC_SYMLOOP_MAX - { "SYMLOOP_MAX", _SC_SYMLOOP_MAX, 1 }, -#else - { "SYMLOOP_MAX", 0, 0 }, -#endif -#ifdef _SC_TIMER_MAX - { "TIMER_MAX", _SC_TIMER_MAX, 1 }, -#else - { "TIMER_MAX", 0, 0 }, -#endif -#ifdef _SC_TTY_NAME_MAX - { "TTY_NAME_MAX", _SC_TTY_NAME_MAX, 1 }, -#else - { "TTY_NAME_MAX", 0, 0 }, -#endif -#ifdef _SC_TZNAME_MAX - { "TZNAME_MAX", _SC_TZNAME_MAX, 1 }, -#else - { "TZNAME_MAX", 0, 0 }, -#endif -#ifdef _SC_2_CHAR_TERM - { "_POSIX2_CHAR_TERM", _SC_2_CHAR_TERM, 1 }, -#else - { "_POSIX2_CHAR_TERM", 0, 0 }, -#endif -#ifdef _SC_2_C_BIND - { "_POSIX2_C_BIND", _SC_2_C_BIND, 1 }, -#else - { "_POSIX2_C_BIND", 0, 0 }, -#endif -#ifdef _SC_2_C_DEV - { "_POSIX2_C_DEV", _SC_2_C_DEV, 1 }, -#else - { "_POSIX2_C_DEV", 0, 0 }, -#endif -#ifdef _SC_2_C_VERSION - { "_POSIX2_C_VERSION", _SC_2_C_VERSION, 1 }, -#else - { "_POSIX2_C_VERSION", 0, 0 }, -#endif -#ifdef _SC_2_FORT_DEV - { "_POSIX2_FORT_DEV", _SC_2_FORT_DEV, 1 }, -#else - { "_POSIX2_FORT_DEV", 0, 0 }, -#endif -#ifdef _SC_2_FORT_RUN - { "_POSIX2_FORT_RUN", _SC_2_FORT_RUN, 1 }, -#else - { "_POSIX2_FORT_RUN", 0, 0 }, -#endif -#ifdef _SC_2_LOCALEDEF - { "_POSIX2_LOCALEDEF", _SC_2_LOCALEDEF, 1 }, -#else - { "_POSIX2_LOCALEDEF", 0, 0 }, -#endif -#ifdef _SC_PBS - { "_POSIX2_PBS", _SC_PBS, 1 }, -#else - { "_POSIX2_PBS", 0, 0 }, -#endif -#ifdef _SC_PBS_ACCOUNTING - { "_POSIX2_PBS_ACCOUNTING", _SC_PBS_ACCOUNTING, 1 }, -#else - { "_POSIX2_PBS_ACCOUNTING", 0, 0 }, -#endif -#ifdef _SC_PBS_CHECKPOINT - { "_POSIX2_PBS_CHECKPOINT", _SC_PBS_CHECKPOINT, 1 }, -#else - { "_POSIX2_PBS_CHECKPOINT", 0, 0 }, -#endif -#ifdef _SC_PBS_LOCATE - { "_POSIX2_PBS_LOCATE", _SC_PBS_LOCATE, 1 }, -#else - { "_POSIX2_PBS_LOCATE", 0, 0 }, -#endif -#ifdef _SC_PBS_MESSAGE - { "_POSIX2_PBS_MESSAGE", _SC_PBS_MESSAGE, 1 }, -#else - { "_POSIX2_PBS_MESSAGE", 0, 0 }, -#endif -#ifdef _SC_PBS_TRACK - { "_POSIX2_PBS_TRACK", _SC_PBS_TRACK, 1 }, -#else - { "_POSIX2_PBS_TRACK", 0, 0 }, -#endif -#ifdef _SC_2_SW_DEV - { "_POSIX2_SW_DEV", _SC_2_SW_DEV, 1 }, -#else - { "_POSIX2_SW_DEV", 0, 0 }, -#endif -#ifdef _SC_2_UPE - { "_POSIX2_UPE", _SC_2_UPE, 1 }, -#else - { "_POSIX2_UPE", 0, 0 }, -#endif -#ifdef _SC_2_VERSION - { "_POSIX2_VERSION", _SC_2_VERSION, 1 }, -#else - { "_POSIX2_VERSION", 0, 0 }, -#endif -#ifdef _SC_ADVISORY_INFO - { "_POSIX_ADVISORY_INFO", _SC_ADVISORY_INFO, 1 }, -#else - { "_POSIX_ADVISORY_INFO", 0, 0 }, -#endif -#ifdef _SC_ASYNCHRONOUS_IO - { "_POSIX_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO, 1 }, -#else - { "_POSIX_ASYNCHRONOUS_IO", 0, 0 }, -#endif -#ifdef _SC_BARRIERS - { "_POSIX_BARRIERS", _SC_BARRIERS, 1 }, -#else - { "_POSIX_BARRIERS", 0, 0 }, -#endif -#ifdef _SC_CLOCK_SELECTION - { "_POSIX_CLOCK_SELECTION", _SC_CLOCK_SELECTION, 1 }, -#else - { "_POSIX_CLOCK_SELECTION", 0, 0 }, -#endif -#ifdef _SC_CPUTIME - { "_POSIX_CPUTIME", _SC_CPUTIME, 1 }, -#else - { "_POSIX_CPUTIME", 0, 0 }, -#endif -#ifdef _SC_FILE_LOCKING - { "_POSIX_FILE_LOCKING", _SC_FILE_LOCKING, 1 }, -#else - { "_POSIX_FILE_LOCKING", 0, 0 }, -#endif -#ifdef _SC_FSYNC - { "_POSIX_FSYNC", _SC_FSYNC, 1 }, -#else - { "_POSIX_FSYNC", 0, 0 }, -#endif -#ifdef _SC_IPV6 - { "_POSIX_IPV6", _SC_IPV6, 1 }, -#else - { "_POSIX_IPV6", 0, 0 }, -#endif -#ifdef _SC_JOB_CONTROL - { "_POSIX_JOB_CONTROL", _SC_JOB_CONTROL, 1 }, -#else - { "_POSIX_JOB_CONTROL", 0, 0 }, -#endif -#ifdef _SC_MAPPED_FILES - { "_POSIX_MAPPED_FILES", _SC_MAPPED_FILES, 1 }, -#else - { "_POSIX_MAPPED_FILES", 0, 0 }, -#endif -#ifdef _SC_MEMLOCK - { "_POSIX_MEMLOCK", _SC_MEMLOCK, 1 }, -#else - { "_POSIX_MEMLOCK", 0, 0 }, -#endif -#ifdef _SC_MEMLOCK_RANGE - { "_POSIX_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE, 1 }, -#else - { "_POSIX_MEMLOCK_RANGE", 0, 0 }, -#endif -#ifdef _SC_MEMORY_PROTECTION - { "_POSIX_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION, 1 }, -#else - { "_POSIX_MEMORY_PROTECTION", 0, 0 }, -#endif -#ifdef _SC_MESSAGE_PASSING - { "_POSIX_MESSAGE_PASSING", _SC_MESSAGE_PASSING, 1 }, -#else - { "_POSIX_MESSAGE_PASSING", 0, 0 }, -#endif -#ifdef _SC_MONOTONIC_CLOCK - { "_POSIX_MONOTONIC_CLOCK", _SC_MONOTONIC_CLOCK, 1 }, -#else - { "_POSIX_MONOTONIC_CLOCK", 0, 0 }, -#endif -#ifdef _SC_PRIORITIZED_IO - { "_POSIX_PRIORITIZED_IO", _SC_PRIORITIZED_IO, 1 }, -#else - { "_POSIX_PRIORITIZED_IO", 0, 0 }, -#endif -#ifdef _SC_PRIORITY_SCHEDULING - { "_POSIX_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING, 1 }, -#else - { "_POSIX_PRIORITY_SCHEDULING", 0, 0 }, -#endif -#ifdef _SC_RAW_SOCKETS - { "_POSIX_RAW_SOCKETS", _SC_RAW_SOCKETS, 1 }, -#else - { "_POSIX_RAW_SOCKETS", 0, 0 }, -#endif -#ifdef _SC_READER_WRITER_LOCKS - { "_POSIX_READER_WRITER_LOCKS", _SC_READER_WRITER_LOCKS, 1 }, -#else - { "_POSIX_READER_WRITER_LOCKS", 0, 0 }, -#endif -#ifdef _SC_REALTIME_SIGNALS - { "_POSIX_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS, 1 }, -#else - { "_POSIX_REALTIME_SIGNALS", 0, 0 }, -#endif -#ifdef _SC_REGEXP - { "_POSIX_REGEXP", _SC_REGEXP, 1 }, -#else - { "_POSIX_REGEXP", 0, 0 }, -#endif -#ifdef _SC_SAVED_IDS - { "_POSIX_SAVED_IDS", _SC_SAVED_IDS, 1 }, -#else - { "_POSIX_SAVED_IDS", 0, 0 }, -#endif -#ifdef _SC_SEMAPHORES - { "_POSIX_SEMAPHORES", _SC_SEMAPHORES, 1 }, -#else - { "_POSIX_SEMAPHORES", 0, 0 }, -#endif -#ifdef _SC_SHARED_MEMORY_OBJECTS - { "_POSIX_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS, 1 }, -#else - { "_POSIX_SHARED_MEMORY_OBJECTS", 0, 0 }, -#endif -#ifdef _SC_SHELL - { "_POSIX_SHELL", _SC_SHELL, 1 }, -#else - { "_POSIX_SHELL", 0, 0 }, -#endif -#ifdef _SC_SPAWN - { "_POSIX_SPAWN", _SC_SPAWN, 1 }, -#else - { "_POSIX_SPAWN", 0, 0 }, -#endif -#ifdef _SC_SPIN_LOCKS - { "_POSIX_SPIN_LOCKS", _SC_SPIN_LOCKS, 1 }, -#else - { "_POSIX_SPIN_LOCKS", 0, 0 }, -#endif -#ifdef _SC_SPORADIC_SERVER - { "_POSIX_SPORADIC_SERVER", _SC_SPORADIC_SERVER, 1 }, -#else - { "_POSIX_SPORADIC_SERVER", 0, 0 }, -#endif -#ifdef _SC_SS_REPL_MAX - { "_POSIX_SS_REPL_MAX", _SC_SS_REPL_MAX, 1 }, -#else - { "_POSIX_SS_REPL_MAX", 0, 0 }, -#endif -#ifdef _SC_SYNCHRONIZED_IO - { "_POSIX_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO, 1 }, -#else - { "_POSIX_SYNCHRONIZED_IO", 0, 0 }, -#endif -#ifdef _SC_THREADS - { "_POSIX_THREADS", _SC_THREADS, 1 }, -#else - { "_POSIX_THREADS", 0, 0 }, -#endif -#ifdef _SC_THREAD_ATTR_STACKADDR - { "_POSIX_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR, 1 }, -#else - { "_POSIX_THREAD_ATTR_STACKADDR", 0, 0 }, -#endif -#ifdef _SC_THREAD_ATTR_STACKSIZE - { "_POSIX_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE, 1 }, -#else - { "_POSIX_THREAD_ATTR_STACKSIZE", 0, 0 }, -#endif -#ifdef _SC_THREAD_CPUTIME - { "_POSIX_THREAD_CPUTIME", _SC_THREAD_CPUTIME, 1 }, -#else - { "_POSIX_THREAD_CPUTIME", 0, 0 }, -#endif -#ifdef _SC_THREAD_PRIORITY_SCHEDULING - { "_POSIX_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING, 1 }, -#else - { "_POSIX_THREAD_PRIORITY_SCHEDULING", 0, 0 }, -#endif -#ifdef _SC_THREAD_PRIO_INHERIT - { "_POSIX_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT, 1 }, -#else - { "_POSIX_THREAD_PRIO_INHERIT", 0, 0 }, -#endif -#ifdef _SC_THREAD_PRIO_PROTECT - { "_POSIX_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT, 1 }, -#else - { "_POSIX_THREAD_PRIO_PROTECT", 0, 0 }, -#endif -#ifdef _SC_THREAD_PROCESS_SHARED - { "_POSIX_THREAD_PROCESS_SHARED", _SC_THREAD_PROCESS_SHARED, 1 }, -#else - { "_POSIX_THREAD_PROCESS_SHARED", 0, 0 }, -#endif -#ifdef _SC_THREAD_SAFE_FUNCTIONS - { "_POSIX_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS, 1 }, -#else - { "_POSIX_THREAD_SAFE_FUNCTIONS", 0, 0 }, -#endif -#ifdef _SC_THREAD_SPORADIC_SERVER - { "_POSIX_THREAD_SPORADIC_SERVER", _SC_THREAD_SPORADIC_SERVER, 1 }, -#else - { "_POSIX_THREAD_SPORADIC_SERVER", 0, 0 }, -#endif -#ifdef _SC_TIMEOUTS - { "_POSIX_TIMEOUTS", _SC_TIMEOUTS, 1 }, -#else - { "_POSIX_TIMEOUTS", 0, 0 }, -#endif -#ifdef _SC_TIMERS - { "_POSIX_TIMERS", _SC_TIMERS, 1 }, -#else - { "_POSIX_TIMERS", 0, 0 }, -#endif -#ifdef _SC_TRACE - { "_POSIX_TRACE", _SC_TRACE, 1 }, -#else - { "_POSIX_TRACE", 0, 0 }, -#endif -#ifdef _SC_TRACE_EVENT_FILTER - { "_POSIX_TRACE_EVENT_FILTER", _SC_TRACE_EVENT_FILTER, 1 }, -#else - { "_POSIX_TRACE_EVENT_FILTER", 0, 0 }, -#endif -#ifdef _SC_TRACE_EVENT_NAME_MAX - { "_POSIX_TRACE_EVENT_NAME_MAX", _SC_TRACE_EVENT_NAME_MAX, 1 }, -#else - { "_POSIX_TRACE_EVENT_NAME_MAX", 0, 0 }, -#endif -#ifdef _SC_TRACE_INHERIT - { "_POSIX_TRACE_INHERIT", _SC_TRACE_INHERIT, 1 }, -#else - { "_POSIX_TRACE_INHERIT", 0, 0 }, -#endif -#ifdef _SC_TRACE_LOG - { "_POSIX_TRACE_LOG", _SC_TRACE_LOG, 1 }, -#else - { "_POSIX_TRACE_LOG", 0, 0 }, -#endif -#ifdef _SC_TRACE_NAME_MAX - { "_POSIX_TRACE_NAME_MAX", _SC_TRACE_NAME_MAX, 1 }, -#else - { "_POSIX_TRACE_NAME_MAX", 0, 0 }, -#endif -#ifdef _SC_TRACE_SYS_MAX - { "_POSIX_TRACE_SYS_MAX", _SC_TRACE_SYS_MAX, 1 }, -#else - { "_POSIX_TRACE_SYS_MAX", 0, 0 }, -#endif -#ifdef _SC_TRACE_USER_EVENT_MAX - { "_POSIX_TRACE_USER_EVENT_MAX", _SC_TRACE_USER_EVENT_MAX, 1 }, -#else - { "_POSIX_TRACE_USER_EVENT_MAX", 0, 0 }, -#endif -#ifdef _SC_TYPED_MEMORY_OBJECTS - { "_POSIX_TYPED_MEMORY_OBJECTS", _SC_TYPED_MEMORY_OBJECTS, 1 }, -#else - { "_POSIX_TYPED_MEMORY_OBJECTS", 0, 0 }, -#endif -#ifdef _SC_V6_ILP32_OFF32 - { "_POSIX_V6_ILP32_OFF32", _SC_V6_ILP32_OFF32, 1 }, -#else - { "_POSIX_V6_ILP32_OFF32", 0, 0 }, -#endif -#ifdef _SC_V6_ILP32_OFFBIG - { "_POSIX_V6_ILP32_OFFBIG", _SC_V6_ILP32_OFFBIG, 1 }, -#else - { "_POSIX_V6_ILP32_OFFBIG", 0, 0 }, -#endif -#ifdef _SC_V6_LP64_OFF64 - { "_POSIX_V6_LP64_OFF64", _SC_V6_LP64_OFF64, 1 }, -#else - { "_POSIX_V6_LP64_OFF64", 0, 0 }, -#endif -#ifdef _SC_V6_LPBIG_OFFBIG - { "_POSIX_V6_LPBIG_OFFBIG", _SC_V6_LPBIG_OFFBIG, 1 }, -#else - { "_POSIX_V6_LPBIG_OFFBIG", 0, 0 }, -#endif -#ifdef _SC_VERSION - { "_POSIX_VERSION", _SC_VERSION, 1 }, -#else - { "_POSIX_VERSION", 0, 0 }, -#endif -#ifdef _SC_XOPEN_CRYPT - { "_XOPEN_CRYPT", _SC_XOPEN_CRYPT, 1 }, -#else - { "_XOPEN_CRYPT", 0, 0 }, -#endif -#ifdef _SC_XOPEN_ENH_I18N - { "_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N, 1 }, -#else - { "_XOPEN_ENH_I18N", 0, 0 }, -#endif -#ifdef _SC_XOPEN_LEGACY - { "_XOPEN_LEGACY", _SC_XOPEN_LEGACY, 1 }, -#else - { "_XOPEN_LEGACY", 0, 0 }, -#endif -#ifdef _SC_XOPEN_REALTIME - { "_XOPEN_REALTIME", _SC_XOPEN_REALTIME, 1 }, -#else - { "_XOPEN_REALTIME", 0, 0 }, -#endif -#ifdef _SC_XOPEN_REALTIME_THREADS - { "_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS, 1 }, -#else - { "_XOPEN_REALTIME_THREADS", 0, 0 }, -#endif -#ifdef _SC_XOPEN_SHM - { "_XOPEN_SHM", _SC_XOPEN_SHM, 1 }, -#else - { "_XOPEN_SHM", 0, 0 }, -#endif -#ifdef _SC_XOPEN_STREAMS - { "_XOPEN_STREAMS", _SC_XOPEN_STREAMS, 1 }, -#else - { "_XOPEN_STREAMS", 0, 0 }, -#endif -#ifdef _SC_XOPEN_UNIX - { "_XOPEN_UNIX", _SC_XOPEN_UNIX, 1 }, -#else - { "_XOPEN_UNIX", 0, 0 }, -#endif -#ifdef _SC_XOPEN_VERSION - { "_XOPEN_VERSION", _SC_XOPEN_VERSION, 1 }, -#else - { "_XOPEN_VERSION", 0, 0 }, -#endif -#ifdef _SC_XCU_VERSION - { "_XOPEN_XCU_VERSION", _SC_XCU_VERSION, 1 }, -#else - { "_XOPEN_XCU_VERSION", 0, 0 }, -#endif - { NULL, 0, 0 } -}; -#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1) -static const struct map * -in_word_set(const char *word) -{ - const struct map *mp; - - for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) { - if (strcmp(word, mp->name) == 0) - return (mp); - } - return (NULL); -} - -int -find_sysconf(const char *name, int *key) -{ - const struct map *rv; -#ifdef APPLE_GETCONF_UNDERSCORE - char *alt; -#endif /* APPLE_GETCONF_UNDERSCORE */ - - rv = in_word_set(name); - if (rv != NULL) { - if (rv->valid) { - *key = rv->key; - return 1; - } - return -1; - } -#ifdef APPLE_GETCONF_UNDERSCORE - if(*name == '_') - alt = (char *)name + 1; - else { - if((alt = (char *)alloca(strlen(name) + 2)) == NULL) - return 0; - *alt = '_'; - strcpy(alt + 1, name); - } - rv = in_word_set(alt); - if (rv != NULL) { - if (rv->valid) { - *key = rv->key; - return 1; - } - return -1; - } -#endif /* APPLE_GETCONF_UNDERSCORE */ - return 0; -} diff --git a/getty.tproj/generate_plist.sh b/getty.tproj/generate_plist.sh index 2170bc9..ef42607 100644 --- a/getty.tproj/generate_plist.sh +++ b/getty.tproj/generate_plist.sh @@ -4,7 +4,7 @@ set -x cp "${SCRIPT_INPUT_FILE_0}" "${SCRIPT_OUTPUT_FILE_0}" case "$PLATFORM_NAME" in -iphone*|appletv*|watch*) +iphone*|appletv*|watch*|bridge*) ;; macosx) /usr/libexec/PlistBuddy -c "Add :Disabled bool true" "${SCRIPT_OUTPUT_FILE_0}" diff --git a/getty.tproj/main.c b/getty.tproj/main.c index 12c78b6..ba501fb 100644 --- a/getty.tproj/main.c +++ b/getty.tproj/main.c @@ -260,7 +260,7 @@ main(int argc, char *argv[]) */ dogettytab(); -#if defined(__APPLE__) && !TARGET_OS_EMBEDDED +#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) if (strncmp(ttyn, _PATH_CONSOLE, sizeof(ttyn)) == 0) ttyopenmode |= O_POPUP; #endif diff --git a/hostinfo.tproj/hostinfo.c b/hostinfo.tproj/hostinfo.c index 1993b30..1828583 100644 --- a/hostinfo.tproj/hostinfo.c +++ b/hostinfo.tproj/hostinfo.c @@ -54,7 +54,6 @@ main(int argc, char *argv[]) kern_return_t ret; unsigned int size, count; char *cpu_name, *cpu_subname; - int i; int mib[2]; size_t len; uint64_t memsize; @@ -99,6 +98,19 @@ main(int argc, char *argv[]) exit(EXIT_FAILURE); } + unsigned int cpu_count = 0; + unsigned int data_count = 0; + struct processor_basic_info *processor_basic_infop = NULL; + ret = host_processor_info(host, + PROCESSOR_BASIC_INFO, + &cpu_count, + (processor_info_array_t *)&processor_basic_infop, + &data_count); + if (ret != KERN_SUCCESS) { + mach_error(argv[0], ret); + exit(EXIT_FAILURE); + } + mib[0] = CTL_HW; mib[1] = HW_MEMSIZE; len = sizeof(memsize); @@ -125,8 +137,11 @@ main(int argc, char *argv[]) printf(" %s (%s)\n", cpu_name, cpu_subname); printf("Processor%s active:", (hi.avail_cpus > 1) ? "s" : ""); - for (i = 0; i < hi.avail_cpus; i++) - printf(" %d", i); + for (int i = 0; i < cpu_count; i++) { + if (processor_basic_infop[i].running) { + printf(" %d", i); + } + } printf("\n"); if (((float)memsize / (1024.0 * 1024.0)) >= 1024.0) diff --git a/iosim.tproj/iosim.c b/iosim.tproj/iosim.c index 51cf5b0..3fde5d1 100644 --- a/iosim.tproj/iosim.c +++ b/iosim.tproj/iosim.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include "panic.h" +#include #define IO_MODE_SEQ 0 #define IO_MODE_RANDOM 1 @@ -25,15 +28,23 @@ #define MAX_THREADS 1000 #define MAX_FILENAME 64 #define MAX_ITERATIONS 10000 -#define LATENCY_BIN_SIZE 500 -#define LATENCY_BINS 11 +#define LATENCY_BIN_SIZE 1000 +#define LATENCY_BINS 31 #define LOW_LATENCY_BIN_SIZE 50 -#define LOW_LATENCY_BINS 11 +#define LOW_LATENCY_BINS 21 #define THROUGHPUT_INTERVAL 5000 #define DEFAULT_FILE_SIZE (262144) #define BLOCKSIZE 1024 #define MAX_CMD_SIZE 256 #define PG_MASK ~(0xFFF) +#define kIONVMeANS2ControllerString "AppleANS2Controller" +#define kIONVMeControllerString "AppleNVMeController" + +typedef enum { + kDefaultDevice = 0, + kNVMeDevice = 1, + kNVMeDeviceANS2 = 2, +} qos_device_type_t; int burst_count = 10; /* Unit: Number ; Desc.: I/O Burst Count */ int inter_burst_duration = 0; /* Unit: msecs ; Desc.: I/O Inter-Burst Duration (-1: Random value [0,100]) */ @@ -47,24 +58,31 @@ int test_duration = 0; /* Unit: secs ; Desc.: Total Test Dura int io_tier = 0; /* Unit: 0/1/2/3; Desc.: I/O Tier */ int file_size = DEFAULT_FILE_SIZE; /* Unit: pages ; Desc.: File Size in 4096 byte blocks */ int cached_io_flag = 0; /* Unit: 0/1 ; Desc.: I/O Caching behavior (no-cached/cached) */ +int io_qos_timeout_ms = 0; /* Unit: msecs ; Desc.: I/O QOS timeout */ char *user_fname; int user_specified_file = 0; +qos_device_type_t qos_device = 0; -int64_t total_io_count; -int64_t total_io_size; -int64_t total_io_time; -int64_t total_burst_count; +int64_t total_io_count = 0; +int64_t total_io_size = 0; +int64_t total_io_time = 0; +int64_t max_io_time = 0; +int64_t total_burst_count = 0; int64_t latency_histogram[LATENCY_BINS]; int64_t burst_latency_histogram[LATENCY_BINS]; int64_t low_latency_histogram[LOW_LATENCY_BINS]; int64_t throughput_histogram[MAX_ITERATIONS]; int64_t throughput_index; +CFRunLoopTimerRef runLoopTimer = NULL; void print_usage(void); -void print_data_percentage(int percent); +void print_data_percentage(double percent); void print_stats(void); unsigned int find_io_bin(int64_t latency, int latency_bin_size, int latency_bins); void signalHandler(int sig); +void assertASP(CFRunLoopTimerRef timer, void *info ); +void start_qos_timer(void); +void stop_qos_timer(void); void perform_io(int fd, char *buf, int size, int type); void *sync_routine(void *arg); void *calculate_throughput(void *arg); @@ -72,7 +90,8 @@ void *io_routine(void *arg); void validate_option(int value, int min, int max, char *option, char *units); void print_test_setup(int value, char *option, char *units, char *comment); void setup_process_io_policy(int io_tier); -void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size); +void setup_qos_device(void); +void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count); void print_usage(void) @@ -92,9 +111,10 @@ print_usage(void) printf("-z: (number) File Size in pages (1 page = 4096 bytes) \n"); printf("-n: (string) File name used for tests (the tool would create files if this option is not specified)\n"); printf("-a: (0/1 : Non-cached/Cached) I/O Caching behavior\n"); + printf("-q: (msecs) I/O QoS timeout. Time of I/O before drive assert and system panic\n"); } -void print_data_percentage(int percent) +void print_data_percentage(double percent) { int count = (int)(round(percent / 5.0)); int spaces = 20 - count; @@ -106,7 +126,7 @@ void print_data_percentage(int percent) printf("|"); } -void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size) +void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count) { double percentage; char label[MAX_FILENAME]; @@ -118,9 +138,9 @@ void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_si else snprintf(label, MAX_FILENAME, "%d - %d usecs", i * latency_bin_size, (i+1) * latency_bin_size); printf("%25s ", label); - percentage = ((double)data[i] * 100.0) / (double)total_io_count; - print_data_percentage((int)percentage); - printf(" %.2lf%%\n", percentage); + percentage = ((double)data[i] * 100.000000) / io_count; + print_data_percentage(percentage); + printf(" %.6lf%%\n", percentage); } printf("\n"); } @@ -135,13 +155,14 @@ void print_stats() printf("Total I/Os : %lld\n", total_io_count); printf("Avg. Latency : %.2lf usecs\n", ((double)total_io_time) / ((double)total_io_count)); + printf("Max. Latency : %.2lf usecs\n", ((double)max_io_time)); printf("Low Latency Histogram: \n"); - print_latency_histogram(low_latency_histogram, LOW_LATENCY_BINS, LOW_LATENCY_BIN_SIZE); + print_latency_histogram(low_latency_histogram, LOW_LATENCY_BINS, LOW_LATENCY_BIN_SIZE, (double)total_io_count); printf("Latency Histogram: \n"); - print_latency_histogram(latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE); + print_latency_histogram(latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_io_count); printf("Burst Avg. Latency Histogram: \n"); - print_latency_histogram(burst_latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE); + print_latency_histogram(burst_latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_burst_count); printf("Throughput Timeline: \n"); @@ -176,6 +197,94 @@ void signalHandler(int sig) exit(0); } +void setup_qos_device(void) +{ + kern_return_t status = kIOReturnError; + io_iterator_t iterator = IO_OBJECT_NULL; + + if(io_qos_timeout_ms <= 0) + return; + + printf ( "*** setup_qos_device *** \n" ); + + status = IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeANS2ControllerString ), &iterator ); + + if ( status != kIOReturnSuccess ) + return; + + if ( iterator != IO_OBJECT_NULL ) { + printf ( "Found NVMe ANS2 Device \n" ); + qos_device = kNVMeDeviceANS2; + } else { + + status= IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeControllerString ), &iterator ); + + if ( status != kIOReturnSuccess ) + return; + + if ( iterator != IO_OBJECT_NULL ) { + printf ( "Found NVMe Device \n" ); + qos_device = kNVMeDevice; + } + else { + printf ( "NVMe Device not found, not setting qos timeout\n" ); + qos_device = kDefaultDevice; + } + } +} + +void assertASP(CFRunLoopTimerRef timer, void *info ) +{ + char command [ 1024 ]; + + if(qos_device == kDefaultDevice) + return; + + printf("assertASP. Timeout of IO exceeds = %d msec\n", io_qos_timeout_ms); + + // kNVMe_ANS2_Force_Assert_offset = 0x13EC, // GP59 + // kNVMe_Force_Assert_Offset = 0x550, + + if(qos_device == kNVMeDeviceANS2) + snprintf ( command, sizeof ( command ), "/usr/local/bin/nvmectl-tool.py -a WriteRegister32 $((0x13EC)) 0xFFFF" ); + else if(qos_device == kNVMeDevice) + snprintf ( command, sizeof ( command ), "/usr/local/bin/nvmectl-tool.py -a WriteRegister32 $((0x550)) 0xFFFF" ); + else + return; + + // Assert ASP + printf("Command : %s\n", command); + system(command); + + // Panic the system as well + panic("IO time > QoS timeout"); + + return; +} + +void start_qos_timer(void) +{ + float timeout_sec; + + if(io_qos_timeout_ms <= 0) + return; + + timeout_sec = (float)io_qos_timeout_ms/1000; + + // Schedule a "timeout" delayed task that checks IO's which take > timeout sec to complete + runLoopTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent()+timeout_sec, 0, 0, 0, assertASP, NULL); + CFRunLoopAddTimer(CFRunLoopGetMain(), runLoopTimer, kCFRunLoopDefaultMode); +} + +void stop_qos_timer(void) +{ + if(runLoopTimer == NULL) + return; + + CFRunLoopTimerInvalidate(runLoopTimer); + CFRunLoopRemoveTimer(CFRunLoopGetMain(), runLoopTimer, kCFRunLoopDefaultMode); + CFRelease(runLoopTimer); +} void perform_io(int fd, char *buf, int size, int type) { @@ -249,7 +358,7 @@ void *io_routine(void *arg) io_thread_id = (int)arg; if (user_specified_file) - strncpy(test_filename, user_fname, MAX_FILENAME); + strlcpy(test_filename, user_fname, MAX_FILENAME); else snprintf(test_filename, MAX_FILENAME, "iosim-%d-%d", (int)getpid(), io_thread_id); @@ -290,13 +399,22 @@ void *io_routine(void *arg) } } + start_qos_timer(); + gettimeofday(&start_tv, NULL); perform_io(fd, data, io_size, workload_type); gettimeofday(&end_tv, NULL); + stop_qos_timer(); + OSAtomicIncrement64(&total_io_count); OSAtomicAdd64(io_size, &total_io_size); elapsed = ((end_tv.tv_sec - start_tv.tv_sec) * 1000000) + (end_tv.tv_usec - start_tv.tv_usec); + + if (elapsed > max_io_time) { + max_io_time = elapsed; + } + OSAtomicAdd64(elapsed, &total_io_time); OSAtomicIncrement64(&(latency_histogram[find_io_bin(elapsed, LATENCY_BIN_SIZE, LATENCY_BINS)])); OSAtomicIncrement64(&(low_latency_histogram[find_io_bin(elapsed, LOW_LATENCY_BIN_SIZE, LOW_LATENCY_BINS)])); @@ -373,7 +491,7 @@ int main(int argc, char *argv[]) pthread_t throughput_thread; char fname[MAX_FILENAME]; - while((option = getopt(argc, argv,"hc:i:d:t:f:m:j:s:x:l:z:n:a:")) != -1) { + while((option = getopt(argc, argv,"hc:i:d:t:f:m:j:s:x:l:z:n:a:q:")) != -1) { switch(option) { case 'c': burst_count = atoi(optarg); @@ -430,6 +548,10 @@ int main(int argc, char *argv[]) cached_io_flag = atoi(optarg); validate_option(cached_io_flag, 0, 1, "I/Os cached/no-cached", ""); break; + case 'q': + io_qos_timeout_ms = atoi(optarg); + validate_option(io_qos_timeout_ms, 0, INT_MAX, "I/O QoS timeout", "msecs"); + break; default: printf("Unknown option %c\n", option); print_usage(); @@ -450,6 +572,7 @@ int main(int argc, char *argv[]) print_test_setup(test_duration, "Test duration", "secs", "0 indicates tool waits for Ctrl+C"); print_test_setup(io_tier, "I/O Tier", "", 0); print_test_setup(cached_io_flag, "I/O Caching", "", "0 indicates non-cached I/Os"); + print_test_setup(io_qos_timeout_ms, "I/O QoS Threshold Timeout", "msecs", 0); print_test_setup(0, "File read-aheads", "", "0 indicates read-aheads disabled"); printf("**********************************************************\n"); @@ -468,6 +591,8 @@ int main(int argc, char *argv[]) system("purge"); setup_process_io_policy(io_tier); + setup_qos_device(); + printf("**********************************************************\n"); printf("Creating threads and generating workload...\n"); @@ -493,9 +618,14 @@ int main(int argc, char *argv[]) exit(1); } - /* All threads are now initialized */ - if (test_duration) - alarm(test_duration); + if(io_qos_timeout_ms > 0) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)test_duration, false); + alarm(1); + } else { + /* All threads are now initialized */ + if (test_duration) + alarm(test_duration); + } for(i=0; i < thread_count; i++) pthread_join(thread_list[i], NULL); diff --git a/iostat.tproj/iostat.8 b/iostat.tproj/iostat.8 index a1487e7..f79c834 100644 --- a/iostat.tproj/iostat.8 +++ b/iostat.tproj/iostat.8 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: src/usr.sbin/iostat/iostat.8,v 1.20 2001/08/07 13:59:48 ru Exp $ +.\" $FreeBSD$ .\" .\" Copyright (c) 1985, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -38,11 +38,7 @@ .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors +.\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" @@ -60,7 +56,7 @@ .\" .\" @(#)iostat.8 8.1 (Berkeley) 6/6/93 .\" -.Dd September 27, 2001 +.Dd May 22, 2015 .Dt IOSTAT 8 .Os .Sh NAME @@ -69,15 +65,16 @@ .Tn I/O statistics .Sh SYNOPSIS -.Nm iostat +.Nm .Op Fl CUdKIoT?\& .Op Fl c Ar count .Op Fl n Ar devs .Op Fl w Ar wait .Op Ar drives .Sh DESCRIPTION -.Nm Iostat -displays kernel +The +.Nm +utility displays kernel .Tn I/O statistics on terminal, device and cpu operations. The first statistics that are printed are averaged over the system uptime. @@ -87,16 +84,8 @@ averaged over that time. .Pp The options are as follows: .Bl -tag -width flag -.\" ========== .It Fl ?\& Display a usage statement and exit. -.\" ========== -.It Fl C -Display CPU statistics. -This is on by default, unless -.Fl d -is specified. -.\" ========== .It Fl c Repeat the display .Ar count @@ -104,7 +93,11 @@ times. If no .Ar wait interval is specified, the default is 1 second. -.\" ========== +.It Fl C +Display CPU statistics. +This is on by default, unless +.Fl d +is specified. .It Fl d Display only device statistics. If this flag is turned on, only device statistics will be displayed, unless @@ -113,48 +106,42 @@ or .Fl U or .Fl T -is also specfied to enable the display of CPU, load average or TTY statistics. -.\" ========== +is also specified to enable the display of CPU, load average or TTY statistics. .It Fl I -Display total statstics for a given time period, rather than average +Display total statistics for a given time period, rather than average statistics for each second during that time period. -.\" ========== .It Fl K In the blocks transferred display (-o), display block count in kilobytes rather then the device native block size. -.\" ========== .It Fl n Display up to .Ar devs number of devices. -.Nm iostat -will display fewer devices if there aren't +The +.Nm +utility will display fewer devices if there are not .Ar devs devices present. -.\" ========== .It Fl o Display old-style -.Nm iostat +.Nm device statistics. -Sectors per second, transfers per second, and miliseconds per seek are +Sectors per second, transfers per second, and milliseconds per seek are displayed. If .Fl I is specified, total blocks/sectors, total transfers, and -miliseconds per seek are displayed. -.\" ========== +milliseconds per seek are displayed. .It Fl T Display TTY statistics. This is on by default, unless .Fl d is specified. -.\" ========== .It Fl U Display system load averages. This is on by default, unless .Fl d is specified. -.\" ========== .It Fl w Pause .Ar wait @@ -164,8 +151,9 @@ If no repeat is specified, the default is infinity. .El .Pp -.Nm Iostat -displays its information in the following format: +The +.Nm +utility displays its information in the following format: .Bl -tag -width flag .It tty .Bl -tag -width indent -compact @@ -177,20 +165,24 @@ characters written to terminals .It devices Device operations. The header of the field is the device name and unit number. -.Nm iostat +The +.Nm +utility will display as many devices as will fit in a standard 80 column screen, or the maximum number of devices in the system, whichever is smaller. If .Fl n is specified on the command line, -.Nm iostat +.Nm will display the smaller of the requested number of devices, and the maximum number of devices in the system. To force -.Nm iostat +.Nm to display specific drives, their names may be supplied on the command line. -.Nm iostat +The +.Nm +utility will not display more devices than will fit in an 80 column screen, unless the .Fl n @@ -198,11 +190,11 @@ argument is given on the command line to specify a maximum number of devices to display, or the list of specified devices exceeds 80 columns. If fewer devices are specified on the command line than will fit in an 80 column screen, -.Nm iostat +.Nm will show only the specified devices. .Pp The standard -.Nm iostat +.Nm device display shows the following statistics: .Pp .Bl -tag -width indent -compact @@ -215,7 +207,7 @@ megabytes per second .El .Pp The standard -.Nm iostat +.Nm device display, with the .Fl I flag specified, shows the following statistics: @@ -230,7 +222,7 @@ total number of megabytes transferred .El .Pp The old-style -.Nm iostat +.Nm display (using .Fl o ) shows the following statistics: @@ -245,7 +237,7 @@ average milliseconds per transaction .El .Pp The old-style -.Nm iostat +.Nm display, with the .Fl I flag specified, shows the following statistics: @@ -295,16 +287,16 @@ and .Fl C flags are given, the TTY and CPU displays will be displayed. .Sh SEE ALSO -.Xr fstat 1 , .Xr netstat 1 , .Xr nfsstat 1 , .Xr ps 1 , -.Xr pstat 8 +.Xr top 1 , +.Xr vm_stat 1 .Pp The sections starting with ``Interpreting system activity'' in .%T "Installing and Operating 4.3BSD" . .Sh HISTORY This version of -.Nm iostat +.Nm first appeared in -.Nm FreeBSD 3.0 . +.Fx 3.0 . diff --git a/iostat.tproj/iostat.c b/iostat.tproj/iostat.c index 58f62a2..a9c73cb 100644 --- a/iostat.tproj/iostat.c +++ b/iostat.tproj/iostat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * Copyright (c) 1999-2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,7 +21,9 @@ * * @APPLE_LICENSE_HEADER_END@ */ -/* +/*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry * All rights reserved. * @@ -48,7 +50,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/usr.sbin/iostat/iostat.c,v 1.22 2001/09/01 07:40:19 kris Exp $ + * $FreeBSD$ */ /* * Parts of this program are derived from the original FreeBSD iostat @@ -66,10 +68,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -176,7 +174,7 @@ static IONotificationPortRef notifyPort; /* local function declarations */ static void usage(void); static void phdr(int signo); -static void do_phdr(); +static void do_phdr(void); static void devstats(int perf_select, long double etime, int havelast); static void cpustats(void); static void loadstats(void); diff --git a/latency.tproj/latency.c b/latency.tproj/latency.c index 8047b2d..afd67cc 100644 --- a/latency.tproj/latency.c +++ b/latency.tproj/latency.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -176,7 +177,7 @@ typedef struct event *event_t; struct event { event_t ev_next; - uintptr_t ev_thread; + uint64_t ev_thread; uint32_t ev_type; uint64_t ev_timestamp; }; @@ -187,10 +188,10 @@ typedef struct lookup *lookup_t; struct lookup { lookup_t lk_next; - uintptr_t lk_thread; - uintptr_t lk_dvp; - long *lk_pathptr; - long lk_pathname[NUMPARMS + 1]; + uint64_t lk_thread; + uint64_t lk_dvp; + int64_t *lk_pathptr; + int64_t lk_pathname[NUMPARMS + 1]; }; @@ -199,8 +200,8 @@ typedef struct threadmap *threadmap_t; struct threadmap { threadmap_t tm_next; - uintptr_t tm_thread; - uintptr_t tm_pthread; + uint64_t tm_thread; + uint64_t tm_pthread; char tm_command[MAXCOMLEN + 1]; char tm_orig_command[MAXCOMLEN + 1]; }; @@ -211,7 +212,7 @@ typedef struct threadrun *threadrun_t; struct threadrun { threadrun_t tr_next; - uintptr_t tr_thread; + uint64_t tr_thread; kd_buf *tr_entry; uint64_t tr_timestamp; int tr_priority; @@ -223,7 +224,7 @@ typedef struct thread_entry *thread_entry_t; struct thread_entry { thread_entry_t te_next; - uintptr_t te_thread; + uint64_t te_thread; }; #define HASH_SIZE 1024 @@ -326,23 +327,23 @@ const char *sched_reasons[] = { static double handle_decrementer(kd_buf *, int); static kd_buf *log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, double i_latency); static void read_command_map(void); -static void enter_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info); -static void exit_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info); -static void print_entry(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd_note); +static void enter_syscall(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info); +static void exit_syscall(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info); +static void print_entry(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd_note); static void log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf *kd_note); static char *find_code(int); -static void pc_to_string(char *pcstring, uintptr_t pc, int max_len, int mode); +static void pc_to_string(char *pcstring, uint64_t pc, int max_len, int mode); static void getdivisor(void); static int sample_sc(void); static void init_code_file(void); static void do_kernel_nm(void); static void open_logfile(const char*); -static int binary_search(kern_sym_t *list, int low, int high, uintptr_t addr); +static int binary_search(kern_sym_t *list, int low, int high, uint64_t addr); -static void create_map_entry(uintptr_t, char *); -static void check_for_thread_update(uintptr_t thread, int debugid_base, kd_buf *kbufp, char **command); -static void log_scheduler(kd_buf *kd_start, kd_buf *kd_stop, kd_buf *end_of_sample, int s_priority, double s_latency, uintptr_t thread); -static int check_for_scheduler_latency(int type, uintptr_t *thread, uint64_t now, kd_buf *kd, kd_buf **kd_start, int *priority, double *latency); +static void create_map_entry(uint64_t, char *); +static void check_for_thread_update(uint64_t thread, int debugid_base, kd_buf *kbufp, char **command); +static void log_scheduler(kd_buf *kd_start, kd_buf *kd_stop, kd_buf *end_of_sample, int s_priority, double s_latency, uint64_t thread); +static int check_for_scheduler_latency(int type, uint64_t *thread, uint64_t now, kd_buf *kd, kd_buf **kd_start, int *priority, double *latency); static void open_rawfile(const char *path); static void screen_update(FILE *); @@ -365,6 +366,8 @@ quit(char *s) set_remove(); } } + endwin(); + printf("latency: "); if (s) { printf("%s", s); @@ -905,6 +908,12 @@ exit_usage(void) exit(1); } +static void +resetscr(void) +{ + (void)endwin(); +} + int main(int argc, char *argv[]) { @@ -1029,9 +1038,10 @@ main(int argc, char *argv[]) if (!RAW_flag) { if (initscr() == NULL) { - printf("Unrecognized TERM type, try vt100\n"); + fprintf(stderr, "Unrecognized TERM type, try vt100\n"); exit(1); } + atexit(resetscr); clear(); refresh(); @@ -1219,7 +1229,7 @@ read_command_map(void) } void -create_map_entry(uintptr_t thread, char *command) +create_map_entry(uint64_t thread, char *command) { threadmap_t tme; @@ -1242,7 +1252,7 @@ create_map_entry(uintptr_t thread, char *command) } static void -delete_thread_entry(uintptr_t thread) +delete_thread_entry(uint64_t thread) { threadmap_t tme; @@ -1270,7 +1280,7 @@ delete_thread_entry(uintptr_t thread) } static void -find_and_insert_tmp_map_entry(uintptr_t pthread, char *command) +find_and_insert_tmp_map_entry(uint64_t pthread, char *command) { threadmap_t tme; @@ -1301,7 +1311,7 @@ find_and_insert_tmp_map_entry(uintptr_t pthread, char *command) } static void -create_tmp_map_entry(uintptr_t thread, uintptr_t pthread) +create_tmp_map_entry(uint64_t thread, uint64_t pthread) { threadmap_t tme; @@ -1321,7 +1331,7 @@ create_tmp_map_entry(uintptr_t thread, uintptr_t pthread) } static threadmap_t -find_thread_entry(uintptr_t thread) +find_thread_entry(uint64_t thread) { threadmap_t tme; @@ -1336,7 +1346,7 @@ find_thread_entry(uintptr_t thread) } static void -find_thread_name(uintptr_t thread, char **command) +find_thread_name(uint64_t thread, char **command) { threadmap_t tme; @@ -1348,7 +1358,7 @@ find_thread_name(uintptr_t thread, char **command) } static void -add_thread_entry_to_list(thread_entry_t *list, uintptr_t thread) +add_thread_entry_to_list(thread_entry_t *list, uint64_t thread) { thread_entry_t te; @@ -1364,7 +1374,7 @@ add_thread_entry_to_list(thread_entry_t *list, uintptr_t thread) } static void -exec_thread_entry(uintptr_t thread, char *command) +exec_thread_entry(uint64_t thread, char *command) { threadmap_t tme; @@ -1383,7 +1393,7 @@ exec_thread_entry(uintptr_t thread, char *command) } static void -record_thread_entry_for_gc(uintptr_t thread) +record_thread_entry_for_gc(uint64_t thread) { add_thread_entry_to_list(&thread_delete_list, thread); } @@ -1468,7 +1478,7 @@ delete_all_thread_entries(void) } static void -insert_run_event(uintptr_t thread, int priority, kd_buf *kd, uint64_t now) +insert_run_event(uint64_t thread, int priority, kd_buf *kd, uint64_t now) { threadrun_t trp; @@ -1499,7 +1509,7 @@ insert_run_event(uintptr_t thread, int priority, kd_buf *kd, uint64_t now) } static threadrun_t -find_run_event(uintptr_t thread) +find_run_event(uint64_t thread) { threadrun_t trp; int hashid = thread & HASH_MASK; @@ -1513,7 +1523,7 @@ find_run_event(uintptr_t thread) } static void -delete_run_event(uintptr_t thread) +delete_run_event(uint64_t thread) { threadrun_t trp = 0; threadrun_t trp_prev; @@ -1571,7 +1581,7 @@ gc_run_events(void) static void -insert_start_event(uintptr_t thread, int type, uint64_t now) +insert_start_event(uint64_t thread, int type, uint64_t now) { event_t evp; @@ -1602,7 +1612,7 @@ insert_start_event(uintptr_t thread, int type, uint64_t now) static uint64_t -consume_start_event(uintptr_t thread, int type, uint64_t now) +consume_start_event(uint64_t thread, int type, uint64_t now) { event_t evp; event_t evp_prev; @@ -1668,7 +1678,7 @@ gc_start_events(void) } static int -thread_in_user_mode(uintptr_t thread, char *command) +thread_in_user_mode(uint64_t thread, char *command) { event_t evp; @@ -1687,7 +1697,7 @@ thread_in_user_mode(uintptr_t thread, char *command) } static lookup_t -handle_lookup_event(uintptr_t thread, int debugid, kd_buf *kdp) +handle_lookup_event(uint64_t thread, int debugid, kd_buf *kdp) { lookup_t lkp; boolean_t first_record = FALSE; @@ -1744,7 +1754,7 @@ handle_lookup_event(uintptr_t thread, int debugid, kd_buf *kdp) } static void -delete_lookup_event(uintptr_t thread, lookup_t lkp_to_delete) +delete_lookup_event(uint64_t thread, lookup_t lkp_to_delete) { lookup_t lkp; lookup_t lkp_prev; @@ -1874,7 +1884,7 @@ sample_sc(void) for (kd = (kd_buf *)my_buffer; kd < end_of_sample; kd++) { kd_buf *kd_start; - uintptr_t thread = kd->arg5; + uint64_t thread = kd->arg5; int type = kd->debugid & DBG_FUNC_MASK; (void)check_for_thread_update(thread, type, kd, NULL); @@ -1914,7 +1924,7 @@ sample_sc(void) } void -enter_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info) +enter_syscall(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info) { char *p; double timestamp; @@ -1939,19 +1949,19 @@ enter_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, u pc_to_string(&pcstring[0], kd->arg2, 58, mode); - fprintf(fp, "%9.1f %8.1f\t\tINTERRUPT[%2lx] @ %-58.58s %8lx %2d %s\n", - timestamp, delta, kd->arg1, &pcstring[0], thread, cpunum, command); + fprintf(fp, "%9.1f %8.1f\t\tINTERRUPT[%2" PRIx64 "] @ %-58.58s %8" PRIx64 " %2d %s\n", + timestamp, delta, (uint64_t)kd->arg1, &pcstring[0], thread, cpunum, command); } else if (type == MACH_vmfault) { - fprintf(fp, "%9.1f %8.1f\t\t%-28.28s %8lx %2d %s\n", + fprintf(fp, "%9.1f %8.1f\t\t%-28.28s %8" PRIx64 " %2d %s\n", timestamp, delta, p, thread, cpunum, command); } else { - fprintf(fp, "%9.1f %8.1f\t\t%-28.28s %-16lx %-16lx %-16lx %-16lx %8lx %2d %s\n", - timestamp, delta, p, kd->arg1, kd->arg2, kd->arg3, kd->arg4, + fprintf(fp, "%9.1f %8.1f\t\t%-28.28s %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n", + timestamp, delta, p, (uint64_t)kd->arg1, (uint64_t)kd->arg2, (uint64_t)kd->arg3, (uint64_t)kd->arg4, thread, cpunum, command); } } else { - fprintf(fp, "%9.1f %8.1f\t\t%-8x %-16lx %-16lx %-16lx %-16lx %8lx %2d %s\n", - timestamp, delta, type, kd->arg1, kd->arg2, kd->arg3, kd->arg4, + fprintf(fp, "%9.1f %8.1f\t\t%-8x %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n", + timestamp, delta, type, (uint64_t)kd->arg1, (uint64_t)kd->arg2, (uint64_t)kd->arg3, (uint64_t)kd->arg4, thread, cpunum, command); } } @@ -1961,7 +1971,7 @@ enter_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, u } void -exit_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info) +exit_syscall(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info) { char *p; uint64_t user_addr; @@ -1981,28 +1991,28 @@ exit_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, ui if ((p = find_code(type))) { if (type == INTERRUPT) { - fprintf(fp, "INTERRUPT %8lx %2d %s\n", thread, cpunum, command); + fprintf(fp, "INTERRUPT %8" PRIx64 " %2d %s\n", thread, cpunum, command); } else if (type == MACH_vmfault && kd->arg4 <= DBG_PAGEIND_FAULT) { user_addr = ((uint64_t)kd->arg1 << 32) | (uint32_t)kd->arg2; - fprintf(fp, "%-28.28s %-10.10s %-16qx %8lx %2d %s\n", + fprintf(fp, "%-28.28s %-10.10s %-16qx %8" PRIx64 " %2d %s\n", p, fault_name[kd->arg4], user_addr, thread, cpunum, command); } else { - fprintf(fp, "%-28.28s %-16lx %-16lx %8lx %2d %s\n", - p, kd->arg1, kd->arg2, + fprintf(fp, "%-28.28s %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n", + p, (uint64_t)kd->arg1, (uint64_t)kd->arg2, thread, cpunum, command); } } else { - fprintf(fp, "%-8x %-16lx %-16lx %8lx %2d %s\n", - type, kd->arg1, kd->arg2, + fprintf(fp, "%-8x %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n", + type, (uint64_t)kd->arg1, (uint64_t)kd->arg2, thread, cpunum, command); } } } void -print_entry(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd_note) +print_entry(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd_note) { char *p; @@ -2021,17 +2031,17 @@ print_entry(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uin } else { fprintf(fp, "%9.1f %8.1f\t\t", timestamp, delta); } - fprintf(fp, "%-28.28s %-16lx %-16lx %-16lx %-16lx %8lx %2d %s\n", - p, kd->arg1, kd->arg2, kd->arg3, kd->arg4, thread, cpunum, command); + fprintf(fp, "%-28.28s %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n", + p, (uint64_t)kd->arg1, (uint64_t)kd->arg2, (uint64_t)kd->arg3, (uint64_t)kd->arg4, thread, cpunum, command); } else { - fprintf(fp, "%9.1f %8.1f\t\t%-8x %-16lx %-16lx %-16lx %-16lx %8lx %2d %s\n", - timestamp, delta, type, kd->arg1, kd->arg2, kd->arg3, kd->arg4, + fprintf(fp, "%9.1f %8.1f\t\t%-8x %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n", + timestamp, delta, type, (uint64_t)kd->arg1, (uint64_t)kd->arg2, (uint64_t)kd->arg3, (uint64_t)kd->arg4, thread, cpunum, command); } } void -check_for_thread_update(uintptr_t thread, int debugid_base, kd_buf *kbufp, char **command) +check_for_thread_update(uint64_t thread, int debugid_base, kd_buf *kbufp, char **command) { if (debugid_base == TRACE_DATA_NEWTHREAD) { /* @@ -2060,7 +2070,7 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf { lookup_t lkp; int mode; - uintptr_t reason; + uint64_t reason; char *p; char *command; char *command1; @@ -2073,7 +2083,7 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf double delta; char joe[32]; - uintptr_t thread = kd->arg5; + uint64_t thread = kd->arg5; int cpunum = CPU_NUMBER(kd); int debugid = kd->debugid; int type = kd->debugid & DBG_FUNC_MASK; @@ -2093,28 +2103,28 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf case CQ_action: pc_to_string(&pcstring[0], kd->arg1, 84, KERNEL_MODE); - fprintf(log_fp, "%9.1f %8.1f\t\tCQ_action @ %-84.84s %8lx %2d %s\n", + fprintf(log_fp, "%9.1f %8.1f\t\tCQ_action @ %-84.84s %8" PRIx64 " %2d %s\n", timestamp, delta, &pcstring[0], thread, cpunum, command); break; case TES_action: pc_to_string(&pcstring[0], kd->arg1, 83, KERNEL_MODE); - fprintf(log_fp, "%9.1f %8.1f\t\tTES_action @ %-83.83s %8lx %2d %s\n", + fprintf(log_fp, "%9.1f %8.1f\t\tTES_action @ %-83.83s %8" PRIx64 " %2d %s\n", timestamp, delta, &pcstring[0], thread, cpunum, command); break; case IES_action: pc_to_string(&pcstring[0], kd->arg1, 83, KERNEL_MODE); - fprintf(log_fp, "%9.1f %8.1f\t\tIES_action @ %-83.83s %8lx %2d %s\n", + fprintf(log_fp, "%9.1f %8.1f\t\tIES_action @ %-83.83s %8" PRIx64 " %2d %s\n", timestamp, delta, &pcstring[0], thread, cpunum, command); break; case IES_filter: pc_to_string(&pcstring[0], kd->arg1, 83, KERNEL_MODE); - fprintf(log_fp, "%9.1f %8.1f\t\tIES_filter @ %-83.83s %8lx %2d %s\n", + fprintf(log_fp, "%9.1f %8.1f\t\tIES_filter @ %-83.83s %8" PRIx64 " %2d %s\n", timestamp, delta, &pcstring[0], thread, cpunum, command); break; @@ -2139,12 +2149,12 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf pc_to_string(&pcstring[0], kd->arg2, 84, mode); - fprintf(log_fp, "%9.1f %8.1f[%.1f]%s\tDECR_TRAP @ %-84.84s %8lx %2d %s\n", + fprintf(log_fp, "%9.1f %8.1f[%.1f]%s\tDECR_TRAP @ %-84.84s %8" PRIx64 " %2d %s\n", timestamp, delta, i_latency, p, &pcstring[0], thread, cpunum, command); break; case DECR_SET: - fprintf(log_fp, "%9.1f %8.1f[%.1f] \t%-28.28s %8lx %2d %s\n", + fprintf(log_fp, "%9.1f %8.1f[%.1f] \t%-28.28s %8" PRIx64 " %2d %s\n", timestamp, delta, (double)kd->arg1/divisor, "DECR_SET", thread, cpunum, command); break; @@ -2155,7 +2165,7 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf if (command1 == EMPTYSTRING) { command1 = command_buf; - sprintf(command1, "%-8lx", kd->arg2); + sprintf(command1, "%-8" PRIx64, (uint64_t)kd->arg2); } if (thread_in_user_mode(kd->arg2, command1)) { p = "U"; @@ -2172,12 +2182,12 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf } if (sched_reason[0] == '?') { - sprintf(joe, "%lx", reason); + sprintf(joe, "%" PRIx64, reason); sched_reason = joe; } - sprintf(sched_info, "%16.16s @ pri %3lu --> %16.16s @ pri %3lu%s", command, kd->arg3, command1, kd->arg4, p); + sprintf(sched_info, "%16.16s @ pri %3" PRIu64 " --> %16.16s @ pri %3" PRIu64 "%s", command, (uint64_t)kd->arg3, command1, (uint64_t)kd->arg4, p); - fprintf(log_fp, "%9.1f %8.1f\t\t%-10.10s[%s] %s %8lx %2d\n", + fprintf(log_fp, "%9.1f %8.1f\t\t%-10.10s[%s] %s %8" PRIx64 " %2d\n", timestamp, delta, "MACH_SCHED", sched_reason, sched_info, thread, cpunum); break; @@ -2195,7 +2205,7 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf clen = 0; } - fprintf(log_fp, "%9.1f %8.1f\t\t%-14.14s %-59s %-16lx %8lx %2d %s\n", + fprintf(log_fp, "%9.1f %8.1f\t\t%-14.14s %-59s %-16" PRIx64 " %8" PRIx64 " %2d %s\n", timestamp, delta, "VFS_LOOKUP", &p[clen], lkp->lk_dvp, thread, cpunum, command); @@ -2250,7 +2260,7 @@ log_range(kd_buf *kd_buffer, kd_buf *kd_start, kd_buf *kd_stop, kd_buf *kd_note, last_timestamp = now; } else { int debugid = kd->debugid; - uintptr_t thread = kd->arg5; + uint64_t thread = kd->arg5; int type = kd->debugid & DBG_FUNC_MASK; if ((type >> 24) == DBG_TRACE) { @@ -2282,7 +2292,7 @@ log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, double i_ double sample_timestamp; char buf1[128]; - uintptr_t thread = kd_beg->arg5; + uint64_t thread = kd_beg->arg5; int cpunum = CPU_NUMBER(kd_end); for (kd_count = 0, kd_start = kd_beg - 1; (kd_start >= (kd_buf *)my_buffer); kd_start--, kd_count++) { @@ -2347,7 +2357,7 @@ log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, double i_ void -log_scheduler(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, int s_priority, double s_latency, uintptr_t thread) +log_scheduler(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, int s_priority, double s_latency, uint64_t thread) { kd_buf *kd_start, *kd_stop; uint64_t now; @@ -2399,7 +2409,7 @@ log_scheduler(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, int s_prior } int -check_for_scheduler_latency(int type, uintptr_t *thread, uint64_t now, kd_buf *kd, kd_buf **kd_start, int *priority, double *latency) +check_for_scheduler_latency(int type, uint64_t *thread, uint64_t now, kd_buf *kd, kd_buf **kd_start, int *priority, double *latency) { int found_latency = 0; @@ -2583,7 +2593,7 @@ do_kernel_nm(void) /* * Build the nm command and create a tmp file with the output */ - sprintf (tmpstr, "/usr/bin/nm -f -n -s __TEXT __text %s > %s", + sprintf (tmpstr, "/usr/bin/nm -n %s -s __TEXT __text > %s", kernelpath, tmp_nm_file); system(tmpstr); @@ -2679,19 +2689,19 @@ do_kernel_nm(void) } void -pc_to_string(char *pcstring, uintptr_t pc, int max_len, int mode) +pc_to_string(char *pcstring, uint64_t pc, int max_len, int mode) { int ret; size_t len; if (mode == USER_MODE) { - sprintf(pcstring, "%-16lx [usermode addr]", pc); + sprintf(pcstring, "%-16" PRIx64 " [usermode addr]", pc); return; } ret = binary_search(kern_sym_tbl, 0, kern_sym_count-1, pc); if (ret == -1 || kern_sym_tbl[ret].k_sym_name == NULL) { - sprintf(pcstring, "%-16lx", pc); + sprintf(pcstring, "%-16" PRIx64, pc); return; } if ((len = kern_sym_tbl[ret].k_sym_len) > (max_len - 8)) { @@ -2700,7 +2710,7 @@ pc_to_string(char *pcstring, uintptr_t pc, int max_len, int mode) memcpy(pcstring, kern_sym_tbl[ret].k_sym_name, len); - sprintf(&pcstring[len], "+0x%-5lx", pc - (uintptr_t)kern_sym_tbl[ret].k_sym_addr); + sprintf(&pcstring[len], "+0x%-5" PRIx64, pc - (uint64_t)kern_sym_tbl[ret].k_sym_addr); } @@ -2708,7 +2718,7 @@ pc_to_string(char *pcstring, uintptr_t pc, int max_len, int mode) * Return -1 if not found, else return index */ int -binary_search(kern_sym_t *list, int low, int high, uintptr_t addr) +binary_search(kern_sym_t *list, int low, int high, uint64_t addr) { int mid; @@ -2721,13 +2731,13 @@ binary_search(kern_sym_t *list, int low, int high, uintptr_t addr) } if (low + 1 == high) { - if ((uintptr_t)list[low].k_sym_addr <= addr && addr < (uintptr_t)list[high].k_sym_addr) { + if ((uint64_t)list[low].k_sym_addr <= addr && addr < (uint64_t)list[high].k_sym_addr) { /* * We have a range match */ return low; } - if ((uintptr_t)list[high].k_sym_addr <= addr) { + if ((uint64_t)list[high].k_sym_addr <= addr) { return high; } /* @@ -2737,7 +2747,7 @@ binary_search(kern_sym_t *list, int low, int high, uintptr_t addr) } mid = (low + high) / 2; - if (addr < (uintptr_t)list[mid].k_sym_addr) { + if (addr < (uint64_t)list[mid].k_sym_addr) { return binary_search(list, low, mid, addr); } diff --git a/login.tproj/login.c b/login.tproj/login.c index d82ee2a..d32a06d 100644 --- a/login.tproj/login.c +++ b/login.tproj/login.c @@ -671,7 +671,7 @@ main(int argc, char *argv[]) bail(SLEEP_EXIT, 1); } -#if defined(__APPLE__) && TARGET_OS_EMBEDDED +#if defined(__APPLE__) && (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) /* on embedded, allow a shell to live in /private/var/personalized_debug/bin/sh */ #define _PATH_DEBUGSHELL "/private/var/personalized_debug/bin/sh" if (stat(pwd->pw_shell, &st) != 0) { @@ -1235,12 +1235,16 @@ getloginname(void) err(1, "malloc()"); do { (void)printf("%s", prompt); - for (p = nbuf; (ch = getchar()) != '\n'; ) { - if (ch == EOF) { - badlogin(username); - bail(NO_SLEEP_EXIT, 0); - } - if (p < nbuf + MAXLOGNAME - 1) + /* rdar://43101375 login process on 2018 hardware is blocked forever waiting on new line char + * The carriage return char is added to the termination condition of the + * for loop because for some reason, '\r' is returned by getchar() on M9 hardware. + */ + for (p = nbuf; (((ch = getchar()) != '\n') && (ch != '\r')); ) { + if (ch == EOF) { + badlogin(username); + bail(NO_SLEEP_EXIT, 0); + } + if (p < nbuf + MAXLOGNAME - 1) *p++ = ch; } } while (p == nbuf); diff --git a/lskq.tproj/common.h b/lskq.tproj/common.h index 84cb434..959ac66 100644 --- a/lskq.tproj/common.h +++ b/lskq.tproj/common.h @@ -24,6 +24,8 @@ #ifndef _LSKQ_COMMON_H_ #define _LSKQ_COMMON_H_ +#include + /* * This file must be kept in sync with xnu headers */ @@ -31,25 +33,53 @@ /* * bsd/sys/event.h */ -#define KN_ACTIVE 0x01 -#define KN_QUEUED 0x02 -#define KN_DISABLED 0x04 -#define KN_DROPPING 0x08 -#define KN_USEWAIT 0x10 -#define KN_ATTACHING 0x20 -#define KN_STAYQUEUED 0x40 -#define KN_DEFERDROP 0x80 -#define KN_TOUCH 0x100 - +__options_decl(kn_status_t, uint16_t /* 12 bits really */, { + KN_ACTIVE = 0x001, /* event has been triggered */ + KN_QUEUED = 0x002, /* event is on queue */ + KN_DISABLED = 0x004, /* event is disabled */ + KN_DROPPING = 0x008, /* knote is being dropped */ + KN_LOCKED = 0x010, /* knote is locked (kq_knlocks) */ + KN_POSTING = 0x020, /* f_event() in flight */ + KN_STAYACTIVE = 0x040, /* force event to stay active */ + KN_DEFERDELETE = 0x080, /* defer delete until re-enabled */ + KN_MERGE_QOS = 0x100, /* f_event() / f_* ran concurrently and overrides must merge */ + KN_REQVANISH = 0x200, /* requested EV_VANISH */ + KN_VANISHED = 0x400, /* has vanished */ + KN_SUPPRESSED = 0x800, /* event is suppressed during delivery */ +}); /* * bsd/sys/eventvar.h */ -#define KQ_SEL 0x01 -#define KQ_SLEEP 0x02 -#define KQ_KEV32 0x08 -#define KQ_KEV64 0x10 -#define KQ_KEV_QOS 0x20 +__options_decl(kq_state_t, uint16_t, { + KQ_SEL = 0x0001, /* select was recorded for kq */ + KQ_SLEEP = 0x0002, /* thread is waiting for events */ + KQ_PROCWAIT = 0x0004, /* thread waiting for processing */ + KQ_KEV32 = 0x0008, /* kq is used with 32-bit events */ + KQ_KEV64 = 0x0010, /* kq is used with 64-bit events */ + KQ_KEV_QOS = 0x0020, /* kq events carry QoS info */ + KQ_WORKQ = 0x0040, /* KQ is bound to process workq */ + KQ_WORKLOOP = 0x0080, /* KQ is part of a workloop */ + KQ_PROCESSING = 0x0100, /* KQ is being processed */ + KQ_DRAIN = 0x0200, /* kq is draining */ + KQ_WAKEUP = 0x0400, /* kq awakened while processing */ + KQ_DYNAMIC = 0x0800, /* kqueue is dynamically managed */ + KQ_R2K_ARMED = 0x1000, /* ast notification armed */ + KQ_HAS_TURNSTILE = 0x2000, /* this kqueue has a turnstile */ +}); + +/* + * bsd/pthread/workqueue_internal.h + */ +__enum_decl(workq_tr_state_t, uint8_t, { + WORKQ_TR_STATE_IDLE = 0, /* request isn't in flight */ + WORKQ_TR_STATE_NEW = 1, /* request is being initiated */ + WORKQ_TR_STATE_QUEUED = 2, /* request is being queued */ + WORKQ_TR_STATE_CANCELED = 3, /* request is canceled */ + WORKQ_TR_STATE_BINDING = 4, /* request is preposted for bind */ + WORKQ_TR_STATE_BOUND = 5, /* request is bound to a thread */ +}); + /* * bsd/sys/signal.h @@ -110,6 +140,9 @@ filt_strs[] = { "VM", "SOCK", "MEMSTATUS", + "EXCEPT", + "CHANNEL", + "WORKLOOP", }; /* @@ -125,6 +158,10 @@ fdtype_strs[] = { "KQUEUE", "PIPE", "FSEVENTS", + "ATALK", + "POLICY", + "CHANNEL", + "NEXUS", }; #endif /* _LSKQ_COMMON_H_ */ diff --git a/lskq.tproj/lskq.1 b/lskq.tproj/lskq.1 index ff79a92..88200e1 100644 --- a/lskq.tproj/lskq.1 +++ b/lskq.tproj/lskq.1 @@ -113,10 +113,10 @@ NOTE_SIGNAL NOTE_REAP .Pp .It EVFILT_TIMER: -.It Sy s u n -NOTE_SECONDS, NOTE_USECONDS, NOTE_NSECONDS -.It Sy a -NOTE_ABSOLUTE +.It Sy s u n m +NOTE_SECONDS, NOTE_USECONDS, NOTE_NSECONDS, NOTE_MACHTIME +.It Sy a A +NOTE_ABSOLUTE, NOTE_MACH_CONTINUOUS_TIME .It Sy c NOTE_CRITICAL .It Sy b @@ -131,6 +131,18 @@ NOTE_TRIGGER NOTE_FFAND .It Sy o NOTE_FFOR +.Pp +.It EVFILT_WORKLOOP: +.It Sy t w i +NOTE_WL_THREAD_REQUEST, NOTE_WL_SYNC_WAIT, NOTE_WL_SYNC_IPC +.It Sy W +NOTE_WL_SYNC_WAKE +.It Sy q +NOTE_WL_UPDATE_QOS +.It Sy o +NOTE_WL_DISCOVER_OWNER +.It Sy e +NOTE_WL_IGNORE_ESTALE .El .It flags kevent generic flags bitmask. @@ -172,21 +184,30 @@ KN_ACTIVE (event has triggered) .It Sy q KN_QUEUED (event has been added to the active list) .It Sy d -KN_DISABLED +KN_DISABLED (knote is disabled) +.It Sy p +KN_SUPPRESSED (event delivery is in flight) .It Sy s -KN_STAYQUEUED (event is marked as always-enqueued on the active list) +KN_STAYACTIVE (event is marked as always-enqueued on the active list) .Pp -.It Sy o -KN_DROPPING -.It Sy u -KN_USEWAIT -.It Sy c -KN_ATTACHING -.It Sy f -KN_DEFERDROP -.It Sy t -KN_TOUCH +.It Sy d +KN_DROPPING (knote is about to be dropped) +.It Sy l +KN_LOCKED (knote is locked) +.It Sy P +KN_POSTING (knote is being posted) +.It Sy m +KN_MERGE_QOS (knote is in override saturating mode) +.Pp +.It Sy D +KN_DEFERDELETE (knote is waiting for deferred-delete ack) +.It Sy v +KN_REQVANISH +.It Sy n +KN_VANISHED .El +.It qos +The QoS requested for the knote. .It data Filter-specific data. .El diff --git a/lskq.tproj/lskq.c b/lskq.tproj/lskq.c index 6a3c74d..6eaa3a8 100644 --- a/lskq.tproj/lskq.c +++ b/lskq.tproj/lskq.c @@ -21,6 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include #include #include #include @@ -32,8 +33,14 @@ #include #include #include +#include +#include #include +#define PRIVATE #include +#undef PRIVATE +#include +#include #include "common.h" @@ -125,25 +132,38 @@ fflags_build(struct kevent_extinfo *info, char *str, int len) case EVFILT_TIMER: { snprintf(str, len, "%c%c%c%c%c ", - (ff & NOTE_SECONDS) ? 's' : - (ff & NOTE_USECONDS) ? 'u' : - (ff & NOTE_NSECONDS) ? 'n' : '?', - (ff & NOTE_ABSOLUTE) ? 'a' : '-', - (ff & NOTE_CRITICAL) ? 'c' : '-', - (ff & NOTE_BACKGROUND) ? 'b' : '-', - (ff & NOTE_LEEWAY) ? 'l' : '-' + (ff & NOTE_SECONDS) ? 's' : + (ff & NOTE_USECONDS) ? 'u' : + (ff & NOTE_NSECONDS) ? 'n' : + (ff & NOTE_MACHTIME) ? 'm' : '?', + (ff & NOTE_ABSOLUTE) ? 'a' : + (ff & NOTE_MACH_CONTINUOUS_TIME) ? 'A' : '-', + (ff & NOTE_CRITICAL) ? 'c' : '-', + (ff & NOTE_BACKGROUND) ? 'b' : '-', + (ff & NOTE_LEEWAY) ? 'l' : '-' ); break; } - case EVFILT_USER: { + case EVFILT_USER: snprintf(str, len, "%c%c%c ", (ff & NOTE_TRIGGER) ? 't' : '-', (ff & NOTE_FFAND) ? 'a' : '-', (ff & NOTE_FFOR) ? 'o' : '-' ); break; - } + + case EVFILT_WORKLOOP: + snprintf(str, len, "%c%c%c%c%c ", + (ff & NOTE_WL_THREAD_REQUEST) ? 't' : + (ff & NOTE_WL_SYNC_WAIT) ? 'w' : + (ff & NOTE_WL_SYNC_IPC) ? 'i' : '-', + (ff & NOTE_WL_SYNC_WAKE) ? 'W' : '-', + (ff & NOTE_WL_UPDATE_QOS) ? 'q' : '-', + (ff & NOTE_WL_DISCOVER_OWNER) ? 'o' : '-', + (ff & NOTE_WL_IGNORE_ESTALE) ? 'e' : '-' + ); + break; default: snprintf(str, len, ""); @@ -157,13 +177,32 @@ fflags_build(struct kevent_extinfo *info, char *str, int len) static inline int filter_is_fd_type(int filter) { - if (filter <= EVFILT_READ && filter >= EVFILT_VNODE) { + switch (filter) { + case EVFILT_VNODE ... EVFILT_READ: + case EVFILT_SOCK: + case EVFILT_NW_CHANNEL: return 1; - } else { + default: return 0; } } +static const char * +thread_qos_name(uint8_t th_qos) +{ + switch (th_qos) { + case 0: return "--"; + case 1: return "MT"; + case 2: return "BG"; + case 3: return "UT"; + case 4: return "DF"; + case 5: return "IN"; + case 6: return "UI"; + case 7: return "MG"; + default: return "??"; + } +} + /* * find index of fd in a list of fdinfo of length nfds */ @@ -252,6 +291,10 @@ print_ident(uint64_t ident, int16_t filter, int width) printf("%#*llx ", width, ident); break; + case EVFILT_WORKLOOP: + printf("%#*llx ", width, ident); + break; + default: printf("%*llu ", width, ident); break; @@ -260,57 +303,166 @@ print_ident(uint64_t ident, int16_t filter, int width) } static void -print_kqfd(int kqfd, int width) +print_kqid(int state, uint64_t kqid) { - if (kqfd == -1) { - printf("%*s ", width, "wq"); + if (state & KQ_WORKQ) { + printf("%18s ", "wq"); + } else if (state & KQ_WORKLOOP) { + printf("%#18" PRIx64 " ", kqid); } else { - printf("%*u ", width, kqfd); + printf("fd %15" PRIi64 " ", kqid); } } #define PROCNAME_WIDTH 20 static void -print_kq_info(int pid, const char *procname, int kqfd, int state) +print_kq_info(int pid, const char *procname, uint64_t kqid, int state) { if (raw) { printf("%5u ", pid); - print_kqfd(kqfd, 5); + print_kqid(state, kqid); printf("%#10x ", state); } else { char tmpstr[PROCNAME_WIDTH+1]; strlcpy(tmpstr, shorten_procname(procname, PROCNAME_WIDTH), PROCNAME_WIDTH+1); printf("%-*s ", PROCNAME_WIDTH, tmpstr); printf("%5u ", pid); - print_kqfd(kqfd, 5); + print_kqid(state, kqid); printf(" %c%c%c ", (state & KQ_SLEEP) ? 'k' : '-', (state & KQ_SEL) ? 's' : '-', - (state & KQ_KEV32) ? '3' : - (state & KQ_KEV64) ? '6' : - (state & KQ_KEV_QOS) ? 'q' : '-' + (state & KQ_WORKQ) ? 'q' : + (state & KQ_WORKLOOP) ? 'l' : '-' ); } } +enum kqtype { + KQTYPE_FD, + KQTYPE_DYNAMIC +}; + +#define POLICY_TIMESHARE 1 +#define POLICY_RR 2 +#define POLICY_FIFO 4 + static int -process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo *fdlist, int nfds) +process_kqueue(int pid, const char *procname, enum kqtype type, uint64_t kqid, + struct proc_fdinfo *fdlist, int nfds) { int ret, i, nknotes; char tmpstr[256]; int maxknotes = 256; /* arbitrary starting point */ + int kq_state; + bool is_kev_64, is_kev_qos; int err = 0; bool overflow = false; + int fd; + bool dynkq_printed = false; /* * get the basic kqueue info */ struct kqueue_fdinfo kqfdinfo = {}; - ret = proc_pidfdinfo(pid, kqfd, PROC_PIDFDKQUEUEINFO, &kqfdinfo, sizeof(kqfdinfo)); - if (ret != sizeof(kqfdinfo) && kqfd != -1) { + struct kqueue_dyninfo kqinfo = {}; + switch (type) { + case KQTYPE_FD: + ret = proc_pidfdinfo(pid, (int)kqid, PROC_PIDFDKQUEUEINFO, &kqfdinfo, sizeof(kqfdinfo)); + fd = (int)kqid; + break; + case KQTYPE_DYNAMIC: + ret = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_INFO, kqid, &kqinfo, sizeof(kqinfo)); + break; + default: + os_crash("invalid kqueue type"); + } + + if (type == KQTYPE_FD && (int)kqid != -1) { + if (ret != sizeof(kqfdinfo)) { /* every proc has an implicit workq kqueue, dont warn if its unused */ - fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid, kqfd); + fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid, + fd); + } + } else if (type == KQTYPE_DYNAMIC) { + if (ret < sizeof(struct kqueue_info)) { + fprintf(stderr, "WARN: kqueue missing (pid %i, kq %#" PRIx64 ")\n", + pid, kqid); + } else { + kqfdinfo.kqueueinfo = kqinfo.kqdi_info; + } + if (verbose && ret >= sizeof(struct kqueue_dyninfo)) { + print_kq_info(pid, procname, kqid, kqinfo.kqdi_info.kq_state); + + if (kqinfo.kqdi_owner) { + printf("%#18llx ", kqinfo.kqdi_owner); // ident + printf("%-9s ", "WL owned"); // filter + } else if (kqinfo.kqdi_servicer) { + printf("%#18llx ", kqinfo.kqdi_servicer); // ident + printf("%-9s ", "WL"); // filter + } else { + printf("%18s ", "-"); // ident + printf("%-9s ", "WL"); // filter + } + dynkq_printed = true; + + if (raw) { + printf("%-10s ", " "); // fflags + printf("%-10s ", " "); // flags + printf("%-10s ", " "); // evst + } else { + const char *reqstate = "???"; + + switch (kqinfo.kqdi_request_state) { + case WORKQ_TR_STATE_IDLE: + reqstate = ""; + break; + case WORKQ_TR_STATE_NEW: + reqstate = "new"; + break; + case WORKQ_TR_STATE_QUEUED: + reqstate = "queued"; + break; + case WORKQ_TR_STATE_CANCELED: + reqstate = "canceled"; + break; + case WORKQ_TR_STATE_BINDING: + reqstate = "binding"; + break; + case WORKQ_TR_STATE_BOUND: + reqstate = "bound"; + break; + } + + printf("%-8s ", reqstate); // fdtype + char policy_type; + switch (kqinfo.kqdi_pol) { + case POLICY_RR: + policy_type = 'R'; + break; + case POLICY_FIFO: + policy_type = 'F'; + case POLICY_TIMESHARE: + case 0: + default: + policy_type = '-'; + break; + } + snprintf(tmpstr, 4, "%c%c%c", (kqinfo.kqdi_pri == 0)?'-':'P', policy_type, (kqinfo.kqdi_cpupercent == 0)?'-':'%'); + printf("%-7s ", tmpstr); // fflags + printf("%-15s ", " "); // flags + printf("%-15s ", " "); // evst + } + + if (!raw && kqinfo.kqdi_pri != 0) { + printf("%3d ", kqinfo.kqdi_pri); //qos + } else { + int qos = MAX(MAX(kqinfo.kqdi_events_qos, kqinfo.kqdi_async_qos), + kqinfo.kqdi_sync_waiter_qos); + printf("%3s ", thread_qos_name(qos)); //qos + } + printf("\n"); + } } /* @@ -322,25 +474,36 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo kqextinfo = malloc(sizeof(struct kevent_extinfo) * maxknotes); } if (!kqextinfo) { - perror("failed allocating memory"); err = errno; + perror("failed allocating memory"); goto out; } errno = 0; - nknotes = proc_pidfdinfo(pid, kqfd, PROC_PIDFDKQUEUE_EXTINFO, kqextinfo, - sizeof(struct kevent_extinfo) * maxknotes); + switch (type) { + case KQTYPE_FD: + nknotes = proc_pidfdinfo(pid, fd, PROC_PIDFDKQUEUE_EXTINFO, + kqextinfo, sizeof(struct kevent_extinfo) * maxknotes); + break; + case KQTYPE_DYNAMIC: + nknotes = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_EXTINFO, kqid, + kqextinfo, sizeof(struct kevent_extinfo) * maxknotes); + break; + default: + os_crash("invalid kqueue type"); + } + if (nknotes <= 0) { if (errno == 0) { /* proc_*() can't distinguish between error and empty list */ } else if (errno == EAGAIN) { goto again; } else if (errno == EBADF) { - fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid, kqfd); + fprintf(stderr, "WARN: FD table changed (pid %i, kq %#" PRIx64 ")\n", pid, kqid); goto out; } else { - perror("failed to get extended kqueue info"); err = errno; + perror("failed to get extended kqueue info"); goto out; } } @@ -356,10 +519,14 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo overflow = true; } + kq_state = kqfdinfo.kqueueinfo.kq_state; + is_kev_64 = (kq_state & PROC_KQUEUE_64); + is_kev_qos = (kq_state & PROC_KQUEUE_QOS); + if (nknotes == 0) { - if (!ignore_empty) { + if (!ignore_empty && !dynkq_printed) { /* for empty kqueues, print a single empty entry */ - print_kq_info(pid, procname, kqfd, kqfdinfo.kqueueinfo.kq_state); + print_kq_info(pid, procname, kqid, kq_state); printf("%18s \n", "-"); } goto out; @@ -368,7 +535,7 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo for (i = 0; i < nknotes; i++) { struct kevent_extinfo *info = &kqextinfo[i]; - print_kq_info(pid, procname, kqfd, kqfdinfo.kqueueinfo.kq_state); + print_kq_info(pid, procname, kqid, kqfdinfo.kqueueinfo.kq_state); print_ident(info->kqext_kev.ident, info->kqext_kev.filter, 18); printf("%-9s ", filt_name(info->kqext_kev.filter)); @@ -377,7 +544,6 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo printf("%#10x ", info->kqext_kev.flags); printf("%#10x ", info->kqext_status); } else { - /* for kevents attached to file descriptors, print the type of FD (file, socket, etc) */ const char *fdstr = ""; if (filter_is_fd_type(info->kqext_kev.filter)) { @@ -413,32 +579,39 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo ); unsigned st = info->kqext_status; - printf("%c%c%c%c %c%c%c%c%c", - (st & KN_ACTIVE) ? 'a' : '-', - (st & KN_QUEUED) ? 'q' : '-', - (st & KN_DISABLED) ? 'd' : '-', - (st & KN_STAYQUEUED) ? 's' : '-', + printf("%c%c%c%c%c %c%c%c%c %c%c%c ", + (st & KN_ACTIVE) ? 'a' : '-', + (st & KN_QUEUED) ? 'q' : '-', + (st & KN_DISABLED) ? 'd' : '-', + (st & KN_SUPPRESSED) ? 'p' : '-', + (st & KN_STAYACTIVE) ? 's' : '-', - (st & KN_DROPPING) ? 'o' : '-', - (st & KN_USEWAIT) ? 'u' : '-', - (st & KN_ATTACHING) ? 'c' : '-', - (st & KN_DEFERDROP) ? 'f' : '-', - (st & KN_TOUCH) ? 't' : '-' + (st & KN_DROPPING) ? 'd' : '-', + (st & KN_LOCKED) ? 'l' : '-', + (st & KN_POSTING) ? 'P' : '-', + (st & KN_MERGE_QOS) ? 'm' : '-', + + (st & KN_DEFERDELETE) ? 'D' : '-', + (st & KN_REQVANISH) ? 'v' : '-', + (st & KN_VANISHED) ? 'n' : '-' ); } + printf("%3s ", thread_qos_name(info->kqext_kev.qos)); + printf("%#18llx ", (unsigned long long)info->kqext_kev.data); if (verbose) { printf("%#18llx ", (unsigned long long)info->kqext_kev.udata); - if (kqfdinfo.kqueueinfo.kq_state & (KQ_KEV64|KQ_KEV_QOS)) { + if (is_kev_qos || is_kev_64) { printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[0]); printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[1]); - } - if (kqfdinfo.kqueueinfo.kq_state & KQ_KEV_QOS) { - printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[2]); - printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[3]); - printf("%#10lx ", (unsigned long)info->kqext_kev.xflags); + + if (is_kev_qos) { + printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[2]); + printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[3]); + printf("%#10lx ", (unsigned long)info->kqext_kev.xflags); + } } } @@ -446,8 +619,8 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo } if (overflow) { - printf(" ***** output truncated (>=%i knotes on kq %i, proc %i) *****\n", - nknotes, kqfd, pid); + printf(" ***** output truncated (>=%i knotes on kq %" PRIu64 ", proc %i) *****\n", + nknotes, kqid, pid); } out: @@ -459,10 +632,46 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo return err; } +static int +pid_kqids(pid_t pid, kqueue_id_t **kqids_out) +{ + static int kqids_len = 256; + static kqueue_id_t *kqids = NULL; + static uint32_t kqids_size; + + int nkqids; + +retry: + if (os_mul_overflow(sizeof(kqueue_id_t), kqids_len, &kqids_size)) { + assert(kqids_len > PROC_PIDDYNKQUEUES_MAX); + kqids_len = PROC_PIDDYNKQUEUES_MAX; + goto retry; + } + if (!kqids) { + kqids = malloc(kqids_size); + os_assert(kqids != NULL); + } + + nkqids = proc_list_dynkqueueids(pid, kqids, kqids_size); + if (nkqids > kqids_len && kqids_len < PROC_PIDDYNKQUEUES_MAX) { + kqids_len *= 2; + if (kqids_len > PROC_PIDDYNKQUEUES_MAX) { + kqids_len = PROC_PIDDYNKQUEUES_MAX; + } + free(kqids); + kqids = NULL; + goto retry; + } + + *kqids_out = kqids; + return MIN(nkqids, kqids_len); +} + static int process_pid(pid_t pid) { - int i, nfds; + int i, nfds, nkqids; + kqueue_id_t *kqids; int ret = 0; int maxfds = 256; /* arbitrary starting point */ struct proc_fdinfo *fdlist = NULL; @@ -473,21 +682,21 @@ process_pid(pid_t pid) fdlist = malloc(sizeof(struct proc_fdinfo) * maxfds); } if (!fdlist) { - perror("failed to allocate"); ret = errno; + perror("failed to allocate"); goto out; } nfds = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdlist, sizeof(struct proc_fdinfo) * maxfds); if (nfds <= 0) { + ret = errno; fprintf(stderr, "%s: failed enumerating file descriptors of process %i: %s", - self, pid, strerror(errno)); - if (errno == EPERM && geteuid() != 0) { + self, pid, strerror(ret)); + if (ret == EPERM && geteuid() != 0) { fprintf(stderr, " (are you root?)"); } fprintf(stderr, "\n"); - ret = errno; goto out; } @@ -514,20 +723,35 @@ process_pid(pid_t pid) } /* handle the special workq kq */ - ret = process_kqueue_on_fd(pid, procname, -1, fdlist, nfds); + ret = process_kqueue(pid, procname, KQTYPE_FD, -1, fdlist, nfds); if (ret) { goto out; } for (i = 0; i < nfds; i++) { if (fdlist[i].proc_fdtype == PROX_FDTYPE_KQUEUE) { - ret = process_kqueue_on_fd(pid, procname, fdlist[i].proc_fd, fdlist, nfds); + ret = process_kqueue(pid, procname, KQTYPE_FD, + (uint64_t)fdlist[i].proc_fd, fdlist, nfds); if (ret) { goto out; } } } + nkqids = pid_kqids(pid, &kqids); + + for (i = 0; i < nkqids; i++) { + ret = process_kqueue(pid, procname, KQTYPE_DYNAMIC, kqids[i], fdlist, nfds); + if (ret) { + goto out; + } + } + + if (nkqids >= PROC_PIDDYNKQUEUES_MAX) { + printf(" ***** output truncated (>=%i dynamic kqueues in proc %i) *****\n", + nkqids, pid); + } + out: if (fdlist) { free(fdlist); @@ -562,8 +786,8 @@ process_all_pids(void) } else if (errno == EAGAIN) { goto again; } else { - perror("failed enumerating pids"); ret = errno; + perror("failed enumerating pids"); goto out; } } @@ -580,7 +804,8 @@ process_all_pids(void) /* listpids gives us pid 0 for some reason */ if (pids[i]) { ret = process_pid(pids[i]); - if (ret) { + /* ignore races with processes exiting */ + if (ret && ret != ESRCH) { goto out; } } @@ -598,24 +823,33 @@ out: static void cheatsheet(void) { + const char *bold = "\033[1m"; + const char *reset = "\033[0m"; + if (!isatty(STDERR_FILENO)) { + bold = reset = ""; + } + fprintf(stderr, "\nFilter-independent flags:\n\n\ -\033[1mcommand pid kq kqst ident filter fdtype fflags flags evst\033[0m\n\ -\033[1m-------------------- ----- ----- ---- ------------------ --------- -------- ------- --------------- ----------\033[0m\n\ - ┌ EV_UDATA_SPECIFIC\n\ - EV_DISPATCH ┐ │┌ EV_FLAG0 (EV_POLL)\n\ - EV_CLEAR ┐│ ││┌ EV_FLAG1 (EV_OOBAND)\n\ - EV_ONESHOT ┐││ │││┌ EV_EOF\n\ - EV_RECEIPT ┐│││ ││││┌ EV_ERROR\n\ - ││││ │││││\n\ -\033[1mlaunchd 1 4 ks- netbiosd 250 PROC ------- andx r1cs upboe aqds oucft\033[0m \n\ - │ │││ ││││ ││││ │││││\n\ - kqueue file descriptor ┘ │││ EV_ADD ┘│││ KN_ACTIVE ┘│││ ││││└ KN_TOUCH\n\ - KQ_SLEEP ┘││ EV_ENABLE ┘││ KN_QUEUED ┘││ │││└ KN_DEFERDROP\n\ - KQ_SEL ┘│ EV_DISABLE ┘│ KN_DISABLED ┘│ ││└ KN_ATTACHING\n\ - KEV32 (3) ┤ EV_DELETE ┘ KN_STAYQUEUED ┘ │└ KN_USEWAIT\n\ - KEV64 (6) ┤ └ KN_DROPPING\n\ - KEV_QOS (q) ┘\n\ - \n"); +%s\ +command pid kq kqst knid filter fdtype fflags flags evst qos%s\n%s\ +-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- -------------- ---%s\n\ + ┌ EV_UDATA_SPECIFIC\n\ + EV_DISPATCH ┐ │┌ EV_FLAG0 (EV_POLL)\n\ + EV_CLEAR ┐│ ││┌ EV_FLAG1 (EV_OOBAND)\n\ + EV_ONESHOT ┐││ │││┌ EV_EOF\n\ + EV_RECEIPT ┐│││ ││││┌ EV_ERROR\n\ + ││││ │││││\n%s\ +launchd 1 4 ks- netbiosd 250 PROC ------- andx r1cs upboe aqdps dlPm Dvn IN%s\n\ + │ │││ ││││ │││││ ││││ │││\n\ + kqueue file descriptor/dynamic ID ┘ │││ EV_ADD ┘│││ KN_ACTIVE ┘││││ ││││ ││└ KN_VANISHED\n\ + KQ_SLEEP ┘││ EV_ENABLE ┘││ KN_QUEUED ┘│││ ││││ │└ KN_REQVANISH\n\ + KQ_SEL ┘│ EV_DISABLE ┘│ KN_DISABLED ┘││ ││││ └ KN_DEFERDELETE\n\ + KQ_WORKQ (q) ┤ EV_DELETE ┘ KN_SUPPRESSED ┘│ ││││\n\ + KQ_WORKLOOP (l) ┘ KN_STAYACTIVE ┘ ││││\n\ + ││││\n\ + KN_DROPPING ┘││└ KN_MERGE_QOS\n\ + KN_LOCKED ┘└ KN_POSTING\n\ + \n", bold, reset, bold, reset, bold, reset); } static void @@ -628,20 +862,21 @@ static void print_header(void) { if (raw) { - printf(" pid kq kqst ident filter fflags flags evst data"); - if (verbose) { - printf(" udata ext0 ext1 ext2 ext3 xflags"); - } - printf("\n"); - printf("----- ----- ---------- ------------------ --------- ---------- ---------- ---------- ------------------"); - + printf(" pid kq kqst knid filter fflags flags evst qos data"); } else { - printf("command pid kq kqst ident filter fdtype fflags flags evst data"); - if (verbose) { - printf(" udata ext0 ext1 ext2 ext3 xflags"); - } - printf("\n"); - printf("-------------------- ----- ----- ---- ------------------ --------- -------- ------- --------------- ---------- -----------------"); + printf("command pid kq kqst knid filter fdtype fflags flags evst qos data"); + } + + if (verbose) { + printf(" udata ext0 ext1 ext2 ext3 xflags"); + } + + printf("\n"); + + if (raw) { + printf("----- ------------------ ---------- ------------------ --------- ---------- ---------- ---------- --- ------------------"); + } else { + printf("-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- -------------- --- ------------------"); } if (verbose) { diff --git a/lsmp.tproj/common.h b/lsmp.tproj/common.h index ea07dc2..35014c1 100644 --- a/lsmp.tproj/common.h +++ b/lsmp.tproj/common.h @@ -25,6 +25,7 @@ #define system_cmds_common_h #include +#include "json.h" #define PROC_NAME_LEN 100 #define BUFSTR_LEN 30 @@ -37,6 +38,7 @@ struct prog_configs { boolean_t verbose; int voucher_detail_length; pid_t pid; /* if user focusing only one pid */ + JSON_t json_output; }; extern struct prog_configs lsmp_config; @@ -136,30 +138,47 @@ typedef struct my_per_task_info { #define IKOT_UNKNOWN 39 /* magic catchall */ #define IKOT_MAX_TYPE (IKOT_UNKNOWN+1) /* # of IKOT_ types */ + +#define PORT_FLAG_TO_INDEX(flag) ( __builtin_ctz(flag) ) /* count trailing zeros */ +#define INDEX_TO_PORT_FLAG(idx) ( 1 << idx ) +typedef struct port_status_flag_info { + natural_t flag; /* MACH_PORT_STATUS_FLAG_* */ + const char *compact_name; /* Single character name for compact representation */ + const char *name; /* human readable long name */ +} port_status_flag_info_t; + +/* + * list of names for possible MACH_PORT_STATUS_FLAG_* + * indexed by PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_*) + */ +extern const port_status_flag_info_t port_status_flags[]; + +#define _SHOW_PORT_STATUS_FLAG(flags, flag) \ + (flags & flag) ? port_status_flags[PORT_FLAG_TO_INDEX(flag)].compact_name : "-" #define SHOW_PORT_STATUS_FLAGS(flags) \ - (flags & MACH_PORT_STATUS_FLAG_TEMPOWNER) ?"T":"-", \ - (flags & MACH_PORT_STATUS_FLAG_GUARDED) ?"G":"-", \ - (flags & MACH_PORT_STATUS_FLAG_STRICT_GUARD) ?"S":"-", \ - (flags & MACH_PORT_STATUS_FLAG_IMP_DONATION) ?"I":"-", \ - (flags & MACH_PORT_STATUS_FLAG_REVIVE) ?"R":"-", \ - (flags & MACH_PORT_STATUS_FLAG_TASKPTR) ?"P":"-" + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_TEMPOWNER), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_GUARDED), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_STRICT_GUARD), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_IMP_DONATION), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_REVIVE), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_TASKPTR) -uint32_t show_recipe_detail(mach_voucher_attr_recipe_t recipe, char * voucher_outstr, uint32_t maxlen); -char *copy_voucher_detail(mach_port_t task, mach_port_name_t voucher); +uint32_t show_recipe_detail(mach_voucher_attr_recipe_t recipe, char * voucher_outstr, uint32_t maxlen, JSON_t json); +char *copy_voucher_detail(mach_port_t task, mach_port_name_t voucher, JSON_t json); /* mach port related functions */ const char * kobject_name(natural_t kotype); void get_receive_port_context(task_t taskp, mach_port_name_t portname, mach_port_context_t *context); int get_recieve_port_status(task_t taskp, mach_port_name_t portname, mach_port_info_ext_t *info); -void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos); +void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, JSON_t json); /* task and thread related helper functions */ kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_t target_task); my_per_task_info_t * allocate_taskinfo_memory(uint32_t taskCount); void deallocate_taskinfo_memory(my_per_task_info_t *data); -kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo); -kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo); +kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo, JSON_t json); +kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo, JSON_t json); my_per_task_info_t * get_taskinfo_by_kobject(natural_t kobj); void get_exc_behavior_string(exception_behavior_t b, char *out_string, size_t len); @@ -168,6 +187,6 @@ kern_return_t get_taskinfo_of_receiver_by_send_right(ipc_info_name_t *sendright, kern_return_t get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name, ipc_info_name_t *out_sendright); /* basic util functions */ -uint32_t print_hex_data(char *outstr, size_t maxlen, char *prefix, char *desc, void *addr, int len); +uint32_t print_hex_data(char *outstr, uint32_t maxlen, char *prefix, char *desc, void *addr, int len); #endif diff --git a/lsmp.tproj/json.h b/lsmp.tproj/json.h new file mode 100644 index 0000000..2a87a8b --- /dev/null +++ b/lsmp.tproj/json.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * Provides a stream-based API for generating JSON output + * + * Handles tedious tasks like worrying about comma placement (and avoiding trailing commas). + * Assumes strings are already escaped (if necessary) and does no error checking (thus it + * may produce invalid JSON when used improperly). + * + * As a convenience, when the provided `json` stream is NULL (i.e. it was never initialized + * by `JSON_OPEN`) these APIs will do nothing. + * + * Example usage: + * + * JSON_t json = JSON_OPEN("/path/to/output.json") + * JSON_OBJECT_BEGIN(json); // root object + * + * JSON_OBJECT_SET(json, version, %.1f, 1.0); + * JSON_OBJECT_SET_BOOL(json, has_fruit, 1); + * + * // Note the required quotes for strings (formatted or not) + * char *mystr = "hello"; + * JSON_OBJECT_SET(json, formatted_string, "%s", mystr); + * JSON_OBJECT_SET(json, literal_string, "my literal string"); + * + * JSON_KEY(json, fruit_array); + * JSON_ARRAY_BEGIN(json); // fruit_array + * JSON_ARRAY_APPEND(json, "my literal string"); + * JSON_ARRAY_APPEND(json, "<0x%08llx>", 0xface); + * JSON_ARRAY_APPEND(json, %d, 3); + * JSON_ARRAY_END(json); // fruit_array + * + * JSON_OBJECT_END(json); // root object + * JSON_CLOSE(json); + */ + +#ifndef _JSON_H_ +#define _JSON_H_ + +#include +#include +#include + +#define _JSON_IF(json, code) \ + if (json != NULL) { \ + code; \ + } +#define _JSON_COMMA(json) \ + if (json->require_comma) { \ + fprintf(json->stream, ","); \ + } + +struct _JSON { + FILE* stream; + bool require_comma; +}; +typedef struct _JSON * JSON_t; + +#pragma mark Open/Close +/* Return a new JSON_t stream */ +static inline JSON_t JSON_OPEN(const char *path) { + JSON_t p = malloc(sizeof(struct _JSON)); + p->stream = fopen(path, "w+"); + p->require_comma = false; + return p; +} + +/* Close an existing JSON stream, removing trailing commas */ +#define JSON_CLOSE(json) _JSON_IF(json, fclose(json->stream); free(json)) + +#pragma mark Keys/Values +/* Output the `key` half of a key/value pair */ +#define JSON_KEY(json, key) _JSON_IF(json, _JSON_COMMA(json); fprintf(json->stream, "\"" #key "\":"); json->require_comma = false) +/* Output the `value` half of a key/value pair */ +#define JSON_VALUE(json, format, ...) _JSON_IF(json, fprintf(json->stream, #format, ##__VA_ARGS__); json->require_comma = true) + +#define _JSON_BEGIN(json, character) _JSON_COMMA(json); fprintf(json->stream, #character); json->require_comma = false; +#define _JSON_END(json, character) fprintf(json->stream, #character); json->require_comma = true; +#define _JSON_BOOL(val) ( val ? "true" : "false" ) + +#pragma mark Objects +/* Start a new JSON object */ +#define JSON_OBJECT_BEGIN(json) _JSON_IF(json, _JSON_BEGIN(json, {)) +/* Set a value in the current JSON object */ +#define JSON_OBJECT_SET(json, key, format, ...) _JSON_IF(json, JSON_KEY(json, key); JSON_VALUE(json, format, ##__VA_ARGS__)) +/* Set a boolean in the current JSON object */ +#define JSON_OBJECT_SET_BOOL(json, key, value) JSON_OBJECT_SET(json, key, %s, _JSON_BOOL(value)) +/* End the current JSON object */ +#define JSON_OBJECT_END(json) _JSON_IF(json, _JSON_END(json, })) + +#pragma mark Arrays +/* Start a new JSON array */ +#define JSON_ARRAY_BEGIN(json) _JSON_IF(json, _JSON_BEGIN(json, [)) +/* Append a value to the current JSON array */ +#define JSON_ARRAY_APPEND(json, format, ...) _JSON_IF(json, _JSON_COMMA(json); JSON_VALUE(json, format, ##__VA_ARGS__)) +/* End the current JSON array */ +#define JSON_ARRAY_END(json) _JSON_IF(json, _JSON_END(json, ])) + +#endif /* _JSON_H_ */ diff --git a/lsmp.tproj/lsmp.1 b/lsmp.tproj/lsmp.1 index 1b4d28d..d4c70e9 100644 --- a/lsmp.tproj/lsmp.1 +++ b/lsmp.tproj/lsmp.1 @@ -21,6 +21,10 @@ Show information in detail for Kernel object based ports. Including thread ports .Nm lsmp .Ar -a Show mach port usage for all tasks in the system +.Pp +.Nm lsmp +.Ar -j +Save output as JSON to . .Sh DESCRIPTION The .Nm lsmp diff --git a/lsmp.tproj/lsmp.c b/lsmp.tproj/lsmp.c index c2e3330..71a7c68 100644 --- a/lsmp.tproj/lsmp.c +++ b/lsmp.tproj/lsmp.c @@ -27,9 +27,11 @@ #include #include #include +#include #include "common.h" +#include "json.h" -#if TARGET_OS_EMBEDDED +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) #define TASK_FOR_PID_USAGE_MESG "\nPlease check your boot-args to ensure you have access to task_for_pid()." #else #define TASK_FOR_PID_USAGE_MESG "" @@ -40,21 +42,33 @@ struct prog_configs lsmp_config = { .show_voucher_details = FALSE, .verbose = FALSE, .pid = 0, + .json_output = NULL, }; -my_per_task_info_t *psettaskinfo; -mach_msg_type_number_t taskCount; - static void print_usage(char *progname) { fprintf(stderr, "Usage: %s -p [-a|-v|-h] \n", "lsmp"); fprintf(stderr, "Lists information about mach ports. Please see man page for description of each column.\n"); fprintf(stderr, "\t-p : print all mach ports for process id . \n"); fprintf(stderr, "\t-a : print all mach ports for all processeses. \n"); fprintf(stderr, "\t-v : print verbose details for kernel objects.\n"); + fprintf(stderr, "\t-j : save output as JSON to .\n"); fprintf(stderr, "\t-h : print this help.\n"); exit(1); } +static void print_task_info(my_per_task_info_t *taskinfo, mach_msg_type_number_t taskCount, my_per_task_info_t *psettaskinfo, boolean_t verbose, JSON_t json) { + printf("Process (%d) : %s\n", taskinfo->pid, taskinfo->processName); + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, pid, %d, taskinfo->pid); + JSON_OBJECT_SET(json, name, "%s", taskinfo->processName); + show_task_mach_ports(taskinfo, taskCount, psettaskinfo, json); + print_task_exception_info(taskinfo, json); + if (verbose) { + printf("\n"); + print_task_threads_special_ports(taskinfo, json); + } + JSON_OBJECT_END(json); +} int main(int argc, char *argv[]) { kern_return_t ret; @@ -64,8 +78,10 @@ int main(int argc, char *argv[]) { char *progname = "lsmp"; int i, option = 0; lsmp_config.voucher_detail_length = 128; /* default values for config */ + my_per_task_info_t *psettaskinfo; + mach_msg_type_number_t taskCount; - while((option = getopt(argc, argv, "hvalp:")) != -1) { + while((option = getopt(argc, argv, "hvalp:j:")) != -1) { switch(option) { case 'a': /* user asked for info on all processes */ @@ -91,6 +107,14 @@ int main(int argc, char *argv[]) { } break; + case 'j': + lsmp_config.json_output = JSON_OPEN(optarg); + if (lsmp_config.json_output == NULL) { + fprintf(stderr, "Unable to open \"%s\": %s\n", optarg, strerror(errno)); + exit(1); + } + break; + default: fprintf(stderr, "Unknown argument. \n"); /* Fall through to 'h' */ @@ -178,43 +202,38 @@ int main(int argc, char *argv[]) { if (psettaskinfo[i].pid == lsmp_config.pid) taskinfo = &psettaskinfo[i]; - - ret = KERN_SUCCESS; } + JSON_OBJECT_BEGIN(lsmp_config.json_output); + JSON_OBJECT_SET(lsmp_config.json_output, version, "%.1f", 1.0); + JSON_KEY(lsmp_config.json_output, processes); + JSON_ARRAY_BEGIN(lsmp_config.json_output); + if (lsmp_config.show_all_tasks == FALSE) { if (taskinfo == NULL) { fprintf(stderr, "Failed to find task ipc information for pid %d\n", lsmp_config.pid); exit(1); } - printf("Process (%d) : %s\n", taskinfo->pid, taskinfo->processName); - show_task_mach_ports(taskinfo, taskCount, psettaskinfo); - print_task_exception_info(taskinfo); - printf("\n"); - print_task_threads_special_ports(taskinfo); - + print_task_info(taskinfo, taskCount, psettaskinfo, TRUE, lsmp_config.json_output); } else { for (i=0; i < taskCount; i++) { if (psettaskinfo[i].valid != TRUE) continue; - printf("Process (%d) : %s\n", psettaskinfo[i].pid, psettaskinfo[i].processName); - show_task_mach_ports(&psettaskinfo[i], taskCount, psettaskinfo); - print_task_exception_info(&psettaskinfo[i]); - - if (lsmp_config.verbose) { - printf("\n"); - print_task_threads_special_ports(&psettaskinfo[i]); - } - + print_task_info(&psettaskinfo[i], taskCount, psettaskinfo, lsmp_config.verbose, lsmp_config.json_output); printf("\n\n"); } } + JSON_ARRAY_END(lsmp_config.json_output); + JSON_OBJECT_END(lsmp_config.json_output); + if (taskCount > 1) { vm_deallocate(mach_task_self(), (vm_address_t)tasks, (vm_size_t)taskCount * sizeof(mach_port_t)); } deallocate_taskinfo_memory(psettaskinfo); + JSON_CLOSE(lsmp_config.json_output); + return(0); } diff --git a/lsmp.tproj/port_details.c b/lsmp.tproj/port_details.c index fec26d7..619bf02 100644 --- a/lsmp.tproj/port_details.c +++ b/lsmp.tproj/port_details.c @@ -28,6 +28,7 @@ #include #include #include "common.h" +#include "json.h" const char * kobject_name(natural_t kotype) { @@ -76,43 +77,108 @@ const char * kobject_name(natural_t kotype) } } +const port_status_flag_info_t port_status_flags[] = { + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_TEMPOWNER)] = { + .flag = MACH_PORT_STATUS_FLAG_TEMPOWNER, + .compact_name = "T", + .name = "tempowner", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_GUARDED)] = { + .flag = MACH_PORT_STATUS_FLAG_GUARDED, + .compact_name = "G", + .name = "guarded", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_STRICT_GUARD)] = { + .flag = MACH_PORT_STATUS_FLAG_STRICT_GUARD, + .compact_name = "S", + .name = "strict guard", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_IMP_DONATION)] = { + .flag = MACH_PORT_STATUS_FLAG_IMP_DONATION, + .compact_name = "I", + .name = "imp donation", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_REVIVE)] = { + .flag = MACH_PORT_STATUS_FLAG_REVIVE, + .compact_name = "R", + .name = "revive", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_TASKPTR)] = { + .flag = MACH_PORT_STATUS_FLAG_TASKPTR, + .compact_name = "P", + .name = "taskptr", + }, + {0}, +}; + #define VOUCHER_DETAIL_PREFIX " " static const unsigned int voucher_contents_size = 8192; static uint8_t voucher_contents[voucher_contents_size]; +typedef struct { + int total; + int sendcount; + int receivecount; + int sendoncecount; + int portsetcount; + int deadcount; + int dncount; + int vouchercount; +} task_table_entry_counts; + +typedef task_table_entry_counts * task_table_entry_counts_t; + +static void show_task_table_entry(ipc_info_name_t *entry, my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, task_table_entry_counts_t counts, JSON_t json); static uint32_t safesize (int len){ return (len > 0) ? len : 0; } -uint32_t show_recipe_detail(mach_voucher_attr_recipe_t recipe, char *voucher_outstr, uint32_t maxlen) { +uint32_t show_recipe_detail(mach_voucher_attr_recipe_t recipe, char *voucher_outstr, uint32_t maxlen, JSON_t json) { + JSON_OBJECT_BEGIN(json); + uint32_t len = 0; - len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "Key: %u, ", recipe->key)); - len += safesize(snprintf(&voucher_outstr[len], maxlen - len, "Command: %u, ", recipe->command)); - len += safesize(snprintf(&voucher_outstr[len], maxlen - len, "Previous voucher: 0x%x, ", recipe->previous_voucher)); - len += safesize(snprintf(&voucher_outstr[len], maxlen - len, "Content size: %u\n", recipe->content_size)); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "Key: %u, ", recipe->key); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Command: %u, ", recipe->command); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Previous voucher: 0x%x, ", recipe->previous_voucher); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Content size: %u\n", recipe->content_size); + + JSON_OBJECT_SET(json, key, %u, recipe->key); + JSON_OBJECT_SET(json, command, %u, recipe->command); + JSON_OBJECT_SET(json, previous_voucher, "0x%x", recipe->previous_voucher); + JSON_OBJECT_SET(json, content_size, %u, recipe->content_size); switch (recipe->key) { case MACH_VOUCHER_ATTR_KEY_ATM: - len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "ATM ID: %llu\n", *(uint64_t *)(uintptr_t)recipe->content)); + JSON_OBJECT_SET(json, ATM_ID, %llu, *(uint64_t *)(uintptr_t)recipe->content); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "ATM ID: %llu", *(uint64_t *)(uintptr_t)recipe->content); break; case MACH_VOUCHER_ATTR_KEY_IMPORTANCE: - len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "IMPORTANCE INFO: %s\n", (char *)recipe->content)); + // content may not be valid JSON, exclude + // JSON_OBJECT_SET(json, importance_info, "%s", (char *)recipe->content); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "IMPORTANCE INFO: %s", (char *)recipe->content); break; case MACH_VOUCHER_ATTR_KEY_BANK: - len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "RESOURCE ACCOUNTING INFO: %s\n", (char *)recipe->content)); + // content may not be valid JSON, exclude + // JSON_OBJECT_SET(json, resource_accounting_info, "%s", (char *)recipe->content); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "RESOURCE ACCOUNTING INFO: %s", (char *)recipe->content); break; default: - len += print_hex_data(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX, "Recipe Contents", (void *)recipe->content, MIN(recipe->content_size, lsmp_config.voucher_detail_length)); + len += print_hex_data(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX, "Recipe Contents", (void *)recipe->content, MIN(recipe->content_size, lsmp_config.voucher_detail_length)); break; } + if (len + 1 < maxlen && voucher_outstr[len - 1] != '\n') { + voucher_outstr[len++] = '\n'; + voucher_outstr[len] = '\0'; + } + JSON_OBJECT_END(json); // recipe return len; } -char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher) { +char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher, JSON_t json) { unsigned int recipe_size = voucher_contents_size; kern_return_t kr = KERN_SUCCESS; bzero((void *)&voucher_contents[0], sizeof(voucher_contents)); @@ -132,27 +198,27 @@ char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher) { (mach_voucher_attr_raw_recipe_array_t)&voucher_contents[0], &recipe_size); if (kr != KERN_SUCCESS && kr != KERN_NOT_SUPPORTED) { - plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x Failed to get contents %s\n", v_kobject, mach_error_string(kr))); + plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x Failed to get contents %s\n", v_kobject, mach_error_string(kr)); return voucher_outstr; } if (recipe_size == 0) { - plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x has no contents\n", v_kobject)); + plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x has no contents\n", v_kobject); return voucher_outstr; } - plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x\n", v_kobject)); + plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x\n", v_kobject); unsigned int used_size = 0; mach_voucher_attr_recipe_t recipe = NULL; while (recipe_size > used_size) { recipe = (mach_voucher_attr_recipe_t)&voucher_contents[used_size]; if (recipe->key) { - plen += show_recipe_detail(recipe, &voucher_outstr[plen], detail_maxlen - plen); + plen += show_recipe_detail(recipe, &voucher_outstr[plen], safesize(detail_maxlen - plen), json); } used_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size; } } else { - plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Invalid voucher: 0x%x\n", voucher)); + plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Invalid voucher: 0x%x\n", voucher); } return voucher_outstr; @@ -196,267 +262,445 @@ int get_recieve_port_status(task_t taskp, mach_port_name_t portname, mach_port_i return 0; } -void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos) +void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, JSON_t json) { - int i, emptycount = 0, portsetcount = 0, sendcount = 0, receivecount = 0, sendoncecount = 0, deadcount = 0, dncount = 0, vouchercount = 0, pid; - kern_return_t ret; - pid_for_task(taskinfo->task, &pid); + int i; + task_table_entry_counts counts = {0}; + + counts.total = taskinfo->tableCount + taskinfo->treeCount; + + JSON_KEY(json, ports) + JSON_ARRAY_BEGIN(json); printf(" name ipc-object rights flags boost reqs recv send sonce oref qlimit msgcount context identifier type\n"); printf("--------- ---------- ---------- -------- ----- ---- ----- ----- ----- ---- ------ -------- ------------------ ----------- ------------\n"); - for (i = 0; i < taskinfo->tableCount; i++) { - int j, k; - boolean_t send = FALSE; - boolean_t sendonce = FALSE; - boolean_t dnreq = FALSE; - int sendrights = 0; - unsigned int kotype = 0; - vm_offset_t kobject = (vm_offset_t)0; + for (i = 0; i < taskinfo->tableCount; i++) { + show_task_table_entry(&taskinfo->table[i], taskinfo, taskCount, allTaskInfos, &counts, json); + } - /* skip empty slots in the table */ - if ((taskinfo->table[i].iin_type & MACH_PORT_TYPE_ALL_RIGHTS) == 0) { - emptycount++; - continue; + JSON_ARRAY_END(json); // ports + JSON_OBJECT_SET(json, total, %d, counts.total); + JSON_OBJECT_SET(json, send_rights, %d, counts.sendcount); + JSON_OBJECT_SET(json, receive_rights, %d, counts.receivecount); + JSON_OBJECT_SET(json, send_once_rights, %d, counts.sendoncecount); + JSON_OBJECT_SET(json, port_sets, %d, counts.portsetcount); + JSON_OBJECT_SET(json, dead_names, %d, counts.deadcount); + JSON_OBJECT_SET(json, dead_name requests, %d, counts.dncount); + JSON_OBJECT_SET(json, vouchers, %d, counts.vouchercount); + + printf("\n"); + printf("total = %d\n", counts.total); + printf("SEND = %d\n", counts.sendcount); + printf("RECEIVE = %d\n", counts.receivecount); + printf("SEND_ONCE = %d\n", counts.sendoncecount); + printf("PORT_SET = %d\n", counts.portsetcount); + printf("DEAD_NAME = %d\n", counts.deadcount); + printf("DNREQUEST = %d\n", counts.dncount); + printf("VOUCHERS = %d\n", counts.vouchercount); +} + +static void show_task_table_entry(ipc_info_name_t *entry, my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, task_table_entry_counts_t counts, JSON_t json) { + int j, k, port_status_flag_idx; + kern_return_t ret; + boolean_t send = FALSE; + boolean_t dnreq = FALSE; + int sendrights = 0; + unsigned int kotype = 0; + vm_offset_t kobject = (vm_offset_t)0; + + /* skip empty slots in the table */ + if ((entry->iin_type & MACH_PORT_TYPE_ALL_RIGHTS) == 0) { + counts->total--; + return; + } + + if (entry->iin_type == MACH_PORT_TYPE_PORT_SET) { + mach_port_name_array_t members; + mach_msg_type_number_t membersCnt; + + ret = mach_port_get_set_status(taskinfo->task, + entry->iin_name, + &members, &membersCnt); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "mach_port_get_set_status(0x%08x) failed: %s\n", + entry->iin_name, + mach_error_string(ret)); + return; } - if (taskinfo->table[i].iin_type == MACH_PORT_TYPE_PORT_SET) { - mach_port_name_array_t members; - mach_msg_type_number_t membersCnt; + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, type, "port set"); + JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object); - ret = mach_port_get_set_status(taskinfo->task, - taskinfo->table[i].iin_name, - &members, &membersCnt); - if (ret != KERN_SUCCESS) { - fprintf(stderr, "mach_port_get_set_status(0x%08x) failed: %s\n", - taskinfo->table[i].iin_name, - mach_error_string(ret)); - continue; - } - printf("0x%08x 0x%08x port-set -------- --- 1 %d members\n", - taskinfo->table[i].iin_name, - taskinfo->table[i].iin_object, - membersCnt); - /* get some info for each portset member */ - for (j = 0; j < membersCnt; j++) { - for (k = 0; k < taskinfo->tableCount; k++) { - if (taskinfo->table[k].iin_name == members[j]) { - mach_port_info_ext_t info; - mach_port_status_t port_status; - mach_port_context_t port_context = (mach_port_context_t)0; - if (0 != get_recieve_port_status(taskinfo->task, taskinfo->table[k].iin_name, &info)) { - bzero((void *)&info, sizeof(info)); + JSON_KEY(json, members); + JSON_ARRAY_BEGIN(json); + + printf("0x%08x 0x%08x port-set -------- --- 1 %d members\n", + entry->iin_name, + entry->iin_object, + membersCnt); + /* get some info for each portset member */ + for (j = 0; j < membersCnt; j++) { + for (k = 0; k < taskinfo->tableCount; k++) { + if (taskinfo->table[k].iin_name == members[j]) { + mach_port_info_ext_t info; + mach_port_status_t port_status; + mach_port_context_t port_context = (mach_port_context_t)0; + if (0 != get_recieve_port_status(taskinfo->task, taskinfo->table[k].iin_name, &info)) { + bzero((void *)&info, sizeof(info)); + } + port_status = info.mpie_status; + get_receive_port_context(taskinfo->task, taskinfo->table[k].iin_name, &port_context); + + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", taskinfo->table[k].iin_object); + + JSON_KEY(json, rights); + JSON_ARRAY_BEGIN(json); + JSON_ARRAY_APPEND(json, "recv"); + if (taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) { + JSON_ARRAY_APPEND(json, "send"); + } + JSON_ARRAY_END(json); // rights + + JSON_KEY(json, port_status_flags); + JSON_ARRAY_BEGIN(json); + port_status_flag_idx = 0; + while (0 != port_status_flags[port_status_flag_idx++].flag) { + if (port_status.mps_flags & INDEX_TO_PORT_FLAG(port_status_flag_idx)) { + JSON_ARRAY_APPEND(json, "%s", port_status_flags[port_status_flag_idx].name); } - port_status = info.mpie_status; - get_receive_port_context(taskinfo->task, taskinfo->table[k].iin_name, &port_context); - printf(" - 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx 0x%08x (%d) %s\n", - taskinfo->table[k].iin_object, - (taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) ? "recv,send ":"recv ", - SHOW_PORT_STATUS_FLAGS(port_status.mps_flags), - info.mpie_boost_cnt, - (taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-", - (port_status.mps_nsrequest) ? "N" : "-", - (port_status.mps_pdrequest) ? "P" : "-", - 1, - taskinfo->table[k].iin_urefs, - port_status.mps_sorights, - (port_status.mps_srights) ? "Y" : "N", - port_status.mps_qlimit, - port_status.mps_msgcount, - (uint64_t)port_context, - taskinfo->table[k].iin_name, - pid, - taskinfo->processName); + } + JSON_ARRAY_END(json); // port status flags + JSON_OBJECT_SET(json, boosts, %d, info.mpie_boost_cnt); + + JSON_KEY(json, notifications); + JSON_ARRAY_BEGIN(json); + if (taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) { + JSON_ARRAY_APPEND(json, "dead name"); + } + if (port_status.mps_nsrequest) { + JSON_ARRAY_APPEND(json, "no sender"); + } + if (port_status.mps_nsrequest) { + JSON_ARRAY_APPEND(json, "port destroy request"); + } + JSON_ARRAY_END(json); // notifications + + JSON_OBJECT_SET(json, recv_rights, %d, 1); + JSON_OBJECT_SET(json, send_rights, %d, taskinfo->table[k].iin_urefs); + JSON_OBJECT_SET(json, send_once_rights, %d, port_status.mps_sorights); + JSON_OBJECT_SET_BOOL(json, oref, port_status.mps_srights); + JSON_OBJECT_SET(json, queue_limit, %d, port_status.mps_qlimit); + JSON_OBJECT_SET(json, msg_count, %d, port_status.mps_msgcount); + JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)port_context); + JSON_OBJECT_SET(json, identifier, "0x%08x", taskinfo->table[k].iin_name); + JSON_OBJECT_SET(json, pid, %d, taskinfo->pid); + JSON_OBJECT_SET(json, process, "%s", taskinfo->processName); + JSON_OBJECT_END(json); // member + + printf(" - 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx 0x%08x (%d) %s\n", + taskinfo->table[k].iin_object, + (taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) ? "recv,send ":"recv ", + SHOW_PORT_STATUS_FLAGS(port_status.mps_flags), + info.mpie_boost_cnt, + (taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-", + (port_status.mps_nsrequest) ? "N" : "-", + (port_status.mps_pdrequest) ? "P" : "-", + 1, + taskinfo->table[k].iin_urefs, + port_status.mps_sorights, + (port_status.mps_srights) ? "Y" : "N", + port_status.mps_qlimit, + port_status.mps_msgcount, + (uint64_t)port_context, + taskinfo->table[k].iin_name, + taskinfo->pid, + taskinfo->processName); + break; + } + } + } + + JSON_ARRAY_END(json); // members + JSON_OBJECT_END(json); // port-set + + ret = vm_deallocate(mach_task_self(), (vm_address_t)members, + membersCnt * sizeof(mach_port_name_t)); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "vm_deallocate() failed: %s\n", + mach_error_string(ret)); + exit(1); + } + counts->portsetcount++; + return; + } + + if (entry->iin_type & MACH_PORT_TYPE_SEND) { + send = TRUE; + sendrights = entry->iin_urefs; + counts->sendcount++; + } + + if (entry->iin_type & MACH_PORT_TYPE_DNREQUEST) { + dnreq = TRUE; + counts->dncount++; + } + + if (entry->iin_type & MACH_PORT_TYPE_RECEIVE) { + mach_port_status_t status; + mach_port_info_ext_t info; + mach_port_context_t context = (mach_port_context_t)0; + struct k2n_table_node *k2nnode; + ret = get_recieve_port_status(taskinfo->task, entry->iin_name, &info); + get_receive_port_context(taskinfo->task, entry->iin_name, &context); + /* its ok to fail in fetching attributes */ + if (ret < 0) { + return; + } + status = info.mpie_status; + + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, type, "port"); + JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object); + + JSON_KEY(json, rights); + JSON_ARRAY_BEGIN(json); + JSON_ARRAY_APPEND(json, "recv"); + if (send) JSON_ARRAY_APPEND(json, "send"); + JSON_ARRAY_END(json); // rights + + JSON_KEY(json, port_status_flags); + JSON_ARRAY_BEGIN(json); + port_status_flag_idx = 0; + while (0 != port_status_flags[port_status_flag_idx++].flag) { + if (status.mps_flags & INDEX_TO_PORT_FLAG(port_status_flag_idx)) { + JSON_ARRAY_APPEND(json, "%s", port_status_flags[port_status_flag_idx].name); + } + } + JSON_ARRAY_END(json); // port status flags + JSON_OBJECT_SET(json, boosts, %d, info.mpie_boost_cnt); + + JSON_KEY(json, notifications); + JSON_ARRAY_BEGIN(json); + if (dnreq) { + JSON_ARRAY_APPEND(json, "dead name"); + } + if (status.mps_nsrequest) { + JSON_ARRAY_APPEND(json, "no sender"); + } + if (status.mps_nsrequest) { + JSON_ARRAY_APPEND(json, "port destroy request"); + } + JSON_ARRAY_END(json); // notifications + + JSON_OBJECT_SET(json, recv_rights, %d, 1); + JSON_OBJECT_SET(json, send_rights, %d, sendrights); + JSON_OBJECT_SET(json, send_once_rights, %d, status.mps_sorights); + JSON_OBJECT_SET_BOOL(json, oref, status.mps_srights); + JSON_OBJECT_SET(json, queue_limit, %d, status.mps_qlimit); + JSON_OBJECT_SET(json, msg_count, %d, status.mps_msgcount); + JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)context); + JSON_OBJECT_END(json); // port + + printf("0x%08x 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx \n", + entry->iin_name, + entry->iin_object, + (send) ? "recv,send ":"recv ", + SHOW_PORT_STATUS_FLAGS(status.mps_flags), + info.mpie_boost_cnt, + (dnreq) ? "D":"-", + (status.mps_nsrequest) ? "N":"-", + (status.mps_pdrequest) ? "P":"-", + 1, + sendrights, + status.mps_sorights, + (status.mps_srights) ? "Y":"N", + status.mps_qlimit, + status.mps_msgcount, + (uint64_t)context); + counts->receivecount++; + + /* show other rights (in this and other tasks) for the port */ + for (j = 0; j < taskCount; j++) { + if (allTaskInfos[j].valid == FALSE) + continue; + + k2nnode = k2n_table_lookup(allTaskInfos[j].k2ntable, entry->iin_object); + + while (k2nnode) { + if (k2nnode->info_name != entry) { + assert(k2nnode->info_name->iin_object == entry->iin_object); + + printf(" + %s -------- %s%s%s %5d <- 0x%08x (%d) %s\n", + (k2nnode->info_name->iin_type & MACH_PORT_TYPE_SEND_ONCE) ? + "send-once " : "send ", + (k2nnode->info_name->iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-", + "-", + "-", + k2nnode->info_name->iin_urefs, + k2nnode->info_name->iin_name, + allTaskInfos[j].pid, + allTaskInfos[j].processName); + } + + k2nnode = k2n_table_lookup_next(k2nnode, entry->iin_object); + } + } + return; + } + else if (entry->iin_type & MACH_PORT_TYPE_DEAD_NAME) + { + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, type, "dead name"); + JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object); + JSON_OBJECT_SET(json, send_rights, %d, entry->iin_urefs); + JSON_OBJECT_END(json); // dead name + + printf("0x%08x 0x%08x dead-name -------- --- %5d \n", + entry->iin_name, + entry->iin_object, + entry->iin_urefs); + counts->deadcount++; + return; + } + + if (entry->iin_type & MACH_PORT_TYPE_SEND_ONCE) { + counts->sendoncecount++; + } + + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object); + + JSON_KEY(json, rights); + JSON_ARRAY_BEGIN(json); + JSON_ARRAY_APPEND(json, "%s", (send) ? "send":"send once"); + JSON_ARRAY_END(json); //rights + + JSON_KEY(json, notifications); + JSON_ARRAY_BEGIN(json); + if (dnreq) JSON_ARRAY_APPEND(json, "dead name"); + JSON_ARRAY_END(json); // notifications + + JSON_OBJECT_SET(json, send_rights, %d, (send) ? sendrights : 0); + + printf("0x%08x 0x%08x %s -------- %s%s%s %5.0d ", + entry->iin_name, + entry->iin_object, + (send) ? "send ":"send-once ", + (dnreq) ? "D":"-", + "-", + "-", + (send) ? sendrights : 0); + + /* converting to kobjects is not always supported */ + ret = mach_port_kernel_object(taskinfo->task, + entry->iin_name, + &kotype, (unsigned *)&kobject); + if (ret == KERN_SUCCESS && kotype != 0) { + JSON_OBJECT_SET(json, identifier, "0x%08x", (natural_t)kobject); + JSON_OBJECT_SET(json, type, "%s", kobject_name(kotype)); + printf(" 0x%08x %s", (natural_t)kobject, kobject_name(kotype)); + if ((kotype == IKOT_TASK_RESUME) || (kotype == IKOT_TASK) || (kotype == IKOT_TASK_NAME)) { + if (taskinfo->task_kobject == kobject) { + /* neat little optimization since in most cases tasks have themselves in their ipc space */ + JSON_OBJECT_SET(json, pid, %d, taskinfo->pid); + JSON_OBJECT_SET(json, process, "%s", taskinfo->processName); + printf(" SELF (%d) %s", taskinfo->pid, taskinfo->processName); + } else { + my_per_task_info_t * _found_task = get_taskinfo_by_kobject((natural_t)kobject); + JSON_OBJECT_SET(json, pid, %d, _found_task->pid); + JSON_OBJECT_SET(json, process, "%s", _found_task->processName); + printf(" (%d) %s", _found_task->pid, _found_task->processName); + } + } + + if (kotype == IKOT_THREAD) { + for (int i = 0; i < taskinfo->threadCount; i++) { + if (taskinfo->threadInfos[i].th_kobject == kobject) { + printf(" (%#llx)", taskinfo->threadInfos[i].th_id); break; } } } - ret = vm_deallocate(mach_task_self(), (vm_address_t)members, - membersCnt * sizeof(mach_port_name_t)); - if (ret != KERN_SUCCESS) { - fprintf(stderr, "vm_deallocate() failed: %s\n", - mach_error_string(ret)); - exit(1); - } - portsetcount++; - continue; - } - - if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_SEND) { - send = TRUE; - sendrights = taskinfo->table[i].iin_urefs; - sendcount++; - } - - if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_DNREQUEST) { - dnreq = TRUE; - dncount++; - } - - if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_RECEIVE) { - mach_port_status_t status; - mach_port_info_ext_t info; - mach_port_context_t context = (mach_port_context_t)0; - struct k2n_table_node *k2nnode; - ret = get_recieve_port_status(taskinfo->task, taskinfo->table[i].iin_name, &info); - get_receive_port_context(taskinfo->task, taskinfo->table[i].iin_name, &context); - /* its ok to fail in fetching attributes */ - if (ret < 0) { - continue; + printf("\n"); + if (kotype == IKOT_VOUCHER) { + counts->vouchercount++; + if (lsmp_config.show_voucher_details) { + JSON_KEY(json, recipes); + JSON_ARRAY_BEGIN(json); + char * detail = copy_voucher_detail(taskinfo->task, entry->iin_name, json); + JSON_ARRAY_END(json); // recipes + printf("%s\n", detail); + free(detail); } - status = info.mpie_status; - printf("0x%08x 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx \n", - taskinfo->table[i].iin_name, - taskinfo->table[i].iin_object, - (send) ? "recv,send ":"recv ", - SHOW_PORT_STATUS_FLAGS(status.mps_flags), - info.mpie_boost_cnt, - (dnreq) ? "D":"-", - (status.mps_nsrequest) ? "N":"-", - (status.mps_pdrequest) ? "P":"-", - 1, - sendrights, - status.mps_sorights, - (status.mps_srights) ? "Y":"N", - status.mps_qlimit, - status.mps_msgcount, - (uint64_t)context); - receivecount++; + } + JSON_OBJECT_END(json); // kobject + return; + } - /* show other rights (in this and other tasks) for the port */ - for (j = 0; j < taskCount; j++) { - if (allTaskInfos[j].valid == FALSE) - continue; + /* not kobject - find the receive right holder */ + my_per_task_info_t *recv_holder_taskinfo; + mach_port_name_t recv_name = MACH_PORT_NULL; + if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(entry, &recv_holder_taskinfo, &recv_name)) { + mach_port_status_t port_status; + mach_port_info_ext_t info; + mach_port_context_t port_context = (mach_port_context_t)0; + if (0 != get_recieve_port_status(recv_holder_taskinfo->task, recv_name, &info)) { + bzero((void *)&port_status, sizeof(port_status)); + } + port_status = info.mpie_status; + get_receive_port_context(recv_holder_taskinfo->task, recv_name, &port_context); - k2nnode = k2n_table_lookup(allTaskInfos[j].k2ntable, taskinfo->table[i].iin_object); + JSON_OBJECT_SET(json, queue_limit, %d, port_status.mps_qlimit); + JSON_OBJECT_SET(json, msg_count, %d, port_status.mps_msgcount); + JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)port_context); + JSON_OBJECT_SET(json, identifier, "0x%08x", recv_name); + JSON_OBJECT_SET(json, pid, %d, recv_holder_taskinfo->pid); + JSON_OBJECT_SET(json, process, "%s", recv_holder_taskinfo->processName); - while (k2nnode) { - if (k2nnode->info_name != &taskinfo->table[i]) { - assert(k2nnode->info_name->iin_object == taskinfo->table[i].iin_object); + printf(" -> %6d %8d 0x%016llx 0x%08x (%d) %s\n", + port_status.mps_qlimit, + port_status.mps_msgcount, + (uint64_t)port_context, + recv_name, + recv_holder_taskinfo->pid, + recv_holder_taskinfo->processName); - printf(" + %s -------- %s%s%s %5d <- 0x%08x (%d) %s\n", - (k2nnode->info_name->iin_type & MACH_PORT_TYPE_SEND_ONCE) ? - "send-once " : "send ", - (k2nnode->info_name->iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-", - "-", - "-", - k2nnode->info_name->iin_urefs, - k2nnode->info_name->iin_name, - allTaskInfos[j].pid, - allTaskInfos[j].processName); - } - - k2nnode = k2n_table_lookup_next(k2nnode, k2nnode->info_name->iin_name); - } - } - continue; - } - else if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_DEAD_NAME) - { - printf("0x%08x 0x%08x dead-name -------- --- %5d \n", - taskinfo->table[i].iin_name, - taskinfo->table[i].iin_object, - taskinfo->table[i].iin_urefs); - deadcount++; - continue; - } - - if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_SEND_ONCE) { - sendonce = TRUE; - sendoncecount++; - } - - printf("0x%08x 0x%08x %s -------- %s%s%s %5.0d ", - taskinfo->table[i].iin_name, - taskinfo->table[i].iin_object, - (send) ? "send ":"send-once ", - (dnreq) ? "D":"-", - "-", - "-", - (send) ? sendrights : 0); - - /* converting to kobjects is not always supported */ - ret = mach_port_kernel_object(taskinfo->task, - taskinfo->table[i].iin_name, - &kotype, (unsigned *)&kobject); - if (ret == KERN_SUCCESS && kotype != 0) { - printf(" 0x%08x %s", (natural_t)kobject, kobject_name(kotype)); - if ((kotype == IKOT_TASK_RESUME) || (kotype == IKOT_TASK) || (kotype == IKOT_TASK_NAME)) { - if (taskinfo->task_kobject == kobject) { - /* neat little optimization since in most cases tasks have themselves in their ipc space */ - printf(" SELF (%d) %s", taskinfo->pid, taskinfo->processName); - } else { - my_per_task_info_t * _found_task = get_taskinfo_by_kobject((natural_t)kobject); - printf(" (%d) %s", _found_task->pid, _found_task->processName); - } - } - - printf("\n"); - if (kotype == IKOT_VOUCHER) { - vouchercount++; - if (lsmp_config.show_voucher_details) { - char * detail = copy_voucher_detail(taskinfo->task, taskinfo->table[i].iin_name); - printf("%s\n", detail); - free(detail); - } - } - continue; - } - - /* not kobject - find the receive right holder */ - my_per_task_info_t *recv_holder_taskinfo; - mach_port_name_t recv_name = MACH_PORT_NULL; - if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(&taskinfo->table[i], &recv_holder_taskinfo, &recv_name)) { - mach_port_status_t port_status; - mach_port_info_ext_t info; - mach_port_context_t port_context = (mach_port_context_t)0; - if (0 != get_recieve_port_status(recv_holder_taskinfo->task, recv_name, &info)) { - bzero((void *)&port_status, sizeof(port_status)); - } - port_status = info.mpie_status; - get_receive_port_context(recv_holder_taskinfo->task, recv_name, &port_context); - printf(" -> %6d %8d 0x%016llx 0x%08x (%d) %s\n", - port_status.mps_qlimit, - port_status.mps_msgcount, - (uint64_t)port_context, - recv_name, - recv_holder_taskinfo->pid, - recv_holder_taskinfo->processName); - - } else - printf(" 0x00000000 (-) Unknown Process\n"); - - } - printf("total = %d\n", taskinfo->tableCount + taskinfo->treeCount - emptycount); - printf("SEND = %d\n", sendcount); - printf("RECEIVE = %d\n", receivecount); - printf("SEND_ONCE = %d\n", sendoncecount); - printf("PORT_SET = %d\n", portsetcount); - printf("DEAD_NAME = %d\n", deadcount); - printf("DNREQUEST = %d\n", dncount); - printf("VOUCHERS = %d\n", vouchercount); + } else { + JSON_OBJECT_SET(json, identifier, "0x%08x", 0); + JSON_OBJECT_SET(json, pid, %d, -1); + JSON_OBJECT_SET(json, process, "unknown"); + printf(" 0x00000000 (-) Unknown Process\n"); + } + JSON_OBJECT_END(json); // non-kobject } -uint32_t print_hex_data(char *outstr, size_t maxlen, char *prefix, char *desc, void *addr, int len) { +uint32_t print_hex_data(char *outstr, uint32_t maxlen, char *prefix, char *desc, void *addr, int len) { int i; unsigned char buff[17]; unsigned char *pc = addr; uint32_t plen = 0; if (desc != NULL) - plen += safesize(snprintf(&outstr[len], maxlen - plen, "%s%s:\n", prefix, desc)); + plen += snprintf(&outstr[plen], safesize(maxlen - plen), "%s%s:\n", prefix, desc); for (i = 0; i < len; i++) { if ((i % 16) == 0) { if (i != 0) - plen += safesize(snprintf(&outstr[len], maxlen - plen, " %s\n", buff)); + plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %s\n", buff); - plen += safesize(snprintf(&outstr[len], maxlen - plen, "%s %04x ", prefix, i)); + plen += snprintf(&outstr[plen], safesize(maxlen - plen), "%s %04x ", prefix, i); } - plen += safesize(snprintf(&outstr[len], maxlen - plen, " %02x", pc[i])); + plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %02x", pc[i]); if ((pc[i] < 0x20) || (pc[i] > 0x7e)) buff[i % 16] = '.'; @@ -466,11 +710,11 @@ uint32_t print_hex_data(char *outstr, size_t maxlen, char *prefix, char *desc, v } while ((i % 16) != 0) { - plen += safesize(snprintf(&outstr[len], maxlen - plen, " ")); + plen += snprintf(&outstr[plen], safesize(maxlen - plen), " "); i++; } - plen += safesize(snprintf(&outstr[len], maxlen - plen, " %s\n", buff)); + plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %s\n", buff); return plen; } diff --git a/lsmp.tproj/task_details.c b/lsmp.tproj/task_details.c index aeaa160..f642e0d 100644 --- a/lsmp.tproj/task_details.c +++ b/lsmp.tproj/task_details.c @@ -43,7 +43,7 @@ static uint32_t k2n_hash(natural_t kobject) { return (uint64_t)kobject * 2654435761 >> 32; } -struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natural_t kobject) { +static struct k2n_table_node *k2n_table_lookup_next_internal(struct k2n_table_node *node, natural_t kobject) { while (node) { if (kobject == node->kobject) return node; @@ -54,6 +54,13 @@ struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natura return NULL; } +struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natural_t kobject) { + if (!node) { + return NULL; + } + return k2n_table_lookup_next_internal(node->next, kobject); +} + struct k2n_table_node *k2n_table_lookup(struct k2n_table_node **table, natural_t kobject) { uint32_t hv; struct k2n_table_node *node; @@ -62,7 +69,7 @@ struct k2n_table_node *k2n_table_lookup(struct k2n_table_node **table, natural_t node = table[hv & K2N_TABLE_MASK]; - return k2n_table_lookup_next(node, kobject); + return k2n_table_lookup_next_internal(node, kobject); } static void k2n_table_enter(struct k2n_table_node **table, natural_t kobject, ipc_info_name_t *info_name) { @@ -76,6 +83,7 @@ static void k2n_table_enter(struct k2n_table_node **table, natural_t kobject, ip node->kobject = kobject; node->info_name = info_name; + assert(kobject == info_name->iin_object); node->next = table[hv & K2N_TABLE_MASK]; table[hv & K2N_TABLE_MASK] = node; @@ -191,7 +199,7 @@ kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_t target_ } if (KERN_SUCCESS == thread_get_mach_voucher(threadPorts[i], 0, &th_voucher) && th_voucher != IPC_VOUCHER_NULL) { - char *detail = copy_voucher_detail(mach_task_self(), th_voucher); + char *detail = copy_voucher_detail(mach_task_self(), th_voucher, NULL); taskinfo->threadInfos[i].voucher_detail = strndup(detail, VOUCHER_DETAIL_MAXLEN); free(detail); @@ -242,19 +250,19 @@ void get_exc_behavior_string(exception_behavior_t b, char *out_string, size_t le out_string[0]='\0'; if (b & MACH_EXCEPTION_CODES) - strncat(out_string, "MACH +", len); + strlcat(out_string, "MACH +", len); switch (b & ~MACH_EXCEPTION_CODES) { case EXCEPTION_DEFAULT: - strncat(out_string, " DEFAULT", len); + strlcat(out_string, " DEFAULT", len); break; case EXCEPTION_STATE: - strncat(out_string, " STATE", len); + strlcat(out_string, " STATE", len); break; case EXCEPTION_STATE_IDENTITY: - strncat(out_string, " IDENTITY", len); + strlcat(out_string, " IDENTITY", len); break; default: - strncat(out_string, " UNKNOWN", len); + strlcat(out_string, " UNKNOWN", len); } } @@ -263,37 +271,40 @@ void get_exc_mask_string(exception_mask_t m, char *out_string, size_t len) out_string[0]='\0'; if (m & (1<exceptionInfo.count; i++) { if (taskinfo->exceptionInfo.ports[i] != MACH_PORT_NULL) { @@ -303,17 +314,27 @@ kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo) header_required = FALSE; } get_exc_behavior_string(taskinfo->exceptionInfo.behaviors[i], behavior_string, sizeof(behavior_string)); - get_exc_mask_string(taskinfo->exceptionInfo.masks[i], mask_string, 200); + get_exc_mask_string(taskinfo->exceptionInfo.masks[i], mask_string, sizeof(mask_string)); + + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, port, "0x%08x", taskinfo->exceptionInfo.ports[i]); + JSON_OBJECT_SET(json, flavor, "0x%03x", taskinfo->exceptionInfo.flavors[i]); + JSON_OBJECT_SET(json, behavior, "%s", behavior_string); + JSON_OBJECT_SET(json, mask, "%s", mask_string); + JSON_OBJECT_END(json); // exception port + printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo->exceptionInfo.ports[i], taskinfo->exceptionInfo.flavors[i], behavior_string, mask_string); } } + JSON_ARRAY_END(json); // exception ports + return KERN_SUCCESS; } -kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo) +kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo, JSON_t json) { kern_return_t kret = KERN_SUCCESS; mach_msg_type_number_t threadcount = taskinfo->threadCount; @@ -321,7 +342,12 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo) boolean_t newline_required = TRUE; struct my_per_thread_info * info = NULL; + JSON_KEY(json, threads); + JSON_ARRAY_BEGIN(json); + for (int i = 0; i < threadcount; i++) { + JSON_OBJECT_BEGIN(json); + info = &taskinfo->threadInfos[i]; if (header_required) { printf("Thread_KObject Thread-ID Port Description."); @@ -337,12 +363,19 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo) /* TODO: Should print tid and stuff */ printf("0x%08x ", info->th_kobject); printf("0x%llx ", info->th_id); + + JSON_OBJECT_SET(json, kobject, "0x%08x", info->th_kobject); + JSON_OBJECT_SET(json, tid, "0x%llx", info->th_id); } if (info->voucher_detail != NULL) { + /* TODO: include voucher detail in JSON */ printf("%s\n", info->voucher_detail); } + JSON_KEY(json, exception_ports); + JSON_ARRAY_BEGIN(json); + /* print the thread exception ports also */ if (taskinfo->threadExceptionInfos != NULL) { @@ -354,6 +387,8 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo) if (excinfo->count > 0) { boolean_t header_required = TRUE; for (int i = 0; i < excinfo->count; i++) { + JSON_OBJECT_BEGIN(json); + if (excinfo->ports[i] != MACH_PORT_NULL) { if (header_required) { printf("\n exc_port flavor mask -> name owner\n"); @@ -361,6 +396,12 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo) } get_exc_behavior_string(excinfo->behaviors[i], behavior_string, sizeof(behavior_string)); get_exc_mask_string(excinfo->masks[i], mask_string, sizeof(mask_string)); + + JSON_OBJECT_SET(json, port, "0x%08x", excinfo->ports[i]); + JSON_OBJECT_SET(json, flavor, "0x%03x", excinfo->flavors[i]); + JSON_OBJECT_SET(json, behavior, "%s", behavior_string); + JSON_OBJECT_SET(json, mask, "%s", mask_string); + printf(" 0x%08x 0x%03x <%s> %s " , excinfo->ports[i], excinfo->flavors[i], behavior_string, mask_string); ipc_info_name_t actual_sendinfo; @@ -368,6 +409,12 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo) my_per_task_info_t *recv_holder_taskinfo; mach_port_name_t recv_name = MACH_PORT_NULL; if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(&actual_sendinfo, &recv_holder_taskinfo, &recv_name)) { + + JSON_OBJECT_SET(json, name, "0x%08x", recv_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", actual_sendinfo.iin_object); + JSON_OBJECT_SET(json, pid, %d, recv_holder_taskinfo->pid); + JSON_OBJECT_SET(json, process, "%s", recv_holder_taskinfo->processName); + printf(" -> 0x%08x 0x%08x (%d) %s\n", recv_name, actual_sendinfo.iin_object, @@ -382,13 +429,15 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo) printf("\n"); } - + JSON_OBJECT_END(json); // exception port } } - } - + JSON_ARRAY_END(json); // exception ports + JSON_OBJECT_END(json); // thread } + + JSON_ARRAY_END(json); // threads printf("\n"); return kret; } diff --git a/ltop.tproj/ltop.c b/ltop.tproj/ltop.c index 9a4c90d..42348e7 100644 --- a/ltop.tproj/ltop.c +++ b/ltop.tproj/ltop.c @@ -282,7 +282,7 @@ get_all_info(void) static void print_num(int64_t num, int64_t delta) { - char suf = '\0'; + const char *suf = ""; char posneg = ' '; int numwidth; @@ -297,23 +297,22 @@ print_num(int64_t num, int64_t delta) if (llabs(num) > 10000000000) { num /= 1000000000; - suf = 'G'; + suf = "G"; } else if (llabs(num) > 10000000) { num /= 1000000; - suf = 'M'; + suf = "M"; } else if (llabs(num) > 100000) { num /= 1000; - suf = 'K'; + suf = "K"; } posneg = (delta < 0) ? '-' : ((delta > 0) ? '+' : ' '); numwidth = 10; - if (suf != '\0') - numwidth--; + numwidth -= strlen(suf); - printf("%*lld%c%c ", numwidth, num, suf, posneg); + printf("%*lld%s%c ", numwidth, num, suf, posneg); } static void diff --git a/memory_pressure.tproj/memory_pressure.c b/memory_pressure.tproj/memory_pressure.c index eb72738..2b2784e 100644 --- a/memory_pressure.tproj/memory_pressure.c +++ b/memory_pressure.tproj/memory_pressure.c @@ -41,6 +41,7 @@ unsigned long phys_mem = 0; /* amount of physical memory in bytes */ unsigned int phys_pages = 0; /* number of physical memory pages */ int sleep_seconds = 1; +int requested_hysteresis_seconds = 0; boolean_t quiet_mode_on = FALSE; boolean_t simulate_mode_on = FALSE; @@ -83,6 +84,7 @@ usage(void) " -p - allocate memory until percent free is this (or less)\n" " -s - how long to sleep between checking for a set percent level\n" " -w - don't allocate, just wait until percent free is this then exit\n" + " -y - Hysteresis Interval: how long to wait after requested percntage free is reached, before exiting program. Requires the usage of the -p option\n" " -v - print VM statistics every sampling interval\n" " -Q - reduces the tool's output\n" " -S - simulate the system's memory pressure level without applying any real pressure\n" @@ -457,6 +459,7 @@ munch_for_percentage(unsigned int sleep_seconds, unsigned int wait_percent_free, { int total_pages_allocated = 0; + int current_stable_timer = 0; /* in seconds */ unsigned int current_percent = 0; boolean_t page_op = PAGE_OP_FREE; unsigned int pages_to_process = 0; @@ -539,6 +542,18 @@ munch_for_percentage(unsigned int sleep_seconds, unsigned int wait_percent_free, printf("."); fflush(stdout); } + + /* Stability has been reached; Increment current_stable_timer by sleep_seconds */ + + if (current_stable_timer <= requested_hysteresis_seconds){ + current_stable_timer += sleep_seconds; + /* Debug only */ + /* printf("\n Percentage Free stable for %d seconds", current_stable_timer); */ + } else { + printf ("\n Maintained memory pressure to %d percent free for more than %d seconds. Stopping pressure now.", current_percent, requested_hysteresis_seconds); + return; + } + print_vm_stats_on_page_processing = FALSE; } @@ -566,7 +581,7 @@ main(int argc, char * const argv[]) unsigned int print_vm_stats = 0; char level[10]; - while ((opt = getopt(argc, argv, "hl:p:s:w:vQS")) != -1) { + while ((opt = getopt(argc, argv, "hl:p:s:w:y:vQS")) != -1) { switch (opt) { case 'h': usage(); @@ -599,6 +614,9 @@ main(int argc, char * const argv[]) case 'w': wait_percent_free = atoi(optarg); break; + case 'y': + requested_hysteresis_seconds = atoi(optarg); + break; case 'v': print_vm_stats = 1; break; @@ -618,6 +636,13 @@ main(int argc, char * const argv[]) return 0; } + if (requested_hysteresis_seconds > 0) { + if (desired_percent == 0) { + printf("Hysteresis time may only be specified in conjunction with a non-zero value for the -p option. \n"); + usage(); + } + } + phys_mem = read_sysctl_int("hw.physmem"); phys_pages = (unsigned int) (phys_mem / PAGE_SIZE); diff --git a/mkfile.tproj/mkfile.8 b/mkfile.tproj/mkfile.8 index 59c772b..d67fbfc 100644 --- a/mkfile.tproj/mkfile.8 +++ b/mkfile.tproj/mkfile.8 @@ -44,4 +44,4 @@ re-exported before the client will be able to access it. This action may only be done when the client is not running. .SH "SEE ALSO" .TP -chmod(2), stat(2), sticky(8) +chmod(2), stat(2), sticky(7) diff --git a/mslutil/mslutil.1 b/mslutil/mslutil.1 new file mode 100644 index 0000000..ea4f068 --- /dev/null +++ b/mslutil/mslutil.1 @@ -0,0 +1,46 @@ +.\" Copyright (c) 2017, Apple Computer, Inc. All rights reserved. +.\" +.Dd March 31, 2017 +.Dt MSLUTIL 1 +.Os "Mac OS X" +.Sh NAME +.Nm mslutil +.Nd Tool to enable / disable malloc stack logging on a specific proces +.Sh SYNOPSIS +.Nm mslutil pid [--enable flavor] | [--disable] +.Sh DESCRIPTION +The +.Nm mslutil +utility enables/disables malloc stack logging on the process specified by +.Nm pid. +It requires root privileges. +.Pp +The options are as follows: +.Bl -tag -width Ds +.\" ========== +.It Fl -enable +Specifying the +.Fl -enable +option enables malloc stack logging, using the +.Pa flavor +provided. +The supported flavors are: +.Pp +.Pa full +Standard malloc stack logging that records both vm and malloc calls +.Pp +.Pa malloc +Standard malloc stack logging that records only malloc calls +.Pp +.Pa vm +Standard malloc stack logging that records only vm calls +.Pp +.Pa lite +Lite mode of malloc stack logging. +.\" ========== +.It Fl -disable +Specifying the +.Fl -disable +option disables any current mode of malloc stack logging. +.\" ========== +.El diff --git a/mslutil/mslutil.c b/mslutil/mslutil.c new file mode 100644 index 0000000..5738d4a --- /dev/null +++ b/mslutil/mslutil.c @@ -0,0 +1,96 @@ +// +// mslutil.c +// mslutil +// +// Created by Christopher Deppe on 3/31/17. +// + +#include +#include +#include +#include +#include +#include + +#define BSD_PID_MAX 99999 /* Copy of PID_MAX from sys/proc_internal.h. */ + +static void print_usage() +{ + printf("usage: mslutil pid [--disable] | [--enable malloc | vm | full | lite | vmlite]\n"); +} + +static int send_msl_command(uint64_t pid, uint64_t flavor) +{ + uint64_t flags = flavor; + flags <<= 32; + + flags |= (pid & 0xFFFFFFFF); + + int ret = sysctlbyname("kern.memorystatus_vm_pressure_send", 0, 0, &flags, sizeof(flags)); + + if (ret) { + printf("send_msl_command - sysctl: kern.memorystatus_vm_pressure_send failed %s\n", strerror(errno)); + } else { + printf("send_msl_command - success!\n"); + } + + return ret; +} + +int main(int argc, const char * argv[]) +{ + if (argc < 3) { + print_usage(); + exit(1); + } + + int ret = -1; + + pid_t pid = atoi(argv[1]); + + if (pid <= 0 || pid > BSD_PID_MAX) { + printf("Invalid pid\n"); + exit(1); + } + + if (strcmp(argv[2], "--enable") == 0) { + if (argc < 4) { + print_usage(); + exit(1); + } + + uint64_t flavor = 0; + + if (strcmp(argv[3], "full") == 0) { + flavor = MEMORYSTATUS_ENABLE_MSL_MALLOC | MEMORYSTATUS_ENABLE_MSL_VM; + } else if (strcmp(argv[3], "malloc") == 0) { + flavor = MEMORYSTATUS_ENABLE_MSL_MALLOC; + } else if (strcmp(argv[3], "vm") == 0) { + flavor = MEMORYSTATUS_ENABLE_MSL_VM; + } else if (strcmp(argv[3], "lite") == 0) { + flavor = MEMORYSTATUS_ENABLE_MSL_LITE_FULL; + } else if (strcmp(argv[3], "vmlite") == 0) { + flavor = MEMORYSTATUS_ENABLE_MSL_LITE_VM; + } + + if (flavor == 0) { + print_usage(); + exit(1); + } + + ret = send_msl_command(pid, flavor); + } else if (strcmp(argv[2], "--disable") == 0) { + ret = send_msl_command(pid, MEMORYSTATUS_DISABLE_MSL); + } else { + print_usage(); + exit(1); + } + + if (ret != 0) { + exit(1); + } else { + exit(0); + } +} + + diff --git a/nvram.tproj/nvram.c b/nvram.tproj/nvram.c index 96d2c0b..7bdf9c6 100644 --- a/nvram.tproj/nvram.c +++ b/nvram.tproj/nvram.c @@ -56,6 +56,28 @@ static io_registry_entry_t gOptionsRef; static bool gUseXML; static bool gUseForceSync; +#if TARGET_OS_BRIDGE /* Stuff for nvram bridge -> intel */ +#include +#include + +static kern_return_t LinkMacNVRAMSymbols(void); +static kern_return_t GetMacOFVariable(char *name, char **value); +static kern_return_t SetMacOFVariable(char *name, char *value); +static kern_return_t DeleteMacOFVariable(char *name); + +static bool gBridgeToIntel; +static void *gDL_handle; +static void *gNvramInterface; + +static void (*hostInterfaceInitialize_fptr)(void); +static void *(*createNvramHostInterface_fptr)(const char *handle); +static kern_return_t (*destroyNvramHostInterface_fptr)(void *interface); +static kern_return_t (*getNVRAMVariable_fptr)(void *interface, char *name, char **buffer, uint32_t *size); +static kern_return_t (*setNVRAMVariable_fptr)(void *interface, char *name, char *buffer); +static kern_return_t (*deleteNVRAMVariable_fptr)(void *interface, char *name); +static void (*hostInterfaceDeinitialize_fptr)(void); /* may not need? */ + +#endif /* TARGET_OS_BRIDGE */ int main(int argc, char **argv) { @@ -88,6 +110,12 @@ int main(int argc, char **argv) for (str += 1 ; *str; str++) { switch (*str) { case 'p' : +#if TARGET_OS_BRIDGE + if (gBridgeToIntel) { + fprintf(stderr, "-p not supported for Mac NVRAM store.\n"); + return 1; + } +#endif PrintOFVariables(); break; @@ -96,6 +124,12 @@ int main(int argc, char **argv) break; case 'f': +#if TARGET_OS_BRIDGE + if (gBridgeToIntel) { + fprintf(stderr, "-f not supported for Mac NVRAM store.\n"); + return 1; + } +#endif cnt++; if (cnt < argc && *argv[cnt] != '-') { ParseFile(argv[cnt]); @@ -104,16 +138,33 @@ int main(int argc, char **argv) } break; - case 'd': - cnt++; - if (cnt < argc && *argv[cnt] != '-') { - DeleteOFVariable(argv[cnt]); - } else { - UsageMessage("missing name"); - } - break; + case 'd': + cnt++; + if (cnt < argc && *argv[cnt] != '-') { +#if TARGET_OS_BRIDGE + if (gBridgeToIntel) { + if ((result = DeleteMacOFVariable(argv[cnt])) != KERN_SUCCESS) { + errx(1, "Error deleting variable - '%s': %s (0x%08x)", argv[cnt], + mach_error_string(result), result); + } + } + else +#endif + { + DeleteOFVariable(argv[cnt]); + } + } else { + UsageMessage("missing name"); + } + break; case 'c': +#if TARGET_OS_BRIDGE + if (gBridgeToIntel) { + fprintf(stderr, "-c not supported for Mac NVRAM store.\n"); + return 1; + } +#endif ClearOFVariables(); break; case 's': @@ -121,6 +172,17 @@ int main(int argc, char **argv) // commit the variable to nonvolatile storage gUseForceSync = true; break; +#if TARGET_OS_BRIDGE + case 'm': + // used to set nvram variables on the Intel side + // from the ARM side (Bridge -> Mac) + fprintf(stdout, "Using Mac NVRAM store.\n"); + + LinkMacNVRAMSymbols(); + gBridgeToIntel = true; + break; +#endif + default: strcpy(errorMessage, "no such option as --"); errorMessage[strlen(errorMessage)-1] = *str; @@ -159,6 +221,9 @@ static void UsageMessage(char *message) printf("\t-f set firmware variables from a text file\n"); printf("\t-d delete the named variable\n"); printf("\t-c delete all variables\n"); +#if TARGET_OS_BRIDGE + printf("\t-m set nvram variables on macOS from bridgeOS\n"); +#endif printf("\tname=value set named variable\n"); printf("\tname print variable\n"); printf("Note that arguments and options are executed in order.\n"); @@ -405,18 +470,40 @@ static void SetOrGetOFVariable(char *str) if (set == 1) { // On sets, the OF variable's value follows the equal sign. value = str; - - result = SetOFVariable(name, value); - NVRamSyncNow(name); /* Try syncing the new data to device, best effort! */ +#if TARGET_OS_BRIDGE + if (gBridgeToIntel) { + result = SetMacOFVariable(name, value); + } + else +#endif + { + result = SetOFVariable(name, value); + NVRamSyncNow(name); /* Try syncing the new data to device, best effort! */ + } if (result != KERN_SUCCESS) { errx(1, "Error setting variable - '%s': %s", name, mach_error_string(result)); } } else { - result = GetOFVariable(name, &nameRef, &valueRef); - if (result != KERN_SUCCESS) { - errx(1, "Error getting variable - '%s': %s", name, - mach_error_string(result)); +#if TARGET_OS_BRIDGE + if (gBridgeToIntel) { + result = GetMacOFVariable(name, &value); + if (result != KERN_SUCCESS) { + errx(1, "Error getting variable - '%s': %s", name, + mach_error_string(result)); + } + nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8); + valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, kCFStringEncodingUTF8); + free(value); + } + else +#endif + { + result = GetOFVariable(name, &nameRef, &valueRef); + if (result != KERN_SUCCESS) { + errx(1, "Error getting variable - '%s': %s", name, + mach_error_string(result)); + } } PrintOFVariable(nameRef, valueRef, 0); @@ -425,6 +512,51 @@ static void SetOrGetOFVariable(char *str) } } +#if TARGET_OS_BRIDGE +static kern_return_t LinkMacNVRAMSymbols() +{ + gDL_handle = dlopen("libMacEFIHostInterface.dylib", RTLD_LAZY); + if (gDL_handle == NULL) { + errx(errno, "Failed to dlopen libMacEFIHostInterface.dylib"); + return KERN_FAILURE; /* NOTREACHED */ + } + + hostInterfaceInitialize_fptr = dlsym(gDL_handle, "hostInterfaceInitialize"); + if (hostInterfaceInitialize_fptr == NULL) { + errx(errno, "failed to link hostInterfaceInitialize"); + } + createNvramHostInterface_fptr = dlsym(gDL_handle, "createNvramHostInterface"); + if (createNvramHostInterface_fptr == NULL) { + errx(errno, "failed to link createNvramHostInterface"); + } + destroyNvramHostInterface_fptr = dlsym(gDL_handle, "destroyNvramHostInterface"); + if (destroyNvramHostInterface_fptr == NULL) { + errx(errno, "failed to link destroyNvramHostInterface"); + } + getNVRAMVariable_fptr = dlsym(gDL_handle, "getNVRAMVariable"); + if (getNVRAMVariable_fptr == NULL) { + errx(errno, "failed to link getNVRAMVariable"); + } + setNVRAMVariable_fptr = dlsym(gDL_handle, "setNVRAMVariable"); + if (setNVRAMVariable_fptr == NULL) { + errx(errno, "failed to link setNVRAMVariable"); + } + deleteNVRAMVariable_fptr = dlsym(gDL_handle, "deleteNVRAMVariable"); + if (deleteNVRAMVariable_fptr == NULL) { + errx(errno, "failed to link deleteNVRAMVariable"); + } + hostInterfaceDeinitialize_fptr = dlsym(gDL_handle, "hostInterfaceDeinitialize"); + if (hostInterfaceDeinitialize_fptr == NULL) { + errx(errno, "failed to link hostInterfaceDeinitialize"); + } + + /* also do the initialization */ + hostInterfaceInitialize_fptr(); + gNvramInterface = createNvramHostInterface_fptr(NULL); + + return KERN_SUCCESS; +} +#endif // GetOFVariable(name, nameRef, valueRef) // @@ -446,6 +578,19 @@ static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef, return KERN_SUCCESS; } +#if TARGET_OS_BRIDGE +// GetMacOFVariable(name, value) +// +// Get the named firmware variable from the Intel side. +// Return the value in value +// +static kern_return_t GetMacOFVariable(char *name, char **value) +{ + uint32_t value_size; + + return getNVRAMVariable_fptr(gNvramInterface, name, value, &value_size); +} +#endif // SetOFVariable(name, value) // @@ -458,58 +603,64 @@ static kern_return_t SetOFVariable(char *name, char *value) CFTypeID typeID; kern_return_t result = KERN_SUCCESS; - nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, - kCFStringEncodingUTF8); - if (nameRef == 0) { - errx(1, "Error creating CFString for key %s", name); - } - - valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, nameRef, 0, 0); - if (valueRef) { - typeID = CFGetTypeID(valueRef); - CFRelease(valueRef); - - valueRef = ConvertValueToCFTypeRef(typeID, value); - if (valueRef == 0) { - errx(1, "Error creating CFTypeRef for value %s", value); - } result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); - } else { - while (1) { - // In the default case, try data, string, number, then boolean. - - valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value); - if (valueRef != 0) { - result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); - if (result == KERN_SUCCESS) break; - } - - valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value); - if (valueRef != 0) { - result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); - if (result == KERN_SUCCESS) break; - } - - valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value); - if (valueRef != 0) { - result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); - if (result == KERN_SUCCESS) break; - } - - valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value); - if (valueRef != 0) { - result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); - if (result == KERN_SUCCESS) break; - } - - break; + nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, + kCFStringEncodingUTF8); + if (nameRef == 0) { + errx(1, "Error creating CFString for key %s", name); + } + + valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, nameRef, 0, 0); + if (valueRef) { + typeID = CFGetTypeID(valueRef); + CFRelease(valueRef); + + valueRef = ConvertValueToCFTypeRef(typeID, value); + if (valueRef == 0) { + errx(1, "Error creating CFTypeRef for value %s", value); + } result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); + } else { + while (1) { + // In the default case, try data, string, number, then boolean. + + valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value); + if (valueRef != 0) { + result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); + if (result == KERN_SUCCESS) break; + } + + valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value); + if (valueRef != 0) { + result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); + if (result == KERN_SUCCESS) break; + } + + valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value); + if (valueRef != 0) { + result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); + if (result == KERN_SUCCESS) break; + } + + valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value); + if (valueRef != 0) { + result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef); + if (result == KERN_SUCCESS) break; + } + + break; + } } - } CFRelease(nameRef); return result; } +#if TARGET_OS_BRIDGE +static kern_return_t SetMacOFVariable(char *name, char *value) +{ + return setNVRAMVariable_fptr(gNvramInterface, name, value); +} +#endif // DeleteOFVariable(name) // @@ -521,6 +672,13 @@ static void DeleteOFVariable(char *name) SetOFVariable(kIONVRAMDeletePropertyKey, name); } +#if TARGET_OS_BRIDGE +static kern_return_t DeleteMacOFVariable(char *name) +{ + return deleteNVRAMVariable_fptr(gNvramInterface, name); +} +#endif + static void NVRamSyncNow(char *name) { if (!gUseForceSync) { @@ -586,8 +744,29 @@ static void PrintOFVariable(const void *key, const void *value, void *context) long length; CFTypeID typeID; + if (gUseXML) { + CFDataRef data; + CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, &key, &value, 1, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (dict == NULL) { + errx(1, "Error creating dictionary for variable value"); + } + + data = CFPropertyListCreateData( kCFAllocatorDefault, dict, kCFPropertyListXMLFormat_v1_0, 0, NULL ); + if (data == NULL) { + errx(1, "Error creating xml plist for variable"); + } + + fwrite(CFDataGetBytePtr(data), sizeof(UInt8), CFDataGetLength(data), stdout); + + CFRelease(dict); + CFRelease(data); + return; + } + // Get the OF variable's name. - nameLen = CFStringGetLength(key) + 1; + nameLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key), + kCFStringEncodingUTF8) + 1; nameBuffer = malloc(nameLen); if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) ) nameString = nameBuffer; @@ -609,7 +788,8 @@ static void PrintOFVariable(const void *key, const void *value, void *context) else sprintf(numberBuffer, "0x%x", number); valueString = numberBuffer; } else if (typeID == CFStringGetTypeID()) { - valueLen = CFStringGetLength(value) + 1; + valueLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), + kCFStringEncodingUTF8) + 1; valueBuffer = malloc(valueLen + 1); if ( valueBuffer && CFStringGetCString(value, valueBuffer, valueLen, kCFStringEncodingUTF8) ) valueString = valueBuffer; @@ -683,39 +863,39 @@ static void ClearOFVariable(const void *key, const void *value, void *context) // static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value) { - CFTypeRef valueRef = 0; - long cnt, cnt2, length; - unsigned long number, tmp; + CFTypeRef valueRef = 0; + long cnt, cnt2, length; + unsigned long number, tmp; - if (typeID == CFBooleanGetTypeID()) { - if (!strcmp("true", value)) valueRef = kCFBooleanTrue; - else if (!strcmp("false", value)) valueRef = kCFBooleanFalse; - } else if (typeID == CFNumberGetTypeID()) { - number = strtol(value, 0, 0); - valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, - &number); - } else if (typeID == CFStringGetTypeID()) { - valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, - kCFStringEncodingUTF8); - } else if (typeID == CFDataGetTypeID()) { - length = strlen(value); - for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) { - if (value[cnt] == '%') { - if (!ishexnumber(value[cnt + 1]) || - !ishexnumber(value[cnt + 2])) return 0; - number = toupper(value[++cnt]) - '0'; - if (number > 9) number -= 7; - tmp = toupper(value[++cnt]) - '0'; - if (tmp > 9) tmp -= 7; - number = (number << 4) + tmp; - value[cnt2] = number; - } else value[cnt2] = value[cnt]; - } - valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)value, - cnt2, kCFAllocatorDefault); - } else return 0; + if (typeID == CFBooleanGetTypeID()) { + if (!strcmp("true", value)) valueRef = kCFBooleanTrue; + else if (!strcmp("false", value)) valueRef = kCFBooleanFalse; + } else if (typeID == CFNumberGetTypeID()) { + number = strtol(value, 0, 0); + valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, + &number); + } else if (typeID == CFStringGetTypeID()) { + valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, + kCFStringEncodingUTF8); + } else if (typeID == CFDataGetTypeID()) { + length = strlen(value); + for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) { + if (value[cnt] == '%') { + if (!ishexnumber(value[cnt + 1]) || + !ishexnumber(value[cnt + 2])) return 0; + number = toupper(value[++cnt]) - '0'; + if (number > 9) number -= 7; + tmp = toupper(value[++cnt]) - '0'; + if (tmp > 9) tmp -= 7; + number = (number << 4) + tmp; + value[cnt2] = number; + } else value[cnt2] = value[cnt]; + } + valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)value, + cnt2, kCFAllocatorDefault); + } else return 0; - return valueRef; + return valueRef; } static void SetOFVariableFromFile(const void *key, const void *value, void *context) @@ -729,7 +909,8 @@ static void SetOFVariableFromFile(const void *key, const void *value, void *cont char *nameString; // Get the variable's name. - nameLen = CFStringGetLength(key) + 1; + nameLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key), + kCFStringEncodingUTF8) + 1; nameBuffer = malloc(nameLen); if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) ) nameString = nameBuffer; diff --git a/pagesize.tproj/pagesize.sh b/pagesize.tproj/pagesize.sh old mode 100755 new mode 100644 diff --git a/passwd.tproj/od_passwd.c b/passwd.tproj/od_passwd.c index 02df631..f24883a 100644 --- a/passwd.tproj/od_passwd.c +++ b/passwd.tproj/od_passwd.c @@ -102,7 +102,7 @@ od_passwd(char* uname, char* locn, char* aname) aname = strdup(uname); } - master_mode = (getuid() == 0); + master_mode = (geteuid() == 0); change_pass_on_self = (strcmp(aname, uname) == 0); if (locn) { @@ -150,10 +150,20 @@ od_passwd(char* uname, char* locn, char* aname) printf("Changing password for %s.\n", uname); + bool isSecureToken = false; + if (master_mode) { + CFArrayRef authorityValues = NULL; + authorityValues = ODRecordCopyValues(rec, kODAttributeTypeAuthenticationAuthority, &error); + if (authorityValues != NULL) { + isSecureToken = CFArrayContainsValue(authorityValues, CFRangeMake(0, CFArrayGetCount(authorityValues)), (const void *)CFSTR(";SecureToken;")); + CFRelease(authorityValues); + } + } + /* - * Prompt for password if not super-user, or if changing a remote node. + * Prompt for password if not super-user, or if changing a remote node, or if changing a record that uses SecureToken. */ - int needs_auth = (!master_mode || CFStringCompareWithOptions(location, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo); + int needs_auth = (!master_mode || CFStringCompareWithOptions(location, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo || isSecureToken); if (needs_auth) { char prompt[BUFSIZ]; @@ -166,6 +176,15 @@ od_passwd(char* uname, char* locn, char* aname) if (p) { oldpass = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8); memset(p, 0, strlen(p)); + + if (!change_pass_on_self) { + if (!ODRecordSetNodeCredentials(rec, authname, oldpass, &error)) { + show_error(error); + exit(1); + } + CFRelease(oldpass); + oldpass = NULL; + } } } diff --git a/passwd.tproj/passwd.entitlements b/passwd.tproj/passwd.entitlements new file mode 100644 index 0000000..71bd7ea --- /dev/null +++ b/passwd.tproj/passwd.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.private.opendirectoryd.identity + + + diff --git a/passwd.tproj/passwd.h b/passwd.tproj/passwd.h index 4e70b62..60109db 100644 --- a/passwd.tproj/passwd.h +++ b/passwd.tproj/passwd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016 Apple Inc. All rights reserved. + * Copyright (c) 2011-2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,14 +25,16 @@ #include #define INFO_FILE 1 -#if !TARGET_OS_EMBEDDED +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) #define INFO_NIS 2 #define INFO_OPEN_DIRECTORY 3 #define INFO_PAM 4 #endif extern int file_passwd(char *, char *); +#ifdef INFO_NIS extern int nis_passwd(char *, char *); +#endif #ifdef INFO_OPEN_DIRECTORY extern int od_passwd(char *, char *, char*); #endif diff --git a/proc_uuid_policy.tproj/proc_uuid_policy.1 b/proc_uuid_policy.tproj/proc_uuid_policy.1 index 53d8a87..f7398dd 100644 --- a/proc_uuid_policy.tproj/proc_uuid_policy.1 +++ b/proc_uuid_policy.tproj/proc_uuid_policy.1 @@ -10,7 +10,7 @@ .Nm .Ar verb .Ar policy -.Ar uuid +.Ar uuid | path .Sh DESCRIPTION .Nm sets policy for a specific UUID or mach-o file with the kernel diff --git a/proc_uuid_policy.tproj/proc_uuid_policy.c b/proc_uuid_policy.tproj/proc_uuid_policy.c index dfa8a8c..0078cb9 100644 --- a/proc_uuid_policy.tproj/proc_uuid_policy.c +++ b/proc_uuid_policy.tproj/proc_uuid_policy.c @@ -206,7 +206,7 @@ static void usage(void) { - fprintf(stderr, "usage: %s \n", getprogname()); + fprintf(stderr, "usage: %s \n", getprogname()); fprintf(stderr, "Verbs:\n"); fprintf(stderr, "\tclear\tClear all policies for a given UUID\n"); fprintf(stderr, "\tadd\tAdd a specific policy\n"); diff --git a/reboot.tproj/kextmanager.defs b/reboot.tproj/kextmanager.defs index 35544db..6ae21ce 100644 --- a/reboot.tproj/kextmanager.defs +++ b/reboot.tproj/kextmanager.defs @@ -1,5 +1,5 @@ #include -#if TARGET_OS_EMBEDDED +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) subsystem dummy 0; #else #include diff --git a/reboot.tproj/reboot.c b/reboot.tproj/reboot.c index 8d5b579..372efc7 100644 --- a/reboot.tproj/reboot.c +++ b/reboot.tproj/reboot.c @@ -61,10 +61,9 @@ __unused static const char rcsid[] = #ifdef __APPLE__ #include -#if !TARGET_OS_EMBEDDED -// Darling doesn't have any kernel extensions -//#include "KextManager.h" -//#include +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) +#include "kextmanager.h" +#include #endif #include // allocate #include // task_self, etc @@ -77,7 +76,7 @@ __unused static const char rcsid[] = void usage(void); u_int get_pageins(void); -#if defined(__APPLE__) && !TARGET_OS_EMBEDDED +#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) int reserve_reboot(void); #endif @@ -155,7 +154,7 @@ main(int argc, char *argv[]) err(1, NULL); } -#if defined(__APPLE__) && !TARGET_OS_EMBEDDED +#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) if (!qflag && !lflag) { // shutdown(8) has already checked w/kextd if ((errno = reserve_reboot())) err(1, "couldn't lock for reboot"); @@ -201,9 +200,6 @@ main(int argc, char *argv[]) utx.ut_type = SHUTDOWN_TIME; gettimeofday(&utx.ut_tv, NULL); pututxline(&utx); - - int newvalue = 1; - sysctlbyname("kern.willshutdown", NULL, NULL, &newvalue, sizeof(newvalue)); } #else logwtmp("~", "shutdown", ""); @@ -305,7 +301,7 @@ get_pageins(void) return pageins; } -#if defined(__APPLE__) && !TARGET_OS_EMBEDDED +#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) // XX this routine is also in shutdown.tproj; it would be nice to share #define WAITFORLOCK 1 @@ -315,7 +311,6 @@ get_pageins(void) int reserve_reboot(void) { - /* int rval = ELAST + 1; kern_return_t macherr = KERN_FAILURE; mach_port_t kxport, tport = MACH_PORT_NULL, myport = MACH_PORT_NULL; @@ -363,7 +358,5 @@ finish: mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1); return rval; - */ - return 0; } #endif diff --git a/sc_usage.tproj/sc_usage.c b/sc_usage.tproj/sc_usage.c index d39e8ef..d704479 100644 --- a/sc_usage.tproj/sc_usage.c +++ b/sc_usage.tproj/sc_usage.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2016 Apple Inc. All rights reserved. + * Copyright (c) 1999-2019 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -119,12 +119,12 @@ struct entry { }; struct th_info { - uintptr_t thread; + uint64_t thread; int depth; int vfslookup; int curpri; - long *pathptr; - long pathname[NUMPARMS + 1]; + int64_t *pathptr; + int64_t pathname[NUMPARMS + 1]; struct entry th_entry[MAX_NESTED]; }; @@ -291,6 +291,12 @@ print_time(char *p, unsigned int useconds, unsigned int seconds) } } +static void +resetscr(void) +{ + (void)endwin(); +} + int main(int argc, char *argv[]) { @@ -305,10 +311,12 @@ main(int argc, char *argv[]) exit(1); } - if (0 != reexec_to_match_kernel()) { +#if !defined(__arm64__) + if (0 != reexec_to_match_kernel()) { fprintf(stderr, "Could not re-execute: %d\n", errno); exit(1); } +#endif /* get our name */ if (argc > 0) { @@ -412,6 +420,7 @@ main(int argc, char *argv[]) printf("Unrecognized TERM type, try vt100\n"); exit(1); } + atexit(resetscr); cbreak(); timeout(100); noecho(); @@ -1054,7 +1063,7 @@ find_proc_names(void) } static struct th_info * -find_thread(uintptr_t thread) +find_thread(uint64_t thread) { struct th_info *ti; @@ -1120,7 +1129,7 @@ sort_scalls(void) if ((unsigned long)(((double)now - te->otime) / divisor) > 5000000) { ti->thread = 0; ti->vfslookup = 0; - ti->pathptr = (long *)NULL; + ti->pathptr = (int64_t *)NULL; ti->pathname[0] = 0; num_of_threads--; } @@ -1325,7 +1334,7 @@ sample_sc(void) th_state[i].depth = 0; th_state[i].thread = 0; th_state[i].vfslookup = 0; - th_state[i].pathptr = (long *)NULL; + th_state[i].pathptr = (int64_t *)NULL; th_state[i].pathname[0] = 0; } num_of_threads = 0; @@ -1341,7 +1350,7 @@ sample_sc(void) for (i = 0; i < count; i++) { int debugid, baseid; - uintptr_t thread; + uint64_t thread; int type, code; uint64_t now; struct th_info *ti, *switched_out, *switched_in; @@ -1361,7 +1370,7 @@ sample_sc(void) baseid = debugid & 0xffff0000; if (type == vfs_lookup) { - long *sargptr; + int64_t *sargptr; if ((ti = find_thread(thread)) == (struct th_info *)0) continue; diff --git a/shutdown.tproj/kextmanager.defs b/shutdown.tproj/kextmanager.defs index 35544db..6ae21ce 100644 --- a/shutdown.tproj/kextmanager.defs +++ b/shutdown.tproj/kextmanager.defs @@ -1,5 +1,5 @@ #include -#if TARGET_OS_EMBEDDED +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) subsystem dummy 0; #else #include diff --git a/shutdown.tproj/shutdown.c b/shutdown.tproj/shutdown.c index d6e60d6..904110c 100644 --- a/shutdown.tproj/shutdown.c +++ b/shutdown.tproj/shutdown.c @@ -78,7 +78,6 @@ __FBSDID("$FreeBSD: src/sbin/shutdown/shutdown.c,v 1.28 2005/01/25 08:40:51 delp #include #include #include -#include #include "pathnames.h" #endif /* __APPLE__ */ @@ -484,9 +483,6 @@ die_you_gravy_sucking_pig_dog() utx.ut_type = SHUTDOWN_TIME; gettimeofday(&utx.ut_tv, NULL); pututxline(&utx); - - int newvalue = 1; - sysctlbyname("kern.willshutdown", NULL, NULL, &newvalue, sizeof(newvalue)); } #else logwtmp("~", "shutdown", ""); diff --git a/stackshot.tproj/stackshot.c b/stackshot.tproj/stackshot.c new file mode 100644 index 0000000..0a67d50 --- /dev/null +++ b/stackshot.tproj/stackshot.c @@ -0,0 +1,228 @@ +/* Copyright (c) 2017 Apple Inc. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +uint64_t +stackshot_get_mach_absolute_time(void *buffer, uint32_t size) +{ + kcdata_iter_t iter = kcdata_iter_find_type(kcdata_iter(buffer, size), KCDATA_TYPE_MACH_ABSOLUTE_TIME); + if (!kcdata_iter_valid(iter) || kcdata_iter_size(iter) < sizeof(uint64_t)) { + fprintf(stderr, "bad kcdata\n"); + exit(1); + } + return *(uint64_t *)kcdata_iter_payload(iter); +} + +static void usage(char **argv) +{ + fprintf (stderr, "usage: %s [-d] [-t] >file\n", argv[0]); + fprintf (stderr, " -d : take delta stackshot\n"); + fprintf (stderr, " -b : get bootprofile\n"); + fprintf (stderr, " -c : get coalition data\n"); + fprintf (stderr, " -i : get instructions and cycles\n"); + fprintf (stderr, " -g : get thread group data\n"); + fprintf (stderr, " -s : fork a sleep process\n"); + fprintf (stderr, " -L : disable loadinfo\n"); + fprintf (stderr, " -k : active kernel threads only\n"); + fprintf (stderr, " -I : disable io statistics\n"); + fprintf (stderr, " -S : stress test: while(1) stackshot; \n"); + fprintf (stderr, " -p PID : target a pid\n"); + fprintf (stderr, " -E : grab existing kernel buffer\n"); + exit(1); +} + +void forksleep() { + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + + if (pid == 0) { + execlp("sleep", "sleep", "30", NULL); + perror("execlp"); + exit(1); + } +} + + +int main(int argc, char **argv) { + + uint32_t iostats = 0; + uint32_t active_kernel_threads_only = 0; + uint32_t bootprofile = 0; + uint32_t thread_group = 0; + uint32_t coalition = 0; + uint32_t instrs_cycles = 0; + uint32_t flags = 0; + uint32_t loadinfo = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO; + boolean_t delta = FALSE; + boolean_t sleep = FALSE; + boolean_t stress = FALSE; + pid_t pid = -1; + int c; + + while ((c = getopt(argc, argv, "SgIikbcLdtsp:E")) != EOF) { + switch(c) { + case 'I': + iostats |= STACKSHOT_NO_IO_STATS; + break; + case 'k': + active_kernel_threads_only |= STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY; + loadinfo &= ~STACKSHOT_SAVE_LOADINFO; + break; + case 'b': + bootprofile |= STACKSHOT_GET_BOOT_PROFILE; + break; + case 'c': + coalition |= STACKSHOT_SAVE_JETSAM_COALITIONS; + break; + case 'i': + instrs_cycles |= STACKSHOT_INSTRS_CYCLES; + break; + case 'L': + loadinfo = 0; + break; + case 'g': + thread_group |= STACKSHOT_THREAD_GROUP; + break; + case 'd': + delta = TRUE; + break; + case 's': + sleep = TRUE; + break; + case 'p': + pid = atoi(optarg); + break; + case 'S': + stress = TRUE; + break; + case 'E': + flags = flags | STACKSHOT_RETRIEVE_EXISTING_BUFFER; + break; + case '?': + case 'h': + default: + usage(argv); + break; + } + } + + if (thread_group && delta) { + fprintf(stderr, "stackshot does not support delta snapshots with thread groups\n"); + return 1; + } + + if (optind < argc) { + usage(argv); + } + +top: + ; + + void * config = stackshot_config_create(); + if (!config) { + perror("stackshot_config_create"); + return 1; + } + flags = flags | loadinfo | STACKSHOT_SAVE_IMP_DONATION_PIDS | STACKSHOT_GET_DQ | STACKSHOT_KCDATA_FORMAT | STACKSHOT_THREAD_WAITINFO | + bootprofile | active_kernel_threads_only | iostats | thread_group | coalition | instrs_cycles; + + int err = stackshot_config_set_flags(config, flags); + if (err != 0) { + perror("stackshot_config_set_flags"); + return 1; + } + + if (pid != -1) { + int err = stackshot_config_set_pid(config, pid); + if (err != 0) { + perror("stackshot_config_set_flags"); + return 1; + } + } + + err = stackshot_capture_with_config(config); + if (err != 0) { + perror("stackshot_capture_with_config"); + return 1; + } + + void *buf = stackshot_config_get_stackshot_buffer(config); + if (!buf) { + perror("stackshot_config_get_stackshot_buffer"); + return 1; + } + + uint32_t size = stackshot_config_get_stackshot_size(config); + + if (delta) { + // output the original somewhere? + + uint64_t time = stackshot_get_mach_absolute_time(buf, size); + + err = stackshot_config_dealloc_buffer(config); + assert(!err); + + flags |= STACKSHOT_COLLECT_DELTA_SNAPSHOT; + int err = stackshot_config_set_flags(config, flags); + if (err != 0) { + perror("stackshot_config_set_flags"); + return 1; + } + + err = stackshot_config_set_delta_timestamp(config, time); + if (err != 0) { + perror("stackshot_config_delta_timestamp"); + return 1; + } + + if (sleep) { + forksleep(); + } + usleep(10000); + + err = stackshot_capture_with_config(config); + if (err != 0) { + perror("stackshot_capture_with_config"); + return 1; + } + + buf = stackshot_config_get_stackshot_buffer(config); + if (!buf) { + perror("stackshot_config_get_stackshot_buffer"); + return 1; + } + + size = stackshot_config_get_stackshot_size(config); + + + } + + + if (stress) { + if (config) { + stackshot_config_dealloc(config); + config = NULL; + } + goto top; + } + + fwrite(buf, size, 1, stdout); +} diff --git a/sysctl.tproj/sysctl.c b/sysctl.tproj/sysctl.c index 30b066b..606459a 100644 --- a/sysctl.tproj/sysctl.c +++ b/sysctl.tproj/sysctl.c @@ -680,6 +680,9 @@ oidfmt(int *oid, int len, char *fmt, u_int *kind) } } else if (buf[sizeof(u_int)] == 'L') { *kind = (*kind & ~CTLTYPE) | CTLTYPE_LONG; + if (buf[sizeof(u_int)+1] == 'U') { + *kind = (*kind & ~CTLTYPE) | CTLTYPE_ULONG; + } } break; case CTLTYPE_QUAD: diff --git a/system_cmds.xcodeproj/project.pbxproj b/system_cmds.xcodeproj/project.pbxproj index de9e5df..f52edfb 100644 --- a/system_cmds.xcodeproj/project.pbxproj +++ b/system_cmds.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 1812F1ED1C8F923900F3DC9E /* CopyFiles */, ); dependencies = ( + 358407D62245AD55006A0D8E /* PBXTargetDependency */, + 926913A61EC706130079D787 /* PBXTargetDependency */, 1812F18D1C8F923900F3DC9E /* PBXTargetDependency */, 1812F18F1C8F923900F3DC9E /* PBXTargetDependency */, 1812F1911C8F923900F3DC9E /* PBXTargetDependency */, @@ -26,6 +28,7 @@ 1812F1A31C8F923900F3DC9E /* PBXTargetDependency */, 1812F1A51C8F923900F3DC9E /* PBXTargetDependency */, 1812F1A71C8F923900F3DC9E /* PBXTargetDependency */, + F2291F681FFEBC4F00161936 /* PBXTargetDependency */, 1812F1A91C8F923900F3DC9E /* PBXTargetDependency */, 1812F1AB1C8F923900F3DC9E /* PBXTargetDependency */, 1812F1AD1C8F923900F3DC9E /* PBXTargetDependency */, @@ -70,6 +73,9 @@ C9D64CD21B91066B00CFA43B /* CopyFiles */, ); dependencies = ( + 358407D22245AD40006A0D8E /* PBXTargetDependency */, + 926913A21EC706010079D787 /* PBXTargetDependency */, + 08CE3D361E6E24CC00DF1B78 /* PBXTargetDependency */, C21481471C1A1447003BCA63 /* PBXTargetDependency */, 78DE9DED1B5048D400FE6DF5 /* PBXTargetDependency */, 97999D351AE84D3A00E8B10F /* PBXTargetDependency */, @@ -83,6 +89,7 @@ 1523FE6F1595069900661E82 /* PBXTargetDependency */, BA0A861A1396B41F00D2272C /* PBXTargetDependency */, BA0A861613968ECA00D2272C /* PBXTargetDependency */, + F2291F641FFEBC4000161936 /* PBXTargetDependency */, BA959E8813968D8A00CA9C60 /* PBXTargetDependency */, BA959E8A13968D8A00CA9C60 /* PBXTargetDependency */, BA959E8C13968D8A00CA9C60 /* PBXTargetDependency */, @@ -224,6 +231,9 @@ C9D64CD01B91064700CFA43B /* CopyFiles */, ); dependencies = ( + 358407D42245AD4B006A0D8E /* PBXTargetDependency */, + 926913A41EC706080079D787 /* PBXTargetDependency */, + 08CE3D381E6E24DF00DF1B78 /* PBXTargetDependency */, C21481491C1A14AD003BCA63 /* PBXTargetDependency */, 78DE9DFA1B504D1200FE6DF5 /* PBXTargetDependency */, 97999D371AE84D4100E8B10F /* PBXTargetDependency */, @@ -236,6 +246,7 @@ 1523FE711595069F00661E82 /* PBXTargetDependency */, BA0A861C1396B42600D2272C /* PBXTargetDependency */, BA0A861813968ED500D2272C /* PBXTargetDependency */, + F2291F661FFEBC4700161936 /* PBXTargetDependency */, BA959E9213968DA900CA9C60 /* PBXTargetDependency */, BA959E9413968DA900CA9C60 /* PBXTargetDependency */, BA959E9613968DA900CA9C60 /* PBXTargetDependency */, @@ -269,6 +280,7 @@ BACC1D341377B58A007728F4 /* PBXTargetDependency */, BACC1D361377B58A007728F4 /* PBXTargetDependency */, BACC1D3A1377B58A007728F4 /* PBXTargetDependency */, + 8E556A4B1D3FF7A40038D48B /* PBXTargetDependency */, ); name = All_iOS; productName = All_iOS; @@ -300,10 +312,17 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 08ADC98C1E70715D0001CB70 /* ktrace.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08ADC98B1E70715D0001CB70 /* ktrace.framework */; }; + 08CE3D341E6E22F600DF1B78 /* stackshot.c in Sources */ = {isa = PBXBuildFile; fileRef = 08CE3D321E6E22DE00DF1B78 /* stackshot.c */; }; 08DC488E1A12C2D6008AAF38 /* kpgo.c in Sources */ = {isa = PBXBuildFile; fileRef = 08DC488D1A12C2C6008AAF38 /* kpgo.c */; }; + 0D06BC661E8F091F00C6EC2D /* mslutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 0D06BC651E8F091F00C6EC2D /* mslutil.c */; }; 1523FE6C1595056C00661E82 /* ltop.c in Sources */ = {isa = PBXBuildFile; fileRef = 1523FE6B1595056C00661E82 /* ltop.c */; }; 1523FE6D1595058100661E82 /* ltop.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1523FE6A1595056C00661E82 /* ltop.1 */; }; 1812F1EE1C8F923900F3DC9E /* system_cmds.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = C9D64CCF1B91063200CFA43B /* system_cmds.plist */; }; + 358407CE2245AC09006A0D8E /* cpuctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 358407CD2245AC09006A0D8E /* cpuctl.c */; }; + 358407D02245AC32006A0D8E /* cpuctl.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 358407CF2245AC32006A0D8E /* cpuctl.8 */; }; + 5262264121E8295A0053ABA1 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 189337C21CC7CB4800B2A6A4 /* CoreFoundation.framework */; }; + 5262264221ED5A780053ABA1 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9E0691D1C58BDB800C956EB /* IOKit.framework */; }; 550C19E61804D226001DA380 /* libutil.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BA4B7A091373BA4600003422 /* libutil.dylib */; }; 550C19EC1804D281001DA380 /* iosim.c in Sources */ = {isa = PBXBuildFile; fileRef = 550C19E11804C55E001DA380 /* iosim.c */; }; 550C19ED1804D295001DA380 /* iosim.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 550C19E01804C55E001DA380 /* iosim.1 */; }; @@ -315,9 +334,7 @@ 78DE9DFE1B504D7F00FE6DF5 /* wait4path.c in Sources */ = {isa = PBXBuildFile; fileRef = 78DE9DFC1B504D7F00FE6DF5 /* wait4path.c */; }; 78DE9E001B504DE500FE6DF5 /* wait4path.version in Sources */ = {isa = PBXBuildFile; fileRef = 78DE9DFD1B504D7F00FE6DF5 /* wait4path.version */; }; 78DE9EE61B505F1800FE6DF5 /* wait4path.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 78DE9EE51B505EBF00FE6DF5 /* wait4path.1 */; }; - 8EC391681C973400001E28E6 /* proc_uuid_policy.c in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8EC391671C973400001E28E6 /* proc_uuid_policy.c */; }; 8EC391691C973405001E28E6 /* proc_uuid_policy.c in Sources */ = {isa = PBXBuildFile; fileRef = 8EC391671C973400001E28E6 /* proc_uuid_policy.c */; }; - 8EC3916B1C97341E001E28E6 /* proc_uuid_policy.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8EC3916A1C97341E001E28E6 /* proc_uuid_policy.1 */; }; 8EC3916C1C973429001E28E6 /* proc_uuid_policy.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8EC3916A1C97341E001E28E6 /* proc_uuid_policy.1 */; }; 97999D321AE84CE400E8B10F /* lskq.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 97999D301AE84C7600E8B10F /* lskq.1 */; }; 97999D331AE84D0A00E8B10F /* lskq.c in Sources */ = {isa = PBXBuildFile; fileRef = 97999D311AE84C7600E8B10F /* lskq.c */; }; @@ -492,19 +509,25 @@ C21481401C1A122B003BCA63 /* threads.c in Sources */ = {isa = PBXBuildFile; fileRef = C214811A1C1A11E7003BCA63 /* threads.c */; }; C21481451C1A131D003BCA63 /* gcore.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C21481131C1A11E6003BCA63 /* gcore.1 */; }; C248DBB01C1A1D0500F6E9AF /* libcompression.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C248DBAF1C1A1D0500F6E9AF /* libcompression.dylib */; }; + C2DAA94F1D9F22F000FAC263 /* convert.c in Sources */ = {isa = PBXBuildFile; fileRef = C2DAA94B1D9F22BF00FAC263 /* convert.c */; }; C625B28B16D6F27E00168EF7 /* taskpolicy.c in Sources */ = {isa = PBXBuildFile; fileRef = C625B28A16D6F27E00168EF7 /* taskpolicy.c */; }; C625B28D16D6F27E00168EF7 /* taskpolicy.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C625B28C16D6F27E00168EF7 /* taskpolicy.8 */; }; C65BF57A144BD7C5009028A3 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA9B766D13739D27001BB39F /* CoreFoundation.framework */; }; C96F50B215BDCEC3008682F7 /* libutil.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BA4B7A091373BA4600003422 /* libutil.dylib */; }; C96F50BD15BDFEFB008682F7 /* lsmp.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C96F50AC15BDCBF0008682F7 /* lsmp.1 */; }; C96F50BE15BDFF03008682F7 /* lsmp.c in Sources */ = {isa = PBXBuildFile; fileRef = C96F50AD15BDCE8E008682F7 /* lsmp.c */; }; - C97199F21C5206DE006D9758 /* libktrace.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C97199F11C5206DE006D9758 /* libktrace.dylib */; }; C9779F6E159A2A0C009436FD /* libutil.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BA4B7A091373BA4600003422 /* libutil.dylib */; }; + C99490E52090F56F00246D9D /* zprint.lua in CopyFiles */ = {isa = PBXBuildFile; fileRef = C99490E32090F55D00246D9D /* zprint.lua */; }; C9D64CD11B91065D00CFA43B /* system_cmds.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = C9D64CCF1B91063200CFA43B /* system_cmds.plist */; }; C9D64CD31B91067500CFA43B /* system_cmds.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = C9D64CCF1B91063200CFA43B /* system_cmds.plist */; }; C9E0691A1C58BD7E00C956EB /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA9B766D13739D27001BB39F /* CoreFoundation.framework */; }; C9E0691C1C58BDA000C956EB /* CoreSymbolication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9E0691B1C58BDA000C956EB /* CoreSymbolication.framework */; }; C9E0691E1C58BDB800C956EB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9E0691D1C58BDB800C956EB /* IOKit.framework */; }; + F2291F551FFEBB6A00161936 /* CoreSymbolication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9E0691B1C58BDA000C956EB /* CoreSymbolication.framework */; }; + F2291F601FFEBB9E00161936 /* zlog.c in Sources */ = {isa = PBXBuildFile; fileRef = F2291F5F1FFEBB9E00161936 /* zlog.c */; }; + F27B70282044CB40003C04FC /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F27B70272044CB40003C04FC /* CoreFoundation.framework */; }; + F27B702B2045038B003C04FC /* SymbolicationHelper.c in Sources */ = {isa = PBXBuildFile; fileRef = F27B70292045038B003C04FC /* SymbolicationHelper.c */; }; + F29F5A5F203E5347005B0099 /* zlog.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F29F5A5C203E4403005B0099 /* zlog.1 */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -522,6 +545,20 @@ /* End PBXBuildRule section */ /* Begin PBXContainerItemProxy section */ + 08CE3D351E6E24CC00DF1B78 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 08CE3D281E6E22A200DF1B78; + remoteInfo = stackshot; + }; + 08CE3D371E6E24DF00DF1B78 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 08CE3D281E6E22A200DF1B78; + remoteInfo = stackshot; + }; 08DC488F1A12C6F0008AAF38 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BA2DE9181372FA9100D1913C /* Project object */; @@ -865,6 +902,27 @@ remoteGlobalIDString = BACC1D0D1377B481007728F4; remoteInfo = mean; }; + 358407D12245AD40006A0D8E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3521C84C2245AA92001B3201; + remoteInfo = cpuctl; + }; + 358407D32245AD4B006A0D8E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3521C84C2245AA92001B3201; + remoteInfo = cpuctl; + }; + 358407D52245AD55006A0D8E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3521C84C2245AA92001B3201; + remoteInfo = cpuctl; + }; 550C19EE1804D2AD001DA380 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BA2DE9181372FA9100D1913C /* Project object */; @@ -907,6 +965,13 @@ remoteGlobalIDString = 78DE9DDF1B5045DE00FE6DF5; remoteInfo = wait4path; }; + 8E556A4A1D3FF7A40038D48B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8EC3915B1C9733C2001E28E6; + remoteInfo = proc_uuid_policy; + }; 8EC3916D1C973440001E28E6 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BA2DE9181372FA9100D1913C /* Project object */; @@ -914,6 +979,27 @@ remoteGlobalIDString = 8EC3915B1C9733C2001E28E6; remoteInfo = proc_uuid_policy; }; + 926913A11EC706010079D787 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0D06BC5D1E8F08CB00C6EC2D; + remoteInfo = mslutil; + }; + 926913A31EC706080079D787 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0D06BC5D1E8F08CB00C6EC2D; + remoteInfo = mslutil; + }; + 926913A51EC706130079D787 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0D06BC5D1E8F08CB00C6EC2D; + remoteInfo = mslutil; + }; 97999D341AE84D3A00E8B10F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BA2DE9181372FA9100D1913C /* Project object */; @@ -1600,9 +1686,39 @@ remoteGlobalIDString = C96F50AE15BDCEC3008682F7; remoteInfo = lsmp; }; + F2291F631FFEBC4000161936 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = F2291F501FFEBB6A00161936; + remoteInfo = zlog; + }; + F2291F651FFEBC4700161936 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = F2291F501FFEBB6A00161936; + remoteInfo = zlog; + }; + F2291F671FFEBC4F00161936 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BA2DE9181372FA9100D1913C /* Project object */; + proxyType = 1; + remoteGlobalIDString = F2291F501FFEBB6A00161936; + remoteInfo = zlog; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 08CE3D271E6E22A200DF1B78 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 08DC48831A12C21B008AAF38 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1612,6 +1728,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 0D06BC5C1E8F08CB00C6EC2D /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 1523FE5F1595048900661E82 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1632,6 +1757,16 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 3521C84B2245AA92001B3201 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 358407D02245AC32006A0D8E /* cpuctl.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; 550C19E71804D226001DA380 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -2175,6 +2310,16 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + C99490E22090F53B00246D9D /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/share/recon; + dstSubfolderSpec = 0; + files = ( + C99490E52090F56F00246D9D /* zprint.lua in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; C9D64CD01B91064700CFA43B /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -2191,168 +2336,40 @@ dstPath = /AppleInternal/CoreOS/BATS/unit_tests; dstSubfolderSpec = 0; files = ( - 8EC391681C973400001E28E6 /* proc_uuid_policy.c in CopyFiles */, C9D64CD31B91067500CFA43B /* system_cmds.plist in CopyFiles */, - 8EC3916B1C97341E001E28E6 /* proc_uuid_policy.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + F29F5A5E203E532B005B0099 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F29F5A5F203E5347005B0099 /* zlog.1 in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 08ADC98B1E70715D0001CB70 /* ktrace.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ktrace.framework; path = System/Library/PrivateFrameworks/ktrace.framework; sourceTree = SDKROOT; }; + 08CE3D291E6E22A200DF1B78 /* stackshot */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = stackshot; sourceTree = BUILT_PRODUCTS_DIR; }; + 08CE3D321E6E22DE00DF1B78 /* stackshot.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = stackshot.c; path = stackshot.tproj/stackshot.c; sourceTree = ""; }; 08DC48851A12C21B008AAF38 /* kpgo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = kpgo; sourceTree = BUILT_PRODUCTS_DIR; }; 08DC488D1A12C2C6008AAF38 /* kpgo.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = kpgo.c; sourceTree = ""; }; + 0D06BC5E1E8F08CB00C6EC2D /* mslutil */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mslutil; sourceTree = BUILT_PRODUCTS_DIR; }; + 0D06BC651E8F091F00C6EC2D /* mslutil.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mslutil.c; sourceTree = ""; }; + 0D06BC671E8F0B4100C6EC2D /* mslutil.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = mslutil.1; sourceTree = ""; }; 1523FE631595048900661E82 /* ltop */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ltop; sourceTree = BUILT_PRODUCTS_DIR; }; 1523FE6A1595056C00661E82 /* ltop.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = ltop.1; sourceTree = ""; }; 1523FE6B1595056C00661E82 /* ltop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ltop.c; sourceTree = ""; }; - 1821B00B1C88BEE2000BAA0C /* usage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = usage.cpp; sourceTree = ""; }; - 1821B00C1C88BEE2000BAA0C /* usage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = usage.hpp; sourceTree = ""; }; - 18380CDC1C596E8900DC6B89 /* eostrace.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = eostrace.c; sourceTree = ""; }; - 18410FED1CC881FD00385C96 /* entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = entitlements.plist; sourceTree = ""; }; - 1845E41418EB95810010F451 /* TraceFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TraceFile.cpp; sourceTree = ""; }; - 1845E41518EB95810010F451 /* TraceFile.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TraceFile.hpp; sourceTree = ""; }; - 18597EC818CBC2A300531A50 /* kdprof.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = kdprof.1; sourceTree = ""; }; - 18597ECC18CBC35700531A50 /* Action.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Action.hpp; sourceTree = ""; }; - 18597ECD18CBC35700531A50 /* CollectAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CollectAction.hpp; sourceTree = ""; }; - 18597ECE18CBC35700531A50 /* CollectAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CollectAction.cpp; sourceTree = ""; }; - 18597ECF18CBC35700531A50 /* DisableAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DisableAction.hpp; sourceTree = ""; }; - 18597ED018CBC35700531A50 /* DisableAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DisableAction.cpp; sourceTree = ""; }; - 18597ED118CBC35700531A50 /* EnableAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EnableAction.hpp; sourceTree = ""; }; - 18597ED218CBC35700531A50 /* EnableAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EnableAction.cpp; sourceTree = ""; }; - 18597ED318CBC35700531A50 /* EventPrinting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EventPrinting.hpp; sourceTree = ""; }; - 18597ED418CBC35700531A50 /* EventPrinting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventPrinting.cpp; sourceTree = ""; }; - 18597ED518CBC35700531A50 /* global.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = global.h; sourceTree = ""; }; - 18597ED618CBC35700531A50 /* Globals.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Globals.hpp; sourceTree = ""; }; - 18597ED718CBC35700531A50 /* Globals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Globals.cpp; sourceTree = ""; }; - 18597ED818CBC35700531A50 /* InitializeAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = InitializeAction.hpp; sourceTree = ""; }; - 18597ED918CBC35700531A50 /* InitializeAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InitializeAction.cpp; sourceTree = ""; }; - 18597EDA18CBC35700531A50 /* kdprof.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = kdprof.cpp; sourceTree = ""; }; - 18597EDB18CBC35700531A50 /* NoWrapAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NoWrapAction.hpp; sourceTree = ""; }; - 18597EDC18CBC35700531A50 /* NoWrapAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NoWrapAction.cpp; sourceTree = ""; }; - 18597EDD18CBC35700531A50 /* PrintStateAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PrintStateAction.hpp; sourceTree = ""; }; - 18597EDE18CBC35700531A50 /* PrintStateAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrintStateAction.cpp; sourceTree = ""; }; - 18597EDF18CBC35700531A50 /* RemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RemoveAction.hpp; sourceTree = ""; }; - 18597EE018CBC35700531A50 /* RemoveAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RemoveAction.cpp; sourceTree = ""; }; - 18597EE118CBC35700531A50 /* SaveTraceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SaveTraceAction.hpp; sourceTree = ""; }; - 18597EE218CBC35700531A50 /* SaveTraceAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SaveTraceAction.cpp; sourceTree = ""; }; - 18597EE318CBC35700531A50 /* SleepAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SleepAction.hpp; sourceTree = ""; }; - 18597EE418CBC35700531A50 /* SleepAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SleepAction.cpp; sourceTree = ""; }; - 18597EE518CBC35700531A50 /* SummaryPrinting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SummaryPrinting.hpp; sourceTree = ""; }; - 18597EE618CBC35700531A50 /* SummaryPrinting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SummaryPrinting.cpp; sourceTree = ""; }; - 18597EE718CBC35700531A50 /* TraceFileAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TraceFileAction.hpp; sourceTree = ""; }; - 18597EE818CBC35700531A50 /* TraceFileAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TraceFileAction.cpp; sourceTree = ""; }; - 185B9771191022B200FCB84C /* VoucherContentSysctl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VoucherContentSysctl.cpp; sourceTree = ""; }; - 185B97731910475500FCB84C /* WriteTraceFileAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WriteTraceFileAction.cpp; sourceTree = ""; }; - 185B97741910475500FCB84C /* WriteTraceFileAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WriteTraceFileAction.hpp; sourceTree = ""; }; - 185F75141CC2EFC600B0EA9E /* TypeFilterAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TypeFilterAction.cpp; sourceTree = ""; }; - 185F75151CC2EFC600B0EA9E /* TypeFilterAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TypeFilterAction.hpp; sourceTree = ""; }; - 185F75171CC337D400B0EA9E /* Machine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Machine.cpp; sourceTree = ""; }; - 185F75191CC34EBE00B0EA9E /* WriteTraceFileAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WriteTraceFileAction.cpp; sourceTree = ""; }; - 185F751A1CC34EBE00B0EA9E /* WriteTraceFileAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WriteTraceFileAction.hpp; sourceTree = ""; }; - 18624C851BF6400200FA0575 /* TypeSummary.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TypeSummary.hpp; sourceTree = ""; }; - 18624C861BF64ED700FA0575 /* CPUTypeSummary.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CPUTypeSummary.hpp; sourceTree = ""; }; - 18624C871BF6503600FA0575 /* ProcessTypeSummary.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ProcessTypeSummary.hpp; sourceTree = ""; }; - 18624C881BF651EC00FA0575 /* ThreadTypeSummary.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ThreadTypeSummary.hpp; sourceTree = ""; }; - 186288CF1CC5DA71002950E0 /* UUIDMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UUIDMap.cpp; sourceTree = ""; }; - 186288D01CC5DA71002950E0 /* UUIDMap.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UUIDMap.hpp; sourceTree = ""; }; - 1865513A18CA70B5003B92A7 /* CPPUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPPUtil.h; sourceTree = ""; }; - 1865513B18CA70B5003B92A7 /* UtilAbsInterval.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilAbsInterval.cpp; sourceTree = ""; }; - 1865513C18CA70B5003B92A7 /* UtilAbsInterval.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilAbsInterval.hpp; sourceTree = ""; }; - 1865513D18CA70B5003B92A7 /* UtilAbsTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilAbsTime.cpp; sourceTree = ""; }; - 1865513E18CA70B5003B92A7 /* UtilAbsTime.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilAbsTime.hpp; sourceTree = ""; }; - 1865513F18CA70B5003B92A7 /* UtilAssert.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilAssert.hpp; sourceTree = ""; }; - 1865514018CA70B5003B92A7 /* UtilBase.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilBase.hpp; sourceTree = ""; }; - 1865514118CA70B5003B92A7 /* UtilException.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilException.hpp; sourceTree = ""; }; - 1865514218CA70B5003B92A7 /* UtilFileDescriptor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilFileDescriptor.hpp; sourceTree = ""; }; - 1865514318CA70B5003B92A7 /* UtilLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilLog.cpp; sourceTree = ""; }; - 1865514418CA70B5003B92A7 /* UtilLog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilLog.hpp; sourceTree = ""; }; - 1865514518CA70B5003B92A7 /* UtilMakeUnique.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilMakeUnique.hpp; sourceTree = ""; }; - 1865514618CA70B5003B92A7 /* UtilMappedFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilMappedFile.cpp; sourceTree = ""; }; - 1865514718CA70B5003B92A7 /* UtilMappedFile.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilMappedFile.hpp; sourceTree = ""; }; - 1865514818CA70B5003B92A7 /* UtilMemoryBuffer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilMemoryBuffer.hpp; sourceTree = ""; }; - 1865514918CA70B5003B92A7 /* UtilNanoInterval.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilNanoInterval.hpp; sourceTree = ""; }; - 1865514A18CA70B5003B92A7 /* UtilNanoTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilNanoTime.cpp; sourceTree = ""; }; - 1865514B18CA70B5003B92A7 /* UtilNanoTime.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilNanoTime.hpp; sourceTree = ""; }; - 1865514C18CA70B5003B92A7 /* UtilPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilPath.cpp; sourceTree = ""; }; - 1865514D18CA70B5003B92A7 /* UtilPath.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilPath.hpp; sourceTree = ""; }; - 1865514E18CA70B5003B92A7 /* UtilPrettyPrinting.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilPrettyPrinting.cpp; sourceTree = ""; }; - 1865514F18CA70B5003B92A7 /* UtilPrettyPrinting.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilPrettyPrinting.hpp; sourceTree = ""; }; - 1865515018CA70B5003B92A7 /* UtilString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilString.cpp; sourceTree = ""; }; - 1865515118CA70B5003B92A7 /* UtilString.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilString.hpp; sourceTree = ""; }; - 1865515218CA70B5003B92A7 /* UtilTerminalColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilTerminalColor.cpp; sourceTree = ""; }; - 1865515318CA70B5003B92A7 /* UtilTerminalColor.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilTerminalColor.hpp; sourceTree = ""; }; - 1865515418CA70B5003B92A7 /* UtilTime.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilTime.hpp; sourceTree = ""; }; - 1865515518CA70B5003B92A7 /* UtilTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtilTimer.cpp; sourceTree = ""; }; - 1865515618CA70B5003B92A7 /* UtilTimer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilTimer.hpp; sourceTree = ""; }; - 1865515718CA70B5003B92A7 /* UtilTRange.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilTRange.hpp; sourceTree = ""; }; - 1865515818CA70B5003B92A7 /* UtilTRangeValue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UtilTRangeValue.hpp; sourceTree = ""; }; - 1865518018CA7104003B92A7 /* msa.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = msa.1; sourceTree = ""; }; - 1865518C18CA72F7003B92A7 /* Action.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Action.hpp; sourceTree = ""; }; - 1865518E18CA72F7003B92A7 /* EventProcessing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EventProcessing.hpp; sourceTree = ""; }; - 1865518F18CA72F7003B92A7 /* EventRingBuffer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = EventRingBuffer.hpp; sourceTree = ""; }; - 1865519018CA72F7003B92A7 /* global.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = global.h; sourceTree = ""; }; - 1865519118CA72F7003B92A7 /* Globals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Globals.cpp; sourceTree = ""; }; - 1865519218CA72F7003B92A7 /* Globals.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Globals.hpp; sourceTree = ""; }; - 1865519418CA72F7003B92A7 /* LiveTraceAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LiveTraceAction.cpp; sourceTree = ""; }; - 1865519518CA72F7003B92A7 /* LiveTraceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LiveTraceAction.hpp; sourceTree = ""; }; - 1865519618CA72F7003B92A7 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; - 1865519718CA72F7003B92A7 /* Printing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Printing.cpp; sourceTree = ""; }; - 1865519818CA72F7003B92A7 /* Printing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Printing.hpp; sourceTree = ""; }; - 1865519918CA72F7003B92A7 /* ReadTraceFileAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReadTraceFileAction.cpp; sourceTree = ""; }; - 1865519A18CA72F7003B92A7 /* ReadTraceFileAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ReadTraceFileAction.hpp; sourceTree = ""; }; - 1873F11E1CC52204008950A8 /* BinaryDataTraceEncoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDataTraceEncoder.hpp; sourceTree = ""; }; - 1873F1211CC551DD008950A8 /* DeviceMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DeviceMap.cpp; sourceTree = ""; }; - 1873F1221CC551DD008950A8 /* DeviceMap.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DeviceMap.hpp; sourceTree = ""; }; - 1873F1261CC587E2008950A8 /* BinaryDataTraceDecoder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDataTraceDecoder.hpp; sourceTree = ""; }; - 1875DF421C443513004AC849 /* KDState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KDState.cpp; sourceTree = ""; }; - 1888DB65191A8A4400A0541E /* PrintBuffer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PrintBuffer.hpp; sourceTree = ""; }; - 188959FC1958D287004576E8 /* TaskRequestedPolicy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TaskRequestedPolicy.hpp; sourceTree = ""; }; 189337C21CC7CB4800B2A6A4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; - 189337C41CC7DA8700B2A6A4 /* UtilCoreSymbolication.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = UtilCoreSymbolication.hpp; sourceTree = ""; }; - 189632961C5FD38E00FA9646 /* eostrace.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = eostrace.h; sourceTree = ""; }; - 18C65BAD1CC1868D005194F4 /* TypeFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TypeFilter.cpp; sourceTree = ""; }; - 18C65BAE1CC1868D005194F4 /* TypeFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TypeFilter.hpp; sourceTree = ""; }; - 18C8728718EA128B00F86DD9 /* CPUActivity.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPUActivity.hpp; sourceTree = ""; }; - 18C8728818EA128B00F86DD9 /* CPUSummary.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPUSummary.hpp; sourceTree = ""; }; - 18C8728918EA128B00F86DD9 /* IOActivity.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = IOActivity.hpp; sourceTree = ""; }; - 18C8728A18EA128B00F86DD9 /* KDBG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KDBG.cpp; sourceTree = ""; }; - 18C8728B18EA128B00F86DD9 /* KDBG.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KDBG.hpp; sourceTree = ""; }; - 18C8728C18EA128B00F86DD9 /* KDCPUMapEntry.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KDCPUMapEntry.hpp; sourceTree = ""; }; - 18C8728D18EA128B00F86DD9 /* KDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KDebug.h; sourceTree = ""; }; - 18C8728E18EA128B00F86DD9 /* KDEvent.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KDEvent.hpp; sourceTree = ""; }; - 18C8728F18EA128B00F86DD9 /* KDState.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KDState.hpp; sourceTree = ""; }; - 18C8729018EA128B00F86DD9 /* KDThreadMapEntry.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KDThreadMapEntry.hpp; sourceTree = ""; }; - 18C8729118EA128B00F86DD9 /* Kernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Kernel.cpp; sourceTree = ""; }; - 18C8729218EA128B00F86DD9 /* Kernel.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Kernel.hpp; sourceTree = ""; }; - 18C8729318EA128B00F86DD9 /* Machine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Machine.hpp; sourceTree = ""; }; - 18C8729418EA128B00F86DD9 /* Machine.impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Machine.impl.hpp; sourceTree = ""; }; - 18C8729518EA128B00F86DD9 /* Machine.mutable-impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "Machine.mutable-impl.hpp"; sourceTree = ""; }; - 18C8729618EA128B00F86DD9 /* MachineCPU.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachineCPU.hpp; sourceTree = ""; }; - 18C8729718EA128B00F86DD9 /* MachineCPU.impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachineCPU.impl.hpp; sourceTree = ""; }; - 18C8729818EA128B00F86DD9 /* MachineCPU.mutable-impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "MachineCPU.mutable-impl.hpp"; sourceTree = ""; }; - 18C8729918EA128B00F86DD9 /* MachineMachMsg.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachineMachMsg.hpp; sourceTree = ""; }; - 18C8729A18EA128B00F86DD9 /* MachineProcess.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachineProcess.hpp; sourceTree = ""; }; - 18C8729B18EA128B00F86DD9 /* MachineProcess.impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachineProcess.impl.hpp; sourceTree = ""; }; - 18C8729C18EA128B00F86DD9 /* MachineProcess.mutable-impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "MachineProcess.mutable-impl.hpp"; sourceTree = ""; }; - 18C8729D18EA128B00F86DD9 /* MachineThread.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachineThread.hpp; sourceTree = ""; }; - 18C8729E18EA128B00F86DD9 /* MachineThread.impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachineThread.impl.hpp; sourceTree = ""; }; - 18C8729F18EA128B00F86DD9 /* MachineThread.mutable-impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "MachineThread.mutable-impl.hpp"; sourceTree = ""; }; - 18C872A018EA128B00F86DD9 /* MachineVoucher.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachineVoucher.hpp; sourceTree = ""; }; - 18C872A118EA128B00F86DD9 /* MetaTypes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MetaTypes.hpp; sourceTree = ""; }; - 18C872A218EA128B00F86DD9 /* NurseryMachMsg.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NurseryMachMsg.hpp; sourceTree = ""; }; - 18C872A318EA128B00F86DD9 /* ProcessSummary.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ProcessSummary.hpp; sourceTree = ""; }; - 18C872A418EA128B00F86DD9 /* ThreadSummary.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ThreadSummary.hpp; sourceTree = ""; }; - 18C872A518EA128B00F86DD9 /* TraceCodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TraceCodes.cpp; sourceTree = ""; }; - 18C872A618EA128B00F86DD9 /* TraceCodes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TraceCodes.hpp; sourceTree = ""; }; - 18C872A718EA128B00F86DD9 /* TraceDataHeader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TraceDataHeader.hpp; sourceTree = ""; }; - 18C872A818EA128B00F86DD9 /* VoucherInterval.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VoucherInterval.hpp; sourceTree = ""; }; - 18D0E13119101CD200F93974 /* VoucherContentSysctl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VoucherContentSysctl.hpp; sourceTree = ""; }; - 18D6F9AF1C5A8EF000F472A7 /* eostraced.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = eostraced.c; sourceTree = ""; }; - 18D6F9B11C5A8EFB00F472A7 /* com.apple.eostraced.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.eostraced.plist; sourceTree = ""; }; - 18D8B66519535B92008847DF /* TaskEffectivePolicy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TaskEffectivePolicy.hpp; sourceTree = ""; }; 18EA07101C99C76C006D3005 /* EmbeddedOSSupportHost.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EmbeddedOSSupportHost.framework; path = System/Library/PrivateFrameworks/EmbeddedOSSupportHost.framework; sourceTree = SDKROOT; }; - 18EA07121C99E4E3006D3005 /* copy_of_AppleEmbeddedOSSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = copy_of_AppleEmbeddedOSSupport.c; path = eostrace/copy_of_AppleEmbeddedOSSupport.c; sourceTree = SOURCE_ROOT; }; - 18EA07131C99E4E3006D3005 /* copy_of_AppleEmbeddedOSSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = copy_of_AppleEmbeddedOSSupport.h; path = eostrace/copy_of_AppleEmbeddedOSSupport.h; sourceTree = SOURCE_ROOT; }; + 3521C84D2245AA92001B3201 /* cpuctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cpuctl; sourceTree = BUILT_PRODUCTS_DIR; }; + 3521C8562245AB52001B3201 /* cpuctl.tproj */ = {isa = PBXFileReference; lastKnownFileType = folder; path = cpuctl.tproj; sourceTree = ""; }; + 358407CD2245AC09006A0D8E /* cpuctl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = cpuctl.c; path = cpuctl.tproj/cpuctl.c; sourceTree = ""; }; + 358407CF2245AC32006A0D8E /* cpuctl.8 */ = {isa = PBXFileReference; lastKnownFileType = text; name = cpuctl.8; path = cpuctl.tproj/cpuctl.8; sourceTree = ""; }; 550C19E01804C55E001DA380 /* iosim.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = iosim.1; sourceTree = ""; }; 550C19E11804C55E001DA380 /* iosim.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = iosim.c; sourceTree = ""; }; 550C19EB1804D226001DA380 /* iosim */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = iosim; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2373,8 +2390,7 @@ 97999D2F1AE84C7600E8B10F /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = common.h; path = lskq.tproj/common.h; sourceTree = ""; }; 97999D301AE84C7600E8B10F /* lskq.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; name = lskq.1; path = lskq.tproj/lskq.1; sourceTree = ""; }; 97999D311AE84C7600E8B10F /* lskq.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = lskq.c; path = lskq.tproj/lskq.c; sourceTree = ""; }; - 9FAAF8741C158C0B00E6856D /* ThreadRequestedPolicy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ThreadRequestedPolicy.hpp; sourceTree = ""; }; - 9FAAF8761C158C2300E6856D /* ThreadEffectivePolicy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ThreadEffectivePolicy.hpp; sourceTree = ""; }; + A7C0927020EC491E0068148E /* passwd.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = passwd.entitlements; sourceTree = ""; }; ADA9007717679A8C00161ADF /* purge */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = purge; sourceTree = BUILT_PRODUCTS_DIR; }; ADA900791767A02700161ADF /* purge.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = purge.8; sourceTree = ""; }; ADA9007A1767A02700161ADF /* purge.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = purge.c; sourceTree = ""; }; @@ -2566,7 +2582,6 @@ BA4FD2E51372FAFA0025925C /* zprint.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = zprint.1; sourceTree = ""; }; BA4FD2E61372FAFA0025925C /* zprint.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = zprint.c; sourceTree = ""; }; BA4FD2EF1372FB3D0025925C /* ac */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ac; sourceTree = BUILT_PRODUCTS_DIR; }; - BA4FD2FB1372FB710025925C /* BSD.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = BSD.xcconfig; path = Makefiles/CoreOS/Xcode/BSD.xcconfig; sourceTree = DEVELOPER_DIR; }; BA4FD30D1372FFD80025925C /* accton */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = accton; sourceTree = BUILT_PRODUCTS_DIR; }; BA4FD323137300EE0025925C /* arch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = arch; sourceTree = BUILT_PRODUCTS_DIR; }; BA4FD3411373073E0025925C /* at */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = at; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2621,20 +2636,38 @@ C21481201C1A11E7003BCA63 /* vm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vm.c; path = gcore.tproj/vm.c; sourceTree = ""; }; C21481211C1A11E7003BCA63 /* vm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vm.h; path = gcore.tproj/vm.h; sourceTree = ""; }; C248DBAF1C1A1D0500F6E9AF /* libcompression.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcompression.dylib; path = /usr/lib/libcompression.dylib; sourceTree = ""; }; + C2DAA9491D9F22BF00FAC263 /* gcore-internal.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = "gcore-internal.1"; path = "gcore.tproj/gcore-internal.1"; sourceTree = ""; }; + C2DAA94A1D9F22BF00FAC263 /* convert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = convert.h; path = gcore.tproj/convert.h; sourceTree = ""; }; + C2DAA94B1D9F22BF00FAC263 /* convert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = convert.c; path = gcore.tproj/convert.c; sourceTree = ""; }; C625B28816D6F27E00168EF7 /* taskpolicy */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = taskpolicy; sourceTree = BUILT_PRODUCTS_DIR; }; C625B28A16D6F27E00168EF7 /* taskpolicy.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = taskpolicy.c; sourceTree = ""; }; C625B28C16D6F27E00168EF7 /* taskpolicy.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = taskpolicy.8; sourceTree = ""; }; + C913CA2E2228B622000051A0 /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; C96F50AC15BDCBF0008682F7 /* lsmp.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = lsmp.1; path = lsmp.tproj/lsmp.1; sourceTree = SOURCE_ROOT; }; C96F50AD15BDCE8E008682F7 /* lsmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lsmp.c; path = lsmp.tproj/lsmp.c; sourceTree = SOURCE_ROOT; }; C96F50B715BDCEC3008682F7 /* lsmp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lsmp; sourceTree = BUILT_PRODUCTS_DIR; }; - C97199F11C5206DE006D9758 /* libktrace.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libktrace.dylib; path = usr/lib/libktrace.dylib; sourceTree = SDKROOT; }; + C99490E32090F55D00246D9D /* zprint.lua */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = zprint.lua; sourceTree = ""; }; C9D64CCF1B91063200CFA43B /* system_cmds.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = system_cmds.plist; path = tests/system_cmds.plist; sourceTree = ""; }; C9E0691B1C58BDA000C956EB /* CoreSymbolication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSymbolication.framework; path = System/Library/PrivateFrameworks/CoreSymbolication.framework; sourceTree = SDKROOT; }; C9E0691D1C58BDB800C956EB /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + F2291F5D1FFEBB6A00161936 /* zlog */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = zlog; sourceTree = BUILT_PRODUCTS_DIR; }; + F2291F5F1FFEBB9E00161936 /* zlog.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = zlog.c; sourceTree = ""; }; + F27B70272044CB40003C04FC /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.0.Internal.sdk/System/Library/Frameworks/CoreFoundation.framework; sourceTree = DEVELOPER_DIR; }; + F27B70292045038B003C04FC /* SymbolicationHelper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = SymbolicationHelper.c; sourceTree = ""; }; + F27B702A2045038B003C04FC /* SymbolicationHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SymbolicationHelper.h; sourceTree = ""; }; + F29F5A5A203E12BB005B0099 /* entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = entitlements.plist; sourceTree = ""; }; + F29F5A5C203E4403005B0099 /* zlog.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = zlog.1; sourceTree = ""; }; FEBEE5CF1B0EACEB00166A8B /* entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = entitlements.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 08CE3D261E6E22A200DF1B78 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 08DC48821A12C21B008AAF38 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2642,6 +2675,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 0D06BC5B1E8F08CB00C6EC2D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 1523FE5D1595048900661E82 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2650,10 +2690,19 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 3521C84A2245AA92001B3201 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 550C19E51804D226001DA380 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5262264221ED5A780053ABA1 /* IOKit.framework in Frameworks */, + 5262264121E8295A0053ABA1 /* CoreFoundation.framework in Frameworks */, 550C19E61804D226001DA380 /* libutil.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2747,7 +2796,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C97199F21C5206DE006D9758 /* libktrace.dylib in Frameworks */, + 08ADC98C1E70715D0001CB70 /* ktrace.framework in Frameworks */, BA4B7A0A1373BA4600003422 /* libutil.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3010,6 +3059,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F2291F531FFEBB6A00161936 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F27B70282044CB40003C04FC /* CoreFoundation.framework in Frameworks */, + F2291F551FFEBB6A00161936 /* CoreSymbolication.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -3021,6 +3079,15 @@ path = kpgo.tproj; sourceTree = ""; }; + 0D06BC5F1E8F08CB00C6EC2D /* mslutil */ = { + isa = PBXGroup; + children = ( + 0D06BC651E8F091F00C6EC2D /* mslutil.c */, + 0D06BC671E8F0B4100C6EC2D /* mslutil.1 */, + ); + path = mslutil; + sourceTree = ""; + }; 1523FE691595056C00661E82 /* ltop.tproj */ = { isa = PBXGroup; children = ( @@ -3030,202 +3097,22 @@ path = ltop.tproj; sourceTree = ""; }; - 18380CDB1C596E8900DC6B89 /* eostrace */ = { - isa = PBXGroup; - children = ( - 189632961C5FD38E00FA9646 /* eostrace.h */, - 18380CDC1C596E8900DC6B89 /* eostrace.c */, - 18EA07131C99E4E3006D3005 /* copy_of_AppleEmbeddedOSSupport.h */, - 18EA07121C99E4E3006D3005 /* copy_of_AppleEmbeddedOSSupport.c */, - ); - path = eostrace; - sourceTree = ""; - }; - 18597EC518CBC2A300531A50 /* kdprof */ = { - isa = PBXGroup; - children = ( - 18597ECC18CBC35700531A50 /* Action.hpp */, - 18597ECD18CBC35700531A50 /* CollectAction.hpp */, - 18597ECE18CBC35700531A50 /* CollectAction.cpp */, - 18597ECF18CBC35700531A50 /* DisableAction.hpp */, - 18597ED018CBC35700531A50 /* DisableAction.cpp */, - 18597ED118CBC35700531A50 /* EnableAction.hpp */, - 18597ED218CBC35700531A50 /* EnableAction.cpp */, - 18597ED318CBC35700531A50 /* EventPrinting.hpp */, - 18597ED418CBC35700531A50 /* EventPrinting.cpp */, - 18597ED518CBC35700531A50 /* global.h */, - 18597ED618CBC35700531A50 /* Globals.hpp */, - 18597ED718CBC35700531A50 /* Globals.cpp */, - 18597ED818CBC35700531A50 /* InitializeAction.hpp */, - 18597ED918CBC35700531A50 /* InitializeAction.cpp */, - 18597EDA18CBC35700531A50 /* kdprof.cpp */, - 18597EDB18CBC35700531A50 /* NoWrapAction.hpp */, - 18597EDC18CBC35700531A50 /* NoWrapAction.cpp */, - 18597EDD18CBC35700531A50 /* PrintStateAction.hpp */, - 18597EDE18CBC35700531A50 /* PrintStateAction.cpp */, - 18597EDF18CBC35700531A50 /* RemoveAction.hpp */, - 18597EE018CBC35700531A50 /* RemoveAction.cpp */, - 18597EE118CBC35700531A50 /* SaveTraceAction.hpp */, - 18597EE218CBC35700531A50 /* SaveTraceAction.cpp */, - 18597EE318CBC35700531A50 /* SleepAction.hpp */, - 18597EE418CBC35700531A50 /* SleepAction.cpp */, - 18597EE518CBC35700531A50 /* SummaryPrinting.hpp */, - 18597EE618CBC35700531A50 /* SummaryPrinting.cpp */, - 18597EE718CBC35700531A50 /* TraceFileAction.hpp */, - 18597EE818CBC35700531A50 /* TraceFileAction.cpp */, - 185F75151CC2EFC600B0EA9E /* TypeFilterAction.hpp */, - 185F75141CC2EFC600B0EA9E /* TypeFilterAction.cpp */, - 1821B00C1C88BEE2000BAA0C /* usage.hpp */, - 1821B00B1C88BEE2000BAA0C /* usage.cpp */, - 185F751A1CC34EBE00B0EA9E /* WriteTraceFileAction.hpp */, - 185F75191CC34EBE00B0EA9E /* WriteTraceFileAction.cpp */, - 18410FED1CC881FD00385C96 /* entitlements.plist */, - 18597EC818CBC2A300531A50 /* kdprof.1 */, - ); - path = kdprof; - sourceTree = ""; - }; - 1865513918CA6F8C003B92A7 /* CPPUtil */ = { - isa = PBXGroup; - children = ( - 1865513A18CA70B5003B92A7 /* CPPUtil.h */, - 1865513B18CA70B5003B92A7 /* UtilAbsInterval.cpp */, - 1865513C18CA70B5003B92A7 /* UtilAbsInterval.hpp */, - 1865513D18CA70B5003B92A7 /* UtilAbsTime.cpp */, - 1865513E18CA70B5003B92A7 /* UtilAbsTime.hpp */, - 1865513F18CA70B5003B92A7 /* UtilAssert.hpp */, - 1865514018CA70B5003B92A7 /* UtilBase.hpp */, - 189337C41CC7DA8700B2A6A4 /* UtilCoreSymbolication.hpp */, - 1865514118CA70B5003B92A7 /* UtilException.hpp */, - 1865514218CA70B5003B92A7 /* UtilFileDescriptor.hpp */, - 1865514318CA70B5003B92A7 /* UtilLog.cpp */, - 1865514418CA70B5003B92A7 /* UtilLog.hpp */, - 1865514518CA70B5003B92A7 /* UtilMakeUnique.hpp */, - 1865514618CA70B5003B92A7 /* UtilMappedFile.cpp */, - 1865514718CA70B5003B92A7 /* UtilMappedFile.hpp */, - 1865514818CA70B5003B92A7 /* UtilMemoryBuffer.hpp */, - 1865514918CA70B5003B92A7 /* UtilNanoInterval.hpp */, - 1865514A18CA70B5003B92A7 /* UtilNanoTime.cpp */, - 1865514B18CA70B5003B92A7 /* UtilNanoTime.hpp */, - 1865514C18CA70B5003B92A7 /* UtilPath.cpp */, - 1865514D18CA70B5003B92A7 /* UtilPath.hpp */, - 1865514E18CA70B5003B92A7 /* UtilPrettyPrinting.cpp */, - 1865514F18CA70B5003B92A7 /* UtilPrettyPrinting.hpp */, - 1865515018CA70B5003B92A7 /* UtilString.cpp */, - 1865515118CA70B5003B92A7 /* UtilString.hpp */, - 1865515218CA70B5003B92A7 /* UtilTerminalColor.cpp */, - 1865515318CA70B5003B92A7 /* UtilTerminalColor.hpp */, - 1865515418CA70B5003B92A7 /* UtilTime.hpp */, - 1865515518CA70B5003B92A7 /* UtilTimer.cpp */, - 1865515618CA70B5003B92A7 /* UtilTimer.hpp */, - 1865515718CA70B5003B92A7 /* UtilTRange.hpp */, - 1865515818CA70B5003B92A7 /* UtilTRangeValue.hpp */, - ); - path = CPPUtil; - sourceTree = ""; - }; - 1865517D18CA7104003B92A7 /* msa */ = { - isa = PBXGroup; - children = ( - 1865518C18CA72F7003B92A7 /* Action.hpp */, - 1865518E18CA72F7003B92A7 /* EventProcessing.hpp */, - 1865518F18CA72F7003B92A7 /* EventRingBuffer.hpp */, - 1865519018CA72F7003B92A7 /* global.h */, - 1865519218CA72F7003B92A7 /* Globals.hpp */, - 1865519118CA72F7003B92A7 /* Globals.cpp */, - 1865519518CA72F7003B92A7 /* LiveTraceAction.hpp */, - 1865519418CA72F7003B92A7 /* LiveTraceAction.cpp */, - 1865519618CA72F7003B92A7 /* main.cpp */, - 1888DB65191A8A4400A0541E /* PrintBuffer.hpp */, - 1865519818CA72F7003B92A7 /* Printing.hpp */, - 1865519718CA72F7003B92A7 /* Printing.cpp */, - 1865519A18CA72F7003B92A7 /* ReadTraceFileAction.hpp */, - 1865519918CA72F7003B92A7 /* ReadTraceFileAction.cpp */, - 18D0E13119101CD200F93974 /* VoucherContentSysctl.hpp */, - 185B9771191022B200FCB84C /* VoucherContentSysctl.cpp */, - 185B97741910475500FCB84C /* WriteTraceFileAction.hpp */, - 185B97731910475500FCB84C /* WriteTraceFileAction.cpp */, - 1865518018CA7104003B92A7 /* msa.1 */, - ); - path = msa; - sourceTree = ""; - }; 189337C11CC7CB4800B2A6A4 /* Frameworks */ = { isa = PBXGroup; children = ( + F27B70272044CB40003C04FC /* CoreFoundation.framework */, + 08ADC98B1E70715D0001CB70 /* ktrace.framework */, 189337C21CC7CB4800B2A6A4 /* CoreFoundation.framework */, ); name = Frameworks; sourceTree = ""; }; - 18C8728518EA11B900F86DD9 /* KDBG */ = { + 3F56BF751F56057800266763 /* Recovered References */ = { isa = PBXGroup; children = ( - 18C8728D18EA128B00F86DD9 /* KDebug.h */, - 1873F1261CC587E2008950A8 /* BinaryDataTraceDecoder.hpp */, - 1873F11E1CC52204008950A8 /* BinaryDataTraceEncoder.hpp */, - 18C8728718EA128B00F86DD9 /* CPUActivity.hpp */, - 18C8728818EA128B00F86DD9 /* CPUSummary.hpp */, - 18624C861BF64ED700FA0575 /* CPUTypeSummary.hpp */, - 1873F1221CC551DD008950A8 /* DeviceMap.hpp */, - 1873F1211CC551DD008950A8 /* DeviceMap.cpp */, - 18C8728918EA128B00F86DD9 /* IOActivity.hpp */, - 18C8728B18EA128B00F86DD9 /* KDBG.hpp */, - 18C8728A18EA128B00F86DD9 /* KDBG.cpp */, - 18C8728C18EA128B00F86DD9 /* KDCPUMapEntry.hpp */, - 18C8728E18EA128B00F86DD9 /* KDEvent.hpp */, - 18C8728F18EA128B00F86DD9 /* KDState.hpp */, - 1875DF421C443513004AC849 /* KDState.cpp */, - 18C8729018EA128B00F86DD9 /* KDThreadMapEntry.hpp */, - 18C8729218EA128B00F86DD9 /* Kernel.hpp */, - 18C8729118EA128B00F86DD9 /* Kernel.cpp */, - 18C8729318EA128B00F86DD9 /* Machine.hpp */, - 185F75171CC337D400B0EA9E /* Machine.cpp */, - 18C8729418EA128B00F86DD9 /* Machine.impl.hpp */, - 18C8729518EA128B00F86DD9 /* Machine.mutable-impl.hpp */, - 18C8729618EA128B00F86DD9 /* MachineCPU.hpp */, - 18C8729718EA128B00F86DD9 /* MachineCPU.impl.hpp */, - 18C8729818EA128B00F86DD9 /* MachineCPU.mutable-impl.hpp */, - 18C8729918EA128B00F86DD9 /* MachineMachMsg.hpp */, - 18C8729A18EA128B00F86DD9 /* MachineProcess.hpp */, - 18C8729B18EA128B00F86DD9 /* MachineProcess.impl.hpp */, - 18C8729C18EA128B00F86DD9 /* MachineProcess.mutable-impl.hpp */, - 18C8729D18EA128B00F86DD9 /* MachineThread.hpp */, - 18C8729E18EA128B00F86DD9 /* MachineThread.impl.hpp */, - 18C8729F18EA128B00F86DD9 /* MachineThread.mutable-impl.hpp */, - 18C872A018EA128B00F86DD9 /* MachineVoucher.hpp */, - 18C872A118EA128B00F86DD9 /* MetaTypes.hpp */, - 18C872A218EA128B00F86DD9 /* NurseryMachMsg.hpp */, - 18C872A318EA128B00F86DD9 /* ProcessSummary.hpp */, - 18624C871BF6503600FA0575 /* ProcessTypeSummary.hpp */, - 18D8B66519535B92008847DF /* TaskEffectivePolicy.hpp */, - 9FAAF8761C158C2300E6856D /* ThreadEffectivePolicy.hpp */, - 188959FC1958D287004576E8 /* TaskRequestedPolicy.hpp */, - 9FAAF8741C158C0B00E6856D /* ThreadRequestedPolicy.hpp */, - 18C872A418EA128B00F86DD9 /* ThreadSummary.hpp */, - 18624C881BF651EC00FA0575 /* ThreadTypeSummary.hpp */, - 18C872A618EA128B00F86DD9 /* TraceCodes.hpp */, - 18C872A518EA128B00F86DD9 /* TraceCodes.cpp */, - 18C872A718EA128B00F86DD9 /* TraceDataHeader.hpp */, - 1845E41518EB95810010F451 /* TraceFile.hpp */, - 1845E41418EB95810010F451 /* TraceFile.cpp */, - 18624C851BF6400200FA0575 /* TypeSummary.hpp */, - 18C65BAE1CC1868D005194F4 /* TypeFilter.hpp */, - 18C65BAD1CC1868D005194F4 /* TypeFilter.cpp */, - 186288D01CC5DA71002950E0 /* UUIDMap.hpp */, - 186288CF1CC5DA71002950E0 /* UUIDMap.cpp */, - 18C872A818EA128B00F86DD9 /* VoucherInterval.hpp */, + C248DBAF1C1A1D0500F6E9AF /* libcompression.dylib */, ); - path = KDBG; - sourceTree = ""; - }; - 18D6F9A91C5A8EDB00F472A7 /* eostraced */ = { - isa = PBXGroup; - children = ( - 18D6F9AF1C5A8EF000F472A7 /* eostraced.c */, - 18D6F9B11C5A8EFB00F472A7 /* com.apple.eostraced.plist */, - ); - path = eostraced; + name = "Recovered References"; sourceTree = ""; }; 550C19DF1804C55E001DA380 /* iosim.tproj */ = { @@ -3314,9 +3201,13 @@ BA2DE9161372FA9100D1913C = { isa = PBXGroup; children = ( + 358407CF2245AC32006A0D8E /* cpuctl.8 */, + 358407CD2245AC09006A0D8E /* cpuctl.c */, + 3521C8562245AB52001B3201 /* cpuctl.tproj */, + C913CA2E2228B622000051A0 /* base.xcconfig */, + 08CE3D321E6E22DE00DF1B78 /* stackshot.c */, 18EA07101C99C76C006D3005 /* EmbeddedOSSupportHost.framework */, BA4FD1E11372FAFA0025925C /* APPLE_LICENSE */, - BA4FD2FB1372FB710025925C /* BSD.xcconfig */, C9D64CCF1B91063200CFA43B /* system_cmds.plist */, BA4FD1D91372FAFA0025925C /* ac.tproj */, BA4FD1DD1372FAFA0025925C /* accton.tproj */, @@ -3325,7 +3216,6 @@ BA4FD1F61372FAFA0025925C /* atrun.tproj */, BA4FD1FF1372FAFA0025925C /* chkpasswd.tproj */, BA4FD20A1372FAFA0025925C /* chpass.tproj */, - 1865513918CA6F8C003B92A7 /* CPPUtil */, BA4FD21E1372FAFA0025925C /* dmesg.tproj */, BA4FD2261372FAFA0025925C /* dynamic_pager.tproj */, BA4FD22E1372FAFA0025925C /* fs_usage.tproj */, @@ -3335,8 +3225,6 @@ BA4FD24B1372FAFA0025925C /* hostinfo.tproj */, 550C19DF1804C55E001DA380 /* iosim.tproj */, BA4FD24F1372FAFA0025925C /* iostat.tproj */, - 18C8728518EA11B900F86DD9 /* KDBG */, - 18597EC518CBC2A300531A50 /* kdprof */, 08DC488C1A12C2C5008AAF38 /* kpgo.tproj */, BA4FD2551372FAFA0025925C /* latency.tproj */, BA4FD2591372FAFA0025925C /* login.tproj */, @@ -3346,7 +3234,7 @@ BA4FD2691372FAFA0025925C /* mean.tproj */, B3F0E6DA16E9706E008FAD09 /* memory_pressure.tproj */, BA4FD26C1372FAFA0025925C /* mkfile.tproj */, - 1865517D18CA7104003B92A7 /* msa */, + 0D06BC5F1E8F08CB00C6EC2D /* mslutil */, BA4FD2701372FAFA0025925C /* newgrp.tproj */, BA4FD2741372FAFA0025925C /* nologin.tproj */, BA4FD2791372FAFA0025925C /* nvram.tproj */, @@ -3363,8 +3251,6 @@ BA4FD2BA1372FAFA0025925C /* sysctl.tproj */, C625B28916D6F27E00168EF7 /* taskpolicy.tproj */, BA4FD2BF1372FAFA0025925C /* trace.tproj */, - 18380CDB1C596E8900DC6B89 /* eostrace */, - 18D6F9A91C5A8EDB00F472A7 /* eostraced */, BA4FD2C31372FAFA0025925C /* vifs.tproj */, BA4FD2C71372FAFA0025925C /* vipw.tproj */, 55CCB16716B84ED100B56979 /* vm_purgeable_stat.tproj */, @@ -3373,10 +3259,12 @@ B158E3A1185A836700474677 /* wordexp-helper.tproj */, BA4FD2D11372FAFA0025925C /* zdump.tproj */, BA4FD2D51372FAFA0025925C /* zic.tproj */, + F2291F5E1FFEBB8500161936 /* zlog.tproj */, BA4FD2E31372FAFA0025925C /* zprint.tproj */, BA4B7A0D1373BBB600003422 /* Libraries */, BA4FD2F01372FB3D0025925C /* Products */, 189337C11CC7CB4800B2A6A4 /* Frameworks */, + 3F56BF751F56057800266763 /* Recovered References */, ); sourceTree = ""; }; @@ -3391,7 +3279,6 @@ BA4B7A0D1373BBB600003422 /* Libraries */ = { isa = PBXGroup; children = ( - C97199F11C5206DE006D9758 /* libktrace.dylib */, BA4B79BF1373A53700003422 /* libbsm.dylib */, BA4B7A9313765F8B00003422 /* libncurses.dylib */, BA4B7A091373BA4600003422 /* libutil.dylib */, @@ -3694,6 +3581,7 @@ BA4FD2811372FAFA0025925C /* passwd.tproj */ = { isa = PBXGroup; children = ( + A7C0927020EC491E0068148E /* passwd.entitlements */, BA4FD2831372FAFA0025925C /* file_passwd.c */, BA4FD2841372FAFA0025925C /* nis_passwd.c */, BA4FD2851372FAFA0025925C /* od_passwd.c */, @@ -3851,6 +3739,7 @@ BA4FD2E31372FAFA0025925C /* zprint.tproj */ = { isa = PBXGroup; children = ( + C99490E32090F55D00246D9D /* zprint.lua */, FEBEE5CF1B0EACEB00166A8B /* entitlements.plist */, BA4FD2E51372FAFA0025925C /* zprint.1 */, BA4FD2E61372FAFA0025925C /* zprint.c */, @@ -3910,6 +3799,10 @@ 78DE9DE01B5045DE00FE6DF5 /* wait4path */, C20D8C691C1A102F00C1226B /* gcore */, 8EC391651C9733C2001E28E6 /* proc_uuid_policy */, + 08CE3D291E6E22A200DF1B78 /* stackshot */, + 0D06BC5E1E8F08CB00C6EC2D /* mslutil */, + F2291F5D1FFEBB6A00161936 /* zlog */, + 3521C84D2245AA92001B3201 /* cpuctl */, ); name = Products; sourceTree = ""; @@ -3917,6 +3810,9 @@ C21481371C1A11F0003BCA63 /* gcore.tproj */ = { isa = PBXGroup; children = ( + C2DAA9491D9F22BF00FAC263 /* gcore-internal.1 */, + C2DAA94A1D9F22BF00FAC263 /* convert.h */, + C2DAA94B1D9F22BF00FAC263 /* convert.c */, C214810D1C1A11E6003BCA63 /* corefile.c */, C214810E1C1A11E6003BCA63 /* corefile.h */, C214810F1C1A11E6003BCA63 /* dyld_shared_cache.c */, @@ -3964,6 +3860,18 @@ path = ac.tproj; sourceTree = ""; }; + F2291F5E1FFEBB8500161936 /* zlog.tproj */ = { + isa = PBXGroup; + children = ( + F29F5A5C203E4403005B0099 /* zlog.1 */, + F29F5A5A203E12BB005B0099 /* entitlements.plist */, + F2291F5F1FFEBB9E00161936 /* zlog.c */, + F27B70292045038B003C04FC /* SymbolicationHelper.c */, + F27B702A2045038B003C04FC /* SymbolicationHelper.h */, + ); + path = zlog.tproj; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -4012,6 +3920,23 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 08CE3D281E6E22A200DF1B78 /* stackshot */ = { + isa = PBXNativeTarget; + buildConfigurationList = 08CE3D2F1E6E22A200DF1B78 /* Build configuration list for PBXNativeTarget "stackshot" */; + buildPhases = ( + 08CE3D251E6E22A200DF1B78 /* Sources */, + 08CE3D261E6E22A200DF1B78 /* Frameworks */, + 08CE3D271E6E22A200DF1B78 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = stackshot; + productName = stackshot; + productReference = 08CE3D291E6E22A200DF1B78 /* stackshot */; + productType = "com.apple.product-type.tool"; + }; 08DC48841A12C21B008AAF38 /* kpgo */ = { isa = PBXNativeTarget; buildConfigurationList = 08DC488B1A12C21C008AAF38 /* Build configuration list for PBXNativeTarget "kpgo" */; @@ -4029,6 +3954,23 @@ productReference = 08DC48851A12C21B008AAF38 /* kpgo */; productType = "com.apple.product-type.tool"; }; + 0D06BC5D1E8F08CB00C6EC2D /* mslutil */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0D06BC641E8F08CB00C6EC2D /* Build configuration list for PBXNativeTarget "mslutil" */; + buildPhases = ( + 0D06BC5A1E8F08CB00C6EC2D /* Sources */, + 0D06BC5B1E8F08CB00C6EC2D /* Frameworks */, + 0D06BC5C1E8F08CB00C6EC2D /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mslutil; + productName = mslutil; + productReference = 0D06BC5E1E8F08CB00C6EC2D /* mslutil */; + productType = "com.apple.product-type.tool"; + }; 1523FE5A1595048900661E82 /* ltop */ = { isa = PBXNativeTarget; buildConfigurationList = 1523FE611595048900661E82 /* Build configuration list for PBXNativeTarget "ltop" */; @@ -4046,6 +3988,23 @@ productReference = 1523FE631595048900661E82 /* ltop */; productType = "com.apple.product-type.tool"; }; + 3521C84C2245AA92001B3201 /* cpuctl */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3521C8512245AA92001B3201 /* Build configuration list for PBXNativeTarget "cpuctl" */; + buildPhases = ( + 3521C8492245AA92001B3201 /* Sources */, + 3521C84A2245AA92001B3201 /* Frameworks */, + 3521C84B2245AA92001B3201 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = cpuctl; + productName = cpuctl; + productReference = 3521C84D2245AA92001B3201 /* cpuctl */; + productType = "com.apple.product-type.tool"; + }; 550C19E21804D226001DA380 /* iosim */ = { isa = PBXNativeTarget; buildConfigurationList = 550C19E91804D226001DA380 /* Build configuration list for PBXNativeTarget "iosim" */; @@ -4189,6 +4148,7 @@ BA0A860813968E8500D2272C /* Sources */, BA0A860A13968E8500D2272C /* Frameworks */, BA0A860C13968E8500D2272C /* CopyFiles */, + C99490E22090F53B00246D9D /* CopyFiles */, ); buildRules = ( ); @@ -4861,24 +4821,54 @@ productReference = C96F50B715BDCEC3008682F7 /* lsmp */; productType = "com.apple.product-type.tool"; }; + F2291F501FFEBB6A00161936 /* zlog */ = { + isa = PBXNativeTarget; + buildConfigurationList = F2291F5A1FFEBB6A00161936 /* Build configuration list for PBXNativeTarget "zlog" */; + buildPhases = ( + F2291F511FFEBB6A00161936 /* Sources */, + F2291F531FFEBB6A00161936 /* Frameworks */, + F29F5A5E203E532B005B0099 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = zlog; + productName = ac; + productReference = F2291F5D1FFEBB6A00161936 /* zlog */; + productType = "com.apple.product-type.tool"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ BA2DE9181372FA9100D1913C /* Project object */ = { isa = PBXProject; attributes = { + DefaultBuildSystemTypeForWorkspace = Original; LastUpgradeCheck = 0600; TargetAttributes = { + 08CE3D281E6E22A200DF1B78 = { + CreatedOnToolsVersion = 8.3; + ProvisioningStyle = Automatic; + }; 08DC48841A12C21B008AAF38 = { CreatedOnToolsVersion = 6.3; ProvisioningStyle = Manual; }; + 0D06BC5D1E8F08CB00C6EC2D = { + CreatedOnToolsVersion = 9.0; + ProvisioningStyle = Automatic; + }; 1523FE5A1595048900661E82 = { ProvisioningStyle = Manual; }; 1812F18C1C8F923900F3DC9E = { ProvisioningStyle = Manual; }; + 3521C84C2245AA92001B3201 = { + CreatedOnToolsVersion = 11.0; + ProvisioningStyle = Automatic; + }; 550C19E21804D226001DA380 = { ProvisioningStyle = Manual; }; @@ -5130,6 +5120,10 @@ BA9BF4EF139684B40018C7BB /* zic */, BA959E7E13968C8E00CA9C60 /* zoneinfo */, BA0A860713968E8500D2272C /* zprint */, + 08CE3D281E6E22A200DF1B78 /* stackshot */, + 0D06BC5D1E8F08CB00C6EC2D /* mslutil */, + F2291F501FFEBB6A00161936 /* zlog */, + 3521C84C2245AA92001B3201 /* cpuctl */, ); }; /* End PBXProject section */ @@ -5299,7 +5293,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "set -x\nset -e\n\nmkdir -p \"${DSTROOT}/private/var/at\"\ninstall -o daemon -d \"${DSTROOT}/private/var/at/spool\"\ntouch \"${DSTROOT}/private/var/at/at.deny\"\nmkdir -p \"${DSTROOT}/usr/lib\"\nln -sf ../../var/at \"${DSTROOT}/usr/lib/cron\"\n"; + shellScript = "set -x\nset -e\n\ninstall -o daemon -d \"${DSTROOT}/private/var/at\"\ninstall -o daemon -d \"${DSTROOT}/private/var/at/spool\"\ntouch \"${DSTROOT}/private/var/at/at.deny\"\nmkdir -p \"${DSTROOT}/usr/lib\"\nln -sf ../../var/at \"${DSTROOT}/usr/lib/cron\"\n"; showEnvVarsInLog = 0; }; BAAEB39F13730D5C003EA7A9 /* ShellScript */ = { @@ -5415,6 +5409,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 08CE3D251E6E22A200DF1B78 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 08CE3D341E6E22F600DF1B78 /* stackshot.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 08DC48811A12C21B008AAF38 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -5423,6 +5425,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 0D06BC5A1E8F08CB00C6EC2D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0D06BC661E8F091F00C6EC2D /* mslutil.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 1523FE5B1595048900661E82 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -5431,6 +5441,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 3521C8492245AA92001B3201 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 358407CE2245AC09006A0D8E /* cpuctl.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 550C19E31804D226001DA380 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -5831,6 +5849,7 @@ C21481381C1A1213003BCA63 /* vm.c in Sources */, C21481391C1A1216003BCA63 /* vanilla.c in Sources */, C214813E1C1A122B003BCA63 /* main.c in Sources */, + C2DAA94F1D9F22F000FAC263 /* convert.c in Sources */, C214813F1C1A122B003BCA63 /* sparse.c in Sources */, C21481401C1A122B003BCA63 /* threads.c in Sources */, ); @@ -5854,9 +5873,28 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F2291F511FFEBB6A00161936 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F2291F601FFEBB9E00161936 /* zlog.c in Sources */, + F27B702B2045038B003C04FC /* SymbolicationHelper.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 08CE3D361E6E24CC00DF1B78 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 08CE3D281E6E22A200DF1B78 /* stackshot */; + targetProxy = 08CE3D351E6E24CC00DF1B78 /* PBXContainerItemProxy */; + }; + 08CE3D381E6E24DF00DF1B78 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 08CE3D281E6E22A200DF1B78 /* stackshot */; + targetProxy = 08CE3D371E6E24DF00DF1B78 /* PBXContainerItemProxy */; + }; 08DC48901A12C6F0008AAF38 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 08DC48841A12C21B008AAF38 /* kpgo */; @@ -6102,6 +6140,21 @@ target = BACC1D0D1377B481007728F4 /* mean */; targetProxy = 1812F1EC1C8F923900F3DC9E /* PBXContainerItemProxy */; }; + 358407D22245AD40006A0D8E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3521C84C2245AA92001B3201 /* cpuctl */; + targetProxy = 358407D12245AD40006A0D8E /* PBXContainerItemProxy */; + }; + 358407D42245AD4B006A0D8E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3521C84C2245AA92001B3201 /* cpuctl */; + targetProxy = 358407D32245AD4B006A0D8E /* PBXContainerItemProxy */; + }; + 358407D62245AD55006A0D8E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3521C84C2245AA92001B3201 /* cpuctl */; + targetProxy = 358407D52245AD55006A0D8E /* PBXContainerItemProxy */; + }; 550C19EF1804D2AD001DA380 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 550C19E21804D226001DA380 /* iosim */; @@ -6132,11 +6185,31 @@ target = 78DE9DDF1B5045DE00FE6DF5 /* wait4path */; targetProxy = 78DE9DF91B504D1200FE6DF5 /* PBXContainerItemProxy */; }; + 8E556A4B1D3FF7A40038D48B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8EC3915B1C9733C2001E28E6 /* proc_uuid_policy */; + targetProxy = 8E556A4A1D3FF7A40038D48B /* PBXContainerItemProxy */; + }; 8EC3916E1C973440001E28E6 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 8EC3915B1C9733C2001E28E6 /* proc_uuid_policy */; targetProxy = 8EC3916D1C973440001E28E6 /* PBXContainerItemProxy */; }; + 926913A21EC706010079D787 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0D06BC5D1E8F08CB00C6EC2D /* mslutil */; + targetProxy = 926913A11EC706010079D787 /* PBXContainerItemProxy */; + }; + 926913A41EC706080079D787 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0D06BC5D1E8F08CB00C6EC2D /* mslutil */; + targetProxy = 926913A31EC706080079D787 /* PBXContainerItemProxy */; + }; + 926913A61EC706130079D787 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0D06BC5D1E8F08CB00C6EC2D /* mslutil */; + targetProxy = 926913A51EC706130079D787 /* PBXContainerItemProxy */; + }; 97999D351AE84D3A00E8B10F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 97999D211AE84C0E00E8B10F /* lskq */; @@ -6627,9 +6700,112 @@ target = C96F50AE15BDCEC3008682F7 /* lsmp */; targetProxy = C96F50BB15BDFDB1008682F7 /* PBXContainerItemProxy */; }; + F2291F641FFEBC4000161936 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F2291F501FFEBB6A00161936 /* zlog */; + targetProxy = F2291F631FFEBC4000161936 /* PBXContainerItemProxy */; + }; + F2291F661FFEBC4700161936 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F2291F501FFEBB6A00161936 /* zlog */; + targetProxy = F2291F651FFEBC4700161936 /* PBXContainerItemProxy */; + }; + F2291F681FFEBC4F00161936 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F2291F501FFEBB6A00161936 /* zlog */; + targetProxy = F2291F671FFEBC4F00161936 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 08CE3D2D1E6E22A200DF1B78 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Release; + }; + 08CE3D2E1E6E22A200DF1B78 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders"; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Debug; + }; 08DC48891A12C21C008AAF38 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6710,6 +6886,92 @@ }; name = Debug; }; + 0D06BC621E8F08CB00C6EC2D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Release; + }; + 0D06BC631E8F08CB00C6EC2D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Debug; + }; 1523FE621595048900661E82 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6739,8 +7001,9 @@ }; 18732FE218CBD4A700275344 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BA4FD2FB1372FB710025925C /* BSD.xcconfig */; + baseConfigurationReference = C913CA2E2228B622000051A0 /* base.xcconfig */; buildSettings = { + APPLY_RULES_IN_COPY_FILES = YES; DEBUG_INFORMATION_FORMAT = dwarf; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -6749,6 +7012,7 @@ GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + PLIST_FILE_OUTPUT_FORMAT = binary; SUPPORTED_PLATFORMS = "macosx watchos iphoneos"; WARNING_CFLAGS = ( "-Wall", @@ -6899,6 +7163,7 @@ 18732FF418CBD4A700275344 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = dynamic_pager.tproj/entitlements.plist; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", NO_DIRECT_RPC, @@ -6912,6 +7177,10 @@ 18732FF518CBD4A700275344 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; @@ -7044,6 +7313,7 @@ 1873300218CBD4A700275344 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/local/include"; INSTALL_PATH = /usr/sbin; PRODUCT_NAME = nvram; }; @@ -7059,6 +7329,7 @@ 1873300418CBD4A700275344 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = passwd.tproj/passwd.entitlements; INSTALL_PATH = /usr/bin; "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( "-framework", @@ -7250,6 +7521,8 @@ 1873301918CBD4A700275344 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = ""; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = vm_purgeable_stat.tproj/entitlements.plist; GCC_TREAT_WARNINGS_AS_ERRORS = YES; HEADER_SEARCH_PATHS = ""; INSTALL_PATH = /usr/local/bin; @@ -7260,6 +7533,7 @@ 1873301A18CBD4A700275344 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = "taskpolicy.tproj/taskpolicy-entitlements.plist"; GCC_TREAT_WARNINGS_AS_ERRORS = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders"; INSTALL_PATH = /usr/sbin; @@ -7298,6 +7572,56 @@ }; name = Debug; }; + 3521C8522245AA92001B3201 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + PRODUCT_NAME = cpuctl; + }; + name = Release; + }; + 3521C8532245AA92001B3201 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + PRODUCT_NAME = cpuctl; + }; + name = Debug; + }; 550C19EA1804D226001DA380 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -7311,6 +7635,7 @@ 55CCB17216B84EDA00B56979 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = vm_purgeable_stat.tproj/entitlements.plist; GCC_TREAT_WARNINGS_AS_ERRORS = YES; HEADER_SEARCH_PATHS = ""; INSTALL_PATH = /usr/local/bin; @@ -7366,7 +7691,6 @@ MTL_ENABLE_DEBUG_INFO = NO; New_Setting14GCC_ENABLE_OBJC_GC = unsupported; PRODUCT_NAME = wait4path; - SDKROOT = macosx; STRIP_INSTALLED_PRODUCT_debug = NO; USE_HEADERMAP = NO; WANTS_GET_TASK_ALLOW = NO; @@ -7440,7 +7764,6 @@ New_Setting14GCC_ENABLE_OBJC_GC = unsupported; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = wait4path; - SDKROOT = macosx; STRIP_INSTALLED_PRODUCT_debug = NO; USE_HEADERMAP = NO; WANTS_GET_TASK_ALLOW = NO; @@ -7561,14 +7884,16 @@ }; BA2DE91E1372FA9100D1913C /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BA4FD2FB1372FB710025925C /* BSD.xcconfig */; + baseConfigurationReference = C913CA2E2228B622000051A0 /* base.xcconfig */; buildSettings = { + APPLY_RULES_IN_COPY_FILES = YES; GCC_DYNAMIC_NO_PIC = NO; GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + PLIST_FILE_OUTPUT_FORMAT = binary; SUPPORTED_PLATFORMS = "macosx watchos iphoneos"; WARNING_CFLAGS = ( "-Wall", @@ -7606,6 +7931,7 @@ BA4B79EB1373AF7A00003422 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = dynamic_pager.tproj/entitlements.plist; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", NO_DIRECT_RPC, @@ -7619,6 +7945,10 @@ BA4B7A041373B9E900003422 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); GCC_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_LABEL = YES; @@ -7936,6 +8266,7 @@ BAE589A4137836A00049DD3B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/local/include"; INSTALL_PATH = /usr/sbin; PRODUCT_NAME = nvram; }; @@ -7951,6 +8282,7 @@ BAE589CD1378FCAA0049DD3B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = passwd.tproj/passwd.entitlements; INSTALL_PATH = /usr/bin; "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( "-framework", @@ -8029,6 +8361,7 @@ CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "gcore.tproj/gcore-entitlements.plist"; FRAMEWORK_SEARCH_PATHS = "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Kernel.framework/PrivateHeaders"; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; @@ -8061,6 +8394,7 @@ CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "gcore.tproj/gcore-entitlements.plist"; FRAMEWORK_SEARCH_PATHS = "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Kernel.framework/PrivateHeaders"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -8087,6 +8421,7 @@ C625B28E16D6F27E00168EF7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_ENTITLEMENTS = "taskpolicy.tproj/taskpolicy-entitlements.plist"; GCC_TREAT_WARNINGS_AS_ERRORS = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders"; INSTALL_PATH = /usr/sbin; @@ -8105,9 +8440,56 @@ }; name = Release; }; + F2291F5B1FFEBB6A00161936 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = zlog.tproj/entitlements.plist; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + HEADER_SEARCH_PATHS = ( + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Kernel.framework/PrivateHeaders/mach", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/bsd", + ); + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F2291F5C1FFEBB6A00161936 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = zlog.tproj/entitlements.plist; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + HEADER_SEARCH_PATHS = ( + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Kernel.framework/PrivateHeaders/mach", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/bsd", + ); + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 08CE3D2F1E6E22A200DF1B78 /* Build configuration list for PBXNativeTarget "stackshot" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 08CE3D2D1E6E22A200DF1B78 /* Release */, + 08CE3D2E1E6E22A200DF1B78 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 08DC488B1A12C21C008AAF38 /* Build configuration list for PBXNativeTarget "kpgo" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -8117,6 +8499,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 0D06BC641E8F08CB00C6EC2D /* Build configuration list for PBXNativeTarget "mslutil" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0D06BC621E8F08CB00C6EC2D /* Release */, + 0D06BC631E8F08CB00C6EC2D /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 1523FE611595048900661E82 /* Build configuration list for PBXNativeTarget "ltop" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -8135,6 +8526,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 3521C8512245AA92001B3201 /* Build configuration list for PBXNativeTarget "cpuctl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3521C8522245AA92001B3201 /* Release */, + 3521C8532245AA92001B3201 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 550C19E91804D226001DA380 /* Build configuration list for PBXNativeTarget "iosim" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -8666,6 +9066,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + F2291F5A1FFEBB6A00161936 /* Build configuration list for PBXNativeTarget "zlog" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F2291F5B1FFEBB6A00161936 /* Release */, + F2291F5C1FFEBB6A00161936 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = BA2DE9181372FA9100D1913C /* Project object */; diff --git a/system_cmds.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/system_cmds.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings index 08de0be..a3f43a8 100644 --- a/system_cmds.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ b/system_cmds.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -2,6 +2,8 @@ + BuildSystemType + Original IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded diff --git a/taskpolicy.tproj/taskpolicy-entitlements.plist b/taskpolicy.tproj/taskpolicy-entitlements.plist new file mode 100644 index 0000000..39c14ef --- /dev/null +++ b/taskpolicy.tproj/taskpolicy-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.debugger.root + + + diff --git a/taskpolicy.tproj/taskpolicy.8 b/taskpolicy.tproj/taskpolicy.8 index c6c9a41..b13af54 100644 --- a/taskpolicy.tproj/taskpolicy.8 +++ b/taskpolicy.tproj/taskpolicy.8 @@ -12,6 +12,7 @@ .Op Fl b .Op Fl t Ar thruput_tier .Op Fl l Ar latency_tier +.Op Fl a .Ar program .Oo .Ar arg1 @@ -65,6 +66,8 @@ Set throughput tier of the process to .It Fl l Set latency tier of the process to .Ar latency_tier . +.It Fl a +Run the program with the resource management policies given to applications. .El .Pp .Sh SEE ALSO diff --git a/taskpolicy.tproj/taskpolicy.c b/taskpolicy.tproj/taskpolicy.c index b5e9a60..3260bb6 100644 --- a/taskpolicy.tproj/taskpolicy.c +++ b/taskpolicy.tproj/taskpolicy.c @@ -54,12 +54,12 @@ int main(int argc, char * argv[]) pid_t pid = 0; posix_spawnattr_t attr; extern char **environ; - bool flagx = false, flagX = false, flagb = false, flagB = false; + bool flagx = false, flagX = false, flagb = false, flagB = false, flaga = false; int flagd = -1, flagg = -1; struct task_qos_policy qosinfo = { LATENCY_QOS_TIER_UNSPECIFIED, THROUGHPUT_QOS_TIER_UNSPECIFIED }; uint64_t qos_clamp = POSIX_SPAWN_PROC_CLAMP_NONE; - while ((ch = getopt(argc, argv, "xXbBd:g:c:t:l:p:")) != -1) { + while ((ch = getopt(argc, argv, "xXbBd:g:c:t:l:p:a")) != -1) { switch (ch) { case 'x': flagx = true; @@ -115,6 +115,9 @@ int main(int argc, char * argv[]) usage(); } break; + case 'a': + flaga = true; + break; case '?': default: usage(); @@ -221,6 +224,14 @@ int main(int argc, char * argv[]) if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_set_qos_clamp_np"); } + if (flaga) { + ret = posix_spawnattr_setprocesstype_np(&attr, POSIX_SPAWN_PROC_TYPE_APP_DEFAULT); + if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_setprocesstype_np"); + + ret = posix_spawnattr_set_darwin_role_np(&attr, PRIO_DARWIN_ROLE_UI); + if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawnattr_set_darwin_role_np"); + } + ret = posix_spawnp(&pid, argv[0], NULL, &attr, argv, environ); if (ret != 0) errc(EX_NOINPUT, ret, "posix_spawn"); @@ -230,7 +241,7 @@ int main(int argc, char * argv[]) static void usage(void) { fprintf(stderr, "Usage: %s [-x|-X] [-d ] [-g policy] [-c clamp] [-b] [-t ]\n" - " [-l ] [ [...]]\n", getprogname()); + " [-l ] [-a] [ [...]]\n", getprogname()); fprintf(stderr, " %s [-b|-B] [-t ] [-l ] -p pid\n", getprogname()); exit(EX_USAGE); } diff --git a/tests/system_cmds.plist b/tests/system_cmds.plist index 5f5b961..3d562ac 100644 --- a/tests/system_cmds.plist +++ b/tests/system_cmds.plist @@ -180,6 +180,36 @@ WorkingDirectory /tmp/ + + Arch + platform-native + AsRoot + + Command + + /usr/local/bin/proc_uuid_policy + add + alt-dyld + /usr/bin/yes + + RequiredResources + + + Properties + deviceClass == 'iPhone' + + + IgnoreCrashes + + + TestName + test_proc_uuid_policy + TestSpecificLogs + + + WorkingDirectory + /tmp/ + diff --git a/trace.tproj/trace.1 b/trace.tproj/trace.1 index ac9d270..afbd280 100644 --- a/trace.tproj/trace.1 +++ b/trace.tproj/trace.1 @@ -64,6 +64,11 @@ optionally converted to a human-readable, plain-text format. .Pp .Nm operates according to the command flag specified. +.Pp +NOTE: +.Nm +is obsolete. The command you probably want is +.Xr ktrace 1 .Sh COMMANDS .Bl -tag -width Ds .It Fl h diff --git a/trace.tproj/trace.c b/trace.tproj/trace.c index 190e8b2..f85b336 100644 --- a/trace.tproj/trace.c +++ b/trace.tproj/trace.c @@ -158,7 +158,7 @@ typedef struct event *event_t; struct event { event_t ev_next; - uintptr_t ev_thread; + uint64_t ev_thread; uint32_t ev_debugid; uint64_t ev_timestamp; }; @@ -168,10 +168,10 @@ typedef struct lookup *lookup_t; struct lookup { lookup_t lk_next; - uintptr_t lk_thread; - uintptr_t lk_dvp; - long *lk_pathptr; - long lk_pathname[NUMPARMS + 1]; + uint64_t lk_thread; + uint64_t lk_dvp; + int64_t *lk_pathptr; + int64_t lk_pathname[NUMPARMS + 1]; }; typedef struct threadmap *threadmap_t; @@ -179,8 +179,8 @@ typedef struct threadmap *threadmap_t; struct threadmap { threadmap_t tm_next; - uintptr_t tm_thread; - uintptr_t tm_pthread; + uint64_t tm_thread; + uint64_t tm_pthread; boolean_t tm_deleteme; char tm_command[MAXCOMLEN + 1]; }; @@ -219,7 +219,7 @@ static void codesc_find_dupes(void); static int read_command_map(int, uint32_t); static void read_cpu_map(int); static void find_thread_command(kd_buf *, char **); -static void create_map_entry(uintptr_t, char *); +static void create_map_entry(uint64_t, char *); static void getdivisor(); static unsigned long argtoul(); @@ -240,17 +240,17 @@ static void Log_trace(); static void read_trace(); static void signal_handler(int); static void signal_handler_RAW(int); -static void delete_thread_entry(uintptr_t); -static void find_and_insert_tmp_map_entry(uintptr_t, char *); -static void create_tmp_map_entry(uintptr_t, uintptr_t); -static void find_thread_name(uintptr_t, char **, boolean_t); +static void delete_thread_entry(uint64_t); +static void find_and_insert_tmp_map_entry(uint64_t, char *); +static void create_tmp_map_entry(uint64_t, uint64_t); +static void find_thread_name(uint64_t, char **, boolean_t); static void execute_process(char * const argv[]); static int writetrace(int); static int write_command_map(int); static int debugid_compar(const void *, const void *); -static threadmap_t find_thread_entry(uintptr_t); +static threadmap_t find_thread_entry(uint64_t); static void saw_filter_class(uint8_t class); static void saw_filter_end_range(uint8_t end_class); @@ -591,7 +591,7 @@ int write_command_map(int fd) static -lookup_t handle_lookup_event(uintptr_t thread, int debugid, kd_buf *kdp) +lookup_t handle_lookup_event(uint64_t thread, int debugid, kd_buf *kdp) { lookup_t lkp; int hashid; @@ -640,7 +640,7 @@ lookup_t handle_lookup_event(uintptr_t thread, int debugid, kd_buf *kdp) static -void delete_lookup_event(uintptr_t thread, lookup_t lkp_to_delete) +void delete_lookup_event(uint64_t thread, lookup_t lkp_to_delete) { lookup_t lkp; lookup_t lkp_prev; @@ -671,7 +671,7 @@ void delete_lookup_event(uintptr_t thread, lookup_t lkp_to_delete) static -void insert_start_event(uintptr_t thread, int debugid, uint64_t now) +void insert_start_event(uint64_t thread, int debugid, uint64_t now) { event_t evp; int hashid; @@ -699,7 +699,7 @@ void insert_start_event(uintptr_t thread, int debugid, uint64_t now) static -uint64_t consume_start_event(uintptr_t thread, int debugid, uint64_t now) +uint64_t consume_start_event(uint64_t thread, int debugid, uint64_t now) { event_t evp; event_t evp_prev; @@ -1006,7 +1006,7 @@ void read_trace(void) uint64_t prev; uint64_t prevdelta = 0; uint32_t cpunum = 0; - uintptr_t thread; + uint64_t thread; double x = 0.0; double y = 0.0; double event_elapsed_time = 0; @@ -1057,6 +1057,7 @@ void read_trace(void) debugid = kdp->debugid; debugid_base = debugid & DBG_FUNC_MASK; now = kdp->timestamp & KDBG_TIMESTAMP_MASK; + cpunum = kdbg_get_cpu(kdp); /* * Is this event from an IOP? If so, there will be no @@ -1085,7 +1086,6 @@ void read_trace(void) bias = now; now -= bias; - cpunum = kdbg_get_cpu(kdp); thread = kdp->arg5; if (lines == 64 || firsttime) @@ -1142,7 +1142,7 @@ void read_trace(void) if ( !lkp) { int t_debugid; - uintptr_t t_thread; + uint64_t t_thread; if ((debugid & DBG_FUNC_START) || debugid == MACH_MAKERUNNABLE) { @@ -1227,24 +1227,26 @@ void read_trace(void) len -= 51; else len = 0; -#ifdef __LP64__ +#if defined(__LP64__) || defined(__arm64__) - fprintf(output_file, "%-16lx %-51s %-16lx %-2d %s\n", lkp->lk_dvp, &strptr[len], thread, cpunum, command); + fprintf(output_file, "%-16llx %-51s %-16" PRIx64 " %-2d %s\n", (uint64_t)lkp->lk_dvp, &strptr[len], thread, cpunum, command); #else - fprintf(output_file, "%-8x %-51s %-8lx %-2d %s\n", (unsigned int)lkp->lk_dvp, &strptr[len], thread, cpunum, command); + fprintf(output_file, "%-8x %-51s %-8" PRIx64 " %-2d %s\n", (unsigned int)lkp->lk_dvp, &strptr[len], thread, cpunum, command); #endif delete_lookup_event(thread, lkp); } else if (debugid == TRACE_INFO_STRING) { -#ifdef __LP64__ - fprintf(output_file, "%-32s%-36s %-16lx %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command); +#if defined(__LP64__) || defined(__arm64__) + fprintf(output_file, "%-32s%-36s %-16" PRIx64 " %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command); #else - fprintf(output_file, "%-16s%-46s %-8lx %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command); + fprintf(output_file, "%-16s%-46s %-8" PRIx64 " %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command); #endif } else { -#ifdef __LP64__ - fprintf(output_file, "%-16lx %-16lx %-16lx %-16lx %-16lx %-2d %s\n", kdp->arg1, kdp->arg2, kdp->arg3, kdp->arg4, thread, cpunum, command); +#if defined(__LP64__) || defined(__arm64__) + fprintf(output_file, "%-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-2d %s\n", + (uint64_t)kdp->arg1, (uint64_t)kdp->arg2, (uint64_t)kdp->arg3, (uint64_t)kdp->arg4, thread, cpunum, command); #else - fprintf(output_file, "%-8lx %-8lx %-8lx %-8lx %-8lx %-2d %s\n", kdp->arg1, kdp->arg2, kdp->arg3, kdp->arg4, thread, cpunum, command); + fprintf(output_file, "%-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-2d %s\n", + (uint64_t)kdp->arg1, (uint64_t)kdp->arg2, (uint64_t)kdp->arg3, (uint64_t)kdp->arg4, thread, cpunum, command); #endif } lines++; @@ -1293,12 +1295,15 @@ int main (int argc, char* argv[], char *envp[]) fprintf(stderr, "Could not re-execute: %d\n", errno); exit(1); } - } else { + } +#if !defined(__arm64__) + else { if (0 != reexec_to_match_kernel()) { fprintf(stderr, "Could not re-execute: %d\n", errno); exit(1); } } +#endif if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_PASSIVE) < 0) { printf("setiopolicy failed\n"); exit(1); @@ -2385,8 +2390,9 @@ parse_codefile(const char *filename) if (stat_buf.st_size != 0) { - if ((file_addr = mmap(0, file_size, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_FILE, fd, 0)) == (char*) -1) + file_addr = mmap(0, file_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FILE, fd, 0); + if (file_addr == MAP_FAILED) { printf("Error: Can't map file: %s\n", filename); close(fd); @@ -2426,7 +2432,7 @@ parse_codefile(const char *filename) */ count++; - // Grow the size of codesc to store new entries. + /* Grow the size of codesc to store new entries. */ size_t total_count = codesc_idx + count; code_type_t *new_codesc = (code_type_t *)realloc(codesc, (total_count) * sizeof(code_type_t)); @@ -2437,10 +2443,11 @@ parse_codefile(const char *filename) codesc = new_codesc; bzero((char *)(codesc + codesc_idx), count * sizeof(code_type_t)); - for (line = 1, j = 0; j < file_size && codesc_idx < total_count; codesc_idx++) + for (line = 1, j = 0; j < file_size && codesc_idx < total_count; + codesc_idx++) { /* Skip blank lines */ - while (file_addr[j] == '\n') + while (j < file_size && file_addr[j] == '\n') { j++; line++; @@ -2459,7 +2466,7 @@ parse_codefile(const char *filename) /* We didn't find a debugid code - skip this line */ if (verbose_flag) printf("Error: while parsing line %d, skip\n", line); - while (file_addr[j] != '\n' && j < file_size) + while (j < file_size && file_addr[j] != '\n') j++; codesc_idx--; line++; @@ -2467,15 +2474,25 @@ parse_codefile(const char *filename) } /* Skip whitespace */ - while (file_addr[j] == ' ' || file_addr[j] == '\t') + while (j < file_size && (file_addr[j] == ' ' || file_addr[j] == '\t')) + { j++; + } + + if (j >= file_size) + { + break; + } /* Get around old file that had count at the beginning */ if (file_addr[j] == '\n') { /* missing debugid string - skip */ if (verbose_flag) - printf("Error: while parsing line %d, (0x%x) skip\n", line, codesc[codesc_idx].debugid); + { + printf("Error: while parsing line %d, (0x%x) skip\n", line, + codesc[codesc_idx].debugid); + } j++; codesc_idx--; @@ -2483,12 +2500,25 @@ parse_codefile(const char *filename) continue; } + if (j >= file_size) + { + break; + } + /* Next is the debugid string terminated by a newline */ codesc[codesc_idx].debug_string = &file_addr[j]; /* Null out the newline terminator */ - while ((j < file_size) && (file_addr[j] != '\n')) + while (j < file_size && file_addr[j] != '\n') + { j++; + } + + if (j >= file_size) + { + break; + } + file_addr[j] = '\0'; /* File must be read-write */ j++; line++; @@ -2703,8 +2733,8 @@ read_command_map(int fd, uint32_t count) printf("Thread Command\n"); for (i = 0; i < total_threads; i++) { - printf ("0x%lx %s\n", - mapptr[i].thread, + printf ("0x%" PRIx64 " %s\n", + (uint64_t)mapptr[i].thread, mapptr[i].command); } } @@ -2713,7 +2743,7 @@ read_command_map(int fd, uint32_t count) } void -create_map_entry(uintptr_t thread, char *command) +create_map_entry(uint64_t thread, char *command) { threadmap_t tme; int hashid; @@ -2736,7 +2766,7 @@ create_map_entry(uintptr_t thread, char *command) } void -delete_thread_entry(uintptr_t thread) +delete_thread_entry(uint64_t thread) { threadmap_t tme = 0; threadmap_t tme_prev; @@ -2766,7 +2796,7 @@ delete_thread_entry(uintptr_t thread) } void -find_and_insert_tmp_map_entry(uintptr_t pthread, char *command) +find_and_insert_tmp_map_entry(uint64_t pthread, char *command) { threadmap_t tme = 0; threadmap_t tme_prev; @@ -2801,7 +2831,7 @@ find_and_insert_tmp_map_entry(uintptr_t pthread, char *command) } void -create_tmp_map_entry(uintptr_t thread, uintptr_t pthread) +create_tmp_map_entry(uint64_t thread, uint64_t pthread) { threadmap_t tme; @@ -2821,7 +2851,7 @@ create_tmp_map_entry(uintptr_t thread, uintptr_t pthread) threadmap_t -find_thread_entry(uintptr_t thread) +find_thread_entry(uint64_t thread) { threadmap_t tme; int hashid; @@ -2836,7 +2866,7 @@ find_thread_entry(uintptr_t thread) } void -find_thread_name(uintptr_t thread, char **command, boolean_t deleteme) +find_thread_name(uint64_t thread, char **command, boolean_t deleteme) { threadmap_t tme; @@ -2852,7 +2882,7 @@ find_thread_name(uintptr_t thread, char **command, boolean_t deleteme) void find_thread_command(kd_buf *kbufp, char **command) { - uintptr_t thread; + uint64_t thread; threadmap_t tme; int debugid_base; diff --git a/vm_purgeable_stat.tproj/entitlements.plist b/vm_purgeable_stat.tproj/entitlements.plist new file mode 100644 index 0000000..b21dbd8 --- /dev/null +++ b/vm_purgeable_stat.tproj/entitlements.plist @@ -0,0 +1,8 @@ + + + + + task_for_pid-allow + + + diff --git a/zic.tproj/README b/zic.tproj/README index 985a511..ff974e9 100644 --- a/zic.tproj/README +++ b/zic.tproj/README @@ -1,4 +1,8 @@ -@(#)README 7.11 +@(#)README 8.3 +This file is in the public domain, so clarified as of +2009-05-17 by Arthur David Olson. + +$FreeBSD: head/contrib/tzcode/zic/README 192890 2009-05-27 12:18:39Z edwin $ "What time is it?" -- Richard Deacon as The King "Any time you want it to be." -- Frank Baxter as The Scientist @@ -52,8 +56,10 @@ substituting your desired installation directory for "$HOME/tzdir": To use the new functions, use a "-ltz" option when compiling or linking. -Historical local time information has been included here not because it -is particularly useful, but rather to: +Historical local time information has been included here to: + +* provide a compendium of data about the history of civil time + that is useful even if the data are not 100% accurate; * give an idea of the variety of local time rules that have existed in the past and thus an idea of the variety that may be @@ -63,7 +69,9 @@ is particularly useful, but rather to: system. The information in the time zone data files is by no means authoritative; -if you know that the rules are different from those in a file, by all means +the files currently do not even attempt to cover all time stamps before +1970, and there are undoubtedly errors even for time stamps since 1970. +If you know that the rules are different from those in a file, by all means feel free to change file (and please send the changed version to tz@elsie.nci.nih.gov for use in the future). Europeans take note! diff --git a/zic.tproj/build_zichost.sh b/zic.tproj/build_zichost.sh index f66dea2..9247765 100755 --- a/zic.tproj/build_zichost.sh +++ b/zic.tproj/build_zichost.sh @@ -36,9 +36,10 @@ env -i \ LANG="${LANG}" \ HOME="${HOME}" \ $EXTRA_ARGS \ + TOOLCHAINS="${TOOLCHAINS}" \ xcrun -sdk "${SDKROOT}" xcodebuild install \ -target zic \ - -sdk "macosx" \ + -sdk "macosxinternal" \ SRCROOT="${SRCROOT}" \ OBJROOT="${OBJROOT}" \ SYMROOT="${ZICHOST_SYMROOT}" \ diff --git a/zic.tproj/generate_zoneinfo.sh b/zic.tproj/generate_zoneinfo.sh index 16484d1..feef097 100755 --- a/zic.tproj/generate_zoneinfo.sh +++ b/zic.tproj/generate_zoneinfo.sh @@ -68,22 +68,28 @@ if [ $? -ne 0 ]; then fi if [ -n "$RC_BRIDGE" ]; then - ACTUAL_PLATFORM_NAME="bridge${PLATFORM_NAME#watch}" + ACTUAL_PLATFORM_NAME="bridgeos" else ACTUAL_PLATFORM_NAME="${PLATFORM_NAME}" fi case "$ACTUAL_PLATFORM_NAME" in -iphone*|appletv*|watch*) +bridge*) + LOCALTIME="GMT" + ;; +esac + +case "$ACTUAL_PLATFORM_NAME" in +iphone*|appletv*|watch*|bridge*) mkdir -p "${PRIVATEDIR}/var/db" mkdir -p -m a+rx "${PRIVATEDIR}/var/db/timezone" # This link must precisely start with TZDIR followed by a slash. radar:13532660 ln -hfs "/var/db/timezone/zoneinfo/${LOCALTIME}" "${PRIVATEDIR}/var/db/timezone/localtime" ;; -macosx|bridge*) +macosx) mkdir -p "${PRIVATEDIR}/etc" - ln -hfs "/usr/share/zoneinfo/${LOCALTIME}" "${PRIVATEDIR}/etc/localtime" + ln -hfs "/var/db/timezone/zoneinfo/${LOCALTIME}" "${PRIVATEDIR}/etc/localtime" ;; *) echo "Unsupported platform: $ACTUAL_PLATFORM_NAME" diff --git a/zic.tproj/ialloc.c b/zic.tproj/ialloc.c index fabe0c6..dda367b 100644 --- a/zic.tproj/ialloc.c +++ b/zic.tproj/ialloc.c @@ -1,13 +1,17 @@ -#include +/* +** This file is in the public domain, so clarified as of +** 2006-07-17 by Arthur David Olson. +*/ + #ifndef lint #ifndef NOID -__unused static const char elsieid[] = "@(#)ialloc.c 8.29"; +static const char elsieid[] = "@(#)ialloc.c 8.30"; #endif /* !defined NOID */ #endif /* !defined lint */ #ifndef lint -__unused static const char rcsid[] = - "$FreeBSD: src/usr.sbin/zic/ialloc.c,v 1.6 2000/11/28 18:18:56 charnier Exp $"; +static const char rcsid[] = + "$FreeBSD: head/contrib/tzcode/zic/ialloc.c 192625 2009-05-23 06:31:50Z edwin $"; #endif /* not lint */ /*LINTLIBRARY*/ @@ -17,32 +21,39 @@ __unused static const char rcsid[] = #define nonzero(n) (((n) == 0) ? 1 : (n)) char * -imalloc(const size_t n) +imalloc(n) +const int n; { - return malloc(nonzero(n)); + return malloc((size_t) nonzero(n)); } char * -icalloc(size_t nelem, size_t elsize) +icalloc(nelem, elsize) +int nelem; +int elsize; { if (nelem == 0 || elsize == 0) nelem = elsize = 1; - return calloc(nelem, elsize); + return calloc((size_t) nelem, (size_t) elsize); } void * -irealloc(void * const pointer, const size_t size) +irealloc(pointer, size) +void * const pointer; +const int size; { if (pointer == NULL) return imalloc(size); - return realloc((void *) pointer, nonzero(size)); + return realloc((void *) pointer, (size_t) nonzero(size)); } char * -icatalloc(char * const old, const char * const new) +icatalloc(old, new) +char * const old; +const char * const new; { - char * result; - size_t oldsize, newsize; + register char * result; + register int oldsize, newsize; newsize = (new == NULL) ? 0 : strlen(new); if (old == NULL) @@ -57,20 +68,23 @@ icatalloc(char * const old, const char * const new) } char * -icpyalloc(const char * const string) +icpyalloc(string) +const char * const string; { return icatalloc((char *) NULL, string); } void -ifree(char * const p) +ifree(p) +char * const p; { if (p != NULL) (void) free(p); } void -icfree(char * const p) +icfree(p) +char * const p; { if (p != NULL) (void) free(p); diff --git a/zic.tproj/install_zoneinfo.sh b/zic.tproj/install_zoneinfo.sh index 9bb9af9..50a9deb 100755 --- a/zic.tproj/install_zoneinfo.sh +++ b/zic.tproj/install_zoneinfo.sh @@ -3,21 +3,45 @@ set -e set -x if [ -n "$RC_BRIDGE" ]; then - ACTUAL_PLATFORM_NAME="bridge${PLATFORM_NAME#watch}" + ACTUAL_PLATFORM_NAME="bridgeos" else ACTUAL_PLATFORM_NAME="${PLATFORM_NAME}" fi +# This sets up the paths for the default set of zoneinfo files +# On iOS, watchOS, and tvOS, this is handled by tzd in coordination with +# launchd on first boot. +# On macOS, the directory needs to be setup # during mastering for SIP +# protection, and the symlink for the BaseSystem. +# On bridgeOS tzd doesn't exist, so needs this all setup statically. +default_zoneinfo_setup() +{ + mkdir -p "${DSTROOT}/private/var/db/timezone" + chmod 555 "${DSTROOT}/private/var/db/timezone" + ln -hfs "/usr/share/zoneinfo.default" "${DSTROOT}/private/var/db/timezone/zoneinfo" +} + case "$ACTUAL_PLATFORM_NAME" in -iphone*|appletv*|watch*) +iphone*|appletv*|watch*|macosx) ditto "${BUILT_PRODUCTS_DIR}/zoneinfo" "${DSTROOT}/usr/share/zoneinfo.default" ln -hfs "/var/db/timezone/zoneinfo" "${DSTROOT}/usr/share/zoneinfo" + case "$ACTUAL_PLATFORM_NAME" in + macosx) + default_zoneinfo_setup + ;; + esac ;; -macosx|bridge*) - ditto "${BUILT_PRODUCTS_DIR}/zoneinfo" "${DSTROOT}/usr/share/zoneinfo" +bridge*) + # Since bridgeOS lacks any mechanism to change timezones (currently), + # and in the interest of saving space, only GMT is installed. + mkdir -p "${DSTROOT}/usr/share/zoneinfo.default" + chmod 555 "${DSTROOT}/usr/share/zoneinfo.default" + ditto "${BUILT_PRODUCTS_DIR}/zoneinfo/GMT" "${DSTROOT}/usr/share/zoneinfo.default/GMT" + default_zoneinfo_setup ;; *) echo "Unsupported platform: $ACTUAL_PLATFORM_NAME" exit 1 ;; esac + diff --git a/zic.tproj/private.h b/zic.tproj/private.h index 4ffd069..ae931b0 100644 --- a/zic.tproj/private.h +++ b/zic.tproj/private.h @@ -4,7 +4,7 @@ /* ** This file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +** 1996-06-05 by Arthur David Olson. */ /* @@ -13,7 +13,7 @@ * I have removed all of the ifdef spaghetti which is not relevant to * zic from this file. * - * $FreeBSD: src/usr.sbin/zic/private.h,v 1.7 2004/06/20 21:41:11 stefanf Exp $ + * $FreeBSD: head/contrib/tzcode/zic/private.h 207590 2010-05-03 22:32:26Z emaste $ */ /* @@ -30,10 +30,12 @@ #ifndef lint #ifndef NOID -static const char privatehid[] = "@(#)private.h 7.53"; +static const char privatehid[] = "@(#)private.h 8.6"; #endif /* !defined NOID */ #endif /* !defined lint */ +#define GRANDPARENTED "Local time zone must be set--use tzsetup" + /* ** Defaults for preprocessor symbols. ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. @@ -43,10 +45,6 @@ static const char privatehid[] = "@(#)private.h 7.53"; #define HAVE_GETTEXT 0 #endif /* !defined HAVE_GETTEXT */ -#ifndef HAVE_STRERROR -#define HAVE_STRERROR 1 -#endif /* !defined HAVE_STRERROR */ - #ifndef HAVE_SYMLINK #define HAVE_SYMLINK 1 #endif /* !defined HAVE_SYMLINK */ @@ -71,47 +69,94 @@ static const char privatehid[] = "@(#)private.h 7.53"; #include "stdio.h" #include "errno.h" #include "string.h" -#include "limits.h" /* for CHAR_BIT */ +#include "limits.h" /* for CHAR_BIT et al. */ #include "time.h" #include "stdlib.h" -#if HAVE_GETTEXT - 0 +#if HAVE_GETTEXT #include "libintl.h" -#endif /* HAVE_GETTEXT - 0 */ +#endif /* HAVE_GETTEXT */ -#if HAVE_SYS_WAIT_H - 0 +#if HAVE_SYS_WAIT_H #include /* for WIFEXITED and WEXITSTATUS */ -#endif /* HAVE_SYS_WAIT_H - 0 */ +#endif /* HAVE_SYS_WAIT_H */ -#if HAVE_UNISTD_H - 0 -#include "unistd.h" /* for F_OK and R_OK */ -#endif /* HAVE_UNISTD_H - 0 */ +#if HAVE_UNISTD_H +#include "unistd.h" /* for F_OK and R_OK, and other POSIX goodness */ +#endif /* HAVE_UNISTD_H */ -#if !(HAVE_UNISTD_H - 0) #ifndef F_OK #define F_OK 0 #endif /* !defined F_OK */ #ifndef R_OK #define R_OK 4 #endif /* !defined R_OK */ -#endif /* !(HAVE_UNISTD_H - 0) */ -/* Unlike 's isdigit, this also works if c < 0 | c > UCHAR_MAX. */ +/* Unlike 's isdigit, this also works if c < 0 | c > UCHAR_MAX. */ #define is_digit(c) ((unsigned)(c) - '0' <= 9) -#define P(x) x +/* +** Define HAVE_STDINT_H's default value here, rather than at the +** start, since __GLIBC__'s value depends on previously-included +** files. +** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.) +*/ +#ifndef HAVE_STDINT_H +#define HAVE_STDINT_H \ + (199901 <= __STDC_VERSION__ || \ + 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__))) +#endif /* !defined HAVE_STDINT_H */ + +#if HAVE_STDINT_H +#include "stdint.h" +#endif /* !HAVE_STDINT_H */ + +#ifndef INT_FAST64_MAX +/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ +#if defined LLONG_MAX || defined __LONG_LONG_MAX__ +typedef long long int_fast64_t; +#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ +#if (LONG_MAX >> 31) < 0xffffffff +Please use a compiler that supports a 64-bit integer type (or wider); +you may need to compile with "-DHAVE_STDINT_H". +#endif /* (LONG_MAX >> 31) < 0xffffffff */ +typedef long int_fast64_t; +#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */ +#endif /* !defined INT_FAST64_MAX */ + +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif /* !defined INT32_MAX */ +#ifndef INT32_MIN +#define INT32_MIN (-1 - INT32_MAX) +#endif /* !defined INT32_MIN */ + +/* +** Workarounds for compilers/systems. + */ + +/* +** Some time.h implementations don't declare asctime_r. +** Others might define it as a macro. +** Fix the former without affecting the latter. + */ +#ifndef asctime_r +extern char * asctime_r(struct tm const *, char *); +#endif + + /* ** Private function declarations. */ -char * icalloc P((size_t nelem, size_t elsize)); -char * icatalloc P((char * old, const char * new)); -char * icpyalloc P((const char * string)); -char * imalloc P((size_t n)); -void * irealloc P((void * pointer, size_t size)); -void icfree P((char * pointer)); -void ifree P((char * pointer)); -char * scheck P((const char *string, const char *format)); +char * icalloc (int nelem, int elsize); +char * icatalloc (char * old, const char * new); +char * icpyalloc (const char * string); +char * imalloc (int n); +void * irealloc (void * pointer, int size); +void icfree (char * pointer); +void ifree (char * pointer); +const char * scheck (const char *string, const char *format); /* ** Finally, some convenience items. @@ -133,6 +178,15 @@ char * scheck P((const char *string, const char *format)); #define TYPE_SIGNED(type) (((type) -1) < 0) #endif /* !defined TYPE_SIGNED */ +/* +** Since the definition of TYPE_INTEGRAL contains floating point numbers, +** it cannot be used in preprocessor directives. +*/ + +#ifndef TYPE_INTEGRAL +#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5) +#endif /* !defined TYPE_INTEGRAL */ + #ifndef INT_STRLEN_MAXIMUM /* ** 302 / 1000 is log10(2.0) rounded up. @@ -141,7 +195,8 @@ char * scheck P((const char *string, const char *format)); ** add one more for a minus sign if the type is signed. */ #define INT_STRLEN_MAXIMUM(type) \ - ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type)) + ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ + 1 + TYPE_SIGNED(type)) #endif /* !defined INT_STRLEN_MAXIMUM */ /* @@ -175,11 +230,11 @@ char * scheck P((const char *string, const char *format)); */ #ifndef _ -#if HAVE_GETTEXT - 0 +#if HAVE_GETTEXT #define _(msgid) gettext(msgid) -#else /* !(HAVE_GETTEXT - 0) */ +#else /* !HAVE_GETTEXT */ #define _(msgid) msgid -#endif /* !(HAVE_GETTEXT - 0) */ +#endif /* !HAVE_GETTEXT */ #endif /* !defined _ */ #ifndef TZ_DOMAIN @@ -190,4 +245,28 @@ char * scheck P((const char *string, const char *format)); ** UNIX was a registered trademark of The Open Group in 2003. */ +#ifndef YEARSPERREPEAT +#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ +#endif /* !defined YEARSPERREPEAT */ + +/* +** The Gregorian year averages 365.2425 days, which is 31556952 seconds. +*/ + +#ifndef AVGSECSPERYEAR +#define AVGSECSPERYEAR 31556952L +#endif /* !defined AVGSECSPERYEAR */ + +#ifndef SECSPERREPEAT +#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR) +#endif /* !defined SECSPERREPEAT */ + +#ifndef SECSPERREPEAT_BITS +#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ +#endif /* !defined SECSPERREPEAT_BITS */ + + /* + ** UNIX was a registered trademark of The Open Group in 2003. + */ + #endif /* !defined PRIVATE_H */ diff --git a/zic.tproj/scheck.c b/zic.tproj/scheck.c index dcd4d01..10eea82 100644 --- a/zic.tproj/scheck.c +++ b/zic.tproj/scheck.c @@ -1,31 +1,36 @@ +/* +** This file is in the public domain, so clarified as of +** 2006-07-17 by Arthur David Olson. +*/ + #ifndef lint #ifndef NOID -#include -__unused static const char elsieid[] = "@(#)scheck.c 8.15"; +static const char elsieid[] = "@(#)scheck.c 8.19"; #endif /* !defined lint */ #endif /* !defined NOID */ #ifndef lint -__unused static const char rcsid[] = - "$FreeBSD: src/usr.sbin/zic/scheck.c,v 1.7 2001/07/18 11:27:04 dd Exp $"; +static const char rcsid[] = + "$FreeBSD: head/contrib/tzcode/zic/scheck.c 192625 2009-05-23 06:31:50Z edwin $"; #endif /* not lint */ /*LINTLIBRARY*/ #include "private.h" -char * -scheck(const char * const string, const char * const format) +const char * +scheck(string, format) +const char * const string; +const char * const format; { - char * fbuf; - const char * fp; - char * tp; - int c; - char * result; - char dummy; - static char nada; + register char * fbuf; + register const char * fp; + register char * tp; + register int c; + register const char * result; + char dummy; - result = &nada; + result = ""; if (string == NULL || format == NULL) return result; fbuf = imalloc((int) (2 * strlen(format) + 4)); diff --git a/zic.tproj/tzfile.h b/zic.tproj/tzfile.h new file mode 100644 index 0000000..ec4009b --- /dev/null +++ b/zic.tproj/tzfile.h @@ -0,0 +1,192 @@ +#ifndef TZFILE_H +#define TZFILE_H + + +/* +** This file is in the public domain, so clarified as of +** 1996-06-05 by Arthur David Olson. +** +** $FreeBSD: head/contrib/tzcode/stdtime/tzfile.h 192625 2009-05-23 06:31:50Z edwin $ +*/ + +/* +** This header is for use ONLY with the time conversion code. +** There is no guarantee that it will remain unchanged, +** or that it will remain at all. +** Do NOT copy it to any system include directory. +** Thank you! +*/ + +/* +** ID +*/ + +#ifndef lint +#ifndef NOID +/* +static char tzfilehid[] = "@(#)tzfile.h 8.1"; +*/ +#endif /* !defined NOID */ +#endif /* !defined lint */ + +/* +** Information about time zone files. +*/ + +#ifndef TZDIR +#ifdef UNIFDEF_TZDIR_SYMLINK +#define TZDIR "/var/db/timezone/zoneinfo" /* Time zone object file directory */ +#else /* !UNIFDEF_TZDIR_SYMLINK */ +#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */ +#endif /* UNIFDEF_TZDIR_SYMLINK */ +#endif /* !defined TZDIR */ + +#ifndef TZDEFAULT +#ifdef UNIFDEF_MOVE_LOCALTIME +#define TZDEFAULT "/var/db/timezone/localtime" +#else /* !UNIFDEF_MOVE_LOCALTIME */ +#define TZDEFAULT "/etc/localtime" +#endif /* UNIFDEF_MOVE_LOCALTIME */ +#endif /* !defined TZDEFAULT */ + +#ifndef TZDEFRULES +#define TZDEFRULES "posixrules" +#endif /* !defined TZDEFRULES */ + +/* +** Each file begins with. . . +*/ + +#define TZ_MAGIC "TZif" + +struct tzhead { + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_version[1]; /* '\0' or '2' as of 2005 */ + char tzh_reserved[15]; /* reserved--must be zero */ + char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ +}; + +/* +** . . .followed by. . . +** +** tzh_timecnt (char [4])s coded transition times a la time(2) +** tzh_timecnt (unsigned char)s types of local time starting at above +** tzh_typecnt repetitions of +** one (char [4]) coded UTC offset in seconds +** one (unsigned char) used to set tm_isdst +** one (unsigned char) that's an abbreviation list index +** tzh_charcnt (char)s '\0'-terminated zone abbreviations +** tzh_leapcnt repetitions of +** one (char [4]) coded leap second transition times +** one (char [4]) total correction after above +** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition +** time is standard time, if FALSE, +** transition time is wall clock time +** if absent, transition times are +** assumed to be wall clock time +** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition +** time is UTC, if FALSE, +** transition time is local time +** if absent, transition times are +** assumed to be local time +*/ + +/* +** If tzh_version is '2' or greater, the above is followed by a second instance +** of tzhead and a second instance of the data in which each coded transition +** time uses 8 rather than 4 chars, +** then a POSIX-TZ-environment-variable-style string for use in handling +** instants after the last transition time stored in the file +** (with nothing between the newlines if there is no POSIX representation for +** such instants). +*/ + +/* +** In the current implementation, "tzset()" refuses to deal with files that +** exceed any of the limits below. +*/ + +#ifndef TZ_MAX_TIMES +#define TZ_MAX_TIMES 1200 +#endif /* !defined TZ_MAX_TIMES */ + +#ifndef TZ_MAX_TYPES +#ifndef NOSOLAR +#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ +#endif /* !defined NOSOLAR */ +#ifdef NOSOLAR +/* +** Must be at least 14 for Europe/Riga as of Jan 12 1995, +** as noted by Earl Chew. +*/ +#define TZ_MAX_TYPES 20 /* Maximum number of local time types */ +#endif /* !defined NOSOLAR */ +#endif /* !defined TZ_MAX_TYPES */ + +#ifndef TZ_MAX_CHARS +#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ + /* (limited by what unsigned chars can hold) */ +#endif /* !defined TZ_MAX_CHARS */ + +#ifndef TZ_MAX_LEAPS +#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ +#endif /* !defined TZ_MAX_LEAPS */ + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) +#define MONSPERYEAR 12 + +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define TM_TUESDAY 2 +#define TM_WEDNESDAY 3 +#define TM_THURSDAY 4 +#define TM_FRIDAY 5 +#define TM_SATURDAY 6 + +#define TM_JANUARY 0 +#define TM_FEBRUARY 1 +#define TM_MARCH 2 +#define TM_APRIL 3 +#define TM_MAY 4 +#define TM_JUNE 5 +#define TM_JULY 6 +#define TM_AUGUST 7 +#define TM_SEPTEMBER 8 +#define TM_OCTOBER 9 +#define TM_NOVEMBER 10 +#define TM_DECEMBER 11 + +#define TM_YEAR_BASE 1900 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + +/* +** Since everything in isleap is modulo 400 (or a factor of 400), we know that +** isleap(y) == isleap(y % 400) +** and so +** isleap(a + b) == isleap((a + b) % 400) +** or +** isleap(a + b) == isleap(a % 400 + b % 400) +** This is true even if % means modulo rather than Fortran remainder +** (which is allowed by C89 but not C99). +** We use this to avoid addition overflow problems. +*/ + +#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) + +#endif /* !defined TZFILE_H */ diff --git a/zic.tproj/zic.8 b/zic.tproj/zic.8 index b2a60aa..a84e563 100644 --- a/zic.tproj/zic.8 +++ b/zic.tproj/zic.8 @@ -1,4 +1,4 @@ -.\" $FreeBSD: src/usr.sbin/zic/zic.8,v 1.19 2005/02/13 23:45:54 ru Exp $ +.\" $FreeBSD: head/contrib/tzcode/zic/zic.8 214411 2010-10-27 07:14:46Z edwin $ .Dd June 20, 2004 .Dt ZIC 8 .Os @@ -119,10 +119,13 @@ Any line that is blank (after comment stripping) is ignored. Non-blank lines are expected to be of one of three types: rule lines, zone lines, and link lines. .Pp +Names (such as month names) must be in English and are case insensitive. +Abbreviations, if used, must be unambiguous in context. +.Pp A rule line has the form: -.Dl "Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +.Dl "Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S" For example: -.Dl "Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D +.Dl "Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D" .Pp The fields that make up a rule line are: .Bl -tag -width "LETTER/S" -offset indent @@ -260,9 +263,9 @@ the variable part is null. .El .Pp A zone line has the form: -.Dl "Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL] +.Dl "Zone NAME GMTOFF RULES/SAVE FORMAT [UNTILYEAR [MONTH [DAY [TIME]]]]" For example: -.Dl "Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00 +.Dl "Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00" The fields that make up a zone line are: .Bl -tag -width indent .It NAME @@ -293,15 +296,15 @@ of the time zone abbreviation goes. Alternately, a slash (/) separates standard and daylight abbreviations. -.It UNTIL +.It UNTILYEAR [MONTH [DAY [TIME]]] The time at which the UTC offset or the rule(s) change for a location. It is specified as a year, a month, a day, and a time of day. If this is specified, the time zone information is generated from the given UTC offset and rule change until the time specified. The month, day, and time of day have the same format as the IN, ON, and AT -columns of a rule; trailing columns can be omitted, and default to the -earliest possible value for the missing columns. +fields of a rule; trailing fields can be omitted, and default to the +earliest possible value for the missing fields. .Pp The next line must be a .Dq continuation @@ -310,18 +313,18 @@ string .Dq Zone and the name are omitted, as the continuation line will place information starting at the time specified as the -.Em UNTIL -field in the previous line in the file used by the previous line. -Continuation lines may contain an -.Em UNTIL -field, just as zone lines do, indicating that the next line is a further +.Em until +information in the previous line in the file used by the previous line. +Continuation lines may contain +.Em until +information, just as zone lines do, indicating that the next line is a further continuation. .El .Pp A link line has the form -.Dl "Link LINK-FROM LINK-TO +.Dl "Link LINK-FROM LINK-TO" For example: -.Dl "Link Europe/Istanbul Asia/Istanbul +.Dl "Link Europe/Istanbul Asia/Istanbul" The .Em LINK-FROM field should appear as the @@ -335,9 +338,9 @@ Except for continuation lines, lines may appear in any order in the input. .Pp Lines in the file that describes leap seconds have the following form: -.Dl "Leap YEAR MONTH DAY HH:MM:SS CORR R/S +.Dl "Leap YEAR MONTH DAY HH:MM:SS CORR R/S" For example: -.Dl "Leap 1974 Dec 31 23:59:60 + S +.Dl "Leap 1974 Dec 31 23:59:60 + S" The .Em YEAR , .Em MONTH , @@ -376,12 +379,81 @@ or .Dq Rolling if the leap second time given by the other fields should be interpreted as local wall clock time. -.Sh NOTE +.Sh "EXTENDED EXAMPLE" +Here is an extended example of +.Nm +input, intended to illustrate many of its features. +.br +.ne 22 +.nf +.in +2m +.ta \w'# Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'TYPE\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u +.sp +# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S +Rule Swiss 1940 only - Nov 2 0:00 1:00 S +Rule Swiss 1940 only - Dec 31 0:00 0 - +Rule Swiss 1941 1942 - May Sun>=1 2:00 1:00 S +Rule Swiss 1941 1942 - Oct Sun>=1 0:00 0 +.sp .5 +Rule EU 1977 1980 - Apr Sun>=1 1:00u 1:00 S +Rule EU 1977 only - Sep lastSun 1:00u 0 - +Rule EU 1978 only - Oct 1 1:00u 0 - +Rule EU 1979 1995 - Sep lastSun 1:00u 0 - +Rule EU 1981 max - Mar lastSun 1:00u 1:00 S +Rule EU 1996 max - Oct lastSun 1:00u 0 - +.sp +.ta \w'# Zone\0\0'u +\w'Europe/Zurich\0\0'u +\w'0:34:08\0\0'u +\w'RULES/SAVE\0\0'u +\w'FORMAT\0\0'u +# Zone NAME GMTOFF RULES FORMAT UNTIL +Zone Europe/Zurich 0:34:08 - LMT 1848 Sep 12 + 0:29:44 - BMT 1894 Jun + 1:00 Swiss CE%sT 1981 + 1:00 EU CE%sT +.sp +Link Europe/Zurich Switzerland +.sp +.in +.fi +In this example, the zone is named Europe/Zurich but it has an alias +as Switzerland. +Zurich was 34 minutes and 8 seconds west of GMT until 1848-09-12 +at 00:00, when the offset changed to 29 minutes and 44 seconds. +After 1894-06-01 at 00:00 Swiss daylight saving rules (defined with +lines beginning with "Rule Swiss") apply, and the GMT offset became +one hour. +From 1981 to the present, EU daylight saving rules have applied, +and the UTC offset has remained at one hour. +.Pp +In 1940, daylight saving time applied from November 2 at 00:00 to +December 31 at 00:00. +In 1941 and 1942, daylight saving time applied from the first Sunday +in May at 02:00 to the first Sunday in October at 00:00. +The pre-1981 EU daylight-saving rules have no effect here, but are +included for completeness. +Since 1981, daylight saving has begun on the last Sunday in March +at 01:00 UTC. +Until 1995 it ended the last Sunday in September at 01:00 UTC, but +this changed to the last Sunday in October starting in 1996. +.Pp +For purposes of display, "LMT" and "BMT" were initially used, +respectively. +Since Swiss rules and later EU rules were applied, the display name +for the timezone has been CET for standard time and CEST for daylight +saving time. +.Sh NOTES For areas with more than two types of local time, you may need to use local standard time in the .Em AT field of the earliest transition time's rule to ensure that the earliest transition time recorded in the compiled file is correct. +.Pp +If, for a particular zone, a clock advance caused by the start of +daylight saving coincides with and is equal to a clock retreat +caused by a change in UTC offset, +.Nm +produces a single transition to daylight saving at the new UTC offset +(without any change in wall clock time). +To get separate transitions use multiple zone continuation lines +specifying transition instants using universal time. .Sh FILES .Bl -tag -width /usr/share/zoneinfo -compact .It /usr/share/zoneinfo @@ -391,4 +463,6 @@ standard directory used for created files .Xr ctime 3 , .Xr tzfile 5 , .Xr zdump 8 -.\" @(#)zic.8 7.18 +.\" @(#)zic.8 8.6 +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. diff --git a/zic.tproj/zic.c b/zic.tproj/zic.c index 8efedaa..75db5be 100644 --- a/zic.tproj/zic.c +++ b/zic.tproj/zic.c @@ -1,9 +1,13 @@ -static const char elsieid[] = "@(#)zic.c 7.116"; +/* +** This file is in the public domain, so clarified as of +** 2006-07-17 by Arthur David Olson. +*/ + +static const char elsieid[] = "@(#)zic.c 8.22"; -#include #ifndef lint -__unused static const char rcsid[] = - "$FreeBSD: src/usr.sbin/zic/zic.c,v 1.18 2007/12/03 10:45:44 kevlo Exp $"; +static const char rcsid[] = + "$FreeBSD: head/contrib/tzcode/zic/zic.c 214411 2010-10-27 07:14:46Z edwin $"; #endif /* not lint */ #include "private.h" @@ -14,11 +18,19 @@ __unused static const char rcsid[] = #include #include +#define ZIC_VERSION '2' + +typedef int_fast64_t zic_t; + +#ifndef ZIC_MAX_ABBR_LEN_WO_WARN +#define ZIC_MAX_ABBR_LEN_WO_WARN 6 +#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ + #define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) /* ** On some ancient hosts, predicates like `isspace(C)' are defined -** only if isascii(C) || C == EOF. Modern hosts obey the C Standard, +** only if isascii(C) || C == EOF. Modern hosts obey the C Standard, ** which says they are defined only if C == ((unsigned char) C) || C == EOF. ** Neither the C Standard nor POSIX require that `isascii' exist. ** For portability, we check both ancient and modern requirements. @@ -29,6 +41,11 @@ __unused static const char rcsid[] = #define isascii(x) 1 #endif +#define OFFSET_STRLEN_MAXIMUM (7 + INT_STRLEN_MAXIMUM(long)) +#define RULE_STRLEN_MAXIMUM 8 /* "Mdd.dd.d" */ + +#define end(cp) (strchr((cp), '\0')) + struct rule { const char * r_filename; int r_linenum; @@ -37,6 +54,8 @@ struct rule { int r_loyear; /* for example, 1986 */ int r_hiyear; /* for example, 1986 */ const char * r_yrtype; + int r_lowasnum; + int r_hiwasnum; int r_month; /* 0..11 */ @@ -53,7 +72,7 @@ struct rule { const char * r_abbrvar; /* variable part of abbreviation */ int r_todo; /* a rule to do (used in outzone) */ - time_t r_temp; /* used in outzone */ + zic_t r_temp; /* used in outzone */ }; /* @@ -79,73 +98,81 @@ struct zone { int z_nrules; struct rule z_untilrule; - time_t z_untiltime; + zic_t z_untiltime; }; -static void addtt P((time_t starttime, int type)); -static int addtype P((long gmtoff, const char * abbr, int isdst, - int ttisstd, int ttisgmt)); -static void leapadd P((time_t t, int positive, int rolling, int count)); -static void adjleap P((void)); -static void associate P((void)); -static int ciequal P((const char * ap, const char * bp)); -static void convert P((long val, char * buf)); -static void dolink P((const char * fromfile, const char * tofile)); -static void doabbr P((char * abbr, const char * format, - const char * letters, int isdst)); -static void eat P((const char * name, int num)); -static void eats P((const char * name, int num, - const char * rname, int rnum)); -static long eitol P((int i)); -static void error P((const char * message)); -static char ** getfields P((char * buf)); -static long gethms P((const char * string, const char * errstrng, - int signable)); -static void infile P((const char * filename)); -static void inleap P((char ** fields, int nfields)); -static void inlink P((char ** fields, int nfields)); -static void inrule P((char ** fields, int nfields)); -static int inzcont P((char ** fields, int nfields)); -static int inzone P((char ** fields, int nfields)); -static int inzsub P((char ** fields, int nfields, int iscont)); -static int itsabbr P((const char * abbr, const char * word)); -static int itsdir P((const char * name)); -static int lowerit P((int c)); -static char * memcheck P((char * tocheck)); -static int mkdirs P((char * filename)); -static void newabbr P((const char * abbr)); -static long oadd P((long t1, long t2)); -static void outzone P((const struct zone * zp, int ntzones)); -static void puttzcode P((long code, FILE * fp)); -static int rcomp P((const void * leftp, const void * rightp)); -static time_t rpytime P((const struct rule * rp, int wantedy)); -static void rulesub P((struct rule * rp, +static void addtt(zic_t starttime, int type); +static int addtype(long gmtoff, const char * abbr, int isdst, + int ttisstd, int ttisgmt); +static void leapadd(zic_t t, int positive, int rolling, int count); +static void adjleap(void); +static void associate(void); +static int ciequal(const char * ap, const char * bp); +static void convert(long val, char * buf); +static void convert64(zic_t val, char * buf); +static void dolink(const char * fromfield, const char * tofield); +static void doabbr(char * abbr, const char * format, + const char * letters, int isdst, int doquotes); +static void eat(const char * name, int num); +static void eats(const char * name, int num, + const char * rname, int rnum); +static long eitol(int i); +static void error(const char * message); +static char ** getfields(char * buf); +static long gethms(const char * string, const char * errstrng, + int signable); +static void infile(const char * filename); +static void inleap(char ** fields, int nfields); +static void inlink(char ** fields, int nfields); +static void inrule(char ** fields, int nfields); +static int inzcont(char ** fields, int nfields); +static int inzone(char ** fields, int nfields); +static int inzsub(char ** fields, int nfields, int iscont); +static int is32(zic_t x); +static int itsabbr(const char * abbr, const char * word); +static int itsdir(const char * name); +static int lowerit(int c); +static char * memcheck(char * tocheck); +static int mkdirs(char * filename); +static void newabbr(const char * abbr); +static long oadd(long t1, long t2); +static void outzone(const struct zone * zp, int ntzones); +static void puttzcode(long code, FILE * fp); +static void puttzcode64(zic_t code, FILE * fp); +static int rcomp(const void * leftp, const void * rightp); +static zic_t rpytime(const struct rule * rp, int wantedy); +static void rulesub(struct rule * rp, const char * loyearp, const char * hiyearp, const char * typep, const char * monthp, - const char * dayp, const char * timep)); -static void setboundaries P((void)); -static void setgroup P((gid_t *flag, const char *name)); -static void setuser P((uid_t *flag, const char *name)); -static time_t tadd P((time_t t1, long t2)); -static void usage P((void)); -static void writezone P((const char * name)); -static int yearistype P((int year, const char * type)); - -#if !(HAVE_STRERROR - 0) -static char * strerror P((int)); -#endif /* !(HAVE_STRERROR - 0) */ + const char * dayp, const char * timep); +static int stringoffset(char * result, long offset); +static int stringrule(char * result, const struct rule * rp, + long dstoff, long gmtoff); +static void stringzone(char * result, + const struct zone * zp, int ntzones); +static void setboundaries(void); +static void setgroup(gid_t *flag, const char *name); +static void setuser(uid_t *flag, const char *name); +static zic_t tadd(zic_t t1, long t2); +static void usage(FILE *stream, int status); +static void writezone(const char * name, const char * string); +static int yearistype(int year, const char * type); static int charcnt; static int errors; static const char * filename; static int leapcnt; +static int leapseen; +static int leapminyear; +static int leapmaxyear; static int linenum; -static time_t max_time; +static int max_abbrvar_len; +static int max_format_len; +static zic_t max_time; static int max_year; -static int max_year_representable; -static time_t min_time; +static zic_t min_time; static int min_year; -static int min_year_representable; +static zic_t min_time; static int noise; static const char * rfilename; static int rlinenum; @@ -254,8 +281,8 @@ struct lookup { const int l_value; }; -static struct lookup const * byword P((const char * string, - const struct lookup * lp)); +static struct lookup const * byword(const char * string, + const struct lookup * lp); static struct lookup const line_codes[] = { { "Rule", LC_RULE }, @@ -332,7 +359,7 @@ static const int len_years[2] = { }; static struct attype { - time_t at; + zic_t at; unsigned char type; } attypes[TZ_MAX_TIMES]; static long gmtoffs[TZ_MAX_TYPES]; @@ -341,7 +368,7 @@ static unsigned char abbrinds[TZ_MAX_TYPES]; static char ttisstds[TZ_MAX_TYPES]; static char ttisgmts[TZ_MAX_TYPES]; static char chars[TZ_MAX_CHARS]; -static time_t trans[TZ_MAX_LEAPS]; +static zic_t trans[TZ_MAX_LEAPS]; static long corr[TZ_MAX_LEAPS]; static char roll[TZ_MAX_LEAPS]; @@ -350,7 +377,8 @@ static char roll[TZ_MAX_LEAPS]; */ static char * -memcheck(char * const ptr) +memcheck(ptr) +char * const ptr; { if (ptr == NULL) errx(EXIT_FAILURE, _("memory exhausted")); @@ -366,21 +394,12 @@ memcheck(char * const ptr) ** Error handling. */ -#if !(HAVE_STRERROR - 0) -static char * -strerror(int errnum) -{ - extern char * sys_errlist[]; - extern int sys_nerr; - - return (errnum > 0 && errnum <= sys_nerr) ? - sys_errlist[errnum] : _("Unknown system error"); -} -#endif /* !(HAVE_STRERROR - 0) */ - static void -eats(const char * const name, const int num, const char * const rname, - const int rnum) +eats(name, num, rname, rnum) +const char * const name; +const int num; +const char * const rname; +const int rnum; { filename = name; linenum = num; @@ -389,13 +408,16 @@ eats(const char * const name, const int num, const char * const rname, } static void -eat(const char * const name, const int num) +eat(name, num) +const char * const name; +const int num; { eats(name, num, (char *) NULL, -1); } static void -error(const char * const string) +error(string) +const char * const string; { /* ** Match the format of "cc" to allow sh users to @@ -412,7 +434,8 @@ error(const char * const string) } static void -warning(const char * const string) +warning(string) +const char * const string; { char * cp; @@ -424,12 +447,14 @@ warning(const char * const string) } static void -usage P((void)) -{ - (void) fprintf(stderr, "%s\n%s\n", -_("usage: zic [--version] [-s] [-v] [-l localtime] [-p posixrules] [-d directory]"), -_(" [-L leapseconds] [-y yearistype] [filename ... ]")); - (void) exit(EXIT_FAILURE); +usage(FILE *stream, int status) + { + (void) fprintf(stream, _("usage is zic \ +[ --version ] [--help] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\ +\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\ +\n\ +Report bugs to tz@elsie.nci.nih.gov.\n")); + exit(status); } static const char * psxrules; @@ -437,7 +462,6 @@ static const char * lcltime; static const char * directory; static const char * leapsec; static const char * yitcommand; -static int sflag = FALSE; static int Dflag; static uid_t uflag = (uid_t)-1; static gid_t gflag = (gid_t)-1; @@ -445,30 +469,39 @@ static mode_t mflag = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR); int -main(int argc, char * argv[]) +main(argc, argv) +int argc; +char * argv[]; { - int i; - int j; - int c; + register int i; + register int j; + register int c; #ifdef unix (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); #endif /* defined unix */ -#if HAVE_GETTEXT - 0 - (void) setlocale(LC_MESSAGES, ""); +#if HAVE_GETTEXT + (void) setlocale(LC_ALL, ""); #ifdef TZ_DOMAINDIR (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); #endif /* defined TEXTDOMAINDIR */ (void) textdomain(TZ_DOMAIN); -#endif /* HAVE_GETTEXT - 0 */ +#endif /* HAVE_GETTEXT */ + if (TYPE_BIT(zic_t) < 64) { + (void) fprintf(stderr, "zic: %s\n", + _("wild compilation-time specification of zic_t")); + exit(EXIT_FAILURE); + } for (i = 1; i < argc; ++i) if (strcmp(argv[i], "--version") == 0) { errx(EXIT_SUCCESS, "%s", elsieid); + } else if (strcmp(argv[i], "--help") == 0) { + usage(stdout, EXIT_SUCCESS); } while ((c = getopt(argc, argv, "Dd:g:l:m:p:L:u:vsy:")) != -1) switch (c) { default: - usage(); + usage(stderr, EXIT_FAILURE); case 'D': Dflag = 1; break; @@ -527,11 +560,11 @@ _("more than one -L option specified")); noise = TRUE; break; case 's': - sflag = TRUE; + (void) printf("zic: -s ignored\n"); break; } if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) - usage(); /* usage message by request */ + usage(stderr, EXIT_FAILURE); /* usage message by request */ if (directory == NULL) directory = TZDIR; if (yitcommand == NULL) @@ -547,7 +580,7 @@ _("more than one -L option specified")); for (i = optind; i < argc; ++i) infile(argv[i]); if (errors) - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); associate(); for (i = 0; i < nzones; i = j) { /* @@ -563,6 +596,11 @@ _("more than one -L option specified")); for (i = 0; i < nlinks; ++i) { eat(links[i].l_filename, links[i].l_linenum); dolink(links[i].l_from, links[i].l_to); + if (noise) + for (j = 0; j < nlinks; ++j) + if (strcmp(links[i].l_to, + links[j].l_from) == 0) + warning(_("link to link")); } if (lcltime != NULL) { eat("command line", 1); @@ -576,24 +614,26 @@ _("more than one -L option specified")); } static void -dolink(const char * const fromfile, const char * const tofile) +dolink(fromfield, tofield) +const char * const fromfield; +const char * const tofield; { - char * fromname; - char * toname; + register char * fromname; + register char * toname; - if (fromfile[0] == '/') - fromname = ecpyalloc(fromfile); + if (fromfield[0] == '/') + fromname = ecpyalloc(fromfield); else { fromname = ecpyalloc(directory); fromname = ecatalloc(fromname, "/"); - fromname = ecatalloc(fromname, fromfile); + fromname = ecatalloc(fromname, fromfield); } - if (tofile[0] == '/') - toname = ecpyalloc(tofile); + if (tofield[0] == '/') + toname = ecpyalloc(tofield); else { toname = ecpyalloc(directory); toname = ecatalloc(toname, "/"); - toname = ecatalloc(toname, tofile); + toname = ecatalloc(toname, tofield); } /* ** We get to be careful here since @@ -605,25 +645,30 @@ dolink(const char * const fromfile, const char * const tofile) int result; if (mkdirs(toname) != 0) - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); result = link(fromname, toname); -#if (HAVE_SYMLINK - 0) +#if HAVE_SYMLINK if (result != 0 && - access(fromname, F_OK) == 0 && - !itsdir(fromname)) { - const char *s = tofile; - char * symlinkcontents = NULL; - while ((s = strchr(s+1, '/')) != NULL) - symlinkcontents = ecatalloc(symlinkcontents, "../"); - symlinkcontents = ecatalloc(symlinkcontents, fromfile); - - result = symlink(symlinkcontents, toname); - if (result == 0) + access(fromname, F_OK) == 0 && + !itsdir(fromname)) { + const char *s = tofield; + register char * symlinkcontents = NULL; + while ((s = strchr(s+1, '/')) != NULL) + symlinkcontents = + ecatalloc(symlinkcontents, + "../"); + symlinkcontents = + ecatalloc(symlinkcontents, + fromname); + result = + symlink(symlinkcontents, + toname); + if (result == 0) warning(_("hard link failed, symbolic link used")); - ifree(symlinkcontents); + ifree(symlinkcontents); } -#endif +#endif /* HAVE_SYMLINK */ if (result != 0) { err(EXIT_FAILURE, _("can't link from %s to %s"), fromname, toname); @@ -633,50 +678,25 @@ warning(_("hard link failed, symbolic link used")); ifree(toname); } -#ifndef INT_MAX -#define INT_MAX ((int) (((unsigned)~0)>>1)) -#endif /* !defined INT_MAX */ - -#ifndef INT_MIN -#define INT_MIN ((int) ~(((unsigned)~0)>>1)) -#endif /* !defined INT_MIN */ - -/* -** The tz file format currently allows at most 32-bit quantities. -** This restriction should be removed before signed 32-bit values -** wrap around in 2038, but unfortunately this will require a -** change to the tz file format. -*/ - -#define MAX_BITS_IN_FILE 32 -#define TIME_T_BITS_IN_FILE ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE) +#define TIME_T_BITS_IN_FILE 64 static void -setboundaries P((void)) +setboundaries (void) { - if (TYPE_SIGNED(time_t)) { - min_time = ~ (time_t) 0; - min_time <<= TIME_T_BITS_IN_FILE - 1; - max_time = ~ (time_t) 0 - min_time; - if (sflag) - min_time = 0; - } else { - min_time = 0; - max_time = 2 - sflag; - max_time <<= TIME_T_BITS_IN_FILE - 1; - --max_time; - } - min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year; - max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year; - min_year_representable = min_year; - max_year_representable = max_year; + register int i; + + min_time = -1; + for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) + min_time *= 2; + max_time = -(min_time + 1); } static int -itsdir(const char * const name) +itsdir(name) +const char * const name; { - char * myname; - int accres; + register char * myname; + register int accres; myname = ecpyalloc(name); myname = ecatalloc(myname, "/."); @@ -694,19 +714,21 @@ itsdir(const char * const name) */ static int -rcomp(const void *cp1, const void *cp2) +rcomp(cp1, cp2) +const void * cp1; +const void * cp2; { return strcmp(((const struct rule *) cp1)->r_name, ((const struct rule *) cp2)->r_name); } static void -associate P((void)) +associate(void) { - struct zone * zp; - struct rule * rp; - int base, out; - int i, j; + register struct zone * zp; + register struct rule * rp; + register int base, out; + register int i, j; if (nrules != 0) { (void) qsort((void *) rules, (size_t) nrules, @@ -763,7 +785,7 @@ associate P((void)) */ eat(zp->z_filename, zp->z_linenum); zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"), - TRUE); + TRUE); /* ** Note, though, that if there's no rule, ** a '%s' in the format is a bad thing. @@ -773,20 +795,21 @@ associate P((void)) } } if (errors) - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } static void -infile(const char * name) +infile(name) +const char * name; { - FILE * fp; - char ** fields; - char * cp; - const struct lookup * lp; - int nfields; - int wantcont; - int num; - char buf[BUFSIZ]; + register FILE * fp; + register char ** fields; + register char * cp; + register const struct lookup * lp; + register int nfields; + register int wantcont; + register int num; + char buf[BUFSIZ]; if (strcmp(name, "-") == 0) { name = _("standard input"); @@ -801,7 +824,7 @@ infile(const char * name) cp = strchr(buf, '\n'); if (cp == NULL) { error(_("line too long")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } *cp = '\0'; fields = getfields(buf); @@ -864,9 +887,13 @@ _("panic: invalid l_value %d"), lp->l_value); */ static long -gethms(const char *string, const char * const errstring, const int signable) +gethms(string, errstring, signable) +const char * string; +const char * const errstring; +const int signable; { - int hh, mm, ss, sign; + long hh; + int mm, ss, sign; if (string == NULL || *string == '\0') return 0; @@ -876,31 +903,38 @@ gethms(const char *string, const char * const errstring, const int signable) sign = -1; ++string; } else sign = 1; - if (sscanf(string, scheck(string, "%d"), &hh) == 1) + if (sscanf(string, scheck(string, "%ld"), &hh) == 1) mm = ss = 0; - else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2) + else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2) ss = 0; - else if (sscanf(string, scheck(string, "%d:%d:%d"), + else if (sscanf(string, scheck(string, "%ld:%d:%d"), &hh, &mm, &ss) != 3) { error(errstring); return 0; } - if ((hh < 0 || hh >= HOURSPERDAY || + if (hh < 0 || mm < 0 || mm >= MINSPERHOUR || - ss < 0 || ss > SECSPERMIN) && - !(hh == HOURSPERDAY && mm == 0 && ss == 0)) { + ss < 0 || ss > SECSPERMIN) { error(errstring); return 0; } - if (noise && hh == HOURSPERDAY) + if (LONG_MAX / SECSPERHOUR < hh) { + error(_("time overflow")); + return 0; + } + if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0) warning(_("24:00 not handled by pre-1998 versions of zic")); - return eitol(sign) * - (eitol(hh * MINSPERHOUR + mm) * - eitol(SECSPERMIN) + eitol(ss)); + if (noise && (hh > HOURSPERDAY || + (hh == HOURSPERDAY && (mm != 0 || ss != 0)))) +warning(_("values over 24 hours not handled by pre-2007 versions of zic")); + return oadd(eitol(sign) * hh * eitol(SECSPERHOUR), + eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss))); } static void -inrule(char ** const fields, const int nfields) +inrule(fields, nfields) +register char ** const fields; +const int nfields; { static struct rule r; @@ -919,15 +953,19 @@ inrule(char ** const fields, const int nfields) fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); r.r_name = ecpyalloc(fields[RF_NAME]); r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); + if (max_abbrvar_len < strlen(r.r_abbrvar)) + max_abbrvar_len = strlen(r.r_abbrvar); rules = (struct rule *) (void *) erealloc((char *) rules, (int) ((nrules + 1) * sizeof *rules)); rules[nrules++] = r; } static int -inzone(char ** const fields, const int nfields) +inzone(fields, nfields) +register char ** const fields; +const int nfields; { - int i; + register int i; static char * buf; if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) { @@ -968,7 +1006,9 @@ _("duplicate zone name %s (file \"%s\", line %d)"), } static int -inzcont(char ** const fields, const int nfields) +inzcont(fields, nfields) +register char ** const fields; +const int nfields; { if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) { error(_("wrong number of fields on Zone continuation line")); @@ -978,14 +1018,17 @@ inzcont(char ** const fields, const int nfields) } static int -inzsub(char ** const fields, const int nfields, const int iscont) +inzsub(fields, nfields, iscont) +register char ** const fields; +const int nfields; +const int iscont; { - char * cp; + register char * cp; static struct zone z; - int i_gmtoff, i_rule, i_format; - int i_untilyear, i_untilmonth; - int i_untilday, i_untiltime; - int hasuntil; + register int i_gmtoff, i_rule, i_format; + register int i_untilyear, i_untilmonth; + register int i_untilday, i_untiltime; + register int hasuntil; if (iscont) { i_gmtoff = ZFC_GMTOFF; @@ -1017,6 +1060,8 @@ inzsub(char ** const fields, const int nfields, const int iscont) } z.z_rule = ecpyalloc(fields[i_rule]); z.z_format = ecpyalloc(fields[i_format]); + if (max_format_len < strlen(z.z_format)) + max_format_len = strlen(z.z_format); hasuntil = nfields > i_untilyear; if (hasuntil) { z.z_untilrule.r_filename = filename; @@ -1037,7 +1082,9 @@ inzsub(char ** const fields, const int nfields, const int iscont) zones[nzones - 1].z_untiltime > min_time && zones[nzones - 1].z_untiltime < max_time && zones[nzones - 1].z_untiltime >= z.z_untiltime) { - error(_("Zone continuation line end time is not after end time of previous line")); + error(_( +"Zone continuation line end time is not after end time of previous line" + )); return FALSE; } } @@ -1052,14 +1099,16 @@ inzsub(char ** const fields, const int nfields, const int iscont) } static void -inleap(char ** const fields, const int nfields) +inleap(fields, nfields) +register char ** const fields; +const int nfields; { - const char * cp; - const struct lookup * lp; - int i, j; - int year, month, day; - long dayoff, tod; - time_t t; + register const char * cp; + register const struct lookup * lp; + register int i, j; + int year, month, day; + long dayoff, tod; + zic_t t; if (nfields != LEAP_FIELDS) { error(_("wrong number of fields on Leap line")); @@ -1068,12 +1117,17 @@ inleap(char ** const fields, const int nfields) dayoff = 0; cp = fields[LP_YEAR]; if (sscanf(cp, scheck(cp, "%d"), &year) != 1) { - /* - * Leapin' Lizards! - */ - error(_("invalid leaping year")); - return; + /* + ** Leapin' Lizards! + */ + error(_("invalid leaping year")); + return; } + if (!leapseen || leapmaxyear < year) + leapmaxyear = year; + if (!leapseen || leapminyear > year) + leapminyear = year; + leapseen = TRUE; j = EPOCH_YEAR; while (j != year) { if (year > j) { @@ -1103,7 +1157,7 @@ inleap(char ** const fields, const int nfields) return; } dayoff = oadd(dayoff, eitol(day - 1)); - if (dayoff < 0 && !TYPE_SIGNED(time_t)) { + if (dayoff < 0 && !TYPE_SIGNED(zic_t)) { error(_("time before zero")); return; } @@ -1115,12 +1169,12 @@ inleap(char ** const fields, const int nfields) error(_("time too large")); return; } - t = (time_t) dayoff * SECSPERDAY; + t = (zic_t) dayoff * SECSPERDAY; tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); cp = fields[LP_CORR]; { - int positive; - int count; + register int positive; + int count; if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ positive = FALSE; @@ -1139,7 +1193,9 @@ inleap(char ** const fields, const int nfields) return; } if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) { - error(_("illegal Rolling/Stationary field on Leap line")); + error(_( + "illegal Rolling/Stationary field on Leap line" + )); return; } leapadd(tadd(t, tod), positive, lp->l_value, count); @@ -1147,7 +1203,9 @@ inleap(char ** const fields, const int nfields) } static void -inlink(char ** const fields, const int nfields) +inlink(fields, nfields) +register char ** const fields; +const int nfields; { struct link l; @@ -1173,15 +1231,19 @@ inlink(char ** const fields, const int nfields) } static void -rulesub(struct rule * const rp, const char * const loyearp, - const char * const hiyearp, const char * const typep, - const char * const monthp, const char * const dayp, - const char * const timep) +rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep) +register struct rule * const rp; +const char * const loyearp; +const char * const hiyearp; +const char * const typep; +const char * const monthp; +const char * const dayp; +const char * const timep; { - const struct lookup * lp; - const char * cp; - char * dp; - char * ep; + register const struct lookup * lp; + register const char * cp; + register char * dp; + register char * ep; if ((lp = byword(monthp, mon_names)) == NULL) { error(_("invalid month name")); @@ -1220,7 +1282,8 @@ rulesub(struct rule * const rp, const char * const loyearp, */ cp = loyearp; lp = byword(cp, begin_years); - if (lp != NULL) switch ((int) lp->l_value) { + rp->r_lowasnum = lp == NULL; + if (!rp->r_lowasnum) switch ((int) lp->l_value) { case YR_MINIMUM: rp->r_loyear = INT_MIN; break; @@ -1233,14 +1296,11 @@ rulesub(struct rule * const rp, const char * const loyearp, } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) { error(_("invalid starting year")); return; - } else if (noise) { - if (rp->r_loyear < min_year_representable) - warning(_("starting year too low to be represented")); - else if (rp->r_loyear > max_year_representable) - warning(_("starting year too high to be represented")); } cp = hiyearp; - if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) { + lp = byword(cp, end_years); + rp->r_hiwasnum = lp == NULL; + if (!rp->r_hiwasnum) switch ((int) lp->l_value) { case YR_MINIMUM: rp->r_hiyear = INT_MIN; break; @@ -1256,11 +1316,6 @@ rulesub(struct rule * const rp, const char * const loyearp, } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) { error(_("invalid ending year")); return; - } else if (noise) { - if (rp->r_loyear < min_year_representable) - warning(_("ending year too low to be represented")); - else if (rp->r_loyear > max_year_representable) - warning(_("ending year too high to be represented")); } if (rp->r_loyear > rp->r_hiyear) { error(_("starting year greater than ending year")); @@ -1275,8 +1330,6 @@ rulesub(struct rule * const rp, const char * const loyearp, } rp->r_yrtype = ecpyalloc(typep); } - if (rp->r_loyear < min_year && rp->r_loyear > 0) - min_year = rp->r_loyear; /* ** Day work. ** Accept things such as: @@ -1325,17 +1378,33 @@ rulesub(struct rule * const rp, const char * const loyearp, } static void -convert(const long val, char * const buf) +convert(val, buf) +const long val; +char * const buf; { - int i; - long shift; + register int i; + register int shift; for (i = 0, shift = 24; i < 4; ++i, shift -= 8) buf[i] = val >> shift; } static void -puttzcode(const long val, FILE * const fp) +convert64(val, buf) +const zic_t val; +char * const buf; +{ + register int i; + register int shift; + + for (i = 0, shift = 56; i < 8; ++i, shift -= 8) + buf[i] = val >> shift; +} + +static void +puttzcode(val, fp) +const long val; +FILE * const fp; { char buf[4]; @@ -1343,25 +1412,50 @@ puttzcode(const long val, FILE * const fp) (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); } -static int -atcomp(const void *avp, const void *bvp) +static void +puttzcode64(val, fp) +const zic_t val; +FILE * const fp; { - if (((const struct attype *) avp)->at < ((const struct attype *) bvp)->at) - return -1; - else if (((const struct attype *) avp)->at > ((const struct attype *) bvp)->at) - return 1; - else return 0; + char buf[8]; + + convert64(val, buf); + (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); +} + +static int +atcomp(avp, bvp) +const void * avp; +const void * bvp; +{ + const zic_t a = ((const struct attype *) avp)->at; + const zic_t b = ((const struct attype *) bvp)->at; + + return (a < b) ? -1 : (a > b); +} + +static int +is32(x) +const zic_t x; +{ + return INT32_MIN <= x && x <= INT32_MAX; } static void -writezone(const char * const name) +writezone(name, string) +const char * const name; +const char * const string; { - FILE * fp; - int i, j; - static char * fullname; - static struct tzhead tzh; - time_t ats[TZ_MAX_TIMES]; - unsigned char types[TZ_MAX_TIMES]; + register FILE * fp; + register int i, j; + register int leapcnt32, leapi32; + register int timecnt32, timei32; + register int pass; + static char * fullname; + static const struct tzhead tzh0; + static struct tzhead tzh; + zic_t ats[TZ_MAX_TIMES]; + unsigned char types[TZ_MAX_TIMES]; /* ** Sort. @@ -1384,14 +1478,13 @@ writezone(const char * const name) while (fromi < timecnt && attypes[fromi].type == 0) ++fromi; /* handled by default rule */ for ( ; fromi < timecnt; ++fromi) { - if (toi != 0 - && ((attypes[fromi].at - + gmtoffs[attypes[toi - 1].type]) - <= (attypes[toi - 1].at - + gmtoffs[toi == 1 ? 0 - : attypes[toi - 2].type]))) { - attypes[toi - 1].type = attypes[fromi].type; - continue; + if (toi != 0 && ((attypes[fromi].at + + gmtoffs[attypes[toi - 1].type]) <= + (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0 + : attypes[toi - 2].type]))) { + attypes[toi - 1].type = + attypes[fromi].type; + continue; } if (toi == 0 || attypes[toi - 1].type != attypes[fromi].type) @@ -1406,6 +1499,36 @@ writezone(const char * const name) ats[i] = attypes[i].at; types[i] = attypes[i].type; } + /* + ** Correct for leap seconds. + */ + for (i = 0; i < timecnt; ++i) { + j = leapcnt; + while (--j >= 0) + if (ats[i] > trans[j] - corr[j]) { + ats[i] = tadd(ats[i], corr[j]); + break; + } + } + /* + ** Figure out 32-bit-limited starts and counts. + */ + timecnt32 = timecnt; + timei32 = 0; + leapcnt32 = leapcnt; + leapi32 = 0; + while (timecnt32 > 0 && !is32(ats[timecnt32 - 1])) + --timecnt32; + while (timecnt32 > 0 && !is32(ats[timei32])) { + --timecnt32; + ++timei32; + } + while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1])) + --leapcnt32; + while (leapcnt32 > 0 && !is32(trans[leapi32])) { + --leapcnt32; + ++leapi32; + } fullname = erealloc(fullname, (int) (strlen(directory) + 1 + strlen(name) + 1)); (void) sprintf(fullname, "%s/%s", directory, name); @@ -1418,70 +1541,201 @@ writezone(const char * const name) if ((fp = fopen(fullname, "wb")) == NULL) { if (mkdirs(fullname) != 0) - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); if ((fp = fopen(fullname, "wb")) == NULL) err(EXIT_FAILURE, _("can't create %s"), fullname); } - convert(eitol(typecnt), tzh.tzh_ttisgmtcnt); - convert(eitol(typecnt), tzh.tzh_ttisstdcnt); - convert(eitol(leapcnt), tzh.tzh_leapcnt); - convert(eitol(timecnt), tzh.tzh_timecnt); - convert(eitol(typecnt), tzh.tzh_typecnt); - convert(eitol(charcnt), tzh.tzh_charcnt); - (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); -#define DO(field) (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp) - DO(tzh_magic); - DO(tzh_reserved); - DO(tzh_ttisgmtcnt); - DO(tzh_ttisstdcnt); - DO(tzh_leapcnt); - DO(tzh_timecnt); - DO(tzh_typecnt); - DO(tzh_charcnt); + for (pass = 1; pass <= 2; ++pass) { + register int thistimei, thistimecnt; + register int thisleapi, thisleapcnt; + register int thistimelim, thisleaplim; + int writetype[TZ_MAX_TIMES]; + int typemap[TZ_MAX_TYPES]; + register int thistypecnt; + char thischars[TZ_MAX_CHARS]; + char thischarcnt; + int indmap[TZ_MAX_CHARS]; + + if (pass == 1) { + thistimei = timei32; + thistimecnt = timecnt32; + thisleapi = leapi32; + thisleapcnt = leapcnt32; + } else { + thistimei = 0; + thistimecnt = timecnt; + thisleapi = 0; + thisleapcnt = leapcnt; + } + thistimelim = thistimei + thistimecnt; + thisleaplim = thisleapi + thisleapcnt; + for (i = 0; i < typecnt; ++i) + writetype[i] = thistimecnt == timecnt; + if (thistimecnt == 0) { + /* + ** No transition times fall in the current + ** (32- or 64-bit) window. + */ + if (typecnt != 0) + writetype[typecnt - 1] = TRUE; + } else { + for (i = thistimei - 1; i < thistimelim; ++i) + if (i >= 0) + writetype[types[i]] = TRUE; + /* + ** For America/Godthab and Antarctica/Palmer + */ + if (thistimei == 0) + writetype[0] = TRUE; + } +#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH + /* + ** For some pre-2011 systems: if the last-to-be-written + ** standard (or daylight) type has an offset different from the + ** most recently used offset, + ** append an (unused) copy of the most recently used type + ** (to help get global "altzone" and "timezone" variables + ** set correctly). + */ + { + register int mrudst, mrustd, hidst, histd, type; + + hidst = histd = mrudst = mrustd = -1; + for (i = thistimei; i < thistimelim; ++i) + if (isdsts[types[i]]) + mrudst = types[i]; + else mrustd = types[i]; + for (i = 0; i < typecnt; ++i) + if (writetype[i]) { + if (isdsts[i]) + hidst = i; + else histd = i; + } + if (hidst >= 0 && mrudst >= 0 && hidst != mrudst && + gmtoffs[hidst] != gmtoffs[mrudst]) { + isdsts[mrudst] = -1; + type = addtype(gmtoffs[mrudst], + &chars[abbrinds[mrudst]], + TRUE, + ttisstds[mrudst], + ttisgmts[mrudst]); + isdsts[mrudst] = TRUE; + writetype[type] = TRUE; + } + if (histd >= 0 && mrustd >= 0 && histd != mrustd && + gmtoffs[histd] != gmtoffs[mrustd]) { + isdsts[mrustd] = -1; + type = addtype(gmtoffs[mrustd], + &chars[abbrinds[mrustd]], + FALSE, + ttisstds[mrustd], + ttisgmts[mrustd]); + isdsts[mrustd] = FALSE; + writetype[type] = TRUE; + } + } +#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */ + thistypecnt = 0; + for (i = 0; i < typecnt; ++i) + typemap[i] = writetype[i] ? thistypecnt++ : -1; + for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i) + indmap[i] = -1; + thischarcnt = 0; + for (i = 0; i < typecnt; ++i) { + register char * thisabbr; + + if (!writetype[i]) + continue; + if (indmap[abbrinds[i]] >= 0) + continue; + thisabbr = &chars[abbrinds[i]]; + for (j = 0; j < thischarcnt; ++j) + if (strcmp(&thischars[j], thisabbr) == 0) + break; + if (j == thischarcnt) { + (void) strcpy(&thischars[(int) thischarcnt], + thisabbr); + thischarcnt += strlen(thisabbr) + 1; + } + indmap[abbrinds[i]] = j; + } +#define DO(field) (void) fwrite((void *) tzh.field, \ + (size_t) sizeof tzh.field, (size_t) 1, fp) + tzh = tzh0; + (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); + tzh.tzh_version[0] = ZIC_VERSION; + convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt); + convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt); + convert(eitol(thisleapcnt), tzh.tzh_leapcnt); + convert(eitol(thistimecnt), tzh.tzh_timecnt); + convert(eitol(thistypecnt), tzh.tzh_typecnt); + convert(eitol(thischarcnt), tzh.tzh_charcnt); + DO(tzh_magic); + DO(tzh_version); + DO(tzh_reserved); + DO(tzh_ttisgmtcnt); + DO(tzh_ttisstdcnt); + DO(tzh_leapcnt); + DO(tzh_timecnt); + DO(tzh_typecnt); + DO(tzh_charcnt); #undef DO - for (i = 0; i < timecnt; ++i) { - j = leapcnt; - while (--j >= 0) - if (ats[i] >= trans[j]) { - ats[i] = tadd(ats[i], corr[j]); - break; + for (i = thistimei; i < thistimelim; ++i) + if (pass == 1) + puttzcode((long) ats[i], fp); + else puttzcode64(ats[i], fp); + for (i = thistimei; i < thistimelim; ++i) { + unsigned char uc; + + uc = typemap[types[i]]; + (void) fwrite((void *) &uc, + (size_t) sizeof uc, + (size_t) 1, + fp); + } + for (i = 0; i < typecnt; ++i) + if (writetype[i]) { + puttzcode(gmtoffs[i], fp); + (void) putc(isdsts[i], fp); + (void) putc((unsigned char) indmap[abbrinds[i]], fp); } - puttzcode((long) ats[i], fp); + if (thischarcnt != 0) + (void) fwrite((void *) thischars, + (size_t) sizeof thischars[0], + (size_t) thischarcnt, fp); + for (i = thisleapi; i < thisleaplim; ++i) { + register zic_t todo; + + if (roll[i]) { + if (timecnt == 0 || trans[i] < ats[0]) { + j = 0; + while (isdsts[j]) + if (++j >= typecnt) { + j = 0; + break; + } + } else { + j = 1; + while (j < timecnt && + trans[i] >= ats[j]) + ++j; + j = types[j - 1]; + } + todo = tadd(trans[i], -gmtoffs[j]); + } else todo = trans[i]; + if (pass == 1) + puttzcode((long) todo, fp); + else puttzcode64(todo, fp); + puttzcode(corr[i], fp); + } + for (i = 0; i < typecnt; ++i) + if (writetype[i]) + (void) putc(ttisstds[i], fp); + for (i = 0; i < typecnt; ++i) + if (writetype[i]) + (void) putc(ttisgmts[i], fp); } - if (timecnt > 0) - (void) fwrite((void *) types, (size_t) sizeof types[0], - (size_t) timecnt, fp); - for (i = 0; i < typecnt; ++i) { - puttzcode((long) gmtoffs[i], fp); - (void) putc(isdsts[i], fp); - (void) putc(abbrinds[i], fp); - } - if (charcnt != 0) - (void) fwrite((void *) chars, (size_t) sizeof chars[0], - (size_t) charcnt, fp); - for (i = 0; i < leapcnt; ++i) { - if (roll[i]) { - if (timecnt == 0 || trans[i] < ats[0]) { - j = 0; - while (isdsts[j]) - if (++j >= typecnt) { - j = 0; - break; - } - } else { - j = 1; - while (j < timecnt && trans[i] >= ats[j]) - ++j; - j = types[j - 1]; - } - puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp); - } else puttzcode((long) trans[i], fp); - puttzcode((long) corr[i], fp); - } - for (i = 0; i < typecnt; ++i) - (void) putc(ttisstds[i], fp); - for (i = 0; i < typecnt; ++i) - (void) putc(ttisgmts[i], fp); + (void) fprintf(fp, "\n%s\n", string); if (ferror(fp) || fclose(fp)) errx(EXIT_FAILURE, _("error writing %s"), fullname); if (chmod(fullname, mflag) < 0) @@ -1494,38 +1748,254 @@ writezone(const char * const name) } static void -doabbr(char * const abbr, const char * const format, const char * const letters, - const int isdst) +doabbr(abbr, format, letters, isdst, doquotes) +char * const abbr; +const char * const format; +const char * const letters; +const int isdst; +const int doquotes; { - if (strchr(format, '/') == NULL) { + register char * cp; + register char * slashp; + register int len; + + slashp = strchr(format, '/'); + if (slashp == NULL) { if (letters == NULL) (void) strcpy(abbr, format); else (void) sprintf(abbr, format, letters); - } else if (isdst) - (void) strcpy(abbr, strchr(format, '/') + 1); - else { - (void) strcpy(abbr, format); - *strchr(abbr, '/') = '\0'; + } else if (isdst) { + (void) strcpy(abbr, slashp + 1); + } else { + if (slashp > format) + (void) strncpy(abbr, format, + (unsigned) (slashp - format)); + abbr[slashp - format] = '\0'; + } + if (!doquotes) + return; + for (cp = abbr; *cp != '\0'; ++cp) + if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL && + strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL) + break; + len = strlen(abbr); + if (len > 0 && *cp == '\0') + return; + abbr[len + 2] = '\0'; + abbr[len + 1] = '>'; + for ( ; len > 0; --len) + abbr[len] = abbr[len - 1]; + abbr[0] = '<'; +} + +static void +updateminmax(x) +const int x; +{ + if (min_year > x) + min_year = x; + if (max_year < x) + max_year = x; +} + +static int +stringoffset(result, offset) +char * result; +long offset; +{ + register int hours; + register int minutes; + register int seconds; + + result[0] = '\0'; + if (offset < 0) { + (void) strcpy(result, "-"); + offset = -offset; + } + seconds = offset % SECSPERMIN; + offset /= SECSPERMIN; + minutes = offset % MINSPERHOUR; + offset /= MINSPERHOUR; + hours = offset; + if (hours >= HOURSPERDAY) { + result[0] = '\0'; + return -1; + } + (void) sprintf(end(result), "%d", hours); + if (minutes != 0 || seconds != 0) { + (void) sprintf(end(result), ":%02d", minutes); + if (seconds != 0) + (void) sprintf(end(result), ":%02d", seconds); + } + return 0; +} + +static int +stringrule(result, rp, dstoff, gmtoff) +char * result; +const struct rule * const rp; +const long dstoff; +const long gmtoff; +{ + register long tod; + + result = end(result); + if (rp->r_dycode == DC_DOM) { + register int month, total; + + if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY) + return -1; + total = 0; + for (month = 0; month < rp->r_month; ++month) + total += len_months[0][month]; + (void) sprintf(result, "J%d", total + rp->r_dayofmonth); + } else { + register int week; + + if (rp->r_dycode == DC_DOWGEQ) { + week = 1 + rp->r_dayofmonth / DAYSPERWEEK; + if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth) + return -1; + } else if (rp->r_dycode == DC_DOWLEQ) { + if (rp->r_dayofmonth == len_months[1][rp->r_month]) + week = 5; + else { + week = 1 + rp->r_dayofmonth / DAYSPERWEEK; + if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth) + return -1; + } + } else return -1; /* "cannot happen" */ + (void) sprintf(result, "M%d.%d.%d", + rp->r_month + 1, week, rp->r_wday); + } + tod = rp->r_tod; + if (rp->r_todisgmt) + tod += gmtoff; + if (rp->r_todisstd && rp->r_stdoff == 0) + tod += dstoff; + if (tod < 0) { + result[0] = '\0'; + return -1; + } + if (tod != 2 * SECSPERMIN * MINSPERHOUR) { + (void) strcat(result, "/"); + if (stringoffset(end(result), tod) != 0) + return -1; + } + return 0; +} + +static void +stringzone(result, zpfirst, zonecount) +char * result; +const struct zone * const zpfirst; +const int zonecount; +{ + register const struct zone * zp; + register struct rule * rp; + register struct rule * stdrp; + register struct rule * dstrp; + register int i; + register const char * abbrvar; + + result[0] = '\0'; + zp = zpfirst + zonecount - 1; + stdrp = dstrp = NULL; + for (i = 0; i < zp->z_nrules; ++i) { + rp = &zp->z_rules[i]; + if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX) + continue; + if (rp->r_yrtype != NULL) + continue; + if (rp->r_stdoff == 0) { + if (stdrp == NULL) + stdrp = rp; + else return; + } else { + if (dstrp == NULL) + dstrp = rp; + else return; + } + } + if (stdrp == NULL && dstrp == NULL) { + /* + ** There are no rules running through "max". + ** Let's find the latest rule. + */ + for (i = 0; i < zp->z_nrules; ++i) { + rp = &zp->z_rules[i]; + if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear || + (rp->r_hiyear == stdrp->r_hiyear && + rp->r_month > stdrp->r_month)) + stdrp = rp; + } + if (stdrp != NULL && stdrp->r_stdoff != 0) + return; /* We end up in DST (a POSIX no-no). */ + /* + ** Horrid special case: if year is 2037, + ** presume this is a zone handled on a year-by-year basis; + ** do not try to apply a rule to the zone. + */ + if (stdrp != NULL && stdrp->r_hiyear == 2037) + return; + } + if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0)) + return; + abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; + doabbr(result, zp->z_format, abbrvar, FALSE, TRUE); + if (stringoffset(end(result), -zp->z_gmtoff) != 0) { + result[0] = '\0'; + return; + } + if (dstrp == NULL) + return; + doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE); + if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) + if (stringoffset(end(result), + -(zp->z_gmtoff + dstrp->r_stdoff)) != 0) { + result[0] = '\0'; + return; + } + (void) strcat(result, ","); + if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) { + result[0] = '\0'; + return; + } + (void) strcat(result, ","); + if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) { + result[0] = '\0'; + return; } } static void -outzone(const struct zone * const zpfirst, const int zonecount) +outzone(zpfirst, zonecount) +const struct zone * const zpfirst; +const int zonecount; { - const struct zone * zp; - struct rule * rp; - int i, j; - int usestart, useuntil; - time_t starttime, untiltime; - long gmtoff; - long stdoff; - int year; - long startoff; - int startttisstd; - int startttisgmt; - int type; - char startbuf[BUFSIZ]; + register const struct zone * zp; + register struct rule * rp; + register int i, j; + register int usestart, useuntil; + register zic_t starttime, untiltime; + register long gmtoff; + register long stdoff; + register int year; + register long startoff; + register int startttisstd; + register int startttisgmt; + register int type; + register char * startbuf; + register char * ab; + register char * envvar; + register int max_abbr_len; + register int max_envvar_len; + max_abbr_len = 2 + max_format_len + max_abbrvar_len; + max_envvar_len = 2 * max_abbr_len + 5 * 9; + startbuf = emalloc(max_abbr_len + 1); + ab = emalloc(max_abbr_len + 1); + envvar = emalloc(max_envvar_len + 1); INITIALIZE(untiltime); INITIALIZE(starttime); /* @@ -1535,11 +2005,57 @@ outzone(const struct zone * const zpfirst, const int zonecount) typecnt = 0; charcnt = 0; /* - ** Thanks to Earl Chew (earl@dnd.icp.nec.com.au) + ** Thanks to Earl Chew ** for noting the need to unconditionally initialize startttisstd. */ startttisstd = FALSE; startttisgmt = FALSE; + min_year = max_year = EPOCH_YEAR; + if (leapseen) { + updateminmax(leapminyear); + updateminmax(leapmaxyear + (leapmaxyear < INT_MAX)); + } + for (i = 0; i < zonecount; ++i) { + zp = &zpfirst[i]; + if (i < zonecount - 1) + updateminmax(zp->z_untilrule.r_loyear); + for (j = 0; j < zp->z_nrules; ++j) { + rp = &zp->z_rules[j]; + if (rp->r_lowasnum) + updateminmax(rp->r_loyear); + if (rp->r_hiwasnum) + updateminmax(rp->r_hiyear); + } + } + /* + ** Generate lots of data if a rule can't cover all future times. + */ + stringzone(envvar, zpfirst, zonecount); + if (noise && envvar[0] == '\0') { + register char * wp; + +wp = ecpyalloc(_("no POSIX environment variable for zone")); + wp = ecatalloc(wp, " "); + wp = ecatalloc(wp, zpfirst->z_name); + warning(wp); + ifree(wp); + } + if (envvar[0] == '\0') { + if (min_year >= INT_MIN + YEARSPERREPEAT) + min_year -= YEARSPERREPEAT; + else min_year = INT_MIN; + if (max_year <= INT_MAX - YEARSPERREPEAT) + max_year += YEARSPERREPEAT; + else max_year = INT_MAX; + } + /* + ** For the benefit of older systems, + ** generate data from 1900 through 2037. + */ + if (min_year > 1900) + min_year = 1900; + if (max_year < 2037) + max_year = 2037; for (i = 0; i < zonecount; ++i) { /* ** A guess that may well be corrected later. @@ -1557,7 +2073,7 @@ outzone(const struct zone * const zpfirst, const int zonecount) if (zp->z_nrules == 0) { stdoff = zp->z_stdoff; doabbr(startbuf, zp->z_format, - (char *) NULL, stdoff != 0); + (char *) NULL, stdoff != 0, FALSE); type = addtype(oadd(zp->z_gmtoff, stdoff), startbuf, stdoff != 0, startttisstd, startttisgmt); @@ -1584,10 +2100,9 @@ outzone(const struct zone * const zpfirst, const int zonecount) rp->r_temp = rpytime(rp, year); } for ( ; ; ) { - int k; - time_t jtime, ktime; - long offset; - char buf[BUFSIZ]; + register int k; + register zic_t jtime, ktime; + register long offset; INITIALIZE(ktime); if (useuntil) { @@ -1643,23 +2158,27 @@ outzone(const struct zone * const zpfirst, const int zonecount) stdoff); doabbr(startbuf, zp->z_format, rp->r_abbrvar, - rp->r_stdoff != 0); + rp->r_stdoff != 0, + FALSE); continue; } if (*startbuf == '\0' && - startoff == oadd(zp->z_gmtoff, - stdoff)) { - doabbr(startbuf, zp->z_format, - rp->r_abbrvar, - rp->r_stdoff != 0); + startoff == oadd(zp->z_gmtoff, + stdoff)) { + doabbr(startbuf, + zp->z_format, + rp->r_abbrvar, + rp->r_stdoff != + 0, + FALSE); } } eats(zp->z_filename, zp->z_linenum, rp->r_filename, rp->r_linenum); - doabbr(buf, zp->z_format, rp->r_abbrvar, - rp->r_stdoff != 0); + doabbr(ab, zp->z_format, rp->r_abbrvar, + rp->r_stdoff != 0, FALSE); offset = oadd(zp->z_gmtoff, rp->r_stdoff); - type = addtype(offset, buf, rp->r_stdoff != 0, + type = addtype(offset, ab, rp->r_stdoff != 0, rp->r_todisstd, rp->r_todisgmt); addtt(ktime, type); } @@ -1692,11 +2211,16 @@ error(_("can't determine time zone abbreviation to use just after until time")); starttime = tadd(starttime, -gmtoff); } } - writezone(zpfirst->z_name); + writezone(zpfirst->z_name, envvar); + ifree(startbuf); + ifree(ab); + ifree(envvar); } static void -addtt(const time_t starttime, int type) +addtt(starttime, type) +const zic_t starttime; +int type; { if (starttime <= min_time || (timecnt == 1 && attypes[0].at < min_time)) { @@ -1707,14 +2231,14 @@ addtt(const time_t starttime, int type) if (abbrinds[type] != 0) (void) strcpy(chars, &chars[abbrinds[type]]); abbrinds[0] = 0; - charcnt = (int)strlen(chars) + 1; + charcnt = strlen(chars) + 1; typecnt = 1; timecnt = 0; type = 0; } if (timecnt >= TZ_MAX_TIMES) { error(_("too many transitions?!")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } attypes[timecnt].at = starttime; attypes[timecnt].type = type; @@ -1722,22 +2246,26 @@ addtt(const time_t starttime, int type) } static int -addtype(const long gmtoff, const char * const abbr, const int isdst, - const int ttisstd, const int ttisgmt) +addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt) +const long gmtoff; +const char * const abbr; +const int isdst; +const int ttisstd; +const int ttisgmt; { - int i, j; + register int i, j; if (isdst != TRUE && isdst != FALSE) { error(_("internal error - addtype called with bad isdst")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } if (ttisstd != TRUE && ttisstd != FALSE) { error(_("internal error - addtype called with bad ttisstd")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } if (ttisgmt != TRUE && ttisgmt != FALSE) { error(_("internal error - addtype called with bad ttisgmt")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } /* ** See if there's already an entry for this zone type. @@ -1756,7 +2284,11 @@ addtype(const long gmtoff, const char * const abbr, const int isdst, */ if (typecnt >= TZ_MAX_TYPES) { error(_("too many local time types")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); + } + if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) { + error(_("UTC offset out of range")); + exit(EXIT_FAILURE); } gmtoffs[i] = gmtoff; isdsts[i] = isdst; @@ -1774,19 +2306,23 @@ addtype(const long gmtoff, const char * const abbr, const int isdst, } static void -leapadd(const time_t t, const int positive, const int rolling, int count) +leapadd(t, positive, rolling, count) +const zic_t t; +const int positive; +const int rolling; +int count; { - int i, j; + register int i, j; if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) { error(_("too many leap seconds")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } for (i = 0; i < leapcnt; ++i) if (t <= trans[i]) { if (t == trans[i]) { error(_("repeated leap second moment")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } break; } @@ -1804,10 +2340,10 @@ leapadd(const time_t t, const int positive, const int rolling, int count) } static void -adjleap P((void)) +adjleap(void) { - int i; - long last = 0; + register int i; + register long last = 0; /* ** propagate leap seconds forward @@ -1819,7 +2355,9 @@ adjleap P((void)) } static int -yearistype(const int year, const char * const type) +yearistype(year, type) +const int year; +const char * const type; { static char * buf; int result; @@ -1838,19 +2376,21 @@ yearistype(const int year, const char * const type) error(_("wild result from command execution")); warnx(_("command was '%s', result was %d"), buf, result); for ( ; ; ) - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } static int -lowerit(int a) +lowerit(a) +int a; { a = (unsigned char) a; return (isascii(a) && isupper(a)) ? tolower(a) : a; } -/* case-insensitive equality */ static int -ciequal(const char *ap, const char *bp) +ciequal(ap, bp) /* case-insensitive equality */ +register const char * ap; +register const char * bp; { while (lowerit(*ap) == lowerit(*bp++)) if (*ap++ == '\0') @@ -1859,7 +2399,9 @@ ciequal(const char *ap, const char *bp) } static int -itsabbr(const char *abbr, const char *word) +itsabbr(abbr, word) +register const char * abbr; +register const char * word; { if (lowerit(*abbr) != lowerit(*word)) return FALSE; @@ -1873,10 +2415,12 @@ itsabbr(const char *abbr, const char *word) } static const struct lookup * -byword(const char * const word, const struct lookup * const table) +byword(word, table) +register const char * const word; +register const struct lookup * const table; { - const struct lookup * foundlp; - const struct lookup * lp; + register const struct lookup * foundlp; + register const struct lookup * lp; if (word == NULL || table == NULL) return NULL; @@ -1900,11 +2444,12 @@ byword(const char * const word, const struct lookup * const table) } static char ** -getfields(char *cp) +getfields(cp) +register char * cp; { - char * dp; - char ** array; - int nsubs; + register char * dp; + register char ** array; + register int nsubs; if (cp == NULL) return NULL; @@ -1912,8 +2457,9 @@ getfields(char *cp) emalloc((int) ((strlen(cp) + 1) * sizeof *array)); nsubs = 0; for ( ; ; ) { - while (isascii(*cp) && isspace((unsigned char) *cp)) - ++cp; + while (isascii((unsigned char) *cp) && + isspace((unsigned char) *cp)) + ++cp; if (*cp == '\0' || *cp == '#') break; array[nsubs++] = dp = cp; @@ -1938,22 +2484,26 @@ getfields(char *cp) } static long -oadd(const long t1, const long t2) +oadd(t1, t2) +const long t1; +const long t2; { - long t; + register long t; t = t1 + t2; if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { error(_("time overflow")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return t; } -static time_t -tadd(const time_t t1, const long t2) +static zic_t +tadd(t1, t2) +const zic_t t1; +const long t2; { - time_t t; + register zic_t t; if (t1 == max_time && t2 > 0) return max_time; @@ -1962,7 +2512,7 @@ tadd(const time_t t1, const long t2) t = t1 + t2; if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) { error(_("time overflow")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } return t; } @@ -1972,12 +2522,14 @@ tadd(const time_t t1, const long t2) ** 1970, 00:00 LOCAL time - in that year that the rule refers to. */ -static time_t -rpytime(const struct rule * const rp, const int wantedy) +static zic_t +rpytime(rp, wantedy) +register const struct rule * const rp; +register const int wantedy; { - int y, m, i; - long dayoff; /* with a nod to Margaret O. */ - time_t t; + register int y, m, i; + register long dayoff; /* with a nod to Margaret O. */ + register zic_t t; if (wantedy == INT_MIN) return min_time; @@ -2007,13 +2559,13 @@ rpytime(const struct rule * const rp, const int wantedy) --i; else { error(_("use of 2/29 in non leap-year")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } } --i; dayoff = oadd(dayoff, eitol(i)); if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) { - long wday; + register long wday; #define LDAYSPERWEEK ((long) DAYSPERWEEK) wday = eitol(EPOCH_WDAY); @@ -2041,38 +2593,77 @@ rpytime(const struct rule * const rp, const int wantedy) } if (i < 0 || i >= len_months[isleap(y)][m]) { if (noise) - warning(_("rule goes past start/end of month--will not work with pre-2004 versions of zic")); + warning(_("rule goes past start/end of month--\ +will not work with pre-2004 versions of zic")); } } - if (dayoff < 0 && !TYPE_SIGNED(time_t)) - return min_time; if (dayoff < min_time / SECSPERDAY) return min_time; if (dayoff > max_time / SECSPERDAY) return max_time; - t = (time_t) dayoff * SECSPERDAY; + t = (zic_t) dayoff * SECSPERDAY; return tadd(t, rp->r_tod); } static void -newabbr(const char * const string) +newabbr(string) +const char * const string; { - int i; + register int i; - i = (int)strlen(string) + 1; + if (strcmp(string, GRANDPARENTED) != 0) { + register const char * cp; + register char * wp; + + /* + ** Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics + ** optionally followed by a + or - and a number from 1 to 14. + */ + cp = string; + wp = NULL; + while (isascii((unsigned char) *cp) && + isalpha((unsigned char) *cp)) + ++cp; + if (cp - string == 0) +wp = _("time zone abbreviation lacks alphabetic at start"); + if (noise && cp - string > 3) +wp = _("time zone abbreviation has more than 3 alphabetics"); + if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN) +wp = _("time zone abbreviation has too many alphabetics"); + if (wp == NULL && (*cp == '+' || *cp == '-')) { + ++cp; + if (isascii((unsigned char) *cp) && + isdigit((unsigned char) *cp)) + if (*cp++ == '1' && + *cp >= '0' && *cp <= '4') + ++cp; + } + if (*cp != '\0') +wp = _("time zone abbreviation differs from POSIX standard"); + if (wp != NULL) { + wp = ecpyalloc(wp); + wp = ecatalloc(wp, " ("); + wp = ecatalloc(wp, string); + wp = ecatalloc(wp, ")"); + warning(wp); + ifree(wp); + } + } + i = strlen(string) + 1; if (charcnt + i > TZ_MAX_CHARS) { error(_("too many, or too long, time zone abbreviations")); - (void) exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } (void) strcpy(&chars[charcnt], string); charcnt += eitol(i); } static int -mkdirs(char * const argname) +mkdirs(argname) +char * argname; { - char * name; - char * cp; + register char * name; + register char * cp; if (argname == NULL || *argname == '\0' || Dflag) return 0; @@ -2110,7 +2701,8 @@ mkdirs(char * const argname) } static long -eitol(const int i) +eitol(i) +const int i; { long l; @@ -2124,7 +2716,9 @@ eitol(const int i) #include static void -setgroup(gid_t *flag, const char *name) +setgroup(flag, name) + gid_t *flag; + const char *name; { struct group *gr; @@ -2138,7 +2732,7 @@ setgroup(gid_t *flag, const char *name) ul = strtoul(name, &ep, 10); if (ul == (unsigned long)(gid_t)ul && *ep == '\0') { - *flag = (gid_t)ul; + *flag = ul; return; } errx(EXIT_FAILURE, _("group `%s' not found"), name); @@ -2147,7 +2741,9 @@ setgroup(gid_t *flag, const char *name) } static void -setuser(uid_t *flag, const char *name) +setuser(flag, name) + uid_t *flag; + const char *name; { struct passwd *pw; @@ -2161,7 +2757,7 @@ setuser(uid_t *flag, const char *name) ul = strtoul(name, &ep, 10); if (ul == (unsigned long)(gid_t)ul && *ep == '\0') { - *flag = (uid_t)ul; + *flag = ul; return; } errx(EXIT_FAILURE, _("user `%s' not found"), name); diff --git a/zlog.tproj/SymbolicationHelper.c b/zlog.tproj/SymbolicationHelper.c new file mode 100644 index 0000000..68a1285 --- /dev/null +++ b/zlog.tproj/SymbolicationHelper.c @@ -0,0 +1,181 @@ +// +// SymbolicationHelper.c +// zlog +// +// Created by Rasha Eqbal on 2/26/18. +// + +#include "SymbolicationHelper.h" + +/* + * Most of the CoreSymbolication code here has been copied from ioclasscount in the IOKitTools project. + */ + +#define kAddressKey CFSTR("Address") +#define kNameKey CFSTR("Name") +#define kPathKey CFSTR("Path") +#define kSegmentsKey CFSTR("Segments") +#define kSizeKey CFSTR("Size") +#define kUuidKey CFSTR("UUID") + +static void AddSymbolOwnerSummary(CSSymbolOwnerRef owner, CFMutableDictionaryRef binaryImages); +static void ShowBinaryImage(const void *key, const void *value, void *context); + +/* + * Symbolicates 'addr' using the 'symbolicator' passed in. + * Adds owner info to 'binaryImages' for offline symbolication. + * + * Top-level function that needs to be called on each frame address in the backtrace. + */ +void PrintSymbolicatedAddress(CSSymbolicatorRef symbolicator, mach_vm_address_t addr, CFMutableDictionaryRef binaryImages) +{ + printf("0x%llx", addr); + + CSSymbolOwnerRef ownerInfo = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, addr, kCSNow); + if (!CSIsNull(ownerInfo)) { + const char *moduleName = CSSymbolOwnerGetName(ownerInfo); + if (moduleName) { + printf(" <%s>", moduleName); + } + } + + CSSymbolRef symbolInfo = CSSymbolicatorGetSymbolWithAddressAtTime(symbolicator, addr, kCSNow); + if (!CSIsNull(symbolInfo)) { + printf(" %s", CSSymbolGetName(symbolInfo)); + } + + CSSourceInfoRef sourceInfo = CSSymbolicatorGetSourceInfoWithAddressAtTime(symbolicator, addr, kCSNow); + if (!CSIsNull(sourceInfo)) { + const char *fileName = CSSourceInfoGetPath(sourceInfo); + if (fileName) { + printf(" at %s:%d", fileName, CSSourceInfoGetLineNumber(sourceInfo)); + } + } + printf("\n"); + + AddSymbolOwnerSummary(ownerInfo, binaryImages); +} + +/* + * Adds symbolication information for 'owner' to 'binaryImages' to help with offline symbolication. + * + * This is called from PrintSymbolicatedAddress() on the symbol owner for each address it symbolicates. + */ +static void AddSymbolOwnerSummary(CSSymbolOwnerRef owner, CFMutableDictionaryRef binaryImages) +{ + const CFUUIDBytes *uuidBytes = NULL; + CFUUIDRef uuid = NULL; + CFStringRef uuidString = NULL, path = NULL, name = NULL; + CFMutableDictionaryRef summaryDict = NULL; + __block CSSegmentRef textSegment = kCSNull, textExecSegment = kCSNull; + CSSegmentRef segment = kCSNull; + CSRange range; + CFNumberRef address = NULL, size = NULL; + +#define RETURN_IF_NULL(ptr) \ +if (!(ptr)) { \ +goto cleanup; \ +} + + uuidBytes = CSSymbolOwnerGetCFUUIDBytes(owner); + if (uuidBytes) { + uuid = CFUUIDCreateFromUUIDBytes(NULL, *uuidBytes); + if (uuid) { + uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid); + } + } + RETURN_IF_NULL(uuidString); + + if (!CFDictionaryContainsKey(binaryImages, uuidString)) { + summaryDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + RETURN_IF_NULL(summaryDict); + + CFDictionarySetValue(summaryDict, kUuidKey, uuidString); + + path = CFStringCreateWithCString(kCFAllocatorDefault, CSSymbolOwnerGetPath(owner), kCFStringEncodingUTF8); + RETURN_IF_NULL(path); + CFDictionarySetValue(summaryDict, kPathKey, path); + + name = CFStringCreateWithCString(kCFAllocatorDefault, CSSymbolOwnerGetName(owner), kCFStringEncodingUTF8); + RETURN_IF_NULL(name); + CFDictionarySetValue(summaryDict, kNameKey, name); + + CSSymbolOwnerForeachSegment(owner, ^(CSSegmentRef segment) { + if (strcmp(CSRegionGetName(segment), "__TEXT SEGMENT") == 0) { + textSegment = segment; + CSRetain(textSegment); + } else if (strcmp(CSRegionGetName(segment), "__TEXT_EXEC SEGMENT") == 0) { + textExecSegment = segment; + CSRetain(textExecSegment); + } + }); + + segment = !CSIsNull(textExecSegment) ? textExecSegment : textSegment; + if (CSIsNull(segment)) { + goto cleanup; + } + range = CSRegionGetRange(segment); + + address = CFNumberCreate(NULL, kCFNumberLongLongType, &range.location); + RETURN_IF_NULL(address); + CFDictionarySetValue(summaryDict, kAddressKey, address); + + size = CFNumberCreate(NULL, kCFNumberLongLongType, &range.length); + RETURN_IF_NULL(size); + CFDictionarySetValue(summaryDict, kSizeKey, size); + + CFDictionarySetValue(binaryImages, uuidString, summaryDict); + } + +cleanup: + if (size) CFRelease(size); + if (address) CFRelease(address); + if (!CSIsNull(textExecSegment)) CSRelease(textExecSegment); + if (!CSIsNull(textSegment)) CSRelease(textSegment); + if (name) CFRelease(name); + if (path) CFRelease(path); + if (summaryDict) CFRelease(summaryDict); + if (uuidString) CFRelease(uuidString); + if (uuid) CFRelease(uuid); +} + +/* + * Prints offline symbolication information for the images passed in 'binaryImages'. + * + * Top-level function that needs to be called if the tool wants to include support + * for offline symbolication. + */ +void PrintBinaryImagesInfo(CFMutableDictionaryRef binaryImages) +{ + if (CFDictionaryGetCount(binaryImages) > 0) { + printf("\nBinary Images:\n"); + CFDictionaryApplyFunction(binaryImages, ShowBinaryImage, NULL); + } else { + printf("No binary images\n"); + } +} + +/* + * Prints information about a binary image necessary for offline symbolication. + * + * This is called from PrintBinaryImagesInfo() on each element in 'binaryImages'. + */ +static void ShowBinaryImage(const void *key, const void *value, void *context) +{ + char nameString[256] = {0}, uuidString[256] = {0}, pathString[256] = {0}; + CFStringRef uuid = (CFStringRef)key; + CFStringGetCString(uuid, uuidString, sizeof(uuidString), kCFStringEncodingASCII); + CFDictionaryRef summary = (CFDictionaryRef)value; + + CFStringRef name = CFDictionaryGetValue(summary, kNameKey); + CFStringGetCString(name, nameString, sizeof(nameString), kCFStringEncodingASCII); + CFStringRef path = CFDictionaryGetValue(summary, kPathKey); + CFStringGetCString(path, pathString, sizeof(pathString), kCFStringEncodingASCII); + CFNumberRef addressNumber = CFDictionaryGetValue(summary, kAddressKey); + CFNumberRef sizeNumber = CFDictionaryGetValue(summary, kSizeKey); + int64_t address, size; + CFNumberGetValue(addressNumber, kCFNumberSInt64Type, &address); + CFNumberGetValue(sizeNumber, kCFNumberSInt64Type, &size); + + printf("%p - %p %s <%s> %s\n", (void*)address, (void*)address + size, nameString, uuidString, pathString); +} diff --git a/zlog.tproj/SymbolicationHelper.h b/zlog.tproj/SymbolicationHelper.h new file mode 100644 index 0000000..f9d90c5 --- /dev/null +++ b/zlog.tproj/SymbolicationHelper.h @@ -0,0 +1,35 @@ +// +// SymbolicationHelper.h +// zlog +// +// Created by Rasha Eqbal on 2/26/18. +// + +#ifndef SymbolicationHelper_h +#define SymbolicationHelper_h + +#include +#include + +/* + * Call this function on each address that needs to be symbolicated. + * + * sym: The CSSymbolicatorRef which will be used for symbolication. For example, to symbolicate + * kernel addresses create a CSSymbolicatorRef by calling CSSymbolicatorCreateWithMachKernel(). + * addr: The address that needs to be symbolicated. + * binaryImages: The dictionary that aggregates binary image info for offline symbolication. + */ +void PrintSymbolicatedAddress(CSSymbolicatorRef sym, mach_vm_address_t addr, CFMutableDictionaryRef binaryImages); + +/* + * Call this function to dump binary image info required for offline symbolication. + * + * binaryImages: The dictionary that stores this info. + * + * The preferred way to use this is to create a CFMutableDictionaryRef with a call to CFDictionaryCreateMutable() + * and pass it in to PrintSymbolicatedAddress() when symbolicating addresses. This will auto-populate the dictionary, + * which just needs to be passed in here to print the relevant information. + */ +void PrintBinaryImagesInfo(CFMutableDictionaryRef binaryImages); + +#endif /* SymbolicationHelper_h */ diff --git a/zlog.tproj/entitlements.plist b/zlog.tproj/entitlements.plist new file mode 100644 index 0000000..600122d --- /dev/null +++ b/zlog.tproj/entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.private.kernel.get-kext-info + + + diff --git a/zlog.tproj/zlog.1 b/zlog.tproj/zlog.1 new file mode 100644 index 0000000..141caa9 --- /dev/null +++ b/zlog.tproj/zlog.1 @@ -0,0 +1,56 @@ +.\" Copyright (c) 2018, Apple Inc. All rights reserved. +.\" +.Dd February 21, 2018 +.Dt ZLOG 1 +.Os "Mac OS X" +.Sh NAME +.Nm zlog +.Nd show allocation backtraces for kernel zones +.Sh SYNOPSIS +.Nm +.Op Fl t +.Op Fl z Ar name Op Fl n Ar num | Fl l +.Op Fl h +.Sh DESCRIPTION +.Nm +displays allocation (and free, if used in corruption tracking mode with +the boot-arg "-zc") backtraces for zones that have zone logging enabled. +Zone logging can be turned on by using the boot-arg "zlog=", +where 'N' can range from 1 to 10, and 'name' is the name of the zone to +be tracked. +.Pp +.Nm +interprets the following options: +.Pp +.Bl -tag -width "disable -" +.\" -t +.It Fl t +(Default) list all the zones that have logging enabled +.\" -z +.It Fl z Ar name +show all allocation backtraces for zone +.Ar name +.\" -n +.It Fl n Ar num +Can be used in combination with the +.Fl z +option to show the top +.Ar num +backtraces with the most active references in the zone +.Ar name +.\" -l +.It Fl l +Can be used in combination with the +.Fl z +option to show the backtrace most likely contributing to a leak in the zone +.Ar name +(prints the backtrace with the most active references) +.\" -h +.It Fl h +show the help text +.El +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr zprint 1 , +.Xr ioclasscount 1 diff --git a/zlog.tproj/zlog.c b/zlog.tproj/zlog.c new file mode 100644 index 0000000..c08b8bc --- /dev/null +++ b/zlog.tproj/zlog.c @@ -0,0 +1,241 @@ +// +// zlog.c +// zlog +// +// Created by Rasha Eqbal on 1/4/18. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SymbolicationHelper.h" + +extern kern_return_t +mach_zone_get_btlog_records(host_priv_t host, + mach_zone_name_t name, + zone_btrecord_array_t *recsp, + mach_msg_type_number_t *recsCntp); +extern kern_return_t +mach_zone_get_zlog_zones(host_priv_t host, + mach_zone_name_array_t *namesp, + mach_msg_type_number_t *namesCntp); + +static int compare_zone_btrecords(const void *left, const void *right); +static void usage(FILE *stream, char **argv); +static void print_zone_info(const char *name); +static void get_zone_btrecords(const char *name, int topN); +static void list_zones_with_zlog_enabled(void); + +static void usage(FILE *stream, char **argv) +{ + fprintf (stream, "usage: %s [-t] [-z name [-n num | -l]] [-h]\n", argv[0]); + fprintf (stream, " -t : list all the zones that have logging enabled\n"); + fprintf (stream, " -z : show all allocation backtraces for zone \n"); + fprintf (stream, " -n : show top backtraces with the most active references in zone \n"); + fprintf (stream, " -l : show the backtrace most likely contributing to a leak in zone \n"); + fprintf (stream, " (prints the backtrace with the most active references)\n"); + fprintf (stream, " -h : print this help text\n"); + exit(stream != stdout); +} + +static int compare_zone_btrecords(const void *left, const void *right) +{ + zone_btrecord_t *btl = (zone_btrecord_t *)left; + zone_btrecord_t *btr = (zone_btrecord_t *)right; + + return (btr->ref_count - btl->ref_count); +} + +static void print_zone_info(const char *name) +{ + mach_zone_name_t zname; + mach_zone_info_t zone_info; + kern_return_t kr; + + strcpy(zname.mzn_name, name); + kr = mach_zone_info_for_zone(mach_host_self(), zname, &zone_info); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "error: call to mach_zone_info_for_zone() failed: %s\n", mach_error_string(kr)); + exit(1); + } + printf("zone name : %s\n", name); + printf("element size (bytes) : %lld\n", zone_info.mzi_elem_size); + printf("in-use size (bytes) : %lld\n", zone_info.mzi_count * zone_info.mzi_elem_size); + printf("total size (bytes) : %lld\n", zone_info.mzi_cur_size); + printf("\n"); +} + +static void get_zone_btrecords(const char *name, int topN) +{ + kern_return_t kr; + int i, j, index; + mach_zone_name_t zname; + unsigned int recs_count = 0; + zone_btrecord_t *recs, *recs_addr = NULL; + CSSymbolicatorRef kernelSym; + CFMutableDictionaryRef binaryImages; + + /* Create kernel symbolicator */ + kernelSym = CSSymbolicatorCreateWithMachKernel(); + if (CSIsNull(kernelSym)) { + fprintf(stderr, "error: CSSymbolicatorCreateWithMachKernel() returned NULL\n"); + exit(1); + } + /* Create dictionary to collect binary image info for offline symbolication */ + binaryImages = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + /* Query the kernel for backtrace records */ + strcpy(zname.mzn_name, name); + kr = mach_zone_get_btlog_records(mach_host_self(), zname, &recs_addr, &recs_count); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "error: call to mach_zone_get_btlog_records() failed: %s\n", mach_error_string(kr)); + exit(1); + } + + if (recs_count == 0) { + goto finish; + } + + recs = recs_addr; + if (topN == 1) { + /* Print the backtrace with the highest no. of refs */ + index = 0; + for (i = 0; i < recs_count; i++) { + if (recs[i].ref_count > recs[index].ref_count) { + index = i; + } + } + recs = recs_addr + index; + } else if (topN == 0) { + /* Print all backtraces */ + topN = recs_count; + } else { + /* Sort the records by no. of refs, and print the top */ + qsort(recs, recs_count, sizeof *recs, compare_zone_btrecords); + } + + printf("printing top %d (out of %d) allocation backtrace(s) for zone %s\n", topN, recs_count, zname.mzn_name); + + for (i = 0; i < topN; i++) { + printf("\nactive refs: %d operation type: %s\n", recs[i].ref_count, + (recs[i].operation_type == ZOP_ALLOC)? "ALLOC": (recs[i].operation_type == ZOP_FREE)? "FREE": "UNKNOWN"); + + for (j = 0; j < MAX_ZTRACE_DEPTH; j++) { + mach_vm_address_t addr = (mach_vm_address_t)recs[i].bt[j]; + if (!addr) { + break; + } + PrintSymbolicatedAddress(kernelSym, addr, binaryImages); + } + } + + /* Print relevant info for offline symbolication */ + PrintBinaryImagesInfo(binaryImages); + CFRelease(binaryImages); + +finish: + if ((recs_addr != NULL) && (recs_count != 0)) { + kr = vm_deallocate(mach_task_self(), (vm_address_t) recs_addr, (vm_size_t) (recs_count * sizeof *recs)); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "call to vm_deallocate() failed: %s\n", mach_error_string(kr)); + exit(1); + } + } + CSRelease(kernelSym); +} + +static void list_zones_with_zlog_enabled(void) +{ + kern_return_t kr; + mach_zone_name_t *name = NULL; + unsigned int name_count = 0, i; + + /* Get names for zones that have zone logging enabled */ + kr = mach_zone_get_zlog_zones(mach_host_self(), &name, &name_count); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "error: call to mach_zone_get_zlog_zones() failed: %s\n", mach_error_string(kr)); + exit(1); + } + + if (name_count == 0) { + printf("zlog not enabled for any zones.\n"); + } else { + printf("zlog enabled for zones...\n"); + } + + for (i = 0; i < name_count; i++) { + print_zone_info(name[i].mzn_name); + } + + if ((name != NULL) && (name_count != 0)) { + kr = vm_deallocate(mach_task_self(), (vm_address_t) name, (vm_size_t) (name_count * sizeof *name)); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "call to vm_deallocate() failed: %s\n", mach_error_string(kr)); + exit(1); + } + } +} + +#define VERSION_STRING "zlog output version: 1" + +int main(int argc, char *argv[]) +{ + int c, topN = 0; + const char *zone_name = NULL; + + /* Identifier string for SpeedTracer parsing */ + printf("%s\n\n", VERSION_STRING); + + if (argc == 1) { + /* default when no arguments are specified */ + list_zones_with_zlog_enabled(); + printf("Run 'zlog -h' for usage info.\n"); + return 0; + } + + while ((c = getopt(argc, argv, "tz:n:lh")) != -1) { + switch(c) { + case 't': + list_zones_with_zlog_enabled(); + break; + case 'z': + zone_name = optarg; + break; + case 'n': + topN = atoi(optarg); + break; + case 'l': + topN = 1; + break; + case 'h': + usage(stdout, argv); + break; + case '?': + default: + usage(stderr, argv); + break; + } + } + + if (optind < argc) { + usage(stderr, argv); + } + + if (zone_name) { + print_zone_info(zone_name); + get_zone_btrecords(zone_name, topN); + } else { + /* -n or -l was specified without -z */ + if (topN != 0) { + usage(stderr, argv); + } + } + + return 0; +} diff --git a/zprint.tproj/zprint.c b/zprint.tproj/zprint.c index 104161c..a968dc3 100644 --- a/zprint.tproj/zprint.c +++ b/zprint.tproj/zprint.c @@ -90,23 +90,22 @@ #define PRINTK(fmt, value) \ printf(fmt "K", (value) / 1024 ) /* ick */ -static void usage(void); -static void printzone(mach_zone_name_t *, task_zone_info_t *); -static void colprintzone(mach_zone_name_t *, task_zone_info_t *); -static int find_deltas(mach_zone_name_t *, task_zone_info_t *, task_zone_info_t *, char *, int, int); +static void usage(FILE *stream); +static void printzone(mach_zone_name_t *, mach_zone_info_t *); +static void colprintzone(mach_zone_name_t *, mach_zone_info_t *); +static int find_deltas(mach_zone_name_t *, mach_zone_info_t *, mach_zone_info_t *, char *, int, int); static void colprintzoneheader(void); static boolean_t substr(const char *a, size_t alen, const char *b, size_t blen); -static int SortName(const void * left, const void * right); -static int SortSize(const void * left, const void * right); -static void PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt, int (*func)(const void *, const void *), boolean_t column); +static int SortName(void * thunk, const void * left, const void * right); +static int SortSize(void * thunk, const void * left, const void * right); +static void PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt, + mach_zone_info_t *zoneInfo, mach_zone_name_t *zoneNames, + unsigned int zoneCnt, uint64_t zoneElements, + int (*func)(void *, const void *, const void *), boolean_t column); static char *program; -static pid_t pid = 0; -static task_t task = TASK_NULL; -static boolean_t ShowPid = FALSE; - static boolean_t ShowDeltas = FALSE; static boolean_t ShowWasted = FALSE; static boolean_t ShowTotal = FALSE; @@ -118,13 +117,63 @@ static boolean_t PrintHeader = TRUE; static unsigned long long totalsize = 0; static unsigned long long totalused = 0; static unsigned long long totalsum = 0; -static unsigned long long pidsum = 0; +static unsigned long long totalfragmented = 0; +static unsigned long long totalcollectable = 0; static int last_time = 0; static char *zname = NULL; static size_t znamelen = 0; +#define LEFTALIGN -1 +#define RIGHTALIGN 1 + +typedef struct { + char *line1; + char *line2; + int colwidth; + int alignment; + bool visible; +} column_format; + +enum { + COL_ZONE_NAME, + COL_ELEM_SIZE, + COL_CUR_SIZE, + COL_MAX_SIZE, + COL_CUR_ELTS, + COL_MAX_ELTS, + COL_CUR_INUSE, + COL_ALLOC_SIZE, + COL_ALLOC_COUNT, + COL_ZONE_FLAGS, + COL_FRAG_SIZE, + COL_FREE_SIZE, + COL_TOTAL_ALLOCS, + COL_MAX +}; + +/* + * The order in which the columns appear below should match + * the order in which the values are printed in colprintzone(). + */ +static column_format columns[] = { + [COL_ZONE_NAME] = { "", "zone name", 25, LEFTALIGN, true }, + [COL_ELEM_SIZE] = { "elem", "size", 6, RIGHTALIGN, true }, + [COL_CUR_SIZE] = { "cur", "size", 11, RIGHTALIGN, true }, + [COL_MAX_SIZE] = { "max", "size", 11, RIGHTALIGN, true }, + [COL_CUR_ELTS] = { "cur", "#elts", 10, RIGHTALIGN, true }, + [COL_MAX_ELTS] = { "max", "#elts", 11, RIGHTALIGN, true }, + [COL_CUR_INUSE] = { "cur", "inuse", 11, RIGHTALIGN, true }, + [COL_ALLOC_SIZE] = { "alloc", "size", 6, RIGHTALIGN, true }, + [COL_ALLOC_COUNT] = { "alloc", "count", 6, RIGHTALIGN, true }, + [COL_ZONE_FLAGS] = { "", "", 2, RIGHTALIGN, true }, + /* additional columns for special flags, not visible by default */ + [COL_FRAG_SIZE] = { "frag", "size", 9, RIGHTALIGN, false }, + [COL_FREE_SIZE] = { "free", "size", 9, RIGHTALIGN, false }, + [COL_TOTAL_ALLOCS] = { "total", "allocs", 17, RIGHTALIGN, false } +}; + static void sigintr(__unused int signum) { @@ -132,10 +181,20 @@ sigintr(__unused int signum) } static void -usage(void) +usage(FILE *stream) { - fprintf(stderr, "usage: %s [-w] [-s] [-c] [-h] [-t] [-d] [-l] [-L] [-p ] [name]\n", program); - exit(1); + fprintf(stream, "usage: %s [-w] [-s] [-c] [-h] [-H] [-t] [-d] [-l] [-L] [name]\n\n", program); + fprintf(stream, "\t-w\tshow wasted memory for each zone\n"); + fprintf(stream, "\t-s\tsort zones by wasted memory\n"); + fprintf(stream, "\t-c\t(default) display output formatted in columns\n"); + fprintf(stream, "\t-h\tdisplay this help message\n"); + fprintf(stream, "\t-H\thide column names\n"); + fprintf(stream, "\t-t\tdisplay the total size of allocations over the life of the zone\n"); + fprintf(stream, "\t-d\tdisplay deltas over time\n"); + fprintf(stream, "\t-l\t(default) display wired memory info after zone info\n"); + fprintf(stream, "\t-L\tdo not show wired memory info, only show zone info\n"); + fprintf(stream, "\nAny option (including default options) can be overridden by specifying the option in upper-case.\n\n"); + exit(stream != stdout); } int @@ -143,17 +202,18 @@ main(int argc, char **argv) { mach_zone_name_t *name = NULL; unsigned int nameCnt = 0; - task_zone_info_t *info = NULL; + mach_zone_info_t *info = NULL; unsigned int infoCnt = 0; mach_memory_info_t *wiredInfo = NULL; unsigned int wiredInfoCnt = 0; - task_zone_info_t *max_info = NULL; + mach_zone_info_t *max_info = NULL; char *deltas = NULL; + uint64_t zoneElements; kern_return_t kr; int i, j; int first_time = 1; - int must_print = 1; + int must_print = 1; int interval = 1; signal(SIGINT, sigintr); @@ -187,20 +247,15 @@ main(int argc, char **argv) ColFormat = TRUE; else if (streql(argv[i], "-C")) ColFormat = FALSE; + else if (streql(argv[i], "-h")) + usage(stdout); else if (streql(argv[i], "-H")) PrintHeader = FALSE; - else if (streql(argv[i], "-p")) { - ShowPid = TRUE; - if (i < argc - 1) { - pid = atoi(argv[i+1]); - i++; - } else - usage(); - } else if (streql(argv[i], "--")) { + else if (streql(argv[i], "--")) { i++; break; } else if (argv[i][0] == '-') - usage(); + usage(stderr); else break; } @@ -217,7 +272,7 @@ main(int argc, char **argv) break; default: - usage(); + usage(stderr); } if (ShowDeltas) { @@ -226,163 +281,139 @@ main(int argc, char **argv) PrintHeader = TRUE; } - if (ShowPid) { - kr = task_for_pid(mach_task_self(), pid, &task); + if (ShowWasted) { + columns[COL_FRAG_SIZE].visible = true; + columns[COL_FREE_SIZE].visible = true; + } + if (ShowTotal) { + columns[COL_TOTAL_ALLOCS].visible = true; + } + + for (;;) { + kr = mach_memory_info(mach_host_self(), + &name, &nameCnt, &info, &infoCnt, + &wiredInfo, &wiredInfoCnt); if (kr != KERN_SUCCESS) { - fprintf(stderr, "%s: task_for_pid(%d) failed: %s (try running as root)\n", - program, pid, mach_error_string(kr)); + fprintf(stderr, "%s: mach_memory_info: %s (try running as root)\n", + program, mach_error_string(kr)); exit(1); } - } - for (;;) { - if (ShowPid) { - kr = task_zone_info(task, &name, &nameCnt, &info, &infoCnt); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "%s: task_zone_info: %s\n", - program, mach_error_string(kr)); - exit(1); - } - } else { - mach_zone_info_t *zinfo = NULL; + if (nameCnt != infoCnt) { + fprintf(stderr, "%s: mach_zone_name/ mach_zone_info: counts not equal?\n", + program); + exit(1); + } - kr = mach_memory_info(mach_host_self(), - &name, &nameCnt, &zinfo, &infoCnt, - &wiredInfo, &wiredInfoCnt); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "%s: mach_zone_info: %s\n", - program, mach_error_string(kr)); - exit(1); - } + if (first_time) { + deltas = (char *)malloc(infoCnt); + max_info = (mach_zone_info_t *)malloc((infoCnt * sizeof *info)); + } - kr = vm_allocate(mach_task_self(), (vm_address_t *)&info, - infoCnt * sizeof *info, VM_FLAGS_ANYWHERE); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "%s vm_allocate: %s\n", - program, mach_error_string(kr)); - exit(1); - } - for (i = 0; i < infoCnt; i++) { - *(mach_zone_info_t *)(info + i) = zinfo[i]; - info[i].tzi_caller_acct = 0; - info[i].tzi_task_alloc = 0; - info[i].tzi_task_free = 0; - } - kr = vm_deallocate(mach_task_self(), (vm_address_t) zinfo, - (vm_size_t) (infoCnt * sizeof *zinfo)); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "%s: vm_deallocate: %s\n", - program, mach_error_string(kr)); - exit(1); - } - } + if (SortZones) { + for (i = 0; i < nameCnt-1; i++) { + for (j = i+1; j < nameCnt; j++) { + unsigned long long wastei, wastej; - if (nameCnt != infoCnt) { - fprintf(stderr, "%s: mach/task_zone_info: counts not equal?\n", - program); - exit(1); - } + wastei = (info[i].mzi_cur_size - + (info[i].mzi_elem_size * + info[i].mzi_count)); + wastej = (info[j].mzi_cur_size - + (info[j].mzi_elem_size * + info[j].mzi_count)); - if (first_time) { - deltas = (char *)malloc(infoCnt); - max_info = (task_zone_info_t *)malloc((infoCnt * sizeof *info)); - } + if (wastej > wastei) { + mach_zone_info_t tinfo; + mach_zone_name_t tname; - if (SortZones) { - for (i = 0; i < nameCnt-1; i++) - for (j = i+1; j < nameCnt; j++) { - unsigned long long wastei, wastej; + tinfo = info[i]; + info[i] = info[j]; + info[j] = tinfo; - wastei = (info[i].tzi_cur_size - - (info[i].tzi_elem_size * - info[i].tzi_count)); - wastej = (info[j].tzi_cur_size - - (info[j].tzi_elem_size * - info[j].tzi_count)); - - if (wastej > wastei) { - task_zone_info_t tinfo; - mach_zone_name_t tname; - - tinfo = info[i]; - info[i] = info[j]; - info[j] = tinfo; - - tname = name[i]; - name[i] = name[j]; - name[j] = tname; + tname = name[i]; + name[i] = name[j]; + name[j] = tname; + } } } - } - - must_print = find_deltas(name, info, max_info, deltas, infoCnt, first_time); - if (must_print) { - if (ColFormat) { - if (!first_time) - printf("\n"); - colprintzoneheader(); } - for (i = 0; i < nameCnt; i++) { - if (deltas[i]) { - if (ColFormat) - colprintzone(&name[i], &info[i]); - else - printzone(&name[i], &info[i]); + + must_print = find_deltas(name, info, max_info, deltas, infoCnt, first_time); + zoneElements = 0; + if (must_print) { + if (ColFormat) { + if (!first_time) + printf("\n"); + colprintzoneheader(); + } + for (i = 0; i < nameCnt; i++) { + if (deltas[i]) { + if (ColFormat) + colprintzone(&name[i], &info[i]); + else + printzone(&name[i], &info[i]); + zoneElements += info[i].mzi_count; + } } } - } - if (ShowLarge && first_time) { - PrintLarge(wiredInfo, wiredInfoCnt, - SortZones ? &SortSize : &SortName, ColFormat); - } - - first_time = 0; - - if ((name != NULL) && (nameCnt != 0)) { - kr = vm_deallocate(mach_task_self(), (vm_address_t) name, - (vm_size_t) (nameCnt * sizeof *name)); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "%s: vm_deallocate: %s\n", - program, mach_error_string(kr)); - exit(1); + if (ShowLarge && first_time) { + PrintLarge(wiredInfo, wiredInfoCnt, &info[0], &name[0], + nameCnt, zoneElements, + SortZones ? &SortSize : &SortName, ColFormat); } - } - if ((info != NULL) && (infoCnt != 0)) { - kr = vm_deallocate(mach_task_self(), (vm_address_t) info, - (vm_size_t) (infoCnt * sizeof *info)); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "%s: vm_deallocate: %s\n", - program, mach_error_string(kr)); - exit(1); + first_time = 0; + + if ((name != NULL) && (nameCnt != 0)) { + kr = vm_deallocate(mach_task_self(), (vm_address_t) name, + (vm_size_t) (nameCnt * sizeof *name)); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "%s: vm_deallocate: %s\n", + program, mach_error_string(kr)); + exit(1); + } } - } - if ((wiredInfo != NULL) && (wiredInfoCnt != 0)) { - kr = vm_deallocate(mach_task_self(), (vm_address_t) wiredInfo, - (vm_size_t) (wiredInfoCnt * sizeof *wiredInfo)); - if (kr != KERN_SUCCESS) { - fprintf(stderr, "%s: vm_deallocate: %s\n", - program, mach_error_string(kr)); - exit(1); + if ((info != NULL) && (infoCnt != 0)) { + kr = vm_deallocate(mach_task_self(), (vm_address_t) info, + (vm_size_t) (infoCnt * sizeof *info)); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "%s: vm_deallocate: %s\n", + program, mach_error_string(kr)); + exit(1); + } } + + if ((wiredInfo != NULL) && (wiredInfoCnt != 0)) { + kr = vm_deallocate(mach_task_self(), (vm_address_t) wiredInfo, + (vm_size_t) (wiredInfoCnt * sizeof *wiredInfo)); + if (kr != KERN_SUCCESS) { + fprintf(stderr, "%s: vm_deallocate: %s\n", + program, mach_error_string(kr)); + exit(1); + } + } + + if ((ShowWasted||ShowTotal) && PrintHeader && !ShowDeltas) { + printf("\nZONE TOTALS\n"); + printf("---------------------------------------------\n"); + printf("TOTAL SIZE = %llu\n", totalsize); + printf("TOTAL USED = %llu\n", totalused); + if (ShowWasted) { + printf("TOTAL WASTED = %llu\n", totalsize - totalused); + printf("TOTAL FRAGMENTED = %llu\n", totalfragmented); + printf("TOTAL COLLECTABLE = %llu\n", totalcollectable); + } + if (ShowTotal) + printf("TOTAL ALLOCS = %llu\n", totalsum); + } + + if (ShowDeltas == FALSE || last_time) + break; + + sleep(interval); } - - if ((ShowWasted||ShowTotal) && PrintHeader && !ShowDeltas) { - printf("TOTAL SIZE = %llu\n", totalsize); - printf("TOTAL USED = %llu\n", totalused); - if (ShowWasted) - printf("TOTAL WASTED = %llu\n", totalsize - totalused); - if (ShowTotal) - printf("TOTAL ALLOCS = %llu\n", totalsum); - } - - if (ShowDeltas == FALSE || last_time) - break; - - sleep(interval); - } exit(0); } @@ -401,64 +432,55 @@ substr(const char *a, size_t alen, const char *b, size_t blen) } static void -printzone(mach_zone_name_t *name, task_zone_info_t *info) +printzone(mach_zone_name_t *name, mach_zone_info_t *info) { - unsigned long long used, size; + unsigned long long used, size, fragmented, collectable; printf("%.*s zone:\n", (int)sizeof name->mzn_name, name->mzn_name); printf("\tcur_size: %lluK bytes (%llu elements)\n", - info->tzi_cur_size/1024, - (info->tzi_elem_size == 0) ? 0 : - info->tzi_cur_size/info->tzi_elem_size); + info->mzi_cur_size/1024, + (info->mzi_elem_size == 0) ? 0 : + info->mzi_cur_size/info->mzi_elem_size); printf("\tmax_size: %lluK bytes (%llu elements)\n", - info->tzi_max_size/1024, - (info->tzi_elem_size == 0) ? 0 : - info->tzi_max_size/info->tzi_elem_size); + info->mzi_max_size/1024, + (info->mzi_elem_size == 0) ? 0 : + info->mzi_max_size/info->mzi_elem_size); printf("\telem_size: %llu bytes\n", - info->tzi_elem_size); + info->mzi_elem_size); printf("\t# of elems: %llu\n", - info->tzi_count); + info->mzi_count); printf("\talloc_size: %lluK bytes (%llu elements)\n", - info->tzi_alloc_size/1024, - (info->tzi_elem_size == 0) ? 0 : - info->tzi_alloc_size/info->tzi_elem_size); - if (info->tzi_exhaustible) + info->mzi_alloc_size/1024, + (info->mzi_elem_size == 0) ? 0 : + info->mzi_alloc_size/info->mzi_elem_size); + if (info->mzi_exhaustible) printf("\tEXHAUSTIBLE\n"); - if (info->tzi_collectable) + if (GET_MZI_COLLECTABLE_FLAG(info->mzi_collectable)) printf("\tCOLLECTABLE\n"); - if (ShowPid && info->tzi_caller_acct) - printf("\tCALLER ACCOUNTED\n"); - if (ShowPid) { - pidsum += info->tzi_task_alloc - info->tzi_task_free; - printf("\tproc_alloc_size: %8dK bytes (%llu elements)\n", - (int)((info->tzi_task_alloc - info->tzi_task_free)/1024), - (info->tzi_elem_size == 0) ? 0 : - (info->tzi_task_alloc - info->tzi_task_free)/info->tzi_elem_size); - } if (ShowWasted) { - totalused += used = info->tzi_elem_size * info->tzi_count; - totalsize += size = info->tzi_cur_size; + totalused += used = info->mzi_elem_size * info->mzi_count; + totalsize += size = info->mzi_cur_size; + totalcollectable += collectable = GET_MZI_COLLECTABLE_BYTES(info->mzi_collectable); + totalfragmented += fragmented = size - used - collectable; printf("\t\t\t\t\tWASTED: %llu\n", size - used); + printf("\t\t\t\t\tFRAGMENTED: %llu\n", fragmented); + printf("\t\t\t\t\tCOLLECTABLE: %llu\n", collectable); } if (ShowTotal) { - totalsum += info->tzi_sum_size; + totalsum += info->mzi_sum_size; printf("\t\t\t\t\tTOTAL: %llu\n", totalsum); - if (ShowPid) - printf("\t\t\t\t\tPID TOTAL: %llu\n", pidsum); } } static void -colprintzone(mach_zone_name_t *zone_name, task_zone_info_t *info) +colprintzone(mach_zone_name_t *zone_name, mach_zone_info_t *info) { char *name = zone_name->mzn_name; int j, namewidth; - unsigned long long used, size; + unsigned long long used, size, fragmented, collectable; + + namewidth = columns[COL_ZONE_NAME].colwidth; - namewidth = 25; - if (ShowWasted || ShowTotal) { - namewidth -= 7; - } for (j = 0; j < namewidth - 1 && name[j]; j++) { if (name[j] == ' ') { putchar('.'); @@ -477,79 +499,101 @@ colprintzone(mach_zone_name_t *zone_name, task_zone_info_t *info) putchar(' '); } } - printf(" %6llu", info->tzi_elem_size); - PRINTK(" %10llu", info->tzi_cur_size); - if (info->tzi_max_size / 1024 > 9999999) { - printf(" --------"); - } else { - PRINTK(" %10llu", info->tzi_max_size); - } - printf(" %10llu", info->tzi_cur_size / info->tzi_elem_size); - if (info->tzi_max_size / 1024 >= 999999999) { - printf(" ----------"); - } else { - printf(" %11llu", info->tzi_max_size / info->tzi_elem_size); - } - printf(" %11llu", info->tzi_count); - PRINTK(" %5llu", info->tzi_alloc_size); - printf(" %6llu", info->tzi_alloc_size / info->tzi_elem_size); - totalused += used = info->tzi_elem_size * info->tzi_count; - totalsize += size = info->tzi_cur_size; - totalsum += info->tzi_sum_size; - printf(" %c%c%c", - (info->tzi_exhaustible ? 'X' : ' '), - (info->tzi_caller_acct ? 'A' : ' '), - (info->tzi_collectable ? 'C' : ' ')); - if (ShowWasted) { - PRINTK(" %8llu", size - used); +#define PRINTCOL(value, index) \ + if (columns[(index)].visible) { \ + printf(" %*llu", columns[(index)].colwidth * columns[(index)].alignment, (value)); \ } - if (ShowPid) { - printf("%8dK", (int)((info->tzi_task_alloc - info->tzi_task_free)/1024)); +#define PRINTCOLSTR(value, index) \ + if (columns[(index)].visible) { \ + printf(" %*s", columns[(index)].colwidth * columns[(index)].alignment, (value)); \ } - if (ShowTotal) { - if (info->tzi_sum_size < 1024) - printf(" %16lluB", info->tzi_sum_size); - else - PRINTK(" %16llu", info->tzi_sum_size); +#define PRINTCOLK(value, index) \ + if (columns[(index)].visible) { \ + printf(" %*lluK", (columns[(index)].colwidth - 1) * columns[(index)].alignment, (value) / 1024 ); \ } +#define PRINTCOLSZ(value, index) \ + if (columns[(index)].visible) { \ + if ((value) < 1024) { \ + printf(" %*lluB", (columns[(index)].colwidth - 1) * columns[(index)].alignment, (value)); \ + } else { \ + PRINTCOLK(value, index) \ + } \ + } + + + PRINTCOL(info->mzi_elem_size, COL_ELEM_SIZE); + PRINTCOLK(info->mzi_cur_size, COL_CUR_SIZE); + if (info->mzi_max_size / 1024 > 9999999) { + /* + * Zones with preposterously large maximum sizes are shown with `-------' + * in the max size and max num elts fields. + */ + PRINTCOLSTR("-------", COL_MAX_SIZE); + } else { + PRINTCOLK(info->mzi_max_size, COL_MAX_SIZE); + } + PRINTCOL(info->mzi_cur_size / info->mzi_elem_size, COL_CUR_ELTS); + if (info->mzi_max_size / 1024 > 9999999) { + PRINTCOLSTR("-------", COL_MAX_ELTS); + } else { + PRINTCOL(info->mzi_max_size / info->mzi_elem_size, COL_MAX_ELTS); + } + PRINTCOL(info->mzi_count, COL_CUR_INUSE); + PRINTCOLK(info->mzi_alloc_size, COL_ALLOC_SIZE); + PRINTCOL(info->mzi_alloc_size / info->mzi_elem_size, COL_ALLOC_COUNT); + + totalused += used = info->mzi_elem_size * info->mzi_count; + totalsize += size = info->mzi_cur_size; + totalsum += info->mzi_sum_size; + totalcollectable += collectable = GET_MZI_COLLECTABLE_BYTES(info->mzi_collectable); + totalfragmented += fragmented = size - used - collectable; + + printf(" %c%c", + (info->mzi_exhaustible ? 'X' : ' '), + (GET_MZI_COLLECTABLE_FLAG(info->mzi_collectable) ? 'C' : ' ')); + + PRINTCOLSZ(fragmented, COL_FRAG_SIZE); + PRINTCOLSZ(collectable, COL_FREE_SIZE); + PRINTCOLSZ(info->mzi_sum_size, COL_TOTAL_ALLOCS); + printf("\n"); } + static void colprintzoneheader(void) { + int i, totalwidth = 0; + if (! PrintHeader) { return; } - printf("%s elem cur max cur max" - " cur alloc alloc %s%s\n", - (ShowWasted||ShowTotal)? "" : " ", - (ShowWasted)? " ":"", - (ShowPid) ? " PID" : "" ); - printf("zone name%s size size size #elts #elts" - " inuse size count ", (ShowWasted||ShowTotal)? " " : " " ); - if (ShowWasted) - printf(" wasted"); - if (ShowPid) - printf(" Allocs"); - if (ShowTotal) - printf(" Total Allocs"); - printf("\n%s-------------------------------------------------------" - "-----------------------------------------------", - (ShowWasted||ShowTotal)? "" : "-------"); - if (ShowWasted) - printf("----------"); - if (ShowPid) - printf("---------"); - if (ShowTotal) - printf("------------------"); + + for (i = 0; i < COL_MAX; i++) { + if (columns[i].visible) { + printf("%*s ", columns[i].colwidth * columns[i].alignment, columns[i].line1); + } + } + printf("\n"); + + for (i = 0; i < COL_MAX; i++) { + if (columns[i].visible) { + printf("%*s ", columns[i].colwidth * columns[i].alignment, columns[i].line2); + totalwidth += (columns[i].colwidth + 1); + } + } + + printf("\n"); + for (i = 0; i < totalwidth; i++) { + printf("-"); + } printf("\n"); } int -find_deltas(mach_zone_name_t *name, task_zone_info_t *info, task_zone_info_t *max_info, +find_deltas(mach_zone_name_t *name, mach_zone_info_t *info, mach_zone_info_t *max_info, char *deltas, int cnt, int first_time) { int i; @@ -559,10 +603,10 @@ find_deltas(mach_zone_name_t *name, task_zone_info_t *info, task_zone_info_t *ma deltas[i] = 0; if (substr(zname, znamelen, name[i].mzn_name, strnlen(name[i].mzn_name, sizeof name[i].mzn_name))) { - if (first_time || info->tzi_cur_size > max_info->tzi_cur_size || - (ShowTotal && ((info->tzi_sum_size >> 1) > max_info->tzi_sum_size))) { - max_info->tzi_cur_size = info->tzi_cur_size; - max_info->tzi_sum_size = info->tzi_sum_size; + if (first_time || info->mzi_cur_size > max_info->mzi_cur_size || + (ShowTotal && ((info->mzi_sum_size >> 1) > max_info->mzi_sum_size))) { + max_info->mzi_cur_size = info->mzi_cur_size; + max_info->mzi_sum_size = info->mzi_sum_size; deltas[i] = 1; found_one = 1; } @@ -629,8 +673,10 @@ kern_vm_counter_name(uint64_t tag) case (VM_KERN_COUNT_MANAGED): name = "VM_KERN_COUNT_MANAGED"; break; case (VM_KERN_COUNT_RESERVED): name = "VM_KERN_COUNT_RESERVED"; break; case (VM_KERN_COUNT_WIRED): name = "VM_KERN_COUNT_WIRED"; break; + case (VM_KERN_COUNT_WIRED_BOOT): name = "VM_KERN_COUNT_WIRED_BOOT"; break; case (VM_KERN_COUNT_WIRED_MANAGED): name = "VM_KERN_COUNT_WIRED_MANAGED"; break; case (VM_KERN_COUNT_STOLEN): name = "VM_KERN_COUNT_STOLEN"; break; + case (VM_KERN_COUNT_BOOT_STOLEN): name = "VM_KERN_COUNT_BOOT_STOLEN"; break; case (VM_KERN_COUNT_LOPAGE): name = "VM_KERN_COUNT_LOPAGE"; break; case (VM_KERN_COUNT_MAP_KERNEL): name = "VM_KERN_COUNT_MAP_KERNEL"; break; case (VM_KERN_COUNT_MAP_ZONE): name = "VM_KERN_COUNT_MAP_ZONE"; break; @@ -661,10 +707,12 @@ static CFMutableDictionaryRef gTagDict; static mach_memory_info_t * gSites; static char * -GetSiteName(int siteIdx) +GetSiteName(int siteIdx, mach_zone_name_t * zoneNames, unsigned int zoneNamesCnt) { const char * name; + uintptr_t kmodid; char * result; + char * append; mach_vm_address_t addr; CFDictionaryRef kextInfo; CFStringRef bundleID; @@ -681,7 +729,13 @@ GetSiteName(int siteIdx) site = &gSites[siteIdx]; addr = site->site; type = (VM_KERN_SITE_TYPE & site->flags); - switch (type) + kmodid = 0; + + if (VM_KERN_SITE_NAMED & site->flags) + { + asprintf(&result, "%s", &site->name[0]); + } + else switch (type) { case VM_KERN_SITE_TAG: result = kern_vm_tag_name(addr); @@ -692,14 +746,18 @@ GetSiteName(int siteIdx) break; case VM_KERN_SITE_KMOD: - kextInfo = CFDictionaryGetValue(gTagDict, (const void *)(uintptr_t) addr); + + kmodid = (uintptr_t) addr; + kextInfo = CFDictionaryGetValue(gTagDict, (const void *)kmodid); if (kextInfo) { bundleID = (CFStringRef)CFDictionaryGetValue(kextInfo, kCFBundleIdentifierKey); name = CFStringGetCStringPtr(bundleID, kCFStringEncodingUTF8); // wiredSize = (CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleWiredSizeKey)); } - asprintf(&result, "%-64s%3lld", name ? name : "(unknown kmod)", addr); + + if (name) asprintf(&result, "%s", name); + else asprintf(&result, "(unloaded kmod)"); break; case VM_KERN_SITE_KERNEL: @@ -726,24 +784,63 @@ GetSiteName(int siteIdx) break; } - return (result); + if (result + && (VM_KERN_SITE_ZONE & site->flags) + && zoneNames + && (site->zone < zoneNamesCnt)) + { + size_t namelen, zonelen; + namelen = strlen(result); + zonelen = strnlen(zoneNames[site->zone].mzn_name, sizeof(zoneNames[site->zone].mzn_name)); + if (((namelen + zonelen) > 61) && (zonelen < 61)) namelen = (61 - zonelen); + asprintf(&append, "%.*s[%.*s]", + (int)namelen, + result, + (int)zonelen, + zoneNames[site->zone].mzn_name); + free(result); + result = append; + } + if (result && kmodid) + { + asprintf(&append, "%-64s%3ld", result, kmodid); + free(result); + result = append; + } + + return (result); } -static int -SortName(const void * left, const void * right) +struct CompareThunk { + mach_zone_name_t *zoneNames; + unsigned int zoneNamesCnt; +}; + +static int +SortName(void * thunk, const void * left, const void * right) +{ + const struct CompareThunk * t = (typeof(t)) thunk; const int * idxL; const int * idxR; char * l; char * r; + CFStringRef lcf; + CFStringRef rcf; int result; idxL = (typeof(idxL)) left; idxR = (typeof(idxR)) right; - l = GetSiteName(*idxL); - r = GetSiteName(*idxR); + l = GetSiteName(*idxL, t->zoneNames, t->zoneNamesCnt); + r = GetSiteName(*idxR, t->zoneNames, t->zoneNamesCnt); - result = strcmp(l, r); + lcf = CFStringCreateWithCString(kCFAllocatorDefault, l, kCFStringEncodingUTF8); + rcf = CFStringCreateWithCString(kCFAllocatorDefault, r, kCFStringEncodingUTF8); + + result = (int) CFStringCompareWithOptionsAndLocale(lcf, rcf, CFRangeMake(0, CFStringGetLength(lcf)), kCFCompareNumerically, NULL); + + CFRelease(lcf); + CFRelease(rcf); free(l); free(r); @@ -751,7 +848,7 @@ SortName(const void * left, const void * right) } static int -SortSize(const void * left, const void * right) +SortSize(void * thunk, const void * left, const void * right) { const mach_memory_info_t * siteL; const mach_memory_info_t * siteR; @@ -771,10 +868,14 @@ SortSize(const void * left, const void * right) static void PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt, - int (*func)(const void *, const void *), boolean_t column) + mach_zone_info_t *zoneInfo, mach_zone_name_t *zoneNames, + unsigned int zoneCnt, uint64_t zoneElements, + int (*func)(void *, const void *, const void *), boolean_t column) { uint64_t zonetotal; uint64_t top_wired; + uint64_t size; + uint64_t elemsTagged; CFDictionaryRef allKexts; unsigned int idx, site, first; @@ -802,36 +903,55 @@ PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt, for (idx = 0; idx < wiredInfoCnt; idx++) sorted[idx] = idx; first = 0; // VM_KERN_MEMORY_FIRST_DYNAMIC - qsort(&sorted[first], + struct CompareThunk thunk; + thunk.zoneNames = zoneNames; + thunk.zoneNamesCnt = zoneCnt; + qsort_r(&sorted[first], wiredInfoCnt - first, sizeof(sorted[0]), + &thunk, func); + elemsTagged = 0; for (headerPrinted = false, idx = 0; idx < wiredInfoCnt; idx++) { site = sorted[idx]; - if (!gSites[site].size) continue; - if (VM_KERN_COUNT_WIRED == gSites[site].site) top_wired = gSites[site].size; + if ((VM_KERN_SITE_COUNTER & gSites[site].flags) + && (VM_KERN_COUNT_WIRED == gSites[site].site)) top_wired = gSites[site].size; if (VM_KERN_SITE_HIDE & gSites[site].flags) continue; - if (!(VM_KERN_SITE_WIRED & gSites[site].flags)) continue; + if (!((VM_KERN_SITE_WIRED | VM_KERN_SITE_ZONE) & gSites[site].flags)) continue; - name = GetSiteName(site); + if ((VM_KERN_SITE_ZONE & gSites[site].flags) + && gSites[site].zone < zoneCnt) + { + elemsTagged += gSites[site].size / zoneInfo[gSites[site].zone].mzi_elem_size; + } + + if ((gSites[site].size < 1024) && (gSites[site].peak < 1024)) continue; + + name = GetSiteName(site, zoneNames, zoneCnt); if (!substr(zname, znamelen, name, strlen(name))) continue; if (!headerPrinted) { printf("-------------------------------------------------------------------------------------------------------------\n"); - printf(" kmod vm cur\n"); - printf("wired memory id tag size\n"); + printf(" kmod vm peak cur\n"); + printf("wired memory id tag size waste size\n"); printf("-------------------------------------------------------------------------------------------------------------\n"); headerPrinted = true; } printf("%-67s", name); free(name); - printf("%12d", site); + printf("%12d", gSites[site].tag); - printf(" %11s", ""); - PRINTK(" %12llu", gSites[site].size); - totalsize += gSites[site].size; + if (gSites[site].peak) PRINTK(" %10llu", gSites[site].peak); + else printf(" %11s", ""); + + if (gSites[site].collectable_bytes) PRINTK(" %5llu", gSites[site].collectable_bytes); + else printf(" %6s", ""); + + PRINTK(" %9llu", gSites[site].size); + + if (!(VM_KERN_SITE_ZONE & gSites[site].flags)) totalsize += gSites[site].size; printf("\n"); } @@ -841,38 +961,51 @@ PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt, printf("%-67s", "zones"); printf("%12s", ""); printf(" %11s", ""); - PRINTK(" %12llu", zonetotal); + printf(" %6s", ""); + PRINTK(" %9llu", zonetotal); printf("\n"); } if (headerPrinted) { + if (elemsTagged) + { + snprintf(totalstr, sizeof(totalstr), "%lld of %lld", elemsTagged, zoneElements); + printf("zone tags%100s\n", totalstr); + } snprintf(totalstr, sizeof(totalstr), "%6.2fM of %6.2fM", totalsize / 1024.0 / 1024.0, top_wired / 1024.0 / 1024.0); - printf("total%100s\n", totalstr); + printf("total%104s\n", totalstr); } for (headerPrinted = false, idx = 0; idx < wiredInfoCnt; idx++) { site = sorted[idx]; - if (!gSites[site].size) continue; + size = gSites[site].mapped; + if (!size) continue; if (VM_KERN_SITE_HIDE & gSites[site].flags) continue; - if (VM_KERN_SITE_WIRED & gSites[site].flags) continue; + if ((size == gSites[site].size) + && ((VM_KERN_SITE_WIRED | VM_KERN_SITE_ZONE) & gSites[site].flags)) continue; - name = GetSiteName(site); + name = GetSiteName(site, NULL, 0); if (!substr(zname, znamelen, name, strlen(name))) continue; if (!headerPrinted) { printf("-------------------------------------------------------------------------------------------------------------\n"); - printf(" largest\n"); - printf("maps free free size\n"); + printf(" largest peak cur\n"); + printf("maps free free size size\n"); printf("-------------------------------------------------------------------------------------------------------------\n"); headerPrinted = true; } - printf("%-67s", name); + printf("%-55s", name); free(name); - PRINTK(" %10llu", gSites[site].free); - PRINTK(" %10llu", gSites[site].largest); - PRINTK(" %12llu", gSites[site].size); + if (gSites[site].free) PRINTK(" %10llu", gSites[site].free); + else printf(" %11s", ""); + if (gSites[site].largest) PRINTK(" %10llu", gSites[site].largest); + else printf(" %11s", ""); + if (gSites[site].peak) PRINTK(" %10llu", gSites[site].peak); + else printf(" %11s", ""); + PRINTK(" %16llu", size); printf("\n"); } + totalsize = zonetotal; } diff --git a/zprint.tproj/zprint.lua b/zprint.tproj/zprint.lua new file mode 100644 index 0000000..3c7d5fc --- /dev/null +++ b/zprint.tproj/zprint.lua @@ -0,0 +1,127 @@ +require 'strict' + +-- # zprint +-- +-- Parse the output of zprint into tables. + +local zprint = {} + +-- Return the lines inside "dashed" lines -- that is, lines that are entirely +-- made up of dashes (-) in the string `str`. The `skip_dashed` argument +-- controls how many dashed lines to skip before returning the lines between it +-- and the next dashed line. +local function lines_inside_dashes(str, skip_dashed) + local start_pos = 1 + for _ = 1, skip_dashed do + _, start_pos = str:find('\n[-]+\n', start_pos) + end + assert(start_pos, 'found dashed line in output') + local end_pos, _ = str:find('\n[-]+\n', start_pos + 1) + assert(end_pos, 'found ending dashed line in output') + + return str:sub(start_pos + 1, end_pos - 1) +end + +-- Iterate through the zones listed in the given zprint(1) output `zpout`. +-- +-- for zone in zprint_zones(io.stdin:read('*a')) do +-- print(zone.name, zone.size, zone.used_size) +-- end +function zprint.zones(zpout) + -- Get to the first section delimited by dashes. This is where the zones are + -- recorded. + local zones = lines_inside_dashes(zpout, 1) + + -- Create an iterator for each line, for use in our own iteration function. + local lines = zones:gmatch('([^\n]+)\n') + + return function () + -- Grab the next line. + local line = lines() + if not line then + return nil + end + + -- Match each column from zprint's output. + local name, eltsz, cursz_kb, maxsz_kb, nelts, nelts_max, nused, + allocsz_kb = line:match( + '([%S]+)%s+(%d+)%s+(%d+)K%s+(%d+)K%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)') + -- Convert numeric fields to numbers, and into bytes if specified in KiB. + eltsz = tonumber(eltsz) + local cursz = tonumber(cursz_kb) * 1024 + local maxsz = tonumber(maxsz_kb) * 1024 + local usedsz = tonumber(nused) * eltsz + local allocsz = tonumber(allocsz_kb) * 1024 + + -- Return a table representing the zone. + return { + name = name, -- the name of the zone + size = cursz, -- the size of the zone + max_size = maxsz, -- the maximum size of the zone + used_size = usedsz, -- the size of all used elements in the zone + element_size = eltsz, -- the size of each element in the zone + allocation_size = allocsz, -- the size of allocations made for the zone + } + end +end + +-- Iterate through the tags listed in the given zprint(1) output `zpout`. +function zprint.tags(zpout) + -- Get to the third zone delimited by dashes, where the tags are recorded. + local tags = lines_inside_dashes(zpout, 3) + + local lines = tags:gmatch('([^\n]+)\n') + + return function () + local line = lines() + if not line then + return nil + end + + -- Skip any unloaded kmod lines. + while line:match('(unloaded kmod)') do + line = lines() + end + -- End on the zone tags line, since it's not useful. + if line:match('zone tags') then + return nil + end + + -- The line representing a region can take 4 different forms, depending on + -- the type of region. Check for each of them. + + -- Check for 4 columns. + local name, maxsz_kb, cursz_kb = line:match( + '(%S+)%s+%d+%s+%d+%s+(%d+)K%s+(%d+)K$') + if not name then + -- Check for 3 columns. + name, maxsz_kb, cursz_kb = line:match('(%S+)%s+%d+%s+(%d+)K%s+(%d+)K$') + if not name then + -- Check for a two columns. + name, cursz_kb = line:match('(%S+)%s+%d+%s+(%d+)K') + if not name then + -- Check for a single column. + name, cursz_kb = line:match('(%S+)%s+(%d+)K') + end + end + end + -- Convert numeric fields to numbers and then into bytes. + local cursz = tonumber(cursz_kb) * 1024 + local maxsz = maxsz_kb and (tonumber(maxsz_kb) * 1024) + + -- Return a table representing the region. + return { + name = name, + size = cursz, + max_size = maxsz, + } + end +end + +function zprint.total(zpout) + local total = zpout:match('total[^%d]+(%d+.%d+)M of') + local bytes = tonumber(total) * 1024 * 1024 + return bytes +end + +return zprint