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"
28 #include "filesystem.h"
42 #include <sys/ioctl.h>
44 #include <sys/prctl.h>
46 #include <sys/types.h>
48 BC_Signals* BC_Signals::global_signals = 0;
49 static int signal_done = 0;
51 static struct sigaction old_segv = {0, }, old_intr = {0, };
52 static void handle_dump(int n, siginfo_t * info, void *sc);
54 const char *BC_Signals::trap_path = 0;
55 void *BC_Signals::trap_data = 0;
56 void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
57 bool BC_Signals::trap_sigsegv = false;
58 bool BC_Signals::trap_sigintr = false;
60 static void uncatch_sig(int sig, struct sigaction &old)
63 sigaction(sig, &old, &act);
67 static void catch_sig(int sig, struct sigaction &old)
70 memset(&act, 0, sizeof(act));
71 act.sa_sigaction = handle_dump;
72 act.sa_flags = SA_SIGINFO;
73 sigaction(sig, &act, (!old.sa_handler ? &old : 0));
76 static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
77 static void catch_intr() { catch_sig(SIGINT, old_intr); }
78 static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
79 static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
81 void BC_Signals::set_trap_path(const char *path)
86 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
92 void BC_Signals::set_catch_segv(bool v) {
93 if( v == trap_sigsegv ) return;
99 void BC_Signals::set_catch_intr(bool v) {
100 if( v == trap_sigintr ) return;
101 if( v ) catch_intr();
106 static void bc_copy_textfile(int lines, FILE *ofp, const char *fmt,...)
108 va_list ap; va_start(ap, fmt);
109 char bfr[BCTEXTLEN]; vsnprintf(bfr, sizeof(bfr), fmt, ap);
111 FILE *ifp = fopen(bfr,"r");
113 while( --lines >= 0 && fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
117 static void bc_list_openfiles(int lines, FILE *ofp, const char *fmt,...)
119 va_list ap; va_start(ap, fmt);
120 char bfr[BCTEXTLEN]; vsnprintf(bfr, sizeof(bfr), fmt, ap);
122 DIR *dir = opendir(bfr);
124 struct dirent64 *dent;
125 while( --lines >= 0 && (dent = readdir64(dir)) ) {
126 const char *fn = dent->d_name;
127 fprintf(ofp, "%s", fn);
128 char path[BCTEXTLEN], link[BCTEXTLEN];
130 snprintf(path, sizeof(path), "%s/%s", bfr, fn);
131 if( !stat(path,&st) ) {
133 if( S_ISREG(st.st_mode) ) typ = ' ';
134 else if( S_ISDIR(st.st_mode) ) typ = 'd';
135 else if( S_ISBLK(st.st_mode) ) typ = 'b';
136 else if( S_ISCHR(st.st_mode) ) typ = 'c';
137 else if( S_ISFIFO(st.st_mode) ) typ = 'f';
138 else if( S_ISLNK(st.st_mode) ) typ = 'l';
139 else if( S_ISSOCK(st.st_mode) ) typ = 's';
140 if( typ ) fprintf(ofp, "\t%c", typ);
141 fprintf(ofp, "\tsize %jd", st.st_size);
142 int len = readlink(path, link, sizeof(link)-1);
145 fprintf(ofp, "\t-> %s", link);
148 snprintf(path, sizeof(path), "%sinfo/%s", bfr, fn);
149 FILE *fp = fopen(path,"r"); int64_t pos;
151 while( fgets(link, sizeof(link), fp) ) {
152 if( sscanf(link, "pos:%jd", &pos) == 1 ) {
153 fprintf(ofp, "\tpos: %jd", pos);
164 // Can't use Mutex because it would be recursive
165 static pthread_mutex_t *handler_lock = 0;
168 static const char* signal_titles[] =
188 void BC_Signals::dump_stack(FILE *fp)
192 int total = backtrace (buffer, 256);
193 char **result = backtrace_symbols (buffer, total);
194 fprintf(fp, "BC_Signals::dump_stack\n");
195 for(int i = 0; i < total; i++)
197 fprintf(fp, "%s\n", result[i]);
203 void BC_Signals::kill_subs()
205 // List /proc directory
206 struct dirent64 *new_filename;
208 char path[BCTEXTLEN], string[BCTEXTLEN];
209 DIR *dirstream = opendir("/proc");
210 if( !dirstream ) return;
211 pid_t ppid = getpid();
213 while( (new_filename = readdir64(dirstream)) != 0 ) {
214 char *ptr = new_filename->d_name;
215 while( *ptr && *ptr != '.' && !isalpha(*ptr) ) ++ptr;
216 // All digits are numbers
219 // Must be a directory
220 sprintf(path, "/proc/%s", new_filename->d_name);
221 if( stat(path, &ostat) ) continue;
222 if( !S_ISDIR(ostat.st_mode) ) continue;
223 strcat(path, "/stat");
224 FILE *fd = fopen(path, "r");
226 while( !feof(fd) && fgetc(fd)!=')' );
227 //printf("kill_subs %d %d\n", __LINE__, c);
228 for( int sp=2; !feof(fd) && sp>0; )
229 if( fgetc(fd) == ' ' ) --sp;
230 // Read in parent process
231 for( ptr=string; !feof(fd) && (*ptr=fgetc(fd))!=' '; ++ptr );
232 if( (*ptr=fgetc(fd)) == ' ' ) break;
235 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
236 // __LINE__, atoi(new_filename->d_name), getpid(), atoi(string));
237 int parent_process = atoi(string);
238 // Kill if we're the parent
239 if( ppid == parent_process ) {
240 int child_process = atoi(new_filename->d_name);
241 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
242 kill(child_process, SIGKILL);
248 static void signal_entry(int signum)
250 signal(signum, SIG_DFL);
252 pthread_mutex_lock(handler_lock);
253 int done = signal_done;
255 pthread_mutex_unlock(handler_lock);
258 printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
259 signal_titles[signum], getpid(), execution_table.size);
261 BC_Signals::kill_subs();
262 BC_Trace::dump_traces();
263 BC_Trace::dump_locks();
264 BC_Trace::dump_buffers();
265 BC_Trace::delete_temps();
267 // Call user defined signal handler
268 BC_Signals::global_signals->signal_handler(signum);
273 static void signal_entry_recoverable(int signum)
275 printf("signal_entry_recoverable: got %s my pid=%d\n",
276 signal_titles[signum],
281 // used to terminate child processes when program terminates
282 static void handle_exit(int signum)
284 //printf("child %d exit\n", getpid());
289 void BC_Signals::set_sighup_exit(int enable)
293 // causes SIGHUP to be generated when parent dies
294 signal(SIGHUP, handle_exit);
295 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
296 // prevents ^C from signalling child when attached to gdb
298 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
301 signal(SIGHUP, signal_entry);
302 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
307 BC_Signals::BC_Signals()
310 BC_Signals::~BC_Signals()
312 BC_CModels::bcxfer_stop_slicers();
316 int dump_xerror = -1;
318 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
321 XGetErrorText(event->display, event->error_code, string, 1024);
322 fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
323 event->error_code, event->request_code, event->minor_code,
324 (int64_t)event->resourceid, string);
325 if( dump_xerror < 0 ) {
326 char *env = getenv("BC_TRACE_XERROR");
327 dump_xerror = !env || !atoi(env) ? 0 : 1;
335 void BC_Signals::initialize(const char *trap_path)
337 BC_Signals::global_signals = this;
338 BC_Trace::global_trace = this;
339 set_trap_path(trap_path);
340 handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
341 pthread_mutex_init(handler_lock, 0);
342 old_err_handler = XSetErrorHandler(x_error_handler);
346 void BC_Signals::terminate()
348 BC_Signals::global_signals = 0;
349 BC_Trace::global_trace = 0;
350 uncatch_segv(); uncatch_intr();
351 signal(SIGHUP, SIG_DFL);
352 signal(SIGINT, SIG_DFL);
353 signal(SIGQUIT, SIG_DFL);
354 signal(SIGTERM, SIG_DFL);
355 signal(SIGFPE, SIG_DFL);
356 signal(SIGPIPE, SIG_DFL);
357 signal(SIGUSR2, SIG_DFL);
358 XSetErrorHandler(old_err_handler);
362 void BC_Signals::signal_dump(int signum)
364 BC_KeyboardHandler::kill_grabs();
366 signal(SIGUSR2, signal_dump);
374 void BC_Signals::initialize2()
376 signal(SIGHUP, signal_entry);
377 signal(SIGINT, signal_entry);
378 signal(SIGQUIT, signal_entry);
379 // SIGKILL cannot be stopped
380 // signal(SIGKILL, signal_entry);
382 signal(SIGTERM, signal_entry);
383 signal(SIGFPE, signal_entry);
384 signal(SIGPIPE, signal_entry_recoverable);
385 signal(SIGUSR2, signal_dump);
389 void BC_Signals::signal_handler(int signum)
391 printf("BC_Signals::signal_handler\n");
395 const char* BC_Signals::sig_to_str(int number)
397 return signal_titles[number];
402 #include <ucontext.h>
403 #include <sys/wait.h>
408 #define sigregs_t sigcontext
410 static void reg_dump(FILE *fp,sigregs_t *rp)
412 fprintf(fp,"REGS:\n");
413 fprintf(fp," gs: %04x:%04x\n", rp->gs,rp->__gsh);
414 fprintf(fp," fs: %04x:%04x\n", rp->fs,rp->__fsh);
415 fprintf(fp," es: %04x:%04x\n", rp->es,rp->__esh);
416 fprintf(fp," ds: %04x:%04x\n", rp->ds,rp->__dsh);
417 fprintf(fp," edi: %14p %d\n", (void*)rp->edi,rp->edi);
418 fprintf(fp," esi: %14p %d\n", (void*)rp->esi,rp->esi);
419 fprintf(fp," ebp: %14p %d\n", (void*)rp->ebp,rp->ebp);
420 fprintf(fp," esp: %14p %d\n", (void*)rp->esp,rp->esp);
421 fprintf(fp," ebx: %14p %d\n", (void*)rp->ebx,rp->ebx);
422 fprintf(fp," edx: %14p %d\n", (void*)rp->edx,rp->edx);
423 fprintf(fp," ecx: %14p %d\n", (void*)rp->ecx,rp->ecx);
424 fprintf(fp," eax: %14p %d\n", (void*)rp->eax,rp->eax);
425 fprintf(fp," trapno: %14p %d\n", (void*)rp->trapno,rp->trapno);
426 fprintf(fp," err: %14p %d\n", (void*)rp->err,rp->err);
427 fprintf(fp," eip: %14p %d\n", (void*)rp->eip,rp->eip);
428 fprintf(fp," cs: %04xd : %04x\n", rp->cs,rp->__csh);
429 fprintf(fp," eflags: %14p %d\n", (void*)rp->eflags,rp->eflags);
430 fprintf(fp," esp_at_signal: %p %d\n", (void*)rp->esp_at_signal,rp->esp_at_signal);
431 fprintf(fp," ss: %04xd : %04x\n", rp->ss,rp->__ssh);
432 fprintf(fp," oldmask: %14p %d\n", (void*)rp->oldmask,rp->oldmask);
433 fprintf(fp," cr2: %14p %d\n", (void*)rp->cr2,rp->cr2);
440 #define sigregs_t sigcontext
442 static void reg_dump(FILE *fp,sigregs_t *rp)
444 fprintf(fp,"REGS:\n");
445 fprintf(fp," r8: %20p %jd\n", (void*)rp->r8,rp->r8);
446 fprintf(fp," r9: %20p %jd\n", (void*)rp->r9,rp->r9);
447 fprintf(fp," r10: %20p %jd\n", (void*)rp->r10,rp->r10);
448 fprintf(fp," r11: %20p %jd\n", (void*)rp->r11,rp->r11);
449 fprintf(fp," r12: %20p %jd\n", (void*)rp->r12,rp->r12);
450 fprintf(fp," r13: %20p %jd\n", (void*)rp->r13,rp->r13);
451 fprintf(fp," r14: %20p %jd\n", (void*)rp->r14,rp->r14);
452 fprintf(fp," r15: %20p %jd\n", (void*)rp->r15,rp->r15);
453 fprintf(fp," rdi: %20p %jd\n", (void*)rp->rdi,rp->rdi);
454 fprintf(fp," rsi: %20p %jd\n", (void*)rp->rsi,rp->rsi);
455 fprintf(fp," rbp: %20p %jd\n", (void*)rp->rbp,rp->rbp);
456 fprintf(fp," rbx: %20p %jd\n", (void*)rp->rbx,rp->rbx);
457 fprintf(fp," rdx: %20p %jd\n", (void*)rp->rdx,rp->rdx);
458 fprintf(fp," rax: %20p %jd\n", (void*)rp->rax,rp->rax);
459 fprintf(fp," rcx: %20p %jd\n", (void*)rp->rcx,rp->rcx);
460 fprintf(fp," rsp: %20p %jd\n", (void*)rp->rsp,rp->rsp);
461 fprintf(fp," rip: %20p %jd\n", (void*)rp->rip,rp->rip);
462 fprintf(fp," eflags: %14p %jd\n", (void*)rp->eflags,rp->eflags);
463 fprintf(fp," cs: %04x\n", rp->cs);
464 fprintf(fp," gs: %04x\n", rp->gs);
465 fprintf(fp," fs: %04x\n", rp->fs);
466 fprintf(fp," err: %20p %jd\n", (void*)rp->err,rp->err);
467 fprintf(fp," trapno: %20p %jd\n", (void*)rp->trapno,rp->trapno);
468 fprintf(fp," oldmask: %20p %jd\n", (void*)rp->oldmask,rp->oldmask);
469 fprintf(fp," cr2: %20p %jd\n", (void*)rp->cr2,rp->cr2);
475 #if __powerpc__ || __powerpc64__ || __powerpc64le__
476 #include <asm/ptrace.h>
478 #define sigregs_t pt_regs
479 static void reg_dump(FILE *fp,sigregs_t *rp) {}
489 static void handle_dump(int n, siginfo_t * info, void *sc)
491 uncatch_segv(); uncatch_intr();
492 signal(SIGSEGV, SIG_DFL);
493 signal(SIGINT, SIG_DFL);
494 // gotta be root, or the dump is worthless
496 // it is not necessary to be root if ptrace is allowed via:
497 // echo 0 > /proc/sys/kernel/yama/ptrace_scope (usually set to 1)
498 // if( uid != 0 ) return;
499 int pid = getpid(), tid = gettid();
502 ucontext_t *uc = (ucontext_t *)sc;
503 struct sigregs_t *c = (struct sigregs_t *)&uc->uc_mcontext;
506 fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
507 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
511 if( BC_Signals::trap_path ) {
512 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
516 fprintf(stderr,"writing debug data to %s\n", fn);
517 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
518 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
522 strcpy(fn, "stdout");
526 fprintf(fp,"created on %s", ctime(&t));
527 struct passwd *pw = getpwuid(uid);
529 fprintf(fp," by %d:%d %s(%s)\n",
530 pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
532 fprintf(fp,"\nOS:\n"); bc_copy_textfile(16, fp,"/etc/os-release");
533 fprintf(fp,"\nCPUS: %d\n", BC_Resources::get_machine_cpus());
534 fprintf(fp,"\nCPUINFO:\n"); bc_copy_textfile(32, fp,"/proc/cpuinfo");
535 fprintf(fp,"\nTHREADS:\n"); BC_Trace::dump_threads(fp);
536 fprintf(fp,"\nTRACES:\n"); BC_Trace::dump_traces(fp);
537 fprintf(fp,"\nLOCKS:\n"); BC_Trace::dump_locks(fp);
538 fprintf(fp,"\nBUFFERS:\n"); BC_Trace::dump_buffers(fp);
539 fprintf(fp,"\nSHMMEM:\n"); BC_Trace::dump_shm_stats(fp);
540 if( BC_Signals::trap_hook ) {
541 fprintf(fp,"\nMAIN HOOK:\n");
542 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
544 fprintf(fp,"\nVERSION:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/version");
545 fprintf(fp,"\nMEMINFO:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
546 fprintf(fp,"\nSTATUS:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/%d/status",pid);
547 fprintf(fp,"\nFD:\n"); bc_list_openfiles(INT_MAX, fp,"/proc/%d/fd", pid);
548 fprintf(fp,"\nMAPS:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
551 if( tid > 0 && tid != pid )
552 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
554 sprintf(proc_mem,"/proc/%d/mem",pid);
555 int pfd = open(proc_mem,O_RDONLY);
557 fprintf(fp,"\nCODE:\n");
558 for( int i=-32; i<32; ) {
559 uint8_t v; void *vp = (void *)((char*)ip + i);
560 if( !(i & 7) ) fprintf(fp,"%p: ", vp);
561 if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
562 fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
563 if( !(++i & 7) ) fprintf(fp,"\n");
569 fprintf(fp,"err opening: %s, %m\n", proc_mem);
572 BC_Signals::dump_stack(fp);
574 if( fp != stdout ) fclose(fp);
576 char cmd[1024], *cp = cmd;
577 cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
578 "-ex \"thread apply all info registers\" "
579 "-ex \"thread apply all bt full\" "
580 "-ex \"quit\"", pid, pid);
582 cp += sprintf(cp," >> \"%s\"", fn);
583 cp += sprintf(cp," 2>&1");
584 //printf("handle_dump:: pid=%d, cmd='%s' fn='%s'\n",pid,cmd,fn);
587 fprintf(stderr,"** can't start gdb, dump abondoned\n");
592 fprintf(stderr,"** dump complete\n");
595 char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
596 execvp(argv[0], &argv[0]);