Source/PLCrashAsync.h (122 lines of code) (raw):
/*
* Author: Landon Fuller <landonf@plausible.coop>
*
* Copyright (c) 2008-2013 Plausible Labs Cooperative, Inc.
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef PLCRASH_ASYNC_H
#define PLCRASH_ASYNC_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h> // for snprintf
#include <unistd.h>
#include <stdbool.h>
#include <stddef.h>
#include <assert.h>
#include <TargetConditionals.h>
#include <mach/mach.h>
#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST
/*
* iOS does not provide the mach_vm_* APIs, and as such, we can't support both
* 32-bit/64-bit tasks via the same APIs.
*
* In practice, this currently does not matter for iOS as out-of-process execution
* is not permitted; in-process reporting will always target the host process'
* address width.
*/
/** The largest address value that can be represented via the pl_vm_address_t type. */
#ifdef __LP64__
#define PL_VM_ADDRESS_MAX UINT64_MAX
#else
#define PL_VM_ADDRESS_MAX UINT32_MAX
#endif
/** The largest address value that can be represented via the pl_vm_size_t type. */
#ifdef __LP64__
#define PL_VM_SIZE_MAX UINT64_MAX
#else
#define PL_VM_SIZE_MAX UINT32_MAX
#endif
/** The largest offset value that can be represented via the pl_vm_off_t type. */
#define PL_VM_OFF_MAX PTRDIFF_MAX
/** The smallest offset value that can be represented via the pl_vm_off_t type. */
#define PL_VM_OFF_MIN PTRDIFF_MIN
/** VM address type.
* @ingroup plcrash_async */
typedef vm_address_t pl_vm_address_t;
/** VM size type.
* @ingroup plcrash_async */
typedef vm_size_t pl_vm_size_t;
/** VM offset type.
* @ingroup plcrash_async */
typedef ptrdiff_t pl_vm_off_t;
#else
#include <mach/mach_vm.h>
#define PL_HAVE_MACH_VM 1
/** The largest address value that can be represented via the pl_vm_address_t type. */
#define PL_VM_ADDRESS_MAX UINT64_MAX
/** The largest address value that can be represented via the pl_vm_size_t type. */
#define PL_VM_SIZE_MAX UINT64_MAX
/** The largest offset value that can be represented via the pl_vm_off_t type. */
#define PL_VM_OFF_MAX INT64_MAX
/** The smallest offset value that can be represented via the pl_vm_off_t type. */
#define PL_VM_OFF_MIN INT64_MIN
/** Architecture-independent VM address type.
* @ingroup plcrash_async */
typedef mach_vm_address_t pl_vm_address_t;
/** Architecture-independent VM size type.
* @ingroup plcrash_async */
typedef mach_vm_size_t pl_vm_size_t;
/** Architecture-independent VM offset type.
* @ingroup plcrash_async */
typedef int64_t pl_vm_off_t;
#endif /* TARGET_OS_IPHONE */
/** An invalid address value. */
#define PL_VM_ADDRESS_INVALID PL_VM_ADDRESS_MAX
// assert() support. We prefer to leave assertions on in release builds, but need
// to disable them in async-safe code paths.
#ifdef PLCF_RELEASE_BUILD
#define PLCF_ASSERT(expr)
#else
#define PLCF_ASSERT(expr) assert(expr)
#endif /* PLCF_RELEASE_BUILD */
// Debug output support. Lines are capped at 128 (stack space is scarce). This implemention
// is not async-safe and should not be enabled in release builds
#ifdef PLCF_RELEASE_BUILD
#define PLCF_DEBUG(msg, args...)
#else
#define PLCF_DEBUG(msg, args...) {\
char __tmp_output[128];\
snprintf(__tmp_output, sizeof(__tmp_output), "[PLCrashReporter] "); \
plcrash_async_writen(STDERR_FILENO, __tmp_output, strlen(__tmp_output));\
\
snprintf(__tmp_output, sizeof(__tmp_output), ":%d: ", __LINE__); \
plcrash_async_writen(STDERR_FILENO, __func__, strlen(__func__));\
plcrash_async_writen(STDERR_FILENO, __tmp_output, strlen(__tmp_output));\
\
snprintf(__tmp_output, sizeof(__tmp_output), msg, ## args); \
plcrash_async_writen(STDERR_FILENO, __tmp_output, strlen(__tmp_output));\
\
__tmp_output[0] = '\n'; \
plcrash_async_writen(STDERR_FILENO, __tmp_output, 1); \
}
#endif /* PLCF_RELEASE_BUILD */
/**
* @ingroup plcrash_async
* Error return codes.
*/
typedef enum {
/** Success */
PLCRASH_ESUCCESS = 0,
/** Unknown error (if found, is a bug) */
PLCRASH_EUNKNOWN,
/** The output file can not be opened or written to */
PLCRASH_OUTPUT_ERR,
/** No memory available (allocation failed) */
PLCRASH_ENOMEM,
/** Unsupported operation */
PLCRASH_ENOTSUP,
/** Invalid argument */
PLCRASH_EINVAL,
/** Internal error */
PLCRASH_EINTERNAL,
/** Access to the specified resource is denied. */
PLCRASH_EACCESS,
/** The requested resource could not be found. */
PLCRASH_ENOTFOUND,
/** The input data is in an unknown or invalid format. */
PLCRASH_EINVALID_DATA,
} plcrash_error_t;
const char *plcrash_async_strerror (plcrash_error_t error);
bool plcrash_async_address_apply_offset (pl_vm_address_t base_address, pl_vm_off_t offset, pl_vm_address_t *result);
thread_t pl_mach_thread_self (void);
/**
* @internal
* @ingroup plcrash_async
*
* Provides a set of byteswap functions that will swap from the target byte order to the host byte order.
* This is used to provide byte order neutral polymorphism when parsing Mach-O and other file formats.
*/
typedef struct plcrash_async_byteorder {
/** The byte-swap function to use for 16-bit values. */
uint16_t (*swap16)(uint16_t);
/** The byte-swap function to use for 32-bit values. */
uint32_t (*swap32)(uint32_t);
/** The byte-swap function to use for 64-bit values. */
uint64_t (*swap64)(uint64_t);
#ifdef __cplusplus
public:
/** Byte swap a 16-bit value */
uint16_t swap (uint16_t v) const { return swap16(v); }
/** Byte swap a 32-bit value */
uint32_t swap (uint32_t v) const { return swap32(v); }
/** Byte swap a 64-bit value */
uint64_t swap (uint64_t v) const { return swap64(v); }
#endif
} plcrash_async_byteorder_t;
extern const plcrash_async_byteorder_t plcrash_async_byteorder_swapped;
extern const plcrash_async_byteorder_t plcrash_async_byteorder_direct;
extern const plcrash_async_byteorder_t *plcrash_async_byteorder_little_endian (void);
extern const plcrash_async_byteorder_t *plcrash_async_byteorder_big_endian (void);
plcrash_error_t plcrash_async_task_memcpy (mach_port_t task, pl_vm_address_t address, pl_vm_off_t offset, void *dest, pl_vm_size_t len);
plcrash_error_t plcrash_async_task_read_uint8 (task_t task, pl_vm_address_t address, pl_vm_off_t offset, uint8_t *result);
plcrash_error_t plcrash_async_task_read_uint16 (task_t task, const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t address, pl_vm_off_t offset, uint16_t *result);
plcrash_error_t plcrash_async_task_read_uint32 (task_t task, const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t address, pl_vm_off_t offset, uint32_t *result);
plcrash_error_t plcrash_async_task_read_uint64 (task_t task, const plcrash_async_byteorder_t *byteorder,
pl_vm_address_t address, pl_vm_off_t offset, uint64_t *result);
int plcrash_async_strcmp(const char *s1, const char *s2);
int plcrash_async_strncmp(const char *s1, const char *s2, size_t n);
void *plcrash_async_memcpy(void *dest, const void *source, size_t n);
void *plcrash_async_memset(void *dest, uint8_t value, size_t n);
ssize_t plcrash_async_writen (int fd, const void *data, size_t len);
/**
* @internal
* @ingroup plcrash_async_bufio
*
* Async-safe buffered file output. This implementation is only intended for use
* within signal handler execution of crash log output.
*/
typedef struct plcrash_async_file {
/** Output file descriptor */
int fd;
/** Output limit */
off_t limit_bytes;
/** Total bytes written */
off_t total_bytes;
/** Current length of data in buffer */
size_t buflen;
/** Buffered output */
char buffer[256];
} plcrash_async_file_t;
void plcrash_async_file_init (plcrash_async_file_t *file, int fd, off_t output_limit);
bool plcrash_async_file_write (plcrash_async_file_t *file, const void *data, size_t len);
bool plcrash_async_file_flush (plcrash_async_file_t *file);
bool plcrash_async_file_close (plcrash_async_file_t *file);
#ifdef __cplusplus
}
#endif
#endif /* PLCRASH_ASYNC_H */