1 | /* sigsegv.c -- sigsegv handlers
|
---|
2 | *
|
---|
3 | * Copyright (c) 2003 Juan F. Codagnone <juam@users.sourceforge.net>
|
---|
4 | *
|
---|
5 | * Permission is hereby granted, free of charge, to any person obtaining a
|
---|
6 | * copy of this software and associated documentation files (the "Software"),
|
---|
7 | * to deal in the Software without restriction, including without limitation
|
---|
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
---|
9 | * and/or sell copies of the Software, and to permit persons to whom the
|
---|
10 | * Software is furnished to do so, subject to the following conditions:
|
---|
11 | *
|
---|
12 | * The above copyright notice and this permission notice shall be included
|
---|
13 | * in all copies or substantial portions of the Software.
|
---|
14 | *
|
---|
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
---|
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
---|
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
---|
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
---|
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
---|
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
---|
21 | * SOFTWARE.
|
---|
22 | */
|
---|
23 |
|
---|
24 | #ifdef HAVE_CONFIG_H_
|
---|
25 | #include <config.h>
|
---|
26 | #endif
|
---|
27 |
|
---|
28 | #include <stdio.h>
|
---|
29 | #include <stdlib.h>
|
---|
30 | #include <string.h>
|
---|
31 | #include <assert.h>
|
---|
32 | #include <errno.h>
|
---|
33 |
|
---|
34 | #include <unistd.h>
|
---|
35 |
|
---|
36 | #ifdef HAVE_PTHREADS_H
|
---|
37 | # include <pthread.h>
|
---|
38 | #endif
|
---|
39 |
|
---|
40 | /*
|
---|
41 | * http://www.gnu.org/manual/glibc-2.2.3/html_chapter/libc_33.html
|
---|
42 | */
|
---|
43 | #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 1
|
---|
44 | # define HAVE_BACKTRACE
|
---|
45 | # include <execinfo.h>
|
---|
46 | #endif
|
---|
47 | #include <sys/wait.h>
|
---|
48 |
|
---|
49 | #include "crash_sigsegv.h"
|
---|
50 |
|
---|
51 | namespace Crash {
|
---|
52 |
|
---|
53 | static int (* print)(const char *format, ...) = NULL;
|
---|
54 | static int needs_cr = 1;
|
---|
55 |
|
---|
56 | void *sigsegv_set_print( int (* fnc)(const char *format, ...), int _needs_cr)
|
---|
57 | {
|
---|
58 | void *ret;
|
---|
59 |
|
---|
60 | ret = &print;
|
---|
61 | print = fnc;
|
---|
62 | needs_cr = _needs_cr;
|
---|
63 |
|
---|
64 | return ret;
|
---|
65 | }
|
---|
66 |
|
---|
67 | /**
|
---|
68 | * launchs gdb, and feeds myprint with the backtrace
|
---|
69 | */
|
---|
70 | static int dump_pid_son(pid_t pid, const char *binary, int full_bt,
|
---|
71 | int (* myprint)(const char *format, ...))
|
---|
72 | {
|
---|
73 | char tmp[]="/tmp/mrbug-crash-XXXXXX";
|
---|
74 | int ret = 0;
|
---|
75 | int fd;
|
---|
76 |
|
---|
77 | fd = mkstemp(tmp);
|
---|
78 | if( fd == -1 )
|
---|
79 | {
|
---|
80 | (*myprint)("opening gdb command (tempory) file `%s'%s", tmp,
|
---|
81 | needs_cr ? "\n" : "");
|
---|
82 | ret = -1;
|
---|
83 | }
|
---|
84 | else
|
---|
85 | {
|
---|
86 | char gdb_cmd[]="bt\nquit";
|
---|
87 | char gdb_cmd_full[]="bt full\nquit";
|
---|
88 | char cmd[128];
|
---|
89 | FILE *fp;
|
---|
90 |
|
---|
91 | if( full_bt )
|
---|
92 | write(fd, gdb_cmd_full, strlen(gdb_cmd_full));
|
---|
93 | else
|
---|
94 | write(fd, gdb_cmd, strlen(gdb_cmd));
|
---|
95 | close(fd);
|
---|
96 |
|
---|
97 | sprintf(cmd, "gdb -nw -n -batch -x \"%s\" %s %d", tmp, binary,
|
---|
98 | pid);
|
---|
99 | (*myprint)("trying to dump pid: %d (%s)...%s", pid, binary,
|
---|
100 | needs_cr ? "\n" : "");
|
---|
101 |
|
---|
102 | fflush(NULL);
|
---|
103 | fp = popen(cmd, "r");
|
---|
104 | if( fp == NULL )
|
---|
105 | {
|
---|
106 | (*myprint)("err. couldn't exec `%s'%s", cmd,
|
---|
107 | needs_cr ? "\n" : "");
|
---|
108 | ret = -1;
|
---|
109 | }
|
---|
110 | else
|
---|
111 | {
|
---|
112 | char buff[4096];
|
---|
113 | size_t len;
|
---|
114 |
|
---|
115 | while(fgets(buff, sizeof(buff), fp))
|
---|
116 | {
|
---|
117 | len = strlen(buff);
|
---|
118 | if( buff[len-1] == '\n')
|
---|
119 | buff[len-1]=0;
|
---|
120 |
|
---|
121 | (*myprint)("%s%s", buff,needs_cr ? "\n" : "");
|
---|
122 | }
|
---|
123 | fclose(fp);
|
---|
124 | }
|
---|
125 | if( remove(tmp) == -1 )
|
---|
126 | (*myprint)("removing `%s` (@;@)%s", tmp,
|
---|
127 | needs_cr ? "\n" : "");
|
---|
128 | }
|
---|
129 |
|
---|
130 | return ret;
|
---|
131 | }
|
---|
132 |
|
---|
133 | static int dump_pid(pid_t pid, const char *binary, int full_bt )
|
---|
134 | {
|
---|
135 | pid_t mpid;
|
---|
136 | int (* myprint)(const char *format, ...);
|
---|
137 |
|
---|
138 | myprint = print ? (int(*)(const char *format, ...))print : (int(*)(const char *format, ...))printf;
|
---|
139 |
|
---|
140 | /*
|
---|
141 | * clone the process, so we don't make the bt bigger.
|
---|
142 | */
|
---|
143 | mpid = fork();
|
---|
144 | if( mpid == 0 )
|
---|
145 | {
|
---|
146 | dump_pid_son(pid, binary, full_bt, myprint);
|
---|
147 | exit(0);
|
---|
148 | }
|
---|
149 | else if( mpid == -1 )
|
---|
150 | (*myprint)("lunching son: `%s' %s", strerror(errno),
|
---|
151 | needs_cr ? "\n" : "");
|
---|
152 | else
|
---|
153 | {
|
---|
154 | /* father */
|
---|
155 | int status;
|
---|
156 |
|
---|
157 | alarm(0);
|
---|
158 | waitpid(0, &status, 0);
|
---|
159 | if( WIFEXITED(status) && WEXITSTATUS(status)==0 )
|
---|
160 | ;
|
---|
161 | }
|
---|
162 |
|
---|
163 | return 0;
|
---|
164 | }
|
---|
165 |
|
---|
166 | /**
|
---|
167 | * get `pid`'s real path
|
---|
168 | *
|
---|
169 | * \param buff buffer for the output
|
---|
170 | * \param nbuff size of the buffer
|
---|
171 | * \param pid pid processes id to use
|
---|
172 | *
|
---|
173 | * \note this function works only in linux
|
---|
174 | *
|
---|
175 | * \return the buffer
|
---|
176 | */
|
---|
177 | static char *get_path_from_pid(char *buff, size_t nbuff, pid_t pid)
|
---|
178 | {
|
---|
179 | char proc[256];
|
---|
180 | char *ret = NULL;
|
---|
181 | int n;
|
---|
182 |
|
---|
183 | sprintf(proc, "/proc/%d/exe", pid);
|
---|
184 | if( (n=readlink(proc, buff, nbuff)) == -1 )
|
---|
185 | ret = NULL;
|
---|
186 | else
|
---|
187 | {
|
---|
188 | buff[n]=0;
|
---|
189 | ret = buff;
|
---|
190 | }
|
---|
191 |
|
---|
192 | return ret;
|
---|
193 | }
|
---|
194 |
|
---|
195 | static void sigsegv_libc_dump( int (* myprint)(const char *format, ...) )
|
---|
196 | {
|
---|
197 | void *array[48] = {0};
|
---|
198 | unsigned short i;
|
---|
199 | int n;
|
---|
200 | char **res;
|
---|
201 |
|
---|
202 | #ifdef HAVE_BACKTRACE
|
---|
203 | (*myprint)("Backtrace:%c", needs_cr ? "\n" : "");
|
---|
204 | n = backtrace(array, sizeof(array)/(sizeof(*array)));
|
---|
205 | res = backtrace_symbols(array, n);
|
---|
206 | for (i = 0; i < n; i++)
|
---|
207 | (*myprint)("%s%s", res[i], needs_cr ? "\n" : "");
|
---|
208 |
|
---|
209 | (*myprint)("Attempting to generate core file%s",
|
---|
210 | needs_cr ? "" : "");
|
---|
211 | #endif
|
---|
212 | }
|
---|
213 |
|
---|
214 | static void sigsegv_handler_generic(int signal, int full_bt)
|
---|
215 | {
|
---|
216 | char binary[2048];
|
---|
217 | int pid = getpid();
|
---|
218 | int (* myprint)(const char *format, ...);
|
---|
219 |
|
---|
220 | myprint = print ? print : printf;
|
---|
221 | if( get_path_from_pid(binary, sizeof(binary), pid) == NULL)
|
---|
222 | (*myprint)("pid %d does not seems to exist", pid);
|
---|
223 | else
|
---|
224 | {
|
---|
225 | (*myprint)("Segmentation Violation Detected.%s",
|
---|
226 | needs_cr ? "\n" : "");
|
---|
227 | dump_pid(pid, binary, full_bt);
|
---|
228 | sigsegv_libc_dump(myprint);
|
---|
229 | }
|
---|
230 |
|
---|
231 | #ifdef HAVE_PTHREAD_H
|
---|
232 | pthread_kill_other_threads_np();
|
---|
233 | #endif
|
---|
234 | fflush(NULL);
|
---|
235 | abort();
|
---|
236 | }
|
---|
237 |
|
---|
238 | void sigsegv_handler_fnc(int signal)
|
---|
239 | {
|
---|
240 | sigsegv_handler_generic(signal, 0);
|
---|
241 | }
|
---|
242 |
|
---|
243 | void sigsegv_handler_bt_full_fnc(int signal)
|
---|
244 | {
|
---|
245 | sigsegv_handler_generic(signal, 1);
|
---|
246 | }
|
---|
247 |
|
---|
248 | }; // namespace Crash
|
---|