]> git.saurik.com Git - apple/system_cmds.git/commitdiff
system_cmds-790.tar.gz macos-1013 v790
authorApple <opensource@apple.com>
Tue, 26 Sep 2017 16:28:56 +0000 (16:28 +0000)
committerApple <opensource@apple.com>
Tue, 26 Sep 2017 16:28:56 +0000 (16:28 +0000)
60 files changed:
dynamic_pager.tproj/com.apple.dynamic_pager.plist
dynamic_pager.tproj/dynamic_pager.c
dynamic_pager.tproj/entitlements.plist [new file with mode: 0644]
dynamic_pager.tproj/generate_plist.sh
fs_usage.tproj/fs_usage.c
gcore.tproj/convert.c [new file with mode: 0644]
gcore.tproj/convert.h [new file with mode: 0644]
gcore.tproj/corefile.c
gcore.tproj/corefile.h
gcore.tproj/dyld.c
gcore.tproj/dyld.h
gcore.tproj/dyld_shared_cache.c
gcore.tproj/gcore-internal.1 [new file with mode: 0644]
gcore.tproj/loader_additions.h
gcore.tproj/main.c
gcore.tproj/options.h
gcore.tproj/region.h
gcore.tproj/sparse.c
gcore.tproj/sparse.h
gcore.tproj/threads.c
gcore.tproj/utils.c
gcore.tproj/utils.h
gcore.tproj/vanilla.c
gcore.tproj/vanilla.h
gcore.tproj/vm.c
gcore.tproj/vm.h
getty.tproj/generate_plist.sh
hostinfo.tproj/hostinfo.c
iosim.tproj/iosim.c
lskq.tproj/common.h
lskq.tproj/lskq.1
lskq.tproj/lskq.c
lsmp.tproj/common.h
lsmp.tproj/json.h [new file with mode: 0644]
lsmp.tproj/lsmp.1
lsmp.tproj/lsmp.c
lsmp.tproj/port_details.c
lsmp.tproj/task_details.c
ltop.tproj/ltop.c
mkfile.tproj/mkfile.8
mslutil/mslutil.1 [new file with mode: 0644]
mslutil/mslutil.c [new file with mode: 0644]
nvram.tproj/nvram.c
stackshot.tproj/stackshot.c [new file with mode: 0644]
sysctl.tproj/sysctl.c
system_cmds.xcodeproj/project.pbxproj
taskpolicy.tproj/taskpolicy.8
taskpolicy.tproj/taskpolicy.c
trace.tproj/trace.c
zic.tproj/README
zic.tproj/build_zichost.sh
zic.tproj/generate_zoneinfo.sh
zic.tproj/ialloc.c
zic.tproj/install_zoneinfo.sh
zic.tproj/private.h
zic.tproj/scheck.c
zic.tproj/tzfile.h [new file with mode: 0644]
zic.tproj/zic.8
zic.tproj/zic.c
zprint.tproj/zprint.c

index bf95905bf92bc8e10e8027529e1b383767bd1fb3..4214037dbefad2e68d393e1b5812fac2c32862e6 100644 (file)
@@ -16,8 +16,6 @@
        <key>ProgramArguments</key>
        <array>
                <string>/sbin/dynamic_pager</string>
-               <string>-F</string>
-               <string>/private/var/vm/swapfile</string>
        </array>
 </dict>
 </plist>
index 9c0f435f3b102dd5da48d8c4289daf6cd603614a..deb9379c191e80c31b9b4738e0d600b5664b7621 100644 (file)
@@ -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 (file)
index 0000000..2e0c44d
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>com.apple.rootless.volume.VM</key>
+       <true/>
+</dict>
+</plist>
index 288f045baec9fdb5c6964579f9757e9f1131a65b..290a55029465a0b1812c434e8bcea3aeeac7b476 100644 (file)
@@ -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)
index 52894219fd8b7ea599984c6a9cea4248f1caf7af..f7f9bfc29119ba62d82d4ecacf36eecec5ce4c6e 100644 (file)
@@ -40,7 +40,8 @@
 #include <err.h>
 #include <libutil.h>
 
-#include <ktrace.h>
+#include <ktrace/session.h>
+#include <System/sys/kdebug.h>
 #include <assert.h>
 
 #include <sys/disk.h>
@@ -126,6 +127,7 @@ void extend_syscall(uintptr_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);
+int print_open(ktrace_event_t event, uintptr_t flags);
 
 /* metadata info hash routines */
 void meta_add_name(uint64_t blockno, const char *pathname);
@@ -717,7 +719,7 @@ main(int argc, char *argv[])
        char ch;
        int rv;
        bool exclude_pids = false;
-       double time_limit = 0.0;
+       uint64_t time_limit_ns = 0;
 
        get_screenwidth();
 
@@ -760,8 +762,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':
@@ -789,16 +795,15 @@ main(int argc, char *argv[])
        argc -= optind;
        argv += optind;
 
-       if (time_limit > 0.0) {
+       if (time_limit_ns > 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, ^{
+                       dispatch_after(dispatch_time(DISPATCH_TIME_NOW, time_limit_ns),
+                                       dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
+                       ^{
                                ktrace_end(s, 0);
                        });
-                       dispatch_resume(stop_timer);
                }
        }
 
@@ -902,7 +907,7 @@ main(int argc, char *argv[])
                if (!wideflag)
                        get_screenwidth();
        });
-       dispatch_resume(sigwinch_source);
+       dispatch_activate(sigwinch_source);
 
        init_shared_cache_mapping();
 
@@ -923,6 +928,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());
 
@@ -1551,6 +1558,40 @@ 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, uintptr_t flags)
+{
+       char mode[] = "______";
+
+       if (flags & O_RDWR) {
+               mode[0] = 'R';
+               mode[1] = 'W';
+       } else if (flags & O_WRONLY) {
+               mode[1] = 'W';
+       } else {
+               mode[0] = 'R';
+       }
+
+       if (flags & O_CREAT) {
+               mode[2] = 'C';
+       }
+       if (flags & O_APPEND) {
+               mode[3] = 'A';
+       }
+       if (flags & O_TRUNC) {
+               mode[4] = 'T';
+       }
+       if (flags & O_EXCL) {
+               mode[5] = 'E';
+       }
+
+       if (event->arg1) {
+               return printf("      [%3d] (%s) ", (int)event->arg1, mode);
+       } else {
+               return printf(" F=%-3d      (%s) ", (int)event->arg2, mode);
+       }
+}
+
 /*
  * called from:
  *
@@ -2355,46 +2396,15 @@ 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_SOCKET:
                        {
diff --git a/gcore.tproj/convert.c b/gcore.tproj/convert.c
new file mode 100644 (file)
index 0000000..33dad94
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <mach-o/fat.h>
+#include <uuid/uuid.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+#include <sysexits.h>
+#include <time.h>
+
+#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;
+}
+
+/*
+ * 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");
+       }
+
+       const size_t pathsize = path ? strlen(path) : 0;
+       int 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 (file)
index 0000000..03854b8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016 Apple Inc.  All rights reserved.
+ */
+
+#include "options.h"
+
+#include <stdbool.h>
+
+#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 */
index addcda4a3228caee066f4ae56450abe7d879f81e..8242b6e2ab038d389925125dc921a51bfdd9ca47 100644 (file)
@@ -18,6 +18,7 @@
 #include <compression.h>
 #include <sys/param.h>
 #include <libgen.h>
+#include <sys/stat.h>
 
 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;
 
-    assert(vmsize);
-    fr->vmsize = vmsize;
-    assert(fileoff >= 0);
-    fr->fileoff = fileoff;
-    fr->filesize = filesize;
-
-    assert(maxprot & VM_PROT_READ);
-    fr->maxprot = maxprot;
-    fr->initprot = initprot;
-    return fr;
+       /*
+        * A file reference allows different kinds of identifiers for
+        * the reference to be reconstructed.
+        */
+       assert(info->external_pager);
+
+       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);
+       }
+
+       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;
-
-    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;
-    }
+    ssize_t nwritten;
+       const int error = bounded_pwrite(wsd->wsd_fd, addr, size, wsd->wsd_foffset, &wsd->wsd_nocache, &nwritten);
 
-    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;
-
-        kern_return_t kr;
-        const void *srcaddr;
-
-        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;
-        }
+        const struct vm_range vr = {
+            .addr = vmaddr,
+            .size = vmsize,
+        };
+        struct vm_range d, *dp = &d;
 
-        assert(vmsize);
+        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 (opt->compress) {
-            dstbuf = malloc((size_t)vmsize);
-            if (dstbuf) {
+        mach_vm_behavior_set(mach_task_self(), V_ADDR(dp), V_SIZE(dp), VM_BEHAVIOR_SEQUENTIAL);
 
-                filesize = compression_encode_buffer(dstbuf, (size_t)vmsize, srcaddr, (size_t)vmsize, NULL, opt->calgorithm);
+        void *dstbuf = NULL;
+        unsigned algorithm = 0;
+        size_t filesize;
 
-                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);
-
-        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);
+               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));
+        }
 
-        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)
index d455d7e7d0fcb7b1d6a0d81b47355cfc7d9362d0..2bdc43e0d2405dcd437650ccbbe2cfdfb6f591bd 100644 (file)
@@ -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;
 };
index c8235b55364e5ee8be6643a13f4c6fe76b4a34f9..0ff3958e8a8d48e09c9454d2746ba6bd684086f3 100644 (file)
@@ -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;
 }
index cac14096511a4d95562caaae14c985abcf087b0d..02ded2a78e2b5179a3bfa8b79ec819dc49864d47 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "options.h"
 #include "corefile.h"
+#include "utils.h"
 
 #include <mach-o/dyld_images.h>
 #include <mach-o/dyld_process_info.h>
@@ -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 */
index 91aefa5d53edf53bcd0dd6b68a069bb04e5fc5ec..92d0042c9578ceeabe7ae0c9dd8d16ba05ae36fe 100644 (file)
@@ -73,14 +73,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 +89,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-internal.1 b/gcore.tproj/gcore-internal.1
new file mode 100644 (file)
index 0000000..665e2f5
--- /dev/null
@@ -0,0 +1,194 @@
+.Dd 9/29/16\r
+.Dt gcore-internal 1\r
+.Os Darwin\r
+.Sh NAME\r
+.Nm gcore\r
+.Nd get core images of running processes and corpses\r
+.Sh SYNOPSIS\r
+.Nm\r
+.Op Fl x\r
+.Op Fl F\r
+.Op Fl C\r
+.Op Fl Z Ar compopts\r
+.Op Fl t Ar threshold\r
+.Op Fl d\r
+.Ar args ...\r
+.Nm\r
+.Sy conv\r
+.Op Fl L Ar searchpath\r
+.Op Fl z\r
+.Op Fl v\r
+.Op Fl d\r
+.Ar incore outcore\r
+.Nm\r
+.Sy map\r
+.Ar corefile\r
+.Nm\r
+.Sy fref\r
+.Ar corefile\r
+.Sh DESCRIPTION\r
+For an introduction to this command and its options, see\r
+.Xr gcore 1 .\r
+This page describes various experimental capabilities\r
+of the\r
+.Nm\r
+command intended, for the moment, for internal use only.\r
+.Pp\r
+The following set of additional flags are available:\r
+.Bl -tag -width Fl\r
+.It Fl x\r
+Create extended (compact) core files.  With this flag,\r
+.Nm\r
+elides empty and unmapped regions from the dump, and uses\r
+metadata from the VM system and\r
+.Xr dyld 1\r
+to minimize the size of the dump, writing compressed versions of\r
+the active regions of the address space into the dump file.\r
+.Nm\r
+also records file references to the various files mapped into the\r
+address space, potentially including the shared cache, to\r
+avoid duplicating content already present on the filesystem.\r
+Taken together, these techniques can lead to very significant\r
+space savings for the core file, particularly for smaller programs.\r
+.It Fl F\r
+Normally when\r
+.Fl x\r
+is specified,\r
+.Nm\r
+makes conservative assumptions about which files should be\r
+incorporated into the dump as file references so that the\r
+full core file can be recreated later.  This flag attempts to make\r
+.Em every\r
+mapped file into a file reference.  While this can occasionally\r
+be useful for applications that map many files into their address space,\r
+it may be\r
+.Em extremely\r
+difficult to recreate the process image as a result.\r
+Use cautiously!\r
+.El\r
+.Pp\r
+The remaining options are more relevant to the\r
+.Nm\r
+maintainers:\r
+.Bl -tag -width Fl\r
+.It Fl C\r
+Forcibly generate a corpse for the process, even if the process is suspended.\r
+.It Fl Z Ar compopts\r
+Specify compression options e.g. algorithm and chunksize.\r
+.It Fl t Ar threshold\r
+Set the threshold at which I/O caching is disabled to\r
+.Ar threshold\r
+KiBytes.\r
+.It Fl d\r
+Enable debugging of the\r
+.Nm\r
+command.\r
+.El\r
+.Pp\r
+If the\r
+.Ar pid\r
+value is specified as 0,\r
+.Nm\r
+assumes it has been passed a corpse port by its parent;\r
+if so it will generate a core dump for that corpse.  The\r
+.Fl c\r
+flag may not be used in this case, as the process context may no longer exist.\r
+.Pp\r
+The\r
+.Nm\r
+command supports several sub-commands that can be\r
+used with extended core files created using the\r
+.Fl x\r
+flag.  These are:\r
+.Bl -tag -width frefs\r
+.\" -compact -offset indent\r
+.Pp\r
+.It Sy conv\r
+Copy and convert a core file to the "pre-coreinfo" format\r
+compatible with\r
+.Xr lldb(1) .\r
+This operation reads the input core file dereferencing any file\r
+references it contains by copying the content\r
+and decompressing any compressed data into the output core file.\r
+This conversion usually makes the core file substantially larger.\r
+.Pp\r
+Files to be dereferenced must be accessible on the\r
+local filesystem by the same relative paths as they were originally recorded\r
+when the dump was taken.\r
+Files that are Mach-O objects containing UUIDs are required to match\r
+the UUIDs recorded at the time the core dump was taken.\r
+Files are otherwise only checked for matching modification times, and\r
+thus can easily be "forged" using\r
+.Xr touch 1 .\r
+.Pp\r
+Several flags can be used with the conversion:\r
+.Pp\r
+.Bl -tag -width Fl\r
+.It Fl L Ar searchpath\r
+When processing file references,\r
+look for the pathnames in the directories specified in\r
+.Ar searchpath .\r
+These should be specified as a colon-separated\r
+list of base directories which will be prepended to each pathname in turn\r
+for each file reference.\r
+.It Fl z\r
+During conversion, if any mapped file\r
+identified by modification time\r
+cannot be located, substitute zeroed memory.\r
+.It Fl v\r
+Report progress on the conversion as it proceeds.\r
+.It Fl d\r
+Enable debugging of the\r
+.Sy conv\r
+subcommand.\r
+.El\r
+.It Sy map\r
+Print a representation of the address space contained in the core file.\r
+.Pp\r
+.It Sy frefs\r
+Print a list of files corresponding to the file references\r
+in the core file.\r
+Can be used to capture the set of files needed to bind the file references\r
+into the core file at a later time.\r
+.El\r
+.Sh BUGS\r
+.Pp\r
+When using the\r
+.Fl x\r
+flag,\r
+.Nm\r
+will likely incorporate a reference to the shared cache into\r
+.Ar corefile\r
+including the UUID of that cache.\r
+On some platforms, the cache is created when the release is built\r
+and since it resides on a read-only root filesystem it should\r
+generally be easy to retrieve.\r
+However on the desktop, the lifecycle of this cache is managed locally\r
+e.g. with\r
+.Xr update_dyld_shared_cache 1.\r
+When this cache is recreated it is given a new UUID, the directory\r
+entry for the old cache is removed, and the same filename\r
+is used for the new cache.\r
+Thus when the last named copy of the shared cache is removed from the\r
+filesystem, extended core files that contain references to that cache\r
+can no longer be converted.\r
+.Pp\r
+When using the\r
+.Fl x\r
+flag,\r
+.Nm\r
+may be unable to locate the currently mapped shared cache in the filesystem.\r
+In this case \r
+.Nm\r
+does not generate any file references to the content of the\r
+shared cache; it just copies as much of the content\r
+as is needed from the memory image into the core file.\r
+This may lead to substantially larger core files than expected.\r
+.Pp\r
+It would be nice if\r
+.Xr lldb 1\r
+could examine extended core files directly i.e. without the conversion step.\r
+.Pp\r
+.Sh SEE ALSO \r
+.Xr dyld 1 ,\r
+.Xr update_dyld_shared_cache 1\r
index 47e305491c665602734d01ea0e550352e3007ad8..16cf5b55bb89d8fcb7049d8c55ff2fcab66012f5 100644 (file)
 /*
  * Something like this should end up in <mach-o/loader.h>
  */
-#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 */
index d43446801e340be756f4026a721f938a853cfa78..4c2c3aa67b171a5c4d9ccd61d6ce17e2db07f4cf 100644 (file)
@@ -7,6 +7,7 @@
 #include "corefile.h"
 #include "vanilla.h"
 #include "sparse.h"
+#include "convert.h"
 
 #include <sys/types.h>
 #include <sys/sysctl.h>
@@ -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,153 @@ 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,
+};
+
+static int
+gcore_main(int argc, char *const *argv)
+{
 #define        ZOPT_ALG        (0)
 #define        ZOPT_CHSIZE     (ZOPT_ALG + 1)
 
@@ -175,13 +311,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 +335,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 +373,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 +397,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 +410,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 +441,414 @@ 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");
+
+       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();
+
+       if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host))
+               errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh);
+
+    const pid_t apid = atoi(argv[optind]);
+       pid_t pid = apid;
+       mach_port_t corpse = MACH_PORT_NULL;
+       kern_return_t ret;
+
+       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 (pid < 1 || getpid() == pid)
+               errx(EX_DATAERR, "invalid pid: %d", pid);
+
+       if (0 == apid && MACH_PORT_NULL == corpse)
+               errx(EX_DATAERR, "missing or bad corpse from parent");
+
+       task_t task = TASK_NULL;
+       const struct proc_bsdinfo *pbi = NULL;
+
+       if (-1 != kill(pid, 0)) {
+               /* process or corpse that responds to signals */
+               pbi = get_bsdinfo(pid);
+               if (NULL == pbi)
+                       errx(EX_OSERR, "cannot get process info for %d", pid);
+
+               /* 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 (!proc_same_data_model(pbi))
+                       errx(EX_OSERR, "cannot match data model of %d", pid);
+
+               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 (NULL == corefname)
+                       corefname = make_gcore_path(&corefmt, pbi->pbi_pid, pbi->pbi_uid, pbi->pbi_name[0] ? pbi->pbi_name : pbi->pbi_comm);
+
+               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));
+                       }
+               }
+
+               /*
+                * 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) {
+                       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 */
+
+               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 (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(") ");
+        }
+               printf("for pid %d to %s\n", pid, corefname);
+    }
+
+    int ecode;
 
-    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");
+       if (MACH_PORT_NULL == corpse) {
+               assert(TASK_NULL != task);
+
+               /*
+                * 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.
+                */
+
+               int trycorpse = 1;          /* default: use corpses */
+               int badcorpse_is_fatal = 1; /* default: failure to create is an error */
+
+               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 (opt->suspend)
+                       task_suspend(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);
+               }
+
+       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;
+}
 
-    setpageshift();
+#if defined(CONFIG_GCORE_FREF) || defined(CONFIG_GCORE_MAP) || defined(GCONFIG_GCORE_CONV)
 
-    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);
-        }
-    }
+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);
 
-    const struct proc_bsdinfo *pbi = get_bsdinfo(pid);
-    if (NULL == pbi)
-        errx(EX_OSERR, "cannot get bsdinfo about %d", pid);
+       struct mach_header mh;
+       if (-1 == pread(fd, &mh, sizeof (mh), 0))
+               errc(EX_OSERR, errno, "cannot read mach header from %s", infile);
 
-    /*
-     * 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);
+       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;
+}
 
-#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);
-
-    /*
-     * 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 (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 (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);
-    }
 
-    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));
-    }
+#ifdef CONFIG_GCORE_FREF
 
-    /*
-     * 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");
-
-    if (getuid() != pbi->pbi_uid &&
-        setuid(pbi->pbi_uid) == -1)
-        errc(EX_NOPERM, errno, "insufficient privilege");
-
-    int fd;
-
-    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);
-
-        struct stat st;
-
-        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);
-        }
-    }
+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]));
+}
 
-    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 (0 != opt->sizebound) {
-                hsize_str_t hstr;
-                printf(", %s", str_hsize(hstr, opt->sizebound));
-            }
-            printf(") ");
-        }
-        printf("for pid %d to %s\n", pid, corefname);
-    }
-    int ecode;
+#endif /* CONFIG_GCORE_FREF */
 
-    /*
-     * 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.
-     */
-
-    int trycorpse = 1;          /* default: use corpses */
-    int badcorpse_is_fatal = 1; /* default: failure to create a corpse is an error */
-
-    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;
-    }
+#ifdef CONFIG_GCORE_MAP
 
-    if (opt->suspend)
-        task_suspend(task);
-
-    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);
-    }
+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]));
+}
 
-out:
-    if (opt->suspend)
-        task_resume(task);
-
-    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);
+#endif
 
-    return 0;
+#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] incore outcore\n", pgm, argv[1]);
+       });
+
+       char *searchpath = NULL;
+       bool zf = false;
+
+       int c;
+       optind = 2;
+       while ((c = getopt(argc, argv, "dzvL:")) != -1) {
+               switch (c) {
+                               /*
+                                * likely documented options
+                                */
+                       case 'L':
+                               searchpath = strdup(optarg);
+                               break;
+                       case 'z':
+                               zf = true;
+                               break;
+                       case 'v':
+                               options.verbose++;
+                               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);
 }
index 051be4ae0b01b5c68116de4b5cb11c6e035698e5..57e77687f06b617ba971eb42081b1849b1298594 100644 (file)
 
 #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 */
 #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
 };
 
 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 */
index a4f0afb95e4e61579e78f724c90f283d17993208..77853b2a831e577b9e93dbfe54361ec9464f9608 100644 (file)
 #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;
 
index 26d4dc789086f9c03aaa236bd60a829105f24964..ad5d91725952c19c9d31a01b52f3ca069c7bef5c 100644 (file)
@@ -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,59 +131,41 @@ 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 %zd for %zd 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,
+                           str_prot(sc->initprot),
                            sc->fileoff, sc->filesize,
                            sc->nsects, sc->flags);
                 }
@@ -220,7 +207,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 +224,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 +276,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);
-            }
-        }
-
-        /*
-         * 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;
-        }
-    }
+               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;
+
+                               /* __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 +353,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 +376,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 (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_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_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 +426,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 +437,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 +456,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 +466,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 +478,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;
 
index 1880bc569ab59d0bd64ea0af00c0e1f3aba705d2..cf920ff64aa8b43a71ff13a0f919008b2aedf0ff 100644 (file)
@@ -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) {
index 9903803ef78729dd52a9de6318a76dbe1569ccf4..b1b3d6f814ede08150516b99d1365364d922af2b 100644 (file)
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
+#include <stddef.h>
 
 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);
 }
index 2c90967f014baec085da1d14c41a3e9be7b63c1c..f0edcf842f0d4e0510dcb00104fc01152eb557c4 100644 (file)
@@ -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 <stdarg.h>
 #include <unistd.h>
 #include <libutil.h>
+#include <errno.h>
 
 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;
+}
index 890f837ea57c6df8db0fe3b9b841299397c7dfd8..37eda58d81b119e7ac3f0cc5d1827cfb2e4865f1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Apple Inc.  All rights reserved.
+ * Copyright (c) 2016 Apple Inc.  All rights reserved.
  */
 
 #include <stdio.h>
@@ -9,20 +9,34 @@
 #include <mach/mach_types.h>
 #include <sysexits.h>
 #include <err.h>
+#include <fcntl.h>
 
 #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 */
index 61f9c097e4679add43224115821acfcebcfad0be..2253bffd9f3c1ef9cd8e5e586198adc3bbf80acc 100644 (file)
@@ -15,6 +15,7 @@
 #include <sys/sysctl.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
+#include <sys/mount.h>
 #include <libproc.h>
 
 #include <stdio.h>
 
 #include <mach/mach.h>
 
-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);
-
-    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 (opt->debug)
+    walk_region_list(rhead, simple_region_optimization, NULL);
+
+       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;
+                       }
+
+                       /*
+                        * 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;
 }
index ac29e856564dce0d3417839acf48531d7186fe07..721c6533071b75dbd409df9d851fe99c3ce7b549 100644 (file)
@@ -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);
 
index fdd68146eec9b139601af1651c074ab0d50111a8..22b0efe2b7413ac2b3851e2bb54e5c26a7fda98e 100644 (file)
@@ -15,7 +15,6 @@
 #include <unistd.h>
 #include <stdbool.h>
 #include <assert.h>
-
 #include <sys/queue.h>
 
 /*
@@ -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 ||
index b029bea735a6a339f5a8b82f4a75282559a7a208..b91278282e0b771dd7016271a05adba962007ede 100644 (file)
@@ -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 */
index 2170bc9a2020a536a312b214e3eadb8a503437c5..ef42607190edd281e2b73c32fd69df49688320c1 100644 (file)
@@ -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}"
index 1993b30c2daca7b9e5897d6ae9a87868619c8b47..1828583bc25b732a40746d6f78fab761d50f8c4b 100644 (file)
@@ -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)
index 51cf5b0ad00ccdbf4c273f2b9c5b8e28a7462304..71ec67186eba538fe9919ff9f4fe052b2435d591 100644 (file)
@@ -249,7 +249,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);
 
index 84cb434e2dd05348e242f518b86dcc347509fdb5..872f5769579853a57d0b32baf220329ac4e7e328 100644 (file)
 /*
  * 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
+#define KN_ACTIVE          0x0001
+#define KN_QUEUED          0x0002
+#define KN_DISABLED        0x0004
+#define KN_DROPPING        0x0008
+#define KN_USEWAIT         0x0010
+#define KN_ATTACHING       0x0020
+#define KN_STAYACTIVE      0x0040
+#define KN_DEFERDELETE     0x0080
+#define KN_ATTACHED        0x0100
+#define KN_DISPATCH        0x0200
+#define KN_UDATA_SPECIFIC  0x0400
+#define KN_SUPPRESSED      0x0800
+#define KN_STOLENDROP      0x1000
+#define KN_REQVANISH       0x2000
+#define KN_VANISHED        0x4000
 
 
 /*
@@ -50,6 +56,8 @@
 #define KQ_KEV32      0x08
 #define KQ_KEV64      0x10
 #define KQ_KEV_QOS    0x20
+#define KQ_WORKQ      0x40
+#define KQ_WORKLOOP   0x80
 
 /*
  * bsd/sys/signal.h
@@ -110,6 +118,9 @@ filt_strs[] = {
        "VM",
        "SOCK",
        "MEMSTATUS",
+       "EXCEPT",
+       "CHANNEL",
+       "WORKLOOP",
 };
 
 /*
@@ -125,6 +136,10 @@ fdtype_strs[] = {
        "KQUEUE",
        "PIPE",
        "FSEVENTS",
+       "ATALK",
+       "POLICY",
+       "CHANNEL",
+       "NEXUS",
 };
 
 #endif /* _LSKQ_COMMON_H_ */
index ff79a92251e859bcbd0715674bc86f41cad6ae18..19ae9bcd35acd574e97f228abd0c6e58dba4b874 100644 (file)
@@ -131,6 +131,16 @@ NOTE_TRIGGER
 NOTE_FFAND
 .It Sy o
 NOTE_FFOR
+.Pp
+.It EVFILT_WORKLOOP:
+.It Sy t w
+NOTE_WL_THREAD_REQUEST, NOTE_WL_SYNC_WAIT
+.It Sy !
+NOTE_WL_SYNC_WAKE
+.It Sy q
+NOTE_WL_UPDATE_QOS
+.It Sy O o
+NOTE_WL_UPDATE_OWNER, NOTE_WL_DISCOVER_OWNER
 .El
 .It flags
 kevent generic flags bitmask.
index 6a3c74da2b387155523f28406e169f51b201cb68..3037fc5c9abf3f36925b9bd2985be7d97680cc78 100644 (file)
@@ -21,6 +21,7 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/event.h>
 #include <sys/time.h>
 #include <sys/proc_info.h>
+#include <sys/param.h>
+#include <pthread/pthread.h>
 #include <mach/message.h>
+#define PRIVATE
 #include <libproc.h>
+#undef PRIVATE
+#include <os/assumes.h>
+#include <os/overflow.h>
 
 #include "common.h"
 
@@ -136,14 +143,24 @@ fflags_build(struct kevent_extinfo *info, char *str, int len)
                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   ",
+                       (ff & NOTE_WL_THREAD_REQUEST) ? 't' :
+                       (ff & NOTE_WL_SYNC_WAIT)      ? 'w' : '-',
+                       (ff & NOTE_WL_SYNC_WAKE)      ? '!' : '-',
+                       (ff & NOTE_WL_UPDATE_QOS)     ? 'q' : '-',
+                       (ff & NOTE_WL_UPDATE_OWNER)   ? 'O' :
+                       (ff & NOTE_WL_DISCOVER_OWNER) ? 'o' : '-'
+               );
+               break;
 
        default:
                snprintf(str, len, "");
@@ -157,13 +174,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 +288,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 +300,117 @@ 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
+};
+
 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);
+
+                       printf("%18s ", " "); // ident
+                       printf("%-9s ", " "); // filter
+                       dynkq_printed = true;
+
+                       if (raw) {
+                               printf("%-10s ", " "); // fflags
+                               printf("%-10s ", " "); // flags
+                               printf("%-10s ", " "); // evst
+                       } else {
+                               printf("%-8s ", " "); // fdtype
+                               printf("%-7s ", " "); // fflags
+                               printf("%-15s ", " "); // flags
+                               printf("%-17s ", " "); // evst
+                       }
+
+                       int qos = MAX(MAX(kqinfo.kqdi_events_qos, kqinfo.kqdi_async_qos),
+                                       kqinfo.kqdi_sync_waiter_qos);
+                       printf("%3s ", thread_qos_name(qos));
+                       printf("%-18s ", " "); // data
+                       printf("%-18s ", " "); // udata
+                       printf("%#18llx ", kqinfo.kqdi_servicer); // ext0
+                       printf("%#18llx ", kqinfo.kqdi_owner);    // ext1
+                       printf("\n");
+               }
        }
 
        /*
@@ -322,25 +422,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 +467,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 +483,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 +492,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 +527,42 @@ 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",
+                       printf("%c%c%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_STAYQUEUED) ? 's' : '-',
+                                       (st & KN_STAYACTIVE) ? 's' : '-',
+
+                                       (st & KN_DROPPING)  ? 'd' : '-',
+                                       (st & KN_USEWAIT)   ? 'w' : '-',
+                                       (st & KN_ATTACHING) ? 'c' : '-',
+                                       (st & KN_ATTACHED)  ? 'a' : '-',
+
+                                       (st & KN_DISPATCH)       ? 's' : '-',
+                                       (st & KN_UDATA_SPECIFIC) ? 'u' : '-',
+                                       (st & KN_SUPPRESSED)     ? 'p' : '-',
+                                       (st & KN_STOLENDROP)     ? 't' : '-',
 
-                                       (st & KN_DROPPING)   ? 'o' : '-',
-                                       (st & KN_USEWAIT)    ? 'u' : '-',
-                                       (st & KN_ATTACHING)  ? 'c' : '-',
-                                       (st & KN_DEFERDROP)  ? 'f' : '-',
-                                       (st & KN_TOUCH)      ? 't' : '-'
+                                       (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 +570,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 +583,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 +633,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 +674,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 +737,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 +755,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;
                        }
                }
@@ -599,22 +775,28 @@ static void
 cheatsheet(void)
 {
        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\
+\033[1m\
+command                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[1m\
+launchd                  1                  4  ks- netbiosd       250 PROC               ------- andx r1cs upboe aqds dwca supt vn\033[0m \n\
+                                            │  │││                                               ││││            ││││ ││││ ││││ ││\n\
+          kqueue file descriptor/dynamic ID ┘  │││                                        EV_ADD ┘│││  KN_ACTIVE ┘│││ ││││ ││││ ││\n\
+                                      KQ_SLEEP ┘││                                      EV_ENABLE ┘││   KN_QUEUED ┘││ ││││ ││││ ││\n\
+                                         KQ_SEL ┘│                                      EV_DISABLE ┘│  KN_DISABLED ┘│ ││││ ││││ │└ KN_VANISHED\n\
+                                    KQ_WORKQ (q) ┤                                        EV_DELETE ┘ KN_STAYACTIVE ┘ ││││ ││││ └ KN_REQVANISH\n\
+                                 KQ_WORKLOOP (l) ┘                                                                    ││││ ││││\n\
+                                                                                                          KN_DROPPING ┘│││ │││└ KN_STOLENDROP\n\
+                                                                                                            KN_USEWAIT ┘││ ││└ KN_SUPPRESSED\n\
+                                                                                                           KN_ATTACHING ┘│ │└ KN_UDATA_SPECIFIC\n\
+                                                                                                             KN_ATTACHED ┘ └ KN_DISPATCH\n\
        \n");
 }
 
@@ -628,20 +810,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               knid filter    fdtype   fflags       flags             evst       qos               data");
+       }
+
+       if (verbose) {
+               printf("              udata    servicer / ext0       owner / ext1               ext2               ext3     xflags");
+       }
+
+       printf("\n");
 
+       if (raw) {
+               printf("----- ------------------ ---------- ------------------ --------- ---------- ---------- ---------- --- ------------------");
        } 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("-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- ----------------- --- ------------------");
        }
 
        if (verbose) {
index ea07dc22cdee60e3ee4bb18c98b2961bbf44beb8..d9552ca9d51dcc849fb964e2065601b0d4bc21ba 100644 (file)
@@ -25,6 +25,7 @@
 #define system_cmds_common_h
 
 #include <mach/mach.h>
+#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);
diff --git a/lsmp.tproj/json.h b/lsmp.tproj/json.h
new file mode 100644 (file)
index 0000000..2a87a8b
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#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_ */
index 1b4d28d321f282b36dcf23f91115b8ae1b120ff9..d4c70e938b64833b9767240b64c27ef4ec31d157 100644 (file)
@@ -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 <path>
+Save output as JSON to <path>.
 .Sh DESCRIPTION
 The
 .Nm lsmp
index c2e333008f2c0506ccf936940d670c7b0ec54f4e..2e46f7009d7203b4aaff338d190e51678d05b3e9 100644 (file)
@@ -27,7 +27,9 @@
 #include <stdlib.h>
 #include <libproc.h>
 #include <TargetConditionals.h>
+#include <errno.h>
 #include "common.h"
+#include "json.h"
 
 #if TARGET_OS_EMBEDDED
 #define TASK_FOR_PID_USAGE_MESG "\nPlease check your boot-args to ensure you have access to task_for_pid()."
@@ -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 <pid> [-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 <pid> :  print all mach ports for process id <pid>. \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 <path> :  save output as JSON to <path>.\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' */
@@ -182,39 +206,36 @@ int main(int argc, char *argv[]) {
         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);
 }
index fec26d74bf45f33f31fed059cce68007bf498015..caed13302bfd6bec14b3e37c23f2289c416e7e75 100644 (file)
@@ -28,6 +28,7 @@
 #include <mach/mach.h>
 #include <mach/mach_voucher.h>
 #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));
 
+    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 += safesize(snprintf(&voucher_outstr[len], 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 += safesize(snprintf(&voucher_outstr[len], 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 += safesize(snprintf(&voucher_outstr[len], 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));
             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));
@@ -147,7 +213,7 @@ char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher) {
         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], detail_maxlen - plen, json);
             }
             used_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size;
         }
@@ -196,246 +262,426 @@ 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;
-
-        /* skip empty slots in the table */
-        if ((taskinfo->table[i].iin_type & MACH_PORT_TYPE_ALL_RIGHTS) == 0) {
-            emptycount++;
-            continue;
+    for (i = 0; i < taskinfo->tableCount; i++) {
+        show_task_table_entry(&taskinfo->table[i], taskinfo, taskCount, allTaskInfos, &counts, json);
+    }
+
+    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 sendonce = 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;
-
-                       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_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);
+
+        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);
-                                               break;
-                                       }
-                               }
-                       }
+                    }
+                    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;
+                }
+            }
+        }
 
-                       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;
+        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);
             }
-            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++;
-
-                       /* 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, taskinfo->table[i].iin_object);
-
-                               while (k2nnode) {
-                                       if (k2nnode->info_name != &taskinfo->table[i]) {
-                                               assert(k2nnode->info_name->iin_object == taskinfo->table[i].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);
-                                       }
+        }
+        JSON_ARRAY_END(json); // port status flags
+        JSON_OBJECT_SET(json, boosts, %d, info.mpie_boost_cnt);
 
-                                       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);
+        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, k2nnode->info_name->iin_name);
             }
+        }
+        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;
+    }
 
-            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);
-                }
+    if (entry->iin_type & MACH_PORT_TYPE_SEND_ONCE) {
+        sendonce = TRUE;
+        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);
             }
-                       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));
+        }
+
+                       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;
+                                       }
+                               }
+                       }
+
+        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);
             }
-            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");
+        }
+        JSON_OBJECT_END(json); // kobject
+        return;
+    }
 
-       }
-       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);
+    /* 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);
+
+        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);
+
+        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 {
+        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) {
index aeaa1602885f68e0cb0c936959cbe93bb09dec0e..e8b1ffe818b9344c4b832cea43f958ba7876953a 100644 (file)
@@ -191,7 +191,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);
 
@@ -288,12 +288,15 @@ void get_exc_mask_string(exception_mask_t m, char *out_string, size_t len)
         strncat(out_string," GUARD", len);
 }
 
-kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo)
+kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo, JSON_t json)
 {
 
     char behavior_string[30];
     char mask_string[200];
 
+    JSON_KEY(json, exception_ports);
+    JSON_ARRAY_BEGIN(json);
+
     boolean_t header_required = TRUE;
     for (int i = 0; i < taskinfo->exceptionInfo.count; i++) {
         if (taskinfo->exceptionInfo.ports[i] != MACH_PORT_NULL) {
@@ -304,16 +307,26 @@ kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo)
             }
             get_exc_behavior_string(taskinfo->exceptionInfo.behaviors[i], behavior_string, sizeof(behavior_string));
             get_exc_mask_string(taskinfo->exceptionInfo.masks[i], mask_string, 200);
+
+            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 +334,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 +355,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 +379,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 <behaviors>           mask   -> name    owner\n");
@@ -361,6 +388,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 +401,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 +421,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;
 }
index 9a4c90d7b980072cd05a9dedfe3c14b2051ad27e..42348e708122cf26a0ec27f993cb525348879dbb 100644 (file)
@@ -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
index 59c772b0173d0ed760ddd96ea973fd56957cb0a4..d67fbfc42bf06e2e864d292bc1ee8aab46a29561 100644 (file)
@@ -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 (file)
index 0000000..ea4f068
--- /dev/null
@@ -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 (file)
index 0000000..7d20637
--- /dev/null
@@ -0,0 +1,94 @@
+//
+//  mslutil.c
+//  mslutil
+//
+//  Created by Christopher Deppe on 3/31/17.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <stack_logging.h>
+
+#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]\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;
+        }
+        
+        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);
+    }
+}
+
+
index 96d2c0ba5ee4e1edee88b548a6637490dc0c1af2..89f27fc1f93ea1e1ba7c4b6b6c4980ee038859ec 100644 (file)
@@ -586,6 +586,26 @@ 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;
   nameBuffer = malloc(nameLen);
diff --git a/stackshot.tproj/stackshot.c b/stackshot.tproj/stackshot.c
new file mode 100644 (file)
index 0000000..7b0ddc8
--- /dev/null
@@ -0,0 +1,233 @@
+/* Copyright (c) 2017 Apple Inc. All rights reserved. */
+
+#include <stdio.h>
+#include <dispatch/dispatch.h>
+#include <sysexits.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <mach/mach_time.h>
+#include <sys/stackshot.h>
+#include <sys/types.h>
+#include <kern/debug.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <kern/kcdata.h>
+
+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, "    -t      : enable tailspin mode\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 tailspin = 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 't':
+            tailspin |= STACKSHOT_TAILSPIN;
+            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 |
+        tailspin | 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);
+}
index 30b066b5245beab964b1e9f58f6cee4aa946bec6..606459a7d56787ec0b72d5d720e550862685bed6 100644 (file)
@@ -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:
index 4b34aee34b780490f9fdde416d04439819d61d8c..da5d7cb7d2c6a22c4418d932a839a26734469662 100644 (file)
@@ -14,6 +14,7 @@
                                1812F1ED1C8F923900F3DC9E /* CopyFiles */,
                        );
                        dependencies = (
+                               926913A61EC706130079D787 /* PBXTargetDependency */,
                                1812F18D1C8F923900F3DC9E /* PBXTargetDependency */,
                                1812F18F1C8F923900F3DC9E /* PBXTargetDependency */,
                                1812F1911C8F923900F3DC9E /* PBXTargetDependency */,
@@ -70,6 +71,8 @@
                                C9D64CD21B91066B00CFA43B /* CopyFiles */,
                        );
                        dependencies = (
+                               926913A21EC706010079D787 /* PBXTargetDependency */,
+                               08CE3D361E6E24CC00DF1B78 /* PBXTargetDependency */,
                                C21481471C1A1447003BCA63 /* PBXTargetDependency */,
                                78DE9DED1B5048D400FE6DF5 /* PBXTargetDependency */,
                                97999D351AE84D3A00E8B10F /* PBXTargetDependency */,
                                C9D64CD01B91064700CFA43B /* CopyFiles */,
                        );
                        dependencies = (
+                               926913A41EC706080079D787 /* PBXTargetDependency */,
+                               08CE3D381E6E24DF00DF1B78 /* PBXTargetDependency */,
                                C21481491C1A14AD003BCA63 /* PBXTargetDependency */,
                                78DE9DFA1B504D1200FE6DF5 /* PBXTargetDependency */,
                                97999D371AE84D4100E8B10F /* PBXTargetDependency */,
 /* 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 */; };
                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 */; };
                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 */; };
 /* 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 */;
                        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 */;
 /* 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;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
+               0D06BC5C1E8F08CB00C6EC2D /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = /usr/share/man/man1/;
+                       dstSubfolderSpec = 0;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
                1523FE5F1595048900661E82 /* CopyFiles */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 2147483647;
 /* 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; };
+               08CE3D301E6E22B000DF1B78 /* stackshot */ = {isa = PBXFileReference; lastKnownFileType = folder; path = stackshot; sourceTree = "<group>"; };
+               08CE3D321E6E22DE00DF1B78 /* stackshot.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = stackshot.c; path = stackshot.tproj/stackshot.c; sourceTree = "<group>"; };
                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 = "<group>"; };
+               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 = "<group>"; };
+               0D06BC671E8F0B4100C6EC2D /* mslutil.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = mslutil.1; sourceTree = "<group>"; };
                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 = "<group>"; };
                1523FE6B1595056C00661E82 /* ltop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ltop.c; sourceTree = "<group>"; };
                C21481201C1A11E7003BCA63 /* vm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vm.c; path = gcore.tproj/vm.c; sourceTree = "<group>"; };
                C21481211C1A11E7003BCA63 /* vm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vm.h; path = gcore.tproj/vm.h; sourceTree = "<group>"; };
                C248DBAF1C1A1D0500F6E9AF /* libcompression.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcompression.dylib; path = /usr/lib/libcompression.dylib; sourceTree = "<absolute>"; };
+               C2DAA9491D9F22BF00FAC263 /* gcore-internal.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = "gcore-internal.1"; path = "gcore.tproj/gcore-internal.1"; sourceTree = "<group>"; };
+               C2DAA94A1D9F22BF00FAC263 /* convert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = convert.h; path = gcore.tproj/convert.h; sourceTree = "<group>"; };
+               C2DAA94B1D9F22BF00FAC263 /* convert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = convert.c; path = gcore.tproj/convert.c; sourceTree = "<group>"; };
                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 = "<group>"; };
                C625B28C16D6F27E00168EF7 /* taskpolicy.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = taskpolicy.8; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+               08CE3D261E6E22A200DF1B78 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                08DC48821A12C21B008AAF38 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               0D06BC5B1E8F08CB00C6EC2D /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                1523FE5D1595048900661E82 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               C97199F21C5206DE006D9758 /* libktrace.dylib in Frameworks */,
+                               08ADC98C1E70715D0001CB70 /* ktrace.framework in Frameworks */,
                                BA4B7A0A1373BA4600003422 /* libutil.dylib in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        path = kpgo.tproj;
                        sourceTree = "<group>";
                };
+               0D06BC5F1E8F08CB00C6EC2D /* mslutil */ = {
+                       isa = PBXGroup;
+                       children = (
+                               0D06BC651E8F091F00C6EC2D /* mslutil.c */,
+                               0D06BC671E8F0B4100C6EC2D /* mslutil.1 */,
+                       );
+                       path = mslutil;
+                       sourceTree = "<group>";
+               };
                1523FE691595056C00661E82 /* ltop.tproj */ = {
                        isa = PBXGroup;
                        children = (
                189337C11CC7CB4800B2A6A4 /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               08ADC98B1E70715D0001CB70 /* ktrace.framework */,
                                189337C21CC7CB4800B2A6A4 /* CoreFoundation.framework */,
                        );
                        name = Frameworks;
                BA2DE9161372FA9100D1913C = {
                        isa = PBXGroup;
                        children = (
+                               08CE3D321E6E22DE00DF1B78 /* stackshot.c */,
+                               08CE3D301E6E22B000DF1B78 /* stackshot */,
                                18EA07101C99C76C006D3005 /* EmbeddedOSSupportHost.framework */,
                                BA4FD1E11372FAFA0025925C /* APPLE_LICENSE */,
                                BA4FD2FB1372FB710025925C /* BSD.xcconfig */,
                                B3F0E6DA16E9706E008FAD09 /* memory_pressure.tproj */,
                                BA4FD26C1372FAFA0025925C /* mkfile.tproj */,
                                1865517D18CA7104003B92A7 /* msa */,
+                               0D06BC5F1E8F08CB00C6EC2D /* mslutil */,
                                BA4FD2701372FAFA0025925C /* newgrp.tproj */,
                                BA4FD2741372FAFA0025925C /* nologin.tproj */,
                                BA4FD2791372FAFA0025925C /* nvram.tproj */,
                                78DE9DE01B5045DE00FE6DF5 /* wait4path */,
                                C20D8C691C1A102F00C1226B /* gcore */,
                                8EC391651C9733C2001E28E6 /* proc_uuid_policy */,
+                               08CE3D291E6E22A200DF1B78 /* stackshot */,
+                               0D06BC5E1E8F08CB00C6EC2D /* mslutil */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                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 */,
 /* 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" */;
                        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" */;
                        attributes = {
                                LastUpgradeCheck = 0600;
                                TargetAttributes = {
+                                       08CE3D281E6E22A200DF1B78 = {
+                                               CreatedOnToolsVersion = 8.3;
+                                               ProvisioningStyle = Automatic;
+                                       };
                                        08DC48841A12C21B008AAF38 = {
                                                CreatedOnToolsVersion = 6.3;
                                                ProvisioningStyle = Manual;
                                        };
+                                       0D06BC5D1E8F08CB00C6EC2D = {
+                                               CreatedOnToolsVersion = 9.0;
+                                               ProvisioningStyle = Automatic;
+                                       };
                                        1523FE5A1595048900661E82 = {
                                                ProvisioningStyle = Manual;
                                        };
                                BA9BF4EF139684B40018C7BB /* zic */,
                                BA959E7E13968C8E00CA9C60 /* zoneinfo */,
                                BA0A860713968E8500D2272C /* zprint */,
+                               08CE3D281E6E22A200DF1B78 /* stackshot */,
+                               0D06BC5D1E8F08CB00C6EC2D /* mslutil */,
                        );
                };
 /* End PBXProject section */
                        );
                        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 */ = {
 /* 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;
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               0D06BC5A1E8F08CB00C6EC2D /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               0D06BC661E8F091F00C6EC2D /* mslutil.c in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                1523FE5B1595048900661E82 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                                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 */,
                        );
 /* 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 */;
                        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 */;
 /* 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 = {
                        };
                        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 = {
                18732FF418CBD4A700275344 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
+                               CODE_SIGN_ENTITLEMENTS = dynamic_pager.tproj/entitlements.plist;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "$(inherited)",
                                        NO_DIRECT_RPC,
                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;
                BA4B79EB1373AF7A00003422 /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
+                               CODE_SIGN_ENTITLEMENTS = dynamic_pager.tproj/entitlements.plist;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "$(inherited)",
                                        NO_DIRECT_RPC,
                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;
 /* 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 = (
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               0D06BC641E8F08CB00C6EC2D /* Build configuration list for PBXNativeTarget "mslutil" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               0D06BC621E8F08CB00C6EC2D /* Release */,
+                               0D06BC631E8F08CB00C6EC2D /* Debug */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+               };
                1523FE611595048900661E82 /* Build configuration list for PBXNativeTarget "ltop" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
index c6c9a41b2d6e1eef790f134b41d8615f8911ee6c..b13af546b854d5055612b45d5a3fc995f2e4c44c 100644 (file)
@@ -12,6 +12,7 @@
 .Op Fl b\r
 .Op Fl t Ar thruput_tier\r
 .Op Fl l Ar latency_tier\r
+.Op Fl a\r
 .Ar program\r
 .Oo\r
 .Ar arg1\r
@@ -65,6 +66,8 @@ Set throughput tier of the process to
 .It Fl l \r
 Set latency tier of the process to \r
 .Ar latency_tier .\r
+.It Fl a\r
+Run the program with the resource management policies given to applications.\r
 .El\r
 .Pp\r
 .Sh SEE ALSO \r
index b5e9a60fdf249b1d963672b9129966606cba7900..3260bb62cf51eb2c794d84ac1401964e883eb9bd 100644 (file)
@@ -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 <policy>] [-g policy] [-c clamp] [-b] [-t <tier>]\n"
-                    "                  [-l <tier>] <program> [<pargs> [...]]\n", getprogname());
+                    "                  [-l <tier>] [-a] <program> [<pargs> [...]]\n", getprogname());
        fprintf(stderr, "       %s [-b|-B] [-t <tier>] [-l <tier>] -p pid\n", getprogname());
        exit(EX_USAGE);
 }
index 190e8b290ba3ece7b563404a4b50a554bdc480dd..98aadc885883b51e2f04db6eded95d55fe0da364 100644 (file)
@@ -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)
index 985a5118468fe0d7f4667e4b362a1af89b124286..ff974e919d0bae73d1044cf0aa7f0fd4a039ea8b 100644 (file)
@@ -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!
 
index f66dea2681d47a1b497bcc5f8be2e744b8166cf4..f2ad1341bbbfe032edb7a9dae2c0af08fa501081 100755 (executable)
@@ -36,6 +36,7 @@ env -i \
        LANG="${LANG}" \
        HOME="${HOME}" \
        $EXTRA_ARGS \
+       TOOLCHAINS="${TOOLCHAINS}" \
        xcrun -sdk "${SDKROOT}" xcodebuild install \
                -target zic \
                -sdk "macosx" \
index 16484d1b86bcd3a4777ebaf4618b28fe572d657c..079e4fb51fbaff4365eae0b467aa9dc2c60c76ba 100755 (executable)
@@ -68,22 +68,22 @@ 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*)
+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"
index fabe0c64e68f442804c41e50b1e2b3923c3f9008..dda367b089765f599231a9366c355ffe4a795ba3 100644 (file)
@@ -1,13 +1,17 @@
-#include <sys/cdefs.h>
+/*
+** 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);
index 9bb9af9300f39dd421b1ff5f9d43164bb1af75fc..776c630b6c9158749b3d49bf8904ffc1ea96c939 100755 (executable)
@@ -3,18 +3,22 @@ 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
 
 case "$ACTUAL_PLATFORM_NAME" in
-iphone*|appletv*|watch*)
+iphone*|appletv*|watch*|macosx|bridge*)
     ditto "${BUILT_PRODUCTS_DIR}/zoneinfo" "${DSTROOT}/usr/share/zoneinfo.default"
     ln -hfs "/var/db/timezone/zoneinfo" "${DSTROOT}/usr/share/zoneinfo"
-    ;;
-macosx|bridge*)
-    ditto "${BUILT_PRODUCTS_DIR}/zoneinfo" "${DSTROOT}/usr/share/zoneinfo"
+    case "$ACTUAL_PLATFORM_NAME" in
+    macosx|bridge*)
+        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"
+        ;;
+    esac
     ;;
 *)
     echo "Unsupported platform: $ACTUAL_PLATFORM_NAME"
index 4ffd0698feafc6556f651926a498f2a40f40df60..ae931b053c8d5def15e2eeaec646597c179fd1cb 100644 (file)
@@ -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 $
  */
 
 /*
 
 #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 <sys/wait.h>  /* 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 <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX.  */
+/* Unlike <ctype.h>'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 */
index dcd4d01131387bd152d72a158a768ea37d8a5467..10eea82d0798de4b8b3004f85224488ed6aa7c05 100644 (file)
@@ -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 <sys/cdefs.h>
-__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 (file)
index 0000000..ec4009b
--- /dev/null
@@ -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 */
index b2a60aa6c6fa99c7a433083a02ed26c02bb1018c..a84e56323602204273d058a1a10b65823627addf 100644 (file)
@@ -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.
index 8efedaac329bacb57ff628f87a21a3a3e5599dda..75db5befcf76915d1953c5054df0c1c07d60203b 100644 (file)
@@ -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 <sys/cdefs.h>
 #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 <sys/types.h>
 #include <unistd.h>
 
+#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 void
+puttzcode64(val, fp)
+const zic_t    val;
+FILE * const   fp;
+{
+       char    buf[8];
+
+       convert64(val, buf);
+       (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
+}
+
 static int
-atcomp(const void *avp, const void *bvp)
+atcomp(avp, bvp)
+const void *   avp;
+const void *   bvp;
 {
-       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;
+       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);
-#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 (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;
                        }
-               puttzcode((long) ats[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];
+                       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;
                        }
-                       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);
+                       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 = 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);
+                       }
+               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);
+       }
+       (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
-outzone(const struct zone * const zpfirst, const int zonecount)
+stringzone(result, zpfirst, zonecount)
+char *                         result;
+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 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(zpfirst, zonecount)
+const struct zone * const      zpfirst;
+const int                      zonecount;
+{
+       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 <pwd.h>
 
 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);
index 104161cea9f78968df7a9c2ceca2880f31e1bd18..34283f2e935872256c025aa27f0aa4c55055ddc1 100644 (file)
@@ -97,9 +97,12 @@ static int  find_deltas(mach_zone_name_t *, task_zone_info_t *, task_zone_info_t
 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,
+                       task_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;
 
@@ -149,6 +152,7 @@ main(int argc, char **argv)
        unsigned int wiredInfoCnt = 0;
        task_zone_info_t *max_info = NULL;
        char            *deltas = NULL;
+       uint64_t        zoneElements;
 
        kern_return_t   kr;
        int             i, j;
@@ -316,6 +320,7 @@ main(int argc, char **argv)
        }
 
        must_print = find_deltas(name, info, max_info, deltas, infoCnt, first_time);
+       zoneElements = 0;
        if (must_print) {
                if (ColFormat) {
                        if (!first_time)
@@ -328,12 +333,14 @@ main(int argc, char **argv)
                                        colprintzone(&name[i], &info[i]);
                                else
                                        printzone(&name[i], &info[i]);
+                               zoneElements += info[i].tzi_count;
                        }
                }
        }
 
        if (ShowLarge && first_time) {
-               PrintLarge(wiredInfo, wiredInfoCnt,
+               PrintLarge(wiredInfo, wiredInfoCnt, &info[0], &name[0],
+                          nameCnt, zoneElements,
                           SortZones ? &SortSize : &SortName, ColFormat);
        }
 
@@ -629,8 +636,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 +670,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 +692,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 +709,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 +747,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);
 }
 
+struct CompareThunk
+{
+    mach_zone_name_t *zoneNames;
+    unsigned int      zoneNamesCnt;
+};
+
 static int
-SortName(const void * left, const void * right)
+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);
+
+    lcf = CFStringCreateWithCString(kCFAllocatorDefault, l, kCFStringEncodingUTF8);
+    rcf = CFStringCreateWithCString(kCFAllocatorDefault, r, kCFStringEncodingUTF8);
+
+    result = (int) CFStringCompareWithOptionsAndLocale(lcf, rcf, CFRangeMake(0, CFStringGetLength(lcf)), kCFCompareNumerically, NULL);
 
-    result = strcmp(l, r);
+    CFRelease(lcf);
+    CFRelease(rcf);
     free(l);
     free(r);
 
@@ -751,7 +811,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 +831,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)
+               task_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 +866,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;
+
+       if ((VM_KERN_SITE_ZONE & gSites[site].flags)
+          && gSites[site].zone < zoneCnt)
+       {
+           elemsTagged += gSites[site].size / zoneInfo[gSites[site].zone].tzi_elem_size;
+       }
+
+       if ((gSites[site].size < 1024) && (gSites[site].peak < 1024)) continue;
 
-       name = GetSiteName(site);
+       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,37 +924,49 @@ 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");
     }