mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2025-04-08 02:11:24 +00:00
new Linux signal handlers
removed the statically linked libbacktrace dynamically linking to libunwind8 when available fixed the c++98 option being passed to the wrong premake filter
This commit is contained in:
parent
584d064523
commit
ac48bc3248
16 changed files with 1379 additions and 183 deletions
257
code/libunwind/libunwind-common.h
Normal file
257
code/libunwind/libunwind-common.h
Normal file
|
@ -0,0 +1,257 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2001-2004 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
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. */
|
||||
|
||||
#define UNW_VERSION_MAJOR 1
|
||||
#define UNW_VERSION_MINOR 1
|
||||
#define UNW_VERSION_EXTRA
|
||||
|
||||
#define UNW_VERSION_CODE(maj,min) (((maj) << 16) | (min))
|
||||
#define UNW_VERSION UNW_VERSION_CODE(UNW_VERSION_MAJOR, UNW_VERSION_MINOR)
|
||||
|
||||
#define UNW_PASTE2(x,y) x##y
|
||||
#define UNW_PASTE(x,y) UNW_PASTE2(x,y)
|
||||
#define UNW_OBJ(fn) UNW_PASTE(UNW_PREFIX, fn)
|
||||
#define UNW_ARCH_OBJ(fn) UNW_PASTE(UNW_PASTE(UNW_PASTE(_U,UNW_TARGET),_), fn)
|
||||
|
||||
#ifdef UNW_LOCAL_ONLY
|
||||
# define UNW_PREFIX UNW_PASTE(UNW_PASTE(_UL,UNW_TARGET),_)
|
||||
#else /* !UNW_LOCAL_ONLY */
|
||||
# define UNW_PREFIX UNW_PASTE(UNW_PASTE(_U,UNW_TARGET),_)
|
||||
#endif /* !UNW_LOCAL_ONLY */
|
||||
|
||||
/* Error codes. The unwind routines return the *negated* values of
|
||||
these error codes on error and a non-negative value on success. */
|
||||
typedef enum
|
||||
{
|
||||
UNW_ESUCCESS = 0, /* no error */
|
||||
UNW_EUNSPEC, /* unspecified (general) error */
|
||||
UNW_ENOMEM, /* out of memory */
|
||||
UNW_EBADREG, /* bad register number */
|
||||
UNW_EREADONLYREG, /* attempt to write read-only register */
|
||||
UNW_ESTOPUNWIND, /* stop unwinding */
|
||||
UNW_EINVALIDIP, /* invalid IP */
|
||||
UNW_EBADFRAME, /* bad frame */
|
||||
UNW_EINVAL, /* unsupported operation or bad value */
|
||||
UNW_EBADVERSION, /* unwind info has unsupported version */
|
||||
UNW_ENOINFO /* no unwind info found */
|
||||
}
|
||||
unw_error_t;
|
||||
|
||||
/* The following enum defines the indices for a couple of
|
||||
(pseudo-)registers which have the same meaning across all
|
||||
platforms. (RO) means read-only. (RW) means read-write. General
|
||||
registers (aka "integer registers") are expected to start with
|
||||
index 0. The number of such registers is architecture-dependent.
|
||||
The remaining indices can be used as an architecture sees fit. The
|
||||
last valid register index is given by UNW_REG_LAST. */
|
||||
typedef enum
|
||||
{
|
||||
UNW_REG_IP = UNW_TDEP_IP, /* (rw) instruction pointer (pc) */
|
||||
UNW_REG_SP = UNW_TDEP_SP, /* (ro) stack pointer */
|
||||
UNW_REG_EH = UNW_TDEP_EH, /* (rw) exception-handling reg base */
|
||||
UNW_REG_LAST = UNW_TDEP_LAST_REG
|
||||
}
|
||||
unw_frame_regnum_t;
|
||||
|
||||
/* Number of exception-handler argument registers: */
|
||||
#define UNW_NUM_EH_REGS UNW_TDEP_NUM_EH_REGS
|
||||
|
||||
typedef enum
|
||||
{
|
||||
UNW_CACHE_NONE, /* no caching */
|
||||
UNW_CACHE_GLOBAL, /* shared global cache */
|
||||
UNW_CACHE_PER_THREAD /* per-thread caching */
|
||||
}
|
||||
unw_caching_policy_t;
|
||||
|
||||
typedef int unw_regnum_t;
|
||||
|
||||
/* The unwind cursor starts at the youngest (most deeply nested) frame
|
||||
and is used to track the frame state as the unwinder steps from
|
||||
frame to frame. It is safe to make (shallow) copies of variables
|
||||
of this type. */
|
||||
typedef struct unw_cursor
|
||||
{
|
||||
unw_word_t opaque[UNW_TDEP_CURSOR_LEN];
|
||||
}
|
||||
unw_cursor_t;
|
||||
|
||||
/* This type encapsulates the entire (preserved) machine-state. */
|
||||
typedef unw_tdep_context_t unw_context_t;
|
||||
|
||||
/* unw_getcontext() fills the unw_context_t pointed to by UC with the
|
||||
machine state as it exists at the call-site. For implementation
|
||||
reasons, this needs to be a target-dependent macro. It's easiest
|
||||
to think of unw_getcontext() as being identical to getcontext(). */
|
||||
#define unw_getcontext(uc) unw_tdep_getcontext(uc)
|
||||
|
||||
/* Return 1 if register number R is a floating-point register, zero
|
||||
otherwise.
|
||||
This routine is signal-safe. */
|
||||
#define unw_is_fpreg(r) unw_tdep_is_fpreg(r)
|
||||
|
||||
typedef unw_tdep_fpreg_t unw_fpreg_t;
|
||||
|
||||
typedef struct unw_addr_space *unw_addr_space_t;
|
||||
|
||||
/* Each target may define it's own set of flags, but bits 0-15 are
|
||||
reserved for general libunwind-use. */
|
||||
#define UNW_PI_FLAG_FIRST_TDEP_BIT 16
|
||||
/* The information comes from a .debug_frame section. */
|
||||
#define UNW_PI_FLAG_DEBUG_FRAME 32
|
||||
|
||||
typedef struct unw_proc_info
|
||||
{
|
||||
unw_word_t start_ip; /* first IP covered by this procedure */
|
||||
unw_word_t end_ip; /* first IP NOT covered by this procedure */
|
||||
unw_word_t lsda; /* address of lang.-spec. data area (if any) */
|
||||
unw_word_t handler; /* optional personality routine */
|
||||
unw_word_t gp; /* global-pointer value for this procedure */
|
||||
unw_word_t flags; /* misc. flags */
|
||||
|
||||
int format; /* unwind-info format (arch-specific) */
|
||||
int unwind_info_size; /* size of the information (if applicable) */
|
||||
void *unwind_info; /* unwind-info (arch-specific) */
|
||||
unw_tdep_proc_info_t extra; /* target-dependent auxiliary proc-info */
|
||||
}
|
||||
unw_proc_info_t;
|
||||
|
||||
/* These are backend callback routines that provide access to the
|
||||
state of a "remote" process. This can be used, for example, to
|
||||
unwind another process through the ptrace() interface. */
|
||||
typedef struct unw_accessors
|
||||
{
|
||||
/* Look up the unwind info associated with instruction-pointer IP.
|
||||
On success, the routine fills in the PROC_INFO structure. */
|
||||
int (*find_proc_info) (unw_addr_space_t, unw_word_t, unw_proc_info_t *,
|
||||
int, void *);
|
||||
|
||||
/* Release any resources (e.g., memory) that were allocated for
|
||||
the unwind info returned in by a previous call to
|
||||
find_proc_info() with NEED_UNWIND_INFO set to 1. */
|
||||
void (*put_unwind_info) (unw_addr_space_t, unw_proc_info_t *, void *);
|
||||
|
||||
/* Return the list-head of the dynamically registered unwind
|
||||
info. */
|
||||
int (*get_dyn_info_list_addr) (unw_addr_space_t, unw_word_t *, void *);
|
||||
|
||||
/* Access aligned word at address ADDR. The value is returned
|
||||
according to the endianness of the host (e.g., if the host is
|
||||
little-endian and the target is big-endian, access_mem() needs
|
||||
to byte-swap the value before returning it). */
|
||||
int (*access_mem) (unw_addr_space_t, unw_word_t, unw_word_t *, int,
|
||||
void *);
|
||||
|
||||
/* Access register number REG at address ADDR. */
|
||||
int (*access_reg) (unw_addr_space_t, unw_regnum_t, unw_word_t *, int,
|
||||
void *);
|
||||
|
||||
/* Access register number REG at address ADDR. */
|
||||
int (*access_fpreg) (unw_addr_space_t, unw_regnum_t,
|
||||
unw_fpreg_t *, int, void *);
|
||||
|
||||
int (*resume) (unw_addr_space_t, unw_cursor_t *, void *);
|
||||
|
||||
/* Optional call back to obtain the name of a (static) procedure.
|
||||
Dynamically generated procedures are handled automatically by
|
||||
libunwind. This callback is optional and may be set to
|
||||
NULL. */
|
||||
int (*get_proc_name) (unw_addr_space_t, unw_word_t, char *, size_t,
|
||||
unw_word_t *, void *);
|
||||
}
|
||||
unw_accessors_t;
|
||||
|
||||
typedef enum unw_save_loc_type
|
||||
{
|
||||
UNW_SLT_NONE, /* register is not saved ("not an l-value") */
|
||||
UNW_SLT_MEMORY, /* register has been saved in memory */
|
||||
UNW_SLT_REG /* register has been saved in (another) register */
|
||||
}
|
||||
unw_save_loc_type_t;
|
||||
|
||||
typedef struct unw_save_loc
|
||||
{
|
||||
unw_save_loc_type_t type;
|
||||
union
|
||||
{
|
||||
unw_word_t addr; /* valid if type==UNW_SLT_MEMORY */
|
||||
unw_regnum_t regnum; /* valid if type==UNW_SLT_REG */
|
||||
}
|
||||
u;
|
||||
unw_tdep_save_loc_t extra; /* target-dependent additional information */
|
||||
}
|
||||
unw_save_loc_t;
|
||||
|
||||
/* These routines work both for local and remote unwinding. */
|
||||
|
||||
#define unw_local_addr_space UNW_OBJ(local_addr_space)
|
||||
#define unw_create_addr_space UNW_OBJ(create_addr_space)
|
||||
#define unw_destroy_addr_space UNW_OBJ(destroy_addr_space)
|
||||
#define unw_get_accessors UNW_ARCH_OBJ(get_accessors)
|
||||
#define unw_init_local UNW_OBJ(init_local)
|
||||
#define unw_init_remote UNW_OBJ(init_remote)
|
||||
#define unw_step UNW_OBJ(step)
|
||||
#define unw_resume UNW_OBJ(resume)
|
||||
#define unw_get_proc_info UNW_OBJ(get_proc_info)
|
||||
#define unw_get_proc_info_by_ip UNW_OBJ(get_proc_info_by_ip)
|
||||
#define unw_get_reg UNW_OBJ(get_reg)
|
||||
#define unw_set_reg UNW_OBJ(set_reg)
|
||||
#define unw_get_fpreg UNW_OBJ(get_fpreg)
|
||||
#define unw_set_fpreg UNW_OBJ(set_fpreg)
|
||||
#define unw_get_save_loc UNW_OBJ(get_save_loc)
|
||||
#define unw_is_signal_frame UNW_OBJ(is_signal_frame)
|
||||
#define unw_handle_signal_frame UNW_OBJ(handle_signal_frame)
|
||||
#define unw_get_proc_name UNW_OBJ(get_proc_name)
|
||||
#define unw_set_caching_policy UNW_OBJ(set_caching_policy)
|
||||
#define unw_regname UNW_ARCH_OBJ(regname)
|
||||
#define unw_flush_cache UNW_ARCH_OBJ(flush_cache)
|
||||
#define unw_strerror UNW_ARCH_OBJ(strerror)
|
||||
|
||||
extern unw_addr_space_t unw_create_addr_space (unw_accessors_t *, int);
|
||||
extern void unw_destroy_addr_space (unw_addr_space_t);
|
||||
extern unw_accessors_t *unw_get_accessors (unw_addr_space_t);
|
||||
extern void unw_flush_cache (unw_addr_space_t, unw_word_t, unw_word_t);
|
||||
extern int unw_set_caching_policy (unw_addr_space_t, unw_caching_policy_t);
|
||||
extern const char *unw_regname (unw_regnum_t);
|
||||
|
||||
extern int unw_init_local (unw_cursor_t *, unw_context_t *);
|
||||
extern int unw_init_remote (unw_cursor_t *, unw_addr_space_t, void *);
|
||||
extern int unw_step (unw_cursor_t *);
|
||||
extern int unw_resume (unw_cursor_t *);
|
||||
extern int unw_get_proc_info (unw_cursor_t *, unw_proc_info_t *);
|
||||
extern int unw_get_proc_info_by_ip (unw_addr_space_t, unw_word_t,
|
||||
unw_proc_info_t *, void *);
|
||||
extern int unw_get_reg (unw_cursor_t *, int, unw_word_t *);
|
||||
extern int unw_set_reg (unw_cursor_t *, int, unw_word_t);
|
||||
extern int unw_get_fpreg (unw_cursor_t *, int, unw_fpreg_t *);
|
||||
extern int unw_set_fpreg (unw_cursor_t *, int, unw_fpreg_t);
|
||||
extern int unw_get_save_loc (unw_cursor_t *, int, unw_save_loc_t *);
|
||||
extern int unw_is_signal_frame (unw_cursor_t *);
|
||||
extern int unw_handle_signal_frame (unw_cursor_t *);
|
||||
extern int unw_get_proc_name (unw_cursor_t *, char *, size_t, unw_word_t *);
|
||||
extern const char *unw_strerror (int);
|
||||
extern int unw_backtrace (void **, int);
|
||||
|
||||
extern unw_addr_space_t unw_local_addr_space;
|
73
code/libunwind/libunwind-coredump.h
Normal file
73
code/libunwind/libunwind-coredump.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
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 libunwind_coredump_h
|
||||
#define libunwind_coredump_h
|
||||
|
||||
#include <libunwind.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Helper routines which make it easy to use libunwind on a coredump.
|
||||
They're available only if UNW_REMOTE_ONLY is _not_ defined and they
|
||||
aren't really part of the libunwind API. They are implemented in a
|
||||
archive library called libunwind-coredump.a. */
|
||||
|
||||
struct UCD_info;
|
||||
|
||||
extern struct UCD_info *_UCD_create(const char *filename);
|
||||
extern void _UCD_destroy(struct UCD_info *);
|
||||
|
||||
extern int _UCD_get_num_threads(struct UCD_info *);
|
||||
extern void _UCD_select_thread(struct UCD_info *, int);
|
||||
extern pid_t _UCD_get_pid(struct UCD_info *);
|
||||
extern int _UCD_get_cursig(struct UCD_info *);
|
||||
extern int _UCD_add_backing_file_at_segment(struct UCD_info *, int phdr_no, const char *filename);
|
||||
extern int _UCD_add_backing_file_at_vaddr(struct UCD_info *,
|
||||
unsigned long vaddr,
|
||||
const char *filename);
|
||||
|
||||
extern int _UCD_find_proc_info (unw_addr_space_t, unw_word_t,
|
||||
unw_proc_info_t *, int, void *);
|
||||
extern void _UCD_put_unwind_info (unw_addr_space_t, unw_proc_info_t *, void *);
|
||||
extern int _UCD_get_dyn_info_list_addr (unw_addr_space_t, unw_word_t *,
|
||||
void *);
|
||||
extern int _UCD_access_mem (unw_addr_space_t, unw_word_t, unw_word_t *, int,
|
||||
void *);
|
||||
extern int _UCD_access_reg (unw_addr_space_t, unw_regnum_t, unw_word_t *,
|
||||
int, void *);
|
||||
extern int _UCD_access_fpreg (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *,
|
||||
int, void *);
|
||||
extern int _UCD_get_proc_name (unw_addr_space_t, unw_word_t, char *, size_t,
|
||||
unw_word_t *, void *);
|
||||
extern int _UCD_resume (unw_addr_space_t, unw_cursor_t *, void *);
|
||||
extern unw_accessors_t _UCD_accessors;
|
||||
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* libunwind_coredump_h */
|
210
code/libunwind/libunwind-dynamic.h
Normal file
210
code/libunwind/libunwind-dynamic.h
Normal file
|
@ -0,0 +1,210 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2002-2004 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
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. */
|
||||
|
||||
/* This file defines the runtime-support routines for dynamically
|
||||
generated code. Even though it is implemented as part of libunwind,
|
||||
it is logically separate from the interface to perform the actual
|
||||
unwinding. In particular, this interface is always used in the
|
||||
context of the unwind target, whereas the rest of the unwind API is
|
||||
used in context of the process that is doing the unwind (which may be
|
||||
a debugger running on another machine, for example).
|
||||
|
||||
Note that the data-structures declared here server a dual purpose:
|
||||
when a program registers a dynamically generated procedure, it uses
|
||||
these structures directly. On the other hand, with remote-unwinding,
|
||||
the data-structures are read from the remote process's memory and
|
||||
translated into internalized versions. To facilitate remote-access,
|
||||
the following rules should be followed in declaring these structures:
|
||||
|
||||
(1) Declare a member as a pointer only if the the information the
|
||||
member points to needs to be internalized as well (e.g., a
|
||||
string representing a procedure name should be declared as
|
||||
"const char *", but the instruction pointer should be declared
|
||||
as unw_word_t).
|
||||
|
||||
(2) Provide sufficient padding to ensure that no implicit padding
|
||||
will be needed on any of the supported target architectures. For
|
||||
the time being, padding data structures with the assumption that
|
||||
sizeof (unw_word_t) == 8 should be sufficient. (Note: it's not
|
||||
impossible to internalize structures with internal padding, but
|
||||
it does make the process a bit harder).
|
||||
|
||||
(3) Don't declare members that contain bitfields or floating-point
|
||||
values.
|
||||
|
||||
(4) Don't declare members with enumeration types. Declare them as
|
||||
int32_t instead. */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
UNW_DYN_STOP = 0, /* end-of-unwind-info marker */
|
||||
UNW_DYN_SAVE_REG, /* save register to another register */
|
||||
UNW_DYN_SPILL_FP_REL, /* frame-pointer-relative register spill */
|
||||
UNW_DYN_SPILL_SP_REL, /* stack-pointer-relative register spill */
|
||||
UNW_DYN_ADD, /* add constant value to a register */
|
||||
UNW_DYN_POP_FRAMES, /* drop one or more stack frames */
|
||||
UNW_DYN_LABEL_STATE, /* name the current state */
|
||||
UNW_DYN_COPY_STATE, /* set the region's entry-state */
|
||||
UNW_DYN_ALIAS /* get unwind info from an alias */
|
||||
}
|
||||
unw_dyn_operation_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */
|
||||
UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */
|
||||
UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */
|
||||
UNW_INFO_FORMAT_ARM_EXIDX /* ARM specific unwind info */
|
||||
}
|
||||
unw_dyn_info_format_t;
|
||||
|
||||
typedef struct unw_dyn_op
|
||||
{
|
||||
int8_t tag; /* what operation? */
|
||||
int8_t qp; /* qualifying predicate register */
|
||||
int16_t reg; /* what register */
|
||||
int32_t when; /* when does it take effect? */
|
||||
unw_word_t val; /* auxiliary value */
|
||||
}
|
||||
unw_dyn_op_t;
|
||||
|
||||
typedef struct unw_dyn_region_info
|
||||
{
|
||||
struct unw_dyn_region_info *next; /* linked list of regions */
|
||||
int32_t insn_count; /* region length (# of instructions) */
|
||||
uint32_t op_count; /* length of op-array */
|
||||
unw_dyn_op_t op[1]; /* variable-length op-array */
|
||||
}
|
||||
unw_dyn_region_info_t;
|
||||
|
||||
typedef struct unw_dyn_proc_info
|
||||
{
|
||||
unw_word_t name_ptr; /* address of human-readable procedure name */
|
||||
unw_word_t handler; /* address of personality routine */
|
||||
uint32_t flags;
|
||||
int32_t pad0;
|
||||
unw_dyn_region_info_t *regions;
|
||||
}
|
||||
unw_dyn_proc_info_t;
|
||||
|
||||
typedef struct unw_dyn_table_info
|
||||
{
|
||||
unw_word_t name_ptr; /* addr. of table name (e.g., library name) */
|
||||
unw_word_t segbase; /* segment base */
|
||||
unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */
|
||||
unw_word_t *table_data;
|
||||
}
|
||||
unw_dyn_table_info_t;
|
||||
|
||||
typedef struct unw_dyn_remote_table_info
|
||||
{
|
||||
unw_word_t name_ptr; /* addr. of table name (e.g., library name) */
|
||||
unw_word_t segbase; /* segment base */
|
||||
unw_word_t table_len; /* must be a multiple of sizeof(unw_word_t)! */
|
||||
unw_word_t table_data;
|
||||
}
|
||||
unw_dyn_remote_table_info_t;
|
||||
|
||||
typedef struct unw_dyn_info
|
||||
{
|
||||
/* doubly-linked list of dyn-info structures: */
|
||||
struct unw_dyn_info *next;
|
||||
struct unw_dyn_info *prev;
|
||||
unw_word_t start_ip; /* first IP covered by this entry */
|
||||
unw_word_t end_ip; /* first IP NOT covered by this entry */
|
||||
unw_word_t gp; /* global-pointer in effect for this entry */
|
||||
int32_t format; /* real type: unw_dyn_info_format_t */
|
||||
int32_t pad;
|
||||
union
|
||||
{
|
||||
unw_dyn_proc_info_t pi;
|
||||
unw_dyn_table_info_t ti;
|
||||
unw_dyn_remote_table_info_t rti;
|
||||
}
|
||||
u;
|
||||
}
|
||||
unw_dyn_info_t;
|
||||
|
||||
typedef struct unw_dyn_info_list
|
||||
{
|
||||
uint32_t version;
|
||||
uint32_t generation;
|
||||
unw_dyn_info_t *first;
|
||||
}
|
||||
unw_dyn_info_list_t;
|
||||
|
||||
/* Return the size (in bytes) of an unw_dyn_region_info_t structure that can
|
||||
hold OP_COUNT ops. */
|
||||
#define _U_dyn_region_info_size(op_count) \
|
||||
((char *) (((unw_dyn_region_info_t *) NULL)->op + (op_count)) \
|
||||
- (char *) NULL)
|
||||
|
||||
/* Register the unwind info for a single procedure.
|
||||
This routine is NOT signal-safe. */
|
||||
extern void _U_dyn_register (unw_dyn_info_t *);
|
||||
|
||||
/* Cancel the unwind info for a single procedure.
|
||||
This routine is NOT signal-safe. */
|
||||
extern void _U_dyn_cancel (unw_dyn_info_t *);
|
||||
|
||||
|
||||
/* Convenience routines. */
|
||||
|
||||
#define _U_dyn_op(_tag, _qp, _when, _reg, _val) \
|
||||
((unw_dyn_op_t) { (_tag), (_qp), (_reg), (_when), (_val) })
|
||||
|
||||
#define _U_dyn_op_save_reg(op, qp, when, reg, dst) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_SAVE_REG, (qp), (when), (reg), (dst)))
|
||||
|
||||
#define _U_dyn_op_spill_fp_rel(op, qp, when, reg, offset) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_SPILL_FP_REL, (qp), (when), (reg), \
|
||||
(offset)))
|
||||
|
||||
#define _U_dyn_op_spill_sp_rel(op, qp, when, reg, offset) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_SPILL_SP_REL, (qp), (when), (reg), \
|
||||
(offset)))
|
||||
|
||||
#define _U_dyn_op_add(op, qp, when, reg, value) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_ADD, (qp), (when), (reg), (value)))
|
||||
|
||||
#define _U_dyn_op_pop_frames(op, qp, when, num_frames) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_POP_FRAMES, (qp), (when), 0, (num_frames)))
|
||||
|
||||
#define _U_dyn_op_label_state(op, label) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_LABEL_STATE, _U_QP_TRUE, -1, 0, (label)))
|
||||
|
||||
#define _U_dyn_op_copy_state(op, label) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_COPY_STATE, _U_QP_TRUE, -1, 0, (label)))
|
||||
|
||||
#define _U_dyn_op_alias(op, qp, when, addr) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_ALIAS, (qp), (when), 0, (addr)))
|
||||
|
||||
#define _U_dyn_op_stop(op) \
|
||||
(*(op) = _U_dyn_op (UNW_DYN_STOP, _U_QP_TRUE, -1, 0, 0))
|
||||
|
||||
/* The target-dependent qualifying predicate which is always TRUE. On
|
||||
IA-64, that's p0 (0), on non-predicated architectures, the value is
|
||||
ignored. */
|
||||
#define _U_QP_TRUE _U_TDEP_QP_TRUE
|
63
code/libunwind/libunwind-ptrace.h
Normal file
63
code/libunwind/libunwind-ptrace.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2004 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
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 libunwind_ptrace_h
|
||||
#define libunwind_ptrace_h
|
||||
|
||||
#include <libunwind.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Helper routines which make it easy to use libunwind via ptrace().
|
||||
They're available only if UNW_REMOTE_ONLY is _not_ defined and they
|
||||
aren't really part of the libunwind API. They are implemented in a
|
||||
archive library called libunwind-ptrace.a. */
|
||||
|
||||
extern void *_UPT_create (pid_t);
|
||||
extern void _UPT_destroy (void *);
|
||||
extern int _UPT_find_proc_info (unw_addr_space_t, unw_word_t,
|
||||
unw_proc_info_t *, int, void *);
|
||||
extern void _UPT_put_unwind_info (unw_addr_space_t, unw_proc_info_t *, void *);
|
||||
extern int _UPT_get_dyn_info_list_addr (unw_addr_space_t, unw_word_t *,
|
||||
void *);
|
||||
extern int _UPT_access_mem (unw_addr_space_t, unw_word_t, unw_word_t *, int,
|
||||
void *);
|
||||
extern int _UPT_access_reg (unw_addr_space_t, unw_regnum_t, unw_word_t *,
|
||||
int, void *);
|
||||
extern int _UPT_access_fpreg (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *,
|
||||
int, void *);
|
||||
extern int _UPT_get_proc_name (unw_addr_space_t, unw_word_t, char *, size_t,
|
||||
unw_word_t *, void *);
|
||||
extern int _UPT_resume (unw_addr_space_t, unw_cursor_t *, void *);
|
||||
extern unw_accessors_t _UPT_accessors;
|
||||
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* libunwind_ptrace_h */
|
139
code/libunwind/libunwind-x86_64.h
Normal file
139
code/libunwind/libunwind-x86_64.h
Normal file
|
@ -0,0 +1,139 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2002-2004 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
Modified for x86_64 by Max Asbock <masbock@us.ibm.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
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 LIBUNWIND_H
|
||||
#define LIBUNWIND_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#define UNW_TARGET x86_64
|
||||
#define UNW_TARGET_X86_64 1
|
||||
|
||||
#define _U_TDEP_QP_TRUE 0 /* see libunwind-dynamic.h */
|
||||
|
||||
/* This needs to be big enough to accommodate "struct cursor", while
|
||||
leaving some slack for future expansion. Changing this value will
|
||||
require recompiling all users of this library. Stack allocation is
|
||||
relatively cheap and unwind-state copying is relatively rare, so we
|
||||
want to err on making it rather too big than too small. */
|
||||
#define UNW_TDEP_CURSOR_LEN 127
|
||||
|
||||
typedef uint64_t unw_word_t;
|
||||
typedef int64_t unw_sword_t;
|
||||
|
||||
typedef long double unw_tdep_fpreg_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
UNW_X86_64_RAX,
|
||||
UNW_X86_64_RDX,
|
||||
UNW_X86_64_RCX,
|
||||
UNW_X86_64_RBX,
|
||||
UNW_X86_64_RSI,
|
||||
UNW_X86_64_RDI,
|
||||
UNW_X86_64_RBP,
|
||||
UNW_X86_64_RSP,
|
||||
UNW_X86_64_R8,
|
||||
UNW_X86_64_R9,
|
||||
UNW_X86_64_R10,
|
||||
UNW_X86_64_R11,
|
||||
UNW_X86_64_R12,
|
||||
UNW_X86_64_R13,
|
||||
UNW_X86_64_R14,
|
||||
UNW_X86_64_R15,
|
||||
UNW_X86_64_RIP,
|
||||
#ifdef CONFIG_MSABI_SUPPORT
|
||||
UNW_X86_64_XMM0,
|
||||
UNW_X86_64_XMM1,
|
||||
UNW_X86_64_XMM2,
|
||||
UNW_X86_64_XMM3,
|
||||
UNW_X86_64_XMM4,
|
||||
UNW_X86_64_XMM5,
|
||||
UNW_X86_64_XMM6,
|
||||
UNW_X86_64_XMM7,
|
||||
UNW_X86_64_XMM8,
|
||||
UNW_X86_64_XMM9,
|
||||
UNW_X86_64_XMM10,
|
||||
UNW_X86_64_XMM11,
|
||||
UNW_X86_64_XMM12,
|
||||
UNW_X86_64_XMM13,
|
||||
UNW_X86_64_XMM14,
|
||||
UNW_X86_64_XMM15,
|
||||
UNW_TDEP_LAST_REG = UNW_X86_64_XMM15,
|
||||
#else
|
||||
UNW_TDEP_LAST_REG = UNW_X86_64_RIP,
|
||||
#endif
|
||||
|
||||
/* XXX Add other regs here */
|
||||
|
||||
/* frame info (read-only) */
|
||||
UNW_X86_64_CFA,
|
||||
|
||||
UNW_TDEP_IP = UNW_X86_64_RIP,
|
||||
UNW_TDEP_SP = UNW_X86_64_RSP,
|
||||
UNW_TDEP_BP = UNW_X86_64_RBP,
|
||||
UNW_TDEP_EH = UNW_X86_64_RAX
|
||||
}
|
||||
x86_64_regnum_t;
|
||||
|
||||
#define UNW_TDEP_NUM_EH_REGS 2 /* XXX Not sure what this means */
|
||||
|
||||
typedef struct unw_tdep_save_loc
|
||||
{
|
||||
/* Additional target-dependent info on a save location. */
|
||||
}
|
||||
unw_tdep_save_loc_t;
|
||||
|
||||
/* On x86_64, we can directly use ucontext_t as the unwind context. */
|
||||
typedef ucontext_t unw_tdep_context_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* no x86-64-specific auxiliary proc-info */
|
||||
}
|
||||
unw_tdep_proc_info_t;
|
||||
|
||||
#include "libunwind-dynamic.h"
|
||||
#include "libunwind-common.h"
|
||||
|
||||
#define unw_tdep_getcontext UNW_ARCH_OBJ(getcontext)
|
||||
#define unw_tdep_is_fpreg UNW_ARCH_OBJ(is_fpreg)
|
||||
|
||||
extern int unw_tdep_getcontext (unw_tdep_context_t *);
|
||||
extern int unw_tdep_is_fpreg (int);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LIBUNWIND_H */
|
34
code/libunwind/libunwind.h
Normal file
34
code/libunwind/libunwind.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* Provide a real file - not a symlink - as it would cause multiarch conflicts
|
||||
when multiple different arch releases are installed simultaneously. */
|
||||
|
||||
#ifndef UNW_REMOTE_ONLY
|
||||
|
||||
#if defined __aarch64__
|
||||
#include "libunwind-aarch64.h"
|
||||
#elif defined __arm__
|
||||
# include "libunwind-arm.h"
|
||||
#elif defined __hppa__
|
||||
# include "libunwind-hppa.h"
|
||||
#elif defined __ia64__
|
||||
# include "libunwind-ia64.h"
|
||||
#elif defined __mips__
|
||||
# include "libunwind-mips.h"
|
||||
#elif defined __powerpc__ && !defined __powerpc64__
|
||||
# include "libunwind-ppc32.h"
|
||||
#elif defined __powerpc64__
|
||||
# include "libunwind-ppc64.h"
|
||||
#elif defined __sh__
|
||||
# include "libunwind-sh.h"
|
||||
#elif defined __i386__
|
||||
# include "libunwind-x86.h"
|
||||
#elif defined __x86_64__
|
||||
# include "libunwind-x86_64.h"
|
||||
#else
|
||||
# error "Unsupported arch"
|
||||
#endif
|
||||
|
||||
#else /* UNW_REMOTE_ONLY */
|
||||
|
||||
# include "libunwind-x86_64.h"
|
||||
|
||||
#endif /* UNW_REMOTE_ONLY */
|
|
@ -5,14 +5,20 @@
|
|||
|
||||
|
||||
typedef struct {
|
||||
char gitHeadHash[24]; // SHA-1 -> 160 bits -> 20 hex chars
|
||||
vm_t* vm;
|
||||
unsigned int crc32; // CRC32
|
||||
} vmCrashInfo_t;
|
||||
char gitHeadHash[24]; // SHA-1 -> 160 bits -> 20 hex chars
|
||||
vm_t* vm;
|
||||
unsigned int crc32;
|
||||
} vmCrash_t;
|
||||
|
||||
typedef struct {
|
||||
vmCrash_t vm[VM_COUNT];
|
||||
char modName[64];
|
||||
char modVersion[64];
|
||||
} crash_t;
|
||||
|
||||
|
||||
static crash_t crash;
|
||||
|
||||
static vmCrashInfo_t crash_vm[VM_COUNT];
|
||||
static char crash_modName[64];
|
||||
static char crash_modVersion[64];
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define NEWLINE "\n"
|
||||
|
@ -39,13 +45,13 @@ static const char* GetVMName(vmIndex_t vmIndex)
|
|||
void Crash_SaveQVMPointer(vmIndex_t vmIndex, vm_t* vm)
|
||||
{
|
||||
if (IsVMIndexValid(vmIndex))
|
||||
crash_vm[vmIndex].vm = vm;
|
||||
crash.vm[vmIndex].vm = vm;
|
||||
}
|
||||
|
||||
void Crash_SaveQVMChecksum(vmIndex_t vmIndex, unsigned int crc32)
|
||||
{
|
||||
if (IsVMIndexValid(vmIndex))
|
||||
crash_vm[vmIndex].crc32 = crc32;
|
||||
crash.vm[vmIndex].crc32 = crc32;
|
||||
}
|
||||
|
||||
void Crash_SaveQVMGitString(const char* varName, const char* varValue)
|
||||
|
@ -63,26 +69,26 @@ void Crash_SaveQVMGitString(const char* varName, const char* varValue)
|
|||
else
|
||||
return;
|
||||
|
||||
Q_strncpyz(crash_vm[index].gitHeadHash, varValue, sizeof(crash_vm[index].gitHeadHash));
|
||||
Q_strncpyz(crash.vm[index].gitHeadHash, varValue, sizeof(crash.vm[index].gitHeadHash));
|
||||
}
|
||||
|
||||
void Crash_SaveModName(const char* modName)
|
||||
{
|
||||
if (modName && *modName != '\0')
|
||||
Q_strncpyz(crash_modName, modName, sizeof(crash_modName));
|
||||
Q_strncpyz(crash.modName, modName, sizeof(crash.modName));
|
||||
}
|
||||
|
||||
void Crash_SaveModVersion(const char* modVersion)
|
||||
{
|
||||
if (modVersion && *modVersion != '\0')
|
||||
Q_strncpyz(crash_modVersion, modVersion, sizeof(crash_modVersion));
|
||||
Q_strncpyz(crash.modVersion, modVersion, sizeof(crash.modVersion));
|
||||
}
|
||||
|
||||
static void PrintQVMInfo(vmIndex_t vmIndex)
|
||||
{
|
||||
static char callStack[MAX_VM_CALL_STACK_DEPTH * 12];
|
||||
|
||||
vmCrashInfo_t* vm = &crash_vm[vmIndex];
|
||||
vmCrash_t* vm = &crash.vm[vmIndex];
|
||||
if (vm->crc32 == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -131,7 +137,7 @@ static void PrintQVMInfo(vmIndex_t vmIndex)
|
|||
static qbool IsAnyVMLoaded()
|
||||
{
|
||||
for (int i = 0; i < VM_COUNT; i++) {
|
||||
if (crash_vm[i].crc32 != 0)
|
||||
if (crash.vm[i].crc32 != 0)
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
@ -206,8 +212,8 @@ void Crash_PrintToFile(const char* engineFilePath)
|
|||
const unsigned int crc32 = CRC32_HashFile(engineFilePath);
|
||||
if (crc32)
|
||||
JSONW_HexValue("engine_crc32", crc32);
|
||||
JSONW_StringValue("mod_name", crash_modName);
|
||||
JSONW_StringValue("mod_version", crash_modVersion);
|
||||
JSONW_StringValue("mod_name", crash.modName);
|
||||
JSONW_StringValue("mod_version", crash.modVersion);
|
||||
|
||||
if (IsAnyVMLoaded()) {
|
||||
JSONW_BeginNamedArray("vms");
|
||||
|
@ -217,3 +223,67 @@ void Crash_PrintToFile(const char* engineFilePath)
|
|||
JSONW_EndArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
static void Print(int fd, const char* msg)
|
||||
{
|
||||
write(fd, msg, strlen(msg));
|
||||
}
|
||||
|
||||
static void PrintVMStackTrace(int fd, vmIndex_t vmIndex)
|
||||
{
|
||||
Print(fd, GetVMName(vmIndex));
|
||||
Print(fd, ": ");
|
||||
|
||||
vmCrash_t* const vmc = &crash.vm[vmIndex];
|
||||
vm_t* const vm = vmc->vm;
|
||||
if (vm == NULL) {
|
||||
Print(fd, "not loaded\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Print(fd, "crc32 ");
|
||||
Print(fd, Q_itohex(vmc->crc32, qtrue, qtrue));
|
||||
Print(fd, " - ");
|
||||
|
||||
int d = vm->callStackDepth;
|
||||
qbool current = qtrue;
|
||||
if (d <= 0 || d > MAX_VM_CALL_STACK_DEPTH) {
|
||||
d = vm->lastCallStackDepth;
|
||||
current = qfalse;
|
||||
}
|
||||
|
||||
if (d <= 0 || d > MAX_VM_CALL_STACK_DEPTH)
|
||||
d = 0;
|
||||
|
||||
Print(fd, current ? "current " : "old ");
|
||||
if (d > 0) {
|
||||
for (int i = 0; i < d; i++) {
|
||||
Print(fd, Q_itohex(vm->callStack[i], qtrue, qtrue));
|
||||
if (i - 1 < d)
|
||||
Print(fd, " ");
|
||||
}
|
||||
}
|
||||
Print(fd, "\r\n");
|
||||
}
|
||||
|
||||
void Crash_PrintVMStackTracesASS(int fd)
|
||||
{
|
||||
if (crash.modName[0] != '\0') {
|
||||
Print(fd, "mod: ");
|
||||
Print(fd, crash.modName);
|
||||
Print(fd, " ");
|
||||
Print(fd, crash.modVersion);
|
||||
Print(fd, "\r\n");
|
||||
}
|
||||
|
||||
for (int i = 0; i < VM_COUNT; i++) {
|
||||
PrintVMStackTrace(fd, (vmIndex_t)i);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,3 +27,6 @@ void Crash_SaveQVMGitString(const char* varName, const char* varValue);
|
|||
void Crash_SaveModName(const char* modName);
|
||||
void Crash_SaveModVersion(const char* modVersion);
|
||||
void Crash_PrintToFile(const char* engineFilePath);
|
||||
#if defined(__linux__)
|
||||
void Crash_PrintVMStackTracesASS(int fd); // async-signal-safe
|
||||
#endif
|
||||
|
|
|
@ -38,7 +38,9 @@ void QGL_EnableLogging( qboolean enable );
|
|||
void QGL_Shutdown( void );
|
||||
|
||||
// signals.c
|
||||
void SIG_Init(void);
|
||||
void SIG_InitChild();
|
||||
void SIG_InitParent();
|
||||
void SIG_Frame();
|
||||
|
||||
extern int q_argc;
|
||||
extern char** q_argv;
|
||||
|
|
|
@ -22,6 +22,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
#include <signal.h>
|
||||
#include <execinfo.h>
|
||||
#include <backtrace.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
#include <cxxabi.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define UNW_LOCAL_ONLY
|
||||
#include "../libunwind/libunwind.h"
|
||||
|
||||
#include "../qcommon/q_shared.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
|
@ -29,36 +39,106 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
#include "linux_local.h"
|
||||
|
||||
|
||||
// columns: Symbol, IsCrash, Desc
|
||||
#define SIGNAL_LIST(X) \
|
||||
X(SIGHUP, qfalse, "hangup detected on controlling terminal or death of controlling process") \
|
||||
X(SIGQUIT, qfalse, "quit from keyboard") \
|
||||
X(SIGILL, qtrue, "illegal instruction") \
|
||||
X(SIGTRAP, qfalse, "trace/breakpoint trap") \
|
||||
X(SIGIOT, qtrue, "IOT trap (a synonym for SIGABRT)") \
|
||||
X(SIGBUS, qtrue, "bus error (bad memory access)") \
|
||||
X(SIGFPE, qtrue, "fatal arithmetic error") \
|
||||
X(SIGSEGV, qtrue, "invalid memory reference") \
|
||||
X(SIGTERM, qfalse, "termination signal") \
|
||||
X(SIGINT, qfalse, "interrupt")
|
||||
/*
|
||||
A sub-process created by fork from a signal handler will most certainly
|
||||
run in the context of the signal handler and should therefore also be
|
||||
limited to calling async-signal-safe functions only.
|
||||
|
||||
So the idea of launching a new process from a handler for doing the heavy
|
||||
lifting (resolving symbols etc) is completely dead.
|
||||
*/
|
||||
|
||||
|
||||
static qboolean Sig_IsCrashSignal(int sig)
|
||||
{
|
||||
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return IsCrash;
|
||||
switch (sig) {
|
||||
SIGNAL_LIST(SIGNAL_ITEM)
|
||||
default: return qfalse;
|
||||
}
|
||||
// the app crashed
|
||||
// columns: Symbol, Desc
|
||||
#define CRASH_SIGNAL_LIST(X) \
|
||||
X(SIGILL, "illegal instruction") \
|
||||
X(SIGIOT, "IOT trap (a synonym for SIGABRT)") \
|
||||
X(SIGBUS, "bus error (bad memory access)") \
|
||||
X(SIGFPE, "fatal arithmetic error") \
|
||||
X(SIGSEGV, "invalid memory reference")
|
||||
|
||||
// the app should terminate normally
|
||||
// columns: Symbol, Desc
|
||||
#define TERM_SIGNAL_LIST(X) \
|
||||
X(SIGHUP, "hangup detected on controlling terminal or death of controlling process") \
|
||||
X(SIGQUIT, "quit from keyboard") \
|
||||
X(SIGTRAP, "trace/breakpoint trap") \
|
||||
X(SIGTERM, "termination signal") \
|
||||
X(SIGINT, "interrupt")
|
||||
|
||||
|
||||
#define SIGNAL_ITEM(Symbol, Desc) 1 +
|
||||
static const int sig_crashSignalCount = CRASH_SIGNAL_LIST(SIGNAL_ITEM) 0;
|
||||
static const int sig_termSignalCount = TERM_SIGNAL_LIST(SIGNAL_ITEM) 0;
|
||||
#undef SIGNAL_ITEM
|
||||
|
||||
|
||||
#define SIGNAL_ITEM(Symbol, Desc) Symbol,
|
||||
static const int sig_crashSignals[sig_crashSignalCount + 1] =
|
||||
{
|
||||
CRASH_SIGNAL_LIST(SIGNAL_ITEM)
|
||||
0
|
||||
};
|
||||
static const int sig_termSignals[sig_termSignalCount + 1] =
|
||||
{
|
||||
TERM_SIGNAL_LIST(SIGNAL_ITEM)
|
||||
0
|
||||
};
|
||||
#undef SIGNAL_ITEM
|
||||
|
||||
|
||||
static char sig_backTraceFilePath[MAX_OSPATH];
|
||||
static char sig_jsonFilePath[MAX_OSPATH];
|
||||
|
||||
|
||||
static void Sig_Unwind_OpenLibrary();
|
||||
static void Sig_Unwind_GetContext();
|
||||
static void Sig_Unwind_Print(FILE* file);
|
||||
static void Sig_Unwind_PrintASS(int fd); // async-signal-safe
|
||||
|
||||
|
||||
// both of these can call Com_Error, which is not safe
|
||||
#define Q_strncpyz Sig_strncpyz
|
||||
#define Q_strcat Sig_strcat
|
||||
|
||||
// make sure we don't use these
|
||||
#define strcpy do_not_use_strcpy
|
||||
#define strcat do_not_use_strcat
|
||||
|
||||
|
||||
// async-signal-safe
|
||||
static void Sig_strncpyz(char* dest, const char* src, int size)
|
||||
{
|
||||
if (!dest || !src || size < 1)
|
||||
return;
|
||||
|
||||
strncpy(dest, src, size - 1);
|
||||
dest[size - 1] = '\0';
|
||||
}
|
||||
|
||||
|
||||
// async-signal-safe
|
||||
static void Sig_strcat(char* dest, int size, const char* src)
|
||||
{
|
||||
if (!dest || !src || size < 1)
|
||||
return;
|
||||
|
||||
const int destLength = strlen(dest);
|
||||
if (destLength >= size)
|
||||
return;
|
||||
|
||||
Sig_strncpyz(dest + destLength, src, size - destLength);
|
||||
}
|
||||
|
||||
|
||||
static const char* Sig_GetDescription(int sig)
|
||||
{
|
||||
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return Desc;
|
||||
switch (sig) {
|
||||
SIGNAL_LIST(SIGNAL_ITEM)
|
||||
#define SIGNAL_ITEM(Symbol, Desc) case Symbol: return Desc;
|
||||
switch (sig)
|
||||
{
|
||||
CRASH_SIGNAL_LIST(SIGNAL_ITEM)
|
||||
TERM_SIGNAL_LIST(SIGNAL_ITEM)
|
||||
default: return "unhandled signal";
|
||||
}
|
||||
#undef SIGNAL_ITEM
|
||||
|
@ -67,17 +147,21 @@ static const char* Sig_GetDescription(int sig)
|
|||
|
||||
static const char* Sig_GetName(int sig)
|
||||
{
|
||||
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return #Symbol;
|
||||
switch (sig) {
|
||||
SIGNAL_LIST(SIGNAL_ITEM)
|
||||
#define SIGNAL_ITEM(Symbol, Desc) case Symbol: return #Symbol;
|
||||
switch (sig)
|
||||
{
|
||||
CRASH_SIGNAL_LIST(SIGNAL_ITEM)
|
||||
TERM_SIGNAL_LIST(SIGNAL_ITEM)
|
||||
default: return "unhandled signal";
|
||||
}
|
||||
#undef SIGNAL_ITEM
|
||||
}
|
||||
|
||||
|
||||
static const char* Sig_CreateFileName(const char* extension)
|
||||
{
|
||||
static void Sig_UpdateFilePaths()
|
||||
{
|
||||
// gmtime and va (sprintf) are not async-signal-safe
|
||||
|
||||
const time_t epochTime = time(NULL);
|
||||
struct tm* const utcTime = gmtime(&epochTime);
|
||||
const int y = 1900 + utcTime->tm_year;
|
||||
|
@ -87,14 +171,37 @@ static const char* Sig_CreateFileName(const char* extension)
|
|||
const int mi = utcTime->tm_min;
|
||||
const int s = utcTime->tm_sec;
|
||||
|
||||
return va("%s-crash-%d.%02d.%02d-%02d.%02d.%02d%s",
|
||||
q_argv[0], y, m, d, h, mi, s, extension);
|
||||
const char* const bn = va(
|
||||
"%s-crash-%d.%02d.%02d-%02d.%02d.%02d",
|
||||
q_argv[0], y, m, d, h, mi, s);
|
||||
char baseName[MAX_OSPATH];
|
||||
Q_strncpyz(baseName, bn, sizeof(baseName));
|
||||
Q_strncpyz(sig_backTraceFilePath, va("%s.bt", baseName), sizeof(sig_backTraceFilePath));
|
||||
Q_strncpyz(sig_jsonFilePath, va("%s.json", baseName), sizeof(sig_jsonFilePath));
|
||||
}
|
||||
|
||||
|
||||
static const char* Sig_BackTraceFilePath()
|
||||
{
|
||||
if (sig_backTraceFilePath[0] == '\0')
|
||||
return "cnq3-crash.bt";
|
||||
|
||||
return sig_backTraceFilePath;
|
||||
}
|
||||
|
||||
|
||||
static const char* Sig_JSONFilePath()
|
||||
{
|
||||
if (sig_jsonFilePath[0] == '\0')
|
||||
return "cnq3-crash.json";
|
||||
|
||||
return sig_jsonFilePath;
|
||||
}
|
||||
|
||||
|
||||
static void Sig_WriteJSON(int sig)
|
||||
{
|
||||
FILE* const file = fopen(Sig_CreateFileName(".json"), "w");
|
||||
FILE* const file = fopen(Sig_JSONFilePath(), "w");
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
|
@ -108,18 +215,12 @@ static void Sig_WriteJSON(int sig)
|
|||
}
|
||||
|
||||
|
||||
// only uses functions safe to call in a signal handler
|
||||
static void Sig_WriteBacktraceSafe()
|
||||
static void Sig_Backtrace_Print(FILE* file)
|
||||
{
|
||||
FILE* const file = fopen(Sig_CreateFileName(".bt"), "w");
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
void* addresses[64];
|
||||
const int addresscount = backtrace(addresses, sizeof(addresses));
|
||||
if (addresscount > 0)
|
||||
{
|
||||
fprintf(file, "backtrace_symbols_fd stack trace:\r\n");
|
||||
fflush(file);
|
||||
backtrace_symbols_fd(addresses, addresscount, fileno(file));
|
||||
}
|
||||
|
@ -127,8 +228,6 @@ static void Sig_WriteBacktraceSafe()
|
|||
{
|
||||
fprintf(file, "The call to backtrace failed\r\n");
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
|
||||
|
@ -138,128 +237,367 @@ static void libbt_ErrorCallback(void* data, const char* msg, int errnum)
|
|||
}
|
||||
|
||||
|
||||
// might not be safe in a signal handler
|
||||
static void Sig_WriteBacktraceUnsafe()
|
||||
static void Sig_Print(int fd, const char* msg)
|
||||
{
|
||||
FILE* const file = fopen(Sig_CreateFileName(".bt"), "a");
|
||||
if (file == NULL)
|
||||
return;
|
||||
write(fd, msg, strlen(msg));
|
||||
}
|
||||
|
||||
fprintf(file, "\r\n\r\n");
|
||||
|
||||
backtrace_state* const state = backtrace_create_state(q_argv[0], 0, libbt_ErrorCallback, file);
|
||||
if (state)
|
||||
static void Sig_PrintLine(int fd, const char* msg)
|
||||
{
|
||||
write(fd, msg, strlen(msg));
|
||||
write(fd, "\r\n", 2);
|
||||
}
|
||||
|
||||
|
||||
static void Sig_PrintSignalCaught(int sig)
|
||||
{
|
||||
// fprintf is not async-signal-safe, but write is
|
||||
Sig_Print(STDERR_FILENO, "\nSignal caught: ");
|
||||
Sig_Print(STDERR_FILENO, Sig_GetName(sig));
|
||||
Sig_Print(STDERR_FILENO, " (");
|
||||
Sig_Print(STDERR_FILENO, Sig_GetDescription(sig));
|
||||
Sig_Print(STDERR_FILENO, ")\r\n");
|
||||
}
|
||||
|
||||
|
||||
static void Sig_HandleExit()
|
||||
{
|
||||
Sys_ConsoleInputShutdown();
|
||||
}
|
||||
|
||||
|
||||
static volatile sig_atomic_t sig_termRequested = 0;
|
||||
|
||||
|
||||
static void Sig_HandleTermSignal(int sig)
|
||||
{
|
||||
Sig_PrintSignalCaught(sig);
|
||||
sig_termRequested = 1;
|
||||
}
|
||||
|
||||
|
||||
static void Sig_PrintAttempt(const char* what)
|
||||
{
|
||||
Sig_Print(STDERR_FILENO, "Attempting to ");
|
||||
Sig_Print(STDERR_FILENO, what);
|
||||
Sig_Print(STDERR_FILENO, "...");
|
||||
}
|
||||
|
||||
|
||||
static void Sig_PrintDone()
|
||||
{
|
||||
Sig_Print(STDERR_FILENO, " done\r\n");
|
||||
}
|
||||
|
||||
|
||||
static void Sig_HandleCrash(int sig)
|
||||
{
|
||||
//
|
||||
// Phase 1: async-signal-safe code
|
||||
//
|
||||
|
||||
Sig_Unwind_GetContext();
|
||||
|
||||
const int fd = open(Sig_BackTraceFilePath(), O_CREAT | O_TRUNC | O_WRONLY);
|
||||
if (fd != -1)
|
||||
{
|
||||
fprintf(file, "libbacktrace stack trace:\r\n");
|
||||
Sig_PrintAttempt("write safe libunwind stack trace");
|
||||
Sig_PrintLine(fd, "safe libunwind stack trace:");
|
||||
Sig_Unwind_PrintASS(fd);
|
||||
Sig_PrintDone();
|
||||
|
||||
Sig_PrintAttempt("write safe mod stack traces");
|
||||
Sig_PrintLine(fd, "\r\n\r\nmod stack traces:");
|
||||
Crash_PrintVMStackTracesASS(fd);
|
||||
Sig_PrintDone();
|
||||
|
||||
// user R+W - group R - others R
|
||||
chmod(Sig_BackTraceFilePath(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
}
|
||||
|
||||
//
|
||||
// Phase 2: buckle up
|
||||
//
|
||||
|
||||
Sig_PrintAttempt("restore tty settings");
|
||||
Sys_ConsoleInputShutdown();
|
||||
Sig_PrintDone();
|
||||
|
||||
Sig_PrintAttempt("write JSON report");
|
||||
Sig_WriteJSON(sig);
|
||||
Sig_PrintDone();
|
||||
|
||||
if (fd != -1)
|
||||
{
|
||||
FILE* const file = fdopen(fd, "a");
|
||||
|
||||
Sig_PrintAttempt("write backtrace stack trace");
|
||||
fprintf(file, "\r\n\r\nbacktrace_symbols_fd stack trace:\r\n");
|
||||
Sig_Backtrace_Print(file);
|
||||
fflush(file);
|
||||
backtrace_print(state, 0, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "The call to backtrace_create_state failed\r\n");
|
||||
Sig_PrintDone();
|
||||
|
||||
Sig_PrintAttempt("write detailed libunwind stack trace");
|
||||
fprintf(file, "\r\n\r\ndetailed libunwind stack trace:\r\n");
|
||||
Sig_Unwind_Print(file);
|
||||
fflush(file);
|
||||
Sig_PrintDone();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Sig_HandleCrashSignal(int sig)
|
||||
{
|
||||
static volatile sig_atomic_t counter = 0;
|
||||
|
||||
Sig_PrintSignalCaught(sig);
|
||||
|
||||
++counter;
|
||||
|
||||
if (counter == 1)
|
||||
Sig_HandleCrash(sig);
|
||||
else if (counter >= 2)
|
||||
Sig_PrintLine(STDERR_FILENO, "Double fault! Shutting down immediately.");
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
|
||||
static void Sig_RegisterSignals(const int* signals, int count, void (*handler)(int), int flags)
|
||||
{
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
|
||||
fclose(file);
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = handler;
|
||||
action.sa_mask = mask;
|
||||
action.sa_flags = flags;
|
||||
for (int i = 0; i < count; ++i)
|
||||
sigaction(signals[i], &action, NULL);
|
||||
}
|
||||
|
||||
|
||||
static qbool sig_crashed = qfalse;
|
||||
static int sig_signal = 0;
|
||||
|
||||
|
||||
// Every call in there needs to be safe when called more than once.
|
||||
static void SIG_HandleCrash(qbool restoreSettings)
|
||||
{
|
||||
if (restoreSettings)
|
||||
{
|
||||
// We crashed and only care about restoring system settings
|
||||
// that the process clean-up won't handle for us.
|
||||
Sys_ConsoleInputShutdown();
|
||||
}
|
||||
|
||||
if (sig_crashed)
|
||||
{
|
||||
Sig_WriteJSON(sig_signal);
|
||||
Sig_WriteBacktraceSafe();
|
||||
Sig_WriteBacktraceUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void SIG_HandleExit()
|
||||
{
|
||||
SIG_HandleCrash(qtrue);
|
||||
}
|
||||
|
||||
|
||||
static void Sig_HandleSignal(int sig)
|
||||
{
|
||||
static int faultCounter = 0;
|
||||
static qbool crashHandled = qfalse;
|
||||
|
||||
sig_signal = sig;
|
||||
faultCounter++;
|
||||
|
||||
if (faultCounter >= 3)
|
||||
{
|
||||
// We're here because the double fault handler failed.
|
||||
// We take no chances this time and exit right away to avoid
|
||||
// calling this function many more times.
|
||||
exit(3);
|
||||
}
|
||||
|
||||
sig_crashed = Sig_IsCrashSignal(sig);
|
||||
if (faultCounter == 2)
|
||||
{
|
||||
// The termination handler failed which means that if we exit right now,
|
||||
// some system settings might still be in a bad state.
|
||||
printf("DOUBLE SIGNAL FAULT: Received signal %d (%s), exiting...\n", sig, Sig_GetName(sig));
|
||||
if (sig_crashed && !crashHandled)
|
||||
{
|
||||
SIG_HandleCrash(qfalse);
|
||||
}
|
||||
exit(2);
|
||||
}
|
||||
|
||||
fprintf(sig_crashed ? stderr : stdout, "Received %s signal %d: %s (%s), exiting...\n",
|
||||
sig_crashed ? "crash" : "termination", sig, Sig_GetName(sig), Sig_GetDescription(sig));
|
||||
|
||||
if (sig_crashed)
|
||||
{
|
||||
SIG_HandleCrash(qtrue);
|
||||
crashHandled = qtrue;
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// attempt a proper shutdown sequence
|
||||
#ifndef DEDICATED
|
||||
CL_Shutdown();
|
||||
#endif
|
||||
SV_Shutdown("Signal caught");
|
||||
Sys_ConsoleInputShutdown();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SIG_Init()
|
||||
{
|
||||
void SIG_InitChild()
|
||||
{
|
||||
// This is unfortunately needed because some code might
|
||||
// call exit and bypass all the clean-up work without
|
||||
// there ever being a real crash.
|
||||
// This happens for instance with the "fatal IO error"
|
||||
// of the X server.
|
||||
atexit(SIG_HandleExit);
|
||||
|
||||
signal(SIGILL, Sig_HandleSignal);
|
||||
signal(SIGIOT, Sig_HandleSignal);
|
||||
signal(SIGBUS, Sig_HandleSignal);
|
||||
signal(SIGFPE, Sig_HandleSignal);
|
||||
signal(SIGSEGV, Sig_HandleSignal);
|
||||
signal(SIGHUP, Sig_HandleSignal);
|
||||
signal(SIGQUIT, Sig_HandleSignal);
|
||||
signal(SIGTRAP, Sig_HandleSignal);
|
||||
signal(SIGTERM, Sig_HandleSignal);
|
||||
signal(SIGINT, Sig_HandleSignal);
|
||||
atexit(Sig_HandleExit);
|
||||
|
||||
// The crash handler gets SA_NODEFER so that we can handle faults it generates.
|
||||
Sig_RegisterSignals(sig_crashSignals, sig_crashSignalCount, Sig_HandleCrashSignal, SA_NODEFER);
|
||||
Sig_RegisterSignals(sig_termSignals, sig_termSignalCount, Sig_HandleTermSignal, 0);
|
||||
|
||||
// Must do this now because it's not safe in a signal handler.
|
||||
Sig_UpdateFilePaths();
|
||||
Sig_Unwind_OpenLibrary();
|
||||
}
|
||||
|
||||
|
||||
void SIG_InitParent()
|
||||
{
|
||||
// Signals are sent to both the parent and the child, which can be an issue.
|
||||
// Our parent process has to ignore all signals handled by the child and
|
||||
// let the child do whatever needs to be done.
|
||||
|
||||
for (int i = 0; i < sig_crashSignalCount; ++i)
|
||||
signal(sig_crashSignals[i], SIG_IGN);
|
||||
|
||||
for (int i = 0; i < sig_termSignalCount; ++i)
|
||||
signal(sig_termSignals[i], SIG_IGN);
|
||||
}
|
||||
|
||||
|
||||
void SIG_Frame()
|
||||
{
|
||||
// Must do this now because it's not safe in a signal handler.
|
||||
Sig_UpdateFilePaths();
|
||||
|
||||
if (sig_termRequested != 0)
|
||||
Com_Quit(0);
|
||||
}
|
||||
|
||||
|
||||
// We require libunwind8 specifically for now.
|
||||
// Even if there was a general symlink without the number,
|
||||
// it would still not be safe to load it because
|
||||
// the library provides no way to query its version.
|
||||
#define LIBUNWIND_PATH "libunwind-" XSTRING(UNW_TARGET) ".so.8"
|
||||
|
||||
|
||||
struct libUnwind_t
|
||||
{
|
||||
unw_context_t context;
|
||||
int (*getcontext)(unw_context_t *);
|
||||
int (*init_local)(unw_cursor_t *, unw_context_t *);
|
||||
int (*step)(unw_cursor_t *);
|
||||
int (*get_reg)(unw_cursor_t *, unw_regnum_t, unw_word_t *);
|
||||
int (*get_proc_name)(unw_cursor_t *, char *, size_t, unw_word_t *);
|
||||
void* handle;
|
||||
qbool valid;
|
||||
};
|
||||
|
||||
|
||||
static libUnwind_t unw;
|
||||
|
||||
|
||||
static void Sig_Unwind_GetContext()
|
||||
{
|
||||
if (!unw.valid)
|
||||
return;
|
||||
|
||||
if (unw.getcontext(&unw.context) != 0)
|
||||
{
|
||||
Sig_PrintLine(STDERR_FILENO, "The call to libunwind's getcontext failed");
|
||||
unw.valid = qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int Sig_Unwind_GetFunction(void** func, const char* name)
|
||||
{
|
||||
*func = dlsym(unw.handle, name);
|
||||
|
||||
return *func != NULL;
|
||||
}
|
||||
|
||||
|
||||
static void Sig_Unwind_OpenLibrary()
|
||||
{
|
||||
// dlopen, dlsym, fprintf are not async-signal-safe
|
||||
|
||||
unw.valid = qfalse;
|
||||
unw.handle = dlopen(LIBUNWIND_PATH, RTLD_NOW);
|
||||
if (unw.handle == NULL)
|
||||
{
|
||||
const char* errorMsg = dlerror();
|
||||
if (errorMsg != NULL)
|
||||
fprintf(stderr, "\nFailed to load %s: %s\n", LIBUNWIND_PATH, errorMsg);
|
||||
else
|
||||
fprintf(stderr, "\nFailed to find/load %s\n", LIBUNWIND_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
#define GET2(Var, Name) \
|
||||
if (!Sig_Unwind_GetFunction((void**)&unw.Var, Name)) \
|
||||
{ \
|
||||
fprintf(stderr, "\nFailed to find libunwind function %s\n", Name); \
|
||||
return; \
|
||||
}
|
||||
#define GET(Name) GET2(Name, XSTRING(UNW_OBJ(Name)))
|
||||
GET2(getcontext, "_Ux86_64_getcontext");
|
||||
GET(init_local);
|
||||
GET(step);
|
||||
GET(get_reg);
|
||||
GET(get_proc_name);
|
||||
#undef GET
|
||||
#undef GET2
|
||||
|
||||
unw.valid = qtrue;
|
||||
}
|
||||
|
||||
|
||||
static int Sig_Unwind_GetFileAndLine(unw_word_t addr, char* file, size_t flen, int* line)
|
||||
{
|
||||
static char buf[1024];
|
||||
|
||||
sprintf(buf, "addr2line -C -e %s -f -i %lx", q_argv[0], (unsigned long)addr);
|
||||
FILE* f = popen(buf, "r");
|
||||
if (f == NULL)
|
||||
return 0;
|
||||
|
||||
fgets(buf, sizeof(buf), f); // function name
|
||||
fgets(buf, sizeof(buf), f); // file and line
|
||||
pclose(f);
|
||||
|
||||
if (buf[0] == '?')
|
||||
return 0;
|
||||
|
||||
// file name before ':'
|
||||
char* p = buf;
|
||||
while (*p != ':')
|
||||
++p;
|
||||
*p = '\0';
|
||||
|
||||
// skip the folder names
|
||||
char* name = strrchr(buf, '/');
|
||||
name = name ? (name + 1) : buf;
|
||||
|
||||
// line number
|
||||
++p;
|
||||
Q_strncpyz(file, name, flen);
|
||||
sscanf(p, "%d", line);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void Sig_Unwind_Print(FILE* fp)
|
||||
{
|
||||
static char name[1024];
|
||||
static char file[1024];
|
||||
|
||||
if (!unw.valid)
|
||||
return;
|
||||
|
||||
unw_cursor_t cursor;
|
||||
if (unw.init_local(&cursor, &unw.context) != 0)
|
||||
{
|
||||
fprintf(fp, "The call to libunwind's init_local failed\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (unw.step(&cursor) > 0)
|
||||
{
|
||||
const char* func = name;
|
||||
unw_word_t ip, sp, offp;
|
||||
if (unw.get_proc_name(&cursor, name, sizeof(name), &offp))
|
||||
{
|
||||
Q_strncpyz(name, "???", sizeof(name));
|
||||
}
|
||||
else
|
||||
{
|
||||
int status;
|
||||
char* niceName = abi::__cxa_demangle(name, NULL, NULL, &status);
|
||||
if (status == 0)
|
||||
{
|
||||
Q_strncpyz(name, niceName, sizeof(name));
|
||||
free(niceName);
|
||||
}
|
||||
}
|
||||
|
||||
int line = 0;
|
||||
unw.get_reg(&cursor, UNW_REG_IP, &ip);
|
||||
if (Sig_Unwind_GetFileAndLine((long)ip, file, sizeof(file), &line))
|
||||
fprintf(fp, "%s at %s:%d\n", name, file, line);
|
||||
else
|
||||
fprintf(fp, "%s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// async-signal-safe
|
||||
void Sig_Unwind_PrintASS(int fd)
|
||||
{
|
||||
static char name[1024];
|
||||
|
||||
if (!unw.valid)
|
||||
return;
|
||||
|
||||
unw_cursor_t cursor;
|
||||
if (unw.init_local(&cursor, &unw.context) != 0)
|
||||
return;
|
||||
|
||||
while (unw.step(&cursor) > 0)
|
||||
{
|
||||
unw_word_t offp;
|
||||
if (unw.get_proc_name(&cursor, name, sizeof(name), &offp))
|
||||
Q_strncpyz(name, "???", sizeof(name));
|
||||
|
||||
Sig_PrintLine(fd, name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -877,6 +877,8 @@ static void Lin_HardRebootHandler( int argc, char** argv )
|
|||
args[argc + 0] = (char*)"nohardreboot";
|
||||
args[argc + 1] = NULL;
|
||||
|
||||
SIG_InitParent();
|
||||
|
||||
for (;;) {
|
||||
if (Lin_RunProcess(args) == 0)
|
||||
_exit(0); // quit without calling atexit handlers
|
||||
|
@ -896,7 +898,7 @@ int main( int argc, char** argv )
|
|||
Lin_HardRebootHandler(argc, argv);
|
||||
#endif
|
||||
|
||||
SIG_Init();
|
||||
SIG_InitChild();
|
||||
|
||||
// merge the command line: we need it in a single chunk
|
||||
int len = 1, i;
|
||||
|
@ -918,6 +920,7 @@ int main( int argc, char** argv )
|
|||
Sys_ConsoleInputInit();
|
||||
|
||||
for (;;) {
|
||||
SIG_Frame();
|
||||
Com_Frame();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ ifeq ($(config),debug_x32)
|
|||
INCLUDES +=
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS +=
|
||||
|
@ -46,7 +46,7 @@ ifeq ($(config),debug_x64)
|
|||
INCLUDES +=
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS +=
|
||||
|
|
|
@ -22,7 +22,7 @@ ifeq ($(config),debug_x32)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -x c++ -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../.build/debug_x32/libbotlib.a -ldl -lm -lbacktrace
|
||||
LIBS += ../../.build/debug_x32/libbotlib.a -ldl -lm
|
||||
LDDEPS += ../../.build/debug_x32/libbotlib.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/debug_x32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -53,7 +53,7 @@ ifeq ($(config),debug_x64)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -x c++ -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../.build/debug_x64/libbotlib.a -ldl -lm -lbacktrace
|
||||
LIBS += ../../.build/debug_x64/libbotlib.a -ldl -lm
|
||||
LDDEPS += ../../.build/debug_x64/libbotlib.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/debug_x64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -84,7 +84,7 @@ ifeq ($(config),release_x32)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -x c++ -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../.build/release_x32/libbotlib.a -ldl -lm -lbacktrace
|
||||
LIBS += ../../.build/release_x32/libbotlib.a -ldl -lm
|
||||
LDDEPS += ../../.build/release_x32/libbotlib.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/release_x32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -115,7 +115,7 @@ ifeq ($(config),release_x64)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -x c++ -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../.build/release_x64/libbotlib.a -ldl -lm -lbacktrace
|
||||
LIBS += ../../.build/release_x64/libbotlib.a -ldl -lm
|
||||
LDDEPS += ../../.build/release_x64/libbotlib.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/release_x64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
|
|
@ -22,7 +22,7 @@ ifeq ($(config),debug_x32)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -pthread -x c++ -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
|
||||
LIBS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
|
||||
LDDEPS += ../../.build/debug_x32/libbotlib.a ../../.build/debug_x32/librenderer.a ../../.build/debug_x32/libfreetype.a ../../.build/debug_x32/liblibjpeg-turbo.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/debug_x32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -53,7 +53,7 @@ ifeq ($(config),debug_x64)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -pthread -x c++ -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
|
||||
LIBS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
|
||||
LDDEPS += ../../.build/debug_x64/libbotlib.a ../../.build/debug_x64/librenderer.a ../../.build/debug_x64/libfreetype.a ../../.build/debug_x64/liblibjpeg-turbo.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/debug_x64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -84,7 +84,7 @@ ifeq ($(config),release_x32)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -pthread -x c++ -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
|
||||
LIBS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
|
||||
LDDEPS += ../../.build/release_x32/libbotlib.a ../../.build/release_x32/librenderer.a ../../.build/release_x32/libfreetype.a ../../.build/release_x32/liblibjpeg-turbo.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../.build/release_x32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -115,7 +115,7 @@ ifeq ($(config),release_x64)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -pthread -x c++ -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
|
||||
LIBS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
|
||||
LDDEPS += ../../.build/release_x64/libbotlib.a ../../.build/release_x64/librenderer.a ../../.build/release_x64/libfreetype.a ../../.build/release_x64/liblibjpeg-turbo.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../.build/release_x64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
|
|
@ -19,7 +19,7 @@ ifeq ($(config),debug_x32)
|
|||
INCLUDES += -I../../code/freetype/include
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS +=
|
||||
|
@ -46,7 +46,7 @@ ifeq ($(config),debug_x64)
|
|||
INCLUDES += -I../../code/freetype/include
|
||||
FORCE_INCLUDE +=
|
||||
ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings
|
||||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -std=c++98
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS +=
|
||||
|
|
|
@ -472,7 +472,7 @@ local function ApplyExeProjectSettings(exeName, server)
|
|||
end
|
||||
|
||||
filter "system:not windows"
|
||||
links { "dl", "m", "backtrace" }
|
||||
links { "dl", "m" }
|
||||
if (server == 0) then
|
||||
buildoptions { "-pthread" }
|
||||
links { "X11", "pthread" }
|
||||
|
@ -655,7 +655,8 @@ solution "cnq3"
|
|||
kind "WindowedApp"
|
||||
language "C++"
|
||||
ApplyExeProjectSettings("cnq3", 0)
|
||||
buildoptions { "-std=c++98" }
|
||||
filter "action:gmake"
|
||||
buildoptions { "-std=c++98" }
|
||||
|
||||
project "cnq3-server"
|
||||
|
||||
|
@ -663,7 +664,8 @@ solution "cnq3"
|
|||
language "C++"
|
||||
defines { "DEDICATED" }
|
||||
ApplyExeProjectSettings("cnq3-server", 1)
|
||||
buildoptions { "-std=c++98" }
|
||||
filter "action:gmake"
|
||||
buildoptions { "-std=c++98" }
|
||||
|
||||
project "botlib"
|
||||
|
||||
|
@ -672,7 +674,8 @@ solution "cnq3"
|
|||
defines { "BOTLIB" }
|
||||
AddSourcesAndHeaders("botlib")
|
||||
ApplyLibProjectSettings()
|
||||
buildoptions { "-std=c++98" }
|
||||
filter "action:gmake"
|
||||
buildoptions { "-std=c++98" }
|
||||
|
||||
project "renderer"
|
||||
|
||||
|
@ -681,7 +684,8 @@ solution "cnq3"
|
|||
AddSourcesAndHeaders("renderer")
|
||||
includedirs { path_src.."/freetype/include" }
|
||||
ApplyLibProjectSettings()
|
||||
buildoptions { "-std=c++98" }
|
||||
filter "action:gmake"
|
||||
buildoptions { "-std=c++98" }
|
||||
|
||||
project "libjpeg-turbo"
|
||||
|
||||
|
|
Loading…
Reference in a new issue