system_cmds-854.40.2

This commit is contained in:
Ariel Abreu 2020-08-19 11:36:54 -04:00
parent c76f955c45
commit c04e2cca25
No known key found for this signature in database
GPG Key ID: ECF8C2B9E8AD3E6B
102 changed files with 9737 additions and 4803 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/system_cmds.xcodeproj/project.xcworkspace
/system_cmds.xcodeproj/xcuserdata
.DS_Store
build/
*~
*.swp

2
.upstream_base_commits Normal file
View File

@ -0,0 +1,2 @@
#freebsd = https://github.com/freebsd/freebsd.git
iostat.tproj/iostat.8 freebsd usr.sbin/iostat/iostat.8 4724d1448f7e7698308a1e05a7bb9b2069d68234

View File

@ -58,7 +58,7 @@ command with no arguments, displays the machine's architecture type.
.Pp
The other use of the
.Nm arch
command it to run a selected architecture of a universal binary.
command is to run a selected architecture of a universal binary.
A universal binary contains code that can run on different architectures.
By default, the operating system will select the architecture that most closely
matches the processor type.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2016 Apple Inc. All rights reserved.
* Copyright (c) 1999-2018 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
@ -76,9 +76,10 @@ static const CPUTypes knownArchs[] = {
#if defined(__i386__) || defined(__x86_64__)
{"i386", CPU_TYPE_I386},
{"x86_64", CPU_TYPE_X86_64},
#elif defined(__arm__) || defined(__arm64__)
{"arm", CPU_TYPE_ARM},
#elif defined(__arm64__)
{"arm64", CPU_TYPE_ARM64},
#elif defined(__arm__)
{"arm", CPU_TYPE_ARM},
#else
#error "Unsupported architecture"
#endif
@ -98,14 +99,18 @@ extern char **environ;
* the arch command is running. NULL means unsupported.
*/
#if defined(__i386__) || defined(__x86_64__)
#define NATIVE_32 "i386"
#define NATIVE_64 "x86_64"
#elif defined(__arm__) || defined(__arm64__)
#define NATIVE_32 "arm"
#define NATIVE_64 "arm64"
#define NATIVE_32 "i386"
#define NATIVE_64 "x86_64"
#elif defined(__arm64__)
#define NATIVE_64 "arm64"
#define NATIVE_32 NULL
#elif defined(__arm__)
#define NATIVE_32 "arm"
#define NATIVE_64 NULL
#else
#error "Unsupported architecture"
#error "Unsupported architecture"
#endif
bool unrecognizednative32seen = false;
bool unrecognizednative64seen = false;

9
base.xcconfig Normal file
View File

@ -0,0 +1,9 @@
CODE_SIGN_IDENTITY = -;
CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion);
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym;
PREBINDING = NO;
// Current macOS
SDKROOT = macosx.internal;
VERSION_INFO_PREFIX = __attribute__((visibility("hidden"))) __
VERSIONING_SYSTEM = apple-generic;

View File

@ -63,7 +63,7 @@
#include <stdio.h>
#include <string.h>
#include <pw_util.h>
#include "pw_util.h"
#include "pw_copy.h"
extern char *tempname;

67
cpuctl.tproj/cpuctl.8 Normal file
View File

@ -0,0 +1,67 @@
.\" Darwin cpuctl man page adapted from NetBSD (credits below):
.\"
.\" $NetBSD: cpuctl.8,v 1.18 2018/01/14 00:45:54 mrg Exp $
.\"
.\" Copyright (c) 2007, 2008, 2012, 2015 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
.\" by Andrew Doran.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd March 18, 2019
.Dt CPUCTL 8
.Os Darwin
.Sh NAME
.Nm cpuctl
.Nd program to control CPUs
.Sh SYNOPSIS
.Nm cpuctl
.Ar command
.Op Ar arguments
.Sh DESCRIPTION
The
.Nm
command can be used to control and inspect the state of CPUs in the system.
.Pp
The first argument,
.Ar command ,
specifies the action to take.
Valid commands are:
.Bl -tag -width offline
.It list
For each CPU in the system, display the current state and time of the last
state change.
.It offline Ar cpu Op Ar cpu ...
Set the specified CPUs off line.
.Pp
At least one CPU in the system must remain on line.
.It online Ar cpu Op Ar cpu ...
Set the specified CPUs on line.
.El
.Sh EXAMPLES
Run
.Dl cpuctl offline 2
and then
.Dl cpuctl list
The output should reflect the fact that CPU#2 was taken offline.

145
cpuctl.tproj/cpuctl.c Normal file
View File

@ -0,0 +1,145 @@
//
// cpuctl.c
// system_cmds
//
// Copyright (c) 2019 Apple Inc. All rights reserved.
//
#include <err.h>
#include <getopt.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <mach/mach.h>
static void usage()
{
printf("usage: cpuctl [ list ]\n");
printf(" cpuctl { offline | online } <cpu> [ <cpu>... ]\n");
exit(EX_USAGE);
}
static void fetch_cpu_info(host_t *priv_port,
processor_port_array_t proc_ports,
mach_msg_type_number_t proc_count,
processor_basic_info_data_t *cpus)
{
for (int i = 0; i < proc_count; i++) {
mach_msg_type_number_t info_count = PROCESSOR_BASIC_INFO_COUNT;
if (processor_info(proc_ports[i], PROCESSOR_BASIC_INFO, priv_port,
(processor_info_t)&cpus[i], &info_count) != KERN_SUCCESS) {
errx(EX_OSERR, "processor_info(%d) failed", i);
}
}
}
static int do_cmd_list(mach_msg_type_number_t proc_count, processor_basic_info_data_t *cpus)
{
int prev_lowest = -1;
for (int i = 0; i < proc_count; i++) {
int lowest_slot = INT_MAX;
int lowest_idx = -1;
for (int j = 0; j < proc_count; j++) {
int slot = cpus[j].slot_num;
if (slot > prev_lowest && slot < lowest_slot) {
lowest_slot = slot;
lowest_idx = j;
}
}
if (lowest_idx == -1)
errx(EX_OSERR, "slot numbers are out of range");
processor_basic_info_data_t *cpu = &cpus[lowest_idx];
printf("CPU%d: %-7s type=%x,%x master=%d\n",
cpu->slot_num,
cpu->running ? "online" : "offline",
cpu->cpu_type,
cpu->cpu_subtype,
cpu->is_master);
prev_lowest = lowest_slot;
}
return 0;
}
static int find_cpu_by_slot(mach_msg_type_number_t proc_count,
processor_basic_info_data_t *cpus,
int slot)
{
for (int i = 0; i < proc_count; i++) {
if (cpus[i].slot_num == slot)
return i;
}
return -1;
}
int main(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "h")) != -1) {
switch (opt) {
case 'h':
usage();
}
}
host_t priv_port;
if (host_get_host_priv_port(mach_host_self(), &priv_port) != KERN_SUCCESS)
errx(EX_OSERR, "host_get_host_priv_port() failed");
processor_port_array_t proc_ports;
mach_msg_type_number_t proc_count;
if (host_processors(priv_port, &proc_ports, &proc_count) != KERN_SUCCESS)
errx(EX_OSERR, "host_processors() failed");
processor_basic_info_data_t *cpus = calloc(proc_count, sizeof(*cpus));
if (!cpus)
errx(EX_OSERR, "calloc() failed");
fetch_cpu_info(&priv_port, proc_ports, proc_count, cpus);
if (optind == argc)
return do_cmd_list(proc_count, cpus);
const char *cmd = argv[optind];
optind++;
if (!strcmp(cmd, "list"))
return do_cmd_list(proc_count, cpus);
bool up = true;
if (!strncmp(cmd, "off", 3))
up = false;
else if (strncmp(cmd, "on", 2))
usage();
if (optind == argc)
usage();
int ret = 0;
for (; optind < argc; optind++) {
char *endp = NULL;
int slot = (int)strtoul(argv[optind], &endp, 0);
if (*endp != 0)
usage();
int cpu = find_cpu_by_slot(proc_count, cpus, slot);
if (cpu == -1)
errx(EX_USAGE, "Invalid CPU ID %d", slot);
if (up) {
if (processor_start(proc_ports[cpu]) != KERN_SUCCESS)
errx(EX_OSERR, "processor_start(%u) failed", cpu);
} else {
if (processor_exit(proc_ports[cpu]) != KERN_SUCCESS)
errx(EX_OSERR, "processor_exit(%u) failed", cpu);
}
}
return ret;
}

View 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>

View 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);
}

View File

@ -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>

View 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)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2016 Apple Inc. All rights reserved.
* Copyright (c) 1999-2019 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
@ -40,18 +40,20 @@
#include <err.h>
#include <libutil.h>
#include <ktrace.h>
#include <assert.h>
#include <ktrace/session.h>
#include <System/sys/kdebug.h>
#include <os/assumes.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <sys/disk.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/syslimits.h>
#include <sys/time.h>
#include <sys/types.h>
#import <mach/clock_types.h>
#import <mach/mach_time.h>
@ -66,7 +68,7 @@
typedef struct th_info {
struct th_info *next;
uintptr_t thread;
uint64_t thread;
/* this is needed for execve()/posix_spawn(), because the command name at the end probe is the new name, which we don't want */
char command[MAXCOMLEN + 1];
@ -76,21 +78,21 @@ typedef struct th_info {
* (e.g., one absolute, one relative). traditional fs_usage behavior was to display the
* *first* lookup, so we need to save it off once we see it.
*/
unsigned long vnodeid; /* the vp of the VFS_LOOKUP we're currently in, 0 if we are not in one */
uint64_t vnodeid; /* the vp of the VFS_LOOKUP we're currently in, 0 if we are not in one */
char pathname[MAXPATHLEN];
char pathname2[MAXPATHLEN];
char *newest_pathname; /* points to pathname2 if it's filled, otherwise pathname if it's filled, otherwise NULL */
int pid;
int type;
unsigned long arg1;
unsigned long arg2;
unsigned long arg3;
unsigned long arg4;
unsigned long arg5;
unsigned long arg6;
unsigned long arg7;
unsigned long arg8;
uint64_t arg1;
uint64_t arg2;
uint64_t arg3;
uint64_t arg4;
uint64_t arg5;
uint64_t arg6;
uint64_t arg7;
uint64_t arg8;
int waited;
uint64_t stime;
} *th_info_t;
@ -98,17 +100,17 @@ typedef struct th_info {
struct diskio {
struct diskio *next;
struct diskio *prev;
unsigned long type;
unsigned long bp;
unsigned long dev;
unsigned long blkno;
unsigned long iosize;
unsigned long io_errno;
unsigned long is_meta;
uint64_t type;
uint64_t bp;
uint64_t dev;
uint64_t blkno;
uint64_t iosize;
uint64_t io_errno;
uint64_t is_meta;
uint64_t vnodeid;
uintptr_t issuing_thread;
uint64_t issuing_thread;
pid_t issuing_pid;
uintptr_t completion_thread;
uint64_t completion_thread;
char issuing_command[MAXCOMLEN + 1];
uint64_t issued_time;
uint64_t completed_time;
@ -120,11 +122,12 @@ struct diskio {
#define HASH_MASK (HASH_SIZE - 1)
void setup_ktrace_callbacks(void);
void extend_syscall(uintptr_t thread, int type, ktrace_event_t event);
void extend_syscall(uint64_t thread, int type, ktrace_event_t event);
/* printing routines */
bool check_filter_mode(pid_t pid, th_info_t ti, unsigned long type, int error, int retval, char *sc_name);
void format_print(th_info_t ti, char *sc_name, ktrace_event_t event, unsigned long type, int format, uint64_t now, uint64_t stime, int waited, const char *pathname, struct diskio *dio);
bool check_filter_mode(pid_t pid, th_info_t ti, uint64_t type, int error, int retval, char *sc_name);
void format_print(th_info_t ti, char *sc_name, ktrace_event_t event, uint64_t type, int format, uint64_t now, uint64_t stime, int waited, const char *pathname, struct diskio *dio);
int print_open(ktrace_event_t event, uint64_t flags);
/* metadata info hash routines */
void meta_add_name(uint64_t blockno, const char *pathname);
@ -134,14 +137,14 @@ void meta_delete_all(void);
/* event ("thread info") routines */
void event_enter(int type, ktrace_event_t event);
void event_exit(char *sc_name, int type, ktrace_event_t event, int format);
th_info_t event_find(uintptr_t thread, int type);
th_info_t event_find(uint64_t thread, int type);
void event_delete(th_info_t ti_to_delete);
void event_delete_all(void);
void event_mark_thread_waited(uintptr_t);
void event_mark_thread_waited(uint64_t);
/* network fd set routines */
void fd_set_is_network(pid_t pid, unsigned long fd, bool set);
bool fd_is_network(pid_t pid, unsigned long fd);
void fd_set_is_network(pid_t pid, uint64_t fd, bool set);
bool fd_is_network(pid_t pid, uint64_t fd);
void fd_clear_pid(pid_t pid);
void fd_clear_all(void);
@ -150,17 +153,17 @@ void init_shared_cache_mapping(void);
void lookup_name(uint64_t user_addr, char **type, char **name);
/* disk I/O tracking routines */
struct diskio *diskio_start(unsigned long type, unsigned long bp, unsigned long dev, unsigned long blkno, unsigned long iosize, ktrace_event_t event);
struct diskio *diskio_find(unsigned long bp);
struct diskio *diskio_complete(unsigned long bp, unsigned long io_errno, unsigned long resid, uintptr_t thread, uint64_t curtime, struct timeval curtime_wall);
struct diskio *diskio_start(uint64_t type, uint64_t bp, uint64_t dev, uint64_t blkno, uint64_t iosize, ktrace_event_t event);
struct diskio *diskio_find(uint64_t bp);
struct diskio *diskio_complete(uint64_t bp, uint64_t io_errno, uint64_t resid, uint64_t thread, uint64_t curtime, struct timeval curtime_wall);
void diskio_print(struct diskio *dio);
void diskio_free(struct diskio *dio);
/* disk name routines */
#define NFS_DEV -1
#define CS_DEV -2
char *generate_cs_disk_name(unsigned long dev, char *s);
char *find_disk_name(unsigned long dev);
char *generate_cs_disk_name(uint64_t dev, char *s);
char *find_disk_name(uint64_t dev);
void cache_disk_names(void);
#define CLASS_MASK 0xff000000
@ -383,7 +386,11 @@ void cache_disk_names(void);
#define BSC_pwrite_nocancel 0x040c067c
#define BSC_aio_suspend_nocancel 0x40c0694
#define BSC_guarded_open_np 0x040c06e4
#define BSC_guarded_open_dprotected_np 0x040c0790
#define BSC_guarded_close_np 0x040c06e8
#define BSC_guarded_write_np 0x040c0794
#define BSC_guarded_pwrite_np 0x040c0798
#define BSC_guarded_writev_np 0x040c079c
#define BSC_fsgetpath 0x040c06ac
@ -406,6 +413,7 @@ void cache_disk_names(void);
#define BSC_msync_extended 0x040e0104
#define BSC_pread_extended 0x040e0264
#define BSC_pwrite_extended 0x040e0268
#define BSC_guarded_pwrite_extended 0x040e0798
#define BSC_mmap_extended 0x040e0314
#define BSC_mmap_extended2 0x040f0314
@ -458,6 +466,7 @@ void cache_disk_names(void);
#define FMT_OPENAT 45
#define FMT_RENAMEAT 46
#define FMT_IOCTL_SYNCCACHE 47
#define FMT_GUARDED_OPEN 48
#define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END)
@ -540,14 +549,18 @@ const struct bsd_syscall bsd_syscalls[MAX_BSD_SYSCALL] = {
NORMAL_SYSCALL(posix_spawn),
SYSCALL_WITH_NOCANCEL(open, FMT_OPEN),
SYSCALL(open_extended, FMT_OPEN),
SYSCALL(guarded_open_np, FMT_OPEN),
SYSCALL(guarded_open_np, FMT_GUARDED_OPEN),
SYSCALL_NAMED(open_dprotected_np, open_dprotected, FMT_OPEN),
SYSCALL(guarded_open_dprotected_np, FMT_GUARDED_OPEN),
SYSCALL(dup, FMT_FD_2),
SYSCALL(dup2, FMT_FD_2),
SYSCALL_WITH_NOCANCEL(close, FMT_FD),
SYSCALL(guarded_close_np, FMT_FD),
SYSCALL_WITH_NOCANCEL(read, FMT_FD_IO),
SYSCALL_WITH_NOCANCEL(write, FMT_FD_IO),
SYSCALL(guarded_write_np, FMT_FD_IO),
SYSCALL(guarded_pwrite_np, FMT_PREAD),
SYSCALL(guarded_writev_np, FMT_FD_IO),
SYSCALL(fgetxattr, FMT_FD),
SYSCALL(fsetxattr, FMT_FD),
SYSCALL(fremovexattr, FMT_FD),
@ -674,7 +687,7 @@ static uint64_t
mach_to_nano(uint64_t mach)
{
uint64_t nanoseconds = 0;
assert(ktrace_convert_timestamp_to_nanoseconds(s, mach, &nanoseconds) == 0);
os_assert(ktrace_convert_timestamp_to_nanoseconds(s, mach, &nanoseconds) == 0);
return nanoseconds;
}
@ -710,18 +723,35 @@ exit_usage(void)
exit(1);
}
static void fs_usage_cleanup(const char *message)
{
if (s){
ktrace_session_destroy(s);
}
fprintf(stderr, "Cleaning up tracing state because of %s\n", message);
}
int
main(int argc, char *argv[])
{
char ch;
int rv;
bool exclude_pids = false;
double time_limit = 0.0;
uint64_t time_limit_ns = 0;
os_set_crash_callback(&fs_usage_cleanup);
get_screenwidth();
s = ktrace_session_create();
assert(s);
os_assert(s != NULL);
(void)ktrace_ignore_process_filter_for_event(s, P_WrData);
(void)ktrace_ignore_process_filter_for_event(s, P_RdData);
(void)ktrace_ignore_process_filter_for_event(s, P_WrMeta);
(void)ktrace_ignore_process_filter_for_event(s, P_RdMeta);
(void)ktrace_ignore_process_filter_for_event(s, P_PgOut);
(void)ktrace_ignore_process_filter_for_event(s, P_PgIn);
while ((ch = getopt(argc, argv, "bewf:R:S:E:t:W")) != -1) {
switch (ch) {
@ -759,8 +789,12 @@ main(int argc, char *argv[])
break;
case 't':
time_limit = atof(optarg);
time_limit_ns = (uint64_t)(NSEC_PER_SEC * atof(optarg));
if (time_limit_ns == 0) {
fprintf(stderr, "ERROR: could not set time limit to %s\n",
optarg);
exit(1);
}
break;
case 'R':
@ -788,17 +822,9 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
if (time_limit > 0.0) {
if (RAW_flag) {
fprintf(stderr, "NOTE: time limit ignored when a raw file is specified\n");
} else {
stop_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(stop_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * time_limit), DISPATCH_TIME_FOREVER, 0);
dispatch_source_set_event_handler(stop_timer, ^{
ktrace_end(s, 0);
});
dispatch_resume(stop_timer);
}
if (time_limit_ns > 0 && RAW_flag) {
fprintf(stderr, "NOTE: time limit ignored when a raw file is specified\n");
time_limit_ns = 0;
}
if (!RAW_flag) {
@ -826,9 +852,9 @@ main(int argc, char *argv[])
ktrace_exclude_process(s, "csh");
ktrace_exclude_process(s, "sh");
ktrace_exclude_process(s, "zsh");
#if TARGET_OS_EMBEDDED
#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
ktrace_exclude_process(s, "dropbear");
#endif /* TARGET_OS_EMBEDDED */
#endif /* (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) */
}
}
@ -881,7 +907,7 @@ main(int argc, char *argv[])
fprintf(stderr, "ERROR: cannot both include and exclude simultaneously\n");
exit(1);
} else {
assert(!rv);
os_assert(!rv);
}
argc--;
@ -901,7 +927,7 @@ main(int argc, char *argv[])
if (!wideflag)
get_screenwidth();
});
dispatch_resume(sigwinch_source);
dispatch_activate(sigwinch_source);
init_shared_cache_mapping();
@ -922,6 +948,8 @@ main(int argc, char *argv[])
ktrace_set_default_event_names_enabled(KTRACE_FEATURE_DISABLED);
ktrace_set_execnames_enabled(s, KTRACE_FEATURE_LAZY);
ktrace_set_vnode_paths_enabled(s, true);
/* no need to symbolicate addresses */
ktrace_set_uuid_map_enabled(s, KTRACE_FEATURE_DISABLED);
rv = ktrace_start(s, dispatch_get_main_queue());
@ -930,6 +958,14 @@ main(int argc, char *argv[])
exit(1);
}
if (time_limit_ns > 0) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, time_limit_ns),
dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
^{
ktrace_end(s, 0);
});
}
dispatch_main();
return 0;
@ -1257,6 +1293,15 @@ setup_ktrace_callbacks(void)
});
}
static void
extend_syscall_rw(th_info_t ti, ktrace_event_t event)
{
ti->arg1 = event->arg1; /* the fd */
ti->arg2 = event->arg2; /* nbytes */
ti->arg3 = event->arg3; /* top half offset */
ti->arg4 = event->arg4; /* bottom half offset */
}
/*
* Handle system call extended trace data.
* pread and pwrite:
@ -1265,14 +1310,15 @@ setup_ktrace_callbacks(void)
* is all we really need.
*/
void
extend_syscall(uintptr_t thread, int type, ktrace_event_t event)
extend_syscall(uint64_t thread, int type, ktrace_event_t event)
{
th_info_t ti;
switch (type) {
case BSC_mmap_extended:
if ((ti = event_find(thread, BSC_mmap)) == NULL)
if ((ti = event_find(thread, BSC_mmap)) == NULL) {
return;
}
ti->arg8 = ti->arg3; /* save protection */
ti->arg1 = event->arg1; /* the fd */
@ -1292,8 +1338,9 @@ extend_syscall(uintptr_t thread, int type, ktrace_event_t event)
case BSC_msync_extended:
if ((ti = event_find(thread, BSC_msync)) == NULL) {
if ((ti = event_find(thread, BSC_msync_nocancel)) == NULL)
if ((ti = event_find(thread, BSC_msync_nocancel)) == NULL) {
return;
}
}
ti->arg4 = event->arg1; /* top half address */
@ -1302,26 +1349,30 @@ extend_syscall(uintptr_t thread, int type, ktrace_event_t event)
case BSC_pread_extended:
if ((ti = event_find(thread, BSC_pread)) == NULL) {
if ((ti = event_find(thread, BSC_pread_nocancel)) == NULL)
if ((ti = event_find(thread, BSC_pread_nocancel)) == NULL) {
return;
}
}
ti->arg1 = event->arg1; /* the fd */
ti->arg2 = event->arg2; /* nbytes */
ti->arg3 = event->arg3; /* top half offset */
ti->arg4 = event->arg4; /* bottom half offset */
extend_syscall_rw(ti, event);
break;
case BSC_pwrite_extended:
if ((ti = event_find(thread, BSC_pwrite)) == NULL) {
if ((ti = event_find(thread, BSC_pwrite_nocancel)) == NULL)
if ((ti = event_find(thread, BSC_pwrite_nocancel)) == NULL) {
return;
}
}
ti->arg1 = event->arg1; /* the fd */
ti->arg2 = event->arg2; /* nbytes */
ti->arg3 = event->arg3; /* top half offset */
ti->arg4 = event->arg4; /* bottom half offset */
extend_syscall_rw(ti, event);
break;
case BSC_guarded_pwrite_extended:
if ((ti = event_find(thread, BSC_guarded_pwrite_np)) == NULL) {
return;
}
extend_syscall_rw(ti, event);
break;
}
}
@ -1329,7 +1380,7 @@ extend_syscall(uintptr_t thread, int type, ktrace_event_t event)
#pragma mark printing routines
static void
get_mode_nibble(char *buf, unsigned long smode, unsigned long special, char x_on, char x_off)
get_mode_nibble(char *buf, uint64_t smode, uint64_t special, char x_on, char x_off)
{
if (smode & 04)
buf[0] = 'r';
@ -1349,7 +1400,7 @@ get_mode_nibble(char *buf, unsigned long smode, unsigned long special, char x_on
}
static void
get_mode_string(unsigned long mode, char *buf)
get_mode_string(uint64_t mode, char *buf)
{
memset(buf, '-', 9);
buf[9] = '\0';
@ -1396,11 +1447,11 @@ clip_64bit(char *s, uint64_t value)
* filters may be combined; default is all filters on (except cachehit)
*/
bool
check_filter_mode(pid_t pid, th_info_t ti, unsigned long type, int error, int retval, char *sc_name)
check_filter_mode(pid_t pid, th_info_t ti, uint64_t type, int error, int retval, char *sc_name)
{
bool ret = false;
int network_fd_isset = 0;
unsigned long fd;
uint64_t fd;
/* cachehit is special -- it's not on by default */
if (sc_name[0] == 'C' && !strcmp(sc_name, "CACHE_HIT")) {
@ -1550,6 +1601,41 @@ check_filter_mode(pid_t pid, th_info_t ti, unsigned long type, int error, int re
return ret;
}
int
print_open(ktrace_event_t event, uint64_t flags)
{
char mode[] = {
'_',
'_',
(flags & O_CREAT) ? 'C' : '_',
(flags & O_APPEND) ? 'A' : '_',
(flags & O_TRUNC) ? 'T' : '_',
(flags & O_EXCL) ? 'E' : '_',
(flags & O_NONBLOCK) ? 'N' : '_',
(flags & O_SHLOCK) ? 'l' : (flags & O_EXLOCK) ? 'L' : '_',
(flags & O_NOFOLLOW) ? 'F' : '_',
(flags & O_SYMLINK) ? 'S' : '_',
(flags & O_EVTONLY) ? 'V' : '_',
(flags & O_CLOEXEC) ? 'X' : '_',
'\0',
};
if (flags & O_RDWR) {
mode[0] = 'R';
mode[1] = 'W';
} else if (flags & O_WRONLY) {
mode[1] = 'W';
} else {
mode[0] = 'R';
}
if (event->arg1) {
return printf(" [%3d] (%s) ", (int)event->arg1, mode);
} else {
return printf(" F=%-3d (%s) ", (int)event->arg2, mode);
}
}
/*
* called from:
*
@ -1559,7 +1645,7 @@ check_filter_mode(pid_t pid, th_info_t ti, unsigned long type, int error, int re
*/
void
format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
unsigned long type, int format, uint64_t now, uint64_t stime,
uint64_t type, int format, uint64_t now, uint64_t stime,
int waited, const char *pathname, struct diskio *dio)
{
uint64_t secs, usecs;
@ -1570,16 +1656,16 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
int len = 0;
int clen = 0;
size_t tlen = 0;
unsigned long class;
uint64_t class;
uint64_t user_addr;
uint64_t user_size;
char *framework_name;
char *framework_type;
char *p1;
char *p2;
char buf[MAXWIDTH];
char buf[2 * PATH_MAX + 64];
char cs_diskname[32];
unsigned long threadid;
uint64_t threadid;
struct timeval now_walltime;
static char timestamp[32];
@ -1589,12 +1675,12 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
mach_time_of_first_event = now;
if (format == FMT_DISKIO || format == FMT_DISKIO_CS) {
assert(dio);
os_assert(dio);
} else {
assert(event);
os_assert(event);
if (format != FMT_UNMAP_INFO)
assert(ti);
os_assert(ti);
}
/* <rdar://problem/19852325> Filter out WindowServer/xcpm ioctls in fs_usage */
@ -1638,7 +1724,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
if (!command_name)
command_name = "";
assert(now_walltime.tv_sec || now_walltime.tv_usec);
os_assert(now_walltime.tv_sec || now_walltime.tv_usec);
/* try and reuse the timestamp string */
if (last_walltime_secs != now_walltime.tv_sec) {
@ -1713,7 +1799,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
if (event->arg1)
clen += printf(" F=%-3d[%3d]", (int)ti->arg1, (int)event->arg1);
else
clen += printf(" F=%-3d B=0x%-6lx", (int)ti->arg1, event->arg2);
clen += printf(" F=%-3d B=0x%-6" PRIx64, (int)ti->arg1, (uint64_t)event->arg2);
break;
@ -1741,7 +1827,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
/*
* pageout
*/
clen += printf(" B=0x%-8lx", event->arg1);
clen += printf(" B=0x%-8" PRIx64, (uint64_t)event->arg1);
break;
case FMT_HFS_update:
@ -1796,12 +1882,12 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
* physical disk I/O
*/
if (dio->io_errno) {
clen += printf(" D=0x%8.8lx [%3d]", dio->blkno, (int)dio->io_errno);
clen += printf(" D=0x%8.8" PRIx64 " [%3d]", dio->blkno, (int)dio->io_errno);
} else {
if (BC_flag)
clen += printf(" D=0x%8.8lx B=0x%-6lx BC:%s /dev/%s ", dio->blkno, dio->iosize, BC_STR(dio->bc_info), find_disk_name(dio->dev));
clen += printf(" D=0x%8.8" PRIx64 " B=0x%-6" PRIx64 " BC:%s /dev/%s ", dio->blkno, dio->iosize, BC_STR(dio->bc_info), find_disk_name(dio->dev));
else
clen += printf(" D=0x%8.8lx B=0x%-6lx /dev/%s ", dio->blkno, dio->iosize, find_disk_name(dio->dev));
clen += printf(" D=0x%8.8" PRIx64 " B=0x%-6" PRIx64 " /dev/%s ", dio->blkno, dio->iosize, find_disk_name(dio->dev));
if (dio->is_meta) {
if (!(type & P_DISKIO_READ)) {
@ -1824,9 +1910,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
* physical disk I/O
*/
if (dio->io_errno)
clen += printf(" D=0x%8.8lx [%3lu]", dio->blkno, dio->io_errno);
clen += printf(" D=0x%8.8" PRIx64 " [%3" PRIu64 "]", dio->blkno, dio->io_errno);
else
clen += printf(" D=0x%8.8lx B=0x%-6lx /dev/%s", dio->blkno, dio->iosize, generate_cs_disk_name(dio->dev, cs_diskname));
clen += printf(" D=0x%8.8" PRIx64 " B=0x%-6" PRIx64 " /dev/%s", dio->blkno, dio->iosize, generate_cs_disk_name(dio->dev, cs_diskname));
break;
@ -1964,6 +2050,34 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
p = "SETLKW";
break;
case F_SETLKWTIMEOUT:
p = "SETLKWTIMEOUT";
break;
case F_GETLKPID:
p = "GETLKPID";
break;
case F_OFD_GETLK:
p = "OFD_GETLK";
break;
case F_OFD_SETLK:
p = "OFD_SETLK";
break;
case F_OFD_SETLKW:
p = "OFD_SETLKW";
break;
case F_OFD_SETLKWTIMEOUT:
p = "OFD_SETLKWTIMEOUT";
break;
case F_OFD_GETLKPID:
p = "OFD_GETLKPID";
break;
case F_PREALLOCATE:
p = "PREALLOCATE";
break;
@ -2051,7 +2165,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
/*
* ioctl
*/
clen += printf(" <DKIOCSYNCHRONIZE> B=%lu /dev/%s", event->arg3, find_disk_name(event->arg1));
clen += printf(" <DKIOCSYNCHRONIZE> B=%" PRIu64 " /dev/%s", (uint64_t)event->arg3, find_disk_name(event->arg1));
break;
}
@ -2078,7 +2192,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
case FMT_UNMAP_INFO:
{
clen += printf(" D=0x%8.8lx B=0x%-6lx /dev/%s", event->arg2, event->arg3, find_disk_name(event->arg1));
clen += printf(" D=0x%8.8" PRIx64 " B=0x%-6" PRIx64 " /dev/%s", (uint64_t)event->arg2, (uint64_t)event->arg3, find_disk_name(event->arg1));
break;
}
@ -2105,7 +2219,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
clen += printf("[%3d] ", (int)event->arg1);
} else {
if (format == FMT_PREAD)
clen += printf(" B=0x%-8lx ", event->arg2);
clen += printf(" B=0x%-8" PRIx64 " ", (uint64_t)event->arg2);
else
clen += printf(" ");
}
@ -2267,7 +2381,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
/*
* fchmod, fchmod_extended, chmod, chmod_extended
*/
unsigned long mode;
uint64_t mode;
if (format == FMT_FCHMOD || format == FMT_FCHMOD_EXT) {
if (event->arg1)
@ -2328,9 +2442,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
case FMT_MOUNT:
{
if (event->arg1)
clen += printf(" [%3d] <FLGS=0x%lx> ", (int)event->arg1, ti->arg3);
clen += printf(" [%3d] <FLGS=0x%" PRIx64 "> ", (int)event->arg1, ti->arg3);
else
clen += printf(" <FLGS=0x%lx> ", ti->arg3);
clen += printf(" <FLGS=0x%" PRIx64 "> ", ti->arg3);
nopadding = 1;
break;
@ -2354,46 +2468,20 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
break;
}
case FMT_OPENAT:
case FMT_OPEN:
{
/*
* open
*/
char mode[7];
memset(mode, '_', 6);
mode[6] = '\0';
if (ti->arg2 & O_RDWR) {
mode[0] = 'R';
mode[1] = 'W';
} else if (ti->arg2 & O_WRONLY) {
mode[1] = 'W';
} else {
mode[0] = 'R';
}
if (ti->arg2 & O_CREAT)
mode[2] = 'C';
if (ti->arg2 & O_APPEND)
mode[3] = 'A';
if (ti->arg2 & O_TRUNC)
mode[4] = 'T';
if (ti->arg2 & O_EXCL)
mode[5] = 'E';
if (event->arg1)
clen += printf(" [%3d] (%s) ", (int)event->arg1, mode);
else
clen += printf(" F=%-3d (%s) ", (int)event->arg2, mode);
clen += print_open(event, ti->arg2);
nopadding = 1;
break;
case FMT_OPENAT:
clen += print_open(event, ti->arg3);
nopadding = 1;
break;
case FMT_GUARDED_OPEN:
clen += print_open(event, ti->arg4);
nopadding = 1;
break;
}
case FMT_SOCKET:
{
@ -2446,9 +2534,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
}
if (event->arg1)
clen += printf(" [%3d] <%s, %s, 0x%lx>", (int)event->arg1, domain, type, ti->arg3);
clen += printf(" [%3d] <%s, %s, 0x%" PRIx64 ">", (int)event->arg1, domain, type, ti->arg3);
else
clen += printf(" F=%-3d <%s, %s, 0x%lx>", (int)event->arg2, domain, type, ti->arg3);
clen += printf(" F=%-3d <%s, %s, 0x%" PRIx64 ">", (int)event->arg2, domain, type, ti->arg3);
break;
}
@ -2470,9 +2558,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
op = "UNKNOWN";
if (event->arg1)
clen += printf(" [%3d] P=0x%8.8lx <%s>", (int)event->arg1, ti->arg2, op);
clen += printf(" [%3d] P=0x%8.8" PRIx64 " <%s>", (int)event->arg1, ti->arg2, op);
else
clen += printf(" P=0x%8.8lx <%s>", ti->arg2, op);
clen += printf(" P=0x%8.8" PRIx64 " <%s>", ti->arg2, op);
break;
}
@ -2482,9 +2570,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
* aio_return [errno] AIOCBP IOSIZE
*/
if (event->arg1)
clen += printf(" [%3d] P=0x%8.8lx", (int)event->arg1, ti->arg1);
clen += printf(" [%3d] P=0x%8.8" PRIx64, (int)event->arg1, ti->arg1);
else
clen += printf(" P=0x%8.8lx B=0x%-8lx", ti->arg1, event->arg2);
clen += printf(" P=0x%8.8" PRIx64 " B=0x%-8" PRIx64, ti->arg1, (uint64_t)event->arg2);
break;
@ -2505,9 +2593,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
*/
if (ti->arg2) {
if (event->arg1)
clen += printf(" [%3d] P=0x%8.8lx", (int)event->arg1, ti->arg2);
clen += printf(" [%3d] P=0x%8." PRIx64, (int)event->arg1, ti->arg2);
else
clen += printf(" P=0x%8.8lx", ti->arg2);
clen += printf(" P=0x%8.8" PRIx64, ti->arg2);
} else {
if (event->arg1)
clen += printf(" F=%-3d[%3d]", (int)ti->arg1, (int)event->arg1);
@ -2522,9 +2610,9 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
* aio_error, aio_read, aio_write [errno] AIOCBP
*/
if (event->arg1)
clen += printf(" [%3d] P=0x%8.8lx", (int)event->arg1, ti->arg1);
clen += printf(" [%3d] P=0x%8.8" PRIx64, (int)event->arg1, ti->arg1);
else
clen += printf(" P=0x%8.8lx", ti->arg1);
clen += printf(" P=0x%8.8" PRIx64, ti->arg1);
break;
@ -2633,7 +2721,7 @@ format_print(th_info_t ti, char *sc_name, ktrace_event_t event,
p2 = " ";
if (columns > MAXCOLS || wideflag)
printf("%s%s %3llu.%06llu%s %s.%lu\n", p1, pathname, secs, usecs, p2, command_name, threadid);
printf("%s%s %3llu.%06llu%s %s.%" PRIu64 "\n", p1, pathname, secs, usecs, p2, command_name, threadid);
else
printf("%s%s %3llu.%06llu%s %-12.12s\n", p1, pathname, secs, usecs, p2, command_name);
@ -2721,7 +2809,7 @@ add_event(ktrace_event_t event, int type)
{
th_info_t ti;
int hashid;
unsigned long eventid;
uint64_t eventid;
if ((ti = th_info_freelist))
th_info_freelist = ti->next;
@ -2756,7 +2844,7 @@ add_event(ktrace_event_t event, int type)
}
th_info_t
event_find(uintptr_t thread, int type)
event_find(uint64_t thread, int type)
{
th_info_t ti;
int hashid;
@ -2851,7 +2939,7 @@ event_enter(int type, ktrace_event_t event)
found = true;
}
assert(found);
os_assert(found);
#endif /* DEBUG */
if ((ti = add_event(event, type)) == NULL)
@ -2894,7 +2982,7 @@ event_exit(char *sc_name, int type, ktrace_event_t event, int format)
}
void
event_mark_thread_waited(uintptr_t thread)
event_mark_thread_waited(uint64_t thread)
{
th_info_t ti;
int hashid;
@ -2924,7 +3012,7 @@ pfs_get(pid_t pid)
struct pid_fd_set *pfs;
int hashid;
assert(pid >= 0);
os_assert(pid >= 0);
hashid = pid & HASH_MASK;
@ -2997,12 +3085,14 @@ fd_clear_all(void)
}
void
fd_set_is_network(pid_t pid, unsigned long fd, bool set)
fd_set_is_network(pid_t pid, uint64_t fd, bool set)
{
struct pid_fd_set *pfs;
if (pid < 0)
return;
if (fd > OPEN_MAX)
return;
pfs = pfs_get(pid);
@ -3011,9 +3101,9 @@ fd_set_is_network(pid_t pid, unsigned long fd, bool set)
if (!set) return;
newsize = MAX((fd + CHAR_BIT) / CHAR_BIT, 2 * pfs->setsize);
newsize = MAX(((size_t)fd + CHAR_BIT) / CHAR_BIT, 2 * pfs->setsize);
pfs->set = reallocf(pfs->set, newsize);
assert(pfs->set);
os_assert(pfs->set != NULL);
bzero(pfs->set + pfs->setsize, newsize - pfs->setsize);
pfs->setsize = newsize;
@ -3026,7 +3116,7 @@ fd_set_is_network(pid_t pid, unsigned long fd, bool set)
}
bool
fd_is_network(pid_t pid, unsigned long fd)
fd_is_network(pid_t pid, uint64_t fd)
{
struct pid_fd_set *pfs;
@ -3321,8 +3411,8 @@ struct diskio *free_diskios = NULL;
struct diskio *busy_diskios = NULL;
struct diskio *
diskio_start(unsigned long type, unsigned long bp, unsigned long dev,
unsigned long blkno, unsigned long iosize, ktrace_event_t event)
diskio_start(uint64_t type, uint64_t bp, uint64_t dev,
uint64_t blkno, uint64_t iosize, ktrace_event_t event)
{
const char *command;
struct diskio *dio;
@ -3365,7 +3455,7 @@ diskio_start(unsigned long type, unsigned long bp, unsigned long dev,
}
struct diskio *
diskio_find(unsigned long bp)
diskio_find(uint64_t bp)
{
struct diskio *dio;
@ -3378,8 +3468,8 @@ diskio_find(unsigned long bp)
}
struct diskio *
diskio_complete(unsigned long bp, unsigned long io_errno, unsigned long resid,
uintptr_t thread, uint64_t curtime, struct timeval curtime_wall)
diskio_complete(uint64_t bp, uint64_t io_errno, uint64_t resid,
uint64_t thread, uint64_t curtime, struct timeval curtime_wall)
{
struct diskio *dio;
@ -3415,7 +3505,7 @@ diskio_print(struct diskio *dio)
{
char *p = NULL;
int len = 0;
unsigned long type;
uint64_t type;
int format = FMT_DISKIO;
char buf[64];
@ -3533,8 +3623,11 @@ diskio_print(struct diskio *dio)
buf[len] = 0;
if (check_filter_mode(-1, NULL, type, 0, 0, buf))
format_print(NULL, buf, NULL, type, format, dio->completed_time, dio->issued_time, 1, "", dio);
if (check_filter_mode(-1, NULL, type, 0, 0, buf)) {
const char *pathname = ktrace_get_path_for_vp(s, dio->vnodeid);
format_print(NULL, buf, NULL, type, format, dio->completed_time,
dio->issued_time, 1, pathname ? pathname : "", dio);
}
}
#pragma mark disk name routines
@ -3604,7 +3697,7 @@ recache_disk_names(void)
}
char *
find_disk_name(unsigned long dev)
find_disk_name(uint64_t dev)
{
struct diskrec *dnp;
int i;
@ -3627,12 +3720,12 @@ find_disk_name(unsigned long dev)
}
char *
generate_cs_disk_name(unsigned long dev, char *s)
generate_cs_disk_name(uint64_t dev, char *s)
{
if (dev == -1)
return "UNKNOWN";
sprintf(s, "disk%lus%lu", (dev >> 16) & 0xffff, dev & 0xffff);
sprintf(s, "disk%" PRIu64 "s%" PRIu64, (dev >> 16) & 0xffff, dev & 0xffff);
return (s);
}

1117
gcore.tproj/convert.c Normal file

File diff suppressed because it is too large Load Diff

24
gcore.tproj/convert.h Normal file
View File

@ -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 */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016 Apple Inc. All rights reserved.
* Copyright (c) 2016-2018 Apple Inc. All rights reserved.
*/
#include "options.h"
@ -18,6 +18,7 @@
#include <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;
/*
* A file reference allows different kinds of identifiers for
* the reference to be reconstructed.
*/
assert(info->external_pager);
assert(vmsize);
fr->vmsize = vmsize;
assert(fileoff >= 0);
fr->fileoff = fileoff;
fr->filesize = filesize;
if (!uuid_is_null(uuid)) {
uuid_copy(fc->id, uuid);
fc->flags = FREF_MAKE_FLAGS(kFREF_ID_UUID);
} else {
struct stat st;
if (-1 != stat(pathname, &st) && 0 != st.st_mtimespec.tv_sec) {
/* "little-endian format timespec structure" */
struct timespec ts = st.st_mtimespec;
ts.tv_nsec = 0; // allow touch(1) to fix things
memset(fc->id, 0, sizeof(fc->id));
memcpy(fc->id, &ts, sizeof(ts));
fc->flags = FREF_MAKE_FLAGS(kFREF_ID_MTIMESPEC_LE);
} else
fc->flags = FREF_MAKE_FLAGS(kFREF_ID_NONE);
}
assert(maxprot & VM_PROT_READ);
fr->maxprot = maxprot;
fr->initprot = initprot;
return fr;
fc->vmaddr = V_ADDR(vr);
assert(V_SIZE(vr));
fc->vmsize = V_SIZE(vr);
assert(F_OFF(fr) >= 0);
fc->fileoff = F_OFF(fr);
fc->filesize = F_SIZE(fr);
assert(info->max_protection & VM_PROT_READ);
fc->maxprot = info->max_protection;
fc->prot = info->protection;
fc->share_mode = info->share_mode;
assert(purgable <= UINT8_MAX);
fc->purgable = (uint8_t)purgable;
assert(info->user_tag <= UINT8_MAX);
fc->tag = (uint8_t)info->user_tag;
fc->extp = info->external_pager;
return fc;
}
/*
@ -176,23 +239,26 @@ static walk_return_t
write_fileref_subregion(const struct region *r, const struct subregion *s, struct write_segment_data *wsd)
{
assert(S_LIBENT(s));
if (opt->debug && !issubregiontype(s, SEG_TEXT) && !issubregiontype(s, SEG_LINKEDIT))
if (OPTIONS_DEBUG(opt, 1) && !issubregiontype(s, SEG_TEXT) && !issubregiontype(s, SEG_LINKEDIT))
printf("%s: unusual segment type %s from %s\n", __func__, S_MACHO_TYPE(s), S_FILENAME(s));
assert((r->r_info.max_protection & VM_PROT_READ) == VM_PROT_READ);
assert((r->r_info.protection & VM_PROT_WRITE) == 0);
const struct libent *le = S_LIBENT(s);
const struct proto_fileref_command *fc = make_fileref_command(wsd->wsd_lc, le, S_ADDR(s), S_SIZE(s), S_MACHO_FILEOFF(s), S_SIZE(s), r->r_info.max_protection, r->r_info.protection);
const struct file_range fr = {
.off = S_MACHO_FILEOFF(s),
.size = S_SIZE(s),
};
const struct proto_fileref_command *fc = make_fileref_command(wsd->wsd_lc, le->le_pathname, le->le_uuid, S_RANGE(s), &fr, &r->r_info, r->r_purgable);
commit_load_command(wsd, (const void *)fc);
if (opt->debug > 1) {
if (OPTIONS_DEBUG(opt, 3)) {
hsize_str_t hstr;
printr(r, "ref '%s' %s (vm %llx-%llx, file offset %lld for %s)\n", S_FILENAME(s), S_MACHO_TYPE(s), (uint64_t)fc->vmaddr, (uint64_t)fc->vmaddr + fc->vmsize, (int64_t)fc->fileoff, str_hsize(hstr, fc->filesize));
}
return WALK_CONTINUE;
}
#ifdef CONFIG_REFSC
/*
* Note that we may be asked to write reference segments whose protections
* are rw- -- this -should- be ok as we don't convert the region to a file
@ -206,12 +272,18 @@ write_fileref_region(const struct region *r, struct write_segment_data *wsd)
assert((r->r_info.max_protection & VM_PROT_READ) == VM_PROT_READ);
assert(!r->r_inzfodregion);
const struct libent *le = r->r_fileref->fr_libent;
const struct proto_fileref_command *fc = make_fileref_command(wsd->wsd_lc, le, R_ADDR(r), R_SIZE(r), r->r_fileref->fr_offset, (size_t)R_SIZE(r), r->r_info.max_protection, r->r_info.protection);
const struct libent *le = r->r_fileref->fr_libent;
const char *pathname = r->r_fileref->fr_pathname;
const struct file_range fr = {
.off = r->r_fileref->fr_offset,
.size = R_SIZE(r),
};
const struct proto_fileref_command *fc = make_fileref_command(wsd->wsd_lc, pathname, le ? le->le_uuid : UUID_NULL, R_RANGE(r), &fr, &r->r_info, r->r_purgable);
commit_load_command(wsd, (const void *)fc);
if (opt->debug > 1) {
if (OPTIONS_DEBUG(opt, 3)) {
hsize_str_t hstr;
printr(r, "ref '%s' %s (vm %llx-%llx, file offset %lld for %s)\n", le->le_filename, "(type?)", (uint64_t)fc->vmaddr, (uint64_t)fc->vmaddr + fc->vmsize, (int64_t)fc->fileoff, str_hsize(hstr, fc->filesize));
printr(r, "ref '%s' %s (vm %llx-%llx, file offset %lld for %s)\n", pathname, "(type?)", (uint64_t)fc->vmaddr, (uint64_t)fc->vmaddr + fc->vmsize, (int64_t)fc->fileoff, str_hsize(hstr, fc->filesize));
}
return WALK_CONTINUE;
}
@ -222,7 +294,6 @@ const struct regionop fileref_ops = {
del_fileref_region,
};
#endif /* CONFIG_REFSC */
#pragma mark -- ZFOD segments written only to the header --
@ -231,7 +302,7 @@ size_zfod_region(const struct region *r, struct size_core *sc)
{
assert(0 == r->r_nsubregions);
assert(r->r_inzfodregion);
sc->headersize += sizeof (native_segment_command_t);
sc->headersize += sizeof_segment_command();
sc->count++;
sc->memsize += R_SIZE(r);
}
@ -242,8 +313,12 @@ write_zfod_region(const struct region *r, struct write_segment_data *wsd)
assert(r->r_info.user_tag != VM_MEMORY_IOKIT);
assert((r->r_info.max_protection & VM_PROT_READ) == VM_PROT_READ);
const void *sc = make_native_segment_command(wsd->wsd_lc, R_ADDR(r), R_SIZE(r), wsd->wsd_foffset, 0, r->r_info.max_protection, r->r_info.protection, 0);
commit_load_command(wsd, sc);
const struct file_range fr = {
.off = wsd->wsd_foffset,
.size = 0,
};
make_segment_command(wsd->wsd_lc, R_RANGE(r), &fr, &r->r_info, 0, VM_PURGABLE_EMPTY);
commit_load_command(wsd, wsd->wsd_lc);
return WALK_CONTINUE;
}
@ -256,35 +331,25 @@ const struct regionop zfod_ops = {
#pragma mark -- Regions containing data --
static walk_return_t
pwrite_memory(struct write_segment_data *wsd, const void *addr, size_t size, mach_vm_offset_t memaddr, size_t memsize)
pwrite_memory(struct write_segment_data *wsd, const void *addr, size_t size, const struct vm_range *vr)
{
assert(size);
int error = 0;
ssize_t nwritten = 0;
ssize_t nwritten;
const int error = bounded_pwrite(wsd->wsd_fd, addr, size, wsd->wsd_foffset, &wsd->wsd_nocache, &nwritten);
if (opt->sizebound > 0 &&
wsd->wsd_foffset + (off_t)size > opt->sizebound) {
error = EFBIG;
} else {
nwritten = pwrite(wsd->wsd_fd, addr, size, wsd->wsd_foffset);
if (nwritten < 0)
error = errno;
}
if (error || opt->debug > 1) {
if (error || OPTIONS_DEBUG(opt, 3)) {
hsize_str_t hsz;
printf("%llx-%llx writing %ld bytes at offset %lld -> ",
memaddr, memaddr+memsize, size, wsd->wsd_foffset);
printvr(vr, "writing %ld bytes at offset %lld -> ", size, wsd->wsd_foffset);
if (error)
printf("err #%d - %s ", error, strerror(error));
else {
printf("%s ", str_hsize(hsz, nwritten));
if (size != (size_t)nwritten)
printf("[%zd - incomplete write!] ", nwritten);
else if (size != memsize)
else if (size != V_SIZE(vr))
printf("(%s in memory) ",
str_hsize(hsz, memsize));
str_hsize(hsz, V_SIZE(vr)));
}
printf("\n");
}
@ -318,16 +383,16 @@ segment_compflags(compression_algorithm ca, unsigned *algnum)
{
switch (ca) {
case COMPRESSION_LZ4:
*algnum = proto_SG_COMP_LZ4;
*algnum = kCOMP_LZ4;
break;
case COMPRESSION_ZLIB:
*algnum = proto_SG_COMP_ZLIB;
*algnum = kCOMP_ZLIB;
break;
case COMPRESSION_LZMA:
*algnum = proto_SG_COMP_LZMA;
*algnum = kCOMP_LZMA;
break;
case COMPRESSION_LZFSE:
*algnum = proto_SG_COMP_LZFSE;
*algnum = kCOMP_LZFSE;
break;
default:
err(EX_SOFTWARE, "unsupported compression algorithm %x", ca);
@ -335,6 +400,190 @@ segment_compflags(compression_algorithm ca, unsigned *algnum)
return 0;
}
static bool
is_file_mapped_shared(const struct region *r)
{
if (r->r_info.external_pager)
switch (r->r_info.share_mode) {
case SM_TRUESHARED: // sm=shm
case SM_SHARED: // sm=ali
case SM_SHARED_ALIASED: // sm=s/a
return true;
default:
break;
}
return false;
}
static walk_return_t
map_memory_range(struct write_segment_data *wsd, const struct region *r, const struct vm_range *vr, struct vm_range *dp)
{
if (r->r_incommregion) {
/*
* Special case: for commpage access, copy from our own address space.
*/
V_SETADDR(dp, 0);
V_SETSIZE(dp, V_SIZE(vr));
kern_return_t kr = mach_vm_allocate(mach_task_self(), &dp->addr, dp->size, VM_FLAGS_ANYWHERE);
if (KERN_SUCCESS != kr || 0 == dp->addr) {
err_mach(kr, r, "mach_vm_allocate c %llx-%llx", V_ADDR(vr), V_ENDADDR(vr));
print_one_memory_region(r);
return WALK_ERROR;
}
if (OPTIONS_DEBUG(opt, 3))
printr(r, "copying from self %llx-%llx\n", V_ADDR(vr), V_ENDADDR(vr));
memcpy((void *)dp->addr, (const void *)V_ADDR(vr), V_SIZE(vr));
return WALK_CONTINUE;
}
if (!r->r_insharedregion && 0 == (r->r_info.protection & VM_PROT_READ)) {
assert(0 != (r->r_info.max_protection & VM_PROT_READ)); // simple_region_optimization()
/*
* Special case: region that doesn't currently have read permission.
* (e.g. --x/r-x permissions with tag 64 - JS JIT generated code
* from com.apple.WebKit.WebContent)
*/
const mach_vm_offset_t pagesize_host = 1u << pageshift_host;
if (OPTIONS_DEBUG(opt, 3))
printr(r, "unreadable (%s/%s), remap with read permission\n",
str_prot(r->r_info.protection), str_prot(r->r_info.max_protection));
V_SETADDR(dp, 0);
V_SETSIZE(dp, V_SIZE(vr));
vm_prot_t cprot, mprot;
kern_return_t kr = mach_vm_remap(mach_task_self(), &dp->addr, V_SIZE(dp), pagesize_host - 1, true, wsd->wsd_task, V_ADDR(vr), true, &cprot, &mprot, VM_INHERIT_NONE);
if (KERN_SUCCESS != kr) {
err_mach(kr, r, "mach_vm_remap() %llx-%llx", V_ADDR(vr), V_ENDADDR(vr));
return WALK_ERROR;
}
assert(r->r_info.protection == cprot && r->r_info.max_protection == mprot);
kr = mach_vm_protect(mach_task_self(), V_ADDR(dp), V_SIZE(dp), false, VM_PROT_READ);
if (KERN_SUCCESS != kr) {
err_mach(kr, r, "mach_vm_protect() %llx-%llx", V_ADDR(vr), V_ENDADDR(vr));
mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp));
return WALK_ERROR;
}
return WALK_CONTINUE;
}
/*
* Most segments with data are read here
*/
vm_offset_t data32 = 0;
mach_msg_type_number_t data32_count;
kern_return_t kr = mach_vm_read(wsd->wsd_task, V_ADDR(vr), V_SIZE(vr), &data32, &data32_count);
switch (kr) {
case KERN_SUCCESS:
V_SETADDR(dp, data32);
V_SETSIZE(dp, data32_count);
break;
case KERN_INVALID_ADDRESS:
if (!r->r_insharedregion &&
(VM_MEMORY_SKYWALK == r->r_info.user_tag || is_file_mapped_shared(r))) {
if (OPTIONS_DEBUG(opt, 1)) {
/* not necessarily an error: mitigation below */
tag_str_t tstr;
printr(r, "mach_vm_read() failed (%s) -- substituting zeroed region\n", str_tagr(tstr, r));
if (OPTIONS_DEBUG(opt, 2))
print_one_memory_region(r);
}
V_SETSIZE(dp, V_SIZE(vr));
kr = mach_vm_allocate(mach_task_self(), &dp->addr, V_SIZE(dp), VM_FLAGS_ANYWHERE);
if (KERN_SUCCESS != kr || 0 == V_ADDR(dp))
err_mach(kr, r, "mach_vm_allocate() z %llx-%llx", V_ADDR(vr), V_ENDADDR(vr));
break;
}
/*FALLTHROUGH*/
default:
err_mach(kr, r, "mach_vm_read() %llx-%llx", V_ADDR(vr), V_SIZE(vr));
if (OPTIONS_DEBUG(opt, 1))
print_one_memory_region(r);
break;
}
if (kr != KERN_SUCCESS) {
V_SETADDR(dp, 0);
return WALK_ERROR;
}
/*
* Sometimes (e.g. searchd) we may not be able to fetch all the pages
* from the underlying mapped file, in which case replace those pages
* with zfod pages (at least they compress efficiently) rather than
* taking a SIGBUS when compressing them.
*
* XXX Perhaps we should just catch the SIGBUS, and if the faulting address
* is in the right range, substitute zfod pages and rerun region compression?
* Complex though, because the compression code may be multithreaded.
*/
if (!r->r_insharedregion && is_file_mapped_shared(r)) {
const mach_vm_offset_t pagesize_host = 1u << pageshift_host;
if (r->r_info.pages_resident * pagesize_host == V_SIZE(dp))
return WALK_CONTINUE; // all pages resident, so skip ..
if (OPTIONS_DEBUG(opt, 2))
printr(r, "probing %llu pages in mapped-shared file\n", V_SIZE(dp) / pagesize_host);
kr = KERN_SUCCESS;
for (mach_vm_offset_t a = V_ADDR(dp); a < V_ENDADDR(dp); a += pagesize_host) {
mach_msg_type_number_t pCount = VM_PAGE_INFO_BASIC_COUNT;
vm_page_info_basic_data_t pInfo;
kr = mach_vm_page_info(mach_task_self(), a, VM_PAGE_INFO_BASIC, (vm_page_info_t)&pInfo, &pCount);
if (KERN_SUCCESS != kr) {
err_mach(kr, NULL, "mach_vm_page_info() at %llx", a);
break;
}
/* If the VM has the page somewhere, assume we can bring it back */
if (pInfo.disposition & (VM_PAGE_QUERY_PAGE_PRESENT | VM_PAGE_QUERY_PAGE_REF | VM_PAGE_QUERY_PAGE_DIRTY))
continue;
/* Force the page to be fetched to see if it faults */
mach_vm_size_t tsize = pagesize_host;
void *tmp = valloc((size_t)tsize);
const mach_vm_address_t vtmp = (mach_vm_address_t)tmp;
switch (kr = mach_vm_read_overwrite(mach_task_self(), a, tsize, vtmp, &tsize)) {
case KERN_SUCCESS:
break;
case KERN_INVALID_ADDRESS: {
/* Content can't be found: replace it and the rest of the region with zero-fill pages */
if (OPTIONS_DEBUG(opt, 2)) {
printr(r, "mach_vm_read_overwrite() failed after %llu pages -- substituting zfod\n", (a - V_ADDR(dp)) / pagesize_host);
print_one_memory_region(r);
}
mach_vm_address_t va = a;
kr = mach_vm_allocate(mach_task_self(), &va, V_ENDADDR(dp) - va, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE);
if (KERN_SUCCESS != kr) {
err_mach(kr, r, "mach_vm_allocate() %llx", a);
} else {
assert(a == va);
a = V_ENDADDR(dp); // no need to look any further
}
break;
}
default:
err_mach(kr, r, "mach_vm_overwrite() %llx", a);
break;
}
free(tmp);
if (KERN_SUCCESS != kr)
break;
}
if (KERN_SUCCESS != kr) {
kr = mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp));
if (KERN_SUCCESS != kr && OPTIONS_DEBUG(opt, 1))
err_mach(kr, r, "mach_vm_deallocate() pre %llx-%llx", V_ADDR(dp), V_ENDADDR(dp));
V_SETADDR(dp, 0);
return WALK_ERROR;
}
}
return WALK_CONTINUE;
}
static walk_return_t
write_memory_range(struct write_segment_data *wsd, const struct region *r, mach_vm_offset_t vmaddr, mach_vm_offset_t vmsize)
{
@ -344,10 +593,6 @@ write_memory_range(struct write_segment_data *wsd, const struct region *r, mach_
walk_return_t step = WALK_CONTINUE;
do {
unsigned algorithm = 0;
void *dstbuf = NULL;
size_t filesize;
vmsize = resid;
/*
@ -360,93 +605,72 @@ write_memory_range(struct write_segment_data *wsd, const struct region *r, mach_
vmsize = opt->chunksize;
assert(vmsize <= INT32_MAX);
mach_vm_offset_t data;
mach_vm_offset_t data_count;
const struct vm_range vr = {
.addr = vmaddr,
.size = vmsize,
};
struct vm_range d, *dp = &d;
kern_return_t kr;
const void *srcaddr;
step = map_memory_range(wsd, r, &vr, dp);
if (WALK_CONTINUE != step)
break;
assert(0 != V_ADDR(dp) && 0 != V_SIZE(dp));
const void *srcaddr = (const void *)V_ADDR(dp);
if (r->r_incommregion) {
/*
* For commpage access, we just copy from our own address space.
*/
data = 0;
data_count = vmsize;
kr = mach_vm_allocate(mach_task_self(), &data, data_count, VM_FLAGS_ANYWHERE);
if (KERN_SUCCESS != kr || data == 0) {
err_mach(kr, r, "subregion %llx-%llx, mach_vm_allocate()", vmaddr, vmaddr + vmsize);
if (opt->debug) {
print_memory_region_header();
ROP_PRINT(r);
}
break;
}
if (opt->debug)
printr(r, "subregion %llx-%llx, copying from self\n", vmaddr, vmaddr+vmsize);
srcaddr = (const void *)memcpy((void *)data, (void *)vmaddr, vmsize);
} else {
/*
* Most segments with data are mapped here
*/
vm_offset_t data32 = 0;
mach_msg_type_number_t data32_count;
kr = mach_vm_read(wsd->wsd_task, vmaddr, vmsize, &data32, &data32_count);
if (KERN_SUCCESS != kr || data32 == 0 || data32_count < vmsize) {
err_mach(kr, r, "subregion %llx-%llx, mach_vm_read()", vmaddr, vmaddr + vmsize);
if (opt->debug) {
print_memory_region_header();
ROP_PRINT(r);
}
break;
}
data = data32;
data_count = data32_count;
mach_vm_behavior_set(mach_task_self(), data, data_count, VM_BEHAVIOR_SEQUENTIAL);
srcaddr = (const void *)data;
}
mach_vm_behavior_set(mach_task_self(), V_ADDR(dp), V_SIZE(dp), VM_BEHAVIOR_SEQUENTIAL);
assert(vmsize);
void *dstbuf = NULL;
unsigned algorithm = 0;
size_t filesize;
if (opt->compress) {
dstbuf = malloc((size_t)vmsize);
if (dstbuf) {
filesize = compression_encode_buffer(dstbuf, (size_t)vmsize, srcaddr, (size_t)vmsize, NULL, opt->calgorithm);
if (filesize > 0 && filesize < vmsize) {
srcaddr = dstbuf;
if (segment_compflags(opt->calgorithm, &algorithm) != 0) {
free(dstbuf);
mach_vm_deallocate(mach_task_self(), data, data_count);
return WALK_ERROR;
}
} else {
free(dstbuf);
dstbuf = NULL;
filesize = (size_t)vmsize;
}
} else
filesize = (size_t)vmsize;
} else
filesize = (size_t)vmsize;
if (opt->extended) {
dstbuf = malloc(V_SIZEOF(dp));
if (dstbuf) {
filesize = compression_encode_buffer(dstbuf, V_SIZEOF(dp), srcaddr, V_SIZEOF(dp), NULL, opt->calgorithm);
if (filesize > 0 && filesize < V_SIZEOF(dp)) {
srcaddr = dstbuf; /* the data source is now heap, compressed */
mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp));
V_SETADDR(dp, 0);
if (segment_compflags(opt->calgorithm, &algorithm) != 0) {
free(dstbuf);
mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp));
V_SETADDR(dp, 0);
step = WALK_ERROR;
break;
}
} else {
free(dstbuf);
dstbuf = NULL;
filesize = V_SIZEOF(dp);
}
} else
filesize = V_SIZEOF(dp);
assert(filesize <= V_SIZEOF(dp));
} else
filesize = V_SIZEOF(dp);
assert(filesize);
native_segment_command_t *sc = make_native_segment_command(wsd->wsd_lc, vmaddr, vmsize, wsd->wsd_foffset, filesize, r->r_info.max_protection, r->r_info.protection, algorithm);
const struct file_range fr = {
.off = wsd->wsd_foffset,
.size = filesize,
};
make_segment_command(wsd->wsd_lc, &vr, &fr, &r->r_info, algorithm, r->r_purgable);
step = pwrite_memory(wsd, srcaddr, filesize, &vr);
if (dstbuf)
free(dstbuf);
if (V_ADDR(dp)) {
kern_return_t kr = mach_vm_deallocate(mach_task_self(), V_ADDR(dp), V_SIZE(dp));
if (KERN_SUCCESS != kr && OPTIONS_DEBUG(opt, 1))
err_mach(kr, r, "mach_vm_deallocate() post %llx-%llx", V_ADDR(dp), V_SIZE(dp));
}
assert((sc->flags == 0) ^ (sc->filesize < sc->vmsize));
step = pwrite_memory(wsd, srcaddr, sc->filesize, vmaddr, sc->vmsize);
if (dstbuf)
free(dstbuf);
mach_vm_deallocate(mach_task_self(), data, data_count);
if (WALK_ERROR == step)
break;
commit_load_command(wsd, (const void *)sc);
resid -= vmsize;
vmaddr += vmsize;
} while (resid);
if (WALK_ERROR == step)
break;
commit_load_command(wsd, wsd->wsd_lc);
resid -= vmsize;
vmaddr += vmsize;
} while (resid);
return step;
}
@ -464,7 +688,7 @@ getvmsize_host(const task_t task, const struct region *r)
if (pageshift_host != pageshift_app) {
is_actual_size(task, r, &vmsize_host);
if (opt->debug && R_SIZE(r) != vmsize_host)
if (OPTIONS_DEBUG(opt, 1) && R_SIZE(r) != vmsize_host)
printr(r, "(region size tweak: was %llx, is %llx)\n", R_SIZE(r), vmsize_host);
}
return vmsize_host;
@ -482,9 +706,7 @@ write_sparse_region(const struct region *r, struct write_segment_data *wsd)
{
assert(r->r_nsubregions);
assert(!r->r_inzfodregion);
#ifdef CONFIG_REFSC
assert(NULL == r->r_fileref);
#endif
const mach_vm_size_t vmsize_host = getvmsize_host(wsd->wsd_task, r);
walk_return_t step = WALK_CONTINUE;
@ -492,7 +714,7 @@ write_sparse_region(const struct region *r, struct write_segment_data *wsd)
for (unsigned i = 0; i < r->r_nsubregions; i++) {
const struct subregion *s = r->r_subregions[i];
if (s->s_isfileref)
if (s->s_isuuidref)
step = write_fileref_subregion(r, s, wsd);
else {
/* Write this one out as real data */
@ -500,7 +722,7 @@ write_sparse_region(const struct region *r, struct write_segment_data *wsd)
if (R_SIZE(r) != vmsize_host) {
if (S_ADDR(s) + vmsize > R_ADDR(r) + vmsize_host) {
vmsize = R_ADDR(r) + vmsize_host - S_ADDR(s);
if (opt->debug)
if (OPTIONS_DEBUG(opt, 3))
printr(r, "(subregion size tweak: was %llx, is %llx)\n",
S_SIZE(s), vmsize);
}
@ -518,9 +740,7 @@ write_vanilla_region(const struct region *r, struct write_segment_data *wsd)
{
assert(0 == r->r_nsubregions);
assert(!r->r_inzfodregion);
#ifdef CONFIG_REFSC
assert(NULL == r->r_fileref);
#endif
const mach_vm_size_t vmsize_host = getvmsize_host(wsd->wsd_task, r);
return write_memory_range(wsd, r, R_ADDR(r), vmsize_host);
@ -542,7 +762,7 @@ static unsigned long
count_memory_range(mach_vm_offset_t vmsize)
{
unsigned long count;
if (opt->compress && opt->chunksize > 0) {
if (opt->chunksize) {
count = (size_t)vmsize / opt->chunksize;
if (vmsize != (mach_vm_offset_t)count * opt->chunksize)
count++;
@ -559,7 +779,7 @@ static void
size_sparse_subregion(const struct subregion *s, struct size_core *sc)
{
const unsigned long count = count_memory_range(S_SIZE(s));
sc->headersize += sizeof (native_segment_command_t) * count;
sc->headersize += sizeof_segment_command() * count;
sc->count += count;
sc->memsize += S_SIZE(s);
}
@ -572,12 +792,12 @@ size_sparse_region(const struct region *r, struct size_core *sc_sparse, struct s
unsigned long entry_total = sc_sparse->count + sc_fileref->count;
for (unsigned i = 0; i < r->r_nsubregions; i++) {
const struct subregion *s = r->r_subregions[i];
if (s->s_isfileref)
if (s->s_isuuidref)
size_fileref_subregion(s, sc_fileref);
else
size_sparse_subregion(s, sc_sparse);
}
if (opt->debug) {
if (OPTIONS_DEBUG(opt, 3)) {
/* caused by compression breaking a large region into chunks */
entry_total = (sc_fileref->count + sc_sparse->count) - entry_total;
if (entry_total > r->r_nsubregions)
@ -598,11 +818,11 @@ size_vanilla_region(const struct region *r, struct size_core *sc)
assert(0 == r->r_nsubregions);
const unsigned long count = count_memory_range(R_SIZE(r));
sc->headersize += sizeof (native_segment_command_t) * count;
sc->headersize += sizeof_segment_command() * count;
sc->count += count;
sc->memsize += R_SIZE(r);
if (opt->debug && count > 1)
if (OPTIONS_DEBUG(opt, 3) && count != 1)
printr(r, "range with 1 region, but requires %lu segment commands\n", count);
}
@ -619,10 +839,8 @@ region_size_memory(struct region *r, void *arg)
if (&zfod_ops == r->r_op)
size_zfod_region(r, &ssd->ssd_zfod);
#ifdef CONFIG_REFSC
else if (&fileref_ops == r->r_op)
size_fileref_region(r, &ssd->ssd_fileref);
#endif
else if (&sparse_ops == r->r_op)
size_sparse_region(r, &ssd->ssd_sparse, &ssd->ssd_fileref);
else if (&vanilla_ops == r->r_op)

View 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;
};

View 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;
}

View 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 */

View File

@ -24,6 +24,7 @@ static const size_t dyld_cache_header_size = sizeof (struct copied_dyld_cache_he
/*
* The shared cache must both contain the magic ID and
* match the uuid we discovered via dyld's information.
* Assumes that the dyld_cache_header grows in a binary compatible fashion.
*/
bool
get_uuid_from_shared_cache_mapping(const void *addr, size_t size, uuid_t out)
@ -31,7 +32,7 @@ get_uuid_from_shared_cache_mapping(const void *addr, size_t size, uuid_t out)
const struct copied_dyld_cache_header *ch = addr;
if (size < sizeof (*ch))
return false;
static const char prefix[] = "dyld_v1 ";
static const char prefix[] = "dyld_v";
if (strncmp(ch->magic, prefix, strlen(prefix)) != 0)
return false;
uuid_copy(out, ch->uuid);
@ -73,14 +74,14 @@ shared_cache_filename(const uuid_t uu)
int d = open(fe->fts_accpath, O_RDONLY);
if (-1 == d) {
if (opt->debug)
if (OPTIONS_DEBUG(opt, 1))
printf("%s: cannot open - %s\n", fe->fts_accpath, strerror(errno));
continue;
}
void *addr = mmap(NULL, dyld_cache_header_size, PROT_READ, MAP_PRIVATE, d, 0);
close(d);
if ((void *)-1 == addr) {
if (opt->debug)
if (OPTIONS_DEBUG(opt, 1))
printf("%s: cannot mmap - %s\n", fe->fts_accpath, strerror(errno));
continue;
}
@ -89,7 +90,7 @@ shared_cache_filename(const uuid_t uu)
if (get_uuid_from_shared_cache_mapping(addr, dyld_cache_header_size, scuuid)) {
if (uuid_compare(uu, scuuid) == 0)
nm = strdup(fe->fts_accpath);
else if (opt->debug) {
else if (OPTIONS_DEBUG(opt, 3)) {
uuid_string_t scstr;
uuid_unparse_lower(scuuid, scstr);
printf("%s: shared cache mismatch (%s)\n", fe->fts_accpath, scstr);

View File

@ -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.security.cs.debugger.root</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,201 @@
.Dd 9/29/16
.Dt gcore-internal 1
.Os Darwin
.Sh NAME
.Nm gcore
.Nd get core images of running processes and corpses
.Sh SYNOPSIS
.Nm
.Op Fl x
.Op Fl F
.Op Fl C
.Op Fl Z Ar compopts
.Op Fl t Ar threshold
.Op Fl d
.Ar args ...
.Nm
.Sy conv
.Op Fl L Ar searchpath
.Op Fl z
.Op Fl s
.Op Fl v
.Op Fl d
.Ar incore outcore
.Nm
.Sy map
.Ar corefile
.Nm
.Sy fref
.Ar corefile
.Sh DESCRIPTION
For an introduction to this command and its options, see
.Xr gcore 1 .
This page describes various experimental capabilities
of the
.Nm
command intended, for the moment, for internal use only.
.Pp
The following set of additional flags are available:
.Bl -tag -width Fl
.It Fl x
Create extended (compact) core files. With this flag,
.Nm
elides empty and unmapped regions from the dump, and uses
metadata from the VM system and
.Xr dyld 1
to minimize the size of the dump, writing compressed versions of
the active regions of the address space into the dump file.
.Nm
also records file references to the various files mapped into the
address space, potentially including the shared cache, to
avoid duplicating content already present on the filesystem.
Taken together, these techniques can lead to very significant
space savings for the core file, particularly for smaller programs.
.It Fl F
Normally when
.Fl x
is specified,
.Nm
makes conservative assumptions about which files should be
incorporated into the dump as file references so that the
full core file can be recreated later. This flag attempts to make
.Em every
mapped file into a file reference. While this can occasionally
be useful for applications that map many files into their address space,
it may be
.Em extremely
difficult to recreate the process image as a result.
Use cautiously!
.El
.Pp
The remaining options are more relevant to the
.Nm
maintainers:
.Bl -tag -width Fl
.It Fl C
Forcibly generate a corpse for the process, even if the process is suspended.
.It Fl Z Ar compopts
Specify compression options e.g. algorithm and chunksize.
.It Fl t Ar threshold
Set the threshold at which I/O caching is disabled to
.Ar threshold
KiBytes.
.It Fl d
Enable debugging of the
.Nm
command.
.El
.Pp
If the
.Ar pid
value is specified as 0,
.Nm
assumes it has been passed a corpse port by its parent;
if so it will generate a core dump for that corpse. The
.Fl c
flag may not be used in this case, as the process context may no longer exist.
.Pp
The
.Nm
command supports several sub-commands that can be
used with extended core files created using the
.Fl x
flag. These are:
.Bl -tag -width frefs
.\" -compact -offset indent
.Pp
.It Sy conv
Copy and convert a core file to the "pre-coreinfo" format
compatible with
.Xr lldb(1) .
This operation reads the input core file dereferencing any file
references it contains by copying the content
and decompressing any compressed data into the output core file.
This conversion usually makes the core file substantially larger.
.Pp
By default, files to be dereferenced must be accessible on the
local filesystem by the same relative paths as they were originally recorded
when the dump was taken.
Files that are Mach-O objects containing UUIDs are required to match
the UUIDs recorded at the time the core dump was taken.
Files are otherwise only checked for matching modification times, and
thus can easily be "forged" using
.Xr touch 1 .
.Pp
Several flags can be used with the conversion:
.Pp
.Bl -tag -width Fl
.It Fl L Ar searchpath
When processing file references,
look for the pathnames in the directories specified in
.Ar searchpath .
These should be specified as a colon-separated
list of base directories which will be prepended to each pathname in turn
for each file reference.
.It Fl z
During conversion, if any mapped file
identified by modification time
cannot be located, substitute zeroed memory.
.It Fl s
When processing file references,
try looking for the pathname through
.Xr dsymForUUID 1
before searching locally.
.It Fl v
Report progress on the conversion as it proceeds.
.It Fl d
Enable debugging of the
.Sy conv
subcommand.
.El
.It Sy map
Print a representation of the address space contained in the core file.
.Pp
.It Sy frefs
Print a list of files corresponding to the file references
in the core file.
Can be used to capture the set of files needed to bind the file references
into the core file at a later time.
.El
.Sh BUGS
.Pp
When using the
.Fl x
flag,
.Nm
will likely incorporate a reference to the shared cache into
.Ar corefile
including the UUID of that cache.
On some platforms, the cache is created when the release is built
and since it resides on a read-only root filesystem it should
generally be easy to retrieve.
However on the desktop, the lifecycle of this cache is managed locally
e.g. with
.Xr update_dyld_shared_cache 1 .
When this cache is recreated it is given a new UUID, the directory
entry for the old cache is removed, and the same filename
is used for the new cache.
Thus when the last named copy of the shared cache is removed from the
filesystem, extended core files that contain references to that cache
can no longer be converted.
.Pp
When using the
.Fl x
flag,
.Nm
may be unable to locate the currently mapped shared cache in the filesystem.
In this case
.Nm
does not generate any file references to the content of the
shared cache; it just copies as much of the content
as is needed from the memory image into the core file.
This may lead to substantially larger core files than expected.
.Pp
It would be nice if
.Xr lldb 1
could examine extended core files directly i.e. without the conversion step.
.Pp
.Sh SEE ALSO
.Xr dyld 1 ,
.Xr update_dyld_shared_cache 1 ,
.Xr dsymForUUID 1

View File

@ -10,56 +10,94 @@
/*
* 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 */

View 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,154 @@ done:
return out;
}
static char *
make_gcore_path(char **corefmtp, pid_t pid, uid_t uid, const char *nm)
{
char *corefmt = *corefmtp;
if (NULL == corefmt) {
const char defcore[] = "%N-%P-%T";
if (NULL == (corefmt = kern_corefile()))
corefmt = strdup(defcore);
else {
// use the same directory as kern.corefile
char *p = strrchr(corefmt, '/');
if (NULL != p) {
*p = '\0';
size_t len = strlen(corefmt) + strlen(defcore) + 2;
char *buf = malloc(len);
snprintf(buf, len, "%s/%s", corefmt, defcore);
free(corefmt);
corefmt = buf;
}
if (OPTIONS_DEBUG(opt, 3))
printf("corefmt '%s'\n", corefmt);
}
}
char *path = format_gcore_name(corefmt, pid, uid, nm);
free(corefmt);
*corefmtp = NULL;
return path;
}
static bool proc_same_data_model(const struct proc_bsdinfo *pbi) {
#if defined(__LP64__)
return (pbi->pbi_flags & PROC_FLAG_LP64) != 0;
#else
return (pbi->pbi_flags & PROC_FLAG_LP64) == 0;
#endif
}
static bool task_same_data_model(const task_flags_info_data_t *tfid) {
#if defined(__LP64__)
return (tfid->flags & TF_LP64) != 0;
#else
return (tfid->flags & TF_LP64) == 0;
#endif
}
/*
* Change credentials for writing out the file
*/
static void
change_credentials(gid_t uid, uid_t gid)
{
if ((getgid() != gid && -1 == setgid(gid)) ||
(getuid() != uid && -1 == setuid(uid)))
errc(EX_NOPERM, errno, "insufficient privilege");
if (uid != getuid() || gid != getgid())
err(EX_OSERR, "wrong credentials");
}
static int
openout(const char *corefname, char **coretname, struct stat *st)
{
const int tfd = open(corefname, O_WRONLY);
if (-1 == tfd) {
if (ENOENT == errno) {
/*
* Arrange for a core file to appear "atomically": write the data
* to the file + ".tmp" suffix, then fchmod and rename it into
* place once the dump completes successfully.
*/
const size_t nmlen = strlen(corefname) + 4 + 1;
char *tnm = malloc(nmlen);
snprintf(tnm, nmlen, "%s.tmp", corefname);
const int fd = open(tnm, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (-1 == fd || -1 == fstat(fd, st))
errc(EX_CANTCREAT, errno, "%s", tnm);
if (!S_ISREG(st->st_mode) || 1 != st->st_nlink)
errx(EX_CANTCREAT, "%s: invalid attributes", tnm);
*coretname = tnm;
return fd;
} else
errc(EX_CANTCREAT, errno, "%s", corefname);
} else if (-1 == fstat(tfd, st)) {
close(tfd);
errx(EX_CANTCREAT, "%s: fstat", corefname);
} else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
/*
* Write dump to a device, no rename!
*/
*coretname = NULL;
return tfd;
} else {
close(tfd);
errc(EX_CANTCREAT, EEXIST, "%s", corefname);
}
}
static int
closeout(int fd, int ecode, char *corefname, char *coretname, const struct stat *st)
{
if (0 != ecode && !opt->preserve && S_ISREG(st->st_mode))
ftruncate(fd, 0); // limit large file clutter
if (0 == ecode && S_ISREG(st->st_mode))
fchmod(fd, 0400); // protect core files
if (-1 == close(fd)) {
warnc(errno, "%s: close", coretname ? coretname : corefname);
ecode = EX_OSERR;
}
if (NULL != coretname) {
if (0 == ecode && -1 == rename(coretname, corefname)) {
warnc(errno, "cannot rename %s to %s", coretname, corefname);
ecode = EX_NOPERM;
}
free(coretname);
}
if (corefname)
free(corefname);
return ecode;
}
const char *pgm;
const struct options *opt;
int
main(int argc, char *const *argv)
{
if (NULL == (pgm = strrchr(*argv, '/')))
pgm = *argv;
else
pgm++;
static const size_t oneK = 1024;
static const size_t oneM = oneK * oneK;
#define LARGEST_CHUNKSIZE INT32_MAX
#define DEFAULT_COMPRESSION_CHUNKSIZE (16 * oneM)
#define DEFAULT_NC_THRESHOLD (17 * oneK)
static struct options options = {
.corpsify = 0,
.suspend = 0,
.preserve = 0,
.verbose = 0,
#ifdef CONFIG_DEBUG
.debug = 0,
#endif
.extended = 0,
.sizebound = 0,
.chunksize = 0,
.calgorithm = COMPRESSION_LZFSE,
.ncthresh = DEFAULT_NC_THRESHOLD,
.dsymforuuid = 0,
};
static int
gcore_main(int argc, char *const *argv)
{
#define ZOPT_ALG (0)
#define ZOPT_CHSIZE (ZOPT_ALG + 1)
@ -175,13 +312,15 @@ main(int argc, char *const *argv)
err_set_exit_b(^(int eval) {
if (EX_USAGE == eval) {
fprintf(stderr,
"usage:\n\t%s [-s] [-v] [[-o file] | [-c pathfmt ]] [-b size] "
"usage:\t%s [-s] [-v] [[-o file] | [-c pathfmt ]] [-b size] "
#if DEBUG
"[-d] [-n] [-i] [-p] [-S] [-z] [-C] "
"[-Z compression-options] "
#ifdef CONFIG_REFSC
"[-R] "
#ifdef CONFIG_DEBUG
"[-d] "
#endif
"[-x] [-C] "
"[-Z compression-options] "
"[-t size] "
"[-F] "
#endif
"pid\n", pgm);
#if DEBUG
@ -197,33 +336,11 @@ main(int argc, char *const *argv)
char *corefmt = NULL;
char *corefname = NULL;
const size_t oneM = 1024 * 1024;
#define LARGEST_CHUNKSIZE INT32_MAX
#define DEFAULT_COMPRESSION_CHUNKSIZE (16 * oneM)
struct options options = {
.corpse = 0,
.suspend = 0,
.preserve = 0,
.verbose = 0,
.debug = 0,
.dryrun = 0,
.sparse = 0,
.sizebound = 0,
.coreinfo = 0,
#ifdef OPTIONS_REFSC
.scfileref = 0,
#endif
.compress = 0,
.chunksize = LARGEST_CHUNKSIZE,
.calgorithm = COMPRESSION_LZFSE,
};
int c;
char *sopts, *value;
while ((c = getopt(argc, argv, "inmvdszpCSRZ:o:c:b:")) != -1) {
while ((c = getopt(argc, argv, "vdsxCFZ:o:c:b:t:")) != -1) {
switch (c) {
/*
@ -257,44 +374,23 @@ main(int argc, char *const *argv)
/*
* dev and debugging help
*/
case 'n': /* write the core file to /dev/null */
options.dryrun++;
break;
#ifdef CONFIG_DEBUG
case 'd': /* debugging */
options.debug++;
options.verbose++;
options.preserve++;
break;
case 'p': /* preserve partial core file (even if errors) */
options.preserve++;
break;
#endif
/*
* Remaining options are experimental and/or
* affect the content of the core file
*/
case 'i': /* include LC_COREINFO data */
options.coreinfo++;
case 'x': /* write extended format (small) core files */
options.extended++;
options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE;
break;
case 'C': /* corpsify rather than suspend */
options.corpse++;
break;
#ifdef CONFIG_REFSC
case 'R': /* include the shared cache by reference */
options.scfileref++;
options.coreinfo++;
break;
#endif
case 'S': /* use dyld info to control the content */
options.sparse++;
options.coreinfo++;
break;
case 'z': /* create compressed LC_SEGMENT* segments */
if (0 == options.compress) {
options.compress++;
options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE;
}
options.coreinfo++;
case 'C': /* forcibly corpsify rather than suspend */
options.corpsify++;
break;
case 'Z': /* control compression options */
/*
@ -302,10 +398,8 @@ main(int argc, char *const *argv)
* (Default to LZ4 compression when the
* process is suspended, LZFSE when corpsed?)
*/
if (0 == options.compress) {
options.compress++;
options.chunksize = DEFAULT_COMPRESSION_CHUNKSIZE;
}
if (0 == options.extended)
errx(EX_USAGE, "illegal flag combination");
sopts = optarg;
while (*sopts) {
size_t chsize;
@ -317,22 +411,17 @@ main(int argc, char *const *argv)
"%s suboption",
zoptkeys[ZOPT_ALG]);
if (strcmp(value, "lz4") == 0)
options.calgorithm =
COMPRESSION_LZ4;
options.calgorithm = COMPRESSION_LZ4;
else if (strcmp(value, "zlib") == 0)
options.calgorithm =
COMPRESSION_ZLIB;
options.calgorithm = COMPRESSION_ZLIB;
else if (strcmp(value, "lzma") == 0)
options.calgorithm =
COMPRESSION_LZMA;
options.calgorithm = COMPRESSION_LZMA;
else if (strcmp(value, "lzfse") == 0)
options.calgorithm =
COMPRESSION_LZFSE;
options.calgorithm = COMPRESSION_LZFSE;
else
errx(EX_USAGE, "unknown algorithm '%s'"
" for %s suboption",
value,
zoptkeys[ZOPT_ALG]);
value, zoptkeys[ZOPT_ALG]);
break;
case ZOPT_CHSIZE: /* set the chunksize */
if (NULL == value)
@ -353,245 +442,422 @@ main(int argc, char *const *argv)
errx(EX_USAGE, "missing suboption");
}
}
break;
case 't': /* set the F_NOCACHE threshold */
if (NULL != optarg) {
size_t tsize = atoi(optarg) * oneK;
if (tsize > 0)
options.ncthresh = tsize;
else
errx(EX_USAGE, "invalid nc threshold");
} else
errx(EX_USAGE, "no threshold specified");
break;
case 'F': /* maximize filerefs */
options.allfilerefs++;
break;
default:
errx(EX_USAGE, "unknown flag");
}
}
if (optind == argc)
errx(EX_USAGE, "no pid specified");
opt = &options;
if (optind == argc)
errx(EX_USAGE, "no pid specified");
if (optind < argc-1)
errx(EX_USAGE, "too many arguments");
if ((opt->dryrun ? 1 : 0) +
(NULL != corefname ? 1 : 0) +
(NULL != corefmt ? 1 : 0) > 1)
errx(EX_USAGE, "specify only one of -n, -o and -c");
opt = &options;
if (NULL != corefname && NULL != corefmt)
errx(EX_USAGE, "specify only one of -o and -c");
if (!opt->extended && opt->allfilerefs)
errx(EX_USAGE, "unknown flag");
setpageshift();
setpageshift();
const pid_t pid = atoi(argv[optind]);
if (pid < 1 || getpid() == pid)
errx(EX_DATAERR, "invalid pid: %d", pid);
if (-1 == kill(pid, 0)) {
switch (errno) {
case ESRCH:
errc(EX_DATAERR, errno, "no process with pid %d", pid);
default:
errc(EX_DATAERR, errno, "pid %d", pid);
}
}
if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host))
errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh);
const struct proc_bsdinfo *pbi = get_bsdinfo(pid);
if (NULL == pbi)
errx(EX_OSERR, "cannot get bsdinfo about %d", pid);
const pid_t apid = atoi(argv[optind]);
pid_t pid = apid;
mach_port_t corpse = MACH_PORT_NULL;
kern_return_t ret;
/*
* make our data model match the data model of the target
*/
if (-1 == reexec_to_match_lp64ness(pbi->pbi_flags & PROC_FLAG_LP64))
errc(1, errno, "cannot match data model of %d", pid);
if (0 == apid) {
/* look for corpse - dead or alive */
mach_port_array_t parray = NULL;
mach_msg_type_number_t pcount = 0;
ret = mach_ports_lookup(mach_task_self(), &parray, &pcount);
if (KERN_SUCCESS == ret && pcount > 0) {
task_t tcorpse = parray[0];
mig_deallocate((vm_address_t)parray, pcount * sizeof (*parray));
pid_t tpid = 0;
ret = pid_for_task(tcorpse, &tpid);
if (KERN_SUCCESS == ret && tpid != getpid()) {
corpse = tcorpse;
pid = tpid;
}
}
}
#if defined(__LP64__)
if ((pbi->pbi_flags & PROC_FLAG_LP64) == 0)
#else
if ((pbi->pbi_flags & PROC_FLAG_LP64) != 0)
#endif
errx(EX_OSERR, "cannot match data model of %d", pid);
if (pid < 1 || getpid() == pid)
errx(EX_DATAERR, "invalid pid: %d", pid);
/*
* These are experimental options for the moment.
* These will likely change.
* Some may become defaults, some may be removed altogether.
*/
if (opt->sparse ||
#ifdef CONFIG_REFSC
opt->scfileref ||
#endif
opt->compress ||
opt->corpse ||
opt->coreinfo)
warnx("experimental option(s) used, "
"resulting corefile may be unusable.");
if (0 == apid && MACH_PORT_NULL == corpse)
errx(EX_DATAERR, "missing or bad corpse from parent");
if (pbi->pbi_ruid != pbi->pbi_svuid ||
pbi->pbi_rgid != pbi->pbi_svgid)
errx(EX_NOPERM, "pid %d - not dumping a set-id process", pid);
task_t task = TASK_NULL;
const struct proc_bsdinfo *pbi = NULL;
const int rc = kill(pid, 0);
if (NULL == corefname) {
if (NULL == corefmt) {
const char defcore[] = "%N-%P-%T";
if (NULL == (corefmt = kern_corefile()))
corefmt = strdup(defcore);
else {
// use the same directory as kern.corefile
char *p = strrchr(corefmt, '/');
if (NULL != p) {
*p = '\0';
size_t len = strlen(corefmt) +
strlen(defcore) + 2;
char *buf = malloc(len);
snprintf(buf, len, "%s/%s", corefmt, defcore);
free(corefmt);
corefmt = buf;
}
if (opt->debug)
printf("corefmt '%s'\n", corefmt);
}
}
corefname = format_gcore_name(corefmt, pbi);
free(corefmt);
}
if (rc == 0) {
/* process or corpse that may respond to signals */
pbi = get_bsdinfo(pid);
}
task_t task;
kern_return_t ret = task_for_pid(mach_task_self(), pid, &task);
if (KERN_SUCCESS != ret) {
if (KERN_FAILURE == ret)
errx(EX_NOPERM, "insufficient privilege");
else
errx(EX_NOPERM, "task_for_pid: %s", mach_error_string(ret));
}
if (rc == 0 && pbi != NULL) {
/* process or corpse that responds to signals */
/*
* Now that we have the task port, we adopt the credentials of
* the target process, *before* opening the core file, and
* analyzing the address space.
*
* If we are unable to match the target credentials, bail out.
*/
if (getgid() != pbi->pbi_gid &&
setgid(pbi->pbi_gid) == -1)
errc(EX_NOPERM, errno, "insufficient privilege");
/* make our data model match the data model of the target */
if (-1 == reexec_to_match_lp64ness(pbi->pbi_flags & PROC_FLAG_LP64))
errc(1, errno, "cannot match data model of %d", pid);
if (getuid() != pbi->pbi_uid &&
setuid(pbi->pbi_uid) == -1)
errc(EX_NOPERM, errno, "insufficient privilege");
if (!proc_same_data_model(pbi))
errx(EX_OSERR, "cannot match data model of %d", pid);
int fd;
if (pbi->pbi_ruid != pbi->pbi_svuid ||
pbi->pbi_rgid != pbi->pbi_svgid)
errx(EX_NOPERM, "pid %d - not dumping a set-id process", pid);
if (opt->dryrun) {
free(corefname);
corefname = strdup("/dev/null");
fd = open(corefname, O_RDWR);
} else {
fd = open(corefname, O_RDWR | O_CREAT | O_EXCL, 0400);
if (NULL == corefname)
corefname = make_gcore_path(&corefmt, pbi->pbi_pid, pbi->pbi_uid, pbi->pbi_name[0] ? pbi->pbi_name : pbi->pbi_comm);
struct stat st;
if (MACH_PORT_NULL == corpse) {
ret = task_for_pid(mach_task_self(), pid, &task);
if (KERN_SUCCESS != ret) {
if (KERN_FAILURE == ret)
errx(EX_NOPERM, "insufficient privilege");
else
errx(EX_NOPERM, "task_for_pid: %s", mach_error_string(ret));
}
}
if (-1 == fd || -1 == fstat(fd, &st))
errc(EX_CANTCREAT, errno, "%s", corefname);
if ((st.st_mode & S_IFMT) != S_IFREG || 1 != st.st_nlink) {
close(fd);
errx(EX_CANTCREAT, "%s: invalid file", corefname);
}
}
/*
* Have either the corpse port or the task port so adopt the
* credentials of the target process, *before* opening the
* core file, and analyzing the address space.
*
* If we are unable to match the target credentials, bail out.
*/
change_credentials(pbi->pbi_uid, pbi->pbi_gid);
} else {
if (MACH_PORT_NULL == corpse) {
if (rc == 0) {
errx(EX_OSERR, "cannot get process info for %d", pid);
}
switch (errno) {
case ESRCH:
errc(EX_DATAERR, errno, "no process with pid %d", pid);
default:
errc(EX_DATAERR, errno, "pid %d", pid);
}
}
/* a corpse with no live process backing it */
if (opt->verbose) {
assert(0 == apid && TASK_NULL == task);
task_flags_info_data_t tfid;
mach_msg_type_number_t count = TASK_FLAGS_INFO_COUNT;
ret = task_info(corpse, TASK_FLAGS_INFO, (task_info_t)&tfid, &count);
if (KERN_SUCCESS != ret)
err_mach(ret, NULL, "task_info");
if (!task_same_data_model(&tfid))
errx(EX_OSERR, "data model mismatch for target corpse");
if (opt->suspend || opt->corpsify)
errx(EX_USAGE, "cannot use -s or -C option with a corpse");
if (NULL != corefmt)
errx(EX_USAGE, "cannot use -c with a corpse");
if (NULL == corefname)
corefname = make_gcore_path(&corefmt, pid, -2, "corpse");
/*
* Only have a corpse, thus no process credentials.
* Switch to nobody.
*/
change_credentials(-2, -2);
}
struct stat cst;
char *coretname = NULL;
const int fd = openout(corefname, &coretname, &cst);
if (opt->verbose) {
printf("Dumping core ");
if (opt->debug) {
printf("(%s%s%s",
opt->sparse ? "sparse" : "normal",
opt->compress ? ", compressed" : "",
#ifdef CONFIG_REFSC
opt->scfileref ? ", scfilerefs" :
#endif
"");
if (OPTIONS_DEBUG(opt, 1)) {
printf("(%s", opt->extended ? "extended" : "vanilla");
if (0 != opt->sizebound) {
hsize_str_t hstr;
printf(", %s", str_hsize(hstr, opt->sizebound));
printf(", <= %s", str_hsize(hstr, opt->sizebound));
}
printf(") ");
}
printf("for pid %d to %s\n", pid, corefname);
printf("for pid %d to %s\n", pid, corefname);
}
int ecode;
/*
* The traditional way to capture a consistent core dump is to
* suspend the process while processing it and writing it out.
* Yet suspending a large process for a long time can have
* unpleasant side-effects. Alternatively dumping from the live
* process can lead to an inconsistent state in the core file.
*
* Instead we can ask xnu to create a 'corpse' - the process is transiently
* suspended while a COW snapshot of the address space is constructed
* in the kernel and dump from that. This vastly reduces the suspend
* time, but it is more resource hungry and thus may fail.
*
* The -s flag (opt->suspend) causes a task_suspend/task_resume
* The -C flag (opt->corpse) causes a corpse be taken, exiting if that fails.
* Both flags can be specified, in which case corpse errors are ignored.
*
* With no flags, we imitate traditional behavior though more
* efficiently: we try to take a corpse-based dump, in the event that
* fails, dump the live process.
*/
if (MACH_PORT_NULL == corpse) {
assert(TASK_NULL != task);
int trycorpse = 1; /* default: use corpses */
int badcorpse_is_fatal = 1; /* default: failure to create a corpse is an error */
/*
* The "traditional" way to capture a consistent core dump is to
* suspend the process while examining it and writing it out.
* Yet suspending a large process for a long time can have
* unpleasant side-effects. Alternatively dumping from the live
* process can lead to an inconsistent state in the core file.
*
* Instead we can ask xnu to create a 'corpse' - the process is transiently
* suspended while a COW snapshot of the address space is constructed
* in the kernel and dump from that. This vastly reduces the suspend
* time, but it is more resource hungry and thus may fail.
*
* The -s flag (opt->suspend) causes a task_suspend/task_resume
* The -C flag (opt->corpse) causes a corpse be taken, exiting if that fails.
* Both flags can be specified, in which case corpse errors are ignored.
*
* With no flags, we imitate traditional behavior though more
* efficiently: we try to take a corpse-based dump, in the event that
* fails, dump the live process.
*/
if (!opt->suspend && !opt->corpse) {
/* try a corpse dump, else dump the live process */
badcorpse_is_fatal = 0;
} else if (opt->suspend) {
trycorpse = opt->corpse;
/* if suspended anyway, ignore corpse-creation errors */
badcorpse_is_fatal = 0;
}
int trycorpse = 1; /* default: use corpses */
int badcorpse_is_fatal = 1; /* default: failure to create is an error */
if (opt->suspend)
task_suspend(task);
if (!opt->suspend && !opt->corpsify) {
/* try a corpse dump, else dump the live process */
badcorpse_is_fatal = 0;
} else if (opt->suspend) {
trycorpse = opt->corpsify;
/* if suspended anyway, ignore corpse-creation errors */
badcorpse_is_fatal = 0;
}
if (trycorpse) {
/*
* Create a corpse from the image before dumping it
*/
mach_port_t corpse = MACH_PORT_NULL;
ret = task_generate_corpse(task, &corpse);
switch (ret) {
case KERN_SUCCESS:
if (opt->debug)
printf("corpse generated on port %x, task %x\n",
corpse, task);
ecode = coredump(corpse, fd);
mach_port_deallocate(mach_task_self(), corpse);
break;
default:
if (badcorpse_is_fatal || opt->debug) {
warnx("failed to snapshot pid %d: %s\n",
pid, mach_error_string(ret));
if (badcorpse_is_fatal) {
ecode = KERN_RESOURCE_SHORTAGE == ret ? EX_TEMPFAIL : EX_OSERR;
goto out;
}
}
ecode = coredump(task, fd);
break;
}
} else {
/*
* Examine the task directly
*/
ecode = coredump(task, fd);
}
if (opt->suspend)
task_suspend(task);
out:
if (opt->suspend)
task_resume(task);
if (trycorpse) {
/*
* Create a corpse from the image before dumping it
*/
ret = task_generate_corpse(task, &corpse);
switch (ret) {
case KERN_SUCCESS:
if (OPTIONS_DEBUG(opt, 1))
printf("Corpse generated on port %x, task %x\n",
corpse, task);
ecode = coredump(corpse, fd, pbi);
mach_port_deallocate(mach_task_self(), corpse);
break;
default:
if (badcorpse_is_fatal || opt->verbose) {
warnx("failed to snapshot pid %d: %s\n",
pid, mach_error_string(ret));
if (badcorpse_is_fatal) {
ecode = KERN_RESOURCE_SHORTAGE == ret ? EX_TEMPFAIL : EX_OSERR;
goto out;
}
}
ecode = coredump(task, fd, pbi);
break;
}
} else {
/*
* Examine the task directly
*/
ecode = coredump(task, fd, pbi);
}
if (0 != ecode && !opt->preserve && !opt->dryrun) {
/*
* try not to leave a half-written mess occupying
* blocks on the filesystem
*/
ftruncate(fd, 0);
unlink(corefname);
}
if (-1 == close(fd))
ecode = EX_OSERR;
if (ecode)
errx(ecode, "failed to dump core for pid %d", pid);
free(corefname);
out:
if (opt->suspend)
task_resume(task);
} else {
/*
* Handed a corpse by our parent.
*/
ecode = coredump(corpse, fd, pbi);
mach_port_deallocate(mach_task_self(), corpse);
}
ecode = closeout(fd, ecode, corefname, coretname, &cst);
if (ecode)
errx(ecode, "failed to dump core for pid %d", pid);
return 0;
}
#if defined(CONFIG_GCORE_FREF) || defined(CONFIG_GCORE_MAP) || defined(GCONFIG_GCORE_CONV)
static int
getcorefd(const char *infile)
{
const int fd = open(infile, O_RDONLY | O_CLOEXEC);
if (-1 == fd)
errc(EX_DATAERR, errno, "cannot open %s", infile);
struct mach_header mh;
if (-1 == pread(fd, &mh, sizeof (mh), 0))
errc(EX_OSERR, errno, "cannot read mach header from %s", infile);
static const char cant_match_data_model[] = "cannot match the data model of %s";
if (-1 == reexec_to_match_lp64ness(MH_MAGIC_64 == mh.magic))
errc(1, errno, cant_match_data_model, infile);
if (NATIVE_MH_MAGIC != mh.magic)
errx(EX_OSERR, cant_match_data_model, infile);
if (MH_CORE != mh.filetype)
errx(EX_DATAERR, "%s is not a mach core file", infile);
return fd;
}
#endif
#ifdef CONFIG_GCORE_FREF
static int
gcore_fref_main(int argc, char *argv[])
{
err_set_exit_b(^(int eval) {
if (EX_USAGE == eval) {
fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]);
}
});
if (2 == argc)
errx(EX_USAGE, "no input corefile");
if (argc > 3)
errx(EX_USAGE, "too many arguments");
opt = &options;
return gcore_fref(getcorefd(argv[2]));
}
#endif /* CONFIG_GCORE_FREF */
#ifdef CONFIG_GCORE_MAP
static int
gcore_map_main(int argc, char *argv[])
{
err_set_exit_b(^(int eval) {
if (EX_USAGE == eval) {
fprintf(stderr, "usage:\t%s %s corefile\n", pgm, argv[1]);
}
});
if (2 == argc)
errx(EX_USAGE, "no input corefile");
if (argc > 3)
errx(EX_USAGE, "too many arguments");
opt = &options;
return gcore_map(getcorefd(argv[2]));
}
#endif
#ifdef CONFIG_GCORE_CONV
static int
gcore_conv_main(int argc, char *argv[])
{
err_set_exit_b(^(int eval) {
if (EX_USAGE == eval)
fprintf(stderr,
"usage:\t%s %s [-v] [-L searchpath] [-z] [-s] incore outcore\n", pgm, argv[1]);
});
char *searchpath = NULL;
bool zf = false;
int c;
optind = 2;
while ((c = getopt(argc, argv, "dzvL:s")) != -1) {
switch (c) {
/*
* likely documented options
*/
case 'L':
searchpath = strdup(optarg);
break;
case 'z':
zf = true;
break;
case 'v':
options.verbose++;
break;
case 's':
options.dsymforuuid++;
break;
/*
* dev and debugging help
*/
#ifdef CONFIG_DEBUG
case 'd':
options.debug++;
options.verbose++;
options.preserve++;
break;
#endif
default:
errx(EX_USAGE, "unknown flag");
}
}
if (optind == argc)
errx(EX_USAGE, "no input corefile");
if (optind == argc - 1)
errx(EX_USAGE, "no output corefile");
if (optind < argc - 2)
errx(EX_USAGE, "too many arguments");
const char *incore = argv[optind];
char *corefname = strdup(argv[optind+1]);
opt = &options;
setpageshift();
if (opt->ncthresh < ((vm_offset_t)1 << pageshift_host))
errx(EX_USAGE, "threshold %lu less than host pagesize", opt->ncthresh);
const int infd = getcorefd(incore);
struct stat cst;
char *coretname = NULL;
const int fd = openout(corefname, &coretname, &cst);
int ecode = gcore_conv(infd, searchpath, zf, fd);
ecode = closeout(fd, ecode, corefname, coretname, &cst);
if (ecode)
errx(ecode, "failed to convert core file successfully");
return 0;
}
#endif
int
main(int argc, char *argv[])
{
if (NULL == (pgm = strrchr(*argv, '/')))
pgm = *argv;
else
pgm++;
#ifdef CONFIG_GCORE_FREF
if (argc > 1 && 0 == strcmp(argv[1], "fref")) {
return gcore_fref_main(argc, argv);
}
#endif
#ifdef CONFIG_GCORE_MAP
if (argc > 1 && 0 == strcmp(argv[1], "map")) {
return gcore_map_main(argc, argv);
}
#endif
#ifdef CONFIG_GCORE_CONV
if (argc > 1 && 0 == strcmp(argv[1], "conv")) {
return gcore_conv_main(argc, argv);
}
#endif
return gcore_main(argc, argv);
}

View File

@ -12,11 +12,14 @@
#if defined(__arm__) || defined(__arm64__)
#define RDAR_23744374 1 /* 'true' while not fixed i.e. enable workarounds */
#define RDAR_28040018 1 /* 'true' while not fixed i.e. enable workarounds */
#endif
#define CONFIG_REFSC 1 /* create shared cache reference segment (-R) */
//#define CONFIG_PURGABLE 1 /* record purgability */
//#define CONFIG_SUBMAP 1 /* include submaps (debugging) */
//#define CONFIG_SUBMAP 1 /* include submaps (debugging output) */
#define CONFIG_GCORE_MAP 1 /* support 'gcore map' */
#define CONFIG_GCORE_CONV 1 /* support 'gcore conv' - new -> old core files */
#define CONFIG_GCORE_FREF 1 /* support 'gcore fref' - referenced file list */
#define CONFIG_DEBUG 1 /* support '-d' option */
#ifdef NDEBUG
#define poison(a, p, s) /* do nothing */
@ -25,23 +28,35 @@
#endif
struct options {
int corpse; // dump from a corpse
int corpsify; // make a corpse to dump from
int suspend; // suspend while dumping
int preserve; // preserve the core file, even if there are errors
int verbose; // be chatty
int debug; // internal debugging: options accumulate. very noisy.
int dryrun; // do all the work, but throw the dump away
int sparse; // use dyld's data about dylibs to reduce the size of the dump
off_t sizebound; // maximum size of the dump
int coreinfo; // create a (currently experimental) 'coreinfo' section
#ifdef CONFIG_REFSC
int scfileref; // create "reference" segments that point at the shared cache
#ifdef CONFIG_DEBUG
int debug; // internal debugging: options accumulate. noisy.
#endif
int compress; // compress the dump
size_t chunksize; // max size of the compressed segment
int extended; // avoid writing out ro mapped files, compress regions
off_t sizebound; // maximum size of the dump
size_t chunksize; // max size of a compressed subregion
compression_algorithm calgorithm; // algorithm in use
size_t ncthresh; // F_NOCACHE enabled *above* this value
int allfilerefs; // if set, every mapped file on the root fs is a fileref
int dsymforuuid; // Try dsysForUUID to retrieve symbol-rich executable
};
extern const struct options *opt;
/*
* == 0 - not verbose
* >= 1 - verbose plus chatty
* >= 2 - tabular summaries
* >= 3 - all
*/
#ifdef CONFIG_DEBUG
#define OPTIONS_DEBUG(opt, lvl) ((opt)->debug && (opt)->debug >= (lvl))
#else
#define OPTIONS_DEBUG(opt, lvl) 0
#endif
#endif /* _OPTIONS_H */

View File

@ -10,36 +10,68 @@
#ifndef _REGION_H
#define _REGION_H
/*
* A range of virtual memory
*/
struct vm_range {
mach_vm_offset_t addr;
mach_vm_offset_t size;
};
#define _V_ADDR(g) ((g)->addr)
#define _V_SIZE(g) ((g)->size)
#define V_SETADDR(g, a) ((g)->addr = (a))
#define V_SETSIZE(g, z) ((g)->size = (z))
#define V_ENDADDR(g) (_V_ADDR(g) + _V_SIZE(g))
static __inline const mach_vm_offset_t V_ADDR(const struct vm_range *vr) {
return _V_ADDR(vr);
}
static __inline const mach_vm_offset_t V_SIZE(const struct vm_range *vr) {
return _V_SIZE(vr);
}
static __inline const size_t V_SIZEOF(const struct vm_range *vr) {
assert((typeof (vr->size))(size_t)_V_SIZE(vr) == _V_SIZE(vr));
return (size_t)_V_SIZE(vr);
}
/*
* A range of offsets into a file
*/
struct file_range {
off_t off;
off_t size;
};
#define F_OFF(f) ((f)->off)
#define F_SIZE(f) ((f)->size)
struct regionop;
struct subregion;
struct region {
STAILQ_ENTRY(region) r_linkage;
mach_vm_offset_t r_address;
mach_vm_offset_t r_size;
struct vm_range r_range;
#define _R_ADDR(r) ((r)->r_address)
#define _R_SIZE(r) ((r)->r_size)
#define R_SETADDR(r, a) ((r)->r_address = (a))
#define R_SETSIZE(r, z) ((r)->r_size = (z))
#define R_RANGE(r) (&(r)->r_range)
#define _R_ADDR(r) _V_ADDR(R_RANGE(r))
#define _R_SIZE(r) _V_SIZE(R_RANGE(r))
#define R_SIZEOF(r) V_SIZEOF(R_RANGE(r))
#define R_SETADDR(r, a) V_SETADDR(R_RANGE(r), (a))
#define R_SETSIZE(r, z) V_SETSIZE(R_RANGE(r), (z))
#define R_ENDADDR(r) (_R_ADDR(r) + _R_SIZE(r))
vm_region_submap_info_data_64_t r_info;
vm_page_info_basic_data_t r_pageinfo;
#ifdef CONFIG_PURGABLE
int r_purgable;
#endif
#ifdef CONFIG_SUBMAP
int r_depth;
#endif
boolean_t
r_insharedregion,
r_inzfodregion,
r_incommregion;
boolean_t r_insharedregion, r_inzfodregion, r_incommregion;
#ifdef CONFIG_REFSC
/*
* This field may be non-NULL if the region is a read-only part
* of a mapped file (i.e. the shared cache) and thus
@ -47,9 +79,9 @@ struct region {
*/
struct {
const struct libent *fr_libent;
const char *fr_pathname;
off_t fr_offset;
} *r_fileref;
#endif
/*
* These (optional) fields are filled in after we parse the information
@ -64,7 +96,6 @@ struct region {
static __inline const mach_vm_offset_t R_ADDR(const struct region *r) {
return _R_ADDR(r);
}
static __inline const mach_vm_offset_t R_SIZE(const struct region *r) {
return _R_SIZE(r);
}
@ -95,9 +126,7 @@ struct regionop {
#define ROP_DELETE(r) (((r)->r_op->rop_delete)(r))
extern const struct regionop vanilla_ops, sparse_ops, zfod_ops;
#ifdef CONFIG_REFSC
extern const struct regionop fileref_ops;
#endif
struct regionhead;

View 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,60 +131,43 @@ add_subregions_for_libent(
case NATIVE_LC_SEGMENT:
sc = (const void *)lc;
char scsegname[17];
strlcpy(scsegname, sc->segname, sizeof (scsegname));
if (0 == sc->vmaddr &&
strcmp(scsegname, SEG_PAGEZERO) == 0)
if (0 == sc->vmaddr && strcmp(sc->segname, SEG_PAGEZERO) == 0)
break;
/* -Depends- on finding a __TEXT segment first! */
if (MACH_VM_MAX_ADDRESS == scoffset) {
if (strcmp(scsegname, SEG_TEXT) == 0)
scoffset = mh_taddr - sc->vmaddr;
else {
/*
* Treat as error - don't want a partial description
* to cause something to be omitted from the dump.
*/
printr(r, "expected %s segment, found %s segment\n", SEG_TEXT, scsegname);
return WALK_ERROR;
}
}
mach_vm_offset_t lo = sc->vmaddr + objoff;
mach_vm_offset_t hi = lo + sc->vmsize;
/* Eliminate non-overlapping sections first */
if (R_ENDADDR(r) - 1 < sc->vmaddr + scoffset)
if (R_ENDADDR(r) - 1 < lo)
break;
if (sc->vmaddr + scoffset + sc->vmsize - 1 < R_ADDR(r))
if (hi - 1 < R_ADDR(r))
break;
/*
* Some part of this segment is in the region.
* Trim the edges in the case where we span regions.
*/
mach_vm_offset_t loaddr = sc->vmaddr + scoffset;
mach_vm_offset_t hiaddr = loaddr + sc->vmsize;
if (loaddr < R_ADDR(r))
loaddr = R_ADDR(r);
if (hiaddr > R_ENDADDR(r))
hiaddr = R_ENDADDR(r);
if (lo < R_ADDR(r))
lo = R_ADDR(r);
if (hi > R_ENDADDR(r))
hi = R_ENDADDR(r);
struct subregionlist *srl = calloc(1, sizeof (*srl));
struct subregion *s = new_subregion(loaddr, hiaddr - loaddr, sc, le);
struct subregion *s = new_subregion(lo, hi - lo, sc, le);
assert(sc->fileoff >= 0);
srl->srl_s = s;
STAILQ_INSERT_HEAD(srlh, srl, srl_linkage);
if (opt->debug > 3) {
if (OPTIONS_DEBUG(opt, 2)) {
hsize_str_t hstr;
printr(r, "subregion %llx-%llx %7s %12s\t%s [%x/%x off %zd for %zd nsects %u flags %x]\n",
printr(r, "subregion %llx-%llx %7s %12s\t%s [%s off %lu for %lu nsects %u flags %x]\n",
S_ADDR(s), S_ENDADDR(s),
str_hsize(hstr, S_SIZE(s)),
scsegname,
sc->segname,
S_FILENAME(s),
sc->initprot, sc->maxprot,
sc->fileoff, sc->filesize,
str_prot(sc->initprot),
(unsigned long)sc->fileoff,
(unsigned long)sc->filesize,
sc->nsects, sc->flags);
}
break;
@ -220,7 +208,7 @@ eliminate_duplicate_subregions(struct region *r)
i++;
continue;
}
if (opt->debug)
if (OPTIONS_DEBUG(opt, 3))
printr(r, "eliding duplicate %s subregion (%llx-%llx) file %s\n",
S_MACHO_TYPE(s1), S_ADDR(s1), S_ENDADDR(s1), S_FILENAME(s1));
/* If the duplicate subregions aren't mapping the same file (?), forget the name */
@ -237,24 +225,24 @@ eliminate_duplicate_subregions(struct region *r)
walk_return_t
decorate_memory_region(struct region *r, void *arg)
{
if (r->r_inzfodregion || r->r_incommregion)
return WALK_CONTINUE;
const dyld_process_info dpi = arg;
__block walk_return_t retval = WALK_CONTINUE;
__block subregionlisthead_t srlhead = STAILQ_HEAD_INITIALIZER(srlhead);
_dyld_process_info_for_each_image(dpi, ^(uint64_t mhaddr, const uuid_t uuid, __unused const char *path) {
_dyld_process_info_for_each_image(dpi, ^(uint64_t __unused mhaddr, const uuid_t uuid, __unused const char *path) {
if (WALK_CONTINUE == retval) {
const struct libent *le = libent_lookup_byuuid(uuid);
assert(le->le_mhaddr == mhaddr);
/*
* Core dumps conventionally contain the whole executable, but we're trying
* to elide everything that can't be found in a file elsewhere.
*/
#if 0
if (MH_EXECUTE == le->le_mh->filetype)
return; // cause the whole a.out to be emitted
#endif
retval = add_subregions_for_libent(&srlhead, r, le->le_mh, le->le_mhaddr, le);
bool shouldskip = false;
if (V_SIZE(&le->le_vr))
shouldskip = (R_ENDADDR(r) < V_ADDR(&le->le_vr) ||
R_ADDR(r) > V_ENDADDR(&le->le_vr));
if (!shouldskip)
retval = add_subregions_for_libent(&srlhead, r, le->le_mh, le->le_mhaddr, le);
}
});
if (WALK_CONTINUE != retval)
@ -289,48 +277,67 @@ decorate_memory_region(struct region *r, void *arg)
eliminate_duplicate_subregions(r);
const struct libent *lesc = NULL; /* libent ref for shared cache */
if (r->r_insharedregion) {
uuid_t uusc;
if (get_sc_uuid(dpi, uusc)) {
lesc = libent_lookup_byuuid(uusc);
assert(NULL == lesc->le_mh && 0 == lesc->le_mhaddr);
}
}
if (r->r_info.external_pager) {
/*
* Only very specific segment types get to be filerefs
*/
for (i = 0; i < r->r_nsubregions; i++) {
struct subregion *s = r->r_subregions[i];
/*
* Anything marked writable is trivially disqualified; we're
* going to copy it anyway.
*/
if (s->s_segcmd.initprot & VM_PROT_WRITE)
continue;
/*
* Only very specific segment types get to be filerefs
*/
for (i = 0; i < r->r_nsubregions; i++) {
struct subregion *s = r->r_subregions[i];
/*
* Anything writable is trivially disqualified
*/
if (s->s_segcmd.initprot & VM_PROT_WRITE)
continue;
/*
* As long as there's a filename, __TEXT and __LINKEDIT
* end up as a file reference.
*
* __LINKEDIT is more complicated: the segment commands point
* at a unified segment in the shared cache mapping.
* Ditto for __UNICODE(?)
*/
if (issubregiontype(s, SEG_TEXT)) {
/* fall through */;
} else if (issubregiontype(s, SEG_LINKEDIT)) {
if (r->r_insharedregion)
s->s_libent = lesc;
} else if (issubregiontype(s, "__UNICODE")) {
if (r->r_insharedregion)
s->s_libent = lesc;
} else
continue;
if (s->s_libent)
s->s_isfileref = true;
}
}
/* __TEXT and __LINKEDIT are our real targets */
if (!issubregiontype(s, SEG_TEXT) && !issubregiontype(s, SEG_LINKEDIT) && !issubregiontype(s, "__UNICODE")) {
if (OPTIONS_DEBUG(opt, 3)) {
hsize_str_t hstr;
printvr(S_RANGE(s), "skipping read-only %s segment %s\n", S_MACHO_TYPE(s), str_hsize(hstr, S_SIZE(s)));
}
continue;
}
if (r->r_insharedregion) {
/*
* Part of the shared region: things get more complicated.
*/
if (r->r_fileref) {
/*
* There's a file reference here for the whole region.
* For __TEXT subregions, we could, in principle (though
* see below) generate references to the individual
* dylibs that dyld reports in the region. If the
* debugger could then use the __LINKEDIT info in the
* file, then we'd be done. But as long as the dump
* includes __LINKEDIT sections, we're going to
* end up generating a file reference to the combined
* __LINKEDIT section in the shared cache anyway, so
* we might as well do that for the __TEXT regions as
* well.
*/
s->s_libent = r->r_fileref->fr_libent;
s->s_isuuidref = true;
} else {
/*
* If we get here, it's likely that the shared cache
* name can't be found e.g. update_dyld_shared_cache(1).
* For __TEXT subregions, we could generate refs to
* the individual dylibs, but note that the mach header
* and segment commands in memory are still pointing
* into the shared cache so any act of reconstruction
* is fiendishly complex. So copy it.
*/
assert(!s->s_isuuidref);
}
} else {
/* Just a regular dylib? */
if (s->s_libent)
s->s_isuuidref = true;
}
}
}
}
assert(WALK_CONTINUE == retval);
done:
@ -347,8 +354,7 @@ done:
* Strip region of all decoration
*
* Invoked (on every region!) after an error during the initial
* 'decoration' phase to discard to discard potentially incomplete
* information.
* 'decoration' phase to discard potentially incomplete information.
*/
walk_return_t
undecorate_memory_region(struct region *r, __unused void *arg)
@ -371,36 +377,44 @@ sparse_region_optimization(struct region *r, __unused void *arg)
* Pure zfod region: almost certainly a more compact
* representation - keep it that way.
*/
if (OPTIONS_DEBUG(opt, 3))
printr(r, "retaining zfod region\n");
assert(&zfod_ops == r->r_op);
return clean_subregions(r);
}
#ifdef CONFIG_REFSC
if (r->r_fileref) {
/*
* Already have a fileref for the whole region: almost
* certainly a more compact representation - keep
* it that way.
*/
assert(&fileref_ops == r->r_op);
return clean_subregions(r);
}
#endif
if (r->r_insharedregion && 0 == r->r_nsubregions) {
/*
* A segment in the shared region needs to be
* identified with an LC_SEGMENT that dyld claims,
* otherwise (we assert) it's not useful to the dump.
*/
if (OPTIONS_DEBUG(opt, 2)) {
hsize_str_t hstr;
printr(r, "not referenced in dyld info => "
"eliding %s range in shared region\n",
str_hsize(hstr, R_SIZE(r)));
}
if (0 == r->r_info.pages_dirtied && 0 == r->r_info.pages_swapped_out)
return WALK_DELETE_REGION;
if (OPTIONS_DEBUG(opt, 2)) {
hsize_str_t hstr;
printr(r, "dirty pages, but not referenced in dyld info => "
"NOT eliding %s range in shared region\n",
str_hsize(hstr, R_SIZE(r)));
}
}
if (r->r_insharedregion && 0 == r->r_nsubregions) {
/*
* A segment in the shared region needs to be
* identified with an LC_SEGMENT that dyld claims,
* otherwise (we assert) it's not useful to the dump.
*/
if (opt->debug) {
hsize_str_t hstr;
printr(r, "not referenced in dyld info => "
"eliding %s range in shared region\n",
str_hsize(hstr, R_SIZE(r)));
}
return WALK_DELETE_REGION;
}
if (r->r_fileref) {
/*
* Already have a fileref for the whole region: already
* a more compact representation - keep it that way.
*/
if (OPTIONS_DEBUG(opt, 3))
printr(r, "retaining fileref region\n");
assert(&fileref_ops == r->r_op);
return clean_subregions(r);
}
if (r->r_nsubregions > 1) {
/*
@ -413,7 +427,7 @@ sparse_region_optimization(struct region *r, __unused void *arg)
struct subregion *s0 = r->r_subregions[i-1];
struct subregion *s1 = r->r_subregions[i];
if (s0->s_isfileref) {
if (s0->s_isuuidref) {
i++;
continue; /* => destined to be a fileref */
}
@ -424,11 +438,9 @@ sparse_region_optimization(struct region *r, __unused void *arg)
if (S_ENDADDR(s0) == S_ADDR(s1)) {
/* directly adjacent subregions */
#if 1
if (opt->debug)
if (OPTIONS_DEBUG(opt, 2))
printr(r, "merging subregions (%llx-%llx + %llx-%llx) -- adjacent\n",
S_ADDR(s0), S_ENDADDR(s0), S_ADDR(s1), S_ENDADDR(s1));
#endif
S_SETSIZE(s0, S_ENDADDR(s1) - S_ADDR(s0));
elide_subregion(r, i);
continue;
@ -445,11 +457,9 @@ sparse_region_optimization(struct region *r, __unused void *arg)
if (pfn[0] == pfn[1] && pfn[0] == endpfn[0] && pfn[0] == endpfn[1]) {
/* two small subregions share a host page */
#if 1
if (opt->debug)
if (OPTIONS_DEBUG(opt, 2))
printr(r, "merging subregions (%llx-%llx + %llx-%llx) -- same page\n",
S_ADDR(s0), S_ENDADDR(s0), S_ADDR(s1), S_ENDADDR(s1));
#endif
S_SETSIZE(s0, S_ENDADDR(s1) - S_ADDR(s0));
elide_subregion(r, i);
continue;
@ -457,11 +467,9 @@ sparse_region_optimization(struct region *r, __unused void *arg)
if (pfn[1] == 1 + endpfn[0]) {
/* subregions are pagewise-adjacent: bigger chunks to compress */
#if 1
if (opt->debug)
if (OPTIONS_DEBUG(opt, 2))
printr(r, "merging subregions (%llx-%llx + %llx-%llx) -- adjacent pages\n",
S_ADDR(s0), S_ENDADDR(s0), S_ADDR(s1), S_ENDADDR(s1));
#endif
S_SETSIZE(s0, S_ENDADDR(s1) - S_ADDR(s0));
elide_subregion(r, i);
continue;
@ -471,6 +479,17 @@ sparse_region_optimization(struct region *r, __unused void *arg)
}
}
if (1 == r->r_nsubregions) {
struct subregion *s = r->r_subregions[0];
if (!s->s_isuuidref &&
R_ADDR(r) == S_ADDR(s) && R_ENDADDR(r) == S_ENDADDR(s)) {
if (OPTIONS_DEBUG(opt, 3))
printr(r, "subregion (%llx-%llx) reverts to region\n",
S_ADDR(s), S_ENDADDR(s));
return clean_subregions(r);
}
}
if (r->r_nsubregions)
r->r_op = &sparse_ops;

View 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) {

View 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);
}

View 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;
}

View 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 */

View 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>
@ -32,8 +33,13 @@
#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);
walk_region_list(rhead, simple_region_optimization, NULL);
if (dpi) {
if (opt->coreinfo || opt->sparse) {
/*
* Snapshot dyld's info ..
*/
if (!libent_build_nametable(task, dpi))
warnx("error parsing dyld data => ignored");
else {
if (opt->coreinfo) {
/*
* Find the a.out load address and uuid, and the dyld mach header for the coreinfo
*/
const struct libent *le;
if (NULL != (le = libent_lookup_first_bytype(MH_EXECUTE))) {
aout_load_addr = le->le_mhaddr;
uuid_copy(aout_uuid, le->le_uuid);
}
if (NULL != (le = libent_lookup_first_bytype(MH_DYLINKER))) {
dyld_addr = le->le_mhaddr;
}
}
if (opt->sparse) {
/*
* Use dyld's view of what's being used in the address
* space to shrink the dump.
*/
if (0 == walk_region_list(rhead, decorate_memory_region, (void *)dpi)) {
if (opt->debug)
printf("Performing sparse dump optimization(s)\n");
walk_region_list(rhead, sparse_region_optimization, NULL);
} else {
walk_region_list(rhead, undecorate_memory_region, NULL);
warnx("error parsing dyld data => ignored");
}
}
}
}
free_task_dyld_info(dpi);
}
if (dpi) {
/*
* Snapshot dyld's info ..
*/
if (!libent_build_nametable(task, dpi))
warnx("error parsing dyld data => ignored");
else {
/*
* Find the a.out load address and uuid, and the dyld mach header for the coreinfo
*/
const struct libent *le;
if (NULL != (le = libent_lookup_first_bytype(MH_EXECUTE))) {
aout_load_addr = le->le_mhaddr;
uuid_copy(aout_uuid, le->le_uuid);
}
if (NULL != (le = libent_lookup_first_bytype(MH_DYLINKER))) {
dyld_addr = le->le_mhaddr;
}
if (opt->debug)
/*
* Use dyld's view of what's being used in the address
* space to shrink the dump.
*/
if (OPTIONS_DEBUG(opt, 1))
printf("Decorating dump with dyld-derived data\n");
if (0 == walk_region_list(rhead, decorate_memory_region, (void *)dpi)) {
if (OPTIONS_DEBUG(opt, 1))
printf("Sparse dump optimization(s)\n");
walk_region_list(rhead, sparse_region_optimization, NULL);
} else {
walk_region_list(rhead, undecorate_memory_region, NULL);
warnx("error parsing dyld data => ignored");
}
}
free_task_dyld_info(dpi);
}
/*
* Hunt for any memory mapped files that we can include by reference
* Depending on whether the bsd part of the task is still present
* we might be able to determine filenames of other regions mapping
* them here - this allows fonts, images, and other read-only content
* to be converted into file references, further reducing the size
* of the dump.
*
* NOTE: Even though the corpse snapshots the VM, the filesystem is
* not correspondingly snapshotted and thus may mutate while the dump
* proceeds - so be pessimistic about inclusion.
*/
if (opt->extended && NULL != pbi) {
if (OPTIONS_DEBUG(opt, 1))
printf("Mapped file optimization\n");
walk_region_list(rhead, label_mapped_files, (void *)pbi);
}
if (OPTIONS_DEBUG(opt, 1))
printf("Optimization(s) done\n");
done:
if (0 == ecode)
ecode = coredump_write(task, fd, rhead, aout_uuid, aout_load_addr, dyld_addr);
return ecode;
}
#ifdef CONFIG_REFSC
struct find_shared_cache_args {
task_t fsc_task;
vm_object_id_t fsc_object_id;
@ -458,10 +667,9 @@ find_shared_cache(struct region *r, void *arg)
if (r->r_pageinfo.offset != 0)
return WALK_CONTINUE; /* must map beginning of file */
if (opt->debug) {
if (OPTIONS_DEBUG(opt, 1)) {
hsize_str_t hstr;
printf("Examining shared cache candidate %llx-%llx (%s)\n",
R_ADDR(r), R_ENDADDR(r), str_hsize(hstr, R_SIZE(r)));
printr(r, "examining %s shared cache candidate\n", str_hsize(hstr, R_SIZE(r)));
}
struct copied_dyld_cache_header *ch;
@ -469,20 +677,20 @@ find_shared_cache(struct region *r, void *arg)
kern_return_t ret = mach_vm_read(fsc->fsc_task, R_ADDR(r), sizeof (*ch), (vm_offset_t *)&ch, &chlen);
if (KERN_SUCCESS != ret) {
err_mach(ret, NULL, "mapping candidate shared region");
err_mach(ret, NULL, "mach_vm_read() candidate shared region header");
return WALK_CONTINUE;
}
uuid_t scuuid;
if (get_uuid_from_shared_cache_mapping(ch, chlen, scuuid) &&
uuid_compare(scuuid, fsc->fsc_uuid) == 0) {
if (opt->debug > 2) {
if (OPTIONS_DEBUG(opt, 1)) {
uuid_string_t uustr;
uuid_unparse_lower(fsc->fsc_uuid, uustr);
printr(r, "found shared cache %s here\n", uustr);
}
if (!r->r_info.external_pager) {
if (opt->debug)
if (OPTIONS_DEBUG(opt, 1))
printf("Hmm. Found shared cache magic# + uuid, but not externally paged?\n");
#if 0
return WALK_CONTINUE; /* should be "paged" from a file */
@ -495,7 +703,7 @@ find_shared_cache(struct region *r, void *arg)
}
mach_vm_deallocate(mach_task_self(), (vm_offset_t)ch, chlen);
if (fsc->fsc_object_id) {
if (opt->debug) {
if (OPTIONS_DEBUG(opt, 3)) {
uuid_string_t uu;
uuid_unparse_lower(fsc->fsc_uuid, uu);
printf("Shared cache objid %llx uuid %s\n",
@ -506,23 +714,23 @@ find_shared_cache(struct region *r, void *arg)
return WALK_CONTINUE;
}
static boolean_t
static bool
compare_region_with_shared_cache(const struct region *r, struct find_shared_cache_args *fsc)
{
struct stat st;
if (-1 == fstat(fsc->fsc_fd, &st)) {
if (opt->debug)
printr(r, "%s - %s\n",
if (OPTIONS_DEBUG(opt, 1))
printr(r, "cannot fstat %s - %s\n",
fsc->fsc_le->le_filename, strerror(errno));
return false;
}
void *file = mmap(NULL, (size_t)R_SIZE(r), PROT_READ, MAP_PRIVATE, fsc->fsc_fd, r->r_pageinfo.offset);
void *file = mmap(NULL, R_SIZEOF(r), PROT_READ, MAP_PRIVATE, fsc->fsc_fd, r->r_pageinfo.offset);
if ((void *)-1L == file) {
if (opt->debug)
if (OPTIONS_DEBUG(opt, 1))
printr(r, "mmap %s - %s\n", fsc->fsc_le->le_filename, strerror(errno));
return false;
}
madvise(file, (size_t)R_SIZE(r), MADV_SEQUENTIAL);
madvise(file, R_SIZEOF(r), MADV_SEQUENTIAL);
vm_offset_t data = 0;
mach_msg_type_number_t data_count;
@ -530,7 +738,7 @@ compare_region_with_shared_cache(const struct region *r, struct find_shared_cach
if (KERN_SUCCESS != kr || data_count < R_SIZE(r)) {
err_mach(kr, r, "mach_vm_read()");
munmap(file, (size_t)R_SIZE(r));
munmap(file, R_SIZEOF(r));
return false;
}
@ -549,7 +757,7 @@ compare_region_with_shared_cache(const struct region *r, struct find_shared_cach
* Check what's really mapped there and reduce the size accordingly.
*/
if (!is_actual_size(fsc->fsc_task, r, &cmpsize)) {
if (opt->debug)
if (OPTIONS_DEBUG(opt, 3))
printr(r, "narrowing the comparison (%llu "
"-> %llu)\n", R_SIZE(r), cmpsize);
}
@ -558,7 +766,7 @@ compare_region_with_shared_cache(const struct region *r, struct find_shared_cach
mach_vm_behavior_set(mach_task_self(), data, data_count, VM_BEHAVIOR_SEQUENTIAL);
const boolean_t thesame = memcmp(file, (void *)data, (size_t)cmpsize) == 0;
const bool thesame = memcmp(file, (void *)data, (size_t)cmpsize) == 0;
#if 0
if (!thesame) {
int diffcount = 0;
@ -577,9 +785,9 @@ compare_region_with_shared_cache(const struct region *r, struct find_shared_cach
}
#endif
mach_vm_deallocate(mach_task_self(), data, data_count);
munmap(file, (size_t)R_SIZE(r));
munmap(file, R_SIZEOF(r));
if (!thesame && opt->debug)
if (!thesame && OPTIONS_DEBUG(opt, 3))
printr(r, "mapped file (%s) region is modified\n", fsc->fsc_le->le_filename);
return thesame;
}
@ -598,47 +806,38 @@ label_shared_cache(struct region *r, void *arg)
return WALK_CONTINUE;
}
if (((r->r_info.protection | r->r_info.max_protection) & VM_PROT_WRITE) != 0) {
/* writable, but was it written? */
if (r->r_info.pages_dirtied + r->r_info.pages_swapped_out != 0)
return WALK_CONTINUE; // a heuristic ..
/* potentially writable, but was it written? */
if (0 != r->r_info.pages_dirtied)
return WALK_CONTINUE;
if (0 != r->r_info.pages_swapped_out)
return WALK_CONTINUE;
if (0 != r->r_info.pages_resident && !r->r_info.external_pager)
return WALK_CONTINUE;
if (OPTIONS_DEBUG(opt, 1))
printr(r, "verifying shared cache content against memory image\n");
if (!compare_region_with_shared_cache(r, fsc)) {
/* bits don't match */
if (OPTIONS_DEBUG(opt, 1))
printr(r, "hmm .. mismatch: using memory image\n");
return WALK_CONTINUE;
}
}
if (opt->debug > 2) {
/* this validation is -really- expensive */
if (!compare_region_with_shared_cache(r, fsc))
printr(r, "WARNING: region should match, but doesn't\n");
}
}
/*
* This mapped file segment will be represented as a reference
* to the file, rather than as a copy of the file.
* to the file, rather than as a copy of the mapped file.
*/
const struct libent *le = libent_lookup_byuuid(fsc->fsc_uuid);
r->r_fileref = calloc(1, sizeof (*r->r_fileref));
if (r->r_fileref) {
r->r_fileref->fr_libent = le;
if (r->r_fileref->fr_libent) {
r->r_fileref->fr_offset = r->r_pageinfo.offset;
r->r_op = &fileref_ops;
} else {
free(r->r_fileref);
r->r_fileref = NULL;
}
}
return WALK_CONTINUE;
addfileref(r, libent_lookup_byuuid(fsc->fsc_uuid), NULL);
return WALK_CONTINUE;
}
#endif /* CONFIG_REFSC */
struct regionhead *
coredump_prepare(task_t task, uuid_t sc_uuid)
{
struct regionhead *rhead = build_region_list(task);
if (opt->debug) {
if (OPTIONS_DEBUG(opt, 2)) {
printf("Region list built\n");
print_memory_region_header();
walk_region_list(rhead, region_print_memory, NULL);
}
@ -653,24 +852,27 @@ coredump_prepare(task_t task, uuid_t sc_uuid)
const struct libent *le;
if (NULL != nm)
le = libent_insert(nm, sc_uuid, 0, NULL);
le = libent_insert(nm, sc_uuid, 0, NULL, NULL, 0);
else {
le = libent_insert("(shared cache)", sc_uuid, 0, NULL);
libent_insert("(anonymous shared cache)", sc_uuid, 0, NULL, NULL, 0);
if (opt->verbose){
uuid_string_t uustr;
uuid_unparse_lower(sc_uuid, uustr);
printf("Shared cache UUID: %s, but no filename => ignored\n", uustr);
return rhead;
printf("Warning: cannot name the shared cache ");
if (OPTIONS_DEBUG(opt, 1)) {
uuid_string_t uustr;
uuid_unparse_lower(sc_uuid, uustr);
printf("(%s) ", uustr);
}
printf("- dump may be large!\n");
}
return rhead;
}
#ifdef CONFIG_REFSC
if (opt->scfileref) {
if (opt->extended) {
/*
* See if we can replace entire regions with references to the shared cache
* by looking at the VM meta-data about those regions.
*/
if (opt->debug) {
if (OPTIONS_DEBUG(opt, 1)) {
uuid_string_t uustr;
uuid_unparse_lower(sc_uuid, uustr);
printf("Searching for shared cache with uuid %s\n", uustr);
@ -694,15 +896,16 @@ coredump_prepare(task_t task, uuid_t sc_uuid)
if (opt->verbose)
printf("Referenced %s\n", nm);
fsca.fsc_le = le;
fsca.fsc_fd = open(fsca.fsc_le->le_filename, O_RDONLY);
walk_region_list(rhead, label_shared_cache, &fsca);
close(fsca.fsc_fd);
fsca.fsc_fd = open(fsca.fsc_le->le_pathname, O_RDONLY);
if (-1 == fsca.fsc_fd)
errc(EX_SOFTWARE, errno, "open %s", fsca.fsc_le->le_pathname);
else {
walk_region_list(rhead, label_shared_cache, &fsca);
close(fsca.fsc_fd);
}
free(nm);
}
}
#endif /* CONFIG_REFSC */
return rhead;
}

View 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);

View 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 ||

View 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 */

View File

@ -1,205 +0,0 @@
/*
* Copyright is disclaimed as to the contents of this file.
*
* $FreeBSD: src/usr.bin/getconf/confstr.gperf,v 1.5 2003/08/22 17:32:07 markm Exp $
*/
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include "getconf.h"
/*
* Override gperf's built-in external scope.
*/
static const struct map *in_word_set(const char *str);
/*
* The Standard seems a bit ambiguous over whether the POSIX_V6_*
* are specified with or without a leading underscore, so we just
* use both.
*/
struct map { const char *name; int key; int valid; };
static const struct map wordlist[] = {
#ifdef _CS_PATH
{ "PATH", _CS_PATH, 1 },
#else
{ "PATH", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFF32_CFLAGS
{ "POSIX_V6_ILP32_OFF32_CFLAGS", _CS_POSIX_V6_ILP32_OFF32_CFLAGS, 1 },
#else
{ "POSIX_V6_ILP32_OFF32_CFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFF32_LDFLAGS
{ "POSIX_V6_ILP32_OFF32_LDFLAGS", _CS_POSIX_V6_ILP32_OFF32_LDFLAGS, 1 },
#else
{ "POSIX_V6_ILP32_OFF32_LDFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFF32_LIBS
{ "POSIX_V6_ILP32_OFF32_LIBS", _CS_POSIX_V6_ILP32_OFF32_LIBS, 1 },
#else
{ "POSIX_V6_ILP32_OFF32_LIBS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS
{ "POSIX_V6_ILP32_OFFBIG_CFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS, 1 },
#else
{ "POSIX_V6_ILP32_OFFBIG_CFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS
{ "POSIX_V6_ILP32_OFFBIG_LDFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS, 1 },
#else
{ "POSIX_V6_ILP32_OFFBIG_LDFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFFBIG_LIBS
{ "POSIX_V6_ILP32_OFFBIG_LIBS", _CS_POSIX_V6_ILP32_OFFBIG_LIBS, 1 },
#else
{ "POSIX_V6_ILP32_OFFBIG_LIBS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LP64_OFF64_CFLAGS
{ "POSIX_V6_LP64_OFF64_CFLAGS", _CS_POSIX_V6_LP64_OFF64_CFLAGS, 1 },
#else
{ "POSIX_V6_LP64_OFF64_CFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LP64_OFF64_LDFLAGS
{ "POSIX_V6_LP64_OFF64_LDFLAGS", _CS_POSIX_V6_LP64_OFF64_LDFLAGS, 1 },
#else
{ "POSIX_V6_LP64_OFF64_LDFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LP64_OFF64_LIBS
{ "POSIX_V6_LP64_OFF64_LIBS", _CS_POSIX_V6_LP64_OFF64_LIBS, 1 },
#else
{ "POSIX_V6_LP64_OFF64_LIBS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS
{ "POSIX_V6_LPBIG_OFFBIG_CFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS, 1 },
#else
{ "POSIX_V6_LPBIG_OFFBIG_CFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS
{ "POSIX_V6_LPBIG_OFFBIG_LDFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS, 1 },
#else
{ "POSIX_V6_LPBIG_OFFBIG_LDFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LIBS
{ "POSIX_V6_LPBIG_OFFBIG_LIBS", _CS_POSIX_V6_LPBIG_OFFBIG_LIBS, 1 },
#else
{ "POSIX_V6_LPBIG_OFFBIG_LIBS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS
{ "POSIX_V6_WIDTH_RESTRICTED_ENVS", _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, 1 },
#else
{ "POSIX_V6_WIDTH_RESTRICTED_ENVS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFF32_CFLAGS
{ "_POSIX_V6_ILP32_OFF32_CFLAGS", _CS_POSIX_V6_ILP32_OFF32_CFLAGS, 1 },
#else
{ "_POSIX_V6_ILP32_OFF32_CFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFF32_LDFLAGS
{ "_POSIX_V6_ILP32_OFF32_LDFLAGS", _CS_POSIX_V6_ILP32_OFF32_LDFLAGS, 1 },
#else
{ "_POSIX_V6_ILP32_OFF32_LDFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFF32_LIBS
{ "_POSIX_V6_ILP32_OFF32_LIBS", _CS_POSIX_V6_ILP32_OFF32_LIBS, 1 },
#else
{ "_POSIX_V6_ILP32_OFF32_LIBS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS
{ "_POSIX_V6_ILP32_OFFBIG_CFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS, 1 },
#else
{ "_POSIX_V6_ILP32_OFFBIG_CFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS
{ "_POSIX_V6_ILP32_OFFBIG_LDFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS, 1 },
#else
{ "_POSIX_V6_ILP32_OFFBIG_LDFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_ILP32_OFFBIG_LIBS
{ "_POSIX_V6_ILP32_OFFBIG_LIBS", _CS_POSIX_V6_ILP32_OFFBIG_LIBS, 1 },
#else
{ "_POSIX_V6_ILP32_OFFBIG_LIBS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LP64_OFF64_CFLAGS
{ "_POSIX_V6_LP64_OFF64_CFLAGS", _CS_POSIX_V6_LP64_OFF64_CFLAGS, 1 },
#else
{ "_POSIX_V6_LP64_OFF64_CFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LP64_OFF64_LDFLAGS
{ "_POSIX_V6_LP64_OFF64_LDFLAGS", _CS_POSIX_V6_LP64_OFF64_LDFLAGS, 1 },
#else
{ "_POSIX_V6_LP64_OFF64_LDFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LP64_OFF64_LIBS
{ "_POSIX_V6_LP64_OFF64_LIBS", _CS_POSIX_V6_LP64_OFF64_LIBS, 1 },
#else
{ "_POSIX_V6_LP64_OFF64_LIBS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS
{ "_POSIX_V6_LPBIG_OFFBIG_CFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS, 1 },
#else
{ "_POSIX_V6_LPBIG_OFFBIG_CFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS
{ "_POSIX_V6_LPBIG_OFFBIG_LDFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS, 1 },
#else
{ "_POSIX_V6_LPBIG_OFFBIG_LDFLAGS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LIBS
{ "_POSIX_V6_LPBIG_OFFBIG_LIBS", _CS_POSIX_V6_LPBIG_OFFBIG_LIBS, 1 },
#else
{ "_POSIX_V6_LPBIG_OFFBIG_LIBS", 0, 0 },
#endif
#ifdef _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS
{ "_POSIX_V6_WIDTH_RESTRICTED_ENVS", _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, 1 },
#else
{ "_POSIX_V6_WIDTH_RESTRICTED_ENVS", 0, 0 },
#endif
#ifdef _CS_DARWIN_USER_DIR
{ "DARWIN_USER_DIR", _CS_DARWIN_USER_DIR, 1 },
#else
{ "DARWIN_USER_DIR", 0, 0 },
#endif
#ifdef _CS_DARWIN_USER_TEMP_DIR
{ "DARWIN_USER_TEMP_DIR", _CS_DARWIN_USER_TEMP_DIR, 1 },
#else
{ "DARWIN_USER_TEMP_DIR", 0, 0 },
#endif
#ifdef _CS_DARWIN_USER_CACHE_DIR
{ "DARWIN_USER_CACHE_DIR", _CS_DARWIN_USER_CACHE_DIR, 1 },
#else
{ "DARWIN_USER_CACHE_DIR", 0, 0 },
#endif
{ NULL, 0, 0 }
};
#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1)
static const struct map *
in_word_set(const char *word)
{
const struct map *mp;
for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) {
if (strcmp(word, mp->name) == 0)
return (mp);
}
return (NULL);
}
int
find_confstr(const char *name, int *key)
{
const struct map *rv;
rv = in_word_set(name);
if (rv != NULL) {
if (rv->valid) {
*key = rv->key;
return 1;
}
return -1;
}
return 0;
}

View File

@ -1,474 +0,0 @@
/*
* Copyright is disclaimed as to the contents of this file.
*
* $FreeBSD: src/usr.bin/getconf/limits.gperf,v 1.2 2003/08/22 17:32:07 markm Exp $
*/
#include <sys/types.h>
#include <string.h>
#include <limits.h>
#ifdef APPLE_GETCONF_UNDERSCORE
#include <alloca.h>
#endif /* APPLE_GETCONF_UNDERSCORE */
#include "getconf.h"
/*
* Override gperf's built-in external scope.
*/
static const struct map *in_word_set(const char *str);
struct map { const char *name; intmax_t value; int valid; };
static const struct map wordlist[] = {
#ifdef _POSIX_AIO_LISTIO_MAX
{ "_POSIX_AIO_LISTIO_MAX", _POSIX_AIO_LISTIO_MAX, 1 },
#else
{ "_POSIX_AIO_LISTIO_MAX", 0, 0 },
#endif
#ifdef _POSIX_AIO_MAX
{ "_POSIX_AIO_MAX", _POSIX_AIO_MAX, 1 },
#else
{ "_POSIX_AIO_MAX", 0, 0 },
#endif
#ifdef _POSIX_ARG_MAX
{ "_POSIX_ARG_MAX", _POSIX_ARG_MAX, 1 },
#else
{ "_POSIX_ARG_MAX", 0, 0 },
#endif
#ifdef _POSIX_CHILD_MAX
{ "_POSIX_CHILD_MAX", _POSIX_CHILD_MAX, 1 },
#else
{ "_POSIX_CHILD_MAX", 0, 0 },
#endif
#ifdef _POSIX_CLOCKRES_MIN
{ "_POSIX_CLOCKRES_MIN", _POSIX_CLOCKRES_MIN, 1 },
#else
{ "_POSIX_CLOCKRES_MIN", 0, 0 },
#endif
#ifdef _POSIX_DELAYTIMER_MAX
{ "_POSIX_DELAYTIMER_MAX", _POSIX_DELAYTIMER_MAX, 1 },
#else
{ "_POSIX_DELAYTIMER_MAX", 0, 0 },
#endif
#ifdef _POSIX_HOST_NAME_MAX
{ "_POSIX_HOST_NAME_MAX", _POSIX_HOST_NAME_MAX, 1 },
#else
{ "_POSIX_HOST_NAME_MAX", 0, 0 },
#endif
#ifdef _POSIX_LINK_MAX
{ "_POSIX_LINK_MAX", _POSIX_LINK_MAX, 1 },
#else
{ "_POSIX_LINK_MAX", 0, 0 },
#endif
#ifdef _POSIX_LOGIN_NAME_MAX
{ "_POSIX_LOGIN_NAME_MAX", _POSIX_LOGIN_NAME_MAX, 1 },
#else
{ "_POSIX_LOGIN_NAME_MAX", 0, 0 },
#endif
#ifdef _POSIX_MAX_CANON
{ "_POSIX_MAX_CANON", _POSIX_MAX_CANON, 1 },
#else
{ "_POSIX_MAX_CANON", 0, 0 },
#endif
#ifdef _POSIX_MAX_INPUT
{ "_POSIX_MAX_INPUT", _POSIX_MAX_INPUT, 1 },
#else
{ "_POSIX_MAX_INPUT", 0, 0 },
#endif
#ifdef _POSIX_MQ_OPEN_MAX
{ "_POSIX_MQ_OPEN_MAX", _POSIX_MQ_OPEN_MAX, 1 },
#else
{ "_POSIX_MQ_OPEN_MAX", 0, 0 },
#endif
#ifdef _POSIX_MQ_PRIO_MAX
{ "_POSIX_MQ_PRIO_MAX", _POSIX_MQ_PRIO_MAX, 1 },
#else
{ "_POSIX_MQ_PRIO_MAX", 0, 0 },
#endif
#ifdef _POSIX_NAME_MAX
{ "_POSIX_NAME_MAX", _POSIX_NAME_MAX, 1 },
#else
{ "_POSIX_NAME_MAX", 0, 0 },
#endif
#ifdef _POSIX_NGROUPS_MAX
{ "_POSIX_NGROUPS_MAX", _POSIX_NGROUPS_MAX, 1 },
#else
{ "_POSIX_NGROUPS_MAX", 0, 0 },
#endif
#ifdef _POSIX_OPEN_MAX
{ "_POSIX_OPEN_MAX", _POSIX_OPEN_MAX, 1 },
#else
{ "_POSIX_OPEN_MAX", 0, 0 },
#endif
#ifdef _POSIX_PATH_MAX
{ "_POSIX_PATH_MAX", _POSIX_PATH_MAX, 1 },
#else
{ "_POSIX_PATH_MAX", 0, 0 },
#endif
#ifdef _POSIX_PIPE_BUF
{ "_POSIX_PIPE_BUF", _POSIX_PIPE_BUF, 1 },
#else
{ "_POSIX_PIPE_BUF", 0, 0 },
#endif
#ifdef _POSIX_RE_DUP_MAX
{ "_POSIX_RE_DUP_MAX", _POSIX_RE_DUP_MAX, 1 },
#else
{ "_POSIX_RE_DUP_MAX", 0, 0 },
#endif
#ifdef _POSIX_RTSIG_MAX
{ "_POSIX_RTSIG_MAX", _POSIX_RTSIG_MAX, 1 },
#else
{ "_POSIX_RTSIG_MAX", 0, 0 },
#endif
#ifdef _POSIX_SEM_NSEMS_MAX
{ "_POSIX_SEM_NSEMS_MAX", _POSIX_SEM_NSEMS_MAX, 1 },
#else
{ "_POSIX_SEM_NSEMS_MAX", 0, 0 },
#endif
#ifdef _POSIX_SEM_VALUE_MAX
{ "_POSIX_SEM_VALUE_MAX", _POSIX_SEM_VALUE_MAX, 1 },
#else
{ "_POSIX_SEM_VALUE_MAX", 0, 0 },
#endif
#ifdef _POSIX_SIGQUEUE_MAX
{ "_POSIX_SIGQUEUE_MAX", _POSIX_SIGQUEUE_MAX, 1 },
#else
{ "_POSIX_SIGQUEUE_MAX", 0, 0 },
#endif
#ifdef _POSIX_SSIZE_MAX
{ "_POSIX_SSIZE_MAX", _POSIX_SSIZE_MAX, 1 },
#else
{ "_POSIX_SSIZE_MAX", 0, 0 },
#endif
#ifdef _POSIX_STREAM_MAX
{ "_POSIX_STREAM_MAX", _POSIX_STREAM_MAX, 1 },
#else
{ "_POSIX_STREAM_MAX", 0, 0 },
#endif
#ifdef _POSIX_SS_REPL_MAX
{ "_POSIX_SS_REPL_MAX", _POSIX_SS_REPL_MAX, 1 },
#else
{ "_POSIX_SS_REPL_MAX", 0, 0 },
#endif
#ifdef _POSIX_SYMLINK_MAX
{ "_POSIX_SYMLINK_MAX", _POSIX_SYMLINK_MAX, 1 },
#else
{ "_POSIX_SYMLINK_MAX", 0, 0 },
#endif
#ifdef _POSIX_SYMLOOP_MAX
{ "_POSIX_SYMLOOP_MAX", _POSIX_SYMLOOP_MAX, 1 },
#else
{ "_POSIX_SYMLOOP_MAX", 0, 0 },
#endif
#ifdef _POSIX_THREAD_DESTRUCTOR_ITERATIONS
{ "_POSIX_THREAD_DESTRUCTOR_ITERATIONS", _POSIX_THREAD_DESTRUCTOR_ITERATIONS, 1 },
#else
{ "_POSIX_THREAD_DESTRUCTOR_ITERATIONS", 0, 0 },
#endif
#ifdef _POSIX_THREAD_KEYS_MAX
{ "_POSIX_THREAD_KEYS_MAX", _POSIX_THREAD_KEYS_MAX, 1 },
#else
{ "_POSIX_THREAD_KEYS_MAX", 0, 0 },
#endif
#ifdef _POSIX_THREAD_THREADS_MAX
{ "_POSIX_THREAD_THREADS_MAX", _POSIX_THREAD_THREADS_MAX, 1 },
#else
{ "_POSIX_THREAD_THREADS_MAX", 0, 0 },
#endif
#ifdef _POSIX_TIMER_MAX
{ "_POSIX_TIMER_MAX", _POSIX_TIMER_MAX, 1 },
#else
{ "_POSIX_TIMER_MAX", 0, 0 },
#endif
#ifdef _POSIX_TRACE_EVENT_NAME_MAX
{ "_POSIX_TRACE_EVENT_NAME_MAX", _POSIX_TRACE_EVENT_NAME_MAX, 1 },
#else
{ "_POSIX_TRACE_EVENT_NAME_MAX", 0, 0 },
#endif
#ifdef _POSIX_TRACE_NAME_MAX
{ "_POSIX_TRACE_NAME_MAX", _POSIX_TRACE_NAME_MAX, 1 },
#else
{ "_POSIX_TRACE_NAME_MAX", 0, 0 },
#endif
#ifdef _POSIX_TRACE_SYS_MAX
{ "_POSIX_TRACE_SYS_MAX", _POSIX_TRACE_SYS_MAX, 1 },
#else
{ "_POSIX_TRACE_SYS_MAX", 0, 0 },
#endif
#ifdef _POSIX_TRACE_USER_EVENT_MAX
{ "_POSIX_TRACE_USER_EVENT_MAX", _POSIX_TRACE_USER_EVENT_MAX, 1 },
#else
{ "_POSIX_TRACE_USER_EVENT_MAX", 0, 0 },
#endif
#ifdef _POSIX_TTY_NAME_MAX
{ "_POSIX_TTY_NAME_MAX", _POSIX_TTY_NAME_MAX, 1 },
#else
{ "_POSIX_TTY_NAME_MAX", 0, 0 },
#endif
#ifdef _POSIX_TZNAME_MAX
{ "_POSIX_TZNAME_MAX", _POSIX_TZNAME_MAX, 1 },
#else
{ "_POSIX_TZNAME_MAX", 0, 0 },
#endif
#ifdef _POSIX2_BC_BASE_MAX
{ "_POSIX2_BC_BASE_MAX", _POSIX2_BC_BASE_MAX, 1 },
#else
{ "_POSIX2_BC_BASE_MAX", 0, 0 },
#endif
#ifdef _POSIX2_BC_DIM_MAX
{ "_POSIX2_BC_DIM_MAX", _POSIX2_BC_DIM_MAX, 1 },
#else
{ "_POSIX2_BC_DIM_MAX", 0, 0 },
#endif
#ifdef _POSIX2_BC_SCALE_MAX
{ "_POSIX2_BC_SCALE_MAX", _POSIX2_BC_SCALE_MAX, 1 },
#else
{ "_POSIX2_BC_SCALE_MAX", 0, 0 },
#endif
#ifdef _POSIX2_BC_STRING_MAX
{ "_POSIX2_BC_STRING_MAX", _POSIX2_BC_STRING_MAX, 1 },
#else
{ "_POSIX2_BC_STRING_MAX", 0, 0 },
#endif
#ifdef _POSIX2_CHARCLASS_NAME_MAX
{ "_POSIX2_CHARCLASS_NAME_MAX", _POSIX2_CHARCLASS_NAME_MAX, 1 },
#else
{ "_POSIX2_CHARCLASS_NAME_MAX", 0, 0 },
#endif
#ifdef _POSIX2_COLL_WEIGHTS_MAX
{ "_POSIX2_COLL_WEIGHTS_MAX", _POSIX2_COLL_WEIGHTS_MAX, 1 },
#else
{ "_POSIX2_COLL_WEIGHTS_MAX", 0, 0 },
#endif
#ifdef _POSIX2_EXPR_NEST_MAX
{ "_POSIX2_EXPR_NEST_MAX", _POSIX2_EXPR_NEST_MAX, 1 },
#else
{ "_POSIX2_EXPR_NEST_MAX", 0, 0 },
#endif
#ifdef _POSIX2_LINE_MAX
{ "_POSIX2_LINE_MAX", _POSIX2_LINE_MAX, 1 },
#else
{ "_POSIX2_LINE_MAX", 0, 0 },
#endif
#ifdef _POSIX2_RE_DUP_MAX
{ "_POSIX2_RE_DUP_MAX", _POSIX2_RE_DUP_MAX, 1 },
#else
{ "_POSIX2_RE_DUP_MAX", 0, 0 },
#endif
#ifdef _XOPEN_IOV_MAX
{ "_XOPEN_IOV_MAX", _XOPEN_IOV_MAX, 1 },
#else
{ "_XOPEN_IOV_MAX", 0, 0 },
#endif
#ifdef _XOPEN_NAME_MAX
{ "_XOPEN_NAME_MAX", _XOPEN_NAME_MAX, 1 },
#else
{ "_XOPEN_NAME_MAX", 0, 0 },
#endif
#ifdef _XOPEN_PATH_MAX
{ "_XOPEN_PATH_MAX", _XOPEN_PATH_MAX, 1 },
#else
{ "_XOPEN_PATH_MAX", 0, 0 },
#endif
#ifdef CHAR_BIT
{ "CHAR_BIT", CHAR_BIT, 1 },
#else
{ "CHAR_BIT", 0, 0 },
#endif
#ifdef CHAR_MAX
{ "CHAR_MAX", CHAR_MAX, 1 },
#else
{ "CHAR_MAX", 0, 0 },
#endif
#ifdef CHAR_MIN
{ "CHAR_MIN", CHAR_MIN, 1 },
#else
{ "CHAR_MIN", 0, 0 },
#endif
#ifdef INT_MAX
{ "INT_MAX", INT_MAX, 1 },
#else
{ "INT_MAX", 0, 0 },
#endif
#ifdef INT_MIN
{ "INT_MIN", INT_MIN, 1 },
#else
{ "INT_MIN", 0, 0 },
#endif
#ifdef LLONG_MIN
{ "LLONG_MIN", LLONG_MIN, 1 },
#else
{ "LLONG_MIN", 0, 0 },
#endif
#ifdef LLONG_MAX
{ "LLONG_MAX", LLONG_MAX, 1 },
#else
{ "LLONG_MAX", 0, 0 },
#endif
#ifdef LONG_BIT
{ "LONG_BIT", LONG_BIT, 1 },
#else
{ "LONG_BIT", 0, 0 },
#endif
#ifdef LONG_MAX
{ "LONG_MAX", LONG_MAX, 1 },
#else
{ "LONG_MAX", 0, 0 },
#endif
#ifdef LONG_MIN
{ "LONG_MIN", LONG_MIN, 1 },
#else
{ "LONG_MIN", 0, 0 },
#endif
#ifdef MB_LEN_MAX
{ "MB_LEN_MAX", MB_LEN_MAX, 1 },
#else
{ "MB_LEN_MAX", 0, 0 },
#endif
#ifdef SCHAR_MAX
{ "SCHAR_MAX", SCHAR_MAX, 1 },
#else
{ "SCHAR_MAX", 0, 0 },
#endif
#ifdef SCHAR_MIN
{ "SCHAR_MIN", SCHAR_MIN, 1 },
#else
{ "SCHAR_MIN", 0, 0 },
#endif
#ifdef SHRT_MAX
{ "SHRT_MAX", SHRT_MAX, 1 },
#else
{ "SHRT_MAX", 0, 0 },
#endif
#ifdef SHRT_MIN
{ "SHRT_MIN", SHRT_MIN, 1 },
#else
{ "SHRT_MIN", 0, 0 },
#endif
#ifdef SSIZE_MAX
{ "SSIZE_MAX", SSIZE_MAX, 1 },
#else
{ "SSIZE_MAX", 0, 0 },
#endif
#ifdef UCHAR_MAX
{ "UCHAR_MAX", UCHAR_MAX, 1 },
#else
{ "UCHAR_MAX", 0, 0 },
#endif
#ifdef UINT_MAX
{ "UINT_MAX", UINT_MAX, 1 },
#else
{ "UINT_MAX", 0, 0 },
#endif
#ifdef ULLONG_MAX
{ "ULLONG_MAX", ULLONG_MAX, 1 },
#else
{ "ULLONG_MAX", 0, 0 },
#endif
#ifdef ULONG_MAX
{ "ULONG_MAX", ULONG_MAX, 1 },
#else
{ "ULONG_MAX", 0, 0 },
#endif
#ifdef USHRT_MAX
{ "USHRT_MAX", USHRT_MAX, 1 },
#else
{ "USHRT_MAX", 0, 0 },
#endif
#ifdef WORD_BIT
{ "WORD_BIT", WORD_BIT, 1 },
#else
{ "WORD_BIT", 0, 0 },
#endif
#ifdef CHARCLASS_NAME_MAX
{ "CHARCLASS_NAME_MAX", CHARCLASS_NAME_MAX, 1 },
#else
{ "CHARCLASS_NAME_MAX", 0, 0 },
#endif
#ifdef NL_ARGMAX
{ "NL_ARGMAX", NL_ARGMAX, 1 },
#else
{ "NL_ARGMAX", 0, 0 },
#endif
#ifdef NL_LANGMAX
{ "ML_LANGMAX", NL_LANGMAX, 1 },
#else
{ "ML_LANGMAX", 0, 0 },
#endif
#ifdef NL_MSGMAX
{ "NL_MSGMAX", NL_MSGMAX, 1 },
#else
{ "NL_MSGMAX", 0, 0 },
#endif
#ifdef NL_NMAX
{ "NL_NMAX", NL_NMAX, 1 },
#else
{ "NL_NMAX", 0, 0 },
#endif
#ifdef NL_SETMAX
{ "NL_SETMAX", NL_SETMAX, 1 },
#else
{ "NL_SETMAX", 0, 0 },
#endif
#ifdef NL_TEXTMAX
{ "NL_TEXTMAX", NL_TEXTMAX, 1 },
#else
{ "NL_TEXTMAX", 0, 0 },
#endif
#ifdef NZERO
{ "NZERO", NZERO, 1 },
#else
{ "NZERO", 0, 0 },
#endif
{ NULL, 0, 0 }
};
#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1)
static const struct map *
in_word_set(const char *word)
{
const struct map *mp;
for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) {
if (strcmp(word, mp->name) == 0)
return (mp);
}
return (NULL);
}
int
find_limit(const char *name, intmax_t *value)
{
const struct map *rv;
#ifdef APPLE_GETCONF_UNDERSCORE
char *alt;
#endif /* APPLE_GETCONF_UNDERSCORE */
rv = in_word_set(name);
if (rv != NULL) {
if (rv->valid) {
*value = rv->value;
return 1;
}
return -1;
}
#ifdef APPLE_GETCONF_UNDERSCORE
if(*name == '_')
alt = (char *)name + 1;
else {
if((alt = (char *)alloca(strlen(name) + 2)) == NULL)
return 0;
*alt = '_';
strcpy(alt + 1, name);
}
rv = in_word_set(alt);
if (rv != NULL) {
if (rv->valid) {
*value = rv->value;
return 1;
}
return -1;
}
#endif /* APPLE_GETCONF_UNDERSCORE */
return 0;
}

View File

@ -1,204 +0,0 @@
/*
* Copyright is disclaimed as to the contents of this file.
*
* $FreeBSD: src/usr.bin/getconf/pathconf.gperf,v 1.4 2003/08/22 17:32:07 markm Exp $
*/
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#ifdef APPLE_GETCONF_UNDERSCORE
#include <alloca.h>
#endif /* APPLE_GETCONF_UNDERSCORE */
#include "getconf.h"
/*
* Override gperf's built-in external scope.
*/
static const struct map *in_word_set(const char *str);
struct map { const char *name; int key; int valid; };
static const struct map wordlist[] = {
#ifdef _PC_FILESIZEBITS
{ "FILESIZEBITS", _PC_FILESIZEBITS, 1 },
#else
{ "FILESIZEBITS", 0, 0 },
#endif
#ifdef _PC_LINK_MAX
{ "LINK_MAX", _PC_LINK_MAX, 1 },
#else
{ "LINK_MAX", 0, 0 },
#endif
#ifdef _PC_MAX_CANON
{ "MAX_CANON", _PC_MAX_CANON, 1 },
#else
{ "MAX_CANON", 0, 0 },
#endif
#ifdef _PC_MAX_INPUT
{ "MAX_INPUT", _PC_MAX_INPUT, 1 },
#else
{ "MAX_INPUT", 0, 0 },
#endif
#ifdef _PC_NAME_MAX
{ "NAME_MAX", _PC_NAME_MAX, 1 },
#else
{ "NAME_MAX", 0, 0 },
#endif
#ifdef _PC_PATH_MAX
{ "PATH_MAX", _PC_PATH_MAX, 1 },
#else
{ "PATH_MAX", 0, 0 },
#endif
#ifdef _PC_PIPE_BUF
{ "PIPE_BUF", _PC_PIPE_BUF, 1 },
#else
{ "PIPE_BUF", 0, 0 },
#endif
#ifdef _PC_ALLOC_SIZE_MIN
{ "POSIX_ALLOC_SIZE_MIN", _PC_ALLOC_SIZE_MIN, 1 },
#else
{ "POSIX_ALLOC_SIZE_MIN", 0, 0 },
#endif
#ifdef _PC_REC_INCR_XFER_SIZE
{ "POSIX_REC_INCR_XFER_SIZE", _PC_REC_INCR_XFER_SIZE, 1 },
#else
{ "POSIX_REC_INCR_XFER_SIZE", 0, 0 },
#endif
#ifdef _PC_REC_MAX_XFER_SIZE
{ "POSIX_REC_MAX_XFER_SIZE", _PC_REC_MAX_XFER_SIZE, 1 },
#else
{ "POSIX_REC_MAX_XFER_SIZE", 0, 0 },
#endif
#ifdef _PC_REC_MIN_XFER_SIZE
{ "POSIX_REC_MIN_XFER_SIZE", _PC_REC_MIN_XFER_SIZE, 1 },
#else
{ "POSIX_REC_MIN_XFER_SIZE", 0, 0 },
#endif
#ifdef _PC_REC_XFER_ALIGN
{ "POSIX_REC_XFER_ALIGN", _PC_REC_XFER_ALIGN, 1 },
#else
{ "POSIX_REC_XFER_ALIGN", 0, 0 },
#endif
#ifdef _PC_2_SYMLINKS
{ "POSIX2_SYMLINKS", _PC_2_SYMLINKS, 1 },
#else
{ "POSIX2_SYMLINKS", 0, 0 },
#endif
#ifdef _PC_SYMLINK_MAX
{ "SYMLINK_MAX", _PC_SYMLINK_MAX, 1 },
#else
{ "SYMLINK_MAX", 0, 0 },
#endif
#ifdef _PC_ACL_EXTENDED
{ "TRUSTEDBSD_ACL_EXTENDED", _PC_ACL_EXTENDED, 1 },
#else
{ "TRUSTEDBSD_ACL_EXTENDED", 0, 0 },
#endif
#ifdef _PC_ACL_PATH_MAX
{ "TRUSTEDBSD_ACL_PATH_MAX", _PC_ACL_PATH_MAX, 1 },
#else
{ "TRUSTEDBSD_ACL_PATH_MAX", 0, 0 },
#endif
#ifdef _PC_CAP_PRESENT
{ "TRUSTEDBSD_CAP_PRESENT", _PC_CAP_PRESENT, 1 },
#else
{ "TRUSTEDBSD_CAP_PRESENT", 0, 0 },
#endif
#ifdef _PC_INF_PRESENT
{ "TRUSTEDBSD_INF_PRESENT", _PC_INF_PRESENT, 1 },
#else
{ "TRUSTEDBSD_INF_PRESENT", 0, 0 },
#endif
#ifdef _PC_MAC_PRESENT
{ "TRUSTEDBSD_MAC_PRESENT", _PC_MAC_PRESENT, 1 },
#else
{ "TRUSTEDBSD_MAC_PRESENT", 0, 0 },
#endif
#ifdef _PC_ASYNC_IO
{ "_POSIX_ASYNC_IO", _PC_ASYNC_IO, 1 },
#else
{ "_POSIX_ASYNC_IO", 0, 0 },
#endif
#ifdef _PC_CHOWN_RESTRICTED
{ "_POSIX_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED, 1 },
#else
{ "_POSIX_CHOWN_RESTRICTED", 0, 0 },
#endif
#ifdef _PC_NO_TRUNC
{ "_POSIX_NO_TRUNC", _PC_NO_TRUNC, 1 },
#else
{ "_POSIX_NO_TRUNC", 0, 0 },
#endif
#ifdef _PC_PATH_MAX
{ "_POSIX_PATH_MAX", _PC_PATH_MAX, 1 },
#else
{ "_POSIX_PATH_MAX", 0, 0 },
#endif
#ifdef _PC_PRIO_IO
{ "_POSIX_PRIO_IO", _PC_PRIO_IO, 1 },
#else
{ "_POSIX_PRIO_IO", 0, 0 },
#endif
#ifdef _PC_SYNC_IO
{ "_POSIX_SYNC_IO", _PC_SYNC_IO, 1 },
#else
{ "_POSIX_SYNC_IO", 0, 0 },
#endif
#ifdef _PC_VDISABLE
{ "_POSIX_VDISABLE", _PC_VDISABLE, 1 },
#else
{ "_POSIX_VDISABLE", 0, 0 },
#endif
{ NULL, 0, 0 }
};
#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1)
static const struct map *
in_word_set(const char *word)
{
const struct map *mp;
for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) {
if (strcmp(word, mp->name) == 0)
return (mp);
}
return (NULL);
}
int
find_pathconf(const char *name, int *key)
{
const struct map *rv;
#ifdef APPLE_GETCONF_UNDERSCORE
char *alt;
#endif /* APPLE_GETCONF_UNDERSCORE */
rv = in_word_set(name);
if (rv != NULL) {
if (rv->valid) {
*key = rv->key;
return 1;
}
return -1;
}
#ifdef APPLE_GETCONF_UNDERSCORE
if(*name == '_')
alt = (char *)name + 1;
else {
if((alt = (char *)alloca(strlen(name) + 2)) == NULL)
return 0;
*alt = '_';
strcpy(alt + 1, name);
}
rv = in_word_set(alt);
if (rv != NULL) {
if (rv->valid) {
*key = rv->key;
return 1;
}
return -1;
}
#endif /* APPLE_GETCONF_UNDERSCORE */
return 0;
}

View File

@ -1,114 +0,0 @@
/*
* Copyright is disclaimed as to the contents of this file.
*
* $FreeBSD: src/usr.bin/getconf/progenv.gperf,v 1.2 2003/08/22 17:32:07 markm Exp $
*/
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include "getconf.h"
/*
* Override gperf's built-in external scope.
*/
static const struct map *in_word_set(const char *str);
/*
* The Standard seems a bit ambiguous over whether the POSIX_V6_*
* are specified with or without a leading underscore, so we just
* use both.
*/
/*
* The alt_path member gives the path containing another `getconf'
* executable which was compiled using the specified programming
* environment. If it is NULL, the current executable is good enough.
* If we ever support multiple environments, this table will need to
* be updated. (We cheat here and define the supported environments
* statically.)
*/
#if defined(__alpha__) || defined(__sparc64__)
#define have_LP64_OFF64 NULL
#elif defined(__APPLE__)
#define have_LP64_OFF64 NULL
#define have_LPBIG_OFFBIG NULL
#endif
#if defined(__i386__) || defined(__powerpc__) || defined(__x86_64__)
#define have_ILP32_OFFBIG NULL
#endif
struct map { const char *name; const char *alt_path; int valid; };
static const struct map wordlist[] = {
#ifdef notdef
{ "POSIX_V6_ILP32_OFF32", notdef, 1 },
#else
{ "POSIX_V6_ILP32_OFF32", 0, 0 },
#endif
#ifdef have_ILP32_OFFBIG
{ "POSIX_V6_ILP32_OFFBIG", have_ILP32_OFFBIG, 1 },
#else
{ "POSIX_V6_ILP32_OFFBIG", 0, 0 },
#endif
#ifdef have_LP64_OFF64
{ "POSIX_V6_LP64_OFF64", have_LP64_OFF64, 1 },
#else
{ "POSIX_V6_LP64_OFF64", 0, 0 },
#endif
#ifdef have_LPBIG_OFFBIG
{ "POSIX_V6_LPBIG_OFFBIG", have_LPBIG_OFFBIG, 1 },
#else
{ "POSIX_V6_LPBIG_OFFBIG", 0, 0 },
#endif
#ifdef notdef
{ "_POSIX_V6_ILP32_OFF32", notdef, 1 },
#else
{ "_POSIX_V6_ILP32_OFF32", 0, 0 },
#endif
#ifdef have_ILP32_OFFBIG
{ "_POSIX_V6_ILP32_OFFBIG", have_ILP32_OFFBIG, 1 },
#else
{ "_POSIX_V6_ILP32_OFFBIG", 0, 0 },
#endif
#ifdef have_LP64_OFF64
{ "_POSIX_V6_LP64_OFF64", have_LP64_OFF64, 1 },
#else
{ "_POSIX_V6_LP64_OFF64", 0, 0 },
#endif
#ifdef have_LPBIG_OFFBIG
{ "_POSIX_V6_LPBIG_OFFBIG", have_LPBIG_OFFBIG, 1 },
#else
{ "_POSIX_V6_LPBIG_OFFBIG", 0, 0 },
#endif
{ NULL, 0, 0 }
};
#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1)
static const struct map *
in_word_set(const char *word)
{
const struct map *mp;
for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) {
if (strcmp(word, mp->name) == 0)
return (mp);
}
return (NULL);
}
int
find_progenv(const char *name, const char **alt_path)
{
const struct map *rv;
rv = in_word_set(name);
if (rv != NULL) {
if (rv->valid) {
*alt_path = rv->alt_path;
return 1;
}
return -1;
}
return 0;
}

View File

@ -1,699 +0,0 @@
/*
* Copyright is disclaimed as to the contents of this file.
*
* $FreeBSD: src/usr.bin/getconf/sysconf.gperf,v 1.5 2003/08/22 17:32:07 markm Exp $
*/
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#ifdef APPLE_GETCONF_UNDERSCORE
#include <alloca.h>
#endif /* APPLE_GETCONF_UNDERSCORE */
#include "getconf.h"
/*
* Override gperf's built-in external scope.
*/
static const struct map *in_word_set(const char *str);
struct map { const char *name; int key; int valid; };
static const struct map wordlist[] = {
#ifdef _SC_AIO_LISTIO_MAX
{ "AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX, 1 },
#else
{ "AIO_LISTIO_MAX", 0, 0 },
#endif
#ifdef _SC_AIO_MAX
{ "AIO_MAX", _SC_AIO_MAX, 1 },
#else
{ "AIO_MAX", 0, 0 },
#endif
#ifdef _SC_AIO_PRIO_DELTA_MAX
{ "AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX, 1 },
#else
{ "AIO_PRIO_DELTA_MAX", 0, 0 },
#endif
#ifdef _SC_ARG_MAX
{ "ARG_MAX", _SC_ARG_MAX, 1 },
#else
{ "ARG_MAX", 0, 0 },
#endif
#ifdef _SC_ATEXIT_MAX
{ "ATEXIT_MAX", _SC_ATEXIT_MAX, 1 },
#else
{ "ATEXIT_MAX", 0, 0 },
#endif
#ifdef _SC_BC_BASE_MAX
{ "BC_BASE_MAX", _SC_BC_BASE_MAX, 1 },
#else
{ "BC_BASE_MAX", 0, 0 },
#endif
#ifdef _SC_BC_DIM_MAX
{ "BC_DIM_MAX", _SC_BC_DIM_MAX, 1 },
#else
{ "BC_DIM_MAX", 0, 0 },
#endif
#ifdef _SC_BC_SCALE_MAX
{ "BC_SCALE_MAX", _SC_BC_SCALE_MAX, 1 },
#else
{ "BC_SCALE_MAX", 0, 0 },
#endif
#ifdef _SC_BC_STRING_MAX
{ "BC_STRING_MAX", _SC_BC_STRING_MAX, 1 },
#else
{ "BC_STRING_MAX", 0, 0 },
#endif
#ifdef _SC_CHILD_MAX
{ "CHILD_MAX", _SC_CHILD_MAX, 1 },
#else
{ "CHILD_MAX", 0, 0 },
#endif
#ifdef _SC_CLK_TCK
{ "CLK_TCK", _SC_CLK_TCK, 1 },
#else
{ "CLK_TCK", 0, 0 },
#endif
#ifdef _SC_COLL_WEIGHTS_MAX
{ "COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX, 1 },
#else
{ "COLL_WEIGHTS_MAX", 0, 0 },
#endif
#ifdef _SC_DELAYTIMER_MAX
{ "DELAYTIMER_MAX", _SC_DELAYTIMER_MAX, 1 },
#else
{ "DELAYTIMER_MAX", 0, 0 },
#endif
#ifdef _SC_EXPR_NEST_MAX
{ "EXPR_NEST_MAX", _SC_EXPR_NEST_MAX, 1 },
#else
{ "EXPR_NEST_MAX", 0, 0 },
#endif
#ifdef _SC_GETGR_R_SIZE_MAX
{ "GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX, 1 },
#else
{ "GETGR_R_SIZE_MAX", 0, 0 },
#endif
#ifdef _SC_GETPW_R_SIZE_MAX
{ "GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX, 1 },
#else
{ "GETPW_R_SIZE_MAX", 0, 0 },
#endif
#ifdef _SC_HOST_NAME_MAX
{ "HOST_NAME_MAX", _SC_HOST_NAME_MAX, 1 },
#else
{ "HOST_NAME_MAX", 0, 0 },
#endif
#ifdef _SC_IOV_MAX
{ "IOV_MAX", _SC_IOV_MAX, 1 },
#else
{ "IOV_MAX", 0, 0 },
#endif
#ifdef _SC_LINE_MAX
{ "LINE_MAX", _SC_LINE_MAX, 1 },
#else
{ "LINE_MAX", 0, 0 },
#endif
#ifdef _SC_LOGIN_NAME_MAX
{ "LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX, 1 },
#else
{ "LOGIN_NAME_MAX", 0, 0 },
#endif
#ifdef _SC_MQ_OPEN_MAX
{ "MQ_OPEN_MAX", _SC_MQ_OPEN_MAX, 1 },
#else
{ "MQ_OPEN_MAX", 0, 0 },
#endif
#ifdef _SC_MQ_PRIO_MAX
{ "MQ_PRIO_MAX", _SC_MQ_PRIO_MAX, 1 },
#else
{ "MQ_PRIO_MAX", 0, 0 },
#endif
#ifdef _SC_NGROUPS_MAX
{ "NGROUPS_MAX", _SC_NGROUPS_MAX, 1 },
#else
{ "NGROUPS_MAX", 0, 0 },
#endif
#ifdef _SC_NPROCESSORS_CONF
{ "NPROCESSORS_CONF", _SC_NPROCESSORS_CONF, 1 },
#else
{ "NPROCESSORS_CONF", 0, 0 },
#endif
#ifdef _SC_NPROCESSORS_ONLN
{ "NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN, 1 },
#else
{ "NPROCESSORS_ONLN", 0, 0 },
#endif
#ifdef _SC_OPEN_MAX
{ "OPEN_MAX", _SC_OPEN_MAX, 1 },
#else
{ "OPEN_MAX", 0, 0 },
#endif
#ifdef _SC_PAGESIZE
{ "PAGESIZE", _SC_PAGESIZE, 1 },
#else
{ "PAGESIZE", 0, 0 },
#endif
#ifdef _SC_PAGESIZE
{ "PAGE_SIZE", _SC_PAGESIZE, 1 },
#else
{ "PAGE_SIZE", 0, 0 },
#endif
#ifdef _SC_PASS_MAX
{ "PASS_MAX", _SC_PASS_MAX, 1 },
#else
{ "PASS_MAX", 0, 0 },
#endif
#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS
{ "PTHREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS, 1 },
#else
{ "PTHREAD_DESTRUCTOR_ITERATIONS", 0, 0 },
#endif
#ifdef _SC_THREAD_KEYS_MAX
{ "PTHREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX, 1 },
#else
{ "PTHREAD_KEYS_MAX", 0, 0 },
#endif
#ifdef _SC_THREAD_STACK_MIN
{ "PTHREAD_STACK_MIN", _SC_THREAD_STACK_MIN, 1 },
#else
{ "PTHREAD_STACK_MIN", 0, 0 },
#endif
#ifdef _SC_THREAD_THREADS_MAX
{ "PTHREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX, 1 },
#else
{ "PTHREAD_THREADS_MAX", 0, 0 },
#endif
#ifdef _SC_RE_DUP_MAX
{ "RE_DUP_MAX", _SC_RE_DUP_MAX, 1 },
#else
{ "RE_DUP_MAX", 0, 0 },
#endif
#ifdef _SC_RTSIG_MAX
{ "RTSIG_MAX", _SC_RTSIG_MAX, 1 },
#else
{ "RTSIG_MAX", 0, 0 },
#endif
#ifdef _SC_SEM_NSEMS_MAX
{ "SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX, 1 },
#else
{ "SEM_NSEMS_MAX", 0, 0 },
#endif
#ifdef _SC_SEM_VALUE_MAX
{ "SEM_VALUE_MAX", _SC_SEM_VALUE_MAX, 1 },
#else
{ "SEM_VALUE_MAX", 0, 0 },
#endif
#ifdef _SC_SIGQUEUE_MAX
{ "SIGQUEUE_MAX", _SC_SIGQUEUE_MAX, 1 },
#else
{ "SIGQUEUE_MAX", 0, 0 },
#endif
#ifdef _SC_STREAM_MAX
{ "STREAM_MAX", _SC_STREAM_MAX, 1 },
#else
{ "STREAM_MAX", 0, 0 },
#endif
#ifdef _SC_SYMLOOP_MAX
{ "SYMLOOP_MAX", _SC_SYMLOOP_MAX, 1 },
#else
{ "SYMLOOP_MAX", 0, 0 },
#endif
#ifdef _SC_TIMER_MAX
{ "TIMER_MAX", _SC_TIMER_MAX, 1 },
#else
{ "TIMER_MAX", 0, 0 },
#endif
#ifdef _SC_TTY_NAME_MAX
{ "TTY_NAME_MAX", _SC_TTY_NAME_MAX, 1 },
#else
{ "TTY_NAME_MAX", 0, 0 },
#endif
#ifdef _SC_TZNAME_MAX
{ "TZNAME_MAX", _SC_TZNAME_MAX, 1 },
#else
{ "TZNAME_MAX", 0, 0 },
#endif
#ifdef _SC_2_CHAR_TERM
{ "_POSIX2_CHAR_TERM", _SC_2_CHAR_TERM, 1 },
#else
{ "_POSIX2_CHAR_TERM", 0, 0 },
#endif
#ifdef _SC_2_C_BIND
{ "_POSIX2_C_BIND", _SC_2_C_BIND, 1 },
#else
{ "_POSIX2_C_BIND", 0, 0 },
#endif
#ifdef _SC_2_C_DEV
{ "_POSIX2_C_DEV", _SC_2_C_DEV, 1 },
#else
{ "_POSIX2_C_DEV", 0, 0 },
#endif
#ifdef _SC_2_C_VERSION
{ "_POSIX2_C_VERSION", _SC_2_C_VERSION, 1 },
#else
{ "_POSIX2_C_VERSION", 0, 0 },
#endif
#ifdef _SC_2_FORT_DEV
{ "_POSIX2_FORT_DEV", _SC_2_FORT_DEV, 1 },
#else
{ "_POSIX2_FORT_DEV", 0, 0 },
#endif
#ifdef _SC_2_FORT_RUN
{ "_POSIX2_FORT_RUN", _SC_2_FORT_RUN, 1 },
#else
{ "_POSIX2_FORT_RUN", 0, 0 },
#endif
#ifdef _SC_2_LOCALEDEF
{ "_POSIX2_LOCALEDEF", _SC_2_LOCALEDEF, 1 },
#else
{ "_POSIX2_LOCALEDEF", 0, 0 },
#endif
#ifdef _SC_PBS
{ "_POSIX2_PBS", _SC_PBS, 1 },
#else
{ "_POSIX2_PBS", 0, 0 },
#endif
#ifdef _SC_PBS_ACCOUNTING
{ "_POSIX2_PBS_ACCOUNTING", _SC_PBS_ACCOUNTING, 1 },
#else
{ "_POSIX2_PBS_ACCOUNTING", 0, 0 },
#endif
#ifdef _SC_PBS_CHECKPOINT
{ "_POSIX2_PBS_CHECKPOINT", _SC_PBS_CHECKPOINT, 1 },
#else
{ "_POSIX2_PBS_CHECKPOINT", 0, 0 },
#endif
#ifdef _SC_PBS_LOCATE
{ "_POSIX2_PBS_LOCATE", _SC_PBS_LOCATE, 1 },
#else
{ "_POSIX2_PBS_LOCATE", 0, 0 },
#endif
#ifdef _SC_PBS_MESSAGE
{ "_POSIX2_PBS_MESSAGE", _SC_PBS_MESSAGE, 1 },
#else
{ "_POSIX2_PBS_MESSAGE", 0, 0 },
#endif
#ifdef _SC_PBS_TRACK
{ "_POSIX2_PBS_TRACK", _SC_PBS_TRACK, 1 },
#else
{ "_POSIX2_PBS_TRACK", 0, 0 },
#endif
#ifdef _SC_2_SW_DEV
{ "_POSIX2_SW_DEV", _SC_2_SW_DEV, 1 },
#else
{ "_POSIX2_SW_DEV", 0, 0 },
#endif
#ifdef _SC_2_UPE
{ "_POSIX2_UPE", _SC_2_UPE, 1 },
#else
{ "_POSIX2_UPE", 0, 0 },
#endif
#ifdef _SC_2_VERSION
{ "_POSIX2_VERSION", _SC_2_VERSION, 1 },
#else
{ "_POSIX2_VERSION", 0, 0 },
#endif
#ifdef _SC_ADVISORY_INFO
{ "_POSIX_ADVISORY_INFO", _SC_ADVISORY_INFO, 1 },
#else
{ "_POSIX_ADVISORY_INFO", 0, 0 },
#endif
#ifdef _SC_ASYNCHRONOUS_IO
{ "_POSIX_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO, 1 },
#else
{ "_POSIX_ASYNCHRONOUS_IO", 0, 0 },
#endif
#ifdef _SC_BARRIERS
{ "_POSIX_BARRIERS", _SC_BARRIERS, 1 },
#else
{ "_POSIX_BARRIERS", 0, 0 },
#endif
#ifdef _SC_CLOCK_SELECTION
{ "_POSIX_CLOCK_SELECTION", _SC_CLOCK_SELECTION, 1 },
#else
{ "_POSIX_CLOCK_SELECTION", 0, 0 },
#endif
#ifdef _SC_CPUTIME
{ "_POSIX_CPUTIME", _SC_CPUTIME, 1 },
#else
{ "_POSIX_CPUTIME", 0, 0 },
#endif
#ifdef _SC_FILE_LOCKING
{ "_POSIX_FILE_LOCKING", _SC_FILE_LOCKING, 1 },
#else
{ "_POSIX_FILE_LOCKING", 0, 0 },
#endif
#ifdef _SC_FSYNC
{ "_POSIX_FSYNC", _SC_FSYNC, 1 },
#else
{ "_POSIX_FSYNC", 0, 0 },
#endif
#ifdef _SC_IPV6
{ "_POSIX_IPV6", _SC_IPV6, 1 },
#else
{ "_POSIX_IPV6", 0, 0 },
#endif
#ifdef _SC_JOB_CONTROL
{ "_POSIX_JOB_CONTROL", _SC_JOB_CONTROL, 1 },
#else
{ "_POSIX_JOB_CONTROL", 0, 0 },
#endif
#ifdef _SC_MAPPED_FILES
{ "_POSIX_MAPPED_FILES", _SC_MAPPED_FILES, 1 },
#else
{ "_POSIX_MAPPED_FILES", 0, 0 },
#endif
#ifdef _SC_MEMLOCK
{ "_POSIX_MEMLOCK", _SC_MEMLOCK, 1 },
#else
{ "_POSIX_MEMLOCK", 0, 0 },
#endif
#ifdef _SC_MEMLOCK_RANGE
{ "_POSIX_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE, 1 },
#else
{ "_POSIX_MEMLOCK_RANGE", 0, 0 },
#endif
#ifdef _SC_MEMORY_PROTECTION
{ "_POSIX_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION, 1 },
#else
{ "_POSIX_MEMORY_PROTECTION", 0, 0 },
#endif
#ifdef _SC_MESSAGE_PASSING
{ "_POSIX_MESSAGE_PASSING", _SC_MESSAGE_PASSING, 1 },
#else
{ "_POSIX_MESSAGE_PASSING", 0, 0 },
#endif
#ifdef _SC_MONOTONIC_CLOCK
{ "_POSIX_MONOTONIC_CLOCK", _SC_MONOTONIC_CLOCK, 1 },
#else
{ "_POSIX_MONOTONIC_CLOCK", 0, 0 },
#endif
#ifdef _SC_PRIORITIZED_IO
{ "_POSIX_PRIORITIZED_IO", _SC_PRIORITIZED_IO, 1 },
#else
{ "_POSIX_PRIORITIZED_IO", 0, 0 },
#endif
#ifdef _SC_PRIORITY_SCHEDULING
{ "_POSIX_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING, 1 },
#else
{ "_POSIX_PRIORITY_SCHEDULING", 0, 0 },
#endif
#ifdef _SC_RAW_SOCKETS
{ "_POSIX_RAW_SOCKETS", _SC_RAW_SOCKETS, 1 },
#else
{ "_POSIX_RAW_SOCKETS", 0, 0 },
#endif
#ifdef _SC_READER_WRITER_LOCKS
{ "_POSIX_READER_WRITER_LOCKS", _SC_READER_WRITER_LOCKS, 1 },
#else
{ "_POSIX_READER_WRITER_LOCKS", 0, 0 },
#endif
#ifdef _SC_REALTIME_SIGNALS
{ "_POSIX_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS, 1 },
#else
{ "_POSIX_REALTIME_SIGNALS", 0, 0 },
#endif
#ifdef _SC_REGEXP
{ "_POSIX_REGEXP", _SC_REGEXP, 1 },
#else
{ "_POSIX_REGEXP", 0, 0 },
#endif
#ifdef _SC_SAVED_IDS
{ "_POSIX_SAVED_IDS", _SC_SAVED_IDS, 1 },
#else
{ "_POSIX_SAVED_IDS", 0, 0 },
#endif
#ifdef _SC_SEMAPHORES
{ "_POSIX_SEMAPHORES", _SC_SEMAPHORES, 1 },
#else
{ "_POSIX_SEMAPHORES", 0, 0 },
#endif
#ifdef _SC_SHARED_MEMORY_OBJECTS
{ "_POSIX_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS, 1 },
#else
{ "_POSIX_SHARED_MEMORY_OBJECTS", 0, 0 },
#endif
#ifdef _SC_SHELL
{ "_POSIX_SHELL", _SC_SHELL, 1 },
#else
{ "_POSIX_SHELL", 0, 0 },
#endif
#ifdef _SC_SPAWN
{ "_POSIX_SPAWN", _SC_SPAWN, 1 },
#else
{ "_POSIX_SPAWN", 0, 0 },
#endif
#ifdef _SC_SPIN_LOCKS
{ "_POSIX_SPIN_LOCKS", _SC_SPIN_LOCKS, 1 },
#else
{ "_POSIX_SPIN_LOCKS", 0, 0 },
#endif
#ifdef _SC_SPORADIC_SERVER
{ "_POSIX_SPORADIC_SERVER", _SC_SPORADIC_SERVER, 1 },
#else
{ "_POSIX_SPORADIC_SERVER", 0, 0 },
#endif
#ifdef _SC_SS_REPL_MAX
{ "_POSIX_SS_REPL_MAX", _SC_SS_REPL_MAX, 1 },
#else
{ "_POSIX_SS_REPL_MAX", 0, 0 },
#endif
#ifdef _SC_SYNCHRONIZED_IO
{ "_POSIX_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO, 1 },
#else
{ "_POSIX_SYNCHRONIZED_IO", 0, 0 },
#endif
#ifdef _SC_THREADS
{ "_POSIX_THREADS", _SC_THREADS, 1 },
#else
{ "_POSIX_THREADS", 0, 0 },
#endif
#ifdef _SC_THREAD_ATTR_STACKADDR
{ "_POSIX_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR, 1 },
#else
{ "_POSIX_THREAD_ATTR_STACKADDR", 0, 0 },
#endif
#ifdef _SC_THREAD_ATTR_STACKSIZE
{ "_POSIX_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE, 1 },
#else
{ "_POSIX_THREAD_ATTR_STACKSIZE", 0, 0 },
#endif
#ifdef _SC_THREAD_CPUTIME
{ "_POSIX_THREAD_CPUTIME", _SC_THREAD_CPUTIME, 1 },
#else
{ "_POSIX_THREAD_CPUTIME", 0, 0 },
#endif
#ifdef _SC_THREAD_PRIORITY_SCHEDULING
{ "_POSIX_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING, 1 },
#else
{ "_POSIX_THREAD_PRIORITY_SCHEDULING", 0, 0 },
#endif
#ifdef _SC_THREAD_PRIO_INHERIT
{ "_POSIX_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT, 1 },
#else
{ "_POSIX_THREAD_PRIO_INHERIT", 0, 0 },
#endif
#ifdef _SC_THREAD_PRIO_PROTECT
{ "_POSIX_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT, 1 },
#else
{ "_POSIX_THREAD_PRIO_PROTECT", 0, 0 },
#endif
#ifdef _SC_THREAD_PROCESS_SHARED
{ "_POSIX_THREAD_PROCESS_SHARED", _SC_THREAD_PROCESS_SHARED, 1 },
#else
{ "_POSIX_THREAD_PROCESS_SHARED", 0, 0 },
#endif
#ifdef _SC_THREAD_SAFE_FUNCTIONS
{ "_POSIX_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS, 1 },
#else
{ "_POSIX_THREAD_SAFE_FUNCTIONS", 0, 0 },
#endif
#ifdef _SC_THREAD_SPORADIC_SERVER
{ "_POSIX_THREAD_SPORADIC_SERVER", _SC_THREAD_SPORADIC_SERVER, 1 },
#else
{ "_POSIX_THREAD_SPORADIC_SERVER", 0, 0 },
#endif
#ifdef _SC_TIMEOUTS
{ "_POSIX_TIMEOUTS", _SC_TIMEOUTS, 1 },
#else
{ "_POSIX_TIMEOUTS", 0, 0 },
#endif
#ifdef _SC_TIMERS
{ "_POSIX_TIMERS", _SC_TIMERS, 1 },
#else
{ "_POSIX_TIMERS", 0, 0 },
#endif
#ifdef _SC_TRACE
{ "_POSIX_TRACE", _SC_TRACE, 1 },
#else
{ "_POSIX_TRACE", 0, 0 },
#endif
#ifdef _SC_TRACE_EVENT_FILTER
{ "_POSIX_TRACE_EVENT_FILTER", _SC_TRACE_EVENT_FILTER, 1 },
#else
{ "_POSIX_TRACE_EVENT_FILTER", 0, 0 },
#endif
#ifdef _SC_TRACE_EVENT_NAME_MAX
{ "_POSIX_TRACE_EVENT_NAME_MAX", _SC_TRACE_EVENT_NAME_MAX, 1 },
#else
{ "_POSIX_TRACE_EVENT_NAME_MAX", 0, 0 },
#endif
#ifdef _SC_TRACE_INHERIT
{ "_POSIX_TRACE_INHERIT", _SC_TRACE_INHERIT, 1 },
#else
{ "_POSIX_TRACE_INHERIT", 0, 0 },
#endif
#ifdef _SC_TRACE_LOG
{ "_POSIX_TRACE_LOG", _SC_TRACE_LOG, 1 },
#else
{ "_POSIX_TRACE_LOG", 0, 0 },
#endif
#ifdef _SC_TRACE_NAME_MAX
{ "_POSIX_TRACE_NAME_MAX", _SC_TRACE_NAME_MAX, 1 },
#else
{ "_POSIX_TRACE_NAME_MAX", 0, 0 },
#endif
#ifdef _SC_TRACE_SYS_MAX
{ "_POSIX_TRACE_SYS_MAX", _SC_TRACE_SYS_MAX, 1 },
#else
{ "_POSIX_TRACE_SYS_MAX", 0, 0 },
#endif
#ifdef _SC_TRACE_USER_EVENT_MAX
{ "_POSIX_TRACE_USER_EVENT_MAX", _SC_TRACE_USER_EVENT_MAX, 1 },
#else
{ "_POSIX_TRACE_USER_EVENT_MAX", 0, 0 },
#endif
#ifdef _SC_TYPED_MEMORY_OBJECTS
{ "_POSIX_TYPED_MEMORY_OBJECTS", _SC_TYPED_MEMORY_OBJECTS, 1 },
#else
{ "_POSIX_TYPED_MEMORY_OBJECTS", 0, 0 },
#endif
#ifdef _SC_V6_ILP32_OFF32
{ "_POSIX_V6_ILP32_OFF32", _SC_V6_ILP32_OFF32, 1 },
#else
{ "_POSIX_V6_ILP32_OFF32", 0, 0 },
#endif
#ifdef _SC_V6_ILP32_OFFBIG
{ "_POSIX_V6_ILP32_OFFBIG", _SC_V6_ILP32_OFFBIG, 1 },
#else
{ "_POSIX_V6_ILP32_OFFBIG", 0, 0 },
#endif
#ifdef _SC_V6_LP64_OFF64
{ "_POSIX_V6_LP64_OFF64", _SC_V6_LP64_OFF64, 1 },
#else
{ "_POSIX_V6_LP64_OFF64", 0, 0 },
#endif
#ifdef _SC_V6_LPBIG_OFFBIG
{ "_POSIX_V6_LPBIG_OFFBIG", _SC_V6_LPBIG_OFFBIG, 1 },
#else
{ "_POSIX_V6_LPBIG_OFFBIG", 0, 0 },
#endif
#ifdef _SC_VERSION
{ "_POSIX_VERSION", _SC_VERSION, 1 },
#else
{ "_POSIX_VERSION", 0, 0 },
#endif
#ifdef _SC_XOPEN_CRYPT
{ "_XOPEN_CRYPT", _SC_XOPEN_CRYPT, 1 },
#else
{ "_XOPEN_CRYPT", 0, 0 },
#endif
#ifdef _SC_XOPEN_ENH_I18N
{ "_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N, 1 },
#else
{ "_XOPEN_ENH_I18N", 0, 0 },
#endif
#ifdef _SC_XOPEN_LEGACY
{ "_XOPEN_LEGACY", _SC_XOPEN_LEGACY, 1 },
#else
{ "_XOPEN_LEGACY", 0, 0 },
#endif
#ifdef _SC_XOPEN_REALTIME
{ "_XOPEN_REALTIME", _SC_XOPEN_REALTIME, 1 },
#else
{ "_XOPEN_REALTIME", 0, 0 },
#endif
#ifdef _SC_XOPEN_REALTIME_THREADS
{ "_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS, 1 },
#else
{ "_XOPEN_REALTIME_THREADS", 0, 0 },
#endif
#ifdef _SC_XOPEN_SHM
{ "_XOPEN_SHM", _SC_XOPEN_SHM, 1 },
#else
{ "_XOPEN_SHM", 0, 0 },
#endif
#ifdef _SC_XOPEN_STREAMS
{ "_XOPEN_STREAMS", _SC_XOPEN_STREAMS, 1 },
#else
{ "_XOPEN_STREAMS", 0, 0 },
#endif
#ifdef _SC_XOPEN_UNIX
{ "_XOPEN_UNIX", _SC_XOPEN_UNIX, 1 },
#else
{ "_XOPEN_UNIX", 0, 0 },
#endif
#ifdef _SC_XOPEN_VERSION
{ "_XOPEN_VERSION", _SC_XOPEN_VERSION, 1 },
#else
{ "_XOPEN_VERSION", 0, 0 },
#endif
#ifdef _SC_XCU_VERSION
{ "_XOPEN_XCU_VERSION", _SC_XCU_VERSION, 1 },
#else
{ "_XOPEN_XCU_VERSION", 0, 0 },
#endif
{ NULL, 0, 0 }
};
#define NWORDS (sizeof(wordlist)/sizeof(wordlist[0]) - 1)
static const struct map *
in_word_set(const char *word)
{
const struct map *mp;
for (mp = wordlist; mp < &wordlist[NWORDS]; mp++) {
if (strcmp(word, mp->name) == 0)
return (mp);
}
return (NULL);
}
int
find_sysconf(const char *name, int *key)
{
const struct map *rv;
#ifdef APPLE_GETCONF_UNDERSCORE
char *alt;
#endif /* APPLE_GETCONF_UNDERSCORE */
rv = in_word_set(name);
if (rv != NULL) {
if (rv->valid) {
*key = rv->key;
return 1;
}
return -1;
}
#ifdef APPLE_GETCONF_UNDERSCORE
if(*name == '_')
alt = (char *)name + 1;
else {
if((alt = (char *)alloca(strlen(name) + 2)) == NULL)
return 0;
*alt = '_';
strcpy(alt + 1, name);
}
rv = in_word_set(alt);
if (rv != NULL) {
if (rv->valid) {
*key = rv->key;
return 1;
}
return -1;
}
#endif /* APPLE_GETCONF_UNDERSCORE */
return 0;
}

View 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}"

View File

@ -260,7 +260,7 @@ main(int argc, char *argv[])
*/
dogettytab();
#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
if (strncmp(ttyn, _PATH_CONSOLE, sizeof(ttyn)) == 0)
ttyopenmode |= O_POPUP;
#endif

View 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)

View File

@ -14,6 +14,9 @@
#include <libkern/OSAtomic.h>
#include <limits.h>
#include <errno.h>
#include <CoreFoundation/CoreFoundation.h>
#include "panic.h"
#include <IOKit/IOKitLib.h>
#define IO_MODE_SEQ 0
#define IO_MODE_RANDOM 1
@ -25,15 +28,23 @@
#define MAX_THREADS 1000
#define MAX_FILENAME 64
#define MAX_ITERATIONS 10000
#define LATENCY_BIN_SIZE 500
#define LATENCY_BINS 11
#define LATENCY_BIN_SIZE 1000
#define LATENCY_BINS 31
#define LOW_LATENCY_BIN_SIZE 50
#define LOW_LATENCY_BINS 11
#define LOW_LATENCY_BINS 21
#define THROUGHPUT_INTERVAL 5000
#define DEFAULT_FILE_SIZE (262144)
#define BLOCKSIZE 1024
#define MAX_CMD_SIZE 256
#define PG_MASK ~(0xFFF)
#define kIONVMeANS2ControllerString "AppleANS2Controller"
#define kIONVMeControllerString "AppleNVMeController"
typedef enum {
kDefaultDevice = 0,
kNVMeDevice = 1,
kNVMeDeviceANS2 = 2,
} qos_device_type_t;
int burst_count = 10; /* Unit: Number ; Desc.: I/O Burst Count */
int inter_burst_duration = 0; /* Unit: msecs ; Desc.: I/O Inter-Burst Duration (-1: Random value [0,100]) */
@ -47,24 +58,31 @@ int test_duration = 0; /* Unit: secs ; Desc.: Total Test Dura
int io_tier = 0; /* Unit: 0/1/2/3; Desc.: I/O Tier */
int file_size = DEFAULT_FILE_SIZE; /* Unit: pages ; Desc.: File Size in 4096 byte blocks */
int cached_io_flag = 0; /* Unit: 0/1 ; Desc.: I/O Caching behavior (no-cached/cached) */
int io_qos_timeout_ms = 0; /* Unit: msecs ; Desc.: I/O QOS timeout */
char *user_fname;
int user_specified_file = 0;
qos_device_type_t qos_device = 0;
int64_t total_io_count;
int64_t total_io_size;
int64_t total_io_time;
int64_t total_burst_count;
int64_t total_io_count = 0;
int64_t total_io_size = 0;
int64_t total_io_time = 0;
int64_t max_io_time = 0;
int64_t total_burst_count = 0;
int64_t latency_histogram[LATENCY_BINS];
int64_t burst_latency_histogram[LATENCY_BINS];
int64_t low_latency_histogram[LOW_LATENCY_BINS];
int64_t throughput_histogram[MAX_ITERATIONS];
int64_t throughput_index;
CFRunLoopTimerRef runLoopTimer = NULL;
void print_usage(void);
void print_data_percentage(int percent);
void print_data_percentage(double percent);
void print_stats(void);
unsigned int find_io_bin(int64_t latency, int latency_bin_size, int latency_bins);
void signalHandler(int sig);
void assertASP(CFRunLoopTimerRef timer, void *info );
void start_qos_timer(void);
void stop_qos_timer(void);
void perform_io(int fd, char *buf, int size, int type);
void *sync_routine(void *arg);
void *calculate_throughput(void *arg);
@ -72,7 +90,8 @@ void *io_routine(void *arg);
void validate_option(int value, int min, int max, char *option, char *units);
void print_test_setup(int value, char *option, char *units, char *comment);
void setup_process_io_policy(int io_tier);
void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size);
void setup_qos_device(void);
void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count);
void
print_usage(void)
@ -92,9 +111,10 @@ print_usage(void)
printf("-z: (number) File Size in pages (1 page = 4096 bytes) \n");
printf("-n: (string) File name used for tests (the tool would create files if this option is not specified)\n");
printf("-a: (0/1 : Non-cached/Cached) I/O Caching behavior\n");
printf("-q: (msecs) I/O QoS timeout. Time of I/O before drive assert and system panic\n");
}
void print_data_percentage(int percent)
void print_data_percentage(double percent)
{
int count = (int)(round(percent / 5.0));
int spaces = 20 - count;
@ -106,7 +126,7 @@ void print_data_percentage(int percent)
printf("|");
}
void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size)
void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_size, double io_count)
{
double percentage;
char label[MAX_FILENAME];
@ -118,9 +138,9 @@ void print_latency_histogram(int64_t *data, int latency_bins, int latency_bin_si
else
snprintf(label, MAX_FILENAME, "%d - %d usecs", i * latency_bin_size, (i+1) * latency_bin_size);
printf("%25s ", label);
percentage = ((double)data[i] * 100.0) / (double)total_io_count;
print_data_percentage((int)percentage);
printf(" %.2lf%%\n", percentage);
percentage = ((double)data[i] * 100.000000) / io_count;
print_data_percentage(percentage);
printf(" %.6lf%%\n", percentage);
}
printf("\n");
}
@ -135,13 +155,14 @@ void print_stats()
printf("Total I/Os : %lld\n", total_io_count);
printf("Avg. Latency : %.2lf usecs\n", ((double)total_io_time) / ((double)total_io_count));
printf("Max. Latency : %.2lf usecs\n", ((double)max_io_time));
printf("Low Latency Histogram: \n");
print_latency_histogram(low_latency_histogram, LOW_LATENCY_BINS, LOW_LATENCY_BIN_SIZE);
print_latency_histogram(low_latency_histogram, LOW_LATENCY_BINS, LOW_LATENCY_BIN_SIZE, (double)total_io_count);
printf("Latency Histogram: \n");
print_latency_histogram(latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE);
print_latency_histogram(latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_io_count);
printf("Burst Avg. Latency Histogram: \n");
print_latency_histogram(burst_latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE);
print_latency_histogram(burst_latency_histogram, LATENCY_BINS, LATENCY_BIN_SIZE, (double)total_burst_count);
printf("Throughput Timeline: \n");
@ -176,6 +197,94 @@ void signalHandler(int sig)
exit(0);
}
void setup_qos_device(void)
{
kern_return_t status = kIOReturnError;
io_iterator_t iterator = IO_OBJECT_NULL;
if(io_qos_timeout_ms <= 0)
return;
printf ( "*** setup_qos_device *** \n" );
status = IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeANS2ControllerString ), &iterator );
if ( status != kIOReturnSuccess )
return;
if ( iterator != IO_OBJECT_NULL ) {
printf ( "Found NVMe ANS2 Device \n" );
qos_device = kNVMeDeviceANS2;
} else {
status= IOServiceGetMatchingServices ( kIOMasterPortDefault, IOServiceMatching ( kIONVMeControllerString ), &iterator );
if ( status != kIOReturnSuccess )
return;
if ( iterator != IO_OBJECT_NULL ) {
printf ( "Found NVMe Device \n" );
qos_device = kNVMeDevice;
}
else {
printf ( "NVMe Device not found, not setting qos timeout\n" );
qos_device = kDefaultDevice;
}
}
}
void assertASP(CFRunLoopTimerRef timer, void *info )
{
char command [ 1024 ];
if(qos_device == kDefaultDevice)
return;
printf("assertASP. Timeout of IO exceeds = %d msec\n", io_qos_timeout_ms);
// kNVMe_ANS2_Force_Assert_offset = 0x13EC, // GP59
// kNVMe_Force_Assert_Offset = 0x550,
if(qos_device == kNVMeDeviceANS2)
snprintf ( command, sizeof ( command ), "/usr/local/bin/nvmectl-tool.py -a WriteRegister32 $((0x13EC)) 0xFFFF" );
else if(qos_device == kNVMeDevice)
snprintf ( command, sizeof ( command ), "/usr/local/bin/nvmectl-tool.py -a WriteRegister32 $((0x550)) 0xFFFF" );
else
return;
// Assert ASP
printf("Command : %s\n", command);
system(command);
// Panic the system as well
panic("IO time > QoS timeout");
return;
}
void start_qos_timer(void)
{
float timeout_sec;
if(io_qos_timeout_ms <= 0)
return;
timeout_sec = (float)io_qos_timeout_ms/1000;
// Schedule a "timeout" delayed task that checks IO's which take > timeout sec to complete
runLoopTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent()+timeout_sec, 0, 0, 0, assertASP, NULL);
CFRunLoopAddTimer(CFRunLoopGetMain(), runLoopTimer, kCFRunLoopDefaultMode);
}
void stop_qos_timer(void)
{
if(runLoopTimer == NULL)
return;
CFRunLoopTimerInvalidate(runLoopTimer);
CFRunLoopRemoveTimer(CFRunLoopGetMain(), runLoopTimer, kCFRunLoopDefaultMode);
CFRelease(runLoopTimer);
}
void perform_io(int fd, char *buf, int size, int type)
{
@ -249,7 +358,7 @@ void *io_routine(void *arg)
io_thread_id = (int)arg;
if (user_specified_file)
strncpy(test_filename, user_fname, MAX_FILENAME);
strlcpy(test_filename, user_fname, MAX_FILENAME);
else
snprintf(test_filename, MAX_FILENAME, "iosim-%d-%d", (int)getpid(), io_thread_id);
@ -290,13 +399,22 @@ void *io_routine(void *arg)
}
}
start_qos_timer();
gettimeofday(&start_tv, NULL);
perform_io(fd, data, io_size, workload_type);
gettimeofday(&end_tv, NULL);
stop_qos_timer();
OSAtomicIncrement64(&total_io_count);
OSAtomicAdd64(io_size, &total_io_size);
elapsed = ((end_tv.tv_sec - start_tv.tv_sec) * 1000000) + (end_tv.tv_usec - start_tv.tv_usec);
if (elapsed > max_io_time) {
max_io_time = elapsed;
}
OSAtomicAdd64(elapsed, &total_io_time);
OSAtomicIncrement64(&(latency_histogram[find_io_bin(elapsed, LATENCY_BIN_SIZE, LATENCY_BINS)]));
OSAtomicIncrement64(&(low_latency_histogram[find_io_bin(elapsed, LOW_LATENCY_BIN_SIZE, LOW_LATENCY_BINS)]));
@ -373,7 +491,7 @@ int main(int argc, char *argv[])
pthread_t throughput_thread;
char fname[MAX_FILENAME];
while((option = getopt(argc, argv,"hc:i:d:t:f:m:j:s:x:l:z:n:a:")) != -1) {
while((option = getopt(argc, argv,"hc:i:d:t:f:m:j:s:x:l:z:n:a:q:")) != -1) {
switch(option) {
case 'c':
burst_count = atoi(optarg);
@ -430,6 +548,10 @@ int main(int argc, char *argv[])
cached_io_flag = atoi(optarg);
validate_option(cached_io_flag, 0, 1, "I/Os cached/no-cached", "");
break;
case 'q':
io_qos_timeout_ms = atoi(optarg);
validate_option(io_qos_timeout_ms, 0, INT_MAX, "I/O QoS timeout", "msecs");
break;
default:
printf("Unknown option %c\n", option);
print_usage();
@ -450,6 +572,7 @@ int main(int argc, char *argv[])
print_test_setup(test_duration, "Test duration", "secs", "0 indicates tool waits for Ctrl+C");
print_test_setup(io_tier, "I/O Tier", "", 0);
print_test_setup(cached_io_flag, "I/O Caching", "", "0 indicates non-cached I/Os");
print_test_setup(io_qos_timeout_ms, "I/O QoS Threshold Timeout", "msecs", 0);
print_test_setup(0, "File read-aheads", "", "0 indicates read-aheads disabled");
printf("**********************************************************\n");
@ -468,6 +591,8 @@ int main(int argc, char *argv[])
system("purge");
setup_process_io_policy(io_tier);
setup_qos_device();
printf("**********************************************************\n");
printf("Creating threads and generating workload...\n");
@ -493,9 +618,14 @@ int main(int argc, char *argv[])
exit(1);
}
/* All threads are now initialized */
if (test_duration)
alarm(test_duration);
if(io_qos_timeout_ms > 0) {
CFRunLoopRunInMode(kCFRunLoopDefaultMode, (CFTimeInterval)test_duration, false);
alarm(1);
} else {
/* All threads are now initialized */
if (test_duration)
alarm(test_duration);
}
for(i=0; i < thread_count; i++)
pthread_join(thread_list[i], NULL);

View File

@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD: src/usr.sbin/iostat/iostat.8,v 1.20 2001/08/07 13:59:48 ru Exp $
.\" $FreeBSD$
.\"
.\" Copyright (c) 1985, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -38,11 +38,7 @@
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" 3. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
@ -60,7 +56,7 @@
.\"
.\" @(#)iostat.8 8.1 (Berkeley) 6/6/93
.\"
.Dd September 27, 2001
.Dd May 22, 2015
.Dt IOSTAT 8
.Os
.Sh NAME
@ -69,15 +65,16 @@
.Tn I/O
statistics
.Sh SYNOPSIS
.Nm iostat
.Nm
.Op Fl CUdKIoT?\&
.Op Fl c Ar count
.Op Fl n Ar devs
.Op Fl w Ar wait
.Op Ar drives
.Sh DESCRIPTION
.Nm Iostat
displays kernel
The
.Nm
utility displays kernel
.Tn I/O
statistics on terminal, device and cpu operations.
The first statistics that are printed are averaged over the system uptime.
@ -87,16 +84,8 @@ averaged over that time.
.Pp
The options are as follows:
.Bl -tag -width flag
.\" ==========
.It Fl ?\&
Display a usage statement and exit.
.\" ==========
.It Fl C
Display CPU statistics.
This is on by default, unless
.Fl d
is specified.
.\" ==========
.It Fl c
Repeat the display
.Ar count
@ -104,7 +93,11 @@ times.
If no
.Ar wait
interval is specified, the default is 1 second.
.\" ==========
.It Fl C
Display CPU statistics.
This is on by default, unless
.Fl d
is specified.
.It Fl d
Display only device statistics.
If this flag is turned on, only device statistics will be displayed, unless
@ -113,48 +106,42 @@ or
.Fl U
or
.Fl T
is also specfied to enable the display of CPU, load average or TTY statistics.
.\" ==========
is also specified to enable the display of CPU, load average or TTY statistics.
.It Fl I
Display total statstics for a given time period, rather than average
Display total statistics for a given time period, rather than average
statistics for each second during that time period.
.\" ==========
.It Fl K
In the blocks transferred display (-o), display block count in kilobytes rather
then the device native block size.
.\" ==========
.It Fl n
Display up to
.Ar devs
number of devices.
.Nm iostat
will display fewer devices if there aren't
The
.Nm
utility will display fewer devices if there are not
.Ar devs
devices present.
.\" ==========
.It Fl o
Display old-style
.Nm iostat
.Nm
device statistics.
Sectors per second, transfers per second, and miliseconds per seek are
Sectors per second, transfers per second, and milliseconds per seek are
displayed.
If
.Fl I
is specified, total blocks/sectors, total transfers, and
miliseconds per seek are displayed.
.\" ==========
milliseconds per seek are displayed.
.It Fl T
Display TTY statistics.
This is on by default, unless
.Fl d
is specified.
.\" ==========
.It Fl U
Display system load averages.
This is on by default, unless
.Fl d
is specified.
.\" ==========
.It Fl w
Pause
.Ar wait
@ -164,8 +151,9 @@ If no repeat
is specified, the default is infinity.
.El
.Pp
.Nm Iostat
displays its information in the following format:
The
.Nm
utility displays its information in the following format:
.Bl -tag -width flag
.It tty
.Bl -tag -width indent -compact
@ -177,20 +165,24 @@ characters written to terminals
.It devices
Device operations.
The header of the field is the device name and unit number.
.Nm iostat
The
.Nm
utility
will display as many devices as will fit in a standard 80 column screen, or
the maximum number of devices in the system, whichever is smaller.
If
.Fl n
is specified on the command line,
.Nm iostat
.Nm
will display the smaller of the
requested number of devices, and the maximum number of devices in the system.
To force
.Nm iostat
.Nm
to display specific drives, their names may be supplied on the command
line.
.Nm iostat
The
.Nm
utility
will not display more devices than will fit in an 80 column screen, unless
the
.Fl n
@ -198,11 +190,11 @@ argument is given on the command line to specify a maximum number of
devices to display, or the list of specified devices exceeds 80 columns.
If fewer devices are specified on the command line than will fit in an 80
column screen,
.Nm iostat
.Nm
will show only the specified devices.
.Pp
The standard
.Nm iostat
.Nm
device display shows the following statistics:
.Pp
.Bl -tag -width indent -compact
@ -215,7 +207,7 @@ megabytes per second
.El
.Pp
The standard
.Nm iostat
.Nm
device display, with the
.Fl I
flag specified, shows the following statistics:
@ -230,7 +222,7 @@ total number of megabytes transferred
.El
.Pp
The old-style
.Nm iostat
.Nm
display (using
.Fl o )
shows the following statistics:
@ -245,7 +237,7 @@ average milliseconds per transaction
.El
.Pp
The old-style
.Nm iostat
.Nm
display, with the
.Fl I
flag specified, shows the following statistics:
@ -295,16 +287,16 @@ and
.Fl C
flags are given, the TTY and CPU displays will be displayed.
.Sh SEE ALSO
.Xr fstat 1 ,
.Xr netstat 1 ,
.Xr nfsstat 1 ,
.Xr ps 1 ,
.Xr pstat 8
.Xr top 1 ,
.Xr vm_stat 1
.Pp
The sections starting with ``Interpreting system activity'' in
.%T "Installing and Operating 4.3BSD" .
.Sh HISTORY
This version of
.Nm iostat
.Nm
first appeared in
.Nm FreeBSD 3.0 .
.Fx 3.0 .

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2016 Apple Inc. All rights reserved.
* Copyright (c) 1999-2019 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
@ -21,7 +21,9 @@
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1997, 1998, 2000, 2001 Kenneth D. Merry
* All rights reserved.
*
@ -48,7 +50,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: src/usr.sbin/iostat/iostat.c,v 1.22 2001/09/01 07:40:19 kris Exp $
* $FreeBSD$
*/
/*
* Parts of this program are derived from the original FreeBSD iostat
@ -66,10 +68,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
@ -176,7 +174,7 @@ static IONotificationPortRef notifyPort;
/* local function declarations */
static void usage(void);
static void phdr(int signo);
static void do_phdr();
static void do_phdr(void);
static void devstats(int perf_select, long double etime, int havelast);
static void cpustats(void);
static void loadstats(void);

View File

@ -40,6 +40,7 @@
#include <libutil.h>
#include <errno.h>
#include <err.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/param.h>
@ -176,7 +177,7 @@ typedef struct event *event_t;
struct event {
event_t ev_next;
uintptr_t ev_thread;
uint64_t ev_thread;
uint32_t ev_type;
uint64_t ev_timestamp;
};
@ -187,10 +188,10 @@ typedef struct lookup *lookup_t;
struct lookup {
lookup_t lk_next;
uintptr_t lk_thread;
uintptr_t lk_dvp;
long *lk_pathptr;
long lk_pathname[NUMPARMS + 1];
uint64_t lk_thread;
uint64_t lk_dvp;
int64_t *lk_pathptr;
int64_t lk_pathname[NUMPARMS + 1];
};
@ -199,8 +200,8 @@ typedef struct threadmap *threadmap_t;
struct threadmap {
threadmap_t tm_next;
uintptr_t tm_thread;
uintptr_t tm_pthread;
uint64_t tm_thread;
uint64_t tm_pthread;
char tm_command[MAXCOMLEN + 1];
char tm_orig_command[MAXCOMLEN + 1];
};
@ -211,7 +212,7 @@ typedef struct threadrun *threadrun_t;
struct threadrun {
threadrun_t tr_next;
uintptr_t tr_thread;
uint64_t tr_thread;
kd_buf *tr_entry;
uint64_t tr_timestamp;
int tr_priority;
@ -223,7 +224,7 @@ typedef struct thread_entry *thread_entry_t;
struct thread_entry {
thread_entry_t te_next;
uintptr_t te_thread;
uint64_t te_thread;
};
#define HASH_SIZE 1024
@ -326,23 +327,23 @@ const char *sched_reasons[] = {
static double handle_decrementer(kd_buf *, int);
static kd_buf *log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, double i_latency);
static void read_command_map(void);
static void enter_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info);
static void exit_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info);
static void print_entry(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd_note);
static void enter_syscall(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info);
static void exit_syscall(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info);
static void print_entry(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd_note);
static void log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf *kd_note);
static char *find_code(int);
static void pc_to_string(char *pcstring, uintptr_t pc, int max_len, int mode);
static void pc_to_string(char *pcstring, uint64_t pc, int max_len, int mode);
static void getdivisor(void);
static int sample_sc(void);
static void init_code_file(void);
static void do_kernel_nm(void);
static void open_logfile(const char*);
static int binary_search(kern_sym_t *list, int low, int high, uintptr_t addr);
static int binary_search(kern_sym_t *list, int low, int high, uint64_t addr);
static void create_map_entry(uintptr_t, char *);
static void check_for_thread_update(uintptr_t thread, int debugid_base, kd_buf *kbufp, char **command);
static void log_scheduler(kd_buf *kd_start, kd_buf *kd_stop, kd_buf *end_of_sample, int s_priority, double s_latency, uintptr_t thread);
static int check_for_scheduler_latency(int type, uintptr_t *thread, uint64_t now, kd_buf *kd, kd_buf **kd_start, int *priority, double *latency);
static void create_map_entry(uint64_t, char *);
static void check_for_thread_update(uint64_t thread, int debugid_base, kd_buf *kbufp, char **command);
static void log_scheduler(kd_buf *kd_start, kd_buf *kd_stop, kd_buf *end_of_sample, int s_priority, double s_latency, uint64_t thread);
static int check_for_scheduler_latency(int type, uint64_t *thread, uint64_t now, kd_buf *kd, kd_buf **kd_start, int *priority, double *latency);
static void open_rawfile(const char *path);
static void screen_update(FILE *);
@ -365,6 +366,8 @@ quit(char *s)
set_remove();
}
}
endwin();
printf("latency: ");
if (s) {
printf("%s", s);
@ -905,6 +908,12 @@ exit_usage(void)
exit(1);
}
static void
resetscr(void)
{
(void)endwin();
}
int
main(int argc, char *argv[])
{
@ -1029,9 +1038,10 @@ main(int argc, char *argv[])
if (!RAW_flag) {
if (initscr() == NULL) {
printf("Unrecognized TERM type, try vt100\n");
fprintf(stderr, "Unrecognized TERM type, try vt100\n");
exit(1);
}
atexit(resetscr);
clear();
refresh();
@ -1219,7 +1229,7 @@ read_command_map(void)
}
void
create_map_entry(uintptr_t thread, char *command)
create_map_entry(uint64_t thread, char *command)
{
threadmap_t tme;
@ -1242,7 +1252,7 @@ create_map_entry(uintptr_t thread, char *command)
}
static void
delete_thread_entry(uintptr_t thread)
delete_thread_entry(uint64_t thread)
{
threadmap_t tme;
@ -1270,7 +1280,7 @@ delete_thread_entry(uintptr_t thread)
}
static void
find_and_insert_tmp_map_entry(uintptr_t pthread, char *command)
find_and_insert_tmp_map_entry(uint64_t pthread, char *command)
{
threadmap_t tme;
@ -1301,7 +1311,7 @@ find_and_insert_tmp_map_entry(uintptr_t pthread, char *command)
}
static void
create_tmp_map_entry(uintptr_t thread, uintptr_t pthread)
create_tmp_map_entry(uint64_t thread, uint64_t pthread)
{
threadmap_t tme;
@ -1321,7 +1331,7 @@ create_tmp_map_entry(uintptr_t thread, uintptr_t pthread)
}
static threadmap_t
find_thread_entry(uintptr_t thread)
find_thread_entry(uint64_t thread)
{
threadmap_t tme;
@ -1336,7 +1346,7 @@ find_thread_entry(uintptr_t thread)
}
static void
find_thread_name(uintptr_t thread, char **command)
find_thread_name(uint64_t thread, char **command)
{
threadmap_t tme;
@ -1348,7 +1358,7 @@ find_thread_name(uintptr_t thread, char **command)
}
static void
add_thread_entry_to_list(thread_entry_t *list, uintptr_t thread)
add_thread_entry_to_list(thread_entry_t *list, uint64_t thread)
{
thread_entry_t te;
@ -1364,7 +1374,7 @@ add_thread_entry_to_list(thread_entry_t *list, uintptr_t thread)
}
static void
exec_thread_entry(uintptr_t thread, char *command)
exec_thread_entry(uint64_t thread, char *command)
{
threadmap_t tme;
@ -1383,7 +1393,7 @@ exec_thread_entry(uintptr_t thread, char *command)
}
static void
record_thread_entry_for_gc(uintptr_t thread)
record_thread_entry_for_gc(uint64_t thread)
{
add_thread_entry_to_list(&thread_delete_list, thread);
}
@ -1468,7 +1478,7 @@ delete_all_thread_entries(void)
}
static void
insert_run_event(uintptr_t thread, int priority, kd_buf *kd, uint64_t now)
insert_run_event(uint64_t thread, int priority, kd_buf *kd, uint64_t now)
{
threadrun_t trp;
@ -1499,7 +1509,7 @@ insert_run_event(uintptr_t thread, int priority, kd_buf *kd, uint64_t now)
}
static threadrun_t
find_run_event(uintptr_t thread)
find_run_event(uint64_t thread)
{
threadrun_t trp;
int hashid = thread & HASH_MASK;
@ -1513,7 +1523,7 @@ find_run_event(uintptr_t thread)
}
static void
delete_run_event(uintptr_t thread)
delete_run_event(uint64_t thread)
{
threadrun_t trp = 0;
threadrun_t trp_prev;
@ -1571,7 +1581,7 @@ gc_run_events(void)
static void
insert_start_event(uintptr_t thread, int type, uint64_t now)
insert_start_event(uint64_t thread, int type, uint64_t now)
{
event_t evp;
@ -1602,7 +1612,7 @@ insert_start_event(uintptr_t thread, int type, uint64_t now)
static uint64_t
consume_start_event(uintptr_t thread, int type, uint64_t now)
consume_start_event(uint64_t thread, int type, uint64_t now)
{
event_t evp;
event_t evp_prev;
@ -1668,7 +1678,7 @@ gc_start_events(void)
}
static int
thread_in_user_mode(uintptr_t thread, char *command)
thread_in_user_mode(uint64_t thread, char *command)
{
event_t evp;
@ -1687,7 +1697,7 @@ thread_in_user_mode(uintptr_t thread, char *command)
}
static lookup_t
handle_lookup_event(uintptr_t thread, int debugid, kd_buf *kdp)
handle_lookup_event(uint64_t thread, int debugid, kd_buf *kdp)
{
lookup_t lkp;
boolean_t first_record = FALSE;
@ -1744,7 +1754,7 @@ handle_lookup_event(uintptr_t thread, int debugid, kd_buf *kdp)
}
static void
delete_lookup_event(uintptr_t thread, lookup_t lkp_to_delete)
delete_lookup_event(uint64_t thread, lookup_t lkp_to_delete)
{
lookup_t lkp;
lookup_t lkp_prev;
@ -1874,7 +1884,7 @@ sample_sc(void)
for (kd = (kd_buf *)my_buffer; kd < end_of_sample; kd++) {
kd_buf *kd_start;
uintptr_t thread = kd->arg5;
uint64_t thread = kd->arg5;
int type = kd->debugid & DBG_FUNC_MASK;
(void)check_for_thread_update(thread, type, kd, NULL);
@ -1914,7 +1924,7 @@ sample_sc(void)
}
void
enter_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info)
enter_syscall(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info)
{
char *p;
double timestamp;
@ -1939,19 +1949,19 @@ enter_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, u
pc_to_string(&pcstring[0], kd->arg2, 58, mode);
fprintf(fp, "%9.1f %8.1f\t\tINTERRUPT[%2lx] @ %-58.58s %8lx %2d %s\n",
timestamp, delta, kd->arg1, &pcstring[0], thread, cpunum, command);
fprintf(fp, "%9.1f %8.1f\t\tINTERRUPT[%2" PRIx64 "] @ %-58.58s %8" PRIx64 " %2d %s\n",
timestamp, delta, (uint64_t)kd->arg1, &pcstring[0], thread, cpunum, command);
} else if (type == MACH_vmfault) {
fprintf(fp, "%9.1f %8.1f\t\t%-28.28s %8lx %2d %s\n",
fprintf(fp, "%9.1f %8.1f\t\t%-28.28s %8" PRIx64 " %2d %s\n",
timestamp, delta, p, thread, cpunum, command);
} else {
fprintf(fp, "%9.1f %8.1f\t\t%-28.28s %-16lx %-16lx %-16lx %-16lx %8lx %2d %s\n",
timestamp, delta, p, kd->arg1, kd->arg2, kd->arg3, kd->arg4,
fprintf(fp, "%9.1f %8.1f\t\t%-28.28s %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n",
timestamp, delta, p, (uint64_t)kd->arg1, (uint64_t)kd->arg2, (uint64_t)kd->arg3, (uint64_t)kd->arg4,
thread, cpunum, command);
}
} else {
fprintf(fp, "%9.1f %8.1f\t\t%-8x %-16lx %-16lx %-16lx %-16lx %8lx %2d %s\n",
timestamp, delta, type, kd->arg1, kd->arg2, kd->arg3, kd->arg4,
fprintf(fp, "%9.1f %8.1f\t\t%-8x %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n",
timestamp, delta, type, (uint64_t)kd->arg1, (uint64_t)kd->arg2, (uint64_t)kd->arg3, (uint64_t)kd->arg4,
thread, cpunum, command);
}
}
@ -1961,7 +1971,7 @@ enter_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, u
}
void
exit_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info)
exit_syscall(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, int print_info)
{
char *p;
uint64_t user_addr;
@ -1981,28 +1991,28 @@ exit_syscall(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, ui
if ((p = find_code(type))) {
if (type == INTERRUPT) {
fprintf(fp, "INTERRUPT %8lx %2d %s\n", thread, cpunum, command);
fprintf(fp, "INTERRUPT %8" PRIx64 " %2d %s\n", thread, cpunum, command);
} else if (type == MACH_vmfault && kd->arg4 <= DBG_PAGEIND_FAULT) {
user_addr = ((uint64_t)kd->arg1 << 32) | (uint32_t)kd->arg2;
fprintf(fp, "%-28.28s %-10.10s %-16qx %8lx %2d %s\n",
fprintf(fp, "%-28.28s %-10.10s %-16qx %8" PRIx64 " %2d %s\n",
p, fault_name[kd->arg4], user_addr,
thread, cpunum, command);
} else {
fprintf(fp, "%-28.28s %-16lx %-16lx %8lx %2d %s\n",
p, kd->arg1, kd->arg2,
fprintf(fp, "%-28.28s %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n",
p, (uint64_t)kd->arg1, (uint64_t)kd->arg2,
thread, cpunum, command);
}
} else {
fprintf(fp, "%-8x %-16lx %-16lx %8lx %2d %s\n",
type, kd->arg1, kd->arg2,
fprintf(fp, "%-8x %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n",
type, (uint64_t)kd->arg1, (uint64_t)kd->arg2,
thread, cpunum, command);
}
}
}
void
print_entry(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd_note)
print_entry(FILE *fp, kd_buf *kd, uint64_t thread, int type, char *command, uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd_note)
{
char *p;
@ -2021,17 +2031,17 @@ print_entry(FILE *fp, kd_buf *kd, uintptr_t thread, int type, char *command, uin
} else {
fprintf(fp, "%9.1f %8.1f\t\t", timestamp, delta);
}
fprintf(fp, "%-28.28s %-16lx %-16lx %-16lx %-16lx %8lx %2d %s\n",
p, kd->arg1, kd->arg2, kd->arg3, kd->arg4, thread, cpunum, command);
fprintf(fp, "%-28.28s %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n",
p, (uint64_t)kd->arg1, (uint64_t)kd->arg2, (uint64_t)kd->arg3, (uint64_t)kd->arg4, thread, cpunum, command);
} else {
fprintf(fp, "%9.1f %8.1f\t\t%-8x %-16lx %-16lx %-16lx %-16lx %8lx %2d %s\n",
timestamp, delta, type, kd->arg1, kd->arg2, kd->arg3, kd->arg4,
fprintf(fp, "%9.1f %8.1f\t\t%-8x %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %8" PRIx64 " %2d %s\n",
timestamp, delta, type, (uint64_t)kd->arg1, (uint64_t)kd->arg2, (uint64_t)kd->arg3, (uint64_t)kd->arg4,
thread, cpunum, command);
}
}
void
check_for_thread_update(uintptr_t thread, int debugid_base, kd_buf *kbufp, char **command)
check_for_thread_update(uint64_t thread, int debugid_base, kd_buf *kbufp, char **command)
{
if (debugid_base == TRACE_DATA_NEWTHREAD) {
/*
@ -2060,7 +2070,7 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf
{
lookup_t lkp;
int mode;
uintptr_t reason;
uint64_t reason;
char *p;
char *command;
char *command1;
@ -2073,7 +2083,7 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf
double delta;
char joe[32];
uintptr_t thread = kd->arg5;
uint64_t thread = kd->arg5;
int cpunum = CPU_NUMBER(kd);
int debugid = kd->debugid;
int type = kd->debugid & DBG_FUNC_MASK;
@ -2093,28 +2103,28 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf
case CQ_action:
pc_to_string(&pcstring[0], kd->arg1, 84, KERNEL_MODE);
fprintf(log_fp, "%9.1f %8.1f\t\tCQ_action @ %-84.84s %8lx %2d %s\n",
fprintf(log_fp, "%9.1f %8.1f\t\tCQ_action @ %-84.84s %8" PRIx64 " %2d %s\n",
timestamp, delta, &pcstring[0], thread, cpunum, command);
break;
case TES_action:
pc_to_string(&pcstring[0], kd->arg1, 83, KERNEL_MODE);
fprintf(log_fp, "%9.1f %8.1f\t\tTES_action @ %-83.83s %8lx %2d %s\n",
fprintf(log_fp, "%9.1f %8.1f\t\tTES_action @ %-83.83s %8" PRIx64 " %2d %s\n",
timestamp, delta, &pcstring[0], thread, cpunum, command);
break;
case IES_action:
pc_to_string(&pcstring[0], kd->arg1, 83, KERNEL_MODE);
fprintf(log_fp, "%9.1f %8.1f\t\tIES_action @ %-83.83s %8lx %2d %s\n",
fprintf(log_fp, "%9.1f %8.1f\t\tIES_action @ %-83.83s %8" PRIx64 " %2d %s\n",
timestamp, delta, &pcstring[0], thread, cpunum, command);
break;
case IES_filter:
pc_to_string(&pcstring[0], kd->arg1, 83, KERNEL_MODE);
fprintf(log_fp, "%9.1f %8.1f\t\tIES_filter @ %-83.83s %8lx %2d %s\n",
fprintf(log_fp, "%9.1f %8.1f\t\tIES_filter @ %-83.83s %8" PRIx64 " %2d %s\n",
timestamp, delta, &pcstring[0], thread, cpunum, command);
break;
@ -2139,12 +2149,12 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf
pc_to_string(&pcstring[0], kd->arg2, 84, mode);
fprintf(log_fp, "%9.1f %8.1f[%.1f]%s\tDECR_TRAP @ %-84.84s %8lx %2d %s\n",
fprintf(log_fp, "%9.1f %8.1f[%.1f]%s\tDECR_TRAP @ %-84.84s %8" PRIx64 " %2d %s\n",
timestamp, delta, i_latency, p, &pcstring[0], thread, cpunum, command);
break;
case DECR_SET:
fprintf(log_fp, "%9.1f %8.1f[%.1f] \t%-28.28s %8lx %2d %s\n",
fprintf(log_fp, "%9.1f %8.1f[%.1f] \t%-28.28s %8" PRIx64 " %2d %s\n",
timestamp, delta, (double)kd->arg1/divisor, "DECR_SET", thread, cpunum, command);
break;
@ -2155,7 +2165,7 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf
if (command1 == EMPTYSTRING) {
command1 = command_buf;
sprintf(command1, "%-8lx", kd->arg2);
sprintf(command1, "%-8" PRIx64, (uint64_t)kd->arg2);
}
if (thread_in_user_mode(kd->arg2, command1)) {
p = "U";
@ -2172,12 +2182,12 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf
}
if (sched_reason[0] == '?') {
sprintf(joe, "%lx", reason);
sprintf(joe, "%" PRIx64, reason);
sched_reason = joe;
}
sprintf(sched_info, "%16.16s @ pri %3lu --> %16.16s @ pri %3lu%s", command, kd->arg3, command1, kd->arg4, p);
sprintf(sched_info, "%16.16s @ pri %3" PRIu64 " --> %16.16s @ pri %3" PRIu64 "%s", command, (uint64_t)kd->arg3, command1, (uint64_t)kd->arg4, p);
fprintf(log_fp, "%9.1f %8.1f\t\t%-10.10s[%s] %s %8lx %2d\n",
fprintf(log_fp, "%9.1f %8.1f\t\t%-10.10s[%s] %s %8" PRIx64 " %2d\n",
timestamp, delta, "MACH_SCHED", sched_reason, sched_info, thread, cpunum);
break;
@ -2195,7 +2205,7 @@ log_info(uint64_t now, uint64_t idelta, uint64_t start_bias, kd_buf *kd, kd_buf
clen = 0;
}
fprintf(log_fp, "%9.1f %8.1f\t\t%-14.14s %-59s %-16lx %8lx %2d %s\n",
fprintf(log_fp, "%9.1f %8.1f\t\t%-14.14s %-59s %-16" PRIx64 " %8" PRIx64 " %2d %s\n",
timestamp, delta, "VFS_LOOKUP",
&p[clen], lkp->lk_dvp, thread, cpunum, command);
@ -2250,7 +2260,7 @@ log_range(kd_buf *kd_buffer, kd_buf *kd_start, kd_buf *kd_stop, kd_buf *kd_note,
last_timestamp = now;
} else {
int debugid = kd->debugid;
uintptr_t thread = kd->arg5;
uint64_t thread = kd->arg5;
int type = kd->debugid & DBG_FUNC_MASK;
if ((type >> 24) == DBG_TRACE) {
@ -2282,7 +2292,7 @@ log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, double i_
double sample_timestamp;
char buf1[128];
uintptr_t thread = kd_beg->arg5;
uint64_t thread = kd_beg->arg5;
int cpunum = CPU_NUMBER(kd_end);
for (kd_count = 0, kd_start = kd_beg - 1; (kd_start >= (kd_buf *)my_buffer); kd_start--, kd_count++) {
@ -2347,7 +2357,7 @@ log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, double i_
void
log_scheduler(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, int s_priority, double s_latency, uintptr_t thread)
log_scheduler(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, int s_priority, double s_latency, uint64_t thread)
{
kd_buf *kd_start, *kd_stop;
uint64_t now;
@ -2399,7 +2409,7 @@ log_scheduler(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, int s_prior
}
int
check_for_scheduler_latency(int type, uintptr_t *thread, uint64_t now, kd_buf *kd, kd_buf **kd_start, int *priority, double *latency)
check_for_scheduler_latency(int type, uint64_t *thread, uint64_t now, kd_buf *kd, kd_buf **kd_start, int *priority, double *latency)
{
int found_latency = 0;
@ -2583,7 +2593,7 @@ do_kernel_nm(void)
/*
* Build the nm command and create a tmp file with the output
*/
sprintf (tmpstr, "/usr/bin/nm -f -n -s __TEXT __text %s > %s",
sprintf (tmpstr, "/usr/bin/nm -n %s -s __TEXT __text > %s",
kernelpath, tmp_nm_file);
system(tmpstr);
@ -2679,19 +2689,19 @@ do_kernel_nm(void)
}
void
pc_to_string(char *pcstring, uintptr_t pc, int max_len, int mode)
pc_to_string(char *pcstring, uint64_t pc, int max_len, int mode)
{
int ret;
size_t len;
if (mode == USER_MODE) {
sprintf(pcstring, "%-16lx [usermode addr]", pc);
sprintf(pcstring, "%-16" PRIx64 " [usermode addr]", pc);
return;
}
ret = binary_search(kern_sym_tbl, 0, kern_sym_count-1, pc);
if (ret == -1 || kern_sym_tbl[ret].k_sym_name == NULL) {
sprintf(pcstring, "%-16lx", pc);
sprintf(pcstring, "%-16" PRIx64, pc);
return;
}
if ((len = kern_sym_tbl[ret].k_sym_len) > (max_len - 8)) {
@ -2700,7 +2710,7 @@ pc_to_string(char *pcstring, uintptr_t pc, int max_len, int mode)
memcpy(pcstring, kern_sym_tbl[ret].k_sym_name, len);
sprintf(&pcstring[len], "+0x%-5lx", pc - (uintptr_t)kern_sym_tbl[ret].k_sym_addr);
sprintf(&pcstring[len], "+0x%-5" PRIx64, pc - (uint64_t)kern_sym_tbl[ret].k_sym_addr);
}
@ -2708,7 +2718,7 @@ pc_to_string(char *pcstring, uintptr_t pc, int max_len, int mode)
* Return -1 if not found, else return index
*/
int
binary_search(kern_sym_t *list, int low, int high, uintptr_t addr)
binary_search(kern_sym_t *list, int low, int high, uint64_t addr)
{
int mid;
@ -2721,13 +2731,13 @@ binary_search(kern_sym_t *list, int low, int high, uintptr_t addr)
}
if (low + 1 == high) {
if ((uintptr_t)list[low].k_sym_addr <= addr && addr < (uintptr_t)list[high].k_sym_addr) {
if ((uint64_t)list[low].k_sym_addr <= addr && addr < (uint64_t)list[high].k_sym_addr) {
/*
* We have a range match
*/
return low;
}
if ((uintptr_t)list[high].k_sym_addr <= addr) {
if ((uint64_t)list[high].k_sym_addr <= addr) {
return high;
}
/*
@ -2737,7 +2747,7 @@ binary_search(kern_sym_t *list, int low, int high, uintptr_t addr)
}
mid = (low + high) / 2;
if (addr < (uintptr_t)list[mid].k_sym_addr) {
if (addr < (uint64_t)list[mid].k_sym_addr) {
return binary_search(list, low, mid, addr);
}

View File

@ -671,7 +671,7 @@ main(int argc, char *argv[])
bail(SLEEP_EXIT, 1);
}
#if defined(__APPLE__) && TARGET_OS_EMBEDDED
#if defined(__APPLE__) && (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
/* on embedded, allow a shell to live in /private/var/personalized_debug/bin/sh */
#define _PATH_DEBUGSHELL "/private/var/personalized_debug/bin/sh"
if (stat(pwd->pw_shell, &st) != 0) {
@ -1235,12 +1235,16 @@ getloginname(void)
err(1, "malloc()");
do {
(void)printf("%s", prompt);
for (p = nbuf; (ch = getchar()) != '\n'; ) {
if (ch == EOF) {
badlogin(username);
bail(NO_SLEEP_EXIT, 0);
}
if (p < nbuf + MAXLOGNAME - 1)
/* rdar://43101375 login process on 2018 hardware is blocked forever waiting on new line char
* The carriage return char is added to the termination condition of the
* for loop because for some reason, '\r' is returned by getchar() on M9 hardware.
*/
for (p = nbuf; (((ch = getchar()) != '\n') && (ch != '\r')); ) {
if (ch == EOF) {
badlogin(username);
bail(NO_SLEEP_EXIT, 0);
}
if (p < nbuf + MAXLOGNAME - 1)
*p++ = ch;
}
} while (p == nbuf);

View File

@ -24,6 +24,8 @@
#ifndef _LSKQ_COMMON_H_
#define _LSKQ_COMMON_H_
#include <stdint.h>
/*
* This file must be kept in sync with xnu headers
*/
@ -31,25 +33,53 @@
/*
* bsd/sys/event.h
*/
#define KN_ACTIVE 0x01
#define KN_QUEUED 0x02
#define KN_DISABLED 0x04
#define KN_DROPPING 0x08
#define KN_USEWAIT 0x10
#define KN_ATTACHING 0x20
#define KN_STAYQUEUED 0x40
#define KN_DEFERDROP 0x80
#define KN_TOUCH 0x100
__options_decl(kn_status_t, uint16_t /* 12 bits really */, {
KN_ACTIVE = 0x001, /* event has been triggered */
KN_QUEUED = 0x002, /* event is on queue */
KN_DISABLED = 0x004, /* event is disabled */
KN_DROPPING = 0x008, /* knote is being dropped */
KN_LOCKED = 0x010, /* knote is locked (kq_knlocks) */
KN_POSTING = 0x020, /* f_event() in flight */
KN_STAYACTIVE = 0x040, /* force event to stay active */
KN_DEFERDELETE = 0x080, /* defer delete until re-enabled */
KN_MERGE_QOS = 0x100, /* f_event() / f_* ran concurrently and overrides must merge */
KN_REQVANISH = 0x200, /* requested EV_VANISH */
KN_VANISHED = 0x400, /* has vanished */
KN_SUPPRESSED = 0x800, /* event is suppressed during delivery */
});
/*
* bsd/sys/eventvar.h
*/
#define KQ_SEL 0x01
#define KQ_SLEEP 0x02
#define KQ_KEV32 0x08
#define KQ_KEV64 0x10
#define KQ_KEV_QOS 0x20
__options_decl(kq_state_t, uint16_t, {
KQ_SEL = 0x0001, /* select was recorded for kq */
KQ_SLEEP = 0x0002, /* thread is waiting for events */
KQ_PROCWAIT = 0x0004, /* thread waiting for processing */
KQ_KEV32 = 0x0008, /* kq is used with 32-bit events */
KQ_KEV64 = 0x0010, /* kq is used with 64-bit events */
KQ_KEV_QOS = 0x0020, /* kq events carry QoS info */
KQ_WORKQ = 0x0040, /* KQ is bound to process workq */
KQ_WORKLOOP = 0x0080, /* KQ is part of a workloop */
KQ_PROCESSING = 0x0100, /* KQ is being processed */
KQ_DRAIN = 0x0200, /* kq is draining */
KQ_WAKEUP = 0x0400, /* kq awakened while processing */
KQ_DYNAMIC = 0x0800, /* kqueue is dynamically managed */
KQ_R2K_ARMED = 0x1000, /* ast notification armed */
KQ_HAS_TURNSTILE = 0x2000, /* this kqueue has a turnstile */
});
/*
* bsd/pthread/workqueue_internal.h
*/
__enum_decl(workq_tr_state_t, uint8_t, {
WORKQ_TR_STATE_IDLE = 0, /* request isn't in flight */
WORKQ_TR_STATE_NEW = 1, /* request is being initiated */
WORKQ_TR_STATE_QUEUED = 2, /* request is being queued */
WORKQ_TR_STATE_CANCELED = 3, /* request is canceled */
WORKQ_TR_STATE_BINDING = 4, /* request is preposted for bind */
WORKQ_TR_STATE_BOUND = 5, /* request is bound to a thread */
});
/*
* bsd/sys/signal.h
@ -110,6 +140,9 @@ filt_strs[] = {
"VM",
"SOCK",
"MEMSTATUS",
"EXCEPT",
"CHANNEL",
"WORKLOOP",
};
/*
@ -125,6 +158,10 @@ fdtype_strs[] = {
"KQUEUE",
"PIPE",
"FSEVENTS",
"ATALK",
"POLICY",
"CHANNEL",
"NEXUS",
};
#endif /* _LSKQ_COMMON_H_ */

View File

@ -113,10 +113,10 @@ NOTE_SIGNAL
NOTE_REAP
.Pp
.It EVFILT_TIMER:
.It Sy s u n
NOTE_SECONDS, NOTE_USECONDS, NOTE_NSECONDS
.It Sy a
NOTE_ABSOLUTE
.It Sy s u n m
NOTE_SECONDS, NOTE_USECONDS, NOTE_NSECONDS, NOTE_MACHTIME
.It Sy a A
NOTE_ABSOLUTE, NOTE_MACH_CONTINUOUS_TIME
.It Sy c
NOTE_CRITICAL
.It Sy b
@ -131,6 +131,18 @@ NOTE_TRIGGER
NOTE_FFAND
.It Sy o
NOTE_FFOR
.Pp
.It EVFILT_WORKLOOP:
.It Sy t w i
NOTE_WL_THREAD_REQUEST, NOTE_WL_SYNC_WAIT, NOTE_WL_SYNC_IPC
.It Sy W
NOTE_WL_SYNC_WAKE
.It Sy q
NOTE_WL_UPDATE_QOS
.It Sy o
NOTE_WL_DISCOVER_OWNER
.It Sy e
NOTE_WL_IGNORE_ESTALE
.El
.It flags
kevent generic flags bitmask.
@ -172,21 +184,30 @@ KN_ACTIVE (event has triggered)
.It Sy q
KN_QUEUED (event has been added to the active list)
.It Sy d
KN_DISABLED
KN_DISABLED (knote is disabled)
.It Sy p
KN_SUPPRESSED (event delivery is in flight)
.It Sy s
KN_STAYQUEUED (event is marked as always-enqueued on the active list)
KN_STAYACTIVE (event is marked as always-enqueued on the active list)
.Pp
.It Sy o
KN_DROPPING
.It Sy u
KN_USEWAIT
.It Sy c
KN_ATTACHING
.It Sy f
KN_DEFERDROP
.It Sy t
KN_TOUCH
.It Sy d
KN_DROPPING (knote is about to be dropped)
.It Sy l
KN_LOCKED (knote is locked)
.It Sy P
KN_POSTING (knote is being posted)
.It Sy m
KN_MERGE_QOS (knote is in override saturating mode)
.Pp
.It Sy D
KN_DEFERDELETE (knote is waiting for deferred-delete ack)
.It Sy v
KN_REQVANISH
.It Sy n
KN_VANISHED
.El
.It qos
The QoS requested for the knote.
.It data
Filter-specific data.
.El

View File

@ -21,6 +21,7 @@
* @APPLE_LICENSE_HEADER_END@
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@ -32,8 +33,14 @@
#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"
@ -125,25 +132,38 @@ fflags_build(struct kevent_extinfo *info, char *str, int len)
case EVFILT_TIMER: {
snprintf(str, len, "%c%c%c%c%c ",
(ff & NOTE_SECONDS) ? 's' :
(ff & NOTE_USECONDS) ? 'u' :
(ff & NOTE_NSECONDS) ? 'n' : '?',
(ff & NOTE_ABSOLUTE) ? 'a' : '-',
(ff & NOTE_CRITICAL) ? 'c' : '-',
(ff & NOTE_BACKGROUND) ? 'b' : '-',
(ff & NOTE_LEEWAY) ? 'l' : '-'
(ff & NOTE_SECONDS) ? 's' :
(ff & NOTE_USECONDS) ? 'u' :
(ff & NOTE_NSECONDS) ? 'n' :
(ff & NOTE_MACHTIME) ? 'm' : '?',
(ff & NOTE_ABSOLUTE) ? 'a' :
(ff & NOTE_MACH_CONTINUOUS_TIME) ? 'A' : '-',
(ff & NOTE_CRITICAL) ? 'c' : '-',
(ff & NOTE_BACKGROUND) ? 'b' : '-',
(ff & NOTE_LEEWAY) ? 'l' : '-'
);
break;
}
case EVFILT_USER: {
case EVFILT_USER:
snprintf(str, len, "%c%c%c ",
(ff & NOTE_TRIGGER) ? 't' : '-',
(ff & NOTE_FFAND) ? 'a' : '-',
(ff & NOTE_FFOR) ? 'o' : '-'
);
break;
}
case EVFILT_WORKLOOP:
snprintf(str, len, "%c%c%c%c%c ",
(ff & NOTE_WL_THREAD_REQUEST) ? 't' :
(ff & NOTE_WL_SYNC_WAIT) ? 'w' :
(ff & NOTE_WL_SYNC_IPC) ? 'i' : '-',
(ff & NOTE_WL_SYNC_WAKE) ? 'W' : '-',
(ff & NOTE_WL_UPDATE_QOS) ? 'q' : '-',
(ff & NOTE_WL_DISCOVER_OWNER) ? 'o' : '-',
(ff & NOTE_WL_IGNORE_ESTALE) ? 'e' : '-'
);
break;
default:
snprintf(str, len, "");
@ -157,13 +177,32 @@ fflags_build(struct kevent_extinfo *info, char *str, int len)
static inline int
filter_is_fd_type(int filter)
{
if (filter <= EVFILT_READ && filter >= EVFILT_VNODE) {
switch (filter) {
case EVFILT_VNODE ... EVFILT_READ:
case EVFILT_SOCK:
case EVFILT_NW_CHANNEL:
return 1;
} else {
default:
return 0;
}
}
static const char *
thread_qos_name(uint8_t th_qos)
{
switch (th_qos) {
case 0: return "--";
case 1: return "MT";
case 2: return "BG";
case 3: return "UT";
case 4: return "DF";
case 5: return "IN";
case 6: return "UI";
case 7: return "MG";
default: return "??";
}
}
/*
* find index of fd in a list of fdinfo of length nfds
*/
@ -252,6 +291,10 @@ print_ident(uint64_t ident, int16_t filter, int width)
printf("%#*llx ", width, ident);
break;
case EVFILT_WORKLOOP:
printf("%#*llx ", width, ident);
break;
default:
printf("%*llu ", width, ident);
break;
@ -260,57 +303,166 @@ print_ident(uint64_t ident, int16_t filter, int width)
}
static void
print_kqfd(int kqfd, int width)
print_kqid(int state, uint64_t kqid)
{
if (kqfd == -1) {
printf("%*s ", width, "wq");
if (state & KQ_WORKQ) {
printf("%18s ", "wq");
} else if (state & KQ_WORKLOOP) {
printf("%#18" PRIx64 " ", kqid);
} else {
printf("%*u ", width, kqfd);
printf("fd %15" PRIi64 " ", kqid);
}
}
#define PROCNAME_WIDTH 20
static void
print_kq_info(int pid, const char *procname, int kqfd, int state)
print_kq_info(int pid, const char *procname, uint64_t kqid, int state)
{
if (raw) {
printf("%5u ", pid);
print_kqfd(kqfd, 5);
print_kqid(state, kqid);
printf("%#10x ", state);
} else {
char tmpstr[PROCNAME_WIDTH+1];
strlcpy(tmpstr, shorten_procname(procname, PROCNAME_WIDTH), PROCNAME_WIDTH+1);
printf("%-*s ", PROCNAME_WIDTH, tmpstr);
printf("%5u ", pid);
print_kqfd(kqfd, 5);
print_kqid(state, kqid);
printf(" %c%c%c ",
(state & KQ_SLEEP) ? 'k' : '-',
(state & KQ_SEL) ? 's' : '-',
(state & KQ_KEV32) ? '3' :
(state & KQ_KEV64) ? '6' :
(state & KQ_KEV_QOS) ? 'q' : '-'
(state & KQ_WORKQ) ? 'q' :
(state & KQ_WORKLOOP) ? 'l' : '-'
);
}
}
enum kqtype {
KQTYPE_FD,
KQTYPE_DYNAMIC
};
#define POLICY_TIMESHARE 1
#define POLICY_RR 2
#define POLICY_FIFO 4
static int
process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo *fdlist, int nfds)
process_kqueue(int pid, const char *procname, enum kqtype type, uint64_t kqid,
struct proc_fdinfo *fdlist, int nfds)
{
int ret, i, nknotes;
char tmpstr[256];
int maxknotes = 256; /* arbitrary starting point */
int kq_state;
bool is_kev_64, is_kev_qos;
int err = 0;
bool overflow = false;
int fd;
bool dynkq_printed = false;
/*
* get the basic kqueue info
*/
struct kqueue_fdinfo kqfdinfo = {};
ret = proc_pidfdinfo(pid, kqfd, PROC_PIDFDKQUEUEINFO, &kqfdinfo, sizeof(kqfdinfo));
if (ret != sizeof(kqfdinfo) && kqfd != -1) {
struct kqueue_dyninfo kqinfo = {};
switch (type) {
case KQTYPE_FD:
ret = proc_pidfdinfo(pid, (int)kqid, PROC_PIDFDKQUEUEINFO, &kqfdinfo, sizeof(kqfdinfo));
fd = (int)kqid;
break;
case KQTYPE_DYNAMIC:
ret = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_INFO, kqid, &kqinfo, sizeof(kqinfo));
break;
default:
os_crash("invalid kqueue type");
}
if (type == KQTYPE_FD && (int)kqid != -1) {
if (ret != sizeof(kqfdinfo)) {
/* every proc has an implicit workq kqueue, dont warn if its unused */
fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid, kqfd);
fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid,
fd);
}
} else if (type == KQTYPE_DYNAMIC) {
if (ret < sizeof(struct kqueue_info)) {
fprintf(stderr, "WARN: kqueue missing (pid %i, kq %#" PRIx64 ")\n",
pid, kqid);
} else {
kqfdinfo.kqueueinfo = kqinfo.kqdi_info;
}
if (verbose && ret >= sizeof(struct kqueue_dyninfo)) {
print_kq_info(pid, procname, kqid, kqinfo.kqdi_info.kq_state);
if (kqinfo.kqdi_owner) {
printf("%#18llx ", kqinfo.kqdi_owner); // ident
printf("%-9s ", "WL owned"); // filter
} else if (kqinfo.kqdi_servicer) {
printf("%#18llx ", kqinfo.kqdi_servicer); // ident
printf("%-9s ", "WL"); // filter
} else {
printf("%18s ", "-"); // ident
printf("%-9s ", "WL"); // filter
}
dynkq_printed = true;
if (raw) {
printf("%-10s ", " "); // fflags
printf("%-10s ", " "); // flags
printf("%-10s ", " "); // evst
} else {
const char *reqstate = "???";
switch (kqinfo.kqdi_request_state) {
case WORKQ_TR_STATE_IDLE:
reqstate = "";
break;
case WORKQ_TR_STATE_NEW:
reqstate = "new";
break;
case WORKQ_TR_STATE_QUEUED:
reqstate = "queued";
break;
case WORKQ_TR_STATE_CANCELED:
reqstate = "canceled";
break;
case WORKQ_TR_STATE_BINDING:
reqstate = "binding";
break;
case WORKQ_TR_STATE_BOUND:
reqstate = "bound";
break;
}
printf("%-8s ", reqstate); // fdtype
char policy_type;
switch (kqinfo.kqdi_pol) {
case POLICY_RR:
policy_type = 'R';
break;
case POLICY_FIFO:
policy_type = 'F';
case POLICY_TIMESHARE:
case 0:
default:
policy_type = '-';
break;
}
snprintf(tmpstr, 4, "%c%c%c", (kqinfo.kqdi_pri == 0)?'-':'P', policy_type, (kqinfo.kqdi_cpupercent == 0)?'-':'%');
printf("%-7s ", tmpstr); // fflags
printf("%-15s ", " "); // flags
printf("%-15s ", " "); // evst
}
if (!raw && kqinfo.kqdi_pri != 0) {
printf("%3d ", kqinfo.kqdi_pri); //qos
} else {
int qos = MAX(MAX(kqinfo.kqdi_events_qos, kqinfo.kqdi_async_qos),
kqinfo.kqdi_sync_waiter_qos);
printf("%3s ", thread_qos_name(qos)); //qos
}
printf("\n");
}
}
/*
@ -322,25 +474,36 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo
kqextinfo = malloc(sizeof(struct kevent_extinfo) * maxknotes);
}
if (!kqextinfo) {
perror("failed allocating memory");
err = errno;
perror("failed allocating memory");
goto out;
}
errno = 0;
nknotes = proc_pidfdinfo(pid, kqfd, PROC_PIDFDKQUEUE_EXTINFO, kqextinfo,
sizeof(struct kevent_extinfo) * maxknotes);
switch (type) {
case KQTYPE_FD:
nknotes = proc_pidfdinfo(pid, fd, PROC_PIDFDKQUEUE_EXTINFO,
kqextinfo, sizeof(struct kevent_extinfo) * maxknotes);
break;
case KQTYPE_DYNAMIC:
nknotes = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_EXTINFO, kqid,
kqextinfo, sizeof(struct kevent_extinfo) * maxknotes);
break;
default:
os_crash("invalid kqueue type");
}
if (nknotes <= 0) {
if (errno == 0) {
/* proc_*() can't distinguish between error and empty list */
} else if (errno == EAGAIN) {
goto again;
} else if (errno == EBADF) {
fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid, kqfd);
fprintf(stderr, "WARN: FD table changed (pid %i, kq %#" PRIx64 ")\n", pid, kqid);
goto out;
} else {
perror("failed to get extended kqueue info");
err = errno;
perror("failed to get extended kqueue info");
goto out;
}
}
@ -356,10 +519,14 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo
overflow = true;
}
kq_state = kqfdinfo.kqueueinfo.kq_state;
is_kev_64 = (kq_state & PROC_KQUEUE_64);
is_kev_qos = (kq_state & PROC_KQUEUE_QOS);
if (nknotes == 0) {
if (!ignore_empty) {
if (!ignore_empty && !dynkq_printed) {
/* for empty kqueues, print a single empty entry */
print_kq_info(pid, procname, kqfd, kqfdinfo.kqueueinfo.kq_state);
print_kq_info(pid, procname, kqid, kq_state);
printf("%18s \n", "-");
}
goto out;
@ -368,7 +535,7 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo
for (i = 0; i < nknotes; i++) {
struct kevent_extinfo *info = &kqextinfo[i];
print_kq_info(pid, procname, kqfd, kqfdinfo.kqueueinfo.kq_state);
print_kq_info(pid, procname, kqid, kqfdinfo.kqueueinfo.kq_state);
print_ident(info->kqext_kev.ident, info->kqext_kev.filter, 18);
printf("%-9s ", filt_name(info->kqext_kev.filter));
@ -377,7 +544,6 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo
printf("%#10x ", info->kqext_kev.flags);
printf("%#10x ", info->kqext_status);
} else {
/* for kevents attached to file descriptors, print the type of FD (file, socket, etc) */
const char *fdstr = "";
if (filter_is_fd_type(info->kqext_kev.filter)) {
@ -413,32 +579,39 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo
);
unsigned st = info->kqext_status;
printf("%c%c%c%c %c%c%c%c%c",
(st & KN_ACTIVE) ? 'a' : '-',
(st & KN_QUEUED) ? 'q' : '-',
(st & KN_DISABLED) ? 'd' : '-',
(st & KN_STAYQUEUED) ? 's' : '-',
printf("%c%c%c%c%c %c%c%c%c %c%c%c ",
(st & KN_ACTIVE) ? 'a' : '-',
(st & KN_QUEUED) ? 'q' : '-',
(st & KN_DISABLED) ? 'd' : '-',
(st & KN_SUPPRESSED) ? 'p' : '-',
(st & KN_STAYACTIVE) ? 's' : '-',
(st & KN_DROPPING) ? 'o' : '-',
(st & KN_USEWAIT) ? 'u' : '-',
(st & KN_ATTACHING) ? 'c' : '-',
(st & KN_DEFERDROP) ? 'f' : '-',
(st & KN_TOUCH) ? 't' : '-'
(st & KN_DROPPING) ? 'd' : '-',
(st & KN_LOCKED) ? 'l' : '-',
(st & KN_POSTING) ? 'P' : '-',
(st & KN_MERGE_QOS) ? 'm' : '-',
(st & KN_DEFERDELETE) ? 'D' : '-',
(st & KN_REQVANISH) ? 'v' : '-',
(st & KN_VANISHED) ? 'n' : '-'
);
}
printf("%3s ", thread_qos_name(info->kqext_kev.qos));
printf("%#18llx ", (unsigned long long)info->kqext_kev.data);
if (verbose) {
printf("%#18llx ", (unsigned long long)info->kqext_kev.udata);
if (kqfdinfo.kqueueinfo.kq_state & (KQ_KEV64|KQ_KEV_QOS)) {
if (is_kev_qos || is_kev_64) {
printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[0]);
printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[1]);
}
if (kqfdinfo.kqueueinfo.kq_state & KQ_KEV_QOS) {
printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[2]);
printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[3]);
printf("%#10lx ", (unsigned long)info->kqext_kev.xflags);
if (is_kev_qos) {
printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[2]);
printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[3]);
printf("%#10lx ", (unsigned long)info->kqext_kev.xflags);
}
}
}
@ -446,8 +619,8 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo
}
if (overflow) {
printf(" ***** output truncated (>=%i knotes on kq %i, proc %i) *****\n",
nknotes, kqfd, pid);
printf(" ***** output truncated (>=%i knotes on kq %" PRIu64 ", proc %i) *****\n",
nknotes, kqid, pid);
}
out:
@ -459,10 +632,46 @@ process_kqueue_on_fd(int pid, const char *procname, int kqfd, struct proc_fdinfo
return err;
}
static int
pid_kqids(pid_t pid, kqueue_id_t **kqids_out)
{
static int kqids_len = 256;
static kqueue_id_t *kqids = NULL;
static uint32_t kqids_size;
int nkqids;
retry:
if (os_mul_overflow(sizeof(kqueue_id_t), kqids_len, &kqids_size)) {
assert(kqids_len > PROC_PIDDYNKQUEUES_MAX);
kqids_len = PROC_PIDDYNKQUEUES_MAX;
goto retry;
}
if (!kqids) {
kqids = malloc(kqids_size);
os_assert(kqids != NULL);
}
nkqids = proc_list_dynkqueueids(pid, kqids, kqids_size);
if (nkqids > kqids_len && kqids_len < PROC_PIDDYNKQUEUES_MAX) {
kqids_len *= 2;
if (kqids_len > PROC_PIDDYNKQUEUES_MAX) {
kqids_len = PROC_PIDDYNKQUEUES_MAX;
}
free(kqids);
kqids = NULL;
goto retry;
}
*kqids_out = kqids;
return MIN(nkqids, kqids_len);
}
static int
process_pid(pid_t pid)
{
int i, nfds;
int i, nfds, nkqids;
kqueue_id_t *kqids;
int ret = 0;
int maxfds = 256; /* arbitrary starting point */
struct proc_fdinfo *fdlist = NULL;
@ -473,21 +682,21 @@ process_pid(pid_t pid)
fdlist = malloc(sizeof(struct proc_fdinfo) * maxfds);
}
if (!fdlist) {
perror("failed to allocate");
ret = errno;
perror("failed to allocate");
goto out;
}
nfds = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdlist,
sizeof(struct proc_fdinfo) * maxfds);
if (nfds <= 0) {
ret = errno;
fprintf(stderr, "%s: failed enumerating file descriptors of process %i: %s",
self, pid, strerror(errno));
if (errno == EPERM && geteuid() != 0) {
self, pid, strerror(ret));
if (ret == EPERM && geteuid() != 0) {
fprintf(stderr, " (are you root?)");
}
fprintf(stderr, "\n");
ret = errno;
goto out;
}
@ -514,20 +723,35 @@ process_pid(pid_t pid)
}
/* handle the special workq kq */
ret = process_kqueue_on_fd(pid, procname, -1, fdlist, nfds);
ret = process_kqueue(pid, procname, KQTYPE_FD, -1, fdlist, nfds);
if (ret) {
goto out;
}
for (i = 0; i < nfds; i++) {
if (fdlist[i].proc_fdtype == PROX_FDTYPE_KQUEUE) {
ret = process_kqueue_on_fd(pid, procname, fdlist[i].proc_fd, fdlist, nfds);
ret = process_kqueue(pid, procname, KQTYPE_FD,
(uint64_t)fdlist[i].proc_fd, fdlist, nfds);
if (ret) {
goto out;
}
}
}
nkqids = pid_kqids(pid, &kqids);
for (i = 0; i < nkqids; i++) {
ret = process_kqueue(pid, procname, KQTYPE_DYNAMIC, kqids[i], fdlist, nfds);
if (ret) {
goto out;
}
}
if (nkqids >= PROC_PIDDYNKQUEUES_MAX) {
printf(" ***** output truncated (>=%i dynamic kqueues in proc %i) *****\n",
nkqids, pid);
}
out:
if (fdlist) {
free(fdlist);
@ -562,8 +786,8 @@ process_all_pids(void)
} else if (errno == EAGAIN) {
goto again;
} else {
perror("failed enumerating pids");
ret = errno;
perror("failed enumerating pids");
goto out;
}
}
@ -580,7 +804,8 @@ process_all_pids(void)
/* listpids gives us pid 0 for some reason */
if (pids[i]) {
ret = process_pid(pids[i]);
if (ret) {
/* ignore races with processes exiting */
if (ret && ret != ESRCH) {
goto out;
}
}
@ -598,24 +823,33 @@ out:
static void
cheatsheet(void)
{
const char *bold = "\033[1m";
const char *reset = "\033[0m";
if (!isatty(STDERR_FILENO)) {
bold = reset = "";
}
fprintf(stderr, "\nFilter-independent flags:\n\n\
\033[1mcommand pid kq kqst ident filter fdtype fflags flags evst\033[0m\n\
\033[1m-------------------- ----- ----- ---- ------------------ --------- -------- ------- --------------- ----------\033[0m\n\
EV_UDATA_SPECIFIC\n\
EV_DISPATCH EV_FLAG0 (EV_POLL)\n\
EV_CLEAR EV_FLAG1 (EV_OOBAND)\n\
EV_ONESHOT EV_EOF\n\
EV_RECEIPT EV_ERROR\n\
\n\
\033[1mlaunchd 1 4 ks- netbiosd 250 PROC ------- andx r1cs upboe aqds oucft\033[0m \n\
\n\
kqueue file descriptor EV_ADD KN_ACTIVE KN_TOUCH\n\
KQ_SLEEP EV_ENABLE KN_QUEUED KN_DEFERDROP\n\
KQ_SEL EV_DISABLE KN_DISABLED KN_ATTACHING\n\
KEV32 (3) EV_DELETE KN_STAYQUEUED KN_USEWAIT\n\
KEV64 (6) KN_DROPPING\n\
KEV_QOS (q) \n\
\n");
%s\
command pid kq kqst knid filter fdtype fflags flags evst qos%s\n%s\
-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- -------------- ---%s\n\
EV_UDATA_SPECIFIC\n\
EV_DISPATCH EV_FLAG0 (EV_POLL)\n\
EV_CLEAR EV_FLAG1 (EV_OOBAND)\n\
EV_ONESHOT EV_EOF\n\
EV_RECEIPT EV_ERROR\n\
\n%s\
launchd 1 4 ks- netbiosd 250 PROC ------- andx r1cs upboe aqdps dlPm Dvn IN%s\n\
\n\
kqueue file descriptor/dynamic ID EV_ADD KN_ACTIVE KN_VANISHED\n\
KQ_SLEEP EV_ENABLE KN_QUEUED KN_REQVANISH\n\
KQ_SEL EV_DISABLE KN_DISABLED KN_DEFERDELETE\n\
KQ_WORKQ (q) EV_DELETE KN_SUPPRESSED \n\
KQ_WORKLOOP (l) KN_STAYACTIVE \n\
\n\
KN_DROPPING KN_MERGE_QOS\n\
KN_LOCKED KN_POSTING\n\
\n", bold, reset, bold, reset, bold, reset);
}
static void
@ -628,20 +862,21 @@ static void
print_header(void)
{
if (raw) {
printf(" pid kq kqst ident filter fflags flags evst data");
if (verbose) {
printf(" udata ext0 ext1 ext2 ext3 xflags");
}
printf("\n");
printf("----- ----- ---------- ------------------ --------- ---------- ---------- ---------- ------------------");
printf(" pid kq kqst knid filter fflags flags evst qos data");
} else {
printf("command pid kq kqst ident filter fdtype fflags flags evst data");
if (verbose) {
printf(" udata ext0 ext1 ext2 ext3 xflags");
}
printf("\n");
printf("-------------------- ----- ----- ---- ------------------ --------- -------- ------- --------------- ---------- -----------------");
printf("command pid kq kqst knid filter fdtype fflags flags evst qos data");
}
if (verbose) {
printf(" udata ext0 ext1 ext2 ext3 xflags");
}
printf("\n");
if (raw) {
printf("----- ------------------ ---------- ------------------ --------- ---------- ---------- ---------- --- ------------------");
} else {
printf("-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- -------------- --- ------------------");
}
if (verbose) {

View 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);
@ -168,6 +187,6 @@ kern_return_t get_taskinfo_of_receiver_by_send_right(ipc_info_name_t *sendright,
kern_return_t get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name, ipc_info_name_t *out_sendright);
/* basic util functions */
uint32_t print_hex_data(char *outstr, size_t maxlen, char *prefix, char *desc, void *addr, int len);
uint32_t print_hex_data(char *outstr, uint32_t maxlen, char *prefix, char *desc, void *addr, int len);
#endif

119
lsmp.tproj/json.h Normal file
View File

@ -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_ */

View 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

View File

@ -27,9 +27,11 @@
#include <stdlib.h>
#include <libproc.h>
#include <TargetConditionals.h>
#include <errno.h>
#include "common.h"
#include "json.h"
#if TARGET_OS_EMBEDDED
#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
#define TASK_FOR_PID_USAGE_MESG "\nPlease check your boot-args to ensure you have access to task_for_pid()."
#else
#define TASK_FOR_PID_USAGE_MESG ""
@ -40,21 +42,33 @@ struct prog_configs lsmp_config = {
.show_voucher_details = FALSE,
.verbose = FALSE,
.pid = 0,
.json_output = NULL,
};
my_per_task_info_t *psettaskinfo;
mach_msg_type_number_t taskCount;
static void print_usage(char *progname) {
fprintf(stderr, "Usage: %s -p <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' */
@ -178,43 +202,38 @@ int main(int argc, char *argv[]) {
if (psettaskinfo[i].pid == lsmp_config.pid)
taskinfo = &psettaskinfo[i];
ret = KERN_SUCCESS;
}
JSON_OBJECT_BEGIN(lsmp_config.json_output);
JSON_OBJECT_SET(lsmp_config.json_output, version, "%.1f", 1.0);
JSON_KEY(lsmp_config.json_output, processes);
JSON_ARRAY_BEGIN(lsmp_config.json_output);
if (lsmp_config.show_all_tasks == FALSE) {
if (taskinfo == NULL) {
fprintf(stderr, "Failed to find task ipc information for pid %d\n", lsmp_config.pid);
exit(1);
}
printf("Process (%d) : %s\n", taskinfo->pid, taskinfo->processName);
show_task_mach_ports(taskinfo, taskCount, psettaskinfo);
print_task_exception_info(taskinfo);
printf("\n");
print_task_threads_special_ports(taskinfo);
print_task_info(taskinfo, taskCount, psettaskinfo, TRUE, lsmp_config.json_output);
} else {
for (i=0; i < taskCount; i++) {
if (psettaskinfo[i].valid != TRUE)
continue;
printf("Process (%d) : %s\n", psettaskinfo[i].pid, psettaskinfo[i].processName);
show_task_mach_ports(&psettaskinfo[i], taskCount, psettaskinfo);
print_task_exception_info(&psettaskinfo[i]);
if (lsmp_config.verbose) {
printf("\n");
print_task_threads_special_ports(&psettaskinfo[i]);
}
print_task_info(&psettaskinfo[i], taskCount, psettaskinfo, lsmp_config.verbose, lsmp_config.json_output);
printf("\n\n");
}
}
JSON_ARRAY_END(lsmp_config.json_output);
JSON_OBJECT_END(lsmp_config.json_output);
if (taskCount > 1) {
vm_deallocate(mach_task_self(), (vm_address_t)tasks, (vm_size_t)taskCount * sizeof(mach_port_t));
}
deallocate_taskinfo_memory(psettaskinfo);
JSON_CLOSE(lsmp_config.json_output);
return(0);
}

View 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));
len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "Key: %u, ", recipe->key);
len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Command: %u, ", recipe->command);
len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Previous voucher: 0x%x, ", recipe->previous_voucher);
len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Content size: %u\n", recipe->content_size);
JSON_OBJECT_SET(json, key, %u, recipe->key);
JSON_OBJECT_SET(json, command, %u, recipe->command);
JSON_OBJECT_SET(json, previous_voucher, "0x%x", recipe->previous_voucher);
JSON_OBJECT_SET(json, content_size, %u, recipe->content_size);
switch (recipe->key) {
case MACH_VOUCHER_ATTR_KEY_ATM:
len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "ATM ID: %llu\n", *(uint64_t *)(uintptr_t)recipe->content));
JSON_OBJECT_SET(json, ATM_ID, %llu, *(uint64_t *)(uintptr_t)recipe->content);
len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "ATM ID: %llu", *(uint64_t *)(uintptr_t)recipe->content);
break;
case MACH_VOUCHER_ATTR_KEY_IMPORTANCE:
len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "IMPORTANCE INFO: %s\n", (char *)recipe->content));
// content may not be valid JSON, exclude
// JSON_OBJECT_SET(json, importance_info, "%s", (char *)recipe->content);
len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "IMPORTANCE INFO: %s", (char *)recipe->content);
break;
case MACH_VOUCHER_ATTR_KEY_BANK:
len += safesize(snprintf(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX "RESOURCE ACCOUNTING INFO: %s\n", (char *)recipe->content));
// content may not be valid JSON, exclude
// JSON_OBJECT_SET(json, resource_accounting_info, "%s", (char *)recipe->content);
len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "RESOURCE ACCOUNTING INFO: %s", (char *)recipe->content);
break;
default:
len += print_hex_data(&voucher_outstr[len], maxlen - len, VOUCHER_DETAIL_PREFIX, "Recipe Contents", (void *)recipe->content, MIN(recipe->content_size, lsmp_config.voucher_detail_length));
len += print_hex_data(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX, "Recipe Contents", (void *)recipe->content, MIN(recipe->content_size, lsmp_config.voucher_detail_length));
break;
}
if (len + 1 < maxlen && voucher_outstr[len - 1] != '\n') {
voucher_outstr[len++] = '\n';
voucher_outstr[len] = '\0';
}
JSON_OBJECT_END(json); // recipe
return len;
}
char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher) {
char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher, JSON_t json) {
unsigned int recipe_size = voucher_contents_size;
kern_return_t kr = KERN_SUCCESS;
bzero((void *)&voucher_contents[0], sizeof(voucher_contents));
@ -132,27 +198,27 @@ char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher) {
(mach_voucher_attr_raw_recipe_array_t)&voucher_contents[0],
&recipe_size);
if (kr != KERN_SUCCESS && kr != KERN_NOT_SUPPORTED) {
plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x Failed to get contents %s\n", v_kobject, mach_error_string(kr)));
plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x Failed to get contents %s\n", v_kobject, mach_error_string(kr));
return voucher_outstr;
}
if (recipe_size == 0) {
plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x has no contents\n", v_kobject));
plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x has no contents\n", v_kobject);
return voucher_outstr;
}
plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Voucher: 0x%x\n", v_kobject));
plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x\n", v_kobject);
unsigned int used_size = 0;
mach_voucher_attr_recipe_t recipe = NULL;
while (recipe_size > used_size) {
recipe = (mach_voucher_attr_recipe_t)&voucher_contents[used_size];
if (recipe->key) {
plen += show_recipe_detail(recipe, &voucher_outstr[plen], detail_maxlen - plen);
plen += show_recipe_detail(recipe, &voucher_outstr[plen], safesize(detail_maxlen - plen), json);
}
used_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size;
}
} else {
plen += safesize(snprintf(&voucher_outstr[plen], detail_maxlen - plen, VOUCHER_DETAIL_PREFIX "Invalid voucher: 0x%x\n", voucher));
plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Invalid voucher: 0x%x\n", voucher);
}
return voucher_outstr;
@ -196,267 +262,445 @@ int get_recieve_port_status(task_t taskp, mach_port_name_t portname, mach_port_i
return 0;
}
void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos)
void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, JSON_t json)
{
int i, emptycount = 0, portsetcount = 0, sendcount = 0, receivecount = 0, sendoncecount = 0, deadcount = 0, dncount = 0, vouchercount = 0, pid;
kern_return_t ret;
pid_for_task(taskinfo->task, &pid);
int i;
task_table_entry_counts counts = {0};
counts.total = taskinfo->tableCount + taskinfo->treeCount;
JSON_KEY(json, ports)
JSON_ARRAY_BEGIN(json);
printf(" name ipc-object rights flags boost reqs recv send sonce oref qlimit msgcount context identifier type\n");
printf("--------- ---------- ---------- -------- ----- ---- ----- ----- ----- ---- ------ -------- ------------------ ----------- ------------\n");
for (i = 0; i < taskinfo->tableCount; i++) {
int j, k;
boolean_t send = FALSE;
boolean_t sendonce = FALSE;
boolean_t dnreq = FALSE;
int sendrights = 0;
unsigned int kotype = 0;
vm_offset_t kobject = (vm_offset_t)0;
for (i = 0; i < taskinfo->tableCount; i++) {
show_task_table_entry(&taskinfo->table[i], taskinfo, taskCount, allTaskInfos, &counts, json);
}
/* skip empty slots in the table */
if ((taskinfo->table[i].iin_type & MACH_PORT_TYPE_ALL_RIGHTS) == 0) {
emptycount++;
continue;
JSON_ARRAY_END(json); // ports
JSON_OBJECT_SET(json, total, %d, counts.total);
JSON_OBJECT_SET(json, send_rights, %d, counts.sendcount);
JSON_OBJECT_SET(json, receive_rights, %d, counts.receivecount);
JSON_OBJECT_SET(json, send_once_rights, %d, counts.sendoncecount);
JSON_OBJECT_SET(json, port_sets, %d, counts.portsetcount);
JSON_OBJECT_SET(json, dead_names, %d, counts.deadcount);
JSON_OBJECT_SET(json, dead_name requests, %d, counts.dncount);
JSON_OBJECT_SET(json, vouchers, %d, counts.vouchercount);
printf("\n");
printf("total = %d\n", counts.total);
printf("SEND = %d\n", counts.sendcount);
printf("RECEIVE = %d\n", counts.receivecount);
printf("SEND_ONCE = %d\n", counts.sendoncecount);
printf("PORT_SET = %d\n", counts.portsetcount);
printf("DEAD_NAME = %d\n", counts.deadcount);
printf("DNREQUEST = %d\n", counts.dncount);
printf("VOUCHERS = %d\n", counts.vouchercount);
}
static void show_task_table_entry(ipc_info_name_t *entry, my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, task_table_entry_counts_t counts, JSON_t json) {
int j, k, port_status_flag_idx;
kern_return_t ret;
boolean_t send = FALSE;
boolean_t dnreq = FALSE;
int sendrights = 0;
unsigned int kotype = 0;
vm_offset_t kobject = (vm_offset_t)0;
/* skip empty slots in the table */
if ((entry->iin_type & MACH_PORT_TYPE_ALL_RIGHTS) == 0) {
counts->total--;
return;
}
if (entry->iin_type == MACH_PORT_TYPE_PORT_SET) {
mach_port_name_array_t members;
mach_msg_type_number_t membersCnt;
ret = mach_port_get_set_status(taskinfo->task,
entry->iin_name,
&members, &membersCnt);
if (ret != KERN_SUCCESS) {
fprintf(stderr, "mach_port_get_set_status(0x%08x) failed: %s\n",
entry->iin_name,
mach_error_string(ret));
return;
}
if (taskinfo->table[i].iin_type == MACH_PORT_TYPE_PORT_SET) {
mach_port_name_array_t members;
mach_msg_type_number_t membersCnt;
JSON_OBJECT_BEGIN(json);
JSON_OBJECT_SET(json, type, "port set");
JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name);
JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object);
ret = mach_port_get_set_status(taskinfo->task,
taskinfo->table[i].iin_name,
&members, &membersCnt);
if (ret != KERN_SUCCESS) {
fprintf(stderr, "mach_port_get_set_status(0x%08x) failed: %s\n",
taskinfo->table[i].iin_name,
mach_error_string(ret));
continue;
}
printf("0x%08x 0x%08x port-set -------- --- 1 %d members\n",
taskinfo->table[i].iin_name,
taskinfo->table[i].iin_object,
membersCnt);
/* get some info for each portset member */
for (j = 0; j < membersCnt; j++) {
for (k = 0; k < taskinfo->tableCount; k++) {
if (taskinfo->table[k].iin_name == members[j]) {
mach_port_info_ext_t info;
mach_port_status_t port_status;
mach_port_context_t port_context = (mach_port_context_t)0;
if (0 != get_recieve_port_status(taskinfo->task, taskinfo->table[k].iin_name, &info)) {
bzero((void *)&info, sizeof(info));
JSON_KEY(json, members);
JSON_ARRAY_BEGIN(json);
printf("0x%08x 0x%08x port-set -------- --- 1 %d members\n",
entry->iin_name,
entry->iin_object,
membersCnt);
/* get some info for each portset member */
for (j = 0; j < membersCnt; j++) {
for (k = 0; k < taskinfo->tableCount; k++) {
if (taskinfo->table[k].iin_name == members[j]) {
mach_port_info_ext_t info;
mach_port_status_t port_status;
mach_port_context_t port_context = (mach_port_context_t)0;
if (0 != get_recieve_port_status(taskinfo->task, taskinfo->table[k].iin_name, &info)) {
bzero((void *)&info, sizeof(info));
}
port_status = info.mpie_status;
get_receive_port_context(taskinfo->task, taskinfo->table[k].iin_name, &port_context);
JSON_OBJECT_BEGIN(json);
JSON_OBJECT_SET(json, ipc-object, "0x%08x", taskinfo->table[k].iin_object);
JSON_KEY(json, rights);
JSON_ARRAY_BEGIN(json);
JSON_ARRAY_APPEND(json, "recv");
if (taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) {
JSON_ARRAY_APPEND(json, "send");
}
JSON_ARRAY_END(json); // rights
JSON_KEY(json, port_status_flags);
JSON_ARRAY_BEGIN(json);
port_status_flag_idx = 0;
while (0 != port_status_flags[port_status_flag_idx++].flag) {
if (port_status.mps_flags & INDEX_TO_PORT_FLAG(port_status_flag_idx)) {
JSON_ARRAY_APPEND(json, "%s", port_status_flags[port_status_flag_idx].name);
}
port_status = info.mpie_status;
get_receive_port_context(taskinfo->task, taskinfo->table[k].iin_name, &port_context);
printf(" - 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx 0x%08x (%d) %s\n",
taskinfo->table[k].iin_object,
(taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) ? "recv,send ":"recv ",
SHOW_PORT_STATUS_FLAGS(port_status.mps_flags),
info.mpie_boost_cnt,
(taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-",
(port_status.mps_nsrequest) ? "N" : "-",
(port_status.mps_pdrequest) ? "P" : "-",
1,
taskinfo->table[k].iin_urefs,
port_status.mps_sorights,
(port_status.mps_srights) ? "Y" : "N",
port_status.mps_qlimit,
port_status.mps_msgcount,
(uint64_t)port_context,
taskinfo->table[k].iin_name,
pid,
taskinfo->processName);
}
JSON_ARRAY_END(json); // port status flags
JSON_OBJECT_SET(json, boosts, %d, info.mpie_boost_cnt);
JSON_KEY(json, notifications);
JSON_ARRAY_BEGIN(json);
if (taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) {
JSON_ARRAY_APPEND(json, "dead name");
}
if (port_status.mps_nsrequest) {
JSON_ARRAY_APPEND(json, "no sender");
}
if (port_status.mps_nsrequest) {
JSON_ARRAY_APPEND(json, "port destroy request");
}
JSON_ARRAY_END(json); // notifications
JSON_OBJECT_SET(json, recv_rights, %d, 1);
JSON_OBJECT_SET(json, send_rights, %d, taskinfo->table[k].iin_urefs);
JSON_OBJECT_SET(json, send_once_rights, %d, port_status.mps_sorights);
JSON_OBJECT_SET_BOOL(json, oref, port_status.mps_srights);
JSON_OBJECT_SET(json, queue_limit, %d, port_status.mps_qlimit);
JSON_OBJECT_SET(json, msg_count, %d, port_status.mps_msgcount);
JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)port_context);
JSON_OBJECT_SET(json, identifier, "0x%08x", taskinfo->table[k].iin_name);
JSON_OBJECT_SET(json, pid, %d, taskinfo->pid);
JSON_OBJECT_SET(json, process, "%s", taskinfo->processName);
JSON_OBJECT_END(json); // member
printf(" - 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx 0x%08x (%d) %s\n",
taskinfo->table[k].iin_object,
(taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) ? "recv,send ":"recv ",
SHOW_PORT_STATUS_FLAGS(port_status.mps_flags),
info.mpie_boost_cnt,
(taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-",
(port_status.mps_nsrequest) ? "N" : "-",
(port_status.mps_pdrequest) ? "P" : "-",
1,
taskinfo->table[k].iin_urefs,
port_status.mps_sorights,
(port_status.mps_srights) ? "Y" : "N",
port_status.mps_qlimit,
port_status.mps_msgcount,
(uint64_t)port_context,
taskinfo->table[k].iin_name,
taskinfo->pid,
taskinfo->processName);
break;
}
}
}
JSON_ARRAY_END(json); // members
JSON_OBJECT_END(json); // port-set
ret = vm_deallocate(mach_task_self(), (vm_address_t)members,
membersCnt * sizeof(mach_port_name_t));
if (ret != KERN_SUCCESS) {
fprintf(stderr, "vm_deallocate() failed: %s\n",
mach_error_string(ret));
exit(1);
}
counts->portsetcount++;
return;
}
if (entry->iin_type & MACH_PORT_TYPE_SEND) {
send = TRUE;
sendrights = entry->iin_urefs;
counts->sendcount++;
}
if (entry->iin_type & MACH_PORT_TYPE_DNREQUEST) {
dnreq = TRUE;
counts->dncount++;
}
if (entry->iin_type & MACH_PORT_TYPE_RECEIVE) {
mach_port_status_t status;
mach_port_info_ext_t info;
mach_port_context_t context = (mach_port_context_t)0;
struct k2n_table_node *k2nnode;
ret = get_recieve_port_status(taskinfo->task, entry->iin_name, &info);
get_receive_port_context(taskinfo->task, entry->iin_name, &context);
/* its ok to fail in fetching attributes */
if (ret < 0) {
return;
}
status = info.mpie_status;
JSON_OBJECT_BEGIN(json);
JSON_OBJECT_SET(json, type, "port");
JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name);
JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object);
JSON_KEY(json, rights);
JSON_ARRAY_BEGIN(json);
JSON_ARRAY_APPEND(json, "recv");
if (send) JSON_ARRAY_APPEND(json, "send");
JSON_ARRAY_END(json); // rights
JSON_KEY(json, port_status_flags);
JSON_ARRAY_BEGIN(json);
port_status_flag_idx = 0;
while (0 != port_status_flags[port_status_flag_idx++].flag) {
if (status.mps_flags & INDEX_TO_PORT_FLAG(port_status_flag_idx)) {
JSON_ARRAY_APPEND(json, "%s", port_status_flags[port_status_flag_idx].name);
}
}
JSON_ARRAY_END(json); // port status flags
JSON_OBJECT_SET(json, boosts, %d, info.mpie_boost_cnt);
JSON_KEY(json, notifications);
JSON_ARRAY_BEGIN(json);
if (dnreq) {
JSON_ARRAY_APPEND(json, "dead name");
}
if (status.mps_nsrequest) {
JSON_ARRAY_APPEND(json, "no sender");
}
if (status.mps_nsrequest) {
JSON_ARRAY_APPEND(json, "port destroy request");
}
JSON_ARRAY_END(json); // notifications
JSON_OBJECT_SET(json, recv_rights, %d, 1);
JSON_OBJECT_SET(json, send_rights, %d, sendrights);
JSON_OBJECT_SET(json, send_once_rights, %d, status.mps_sorights);
JSON_OBJECT_SET_BOOL(json, oref, status.mps_srights);
JSON_OBJECT_SET(json, queue_limit, %d, status.mps_qlimit);
JSON_OBJECT_SET(json, msg_count, %d, status.mps_msgcount);
JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)context);
JSON_OBJECT_END(json); // port
printf("0x%08x 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx \n",
entry->iin_name,
entry->iin_object,
(send) ? "recv,send ":"recv ",
SHOW_PORT_STATUS_FLAGS(status.mps_flags),
info.mpie_boost_cnt,
(dnreq) ? "D":"-",
(status.mps_nsrequest) ? "N":"-",
(status.mps_pdrequest) ? "P":"-",
1,
sendrights,
status.mps_sorights,
(status.mps_srights) ? "Y":"N",
status.mps_qlimit,
status.mps_msgcount,
(uint64_t)context);
counts->receivecount++;
/* show other rights (in this and other tasks) for the port */
for (j = 0; j < taskCount; j++) {
if (allTaskInfos[j].valid == FALSE)
continue;
k2nnode = k2n_table_lookup(allTaskInfos[j].k2ntable, entry->iin_object);
while (k2nnode) {
if (k2nnode->info_name != entry) {
assert(k2nnode->info_name->iin_object == entry->iin_object);
printf(" + %s -------- %s%s%s %5d <- 0x%08x (%d) %s\n",
(k2nnode->info_name->iin_type & MACH_PORT_TYPE_SEND_ONCE) ?
"send-once " : "send ",
(k2nnode->info_name->iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-",
"-",
"-",
k2nnode->info_name->iin_urefs,
k2nnode->info_name->iin_name,
allTaskInfos[j].pid,
allTaskInfos[j].processName);
}
k2nnode = k2n_table_lookup_next(k2nnode, entry->iin_object);
}
}
return;
}
else if (entry->iin_type & MACH_PORT_TYPE_DEAD_NAME)
{
JSON_OBJECT_BEGIN(json);
JSON_OBJECT_SET(json, type, "dead name");
JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name);
JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object);
JSON_OBJECT_SET(json, send_rights, %d, entry->iin_urefs);
JSON_OBJECT_END(json); // dead name
printf("0x%08x 0x%08x dead-name -------- --- %5d \n",
entry->iin_name,
entry->iin_object,
entry->iin_urefs);
counts->deadcount++;
return;
}
if (entry->iin_type & MACH_PORT_TYPE_SEND_ONCE) {
counts->sendoncecount++;
}
JSON_OBJECT_BEGIN(json);
JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name);
JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object);
JSON_KEY(json, rights);
JSON_ARRAY_BEGIN(json);
JSON_ARRAY_APPEND(json, "%s", (send) ? "send":"send once");
JSON_ARRAY_END(json); //rights
JSON_KEY(json, notifications);
JSON_ARRAY_BEGIN(json);
if (dnreq) JSON_ARRAY_APPEND(json, "dead name");
JSON_ARRAY_END(json); // notifications
JSON_OBJECT_SET(json, send_rights, %d, (send) ? sendrights : 0);
printf("0x%08x 0x%08x %s -------- %s%s%s %5.0d ",
entry->iin_name,
entry->iin_object,
(send) ? "send ":"send-once ",
(dnreq) ? "D":"-",
"-",
"-",
(send) ? sendrights : 0);
/* converting to kobjects is not always supported */
ret = mach_port_kernel_object(taskinfo->task,
entry->iin_name,
&kotype, (unsigned *)&kobject);
if (ret == KERN_SUCCESS && kotype != 0) {
JSON_OBJECT_SET(json, identifier, "0x%08x", (natural_t)kobject);
JSON_OBJECT_SET(json, type, "%s", kobject_name(kotype));
printf(" 0x%08x %s", (natural_t)kobject, kobject_name(kotype));
if ((kotype == IKOT_TASK_RESUME) || (kotype == IKOT_TASK) || (kotype == IKOT_TASK_NAME)) {
if (taskinfo->task_kobject == kobject) {
/* neat little optimization since in most cases tasks have themselves in their ipc space */
JSON_OBJECT_SET(json, pid, %d, taskinfo->pid);
JSON_OBJECT_SET(json, process, "%s", taskinfo->processName);
printf(" SELF (%d) %s", taskinfo->pid, taskinfo->processName);
} else {
my_per_task_info_t * _found_task = get_taskinfo_by_kobject((natural_t)kobject);
JSON_OBJECT_SET(json, pid, %d, _found_task->pid);
JSON_OBJECT_SET(json, process, "%s", _found_task->processName);
printf(" (%d) %s", _found_task->pid, _found_task->processName);
}
}
if (kotype == IKOT_THREAD) {
for (int i = 0; i < taskinfo->threadCount; i++) {
if (taskinfo->threadInfos[i].th_kobject == kobject) {
printf(" (%#llx)", taskinfo->threadInfos[i].th_id);
break;
}
}
}
ret = vm_deallocate(mach_task_self(), (vm_address_t)members,
membersCnt * sizeof(mach_port_name_t));
if (ret != KERN_SUCCESS) {
fprintf(stderr, "vm_deallocate() failed: %s\n",
mach_error_string(ret));
exit(1);
}
portsetcount++;
continue;
}
if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_SEND) {
send = TRUE;
sendrights = taskinfo->table[i].iin_urefs;
sendcount++;
}
if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_DNREQUEST) {
dnreq = TRUE;
dncount++;
}
if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_RECEIVE) {
mach_port_status_t status;
mach_port_info_ext_t info;
mach_port_context_t context = (mach_port_context_t)0;
struct k2n_table_node *k2nnode;
ret = get_recieve_port_status(taskinfo->task, taskinfo->table[i].iin_name, &info);
get_receive_port_context(taskinfo->task, taskinfo->table[i].iin_name, &context);
/* its ok to fail in fetching attributes */
if (ret < 0) {
continue;
printf("\n");
if (kotype == IKOT_VOUCHER) {
counts->vouchercount++;
if (lsmp_config.show_voucher_details) {
JSON_KEY(json, recipes);
JSON_ARRAY_BEGIN(json);
char * detail = copy_voucher_detail(taskinfo->task, entry->iin_name, json);
JSON_ARRAY_END(json); // recipes
printf("%s\n", detail);
free(detail);
}
status = info.mpie_status;
printf("0x%08x 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx \n",
taskinfo->table[i].iin_name,
taskinfo->table[i].iin_object,
(send) ? "recv,send ":"recv ",
SHOW_PORT_STATUS_FLAGS(status.mps_flags),
info.mpie_boost_cnt,
(dnreq) ? "D":"-",
(status.mps_nsrequest) ? "N":"-",
(status.mps_pdrequest) ? "P":"-",
1,
sendrights,
status.mps_sorights,
(status.mps_srights) ? "Y":"N",
status.mps_qlimit,
status.mps_msgcount,
(uint64_t)context);
receivecount++;
}
JSON_OBJECT_END(json); // kobject
return;
}
/* show other rights (in this and other tasks) for the port */
for (j = 0; j < taskCount; j++) {
if (allTaskInfos[j].valid == FALSE)
continue;
/* not kobject - find the receive right holder */
my_per_task_info_t *recv_holder_taskinfo;
mach_port_name_t recv_name = MACH_PORT_NULL;
if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(entry, &recv_holder_taskinfo, &recv_name)) {
mach_port_status_t port_status;
mach_port_info_ext_t info;
mach_port_context_t port_context = (mach_port_context_t)0;
if (0 != get_recieve_port_status(recv_holder_taskinfo->task, recv_name, &info)) {
bzero((void *)&port_status, sizeof(port_status));
}
port_status = info.mpie_status;
get_receive_port_context(recv_holder_taskinfo->task, recv_name, &port_context);
k2nnode = k2n_table_lookup(allTaskInfos[j].k2ntable, taskinfo->table[i].iin_object);
JSON_OBJECT_SET(json, queue_limit, %d, port_status.mps_qlimit);
JSON_OBJECT_SET(json, msg_count, %d, port_status.mps_msgcount);
JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)port_context);
JSON_OBJECT_SET(json, identifier, "0x%08x", recv_name);
JSON_OBJECT_SET(json, pid, %d, recv_holder_taskinfo->pid);
JSON_OBJECT_SET(json, process, "%s", recv_holder_taskinfo->processName);
while (k2nnode) {
if (k2nnode->info_name != &taskinfo->table[i]) {
assert(k2nnode->info_name->iin_object == taskinfo->table[i].iin_object);
printf(" -> %6d %8d 0x%016llx 0x%08x (%d) %s\n",
port_status.mps_qlimit,
port_status.mps_msgcount,
(uint64_t)port_context,
recv_name,
recv_holder_taskinfo->pid,
recv_holder_taskinfo->processName);
printf(" + %s -------- %s%s%s %5d <- 0x%08x (%d) %s\n",
(k2nnode->info_name->iin_type & MACH_PORT_TYPE_SEND_ONCE) ?
"send-once " : "send ",
(k2nnode->info_name->iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-",
"-",
"-",
k2nnode->info_name->iin_urefs,
k2nnode->info_name->iin_name,
allTaskInfos[j].pid,
allTaskInfos[j].processName);
}
k2nnode = k2n_table_lookup_next(k2nnode, k2nnode->info_name->iin_name);
}
}
continue;
}
else if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_DEAD_NAME)
{
printf("0x%08x 0x%08x dead-name -------- --- %5d \n",
taskinfo->table[i].iin_name,
taskinfo->table[i].iin_object,
taskinfo->table[i].iin_urefs);
deadcount++;
continue;
}
if (taskinfo->table[i].iin_type & MACH_PORT_TYPE_SEND_ONCE) {
sendonce = TRUE;
sendoncecount++;
}
printf("0x%08x 0x%08x %s -------- %s%s%s %5.0d ",
taskinfo->table[i].iin_name,
taskinfo->table[i].iin_object,
(send) ? "send ":"send-once ",
(dnreq) ? "D":"-",
"-",
"-",
(send) ? sendrights : 0);
/* converting to kobjects is not always supported */
ret = mach_port_kernel_object(taskinfo->task,
taskinfo->table[i].iin_name,
&kotype, (unsigned *)&kobject);
if (ret == KERN_SUCCESS && kotype != 0) {
printf(" 0x%08x %s", (natural_t)kobject, kobject_name(kotype));
if ((kotype == IKOT_TASK_RESUME) || (kotype == IKOT_TASK) || (kotype == IKOT_TASK_NAME)) {
if (taskinfo->task_kobject == kobject) {
/* neat little optimization since in most cases tasks have themselves in their ipc space */
printf(" SELF (%d) %s", taskinfo->pid, taskinfo->processName);
} else {
my_per_task_info_t * _found_task = get_taskinfo_by_kobject((natural_t)kobject);
printf(" (%d) %s", _found_task->pid, _found_task->processName);
}
}
printf("\n");
if (kotype == IKOT_VOUCHER) {
vouchercount++;
if (lsmp_config.show_voucher_details) {
char * detail = copy_voucher_detail(taskinfo->task, taskinfo->table[i].iin_name);
printf("%s\n", detail);
free(detail);
}
}
continue;
}
/* not kobject - find the receive right holder */
my_per_task_info_t *recv_holder_taskinfo;
mach_port_name_t recv_name = MACH_PORT_NULL;
if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(&taskinfo->table[i], &recv_holder_taskinfo, &recv_name)) {
mach_port_status_t port_status;
mach_port_info_ext_t info;
mach_port_context_t port_context = (mach_port_context_t)0;
if (0 != get_recieve_port_status(recv_holder_taskinfo->task, recv_name, &info)) {
bzero((void *)&port_status, sizeof(port_status));
}
port_status = info.mpie_status;
get_receive_port_context(recv_holder_taskinfo->task, recv_name, &port_context);
printf(" -> %6d %8d 0x%016llx 0x%08x (%d) %s\n",
port_status.mps_qlimit,
port_status.mps_msgcount,
(uint64_t)port_context,
recv_name,
recv_holder_taskinfo->pid,
recv_holder_taskinfo->processName);
} else
printf(" 0x00000000 (-) Unknown Process\n");
}
printf("total = %d\n", taskinfo->tableCount + taskinfo->treeCount - emptycount);
printf("SEND = %d\n", sendcount);
printf("RECEIVE = %d\n", receivecount);
printf("SEND_ONCE = %d\n", sendoncecount);
printf("PORT_SET = %d\n", portsetcount);
printf("DEAD_NAME = %d\n", deadcount);
printf("DNREQUEST = %d\n", dncount);
printf("VOUCHERS = %d\n", vouchercount);
} else {
JSON_OBJECT_SET(json, identifier, "0x%08x", 0);
JSON_OBJECT_SET(json, pid, %d, -1);
JSON_OBJECT_SET(json, process, "unknown");
printf(" 0x00000000 (-) Unknown Process\n");
}
JSON_OBJECT_END(json); // non-kobject
}
uint32_t print_hex_data(char *outstr, size_t maxlen, char *prefix, char *desc, void *addr, int len) {
uint32_t print_hex_data(char *outstr, uint32_t maxlen, char *prefix, char *desc, void *addr, int len) {
int i;
unsigned char buff[17];
unsigned char *pc = addr;
uint32_t plen = 0;
if (desc != NULL)
plen += safesize(snprintf(&outstr[len], maxlen - plen, "%s%s:\n", prefix, desc));
plen += snprintf(&outstr[plen], safesize(maxlen - plen), "%s%s:\n", prefix, desc);
for (i = 0; i < len; i++) {
if ((i % 16) == 0) {
if (i != 0)
plen += safesize(snprintf(&outstr[len], maxlen - plen, " %s\n", buff));
plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %s\n", buff);
plen += safesize(snprintf(&outstr[len], maxlen - plen, "%s %04x ", prefix, i));
plen += snprintf(&outstr[plen], safesize(maxlen - plen), "%s %04x ", prefix, i);
}
plen += safesize(snprintf(&outstr[len], maxlen - plen, " %02x", pc[i]));
plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %02x", pc[i]);
if ((pc[i] < 0x20) || (pc[i] > 0x7e))
buff[i % 16] = '.';
@ -466,11 +710,11 @@ uint32_t print_hex_data(char *outstr, size_t maxlen, char *prefix, char *desc, v
}
while ((i % 16) != 0) {
plen += safesize(snprintf(&outstr[len], maxlen - plen, " "));
plen += snprintf(&outstr[plen], safesize(maxlen - plen), " ");
i++;
}
plen += safesize(snprintf(&outstr[len], maxlen - plen, " %s\n", buff));
plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %s\n", buff);
return plen;
}

View File

@ -43,7 +43,7 @@ static uint32_t k2n_hash(natural_t kobject) {
return (uint64_t)kobject * 2654435761 >> 32;
}
struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natural_t kobject) {
static struct k2n_table_node *k2n_table_lookup_next_internal(struct k2n_table_node *node, natural_t kobject) {
while (node) {
if (kobject == node->kobject)
return node;
@ -54,6 +54,13 @@ struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natura
return NULL;
}
struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natural_t kobject) {
if (!node) {
return NULL;
}
return k2n_table_lookup_next_internal(node->next, kobject);
}
struct k2n_table_node *k2n_table_lookup(struct k2n_table_node **table, natural_t kobject) {
uint32_t hv;
struct k2n_table_node *node;
@ -62,7 +69,7 @@ struct k2n_table_node *k2n_table_lookup(struct k2n_table_node **table, natural_t
node = table[hv & K2N_TABLE_MASK];
return k2n_table_lookup_next(node, kobject);
return k2n_table_lookup_next_internal(node, kobject);
}
static void k2n_table_enter(struct k2n_table_node **table, natural_t kobject, ipc_info_name_t *info_name) {
@ -76,6 +83,7 @@ static void k2n_table_enter(struct k2n_table_node **table, natural_t kobject, ip
node->kobject = kobject;
node->info_name = info_name;
assert(kobject == info_name->iin_object);
node->next = table[hv & K2N_TABLE_MASK];
table[hv & K2N_TABLE_MASK] = node;
@ -191,7 +199,7 @@ kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_t target_
}
if (KERN_SUCCESS == thread_get_mach_voucher(threadPorts[i], 0, &th_voucher) && th_voucher != IPC_VOUCHER_NULL) {
char *detail = copy_voucher_detail(mach_task_self(), th_voucher);
char *detail = copy_voucher_detail(mach_task_self(), th_voucher, NULL);
taskinfo->threadInfos[i].voucher_detail = strndup(detail, VOUCHER_DETAIL_MAXLEN);
free(detail);
@ -242,19 +250,19 @@ void get_exc_behavior_string(exception_behavior_t b, char *out_string, size_t le
out_string[0]='\0';
if (b & MACH_EXCEPTION_CODES)
strncat(out_string, "MACH +", len);
strlcat(out_string, "MACH +", len);
switch (b & ~MACH_EXCEPTION_CODES) {
case EXCEPTION_DEFAULT:
strncat(out_string, " DEFAULT", len);
strlcat(out_string, " DEFAULT", len);
break;
case EXCEPTION_STATE:
strncat(out_string, " STATE", len);
strlcat(out_string, " STATE", len);
break;
case EXCEPTION_STATE_IDENTITY:
strncat(out_string, " IDENTITY", len);
strlcat(out_string, " IDENTITY", len);
break;
default:
strncat(out_string, " UNKNOWN", len);
strlcat(out_string, " UNKNOWN", len);
}
}
@ -263,37 +271,40 @@ void get_exc_mask_string(exception_mask_t m, char *out_string, size_t len)
out_string[0]='\0';
if (m & (1<<EXC_BAD_ACCESS))
strncat(out_string, " BAD_ACCESS", len);
strlcat(out_string, " BAD_ACCESS", len);
if (m & (1<<EXC_BAD_INSTRUCTION))
strncat(out_string," BAD_INSTRUCTION", len);
strlcat(out_string," BAD_INSTRUCTION", len);
if (m & (1<<EXC_ARITHMETIC))
strncat(out_string," ARITHMETIC", len);
strlcat(out_string," ARITHMETIC", len);
if (m & (1<<EXC_EMULATION))
strncat(out_string," EMULATION", len);
strlcat(out_string," EMULATION", len);
if (m & (1<<EXC_SOFTWARE))
strncat(out_string," SOFTWARE", len);
strlcat(out_string," SOFTWARE", len);
if (m & (1<<EXC_BREAKPOINT))
strncat(out_string," BREAKPOINT", len);
strlcat(out_string," BREAKPOINT", len);
if (m & (1<<EXC_SYSCALL))
strncat(out_string," SYSCALL", len);
strlcat(out_string," SYSCALL", len);
if (m & (1<<EXC_MACH_SYSCALL))
strncat(out_string," MACH_SYSCALL", len);
strlcat(out_string," MACH_SYSCALL", len);
if (m & (1<<EXC_RPC_ALERT))
strncat(out_string," RPC_ALERT", len);
strlcat(out_string," RPC_ALERT", len);
if (m & (1<<EXC_CRASH))
strncat(out_string," CRASH", len);
strlcat(out_string," CRASH", len);
if (m & (1<<EXC_RESOURCE))
strncat(out_string," RESOURCE", len);
strlcat(out_string," RESOURCE", len);
if (m & (1<<EXC_GUARD))
strncat(out_string," GUARD", len);
strlcat(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) {
@ -303,17 +314,27 @@ kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo)
header_required = FALSE;
}
get_exc_behavior_string(taskinfo->exceptionInfo.behaviors[i], behavior_string, sizeof(behavior_string));
get_exc_mask_string(taskinfo->exceptionInfo.masks[i], mask_string, 200);
get_exc_mask_string(taskinfo->exceptionInfo.masks[i], mask_string, sizeof(mask_string));
JSON_OBJECT_BEGIN(json);
JSON_OBJECT_SET(json, port, "0x%08x", taskinfo->exceptionInfo.ports[i]);
JSON_OBJECT_SET(json, flavor, "0x%03x", taskinfo->exceptionInfo.flavors[i]);
JSON_OBJECT_SET(json, behavior, "%s", behavior_string);
JSON_OBJECT_SET(json, mask, "%s", mask_string);
JSON_OBJECT_END(json); // exception port
printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo->exceptionInfo.ports[i], taskinfo->exceptionInfo.flavors[i], behavior_string, mask_string);
}
}
JSON_ARRAY_END(json); // exception ports
return KERN_SUCCESS;
}
kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo, JSON_t json)
{
kern_return_t kret = KERN_SUCCESS;
mach_msg_type_number_t threadcount = taskinfo->threadCount;
@ -321,7 +342,12 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
boolean_t newline_required = TRUE;
struct my_per_thread_info * info = NULL;
JSON_KEY(json, threads);
JSON_ARRAY_BEGIN(json);
for (int i = 0; i < threadcount; i++) {
JSON_OBJECT_BEGIN(json);
info = &taskinfo->threadInfos[i];
if (header_required) {
printf("Thread_KObject Thread-ID Port Description.");
@ -337,12 +363,19 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
/* TODO: Should print tid and stuff */
printf("0x%08x ", info->th_kobject);
printf("0x%llx ", info->th_id);
JSON_OBJECT_SET(json, kobject, "0x%08x", info->th_kobject);
JSON_OBJECT_SET(json, tid, "0x%llx", info->th_id);
}
if (info->voucher_detail != NULL) {
/* TODO: include voucher detail in JSON */
printf("%s\n", info->voucher_detail);
}
JSON_KEY(json, exception_ports);
JSON_ARRAY_BEGIN(json);
/* print the thread exception ports also */
if (taskinfo->threadExceptionInfos != NULL)
{
@ -354,6 +387,8 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
if (excinfo->count > 0) {
boolean_t header_required = TRUE;
for (int i = 0; i < excinfo->count; i++) {
JSON_OBJECT_BEGIN(json);
if (excinfo->ports[i] != MACH_PORT_NULL) {
if (header_required) {
printf("\n exc_port flavor <behaviors> mask -> name owner\n");
@ -361,6 +396,12 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
}
get_exc_behavior_string(excinfo->behaviors[i], behavior_string, sizeof(behavior_string));
get_exc_mask_string(excinfo->masks[i], mask_string, sizeof(mask_string));
JSON_OBJECT_SET(json, port, "0x%08x", excinfo->ports[i]);
JSON_OBJECT_SET(json, flavor, "0x%03x", excinfo->flavors[i]);
JSON_OBJECT_SET(json, behavior, "%s", behavior_string);
JSON_OBJECT_SET(json, mask, "%s", mask_string);
printf(" 0x%08x 0x%03x <%s> %s " , excinfo->ports[i], excinfo->flavors[i], behavior_string, mask_string);
ipc_info_name_t actual_sendinfo;
@ -368,6 +409,12 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
my_per_task_info_t *recv_holder_taskinfo;
mach_port_name_t recv_name = MACH_PORT_NULL;
if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(&actual_sendinfo, &recv_holder_taskinfo, &recv_name)) {
JSON_OBJECT_SET(json, name, "0x%08x", recv_name);
JSON_OBJECT_SET(json, ipc-object, "0x%08x", actual_sendinfo.iin_object);
JSON_OBJECT_SET(json, pid, %d, recv_holder_taskinfo->pid);
JSON_OBJECT_SET(json, process, "%s", recv_holder_taskinfo->processName);
printf(" -> 0x%08x 0x%08x (%d) %s\n",
recv_name,
actual_sendinfo.iin_object,
@ -382,13 +429,15 @@ kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
printf("\n");
}
JSON_OBJECT_END(json); // exception port
}
}
}
JSON_ARRAY_END(json); // exception ports
JSON_OBJECT_END(json); // thread
}
JSON_ARRAY_END(json); // threads
printf("\n");
return kret;
}

View 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

View File

@ -41,6 +41,7 @@
unsigned long phys_mem = 0; /* amount of physical memory in bytes */
unsigned int phys_pages = 0; /* number of physical memory pages */
int sleep_seconds = 1;
int requested_hysteresis_seconds = 0;
boolean_t quiet_mode_on = FALSE;
boolean_t simulate_mode_on = FALSE;
@ -83,6 +84,7 @@ usage(void)
" -p <percent-free> - allocate memory until percent free is this (or less)\n"
" -s <seconds> - how long to sleep between checking for a set percent level\n"
" -w <percent-free> - don't allocate, just wait until percent free is this then exit\n"
" -y <seconds> - Hysteresis Interval: how long to wait after requested percntage free is reached, before exiting program. Requires the usage of the -p option\n"
" -v <print VM stats> - print VM statistics every sampling interval\n"
" -Q <quiet mode> - reduces the tool's output\n"
" -S - simulate the system's memory pressure level without applying any real pressure\n"
@ -457,6 +459,7 @@ munch_for_percentage(unsigned int sleep_seconds, unsigned int wait_percent_free,
{
int total_pages_allocated = 0;
int current_stable_timer = 0; /* in seconds */
unsigned int current_percent = 0;
boolean_t page_op = PAGE_OP_FREE;
unsigned int pages_to_process = 0;
@ -539,6 +542,18 @@ munch_for_percentage(unsigned int sleep_seconds, unsigned int wait_percent_free,
printf(".");
fflush(stdout);
}
/* Stability has been reached; Increment current_stable_timer by sleep_seconds */
if (current_stable_timer <= requested_hysteresis_seconds){
current_stable_timer += sleep_seconds;
/* Debug only */
/* printf("\n Percentage Free stable for %d seconds", current_stable_timer); */
} else {
printf ("\n Maintained memory pressure to %d percent free for more than %d seconds. Stopping pressure now.", current_percent, requested_hysteresis_seconds);
return;
}
print_vm_stats_on_page_processing = FALSE;
}
@ -566,7 +581,7 @@ main(int argc, char * const argv[])
unsigned int print_vm_stats = 0;
char level[10];
while ((opt = getopt(argc, argv, "hl:p:s:w:vQS")) != -1) {
while ((opt = getopt(argc, argv, "hl:p:s:w:y:vQS")) != -1) {
switch (opt) {
case 'h':
usage();
@ -599,6 +614,9 @@ main(int argc, char * const argv[])
case 'w':
wait_percent_free = atoi(optarg);
break;
case 'y':
requested_hysteresis_seconds = atoi(optarg);
break;
case 'v':
print_vm_stats = 1;
break;
@ -618,6 +636,13 @@ main(int argc, char * const argv[])
return 0;
}
if (requested_hysteresis_seconds > 0) {
if (desired_percent == 0) {
printf("Hysteresis time may only be specified in conjunction with a non-zero value for the -p option. \n");
usage();
}
}
phys_mem = read_sysctl_int("hw.physmem");
phys_pages = (unsigned int) (phys_mem / PAGE_SIZE);

View 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)

46
mslutil/mslutil.1 Normal file
View File

@ -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

96
mslutil/mslutil.c Normal file
View File

@ -0,0 +1,96 @@
//
// 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 | vmlite]\n");
}
static int send_msl_command(uint64_t pid, uint64_t flavor)
{
uint64_t flags = flavor;
flags <<= 32;
flags |= (pid & 0xFFFFFFFF);
int ret = sysctlbyname("kern.memorystatus_vm_pressure_send", 0, 0, &flags, sizeof(flags));
if (ret) {
printf("send_msl_command - sysctl: kern.memorystatus_vm_pressure_send failed %s\n", strerror(errno));
} else {
printf("send_msl_command - success!\n");
}
return ret;
}
int main(int argc, const char * argv[])
{
if (argc < 3) {
print_usage();
exit(1);
}
int ret = -1;
pid_t pid = atoi(argv[1]);
if (pid <= 0 || pid > BSD_PID_MAX) {
printf("Invalid pid\n");
exit(1);
}
if (strcmp(argv[2], "--enable") == 0) {
if (argc < 4) {
print_usage();
exit(1);
}
uint64_t flavor = 0;
if (strcmp(argv[3], "full") == 0) {
flavor = MEMORYSTATUS_ENABLE_MSL_MALLOC | MEMORYSTATUS_ENABLE_MSL_VM;
} else if (strcmp(argv[3], "malloc") == 0) {
flavor = MEMORYSTATUS_ENABLE_MSL_MALLOC;
} else if (strcmp(argv[3], "vm") == 0) {
flavor = MEMORYSTATUS_ENABLE_MSL_VM;
} else if (strcmp(argv[3], "lite") == 0) {
flavor = MEMORYSTATUS_ENABLE_MSL_LITE_FULL;
} else if (strcmp(argv[3], "vmlite") == 0) {
flavor = MEMORYSTATUS_ENABLE_MSL_LITE_VM;
}
if (flavor == 0) {
print_usage();
exit(1);
}
ret = send_msl_command(pid, flavor);
} else if (strcmp(argv[2], "--disable") == 0) {
ret = send_msl_command(pid, MEMORYSTATUS_DISABLE_MSL);
} else {
print_usage();
exit(1);
}
if (ret != 0) {
exit(1);
} else {
exit(0);
}
}

View File

@ -56,6 +56,28 @@ static io_registry_entry_t gOptionsRef;
static bool gUseXML;
static bool gUseForceSync;
#if TARGET_OS_BRIDGE /* Stuff for nvram bridge -> intel */
#include <dlfcn.h>
#include <libMacEFIManager/MacEFIHostInterfaceAPI.h>
static kern_return_t LinkMacNVRAMSymbols(void);
static kern_return_t GetMacOFVariable(char *name, char **value);
static kern_return_t SetMacOFVariable(char *name, char *value);
static kern_return_t DeleteMacOFVariable(char *name);
static bool gBridgeToIntel;
static void *gDL_handle;
static void *gNvramInterface;
static void (*hostInterfaceInitialize_fptr)(void);
static void *(*createNvramHostInterface_fptr)(const char *handle);
static kern_return_t (*destroyNvramHostInterface_fptr)(void *interface);
static kern_return_t (*getNVRAMVariable_fptr)(void *interface, char *name, char **buffer, uint32_t *size);
static kern_return_t (*setNVRAMVariable_fptr)(void *interface, char *name, char *buffer);
static kern_return_t (*deleteNVRAMVariable_fptr)(void *interface, char *name);
static void (*hostInterfaceDeinitialize_fptr)(void); /* may not need? */
#endif /* TARGET_OS_BRIDGE */
int main(int argc, char **argv)
{
@ -88,6 +110,12 @@ int main(int argc, char **argv)
for (str += 1 ; *str; str++) {
switch (*str) {
case 'p' :
#if TARGET_OS_BRIDGE
if (gBridgeToIntel) {
fprintf(stderr, "-p not supported for Mac NVRAM store.\n");
return 1;
}
#endif
PrintOFVariables();
break;
@ -96,6 +124,12 @@ int main(int argc, char **argv)
break;
case 'f':
#if TARGET_OS_BRIDGE
if (gBridgeToIntel) {
fprintf(stderr, "-f not supported for Mac NVRAM store.\n");
return 1;
}
#endif
cnt++;
if (cnt < argc && *argv[cnt] != '-') {
ParseFile(argv[cnt]);
@ -104,16 +138,33 @@ int main(int argc, char **argv)
}
break;
case 'd':
cnt++;
if (cnt < argc && *argv[cnt] != '-') {
DeleteOFVariable(argv[cnt]);
} else {
UsageMessage("missing name");
}
break;
case 'd':
cnt++;
if (cnt < argc && *argv[cnt] != '-') {
#if TARGET_OS_BRIDGE
if (gBridgeToIntel) {
if ((result = DeleteMacOFVariable(argv[cnt])) != KERN_SUCCESS) {
errx(1, "Error deleting variable - '%s': %s (0x%08x)", argv[cnt],
mach_error_string(result), result);
}
}
else
#endif
{
DeleteOFVariable(argv[cnt]);
}
} else {
UsageMessage("missing name");
}
break;
case 'c':
#if TARGET_OS_BRIDGE
if (gBridgeToIntel) {
fprintf(stderr, "-c not supported for Mac NVRAM store.\n");
return 1;
}
#endif
ClearOFVariables();
break;
case 's':
@ -121,6 +172,17 @@ int main(int argc, char **argv)
// commit the variable to nonvolatile storage
gUseForceSync = true;
break;
#if TARGET_OS_BRIDGE
case 'm':
// used to set nvram variables on the Intel side
// from the ARM side (Bridge -> Mac)
fprintf(stdout, "Using Mac NVRAM store.\n");
LinkMacNVRAMSymbols();
gBridgeToIntel = true;
break;
#endif
default:
strcpy(errorMessage, "no such option as --");
errorMessage[strlen(errorMessage)-1] = *str;
@ -159,6 +221,9 @@ static void UsageMessage(char *message)
printf("\t-f set firmware variables from a text file\n");
printf("\t-d delete the named variable\n");
printf("\t-c delete all variables\n");
#if TARGET_OS_BRIDGE
printf("\t-m set nvram variables on macOS from bridgeOS\n");
#endif
printf("\tname=value set named variable\n");
printf("\tname print variable\n");
printf("Note that arguments and options are executed in order.\n");
@ -405,18 +470,40 @@ static void SetOrGetOFVariable(char *str)
if (set == 1) {
// On sets, the OF variable's value follows the equal sign.
value = str;
result = SetOFVariable(name, value);
NVRamSyncNow(name); /* Try syncing the new data to device, best effort! */
#if TARGET_OS_BRIDGE
if (gBridgeToIntel) {
result = SetMacOFVariable(name, value);
}
else
#endif
{
result = SetOFVariable(name, value);
NVRamSyncNow(name); /* Try syncing the new data to device, best effort! */
}
if (result != KERN_SUCCESS) {
errx(1, "Error setting variable - '%s': %s", name,
mach_error_string(result));
}
} else {
result = GetOFVariable(name, &nameRef, &valueRef);
if (result != KERN_SUCCESS) {
errx(1, "Error getting variable - '%s': %s", name,
mach_error_string(result));
#if TARGET_OS_BRIDGE
if (gBridgeToIntel) {
result = GetMacOFVariable(name, &value);
if (result != KERN_SUCCESS) {
errx(1, "Error getting variable - '%s': %s", name,
mach_error_string(result));
}
nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8);
valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value, kCFStringEncodingUTF8);
free(value);
}
else
#endif
{
result = GetOFVariable(name, &nameRef, &valueRef);
if (result != KERN_SUCCESS) {
errx(1, "Error getting variable - '%s': %s", name,
mach_error_string(result));
}
}
PrintOFVariable(nameRef, valueRef, 0);
@ -425,6 +512,51 @@ static void SetOrGetOFVariable(char *str)
}
}
#if TARGET_OS_BRIDGE
static kern_return_t LinkMacNVRAMSymbols()
{
gDL_handle = dlopen("libMacEFIHostInterface.dylib", RTLD_LAZY);
if (gDL_handle == NULL) {
errx(errno, "Failed to dlopen libMacEFIHostInterface.dylib");
return KERN_FAILURE; /* NOTREACHED */
}
hostInterfaceInitialize_fptr = dlsym(gDL_handle, "hostInterfaceInitialize");
if (hostInterfaceInitialize_fptr == NULL) {
errx(errno, "failed to link hostInterfaceInitialize");
}
createNvramHostInterface_fptr = dlsym(gDL_handle, "createNvramHostInterface");
if (createNvramHostInterface_fptr == NULL) {
errx(errno, "failed to link createNvramHostInterface");
}
destroyNvramHostInterface_fptr = dlsym(gDL_handle, "destroyNvramHostInterface");
if (destroyNvramHostInterface_fptr == NULL) {
errx(errno, "failed to link destroyNvramHostInterface");
}
getNVRAMVariable_fptr = dlsym(gDL_handle, "getNVRAMVariable");
if (getNVRAMVariable_fptr == NULL) {
errx(errno, "failed to link getNVRAMVariable");
}
setNVRAMVariable_fptr = dlsym(gDL_handle, "setNVRAMVariable");
if (setNVRAMVariable_fptr == NULL) {
errx(errno, "failed to link setNVRAMVariable");
}
deleteNVRAMVariable_fptr = dlsym(gDL_handle, "deleteNVRAMVariable");
if (deleteNVRAMVariable_fptr == NULL) {
errx(errno, "failed to link deleteNVRAMVariable");
}
hostInterfaceDeinitialize_fptr = dlsym(gDL_handle, "hostInterfaceDeinitialize");
if (hostInterfaceDeinitialize_fptr == NULL) {
errx(errno, "failed to link hostInterfaceDeinitialize");
}
/* also do the initialization */
hostInterfaceInitialize_fptr();
gNvramInterface = createNvramHostInterface_fptr(NULL);
return KERN_SUCCESS;
}
#endif
// GetOFVariable(name, nameRef, valueRef)
//
@ -446,6 +578,19 @@ static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef,
return KERN_SUCCESS;
}
#if TARGET_OS_BRIDGE
// GetMacOFVariable(name, value)
//
// Get the named firmware variable from the Intel side.
// Return the value in value
//
static kern_return_t GetMacOFVariable(char *name, char **value)
{
uint32_t value_size;
return getNVRAMVariable_fptr(gNvramInterface, name, value, &value_size);
}
#endif
// SetOFVariable(name, value)
//
@ -458,58 +603,64 @@ static kern_return_t SetOFVariable(char *name, char *value)
CFTypeID typeID;
kern_return_t result = KERN_SUCCESS;
nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
kCFStringEncodingUTF8);
if (nameRef == 0) {
errx(1, "Error creating CFString for key %s", name);
}
valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, nameRef, 0, 0);
if (valueRef) {
typeID = CFGetTypeID(valueRef);
CFRelease(valueRef);
valueRef = ConvertValueToCFTypeRef(typeID, value);
if (valueRef == 0) {
errx(1, "Error creating CFTypeRef for value %s", value);
} result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
} else {
while (1) {
// In the default case, try data, string, number, then boolean.
valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value);
if (valueRef != 0) {
result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
if (result == KERN_SUCCESS) break;
}
valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value);
if (valueRef != 0) {
result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
if (result == KERN_SUCCESS) break;
}
valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value);
if (valueRef != 0) {
result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
if (result == KERN_SUCCESS) break;
}
valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value);
if (valueRef != 0) {
result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
if (result == KERN_SUCCESS) break;
}
break;
nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
kCFStringEncodingUTF8);
if (nameRef == 0) {
errx(1, "Error creating CFString for key %s", name);
}
valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, nameRef, 0, 0);
if (valueRef) {
typeID = CFGetTypeID(valueRef);
CFRelease(valueRef);
valueRef = ConvertValueToCFTypeRef(typeID, value);
if (valueRef == 0) {
errx(1, "Error creating CFTypeRef for value %s", value);
} result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
} else {
while (1) {
// In the default case, try data, string, number, then boolean.
valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value);
if (valueRef != 0) {
result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
if (result == KERN_SUCCESS) break;
}
valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value);
if (valueRef != 0) {
result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
if (result == KERN_SUCCESS) break;
}
valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value);
if (valueRef != 0) {
result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
if (result == KERN_SUCCESS) break;
}
valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value);
if (valueRef != 0) {
result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
if (result == KERN_SUCCESS) break;
}
break;
}
}
}
CFRelease(nameRef);
return result;
}
#if TARGET_OS_BRIDGE
static kern_return_t SetMacOFVariable(char *name, char *value)
{
return setNVRAMVariable_fptr(gNvramInterface, name, value);
}
#endif
// DeleteOFVariable(name)
//
@ -521,6 +672,13 @@ static void DeleteOFVariable(char *name)
SetOFVariable(kIONVRAMDeletePropertyKey, name);
}
#if TARGET_OS_BRIDGE
static kern_return_t DeleteMacOFVariable(char *name)
{
return deleteNVRAMVariable_fptr(gNvramInterface, name);
}
#endif
static void NVRamSyncNow(char *name)
{
if (!gUseForceSync) {
@ -586,8 +744,29 @@ static void PrintOFVariable(const void *key, const void *value, void *context)
long length;
CFTypeID typeID;
if (gUseXML) {
CFDataRef data;
CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, &key, &value, 1,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict == NULL) {
errx(1, "Error creating dictionary for variable value");
}
data = CFPropertyListCreateData( kCFAllocatorDefault, dict, kCFPropertyListXMLFormat_v1_0, 0, NULL );
if (data == NULL) {
errx(1, "Error creating xml plist for variable");
}
fwrite(CFDataGetBytePtr(data), sizeof(UInt8), CFDataGetLength(data), stdout);
CFRelease(dict);
CFRelease(data);
return;
}
// Get the OF variable's name.
nameLen = CFStringGetLength(key) + 1;
nameLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key),
kCFStringEncodingUTF8) + 1;
nameBuffer = malloc(nameLen);
if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) )
nameString = nameBuffer;
@ -609,7 +788,8 @@ static void PrintOFVariable(const void *key, const void *value, void *context)
else sprintf(numberBuffer, "0x%x", number);
valueString = numberBuffer;
} else if (typeID == CFStringGetTypeID()) {
valueLen = CFStringGetLength(value) + 1;
valueLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value),
kCFStringEncodingUTF8) + 1;
valueBuffer = malloc(valueLen + 1);
if ( valueBuffer && CFStringGetCString(value, valueBuffer, valueLen, kCFStringEncodingUTF8) )
valueString = valueBuffer;
@ -683,39 +863,39 @@ static void ClearOFVariable(const void *key, const void *value, void *context)
//
static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value)
{
CFTypeRef valueRef = 0;
long cnt, cnt2, length;
unsigned long number, tmp;
CFTypeRef valueRef = 0;
long cnt, cnt2, length;
unsigned long number, tmp;
if (typeID == CFBooleanGetTypeID()) {
if (!strcmp("true", value)) valueRef = kCFBooleanTrue;
else if (!strcmp("false", value)) valueRef = kCFBooleanFalse;
} else if (typeID == CFNumberGetTypeID()) {
number = strtol(value, 0, 0);
valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
&number);
} else if (typeID == CFStringGetTypeID()) {
valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value,
kCFStringEncodingUTF8);
} else if (typeID == CFDataGetTypeID()) {
length = strlen(value);
for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) {
if (value[cnt] == '%') {
if (!ishexnumber(value[cnt + 1]) ||
!ishexnumber(value[cnt + 2])) return 0;
number = toupper(value[++cnt]) - '0';
if (number > 9) number -= 7;
tmp = toupper(value[++cnt]) - '0';
if (tmp > 9) tmp -= 7;
number = (number << 4) + tmp;
value[cnt2] = number;
} else value[cnt2] = value[cnt];
}
valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)value,
cnt2, kCFAllocatorDefault);
} else return 0;
if (typeID == CFBooleanGetTypeID()) {
if (!strcmp("true", value)) valueRef = kCFBooleanTrue;
else if (!strcmp("false", value)) valueRef = kCFBooleanFalse;
} else if (typeID == CFNumberGetTypeID()) {
number = strtol(value, 0, 0);
valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
&number);
} else if (typeID == CFStringGetTypeID()) {
valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value,
kCFStringEncodingUTF8);
} else if (typeID == CFDataGetTypeID()) {
length = strlen(value);
for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) {
if (value[cnt] == '%') {
if (!ishexnumber(value[cnt + 1]) ||
!ishexnumber(value[cnt + 2])) return 0;
number = toupper(value[++cnt]) - '0';
if (number > 9) number -= 7;
tmp = toupper(value[++cnt]) - '0';
if (tmp > 9) tmp -= 7;
number = (number << 4) + tmp;
value[cnt2] = number;
} else value[cnt2] = value[cnt];
}
valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)value,
cnt2, kCFAllocatorDefault);
} else return 0;
return valueRef;
return valueRef;
}
static void SetOFVariableFromFile(const void *key, const void *value, void *context)
@ -729,7 +909,8 @@ static void SetOFVariableFromFile(const void *key, const void *value, void *cont
char *nameString;
// Get the variable's name.
nameLen = CFStringGetLength(key) + 1;
nameLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(key),
kCFStringEncodingUTF8) + 1;
nameBuffer = malloc(nameLen);
if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) )
nameString = nameBuffer;

0
pagesize.tproj/pagesize.sh Executable file → Normal file
View File

View File

@ -102,7 +102,7 @@ od_passwd(char* uname, char* locn, char* aname)
aname = strdup(uname);
}
master_mode = (getuid() == 0);
master_mode = (geteuid() == 0);
change_pass_on_self = (strcmp(aname, uname) == 0);
if (locn) {
@ -150,10 +150,20 @@ od_passwd(char* uname, char* locn, char* aname)
printf("Changing password for %s.\n", uname);
bool isSecureToken = false;
if (master_mode) {
CFArrayRef authorityValues = NULL;
authorityValues = ODRecordCopyValues(rec, kODAttributeTypeAuthenticationAuthority, &error);
if (authorityValues != NULL) {
isSecureToken = CFArrayContainsValue(authorityValues, CFRangeMake(0, CFArrayGetCount(authorityValues)), (const void *)CFSTR(";SecureToken;"));
CFRelease(authorityValues);
}
}
/*
* Prompt for password if not super-user, or if changing a remote node.
* Prompt for password if not super-user, or if changing a remote node, or if changing a record that uses SecureToken.
*/
int needs_auth = (!master_mode || CFStringCompareWithOptions(location, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo);
int needs_auth = (!master_mode || CFStringCompareWithOptions(location, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo || isSecureToken);
if (needs_auth) {
char prompt[BUFSIZ];
@ -166,6 +176,15 @@ od_passwd(char* uname, char* locn, char* aname)
if (p) {
oldpass = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
memset(p, 0, strlen(p));
if (!change_pass_on_self) {
if (!ODRecordSetNodeCredentials(rec, authname, oldpass, &error)) {
show_error(error);
exit(1);
}
CFRelease(oldpass);
oldpass = NULL;
}
}
}

View File

@ -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.private.opendirectoryd.identity</key>
<true/>
</dict>
</plist>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2016 Apple Inc. All rights reserved.
* Copyright (c) 2011-2019 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
@ -25,14 +25,16 @@
#include <TargetConditionals.h>
#define INFO_FILE 1
#if !TARGET_OS_EMBEDDED
#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
#define INFO_NIS 2
#define INFO_OPEN_DIRECTORY 3
#define INFO_PAM 4
#endif
extern int file_passwd(char *, char *);
#ifdef INFO_NIS
extern int nis_passwd(char *, char *);
#endif
#ifdef INFO_OPEN_DIRECTORY
extern int od_passwd(char *, char *, char*);
#endif

View File

@ -10,7 +10,7 @@
.Nm
.Ar verb
.Ar policy
.Ar uuid
.Ar uuid | path
.Sh DESCRIPTION
.Nm
sets policy for a specific UUID or mach-o file with the kernel

View File

@ -206,7 +206,7 @@ static
void
usage(void)
{
fprintf(stderr, "usage: %s <verb> <policy> <uuid>\n", getprogname());
fprintf(stderr, "usage: %s <verb> <policy> <uuid | path>\n", getprogname());
fprintf(stderr, "Verbs:\n");
fprintf(stderr, "\tclear\tClear all policies for a given UUID\n");
fprintf(stderr, "\tadd\tAdd a specific policy\n");

View File

@ -1,5 +1,5 @@
#include <TargetConditionals.h>
#if TARGET_OS_EMBEDDED
#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
subsystem dummy 0;
#else
#include <IOKit/kext/kextmanager_mig.defs>

View File

@ -61,10 +61,9 @@ __unused static const char rcsid[] =
#ifdef __APPLE__
#include <TargetConditionals.h>
#if !TARGET_OS_EMBEDDED
// Darling doesn't have any kernel extensions
//#include "KextManager.h"
//#include <IOKit/kext/kextmanager_types.h>
#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
#include "kextmanager.h"
#include <IOKit/kext/kextmanager_types.h>
#endif
#include <mach/mach_port.h> // allocate
#include <mach/mach.h> // task_self, etc
@ -77,7 +76,7 @@ __unused static const char rcsid[] =
void usage(void);
u_int get_pageins(void);
#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
int reserve_reboot(void);
#endif
@ -155,7 +154,7 @@ main(int argc, char *argv[])
err(1, NULL);
}
#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
if (!qflag && !lflag) { // shutdown(8) has already checked w/kextd
if ((errno = reserve_reboot()))
err(1, "couldn't lock for reboot");
@ -201,9 +200,6 @@ main(int argc, char *argv[])
utx.ut_type = SHUTDOWN_TIME;
gettimeofday(&utx.ut_tv, NULL);
pututxline(&utx);
int newvalue = 1;
sysctlbyname("kern.willshutdown", NULL, NULL, &newvalue, sizeof(newvalue));
}
#else
logwtmp("~", "shutdown", "");
@ -305,7 +301,7 @@ get_pageins(void)
return pageins;
}
#if defined(__APPLE__) && !TARGET_OS_EMBEDDED
#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
// XX this routine is also in shutdown.tproj; it would be nice to share
#define WAITFORLOCK 1
@ -315,7 +311,6 @@ get_pageins(void)
int
reserve_reboot(void)
{
/*
int rval = ELAST + 1;
kern_return_t macherr = KERN_FAILURE;
mach_port_t kxport, tport = MACH_PORT_NULL, myport = MACH_PORT_NULL;
@ -363,7 +358,5 @@ finish:
mach_port_mod_refs(tport, myport, MACH_PORT_RIGHT_RECEIVE, -1);
return rval;
*/
return 0;
}
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999-2016 Apple Inc. All rights reserved.
* Copyright (c) 1999-2019 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
@ -119,12 +119,12 @@ struct entry {
};
struct th_info {
uintptr_t thread;
uint64_t thread;
int depth;
int vfslookup;
int curpri;
long *pathptr;
long pathname[NUMPARMS + 1];
int64_t *pathptr;
int64_t pathname[NUMPARMS + 1];
struct entry th_entry[MAX_NESTED];
};
@ -291,6 +291,12 @@ print_time(char *p, unsigned int useconds, unsigned int seconds)
}
}
static void
resetscr(void)
{
(void)endwin();
}
int
main(int argc, char *argv[])
{
@ -305,10 +311,12 @@ main(int argc, char *argv[])
exit(1);
}
if (0 != reexec_to_match_kernel()) {
#if !defined(__arm64__)
if (0 != reexec_to_match_kernel()) {
fprintf(stderr, "Could not re-execute: %d\n", errno);
exit(1);
}
#endif
/* get our name */
if (argc > 0) {
@ -412,6 +420,7 @@ main(int argc, char *argv[])
printf("Unrecognized TERM type, try vt100\n");
exit(1);
}
atexit(resetscr);
cbreak();
timeout(100);
noecho();
@ -1054,7 +1063,7 @@ find_proc_names(void)
}
static struct th_info *
find_thread(uintptr_t thread)
find_thread(uint64_t thread)
{
struct th_info *ti;
@ -1120,7 +1129,7 @@ sort_scalls(void)
if ((unsigned long)(((double)now - te->otime) / divisor) > 5000000) {
ti->thread = 0;
ti->vfslookup = 0;
ti->pathptr = (long *)NULL;
ti->pathptr = (int64_t *)NULL;
ti->pathname[0] = 0;
num_of_threads--;
}
@ -1325,7 +1334,7 @@ sample_sc(void)
th_state[i].depth = 0;
th_state[i].thread = 0;
th_state[i].vfslookup = 0;
th_state[i].pathptr = (long *)NULL;
th_state[i].pathptr = (int64_t *)NULL;
th_state[i].pathname[0] = 0;
}
num_of_threads = 0;
@ -1341,7 +1350,7 @@ sample_sc(void)
for (i = 0; i < count; i++) {
int debugid, baseid;
uintptr_t thread;
uint64_t thread;
int type, code;
uint64_t now;
struct th_info *ti, *switched_out, *switched_in;
@ -1361,7 +1370,7 @@ sample_sc(void)
baseid = debugid & 0xffff0000;
if (type == vfs_lookup) {
long *sargptr;
int64_t *sargptr;
if ((ti = find_thread(thread)) == (struct th_info *)0)
continue;

View File

@ -1,5 +1,5 @@
#include <TargetConditionals.h>
#if TARGET_OS_EMBEDDED
#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
subsystem dummy 0;
#else
#include <IOKit/kext/kextmanager_mig.defs>

View File

@ -78,7 +78,6 @@ __FBSDID("$FreeBSD: src/sbin/shutdown/shutdown.c,v 1.28 2005/01/25 08:40:51 delp
#include <bootstrap_priv.h>
#include <reboot2.h>
#include <utmpx.h>
#include <sys/sysctl.h>
#include "pathnames.h"
#endif /* __APPLE__ */
@ -484,9 +483,6 @@ die_you_gravy_sucking_pig_dog()
utx.ut_type = SHUTDOWN_TIME;
gettimeofday(&utx.ut_tv, NULL);
pututxline(&utx);
int newvalue = 1;
sysctlbyname("kern.willshutdown", NULL, NULL, &newvalue, sizeof(newvalue));
}
#else
logwtmp("~", "shutdown", "");

228
stackshot.tproj/stackshot.c Normal file
View File

@ -0,0 +1,228 @@
/* 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, " -g : get thread group data\n");
fprintf (stderr, " -s : fork a sleep process\n");
fprintf (stderr, " -L : disable loadinfo\n");
fprintf (stderr, " -k : active kernel threads only\n");
fprintf (stderr, " -I : disable io statistics\n");
fprintf (stderr, " -S : stress test: while(1) stackshot; \n");
fprintf (stderr, " -p PID : target a pid\n");
fprintf (stderr, " -E : grab existing kernel buffer\n");
exit(1);
}
void forksleep() {
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
execlp("sleep", "sleep", "30", NULL);
perror("execlp");
exit(1);
}
}
int main(int argc, char **argv) {
uint32_t iostats = 0;
uint32_t active_kernel_threads_only = 0;
uint32_t bootprofile = 0;
uint32_t thread_group = 0;
uint32_t coalition = 0;
uint32_t instrs_cycles = 0;
uint32_t flags = 0;
uint32_t loadinfo = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO;
boolean_t delta = FALSE;
boolean_t sleep = FALSE;
boolean_t stress = FALSE;
pid_t pid = -1;
int c;
while ((c = getopt(argc, argv, "SgIikbcLdtsp:E")) != EOF) {
switch(c) {
case 'I':
iostats |= STACKSHOT_NO_IO_STATS;
break;
case 'k':
active_kernel_threads_only |= STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY;
loadinfo &= ~STACKSHOT_SAVE_LOADINFO;
break;
case 'b':
bootprofile |= STACKSHOT_GET_BOOT_PROFILE;
break;
case 'c':
coalition |= STACKSHOT_SAVE_JETSAM_COALITIONS;
break;
case 'i':
instrs_cycles |= STACKSHOT_INSTRS_CYCLES;
break;
case 'L':
loadinfo = 0;
break;
case 'g':
thread_group |= STACKSHOT_THREAD_GROUP;
break;
case 'd':
delta = TRUE;
break;
case 's':
sleep = TRUE;
break;
case 'p':
pid = atoi(optarg);
break;
case 'S':
stress = TRUE;
break;
case 'E':
flags = flags | STACKSHOT_RETRIEVE_EXISTING_BUFFER;
break;
case '?':
case 'h':
default:
usage(argv);
break;
}
}
if (thread_group && delta) {
fprintf(stderr, "stackshot does not support delta snapshots with thread groups\n");
return 1;
}
if (optind < argc) {
usage(argv);
}
top:
;
void * config = stackshot_config_create();
if (!config) {
perror("stackshot_config_create");
return 1;
}
flags = flags | loadinfo | STACKSHOT_SAVE_IMP_DONATION_PIDS | STACKSHOT_GET_DQ | STACKSHOT_KCDATA_FORMAT | STACKSHOT_THREAD_WAITINFO |
bootprofile | active_kernel_threads_only | iostats | thread_group | coalition | instrs_cycles;
int err = stackshot_config_set_flags(config, flags);
if (err != 0) {
perror("stackshot_config_set_flags");
return 1;
}
if (pid != -1) {
int err = stackshot_config_set_pid(config, pid);
if (err != 0) {
perror("stackshot_config_set_flags");
return 1;
}
}
err = stackshot_capture_with_config(config);
if (err != 0) {
perror("stackshot_capture_with_config");
return 1;
}
void *buf = stackshot_config_get_stackshot_buffer(config);
if (!buf) {
perror("stackshot_config_get_stackshot_buffer");
return 1;
}
uint32_t size = stackshot_config_get_stackshot_size(config);
if (delta) {
// output the original somewhere?
uint64_t time = stackshot_get_mach_absolute_time(buf, size);
err = stackshot_config_dealloc_buffer(config);
assert(!err);
flags |= STACKSHOT_COLLECT_DELTA_SNAPSHOT;
int err = stackshot_config_set_flags(config, flags);
if (err != 0) {
perror("stackshot_config_set_flags");
return 1;
}
err = stackshot_config_set_delta_timestamp(config, time);
if (err != 0) {
perror("stackshot_config_delta_timestamp");
return 1;
}
if (sleep) {
forksleep();
}
usleep(10000);
err = stackshot_capture_with_config(config);
if (err != 0) {
perror("stackshot_capture_with_config");
return 1;
}
buf = stackshot_config_get_stackshot_buffer(config);
if (!buf) {
perror("stackshot_config_get_stackshot_buffer");
return 1;
}
size = stackshot_config_get_stackshot_size(config);
}
if (stress) {
if (config) {
stackshot_config_dealloc(config);
config = NULL;
}
goto top;
}
fwrite(buf, size, 1, stdout);
}

View 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:

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,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>BuildSystemType</key>
<string>Original</string>
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
<false/>
</dict>

View File

@ -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.security.cs.debugger.root</key>
<true/>
</dict>
</plist>

View File

@ -12,6 +12,7 @@
.Op Fl b
.Op Fl t Ar thruput_tier
.Op Fl l Ar latency_tier
.Op Fl a
.Ar program
.Oo
.Ar arg1
@ -65,6 +66,8 @@ Set throughput tier of the process to
.It Fl l
Set latency tier of the process to
.Ar latency_tier .
.It Fl a
Run the program with the resource management policies given to applications.
.El
.Pp
.Sh SEE ALSO

View 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);
}

View File

@ -180,6 +180,36 @@
<key>WorkingDirectory</key>
<string>/tmp/</string>
</dict>
<dict>
<key>Arch</key>
<string>platform-native</string>
<key>AsRoot</key>
<true/>
<key>Command</key>
<array>
<string>/usr/local/bin/proc_uuid_policy</string>
<string>add</string>
<string>alt-dyld</string>
<string>/usr/bin/yes</string>
</array>
<key>RequiredResources</key>
<array>
<dict>
<key>Properties</key>
<string>deviceClass == &apos;iPhone&apos;</string>
</dict>
</array>
<key>IgnoreCrashes</key>
<array>
</array>
<key>TestName</key>
<string>test_proc_uuid_policy</string>
<key>TestSpecificLogs</key>
<array>
</array>
<key>WorkingDirectory</key>
<string>/tmp/</string>
</dict>
</array>
</dict>
</plist>

View File

@ -64,6 +64,11 @@ optionally converted to a human-readable, plain-text format.
.Pp
.Nm
operates according to the command flag specified.
.Pp
NOTE:
.Nm
is obsolete. The command you probably want is
.Xr ktrace 1
.Sh COMMANDS
.Bl -tag -width Ds
.It Fl h

View File

@ -158,7 +158,7 @@ typedef struct event *event_t;
struct event {
event_t ev_next;
uintptr_t ev_thread;
uint64_t ev_thread;
uint32_t ev_debugid;
uint64_t ev_timestamp;
};
@ -168,10 +168,10 @@ typedef struct lookup *lookup_t;
struct lookup {
lookup_t lk_next;
uintptr_t lk_thread;
uintptr_t lk_dvp;
long *lk_pathptr;
long lk_pathname[NUMPARMS + 1];
uint64_t lk_thread;
uint64_t lk_dvp;
int64_t *lk_pathptr;
int64_t lk_pathname[NUMPARMS + 1];
};
typedef struct threadmap *threadmap_t;
@ -179,8 +179,8 @@ typedef struct threadmap *threadmap_t;
struct threadmap {
threadmap_t tm_next;
uintptr_t tm_thread;
uintptr_t tm_pthread;
uint64_t tm_thread;
uint64_t tm_pthread;
boolean_t tm_deleteme;
char tm_command[MAXCOMLEN + 1];
};
@ -219,7 +219,7 @@ static void codesc_find_dupes(void);
static int read_command_map(int, uint32_t);
static void read_cpu_map(int);
static void find_thread_command(kd_buf *, char **);
static void create_map_entry(uintptr_t, char *);
static void create_map_entry(uint64_t, char *);
static void getdivisor();
static unsigned long argtoul();
@ -240,17 +240,17 @@ static void Log_trace();
static void read_trace();
static void signal_handler(int);
static void signal_handler_RAW(int);
static void delete_thread_entry(uintptr_t);
static void find_and_insert_tmp_map_entry(uintptr_t, char *);
static void create_tmp_map_entry(uintptr_t, uintptr_t);
static void find_thread_name(uintptr_t, char **, boolean_t);
static void delete_thread_entry(uint64_t);
static void find_and_insert_tmp_map_entry(uint64_t, char *);
static void create_tmp_map_entry(uint64_t, uint64_t);
static void find_thread_name(uint64_t, char **, boolean_t);
static void execute_process(char * const argv[]);
static int writetrace(int);
static int write_command_map(int);
static int debugid_compar(const void *, const void *);
static threadmap_t find_thread_entry(uintptr_t);
static threadmap_t find_thread_entry(uint64_t);
static void saw_filter_class(uint8_t class);
static void saw_filter_end_range(uint8_t end_class);
@ -591,7 +591,7 @@ int write_command_map(int fd)
static
lookup_t handle_lookup_event(uintptr_t thread, int debugid, kd_buf *kdp)
lookup_t handle_lookup_event(uint64_t thread, int debugid, kd_buf *kdp)
{
lookup_t lkp;
int hashid;
@ -640,7 +640,7 @@ lookup_t handle_lookup_event(uintptr_t thread, int debugid, kd_buf *kdp)
static
void delete_lookup_event(uintptr_t thread, lookup_t lkp_to_delete)
void delete_lookup_event(uint64_t thread, lookup_t lkp_to_delete)
{
lookup_t lkp;
lookup_t lkp_prev;
@ -671,7 +671,7 @@ void delete_lookup_event(uintptr_t thread, lookup_t lkp_to_delete)
static
void insert_start_event(uintptr_t thread, int debugid, uint64_t now)
void insert_start_event(uint64_t thread, int debugid, uint64_t now)
{
event_t evp;
int hashid;
@ -699,7 +699,7 @@ void insert_start_event(uintptr_t thread, int debugid, uint64_t now)
static
uint64_t consume_start_event(uintptr_t thread, int debugid, uint64_t now)
uint64_t consume_start_event(uint64_t thread, int debugid, uint64_t now)
{
event_t evp;
event_t evp_prev;
@ -1006,7 +1006,7 @@ void read_trace(void)
uint64_t prev;
uint64_t prevdelta = 0;
uint32_t cpunum = 0;
uintptr_t thread;
uint64_t thread;
double x = 0.0;
double y = 0.0;
double event_elapsed_time = 0;
@ -1057,6 +1057,7 @@ void read_trace(void)
debugid = kdp->debugid;
debugid_base = debugid & DBG_FUNC_MASK;
now = kdp->timestamp & KDBG_TIMESTAMP_MASK;
cpunum = kdbg_get_cpu(kdp);
/*
* Is this event from an IOP? If so, there will be no
@ -1085,7 +1086,6 @@ void read_trace(void)
bias = now;
now -= bias;
cpunum = kdbg_get_cpu(kdp);
thread = kdp->arg5;
if (lines == 64 || firsttime)
@ -1142,7 +1142,7 @@ void read_trace(void)
if ( !lkp) {
int t_debugid;
uintptr_t t_thread;
uint64_t t_thread;
if ((debugid & DBG_FUNC_START) || debugid == MACH_MAKERUNNABLE) {
@ -1227,24 +1227,26 @@ void read_trace(void)
len -= 51;
else
len = 0;
#ifdef __LP64__
#if defined(__LP64__) || defined(__arm64__)
fprintf(output_file, "%-16lx %-51s %-16lx %-2d %s\n", lkp->lk_dvp, &strptr[len], thread, cpunum, command);
fprintf(output_file, "%-16llx %-51s %-16" PRIx64 " %-2d %s\n", (uint64_t)lkp->lk_dvp, &strptr[len], thread, cpunum, command);
#else
fprintf(output_file, "%-8x %-51s %-8lx %-2d %s\n", (unsigned int)lkp->lk_dvp, &strptr[len], thread, cpunum, command);
fprintf(output_file, "%-8x %-51s %-8" PRIx64 " %-2d %s\n", (unsigned int)lkp->lk_dvp, &strptr[len], thread, cpunum, command);
#endif
delete_lookup_event(thread, lkp);
} else if (debugid == TRACE_INFO_STRING) {
#ifdef __LP64__
fprintf(output_file, "%-32s%-36s %-16lx %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command);
#if defined(__LP64__) || defined(__arm64__)
fprintf(output_file, "%-32s%-36s %-16" PRIx64 " %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command);
#else
fprintf(output_file, "%-16s%-46s %-8lx %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command);
fprintf(output_file, "%-16s%-46s %-8" PRIx64 " %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command);
#endif
} else {
#ifdef __LP64__
fprintf(output_file, "%-16lx %-16lx %-16lx %-16lx %-16lx %-2d %s\n", kdp->arg1, kdp->arg2, kdp->arg3, kdp->arg4, thread, cpunum, command);
#if defined(__LP64__) || defined(__arm64__)
fprintf(output_file, "%-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-2d %s\n",
(uint64_t)kdp->arg1, (uint64_t)kdp->arg2, (uint64_t)kdp->arg3, (uint64_t)kdp->arg4, thread, cpunum, command);
#else
fprintf(output_file, "%-8lx %-8lx %-8lx %-8lx %-8lx %-2d %s\n", kdp->arg1, kdp->arg2, kdp->arg3, kdp->arg4, thread, cpunum, command);
fprintf(output_file, "%-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-2d %s\n",
(uint64_t)kdp->arg1, (uint64_t)kdp->arg2, (uint64_t)kdp->arg3, (uint64_t)kdp->arg4, thread, cpunum, command);
#endif
}
lines++;
@ -1293,12 +1295,15 @@ int main (int argc, char* argv[], char *envp[])
fprintf(stderr, "Could not re-execute: %d\n", errno);
exit(1);
}
} else {
}
#if !defined(__arm64__)
else {
if (0 != reexec_to_match_kernel()) {
fprintf(stderr, "Could not re-execute: %d\n", errno);
exit(1);
}
}
#endif
if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_PASSIVE) < 0) {
printf("setiopolicy failed\n");
exit(1);
@ -2385,8 +2390,9 @@ parse_codefile(const char *filename)
if (stat_buf.st_size != 0)
{
if ((file_addr = mmap(0, file_size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FILE, fd, 0)) == (char*) -1)
file_addr = mmap(0, file_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_FILE, fd, 0);
if (file_addr == MAP_FAILED)
{
printf("Error: Can't map file: %s\n", filename);
close(fd);
@ -2426,7 +2432,7 @@ parse_codefile(const char *filename)
*/
count++;
// Grow the size of codesc to store new entries.
/* Grow the size of codesc to store new entries. */
size_t total_count = codesc_idx + count;
code_type_t *new_codesc = (code_type_t *)realloc(codesc, (total_count) * sizeof(code_type_t));
@ -2437,10 +2443,11 @@ parse_codefile(const char *filename)
codesc = new_codesc;
bzero((char *)(codesc + codesc_idx), count * sizeof(code_type_t));
for (line = 1, j = 0; j < file_size && codesc_idx < total_count; codesc_idx++)
for (line = 1, j = 0; j < file_size && codesc_idx < total_count;
codesc_idx++)
{
/* Skip blank lines */
while (file_addr[j] == '\n')
while (j < file_size && file_addr[j] == '\n')
{
j++;
line++;
@ -2459,7 +2466,7 @@ parse_codefile(const char *filename)
/* We didn't find a debugid code - skip this line */
if (verbose_flag)
printf("Error: while parsing line %d, skip\n", line);
while (file_addr[j] != '\n' && j < file_size)
while (j < file_size && file_addr[j] != '\n')
j++;
codesc_idx--;
line++;
@ -2467,15 +2474,25 @@ parse_codefile(const char *filename)
}
/* Skip whitespace */
while (file_addr[j] == ' ' || file_addr[j] == '\t')
while (j < file_size && (file_addr[j] == ' ' || file_addr[j] == '\t'))
{
j++;
}
if (j >= file_size)
{
break;
}
/* Get around old file that had count at the beginning */
if (file_addr[j] == '\n')
{
/* missing debugid string - skip */
if (verbose_flag)
printf("Error: while parsing line %d, (0x%x) skip\n", line, codesc[codesc_idx].debugid);
{
printf("Error: while parsing line %d, (0x%x) skip\n", line,
codesc[codesc_idx].debugid);
}
j++;
codesc_idx--;
@ -2483,12 +2500,25 @@ parse_codefile(const char *filename)
continue;
}
if (j >= file_size)
{
break;
}
/* Next is the debugid string terminated by a newline */
codesc[codesc_idx].debug_string = &file_addr[j];
/* Null out the newline terminator */
while ((j < file_size) && (file_addr[j] != '\n'))
while (j < file_size && file_addr[j] != '\n')
{
j++;
}
if (j >= file_size)
{
break;
}
file_addr[j] = '\0'; /* File must be read-write */
j++;
line++;
@ -2703,8 +2733,8 @@ read_command_map(int fd, uint32_t count)
printf("Thread Command\n");
for (i = 0; i < total_threads; i++) {
printf ("0x%lx %s\n",
mapptr[i].thread,
printf ("0x%" PRIx64 " %s\n",
(uint64_t)mapptr[i].thread,
mapptr[i].command);
}
}
@ -2713,7 +2743,7 @@ read_command_map(int fd, uint32_t count)
}
void
create_map_entry(uintptr_t thread, char *command)
create_map_entry(uint64_t thread, char *command)
{
threadmap_t tme;
int hashid;
@ -2736,7 +2766,7 @@ create_map_entry(uintptr_t thread, char *command)
}
void
delete_thread_entry(uintptr_t thread)
delete_thread_entry(uint64_t thread)
{
threadmap_t tme = 0;
threadmap_t tme_prev;
@ -2766,7 +2796,7 @@ delete_thread_entry(uintptr_t thread)
}
void
find_and_insert_tmp_map_entry(uintptr_t pthread, char *command)
find_and_insert_tmp_map_entry(uint64_t pthread, char *command)
{
threadmap_t tme = 0;
threadmap_t tme_prev;
@ -2801,7 +2831,7 @@ find_and_insert_tmp_map_entry(uintptr_t pthread, char *command)
}
void
create_tmp_map_entry(uintptr_t thread, uintptr_t pthread)
create_tmp_map_entry(uint64_t thread, uint64_t pthread)
{
threadmap_t tme;
@ -2821,7 +2851,7 @@ create_tmp_map_entry(uintptr_t thread, uintptr_t pthread)
threadmap_t
find_thread_entry(uintptr_t thread)
find_thread_entry(uint64_t thread)
{
threadmap_t tme;
int hashid;
@ -2836,7 +2866,7 @@ find_thread_entry(uintptr_t thread)
}
void
find_thread_name(uintptr_t thread, char **command, boolean_t deleteme)
find_thread_name(uint64_t thread, char **command, boolean_t deleteme)
{
threadmap_t tme;
@ -2852,7 +2882,7 @@ find_thread_name(uintptr_t thread, char **command, boolean_t deleteme)
void
find_thread_command(kd_buf *kbufp, char **command)
{
uintptr_t thread;
uint64_t thread;
threadmap_t tme;
int debugid_base;

View File

@ -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>task_for_pid-allow</key>
<true/>
</dict>
</plist>

View 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!

View File

@ -36,9 +36,10 @@ env -i \
LANG="${LANG}" \
HOME="${HOME}" \
$EXTRA_ARGS \
TOOLCHAINS="${TOOLCHAINS}" \
xcrun -sdk "${SDKROOT}" xcodebuild install \
-target zic \
-sdk "macosx" \
-sdk "macosxinternal" \
SRCROOT="${SRCROOT}" \
OBJROOT="${OBJROOT}" \
SYMROOT="${ZICHOST_SYMROOT}" \

View File

@ -68,22 +68,28 @@ if [ $? -ne 0 ]; then
fi
if [ -n "$RC_BRIDGE" ]; then
ACTUAL_PLATFORM_NAME="bridge${PLATFORM_NAME#watch}"
ACTUAL_PLATFORM_NAME="bridgeos"
else
ACTUAL_PLATFORM_NAME="${PLATFORM_NAME}"
fi
case "$ACTUAL_PLATFORM_NAME" in
iphone*|appletv*|watch*)
bridge*)
LOCALTIME="GMT"
;;
esac
case "$ACTUAL_PLATFORM_NAME" in
iphone*|appletv*|watch*|bridge*)
mkdir -p "${PRIVATEDIR}/var/db"
mkdir -p -m a+rx "${PRIVATEDIR}/var/db/timezone"
# This link must precisely start with TZDIR followed by a slash. radar:13532660
ln -hfs "/var/db/timezone/zoneinfo/${LOCALTIME}" "${PRIVATEDIR}/var/db/timezone/localtime"
;;
macosx|bridge*)
macosx)
mkdir -p "${PRIVATEDIR}/etc"
ln -hfs "/usr/share/zoneinfo/${LOCALTIME}" "${PRIVATEDIR}/etc/localtime"
ln -hfs "/var/db/timezone/zoneinfo/${LOCALTIME}" "${PRIVATEDIR}/etc/localtime"
;;
*)
echo "Unsupported platform: $ACTUAL_PLATFORM_NAME"

View 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);

View File

@ -3,21 +3,45 @@ set -e
set -x
if [ -n "$RC_BRIDGE" ]; then
ACTUAL_PLATFORM_NAME="bridge${PLATFORM_NAME#watch}"
ACTUAL_PLATFORM_NAME="bridgeos"
else
ACTUAL_PLATFORM_NAME="${PLATFORM_NAME}"
fi
# This sets up the paths for the default set of zoneinfo files
# On iOS, watchOS, and tvOS, this is handled by tzd in coordination with
# launchd on first boot.
# On macOS, the directory needs to be setup # during mastering for SIP
# protection, and the symlink for the BaseSystem.
# On bridgeOS tzd doesn't exist, so needs this all setup statically.
default_zoneinfo_setup()
{
mkdir -p "${DSTROOT}/private/var/db/timezone"
chmod 555 "${DSTROOT}/private/var/db/timezone"
ln -hfs "/usr/share/zoneinfo.default" "${DSTROOT}/private/var/db/timezone/zoneinfo"
}
case "$ACTUAL_PLATFORM_NAME" in
iphone*|appletv*|watch*)
iphone*|appletv*|watch*|macosx)
ditto "${BUILT_PRODUCTS_DIR}/zoneinfo" "${DSTROOT}/usr/share/zoneinfo.default"
ln -hfs "/var/db/timezone/zoneinfo" "${DSTROOT}/usr/share/zoneinfo"
case "$ACTUAL_PLATFORM_NAME" in
macosx)
default_zoneinfo_setup
;;
esac
;;
macosx|bridge*)
ditto "${BUILT_PRODUCTS_DIR}/zoneinfo" "${DSTROOT}/usr/share/zoneinfo"
bridge*)
# Since bridgeOS lacks any mechanism to change timezones (currently),
# and in the interest of saving space, only GMT is installed.
mkdir -p "${DSTROOT}/usr/share/zoneinfo.default"
chmod 555 "${DSTROOT}/usr/share/zoneinfo.default"
ditto "${BUILT_PRODUCTS_DIR}/zoneinfo/GMT" "${DSTROOT}/usr/share/zoneinfo.default/GMT"
default_zoneinfo_setup
;;
*)
echo "Unsupported platform: $ACTUAL_PLATFORM_NAME"
exit 1
;;
esac

View 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 $
*/
/*
@ -30,10 +30,12 @@
#ifndef lint
#ifndef NOID
static const char privatehid[] = "@(#)private.h 7.53";
static const char privatehid[] = "@(#)private.h 8.6";
#endif /* !defined NOID */
#endif /* !defined lint */
#define GRANDPARENTED "Local time zone must be set--use tzsetup"
/*
** Defaults for preprocessor symbols.
** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
@ -43,10 +45,6 @@ static const char privatehid[] = "@(#)private.h 7.53";
#define HAVE_GETTEXT 0
#endif /* !defined HAVE_GETTEXT */
#ifndef HAVE_STRERROR
#define HAVE_STRERROR 1
#endif /* !defined HAVE_STRERROR */
#ifndef HAVE_SYMLINK
#define HAVE_SYMLINK 1
#endif /* !defined HAVE_SYMLINK */
@ -71,47 +69,94 @@ static const char privatehid[] = "@(#)private.h 7.53";
#include "stdio.h"
#include "errno.h"
#include "string.h"
#include "limits.h" /* for CHAR_BIT */
#include "limits.h" /* for CHAR_BIT et al. */
#include "time.h"
#include "stdlib.h"
#if HAVE_GETTEXT - 0
#if HAVE_GETTEXT
#include "libintl.h"
#endif /* HAVE_GETTEXT - 0 */
#endif /* HAVE_GETTEXT */
#if HAVE_SYS_WAIT_H - 0
#if HAVE_SYS_WAIT_H
#include <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 */

View 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));

192
zic.tproj/tzfile.h Normal file
View File

@ -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 */

View 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.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,181 @@
//
// SymbolicationHelper.c
// zlog
//
// Created by Rasha Eqbal on 2/26/18.
//
#include "SymbolicationHelper.h"
/*
* Most of the CoreSymbolication code here has been copied from ioclasscount in the IOKitTools project.
*/
#define kAddressKey CFSTR("Address")
#define kNameKey CFSTR("Name")
#define kPathKey CFSTR("Path")
#define kSegmentsKey CFSTR("Segments")
#define kSizeKey CFSTR("Size")
#define kUuidKey CFSTR("UUID")
static void AddSymbolOwnerSummary(CSSymbolOwnerRef owner, CFMutableDictionaryRef binaryImages);
static void ShowBinaryImage(const void *key, const void *value, void *context);
/*
* Symbolicates 'addr' using the 'symbolicator' passed in.
* Adds owner info to 'binaryImages' for offline symbolication.
*
* Top-level function that needs to be called on each frame address in the backtrace.
*/
void PrintSymbolicatedAddress(CSSymbolicatorRef symbolicator, mach_vm_address_t addr, CFMutableDictionaryRef binaryImages)
{
printf("0x%llx", addr);
CSSymbolOwnerRef ownerInfo = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, addr, kCSNow);
if (!CSIsNull(ownerInfo)) {
const char *moduleName = CSSymbolOwnerGetName(ownerInfo);
if (moduleName) {
printf(" <%s>", moduleName);
}
}
CSSymbolRef symbolInfo = CSSymbolicatorGetSymbolWithAddressAtTime(symbolicator, addr, kCSNow);
if (!CSIsNull(symbolInfo)) {
printf(" %s", CSSymbolGetName(symbolInfo));
}
CSSourceInfoRef sourceInfo = CSSymbolicatorGetSourceInfoWithAddressAtTime(symbolicator, addr, kCSNow);
if (!CSIsNull(sourceInfo)) {
const char *fileName = CSSourceInfoGetPath(sourceInfo);
if (fileName) {
printf(" at %s:%d", fileName, CSSourceInfoGetLineNumber(sourceInfo));
}
}
printf("\n");
AddSymbolOwnerSummary(ownerInfo, binaryImages);
}
/*
* Adds symbolication information for 'owner' to 'binaryImages' to help with offline symbolication.
*
* This is called from PrintSymbolicatedAddress() on the symbol owner for each address it symbolicates.
*/
static void AddSymbolOwnerSummary(CSSymbolOwnerRef owner, CFMutableDictionaryRef binaryImages)
{
const CFUUIDBytes *uuidBytes = NULL;
CFUUIDRef uuid = NULL;
CFStringRef uuidString = NULL, path = NULL, name = NULL;
CFMutableDictionaryRef summaryDict = NULL;
__block CSSegmentRef textSegment = kCSNull, textExecSegment = kCSNull;
CSSegmentRef segment = kCSNull;
CSRange range;
CFNumberRef address = NULL, size = NULL;
#define RETURN_IF_NULL(ptr) \
if (!(ptr)) { \
goto cleanup; \
}
uuidBytes = CSSymbolOwnerGetCFUUIDBytes(owner);
if (uuidBytes) {
uuid = CFUUIDCreateFromUUIDBytes(NULL, *uuidBytes);
if (uuid) {
uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
}
}
RETURN_IF_NULL(uuidString);
if (!CFDictionaryContainsKey(binaryImages, uuidString)) {
summaryDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
RETURN_IF_NULL(summaryDict);
CFDictionarySetValue(summaryDict, kUuidKey, uuidString);
path = CFStringCreateWithCString(kCFAllocatorDefault, CSSymbolOwnerGetPath(owner), kCFStringEncodingUTF8);
RETURN_IF_NULL(path);
CFDictionarySetValue(summaryDict, kPathKey, path);
name = CFStringCreateWithCString(kCFAllocatorDefault, CSSymbolOwnerGetName(owner), kCFStringEncodingUTF8);
RETURN_IF_NULL(name);
CFDictionarySetValue(summaryDict, kNameKey, name);
CSSymbolOwnerForeachSegment(owner, ^(CSSegmentRef segment) {
if (strcmp(CSRegionGetName(segment), "__TEXT SEGMENT") == 0) {
textSegment = segment;
CSRetain(textSegment);
} else if (strcmp(CSRegionGetName(segment), "__TEXT_EXEC SEGMENT") == 0) {
textExecSegment = segment;
CSRetain(textExecSegment);
}
});
segment = !CSIsNull(textExecSegment) ? textExecSegment : textSegment;
if (CSIsNull(segment)) {
goto cleanup;
}
range = CSRegionGetRange(segment);
address = CFNumberCreate(NULL, kCFNumberLongLongType, &range.location);
RETURN_IF_NULL(address);
CFDictionarySetValue(summaryDict, kAddressKey, address);
size = CFNumberCreate(NULL, kCFNumberLongLongType, &range.length);
RETURN_IF_NULL(size);
CFDictionarySetValue(summaryDict, kSizeKey, size);
CFDictionarySetValue(binaryImages, uuidString, summaryDict);
}
cleanup:
if (size) CFRelease(size);
if (address) CFRelease(address);
if (!CSIsNull(textExecSegment)) CSRelease(textExecSegment);
if (!CSIsNull(textSegment)) CSRelease(textSegment);
if (name) CFRelease(name);
if (path) CFRelease(path);
if (summaryDict) CFRelease(summaryDict);
if (uuidString) CFRelease(uuidString);
if (uuid) CFRelease(uuid);
}
/*
* Prints offline symbolication information for the images passed in 'binaryImages'.
*
* Top-level function that needs to be called if the tool wants to include support
* for offline symbolication.
*/
void PrintBinaryImagesInfo(CFMutableDictionaryRef binaryImages)
{
if (CFDictionaryGetCount(binaryImages) > 0) {
printf("\nBinary Images:\n");
CFDictionaryApplyFunction(binaryImages, ShowBinaryImage, NULL);
} else {
printf("No binary images\n");
}
}
/*
* Prints information about a binary image necessary for offline symbolication.
*
* This is called from PrintBinaryImagesInfo() on each element in 'binaryImages'.
*/
static void ShowBinaryImage(const void *key, const void *value, void *context)
{
char nameString[256] = {0}, uuidString[256] = {0}, pathString[256] = {0};
CFStringRef uuid = (CFStringRef)key;
CFStringGetCString(uuid, uuidString, sizeof(uuidString), kCFStringEncodingASCII);
CFDictionaryRef summary = (CFDictionaryRef)value;
CFStringRef name = CFDictionaryGetValue(summary, kNameKey);
CFStringGetCString(name, nameString, sizeof(nameString), kCFStringEncodingASCII);
CFStringRef path = CFDictionaryGetValue(summary, kPathKey);
CFStringGetCString(path, pathString, sizeof(pathString), kCFStringEncodingASCII);
CFNumberRef addressNumber = CFDictionaryGetValue(summary, kAddressKey);
CFNumberRef sizeNumber = CFDictionaryGetValue(summary, kSizeKey);
int64_t address, size;
CFNumberGetValue(addressNumber, kCFNumberSInt64Type, &address);
CFNumberGetValue(sizeNumber, kCFNumberSInt64Type, &size);
printf("%p - %p %s <%s> %s\n", (void*)address, (void*)address + size, nameString, uuidString, pathString);
}

View File

@ -0,0 +1,35 @@
//
// SymbolicationHelper.h
// zlog
//
// Created by Rasha Eqbal on 2/26/18.
//
#ifndef SymbolicationHelper_h
#define SymbolicationHelper_h
#include <CoreFoundation/CoreFoundation.h>
#include <CoreSymbolication/CoreSymbolication.h>
/*
* Call this function on each address that needs to be symbolicated.
*
* sym: The CSSymbolicatorRef which will be used for symbolication. For example, to symbolicate
* kernel addresses create a CSSymbolicatorRef by calling CSSymbolicatorCreateWithMachKernel().
* addr: The address that needs to be symbolicated.
* binaryImages: The dictionary that aggregates binary image info for offline symbolication.
*/
void PrintSymbolicatedAddress(CSSymbolicatorRef sym, mach_vm_address_t addr, CFMutableDictionaryRef binaryImages);
/*
* Call this function to dump binary image info required for offline symbolication.
*
* binaryImages: The dictionary that stores this info.
*
* The preferred way to use this is to create a CFMutableDictionaryRef with a call to CFDictionaryCreateMutable()
* and pass it in to PrintSymbolicatedAddress() when symbolicating addresses. This will auto-populate the dictionary,
* which just needs to be passed in here to print the relevant information.
*/
void PrintBinaryImagesInfo(CFMutableDictionaryRef binaryImages);
#endif /* SymbolicationHelper_h */

View File

@ -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.private.kernel.get-kext-info</key>
<true/>
</dict>
</plist>

56
zlog.tproj/zlog.1 Normal file
View File

@ -0,0 +1,56 @@
.\" Copyright (c) 2018, Apple Inc. All rights reserved.
.\"
.Dd February 21, 2018
.Dt ZLOG 1
.Os "Mac OS X"
.Sh NAME
.Nm zlog
.Nd show allocation backtraces for kernel zones
.Sh SYNOPSIS
.Nm
.Op Fl t
.Op Fl z Ar name Op Fl n Ar num | Fl l
.Op Fl h
.Sh DESCRIPTION
.Nm
displays allocation (and free, if used in corruption tracking mode with
the boot-arg "-zc") backtraces for zones that have zone logging enabled.
Zone logging can be turned on by using the boot-arg "zlog<N>=<name>",
where 'N' can range from 1 to 10, and 'name' is the name of the zone to
be tracked.
.Pp
.Nm
interprets the following options:
.Pp
.Bl -tag -width "disable -"
.\" -t
.It Fl t
(Default) list all the zones that have logging enabled
.\" -z
.It Fl z Ar name
show all allocation backtraces for zone
.Ar name
.\" -n
.It Fl n Ar num
Can be used in combination with the
.Fl z
option to show the top
.Ar num
backtraces with the most active references in the zone
.Ar name
.\" -l
.It Fl l
Can be used in combination with the
.Fl z
option to show the backtrace most likely contributing to a leak in the zone
.Ar name
(prints the backtrace with the most active references)
.\" -h
.It Fl h
show the help text
.El
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr zprint 1 ,
.Xr ioclasscount 1

241
zlog.tproj/zlog.c Normal file
View File

@ -0,0 +1,241 @@
//
// zlog.c
// zlog
//
// Created by Rasha Eqbal on 1/4/18.
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach_debug/mach_debug.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreSymbolication/CoreSymbolication.h>
#include "SymbolicationHelper.h"
extern kern_return_t
mach_zone_get_btlog_records(host_priv_t host,
mach_zone_name_t name,
zone_btrecord_array_t *recsp,
mach_msg_type_number_t *recsCntp);
extern kern_return_t
mach_zone_get_zlog_zones(host_priv_t host,
mach_zone_name_array_t *namesp,
mach_msg_type_number_t *namesCntp);
static int compare_zone_btrecords(const void *left, const void *right);
static void usage(FILE *stream, char **argv);
static void print_zone_info(const char *name);
static void get_zone_btrecords(const char *name, int topN);
static void list_zones_with_zlog_enabled(void);
static void usage(FILE *stream, char **argv)
{
fprintf (stream, "usage: %s [-t] [-z name [-n num | -l]] [-h]\n", argv[0]);
fprintf (stream, " -t : list all the zones that have logging enabled\n");
fprintf (stream, " -z <name> : show all allocation backtraces for zone <name>\n");
fprintf (stream, " -n <num> : show top <num> backtraces with the most active references in zone <name>\n");
fprintf (stream, " -l : show the backtrace most likely contributing to a leak in zone <name>\n");
fprintf (stream, " (prints the backtrace with the most active references)\n");
fprintf (stream, " -h : print this help text\n");
exit(stream != stdout);
}
static int compare_zone_btrecords(const void *left, const void *right)
{
zone_btrecord_t *btl = (zone_btrecord_t *)left;
zone_btrecord_t *btr = (zone_btrecord_t *)right;
return (btr->ref_count - btl->ref_count);
}
static void print_zone_info(const char *name)
{
mach_zone_name_t zname;
mach_zone_info_t zone_info;
kern_return_t kr;
strcpy(zname.mzn_name, name);
kr = mach_zone_info_for_zone(mach_host_self(), zname, &zone_info);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "error: call to mach_zone_info_for_zone() failed: %s\n", mach_error_string(kr));
exit(1);
}
printf("zone name : %s\n", name);
printf("element size (bytes) : %lld\n", zone_info.mzi_elem_size);
printf("in-use size (bytes) : %lld\n", zone_info.mzi_count * zone_info.mzi_elem_size);
printf("total size (bytes) : %lld\n", zone_info.mzi_cur_size);
printf("\n");
}
static void get_zone_btrecords(const char *name, int topN)
{
kern_return_t kr;
int i, j, index;
mach_zone_name_t zname;
unsigned int recs_count = 0;
zone_btrecord_t *recs, *recs_addr = NULL;
CSSymbolicatorRef kernelSym;
CFMutableDictionaryRef binaryImages;
/* Create kernel symbolicator */
kernelSym = CSSymbolicatorCreateWithMachKernel();
if (CSIsNull(kernelSym)) {
fprintf(stderr, "error: CSSymbolicatorCreateWithMachKernel() returned NULL\n");
exit(1);
}
/* Create dictionary to collect binary image info for offline symbolication */
binaryImages = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
/* Query the kernel for backtrace records */
strcpy(zname.mzn_name, name);
kr = mach_zone_get_btlog_records(mach_host_self(), zname, &recs_addr, &recs_count);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "error: call to mach_zone_get_btlog_records() failed: %s\n", mach_error_string(kr));
exit(1);
}
if (recs_count == 0) {
goto finish;
}
recs = recs_addr;
if (topN == 1) {
/* Print the backtrace with the highest no. of refs */
index = 0;
for (i = 0; i < recs_count; i++) {
if (recs[i].ref_count > recs[index].ref_count) {
index = i;
}
}
recs = recs_addr + index;
} else if (topN == 0) {
/* Print all backtraces */
topN = recs_count;
} else {
/* Sort the records by no. of refs, and print the top <topN> */
qsort(recs, recs_count, sizeof *recs, compare_zone_btrecords);
}
printf("printing top %d (out of %d) allocation backtrace(s) for zone %s\n", topN, recs_count, zname.mzn_name);
for (i = 0; i < topN; i++) {
printf("\nactive refs: %d operation type: %s\n", recs[i].ref_count,
(recs[i].operation_type == ZOP_ALLOC)? "ALLOC": (recs[i].operation_type == ZOP_FREE)? "FREE": "UNKNOWN");
for (j = 0; j < MAX_ZTRACE_DEPTH; j++) {
mach_vm_address_t addr = (mach_vm_address_t)recs[i].bt[j];
if (!addr) {
break;
}
PrintSymbolicatedAddress(kernelSym, addr, binaryImages);
}
}
/* Print relevant info for offline symbolication */
PrintBinaryImagesInfo(binaryImages);
CFRelease(binaryImages);
finish:
if ((recs_addr != NULL) && (recs_count != 0)) {
kr = vm_deallocate(mach_task_self(), (vm_address_t) recs_addr, (vm_size_t) (recs_count * sizeof *recs));
if (kr != KERN_SUCCESS) {
fprintf(stderr, "call to vm_deallocate() failed: %s\n", mach_error_string(kr));
exit(1);
}
}
CSRelease(kernelSym);
}
static void list_zones_with_zlog_enabled(void)
{
kern_return_t kr;
mach_zone_name_t *name = NULL;
unsigned int name_count = 0, i;
/* Get names for zones that have zone logging enabled */
kr = mach_zone_get_zlog_zones(mach_host_self(), &name, &name_count);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "error: call to mach_zone_get_zlog_zones() failed: %s\n", mach_error_string(kr));
exit(1);
}
if (name_count == 0) {
printf("zlog not enabled for any zones.\n");
} else {
printf("zlog enabled for zones...\n");
}
for (i = 0; i < name_count; i++) {
print_zone_info(name[i].mzn_name);
}
if ((name != NULL) && (name_count != 0)) {
kr = vm_deallocate(mach_task_self(), (vm_address_t) name, (vm_size_t) (name_count * sizeof *name));
if (kr != KERN_SUCCESS) {
fprintf(stderr, "call to vm_deallocate() failed: %s\n", mach_error_string(kr));
exit(1);
}
}
}
#define VERSION_STRING "zlog output version: 1"
int main(int argc, char *argv[])
{
int c, topN = 0;
const char *zone_name = NULL;
/* Identifier string for SpeedTracer parsing */
printf("%s\n\n", VERSION_STRING);
if (argc == 1) {
/* default when no arguments are specified */
list_zones_with_zlog_enabled();
printf("Run 'zlog -h' for usage info.\n");
return 0;
}
while ((c = getopt(argc, argv, "tz:n:lh")) != -1) {
switch(c) {
case 't':
list_zones_with_zlog_enabled();
break;
case 'z':
zone_name = optarg;
break;
case 'n':
topN = atoi(optarg);
break;
case 'l':
topN = 1;
break;
case 'h':
usage(stdout, argv);
break;
case '?':
default:
usage(stderr, argv);
break;
}
}
if (optind < argc) {
usage(stderr, argv);
}
if (zone_name) {
print_zone_info(zone_name);
get_zone_btrecords(zone_name, topN);
} else {
/* -n or -l was specified without -z */
if (topN != 0) {
usage(stderr, argv);
}
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show More