-
Notifications
You must be signed in to change notification settings - Fork 65
/
stacktrace.c
149 lines (121 loc) · 4.23 KB
/
stacktrace.c
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
/*
stacktrace.c - Stacktrace (Using dynamically loaded libbacktrace)
Copyright (C) 2024 LekKit <github.com/LekKit>
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
Alternatively, the contents of this file may be used under the terms
of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or any later version.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "stacktrace.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#if (defined(__unix__) || defined(__APPLE__) || defined(__MINGW32__)) \
&& !defined(NO_STACKTRACE) && !defined(UNDER_CE) && !defined(__SANITIZE_ADDRESS__)
#define SIGNAL_IMPL
#include <stdlib.h>
#include <signal.h>
#ifndef SIGBUS
#define SIGBUS 10
#endif
#endif
// RVVM internal headers come after system headers because of safe_free()
#include "compiler.h"
#include "utils.h"
#include "dlib.h"
/*
* libbacktrace boilerplace
*/
struct backtrace_state;
typedef void (*backtrace_error_callback)(void* data, const char* msg, int errnum);
typedef int (*backtrace_full_callback)(void* data, uintptr_t pc, const char* filename, int lineno, const char* function);
static struct backtrace_state* (*backtrace_create_state)(const char* filename, int threaded,
backtrace_error_callback error_callback, void *data) = NULL;
static int (*backtrace_full)(struct backtrace_state *state, int skip, backtrace_full_callback callback,
backtrace_error_callback error_callback, void *data) = NULL;
static void (*backtrace_print)(struct backtrace_state *state, int skip, FILE* file) = NULL;
static struct backtrace_state* bt_state = NULL;
static void backtrace_dummy_error(void* data, const char* msg, int errnum)
{
UNUSED(data); UNUSED(msg); UNUSED(errnum);
}
static int backtrace_dummy_callback(void* data, uintptr_t pc, const char* filename, int lineno, const char* function)
{
UNUSED(data); UNUSED(pc); UNUSED(filename); UNUSED(lineno); UNUSED(function);
return 0;
}
/*
* Fatal signal stacktraces
*/
#ifdef SIGNAL_IMPL
static void signal_handler(int sig)
{
switch (sig) {
case SIGSEGV:
rvvm_warn("Fatal signal: Segmentation fault!");
break;
case SIGBUS:
rvvm_warn("Fatal signal: Bus fault - Address is non-canonic or misaligned!");
break;
case SIGILL:
rvvm_warn("Fatal signal: Illegal instruction!");
break;
case SIGFPE:
rvvm_warn("Fatal signal: Division by zero!");
break;
default:
rvvm_warn("Fatal signal %d", sig);
break;
}
rvvm_warn("Stacktrace:");
stacktrace_print();
_Exit(-sig);
}
static void set_signal_handler(int sig)
{
void* prev = signal(sig, signal_handler);
if (prev != NULL && prev != (void*)SIG_IGN) {
// Signal already used
signal(sig, prev);
}
}
#endif
static void backtrace_init_once(void)
{
if (rvvm_has_arg("no_stacktrace")) {
return;
}
dlib_ctx_t* libbt = dlib_open("backtrace", DLIB_NAME_PROBE);
backtrace_create_state = dlib_resolve(libbt, "backtrace_create_state");
backtrace_full = dlib_resolve(libbt, "backtrace_full");
backtrace_print = dlib_resolve(libbt, "backtrace_print");
dlib_close(libbt);
if (backtrace_create_state) {
bt_state = backtrace_create_state(NULL, true, backtrace_dummy_error, NULL);
}
if (backtrace_full && bt_state) {
// Preload backtracing data, isolation is enabled later on
backtrace_full(bt_state, 0, backtrace_dummy_callback, backtrace_dummy_error, NULL);
#ifdef SIGNAL_IMPL
set_signal_handler(SIGSEGV);
set_signal_handler(SIGBUS);
set_signal_handler(SIGILL);
set_signal_handler(SIGFPE);
#endif
}
}
void stacktrace_init(void)
{
DO_ONCE(backtrace_init_once());
}
void stacktrace_print(void)
{
stacktrace_init();
if (backtrace_print && bt_state) {
backtrace_print(bt_state, 0, stderr);
}
}