mirror of
https://github.com/darlinghq/darling-system_cmds.git
synced 2024-11-23 03:59:43 +00:00
system_cmds-854.40.2
This commit is contained in:
parent
c76f955c45
commit
c04e2cca25
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/system_cmds.xcodeproj/project.xcworkspace
|
||||
/system_cmds.xcodeproj/xcuserdata
|
||||
.DS_Store
|
||||
build/
|
||||
*~
|
||||
*.swp
|
2
.upstream_base_commits
Normal file
2
.upstream_base_commits
Normal file
@ -0,0 +1,2 @@
|
||||
#freebsd = https://github.com/freebsd/freebsd.git
|
||||
iostat.tproj/iostat.8 freebsd usr.sbin/iostat/iostat.8 4724d1448f7e7698308a1e05a7bb9b2069d68234
|
@ -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.
|
||||
|
@ -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
9
base.xcconfig
Normal 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;
|
@ -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
67
cpuctl.tproj/cpuctl.8
Normal 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
145
cpuctl.tproj/cpuctl.c
Normal 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;
|
||||
}
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
8
dynamic_pager.tproj/entitlements.plist
Normal file
8
dynamic_pager.tproj/entitlements.plist
Normal 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>
|
@ -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)
|
||||
|
@ -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
1117
gcore.tproj/convert.c
Normal file
File diff suppressed because it is too large
Load Diff
24
gcore.tproj/convert.h
Normal file
24
gcore.tproj/convert.h
Normal 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 */
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
8
gcore.tproj/gcore-entitlements.plist
Normal file
8
gcore.tproj/gcore-entitlements.plist
Normal 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>
|
201
gcore.tproj/gcore-internal.1
Normal file
201
gcore.tproj/gcore-internal.1
Normal 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
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
115
gcore.tproj/vm.c
115
gcore.tproj/vm.c
@ -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 ||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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}"
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 .
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
119
lsmp.tproj/json.h
Normal 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_ */
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
46
mslutil/mslutil.1
Normal 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
96
mslutil/mslutil.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
0
pagesize.tproj/pagesize.sh
Executable file → Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
8
passwd.tproj/passwd.entitlements
Normal file
8
passwd.tproj/passwd.entitlements
Normal 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>
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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
228
stackshot.tproj/stackshot.c
Normal 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);
|
||||
}
|
@ -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
@ -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>
|
||||
|
8
taskpolicy.tproj/taskpolicy-entitlements.plist
Normal file
8
taskpolicy.tproj/taskpolicy-entitlements.plist
Normal 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>
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 == 'iPhone'</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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
8
vm_purgeable_stat.tproj/entitlements.plist
Normal file
8
vm_purgeable_stat.tproj/entitlements.plist
Normal 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>
|
@ -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!
|
||||
|
||||
|
@ -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}" \
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
192
zic.tproj/tzfile.h
Normal 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 */
|
112
zic.tproj/zic.8
112
zic.tproj/zic.8
@ -1,4 +1,4 @@
|
||||
.\" $FreeBSD: src/usr.sbin/zic/zic.8,v 1.19 2005/02/13 23:45:54 ru Exp $
|
||||
.\" $FreeBSD: head/contrib/tzcode/zic/zic.8 214411 2010-10-27 07:14:46Z edwin $
|
||||
.Dd June 20, 2004
|
||||
.Dt ZIC 8
|
||||
.Os
|
||||
@ -119,10 +119,13 @@ Any line that is blank (after comment stripping) is ignored.
|
||||
Non-blank lines are expected to be of one of three types:
|
||||
rule lines, zone lines, and link lines.
|
||||
.Pp
|
||||
Names (such as month names) must be in English and are case insensitive.
|
||||
Abbreviations, if used, must be unambiguous in context.
|
||||
.Pp
|
||||
A rule line has the form:
|
||||
.Dl "Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
|
||||
.Dl "Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S"
|
||||
For example:
|
||||
.Dl "Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D
|
||||
.Dl "Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D"
|
||||
.Pp
|
||||
The fields that make up a rule line are:
|
||||
.Bl -tag -width "LETTER/S" -offset indent
|
||||
@ -260,9 +263,9 @@ the variable part is null.
|
||||
.El
|
||||
.Pp
|
||||
A zone line has the form:
|
||||
.Dl "Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL]
|
||||
.Dl "Zone NAME GMTOFF RULES/SAVE FORMAT [UNTILYEAR [MONTH [DAY [TIME]]]]"
|
||||
For example:
|
||||
.Dl "Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00
|
||||
.Dl "Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00"
|
||||
The fields that make up a zone line are:
|
||||
.Bl -tag -width indent
|
||||
.It NAME
|
||||
@ -293,15 +296,15 @@ of the time zone abbreviation goes.
|
||||
Alternately,
|
||||
a slash (/)
|
||||
separates standard and daylight abbreviations.
|
||||
.It UNTIL
|
||||
.It UNTILYEAR [MONTH [DAY [TIME]]]
|
||||
The time at which the UTC offset or the rule(s) change for a location.
|
||||
It is specified as a year, a month, a day, and a time of day.
|
||||
If this is specified,
|
||||
the time zone information is generated from the given UTC offset
|
||||
and rule change until the time specified.
|
||||
The month, day, and time of day have the same format as the IN, ON, and AT
|
||||
columns of a rule; trailing columns can be omitted, and default to the
|
||||
earliest possible value for the missing columns.
|
||||
fields of a rule; trailing fields can be omitted, and default to the
|
||||
earliest possible value for the missing fields.
|
||||
.Pp
|
||||
The next line must be a
|
||||
.Dq continuation
|
||||
@ -310,18 +313,18 @@ string
|
||||
.Dq Zone
|
||||
and the name are omitted, as the continuation line will
|
||||
place information starting at the time specified as the
|
||||
.Em UNTIL
|
||||
field in the previous line in the file used by the previous line.
|
||||
Continuation lines may contain an
|
||||
.Em UNTIL
|
||||
field, just as zone lines do, indicating that the next line is a further
|
||||
.Em until
|
||||
information in the previous line in the file used by the previous line.
|
||||
Continuation lines may contain
|
||||
.Em until
|
||||
information, just as zone lines do, indicating that the next line is a further
|
||||
continuation.
|
||||
.El
|
||||
.Pp
|
||||
A link line has the form
|
||||
.Dl "Link LINK-FROM LINK-TO
|
||||
.Dl "Link LINK-FROM LINK-TO"
|
||||
For example:
|
||||
.Dl "Link Europe/Istanbul Asia/Istanbul
|
||||
.Dl "Link Europe/Istanbul Asia/Istanbul"
|
||||
The
|
||||
.Em LINK-FROM
|
||||
field should appear as the
|
||||
@ -335,9 +338,9 @@ Except for continuation lines,
|
||||
lines may appear in any order in the input.
|
||||
.Pp
|
||||
Lines in the file that describes leap seconds have the following form:
|
||||
.Dl "Leap YEAR MONTH DAY HH:MM:SS CORR R/S
|
||||
.Dl "Leap YEAR MONTH DAY HH:MM:SS CORR R/S"
|
||||
For example:
|
||||
.Dl "Leap 1974 Dec 31 23:59:60 + S
|
||||
.Dl "Leap 1974 Dec 31 23:59:60 + S"
|
||||
The
|
||||
.Em YEAR ,
|
||||
.Em MONTH ,
|
||||
@ -376,12 +379,81 @@ or
|
||||
.Dq Rolling
|
||||
if the leap second time given by the other fields should be interpreted as
|
||||
local wall clock time.
|
||||
.Sh NOTE
|
||||
.Sh "EXTENDED EXAMPLE"
|
||||
Here is an extended example of
|
||||
.Nm
|
||||
input, intended to illustrate many of its features.
|
||||
.br
|
||||
.ne 22
|
||||
.nf
|
||||
.in +2m
|
||||
.ta \w'# Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'TYPE\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u
|
||||
.sp
|
||||
# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
|
||||
Rule Swiss 1940 only - Nov 2 0:00 1:00 S
|
||||
Rule Swiss 1940 only - Dec 31 0:00 0 -
|
||||
Rule Swiss 1941 1942 - May Sun>=1 2:00 1:00 S
|
||||
Rule Swiss 1941 1942 - Oct Sun>=1 0:00 0
|
||||
.sp .5
|
||||
Rule EU 1977 1980 - Apr Sun>=1 1:00u 1:00 S
|
||||
Rule EU 1977 only - Sep lastSun 1:00u 0 -
|
||||
Rule EU 1978 only - Oct 1 1:00u 0 -
|
||||
Rule EU 1979 1995 - Sep lastSun 1:00u 0 -
|
||||
Rule EU 1981 max - Mar lastSun 1:00u 1:00 S
|
||||
Rule EU 1996 max - Oct lastSun 1:00u 0 -
|
||||
.sp
|
||||
.ta \w'# Zone\0\0'u +\w'Europe/Zurich\0\0'u +\w'0:34:08\0\0'u +\w'RULES/SAVE\0\0'u +\w'FORMAT\0\0'u
|
||||
# Zone NAME GMTOFF RULES FORMAT UNTIL
|
||||
Zone Europe/Zurich 0:34:08 - LMT 1848 Sep 12
|
||||
0:29:44 - BMT 1894 Jun
|
||||
1:00 Swiss CE%sT 1981
|
||||
1:00 EU CE%sT
|
||||
.sp
|
||||
Link Europe/Zurich Switzerland
|
||||
.sp
|
||||
.in
|
||||
.fi
|
||||
In this example, the zone is named Europe/Zurich but it has an alias
|
||||
as Switzerland.
|
||||
Zurich was 34 minutes and 8 seconds west of GMT until 1848-09-12
|
||||
at 00:00, when the offset changed to 29 minutes and 44 seconds.
|
||||
After 1894-06-01 at 00:00 Swiss daylight saving rules (defined with
|
||||
lines beginning with "Rule Swiss") apply, and the GMT offset became
|
||||
one hour.
|
||||
From 1981 to the present, EU daylight saving rules have applied,
|
||||
and the UTC offset has remained at one hour.
|
||||
.Pp
|
||||
In 1940, daylight saving time applied from November 2 at 00:00 to
|
||||
December 31 at 00:00.
|
||||
In 1941 and 1942, daylight saving time applied from the first Sunday
|
||||
in May at 02:00 to the first Sunday in October at 00:00.
|
||||
The pre-1981 EU daylight-saving rules have no effect here, but are
|
||||
included for completeness.
|
||||
Since 1981, daylight saving has begun on the last Sunday in March
|
||||
at 01:00 UTC.
|
||||
Until 1995 it ended the last Sunday in September at 01:00 UTC, but
|
||||
this changed to the last Sunday in October starting in 1996.
|
||||
.Pp
|
||||
For purposes of display, "LMT" and "BMT" were initially used,
|
||||
respectively.
|
||||
Since Swiss rules and later EU rules were applied, the display name
|
||||
for the timezone has been CET for standard time and CEST for daylight
|
||||
saving time.
|
||||
.Sh NOTES
|
||||
For areas with more than two types of local time,
|
||||
you may need to use local standard time in the
|
||||
.Em AT
|
||||
field of the earliest transition time's rule to ensure that
|
||||
the earliest transition time recorded in the compiled file is correct.
|
||||
.Pp
|
||||
If, for a particular zone, a clock advance caused by the start of
|
||||
daylight saving coincides with and is equal to a clock retreat
|
||||
caused by a change in UTC offset,
|
||||
.Nm
|
||||
produces a single transition to daylight saving at the new UTC offset
|
||||
(without any change in wall clock time).
|
||||
To get separate transitions use multiple zone continuation lines
|
||||
specifying transition instants using universal time.
|
||||
.Sh FILES
|
||||
.Bl -tag -width /usr/share/zoneinfo -compact
|
||||
.It /usr/share/zoneinfo
|
||||
@ -391,4 +463,6 @@ standard directory used for created files
|
||||
.Xr ctime 3 ,
|
||||
.Xr tzfile 5 ,
|
||||
.Xr zdump 8
|
||||
.\" @(#)zic.8 7.18
|
||||
.\" @(#)zic.8 8.6
|
||||
.\" This file is in the public domain, so clarified as of
|
||||
.\" 2009-05-17 by Arthur David Olson.
|
||||
|
1448
zic.tproj/zic.c
1448
zic.tproj/zic.c
File diff suppressed because it is too large
Load Diff
181
zlog.tproj/SymbolicationHelper.c
Normal file
181
zlog.tproj/SymbolicationHelper.c
Normal 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);
|
||||
}
|
35
zlog.tproj/SymbolicationHelper.h
Normal file
35
zlog.tproj/SymbolicationHelper.h
Normal 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 */
|
8
zlog.tproj/entitlements.plist
Normal file
8
zlog.tproj/entitlements.plist
Normal 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
56
zlog.tproj/zlog.1
Normal 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
241
zlog.tproj/zlog.c
Normal 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
Loading…
Reference in New Issue
Block a user