forked from ianlancetaylor/cgosymbolizer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
symbolizer.c
202 lines (173 loc) · 5.79 KB
/
symbolizer.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unistd.h>
#include "backtrace.h"
#include "config.h"
#include "internal.h"
static void createStateErrorCallback(void* data, const char* msg, int errnum) {}
static struct backtrace_state* cgoBacktraceState;
// Initialize the backtrace state.
void cgoSymbolizerInit(char* filename) {
cgoBacktraceState =
backtrace_create_state(filename, 1, createStateErrorCallback, NULL);
}
struct cgoSymbolizerArg {
uintptr_t pc;
const char* file;
uintptr_t lineno;
const char* func;
uintptr_t entry;
uintptr_t more;
uintptr_t data;
};
struct cgoSymbolizerMore {
struct cgoSymbolizerMore* more;
const char* file;
uintptr_t lineno;
const char* func;
};
// Called via backtrace_pcinfo.
static int callback(void* data, uintptr_t pc, const char* filename, int lineno,
const char* function) {
struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(data);
struct cgoSymbolizerMore* more;
struct cgoSymbolizerMore** pp;
if (arg->file == NULL) {
arg->file = filename;
arg->lineno = lineno;
arg->func = function;
return 0;
}
more = backtrace_alloc(cgoBacktraceState, sizeof(*more), NULL, NULL);
if (more == NULL) {
return 1;
}
more->more = NULL;
more->file = filename;
more->lineno = lineno;
more->func = function;
for (pp = (struct cgoSymbolizerMore**)(&arg->data); *pp != NULL;
pp = &(*pp)->more) {
}
*pp = more;
arg->more = 1;
return 0;
}
// Called via backtrace_pcinfo.
// Just ignore errors and let the caller indicate missing information.
static void errorCallback(void* data, const char* msg, int errnum) {}
// Called via backtrace_syminfo.
// Just set the entry field.
static void syminfoCallback(void* data, uintptr_t pc, const char* symname,
uintptr_t symval, uintptr_t symsize) {
struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(data);
arg->entry = symval;
}
// For the details of how this is called see runtime.SetCgoTraceback.
void cgoSymbolizer(void* parg) {
struct cgoSymbolizerArg* arg = (struct cgoSymbolizerArg*)(parg);
if (arg->data != 0) {
struct cgoSymbolizerMore* more = (struct cgoSymbolizerMore*)(arg->data);
arg->file = more->file;
arg->lineno = more->lineno;
arg->func = more->func;
arg->more = more->more != NULL;
arg->data = (uintptr_t)(more->more);
// If returning the last file/line, we can set the
// entry point field.
if (!arg->more) {
backtrace_syminfo(cgoBacktraceState, arg->pc, syminfoCallback,
errorCallback, (void*)arg);
}
return;
}
arg->file = NULL;
arg->lineno = 0;
arg->func = NULL;
arg->more = 0;
if (cgoBacktraceState == NULL || arg->pc == 0) {
return;
}
backtrace_pcinfo(cgoBacktraceState, arg->pc, callback, errorCallback,
(void*)(arg));
// If returning only one file/line, we can set the entry point field.
if (!arg->more) {
backtrace_syminfo(cgoBacktraceState, arg->pc, syminfoCallback,
errorCallback, (void*)arg);
}
}
void backtraceErrorCallback(void* data, const char* msg, int errnum) {
fprintf(stderr, "Error %d occurred when getting the stacktrace: %s", errnum,
msg);
}
int backtraceCallback(void* data, uintptr_t pc, const char* filename,
int lineno, const char* function) {
fprintf(stderr, "%s\n\t%s:%d pc=0x%lx\n", function, filename, lineno, pc);
return 0;
}
void printBacktrace(int signo, siginfo_t* info, void* context) {
if (!cgoBacktraceState) {
perror("cgoBacktraceState is not initialized");
return;
}
ucontext_t* uc = (ucontext_t*)(context);
fprintf(stderr, "\nSIGNAL CATCH BY NON-GO SIGNAL HANDLER\n");
fprintf(stderr,
"SIGNO: %d; SIGNAME: %s; SI_CODE: %d; SI_ADDR: %p\nBACKTRACE:\n",
signo, strsignal(signo), info->si_code, info->si_addr);
// skip the wrapHandler, sigaction.
backtrace_full(cgoBacktraceState, 3, backtraceCallback,
backtraceErrorCallback, NULL);
fprintf(stderr, "\n\n");
fflush(stderr);
}
#define MAX_SIGNAL 32
static void (*oldSignalHandler[MAX_SIGNAL])(int signo, siginfo_t* info,
void* context);
static void wrapHandler(int signo, siginfo_t* info, void* context) {
printBacktrace(signo, info, context);
oldSignalHandler[signo](signo, info, context);
}
void cgoInstallNonGoHandlerForSignum(int signum) {
if (signum >= MAX_SIGNAL) {
perror("signum is too large");
}
// get old signal handler, it should be a default go signal handler.
struct sigaction old_action;
memset(&old_action, 0, sizeof(old_action));
sigemptyset(&old_action.sa_mask);
if (sigaction(signum, NULL, &old_action) == -1) {
perror("get old signal handler failed");
exit(EXIT_FAILURE);
}
// check if the old signal handler is setted
if (old_action.sa_handler == SIG_DFL) {
fprintf(stderr,
"Go runtime signal handler is not setted, please set it first\n");
exit(EXIT_FAILURE);
}
// check if SA_ONSTACK is setted.
if (old_action.sa_flags & SA_ONSTACK == 0) {
fprintf(stderr, "Go runtime signal handler is setted with SA_ONSTACK\n");
exit(EXIT_FAILURE);
}
oldSignalHandler[signum] = old_action.sa_sigaction;
old_action.sa_sigaction = &wrapHandler;
if (sigaction(signum, &old_action, NULL) == -1) {
perror("set up new signal handler failed");
exit(EXIT_FAILURE);
}
}
void cgoInstallNonGoHandler() {
cgoInstallNonGoHandlerForSignum(SIGSEGV);
cgoInstallNonGoHandlerForSignum(SIGBUS);
cgoInstallNonGoHandlerForSignum(SIGFPE);
}