mirror of
https://github.com/gozfree/gear-lib.git
synced 2026-02-07 12:19:15 +08:00
298 lines
8.7 KiB
C
298 lines
8.7 KiB
C
/******************************************************************************
|
|
* Copyright (C) 2014-2020 Zhifeng Gong <gozfree@163.com>
|
|
*
|
|
* 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 __cplusplus
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
#include <ucontext.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#if !defined(__UCLIBC__)
|
|
#include <execinfo.h>
|
|
#endif
|
|
|
|
#include "libdebug.h"
|
|
|
|
#define BT_SIZE 100
|
|
#define CMD_SIZE 1024
|
|
|
|
/*
|
|
* from /usr/include/bits/signum.h
|
|
*/
|
|
static int signals_all[] = {
|
|
SIGHUP, /* 1 Hangup (POSIX). */
|
|
SIGINT, /* 2 Interrupt (ANSI). */
|
|
SIGQUIT, /* 3 Quit (POSIX). */
|
|
SIGILL, /* 4 Illegal instruction (ANSI). */
|
|
SIGTRAP, /* 5 Trace trap (POSIX). */
|
|
SIGABRT, /* 6 Abort (ANSI). */
|
|
SIGIOT, /* 6 IOT trap (4.2 BSD). */
|
|
SIGBUS, /* 7 BUS error (4.2 BSD). */
|
|
SIGFPE, /* 8 Floating-point exception (ANSI). */
|
|
SIGKILL, /* 9 Kill, unblockable (POSIX). */
|
|
SIGUSR1, /* 10 User-defined signal 1 (POSIX). */
|
|
SIGSEGV, /* 11 Segmentation violation (ANSI). */
|
|
SIGUSR2, /* 12 User-defined signal 2 (POSIX). */
|
|
SIGPIPE, /* 13 Broken pipe (POSIX). */
|
|
SIGALRM, /* 14 Alarm clock (POSIX). */
|
|
SIGTERM, /* 15 Termination (ANSI). */
|
|
SIGSTKFLT, /* 16 Stack fault. */
|
|
SIGCLD, /* 17 Same as SIGCHLD (System V). */
|
|
SIGCHLD, /* 17 Child status has changed (POSIX). */
|
|
SIGCONT, /* 18 Continue (POSIX). */
|
|
SIGSTOP, /* 19 Stop, unblockable (POSIX). */
|
|
SIGTSTP, /* 20 Keyboard stop (POSIX). */
|
|
SIGTTIN, /* 21 Background read from tty (POSIX). */
|
|
SIGTTOU, /* 22 Background write to tty (POSIX). */
|
|
SIGURG, /* 23 Urgent condition on socket (4.2 BSD). */
|
|
SIGXCPU, /* 24 CPU limit exceeded (4.2 BSD). */
|
|
SIGXFSZ, /* 25 File size limit exceeded (4.2 BSD). */
|
|
SIGVTALRM, /* 26 Virtual alarm clock (4.2 BSD). */
|
|
SIGPROF, /* 27 Profiling alarm clock (4.2 BSD). */
|
|
SIGWINCH, /* 28 Window size change (4.3 BSD, Sun). */
|
|
SIGPOLL, /* 29 Pollable event occurred (System V). */
|
|
SIGIO, /* 29 I/O now possible (4.2 BSD). */
|
|
SIGPWR, /* 30 Power failure restart (System V). */
|
|
SIGSYS, /* 31 Bad system call. */
|
|
};
|
|
|
|
static int signals_trace[] =
|
|
{
|
|
SIGILL, /* Illegal instruction (ANSI). */
|
|
SIGABRT, /* Abort (ANSI). */
|
|
SIGBUS, /* BUS error (4.2 BSD). (unaligned access) */
|
|
SIGFPE, /* Floating-point exception (ANSI). */
|
|
SIGSEGV, /* Segmentation violation (ANSI). */
|
|
};
|
|
|
|
#if !defined(__UCLIBC__)
|
|
static void *get_uc_mcontext_pc(ucontext_t *uc)
|
|
{
|
|
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
|
|
/* OSX < 10.6 */
|
|
#if defined(__x86_64__)
|
|
return (void*) uc->uc_mcontext->__ss.__rip;
|
|
#elif defined(__i386__)
|
|
return (void*) uc->uc_mcontext->__ss.__eip;
|
|
#else
|
|
return (void*) uc->uc_mcontext->__ss.__srr0;
|
|
#endif
|
|
#elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
|
|
/* OSX >= 10.6 */
|
|
#if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
|
|
return (void*) uc->uc_mcontext->__ss.__rip;
|
|
#else
|
|
return (void*) uc->uc_mcontext->__ss.__eip;
|
|
#endif
|
|
#elif defined(__linux__)
|
|
/* Linux */
|
|
#if defined(__i386__)
|
|
return (void*) uc->uc_mcontext.gregs[REG_EIP]; /* Linux 32 */
|
|
#elif defined(__X86_64__) || defined(__x86_64__)
|
|
return (void*) uc->uc_mcontext.gregs[REG_RIP]; /* Linux 64 */
|
|
#elif defined(__ia64__) /* Linux IA64 */
|
|
return (void*) uc->uc_mcontext.sc_ip;
|
|
#elif defined(__arm__)
|
|
return (void*) uc->uc_mcontext.arm_pc;
|
|
#elif defined(__aarch64__)
|
|
return (void*) uc->uc_mcontext.pc;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if !defined(__UCLIBC__)
|
|
static void backtrace_symbols_detail(void *array[], int size)
|
|
{
|
|
int i;
|
|
char cmd[CMD_SIZE] = "addr2line -C -f -e ";
|
|
char* prog = cmd + strlen(cmd);
|
|
int r = readlink("/proc/self/exe", prog, sizeof(cmd) - (prog-cmd)-1);
|
|
if (r == -1) {
|
|
fprintf(stderr, "backtrace_symbols_detail unsupported!\n");
|
|
perror("readlink");
|
|
return;
|
|
}
|
|
prog[r] = '\0';
|
|
FILE* fp = popen(cmd, "w");
|
|
if (!fp) {
|
|
perror("popen");
|
|
return;
|
|
}
|
|
for (i = 1; i < size; ++i) {//from 1, ignore this file info
|
|
fprintf(fp, "%p\n", array[i]);
|
|
}
|
|
fclose(fp);
|
|
}
|
|
#endif
|
|
|
|
static void backtrace_handler(int sig_num, siginfo_t *info, void *ucontext)
|
|
{
|
|
#if !defined(__UCLIBC__)
|
|
void *array[BT_SIZE];
|
|
int i, size = 0;
|
|
char **strings = NULL;
|
|
void *caller_addr = get_uc_mcontext_pc((ucontext_t *)ucontext);
|
|
fprintf(stderr, "Program received signal %s.\n", strsignal(sig_num));
|
|
fprintf(stderr, "%d:%s [%p]\n", sig_num, strerror(sig_num), caller_addr);
|
|
|
|
size = backtrace(array, BT_SIZE);
|
|
/* overwrite sigaction with caller's address, but seems no needed */
|
|
array[1] = caller_addr;
|
|
strings = backtrace_symbols(array, size);
|
|
for (i = 0; i < size; ++ i) {
|
|
fprintf(stderr, "#%d %s\n", i, strings[i]);
|
|
}
|
|
backtrace_symbols_detail(array, size);
|
|
#endif
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int debug_backtrace_init(void)
|
|
{
|
|
int i;
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(struct sigaction));
|
|
sa.sa_sigaction = backtrace_handler;
|
|
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
|
int ret = 0;
|
|
for (i = 0; i < (sizeof(signals_trace) / sizeof(int)); ++i) {
|
|
if (sigaction(signals_trace[i], &sa, NULL) != 0) {
|
|
fprintf(stderr, "backtrace failed to set signal handler for %s(%d)!\n",
|
|
strsignal(signals_trace[i]), signals_trace[i]);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void debug_backtrace_dump(void)
|
|
{
|
|
#if !defined(__UCLIBC__)
|
|
void* buffer[BT_SIZE];
|
|
int size = backtrace(buffer, BT_SIZE);
|
|
backtrace_symbols_detail(buffer, size);
|
|
#endif
|
|
}
|
|
|
|
static void debug_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)
|
|
{
|
|
fprintf(stderr, "signal %d (%s) received from %d\n",
|
|
signo, strsignal(signo), siginfo->si_pid);
|
|
|
|
switch (signo) {
|
|
case SIGHUP:
|
|
break;
|
|
case SIGINT:
|
|
break;
|
|
case SIGQUIT:
|
|
break;
|
|
case SIGILL:
|
|
break;
|
|
case SIGTRAP:
|
|
break;
|
|
case SIGABRT://same as SIGIOT:
|
|
break;
|
|
case SIGBUS:
|
|
break;
|
|
case SIGFPE:
|
|
break;
|
|
case SIGKILL:
|
|
break;
|
|
case SIGUSR1:
|
|
break;
|
|
case SIGSEGV:
|
|
signal(signo, SIG_IGN);
|
|
break;
|
|
case SIGUSR2:
|
|
break;
|
|
case SIGPIPE:
|
|
break;
|
|
case SIGALRM:
|
|
break;
|
|
case SIGTERM:
|
|
break;
|
|
case SIGSTKFLT:
|
|
break;
|
|
case SIGCLD://same as SIGCHLD
|
|
break;
|
|
case SIGCONT:
|
|
break;
|
|
case SIGSTOP:
|
|
break;
|
|
case SIGTSTP:
|
|
break;
|
|
case SIGTTIN:
|
|
break;
|
|
case SIGTTOU:
|
|
break;
|
|
case SIGURG:
|
|
break;
|
|
case SIGXCPU:
|
|
break;
|
|
case SIGXFSZ:
|
|
break;
|
|
case SIGVTALRM:
|
|
break;
|
|
case SIGPROF:
|
|
break;
|
|
case SIGWINCH:
|
|
break;
|
|
case SIGPOLL://same as SIGIO
|
|
break;
|
|
case SIGPWR:
|
|
break;
|
|
case SIGSYS://same as SIGUNUSED
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int debug_signals_init(void)
|
|
{
|
|
int i;
|
|
struct sigaction sa;
|
|
memset(&sa, 0, sizeof(struct sigaction));
|
|
sa.sa_sigaction = debug_signal_handler;
|
|
sa.sa_flags = SA_RESTART | SA_SIGINFO;
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
for (i = 0; i < (sizeof(signals_all) / sizeof(int)); ++i) {
|
|
if (sigaction(signals_all[i], &sa, NULL) == -1) {
|
|
fprintf(stderr, "signal failed to set signal handler for %s(%d)!\n",
|
|
strsignal(signals_all[i]), signals_all[i]);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|