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
sigsegv_set_print(int (* fnc)(const char * format,...),int _needs_cr)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 */
dump_pid_son(pid_t pid,const char * binary,int full_bt,int (* myprint)(const char * format,...))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 ssize_t w = 0;
92 if( full_bt )
93 w = write(fd, gdb_cmd_full, strlen(gdb_cmd_full));
94 else
95 w = write(fd, gdb_cmd, strlen(gdb_cmd));
96 if(w == -1) {
97 (*myprint)("Write failed at crash_sigsegv.cpp:92(94)");
98 }
99 close(fd);
100
101 sprintf(cmd, "gdb -nw -n -batch -x \"%s\" %s %d", tmp, binary,
102 pid);
103 (*myprint)("trying to dump pid: %d (%s)...%s", pid, binary,
104 needs_cr ? "\n" : "");
105
106 fflush(NULL);
107 fp = popen(cmd, "r");
108 if( fp == NULL )
109 {
110 (*myprint)("err. couldn't exec `%s'%s", cmd,
111 needs_cr ? "\n" : "");
112 ret = -1;
113 }
114 else
115 {
116 char buff[4096];
117 size_t len;
118
119 while(fgets(buff, sizeof(buff), fp))
120 {
121 len = strlen(buff);
122 if( buff[len-1] == '\n')
123 buff[len-1]=0;
124
125 (*myprint)("%s%s", buff,needs_cr ? "\n" : "");
126 }
127 pclose(fp);
128 }
129 if( remove(tmp) == -1 )
130 (*myprint)("removing `%s` (@;@)%s", tmp,
131 needs_cr ? "\n" : "");
132 }
133
134 return ret;
135 }
136
dump_pid(pid_t pid,const char * binary,int full_bt)137 static int dump_pid(pid_t pid, const char *binary, int full_bt )
138 {
139 pid_t mpid;
140 int (* myprint)(const char *format, ...);
141
142 myprint = print ? (int(*)(const char *format, ...))print : (int(*)(const char *format, ...))printf;
143
144 /*
145 * clone the process, so we don't make the bt bigger.
146 */
147 mpid = fork();
148 if( mpid == 0 )
149 {
150 dump_pid_son(pid, binary, full_bt, myprint);
151 exit(0);
152 }
153 else if( mpid == -1 )
154 (*myprint)("lunching son: `%s' %s", strerror(errno),
155 needs_cr ? "\n" : "");
156 else
157 {
158 /* father */
159 int status;
160
161 alarm(0);
162 waitpid(0, &status, 0);
163 if( WIFEXITED(status) && WEXITSTATUS(status)==0 ) {
164 //
165 }
166 }
167
168 return 0;
169 }
170
171 /**
172 * get `pid`'s real path
173 *
174 * \param buff buffer for the output
175 * \param nbuff size of the buffer
176 * \param pid pid processes id to use
177 *
178 * \note this function works only in linux
179 *
180 * \return the buffer
181 */
get_path_from_pid(char * buff,size_t nbuff,pid_t pid)182 static char *get_path_from_pid(char *buff, size_t nbuff, pid_t pid)
183 {
184 char proc[256];
185 char *ret = NULL;
186 int n;
187
188 sprintf(proc, "/proc/%d/exe", pid);
189 if( (n=readlink(proc, buff, nbuff)) == -1 )
190 ret = NULL;
191 else
192 {
193 buff[n]=0;
194 ret = buff;
195 }
196
197 return ret;
198 }
199
sigsegv_libc_dump(int (* myprint)(const char * format,...))200 static void sigsegv_libc_dump( int (* myprint)(const char *format, ...) )
201 {
202 void *array[48] = {0};
203 unsigned short i;
204 int n;
205 char **res;
206
207 #ifdef HAVE_BACKTRACE
208 (*myprint)("Backtrace:%c", needs_cr ? "\n" : "");
209 n = backtrace(array, sizeof(array)/(sizeof(*array)));
210 res = backtrace_symbols(array, n);
211 for (i = 0; i < n; i++)
212 (*myprint)("%s%s", res[i], needs_cr ? "\n" : "");
213
214 (*myprint)("Attempting to generate core file%s",
215 needs_cr ? "" : "");
216 #endif
217 }
218
sigsegv_handler_generic(int signal,int full_bt)219 static void sigsegv_handler_generic(int signal, int full_bt)
220 {
221 (void)signal;
222 char binary[2048];
223 int pid = getpid();
224 int (* myprint)(const char *format, ...);
225
226 myprint = print ? print : printf;
227 if( get_path_from_pid(binary, sizeof(binary), pid) == NULL) {
228 (*myprint)("pid %d does not seems to exist", pid);
229 }
230 else {
231 (*myprint)("Segmentation Violation Detected.%s",
232 needs_cr ? "\n" : "");
233 dump_pid(pid, binary, full_bt);
234 sigsegv_libc_dump(myprint);
235 }
236
237 #ifdef HAVE_PTHREAD_H
238 pthread_kill_other_threads_np();
239 #endif
240 fflush(NULL);
241 abort();
242 }
243
sigsegv_handler_fnc(int signal)244 void sigsegv_handler_fnc(int signal)
245 {
246 sigsegv_handler_generic(signal, 0);
247 }
248
sigsegv_handler_bt_full_fnc(int signal)249 void sigsegv_handler_bt_full_fnc(int signal)
250 {
251 sigsegv_handler_generic(signal, 1);
252 }
253
254 }; // namespace Crash
255