4 * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcsignals.h"
23 #include "bcwindowbase.h"
24 #include "bccmodels.h"
25 #include "bckeyboard.h"
26 #include "bcresources.h"
39 #include <sys/ioctl.h>
40 #include <sys/prctl.h>
41 #include <sys/types.h>
43 BC_Signals* BC_Signals::global_signals = 0;
44 static int signal_done = 0;
46 static struct sigaction old_segv = {0, }, old_intr = {0, };
47 static void handle_dump(int n, siginfo_t * info, void *sc);
49 const char *BC_Signals::trap_path = 0;
50 void *BC_Signals::trap_data = 0;
51 void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
52 bool BC_Signals::trap_sigsegv = false;
53 bool BC_Signals::trap_sigintr = false;
55 static void uncatch_sig(int sig, struct sigaction &old)
58 sigaction(sig, &old, &act);
62 static void catch_sig(int sig, struct sigaction &old)
65 memset(&act, 0, sizeof(act));
66 act.sa_sigaction = handle_dump;
67 act.sa_flags = SA_SIGINFO;
68 sigaction(sig, &act, (!old.sa_handler ? &old : 0));
71 static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
72 static void catch_intr() { catch_sig(SIGINT, old_intr); }
73 static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
74 static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
76 void BC_Signals::set_trap_path(const char *path)
81 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
87 void BC_Signals::set_catch_segv(bool v) {
88 if( v == trap_sigsegv ) return;
94 void BC_Signals::set_catch_intr(bool v) {
95 if( v == trap_sigintr ) return;
101 static void bc_copy_textfile(int lines, FILE *ofp, const char *fmt,...)
103 va_list ap; va_start(ap, fmt);
104 char bfr[BCTEXTLEN]; vsnprintf(bfr, sizeof(bfr), fmt, ap);
106 FILE *ifp = fopen(bfr,"r");
108 while( --lines >= 0 && fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
112 // Can't use Mutex because it would be recursive
113 static pthread_mutex_t *handler_lock = 0;
116 static const char* signal_titles[] =
136 void BC_Signals::dump_stack(FILE *fp)
139 int total = backtrace (buffer, 256);
140 char **result = backtrace_symbols (buffer, total);
141 fprintf(fp, "BC_Signals::dump_stack\n");
142 for(int i = 0; i < total; i++)
144 fprintf(fp, "%s\n", result[i]);
149 void BC_Signals::kill_subs()
151 // List /proc directory
152 struct dirent64 *new_filename;
154 char path[BCTEXTLEN], string[BCTEXTLEN];
155 DIR *dirstream = opendir("/proc");
156 if( !dirstream ) return;
157 pid_t ppid = getpid();
159 while( (new_filename = readdir64(dirstream)) != 0 ) {
160 char *ptr = new_filename->d_name;
161 while( *ptr && *ptr != '.' && !isalpha(*ptr) ) ++ptr;
162 // All digits are numbers
165 // Must be a directory
166 sprintf(path, "/proc/%s", new_filename->d_name);
167 if( stat(path, &ostat) ) continue;
168 if( !S_ISDIR(ostat.st_mode) ) continue;
169 strcat(path, "/stat");
170 FILE *fd = fopen(path, "r");
172 while( !feof(fd) && fgetc(fd)!=')' );
173 //printf("kill_subs %d %d\n", __LINE__, c);
174 for( int sp=2; !feof(fd) && sp>0; )
175 if( fgetc(fd) == ' ' ) --sp;
176 // Read in parent process
177 for( ptr=string; !feof(fd) && (*ptr=fgetc(fd))!=' '; ++ptr );
178 if( (*ptr=fgetc(fd)) == ' ' ) break;
181 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
182 // __LINE__, atoi(new_filename->d_name), getpid(), atoi(string));
183 int parent_process = atoi(string);
184 // Kill if we're the parent
185 if( ppid == parent_process ) {
186 int child_process = atoi(new_filename->d_name);
187 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
188 kill(child_process, SIGKILL);
194 static void signal_entry(int signum)
196 signal(signum, SIG_DFL);
198 pthread_mutex_lock(handler_lock);
199 int done = signal_done;
201 pthread_mutex_unlock(handler_lock);
204 printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
205 signal_titles[signum], getpid(), execution_table.size);
207 BC_Signals::kill_subs();
208 BC_Trace::dump_traces();
209 BC_Trace::dump_locks();
210 BC_Trace::dump_buffers();
211 BC_Trace::dump_shm_stats();
212 BC_Trace::delete_temps();
214 // Call user defined signal handler
215 BC_Signals::global_signals->signal_handler(signum);
220 static void signal_entry_recoverable(int signum)
222 printf("signal_entry_recoverable: got %s my pid=%d\n",
223 signal_titles[signum],
227 // used to terminate child processes when program terminates
228 static void handle_exit(int signum)
230 //printf("child %d exit\n", getpid());
234 void BC_Signals::set_sighup_exit(int enable)
237 // causes SIGHUP to be generated when parent dies
238 signal(SIGHUP, handle_exit);
239 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
240 // prevents ^C from signalling child when attached to gdb
242 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
245 signal(SIGHUP, signal_entry);
246 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
250 BC_Signals::BC_Signals()
253 BC_Signals::~BC_Signals()
255 BC_CModels::bcxfer_stop_slicers();
259 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
262 XGetErrorText(event->display, event->error_code, string, 1024);
263 fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
264 event->error_code, event->request_code, event->minor_code,
265 (int64_t)event->resourceid, string);
270 void BC_Signals::initialize(const char *trap_path)
272 BC_Signals::global_signals = this;
273 BC_Trace::global_trace = this;
274 set_trap_path(trap_path);
275 handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
276 pthread_mutex_init(handler_lock, 0);
277 old_err_handler = XSetErrorHandler(x_error_handler);
281 void BC_Signals::terminate()
283 BC_Signals::global_signals = 0;
284 BC_Trace::global_trace = 0;
285 uncatch_segv(); uncatch_intr();
286 signal(SIGHUP, SIG_DFL);
287 signal(SIGINT, SIG_DFL);
288 signal(SIGQUIT, SIG_DFL);
289 signal(SIGTERM, SIG_DFL);
290 signal(SIGFPE, SIG_DFL);
291 signal(SIGPIPE, SIG_DFL);
292 signal(SIGUSR2, SIG_DFL);
293 XSetErrorHandler(old_err_handler);
297 void BC_Signals::signal_dump(int signum)
299 BC_KeyboardHandler::kill_grabs();
301 signal(SIGUSR2, signal_dump);
309 void BC_Signals::initialize2()
311 signal(SIGHUP, signal_entry);
312 signal(SIGINT, signal_entry);
313 signal(SIGQUIT, signal_entry);
314 // SIGKILL cannot be stopped
315 // signal(SIGKILL, signal_entry);
317 signal(SIGTERM, signal_entry);
318 signal(SIGFPE, signal_entry);
319 signal(SIGPIPE, signal_entry_recoverable);
320 signal(SIGUSR2, signal_dump);
324 void BC_Signals::signal_handler(int signum)
326 printf("BC_Signals::signal_handler\n");
330 const char* BC_Signals::sig_to_str(int number)
332 return signal_titles[number];
336 #include <ucontext.h>
337 #include <sys/wait.h>
351 static void handle_dump(int n, siginfo_t * info, void *sc)
353 uncatch_segv(); uncatch_intr();
354 signal(SIGSEGV, SIG_DFL);
355 signal(SIGINT, SIG_DFL);
356 // gotta be root, or the dump is worthless
358 // it is not necessary to be root if ptrace is allowed via:
359 // echo 0 > /proc/sys/kernel/yama/ptrace_scope (usually set to 1)
360 // if( uid != 0 ) return;
361 ucontext_t *uc = (ucontext_t *)sc;
362 int pid = getpid(), tid = gettid();
363 struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
364 uint8_t *ip = (uint8_t *)c->IP;
365 fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
366 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
367 (void*)ip, pid, tid);
370 if( BC_Signals::trap_path ) {
371 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
375 fprintf(stderr,"writing debug data to %s\n", fn);
376 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
377 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
378 (void*)c->IP, pid, tid);
381 strcpy(fn, "stdout");
385 fprintf(fp,"created on %s", ctime(&t));
386 struct passwd *pw = getpwuid(uid);
388 fprintf(fp," by %d:%d %s(%s)\n",
389 pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
391 fprintf(fp,"\nCPUS: %d\n", BC_Resources::get_machine_cpus());
392 fprintf(fp,"\nCPUINFO:\n"); bc_copy_textfile(32, fp,"/proc/cpuinfo");
393 fprintf(fp,"\nTHREADS:\n"); BC_Trace::dump_threads(fp);
394 fprintf(fp,"\nTRACES:\n"); BC_Trace::dump_traces(fp);
395 fprintf(fp,"\nLOCKS:\n"); BC_Trace::dump_locks(fp);
396 fprintf(fp,"\nBUFFERS:\n"); BC_Trace::dump_buffers(fp);
397 fprintf(fp,"\nSHMMEM:\n"); BC_Trace::dump_shm_stats(fp);
398 if( BC_Signals::trap_hook ) {
399 fprintf(fp,"\nMAIN HOOK:\n");
400 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
402 fprintf(fp,"\nVERSION:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/version");
403 fprintf(fp,"\nMEMINFO:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
404 fprintf(fp,"\nMAPS:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
406 if( tid > 0 && tid != pid )
407 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
409 sprintf(proc_mem,"/proc/%d/mem",pid);
410 int pfd = open(proc_mem,O_RDONLY);
412 fprintf(fp,"\nCODE:\n");
413 for( int i=-32; i<32; ) {
414 uint8_t v; void *vp = (void *)(ip + i);
415 if( !(i & 7) ) fprintf(fp,"%p: ", vp);
416 if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
417 fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
418 if( !(++i & 7) ) fprintf(fp,"\n");
424 fprintf(fp,"err opening: %s, %m\n", proc_mem);
427 if( fp != stdout ) fclose(fp);
428 char cmd[1024], *cp = cmd;
429 cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
430 "-ex \"thread apply all info registers\" "
431 "-ex \"thread apply all bt full\" "
432 "-ex \"quit\"", pid, pid);
434 cp += sprintf(cp," >> \"%s\"", fn);
435 cp += sprintf(cp," 2>&1");
436 //printf("handle_dump:: pid=%d, cmd='%s' fn='%s'\n",pid,cmd,fn);
439 fprintf(stderr,"** can't start gdb, dump abondoned\n");
444 fprintf(stderr,"** dump complete\n");
447 char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
448 execvp(argv[0], &argv[0]);