-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathvterm_debug.c
More file actions
160 lines (132 loc) · 4.06 KB
/
Copy pathvterm_debug.c
File metadata and controls
160 lines (132 loc) · 4.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
Compiled only into libvterm-debug.so (cmake target `vterm-debug`).
A __attribute__((constructor)) installs signal handlers for the usual
fatal signals; on delivery, a glibc backtrace is dumped to
./vterm-crash-<pid>.log
in the process's current working directory. After dumping, the
handler restores the default disposition and re-raises so the process
still terminates (and a core file lands if rlimits allow).
*/
#ifdef VTERM_DEBUG_BACKTRACE
#define _GNU_SOURCE
#include <execinfo.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define BT_MAX 128
static void *bt_buf[BT_MAX];
static volatile sig_atomic_t crashing = 0;
/* async-signal-safe unsigned-to-decimal; returns digit count written */
static int
_vd_utoa(unsigned long v, char *buf)
{
char tmp[32];
int i = 0;
int j = 0;
if(v == 0) { buf[0] = '0'; return 1; }
while(v > 0) { tmp[i++] = '0' + (v % 10); v /= 10; }
while(i > 0) buf[j++] = tmp[--i];
return j;
}
/* async-signal-safe unsigned-to-hex (no 0x prefix) */
static int
_vd_xtoa(unsigned long v, char *buf)
{
static const char hex[] = "0123456789abcdef";
char tmp[32];
int i = 0;
int j = 0;
if(v == 0) { buf[0] = '0'; return 1; }
while(v > 0) { tmp[i++] = hex[v & 0xF]; v >>= 4; }
while(i > 0) buf[j++] = tmp[--i];
return j;
}
static const char *
_vd_signame(int sig)
{
switch(sig)
{
case SIGSEGV: return "SIGSEGV";
case SIGABRT: return "SIGABRT";
case SIGBUS: return "SIGBUS";
case SIGFPE: return "SIGFPE";
case SIGILL: return "SIGILL";
}
return "SIG?";
}
static void
_vd_handler(int sig, siginfo_t *si, void *ctx)
{
char path[64] = "vterm-crash-";
char num[32];
int fd;
int n;
int off;
const char *name;
(void)ctx;
/* fault inside the handler -> bail rather than recurse */
if(crashing) _exit(128 + sig);
crashing = 1;
off = (int)strlen(path);
off += _vd_utoa((unsigned long)getpid(), &path[off]);
memcpy(&path[off], ".log", 5); /* includes NUL */
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if(fd < 0) fd = STDERR_FILENO;
write(fd, "libvterm crash: ", 16);
name = _vd_signame(sig);
write(fd, name, strlen(name));
write(fd, " (sig ", 6);
n = _vd_utoa((unsigned long)sig, num);
write(fd, num, n);
write(fd, ") pid ", 6);
n = _vd_utoa((unsigned long)getpid(), num);
write(fd, num, n);
if(si != NULL && (sig == SIGSEGV || sig == SIGBUS))
{
write(fd, " fault_addr 0x", 14);
n = _vd_xtoa((unsigned long)si->si_addr, num);
write(fd, num, n);
}
write(fd, " time ", 6);
n = _vd_utoa((unsigned long)time(NULL), num);
write(fd, num, n);
write(fd, "\n", 1);
write(fd,
"(resolve addresses via: addr2line -e /usr/local/lib/libvterm.so <addr>)\n",
73);
write(fd, "--- backtrace ---\n", 18);
n = backtrace(bt_buf, BT_MAX);
backtrace_symbols_fd(bt_buf, n, fd);
write(fd, "--- end backtrace ---\n", 22);
if(fd != STDERR_FILENO) close(fd);
/* restore default disposition and re-raise so the process actually
dies (and may core), preserving normal post-mortem signal behavior */
signal(sig, SIG_DFL);
raise(sig);
}
__attribute__((constructor))
static void
_vd_init(void)
{
struct sigaction sa;
void *dummy[4];
/*
prime libgcc_s so the first call to backtrace() (which dlopens it)
doesn't happen inside the signal handler -- dlopen is not
async-signal-safe.
*/
backtrace(dummy, 4);
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = _vd_handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
sigaction(SIGABRT, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
}
#endif /* VTERM_DEBUG_BACKTRACE */