--- /dev/null
+/*
+ * Copyright (c) 2014 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <sys/stackshot.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/*
+ * System call entry point
+ */
+int __stack_snapshot_with_config(int stackshot_config_version, user_addr_t stackshot_config, size_t stackshot_config_size);
+
+/*
+ * stackshot_config_create: create and initialize the arguments for a stackshot
+ *
+ * Outputs: NULL if malloc fails
+ * a pointer to a new stackshot_config_t on success
+ */
+stackshot_config_t *
+stackshot_config_create(void)
+{
+ stackshot_config_t *s_config;
+
+ s_config = malloc(sizeof(stackshot_config_t));
+ if (s_config == NULL) {
+ return NULL;
+ }
+
+ s_config->sc_pid = -1;
+ s_config->sc_flags = 0;
+ s_config->sc_since_timestamp = 0;
+ s_config->sc_buffer = 0;
+ s_config->sc_size = 0;
+
+ return s_config;
+}
+
+/*
+ * stackshot_config_set_pid: set the PID to be traced
+ *
+ * Inputs: stackshot_config - a pointer to the stackshot_config_t we want to update
+ * pid - process id of process to be traced, or -1 for the entire system
+ *
+ * Outputs: EINVAL if the passed stackshot_config pointer is NULL
+ * 0 on success
+ */
+int
+stackshot_config_set_pid(stackshot_config_t *stackshot_config, int pid)
+{
+ stackshot_config_t *s_config;
+
+ if (stackshot_config == NULL) {
+ return EINVAL;
+ }
+
+ s_config = (stackshot_config_t *) stackshot_config;
+ s_config->sc_pid = pid;
+
+ return 0;
+}
+
+/*
+ * stackshot_config_set_flags: set the flags to be passed for the stackshot
+ *
+ * Inputs: stackshot_config - a pointer to the stackshot_config_t we want to update
+ * flags - flags to pass to stackshot
+ *
+ * Outputs: EINVAL if the passed stackshot_config pointer is NULL
+ * 0 on success
+ */
+int
+stackshot_config_set_flags(stackshot_config_t *stackshot_config, uint32_t flags)
+{
+ stackshot_config_t *s_config;
+
+ if (stackshot_config == NULL) {
+ return EINVAL;
+ }
+
+ s_config = (stackshot_config_t *) stackshot_config;
+ s_config->sc_flags = flags;
+
+ return 0;
+}
+
+/*
+ * stackshot_capture_with_config: take a stackshot with the provided config
+ *
+ * Inputs: stackshot_config - a pointer to the stackshot_config_t we want to use
+ *
+ * Outputs: EINVAL if the passed stackshot_config pointer is NULL, a caller is trying
+ * to reuse a config without deallocating its buffer or if there is a
+ * problem with the arguments
+ * EFAULT if there was a problem with accessing the arguments from the kernel
+ * EPERM if the caller is not privileged
+ * ENOTSUP if the caller is passing a stackshot config version that is not
+ * supported by the kernel (indicates libsyscall:kernel mismatch),
+ * or if the caller is requesting unsupported flags
+ * ENOMEM if the kernel is unable to allocate memory
+ * ENOSPC if the caller doesn't have enough space in their address space for
+ * the kernel to remap the buffer
+ * ENOENT if the caller is requesting an existing buffer that doesn't exist
+ * or the target PID isn't found
+ * 0 on success
+ */
+int
+stackshot_capture_with_config(stackshot_config_t *stackshot_config)
+{
+ int ret;
+ stackshot_config_t *s_config;
+
+ if (stackshot_config == NULL) {
+ return EINVAL;
+ }
+
+ s_config = (stackshot_config_t *) stackshot_config;
+ if (s_config->sc_buffer != 0) {
+ return EINVAL;
+ }
+
+ s_config->sc_out_buffer_addr = &s_config->sc_buffer;
+ s_config->sc_out_size_addr = &s_config->sc_size;
+ ret = __stack_snapshot_with_config(STACKSHOT_CONFIG_TYPE, s_config, sizeof(stackshot_config_t));
+
+ if (ret != 0) {
+ ret = errno;
+ s_config->sc_buffer = 0;
+ s_config->sc_size = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * stackshot_config_get_stackshot_buffer: get a pointer to the buffer containing the stackshot
+ *
+ * Inputs: stackshot_config - a pointer to a stackshot_config_t
+ *
+ * Outputs: NULL if the passed stackshot_config is NULL or if its buffer is NULL
+ * a pointer to the buffer containing the stackshot on success
+ */
+void *
+stackshot_config_get_stackshot_buffer(stackshot_config_t *stackshot_config)
+{
+ stackshot_config_t *s_config;
+
+ if (stackshot_config == NULL) {
+ return NULL;
+ }
+ s_config = (stackshot_config_t *) stackshot_config;
+
+ return ((void *)s_config->sc_buffer);
+}
+
+/*
+ * stackshot_config_get_stackshot_size: get the size of the stackshot buffer
+ *
+ * Inputs: stackshot_config - a pointer to a stackshot_config_t
+ *
+ * Outputs: -1 if the passed stackshot config is NULL or there is no buffer
+ * the length of the stackshot buffer on success
+ */
+uint32_t
+stackshot_config_get_stackshot_size(stackshot_config_t * stackshot_config)
+{
+ if (stackshot_config == NULL || (void *)stackshot_config->sc_buffer == NULL) {
+ return -1;
+ }
+
+ return stackshot_config->sc_size;
+}
+
+/*
+ * stackshot_config_set_size_hint: set the size of the stackshot buffer
+ *
+ * Inputs: stackshot_config - a pointer to a stackshot_config_t
+ * suggested_size - hint for size allocation of stackshot
+ *
+ * Outputs: -1 if the passed stackshot config is NULL or there is existing stackshot buffer set.
+ * the length of the stackshot buffer on success.
+ */
+int
+stackshot_config_set_size_hint(stackshot_config_t *stackshot_config, uint32_t suggested_size)
+{
+ if (stackshot_config == NULL || (void *)stackshot_config->sc_buffer != NULL) {
+ return -1;
+ }
+
+ stackshot_config->sc_size = suggested_size;
+
+ return 0;
+}
+
+/*
+ * stackshot_config_dealloc_buffer: dealloc the stackshot buffer and reset the size so that a
+ * stackshot_config_t can be used again
+ *
+ * Inputs: stackshot_config - a pointer to a stackshot_config_t
+ *
+ * Outputs: EINVAL if the passed stackshot_config is NULL or if its buffer is NULL
+ * 0 otherwise
+ */
+int
+stackshot_config_dealloc_buffer(stackshot_config_t *stackshot_config)
+{
+ stackshot_config_t *s_config;
+
+ if (stackshot_config == NULL) {
+ return EINVAL;
+ }
+ s_config = (stackshot_config_t *) stackshot_config;
+
+ if (s_config->sc_size && s_config->sc_buffer) {
+ mach_vm_deallocate(mach_task_self(), (mach_vm_offset_t)s_config->sc_buffer, (mach_vm_size_t)s_config->sc_size);
+ }
+
+ s_config->sc_buffer = 0;
+ s_config->sc_size = 0;
+
+ return 0;
+}
+
+/*
+ * stackshot_config_dealloc: dealloc the stackshot buffer and the stackshot config
+ *
+ * Inputs: stackshot_config - a pointer to a stackshot_config_t
+ *
+ * Outputs: EINVAL if the passed stackshot_cofnig is NULL
+ * 0 otherwise
+ */
+int
+stackshot_config_dealloc(stackshot_config_t *stackshot_config)
+{
+ stackshot_config_t *s_config;
+
+ if (stackshot_config == NULL) {
+ return EINVAL;
+ }
+ s_config = (stackshot_config_t *) stackshot_config;
+
+ if (s_config->sc_size && s_config->sc_buffer) {
+ mach_vm_deallocate(mach_task_self(), (mach_vm_offset_t)s_config->sc_buffer, (mach_vm_size_t)s_config->sc_size);
+ }
+
+ s_config->sc_buffer = 0;
+ s_config->sc_size = 0;
+
+ free(s_config);
+ return 0;
+}