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"
38 #include <sys/ioctl.h>
39 #include <sys/prctl.h>
40 #include <sys/types.h>
42 BC_Signals* BC_Signals::global_signals = 0;
43 static int signal_done = 0;
44 static int table_id = 0;
46 static bc_locktrace_t* new_bc_locktrace(void *ptr,
50 bc_locktrace_t *result = (bc_locktrace_t*)malloc(sizeof(bc_locktrace_t));
52 result->title = title;
53 result->location = location;
55 result->id = table_id++;
56 result->tid = pthread_self();
61 static struct sigaction old_segv = {0, }, old_intr = {0, };
62 static void handle_dump(int n, siginfo_t * info, void *sc);
64 const char *BC_Signals::trap_path = 0;
65 void *BC_Signals::trap_data = 0;
66 void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
67 bool BC_Signals::trap_sigsegv = false;
68 bool BC_Signals::trap_sigintr = false;
70 static void uncatch_sig(int sig, struct sigaction &old)
73 sigaction(sig, &old, &act);
77 static void catch_sig(int sig, struct sigaction &old)
80 memset(&act, 0, sizeof(act));
81 act.sa_sigaction = handle_dump;
82 act.sa_flags = SA_SIGINFO;
83 sigaction(sig, &act, (!old.sa_handler ? &old : 0));
86 static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
87 static void catch_intr() { catch_sig(SIGINT, old_intr); }
88 static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
89 static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
91 void BC_Signals::set_trap_path(const char *path)
96 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
102 void BC_Signals::set_catch_segv(bool v) {
103 if( v == trap_sigsegv ) return;
104 if( v ) catch_segv();
109 void BC_Signals::set_catch_intr(bool v) {
110 if( v == trap_sigintr ) return;
111 if( v ) catch_intr();
120 const char *location;
123 static bc_buffertrace_t* new_bc_buffertrace(int size, void *ptr, const char *location)
125 bc_buffertrace_t *result = (bc_buffertrace_t*)malloc(sizeof(bc_buffertrace_t));
128 result->location = location;
132 static void bc_copy_textfile(int lines, FILE *ofp, const char *fmt,...)
134 va_list ap; va_start(ap, fmt);
135 char bfr[BCTEXTLEN]; vsnprintf(bfr, sizeof(bfr), fmt, ap);
137 FILE *ifp = fopen(bfr,"r");
139 while( --lines >= 0 && fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
144 // Need our own table to avoid recursion with the memory manager
150 // This points to the next value to replace if the table wraps around
154 static void* append_table(bc_table_t *table, void *ptr)
156 if(table->allocation <= table->size)
158 if(table->allocation)
160 int new_allocation = table->allocation * 2;
161 void **new_values = (void**)calloc(new_allocation, sizeof(void*));
162 memcpy(new_values, table->values, sizeof(void*) * table->size);
164 table->values = new_values;
165 table->allocation = new_allocation;
169 table->allocation = 4096;
170 table->values = (void**)calloc(table->allocation, sizeof(void*));
174 table->values[table->size++] = ptr;
178 // Replace item in table pointed to by current_value and advance
180 static void* overwrite_table(bc_table_t *table, void *ptr)
182 free(table->values[table->current_value]);
183 table->values[table->current_value++] = ptr;
184 if(table->current_value >= table->size) table->current_value = 0;
188 static void clear_table(bc_table_t *table, int delete_objects)
192 for(int i = 0; i < table->size; i++)
194 free(table->values[i]);
200 static void clear_table_entry(bc_table_t *table, int number, int delete_object)
202 if(delete_object) free(table->values[number]);
203 for(int i = number; i < table->size - 1; i++)
205 table->values[i] = table->values[i + 1];
210 // Table of functions currently running.
211 static bc_table_t execution_table = { 0, 0, 0, 0 };
213 // Table of locked positions
214 static bc_table_t lock_table = { 0, 0, 0, 0 };
217 static bc_table_t memory_table = { 0, 0, 0, 0 };
219 static bc_table_t temp_files = { 0, 0, 0, 0 };
221 // Can't use Mutex because it would be recursive
222 static pthread_mutex_t *lock = 0;
223 static pthread_mutex_t *handler_lock = 0;
224 // incase lock set after task ends
225 static pthread_t last_lock_thread = 0;
226 static const char *last_lock_title = 0;
227 static const char *last_lock_location = 0;
228 // Don't trace memory until this is true to avoid initialization
229 static int trace_memory = 0;
232 static const char* signal_titles[] =
252 void BC_Signals::dump_stack(FILE *fp)
255 int total = backtrace (buffer, 256);
256 char **result = backtrace_symbols (buffer, total);
257 fprintf(fp, "BC_Signals::dump_stack\n");
258 for(int i = 0; i < total; i++)
260 fprintf(fp, "%s\n", result[i]);
265 void BC_Signals::kill_subs()
267 // List /proc directory
269 struct dirent64 *new_filename;
271 char path[BCTEXTLEN];
272 char string[BCTEXTLEN];
274 dirstream = opendir("/proc");
275 if(!dirstream) return;
277 while( (new_filename = readdir64(dirstream)) != 0 )
279 // All digits are numbers
280 char *ptr = new_filename->d_name;
284 if(*ptr == '.' || isalpha(*ptr++))
291 if(got_alpha) continue;
293 // Must be a directory
294 sprintf(path, "/proc/%s", new_filename->d_name);
295 if(!stat(path, &ostat))
297 if(S_ISDIR(ostat.st_mode))
300 strcat(path, "/stat");
301 //printf("kill_subs %d %s\n", __LINE__, path);
302 FILE *fd = fopen(path, "r");
304 // Must search forwards because the file is 0 length
310 //printf("kill_subs %d %d\n", __LINE__, c);
313 // Search for 2 spaces
315 while(!feof(fd) && spaces < 2)
322 // Read in parent process
335 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
337 // atoi(new_filename->d_name),
340 int parent_process = atoi(string);
341 int child_process = atoi(new_filename->d_name);
343 // Kill if we're the parent
344 if(getpid() == parent_process)
346 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
347 kill(child_process, SIGKILL);
359 static void signal_entry(int signum)
361 signal(signum, SIG_DFL);
363 pthread_mutex_lock(handler_lock);
366 pthread_mutex_unlock(handler_lock);
371 pthread_mutex_unlock(handler_lock);
374 printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
375 signal_titles[signum],
377 execution_table.size);
379 BC_Signals::kill_subs();
380 BC_Signals::dump_traces();
381 BC_Signals::dump_locks();
382 BC_Signals::dump_buffers();
383 BC_Signals::delete_temps();
385 // Call user defined signal handler
386 BC_Signals::global_signals->signal_handler(signum);
391 static void signal_entry_recoverable(int signum)
393 printf("signal_entry_recoverable: got %s my pid=%d\n",
394 signal_titles[signum],
398 // used to terminate child processes when program terminates
399 static void handle_exit(int signum)
401 //printf("child %d exit\n", getpid());
405 void BC_Signals::set_sighup_exit(int enable)
408 // causes SIGHUP to be generated when parent dies
409 signal(SIGHUP, handle_exit);
410 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
411 // prevents ^C from signalling child when attached to gdb
413 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
416 signal(SIGHUP, signal_entry);
417 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
421 BC_Signals::BC_Signals()
424 BC_Signals::~BC_Signals()
426 BC_CModels::bcxfer_stop_slicers();
429 void BC_Signals::dump_traces(FILE *fp)
432 if(execution_table.size)
434 for(int i = execution_table.current_value; i < execution_table.size; i++)
435 fprintf(fp," %s\n", (char*)execution_table.values[i]);
436 for(int i = 0; i < execution_table.current_value; i++)
437 fprintf(fp," %s\n", (char*)execution_table.values[i]);
442 void BC_Signals::dump_locks(FILE *fp)
446 fprintf(fp,"signal_entry: lock table size=%d\n", lock_table.size);
447 for(int i = 0; i < lock_table.size; i++)
449 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
450 fprintf(fp," %p %s %s %p%s\n", table->ptr,
451 table->title, table->location, (void*)table->tid,
452 table->is_owner ? " *" : "");
457 void BC_Signals::dump_buffers(FILE *fp)
460 pthread_mutex_lock(lock);
462 fprintf(fp,"BC_Signals::dump_buffers: buffer table size=%d\n", memory_table.size);
463 for(int i = 0; i < memory_table.size; i++)
465 bc_buffertrace_t *entry = (bc_buffertrace_t*)memory_table.values[i];
466 fprintf(fp," %d %p %s\n", entry->size, entry->ptr, entry->location);
468 pthread_mutex_unlock(lock);
472 void BC_Signals::delete_temps()
474 pthread_mutex_lock(lock);
475 if(temp_files.size) printf("BC_Signals::delete_temps: deleting %d temp files\n", temp_files.size);
476 for(int i = 0; i < temp_files.size; i++)
478 printf(" %s\n", (char*)temp_files.values[i]);
479 remove((char*)temp_files.values[i]);
481 pthread_mutex_unlock(lock);
484 void BC_Signals::reset_locks()
486 pthread_mutex_unlock(lock);
489 void BC_Signals::set_temp(char *string)
491 char *new_string = strdup(string);
492 append_table(&temp_files, new_string);
495 void BC_Signals::unset_temp(char *string)
497 for(int i = 0; i < temp_files.size; i++)
499 if(!strcmp((char*)temp_files.values[i], string))
501 clear_table_entry(&temp_files, i, 1);
508 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
511 XGetErrorText(event->display, event->error_code, string, 1024);
512 fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
513 event->error_code, event->request_code, event->minor_code,
514 (int64_t)event->resourceid, string);
519 void BC_Signals::initialize()
521 BC_Signals::global_signals = this;
522 lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
523 handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
524 pthread_mutex_init(lock, 0);
525 pthread_mutex_init(handler_lock, 0);
526 old_err_handler = XSetErrorHandler(x_error_handler);
530 void BC_Signals::terminate()
532 BC_Signals::global_signals = 0;
533 uncatch_segv(); uncatch_intr();
534 signal(SIGHUP, SIG_DFL);
535 signal(SIGINT, SIG_DFL);
536 signal(SIGQUIT, SIG_DFL);
537 signal(SIGTERM, SIG_DFL);
538 signal(SIGFPE, SIG_DFL);
539 signal(SIGPIPE, SIG_DFL);
540 signal(SIGUSR2, SIG_DFL);
541 XSetErrorHandler(old_err_handler);
544 // callable from debugger
548 BC_Signals::dump_traces();
549 BC_Signals::dump_locks();
550 BC_Signals::dump_buffers();
554 void BC_Signals::signal_dump(int signum)
556 BC_KeyboardHandler::kill_grabs();
558 signal(SIGUSR2, signal_dump);
566 void BC_Signals::initialize2()
568 signal(SIGHUP, signal_entry);
569 signal(SIGINT, signal_entry);
570 signal(SIGQUIT, signal_entry);
571 // SIGKILL cannot be stopped
572 // signal(SIGKILL, signal_entry);
574 signal(SIGTERM, signal_entry);
575 signal(SIGFPE, signal_entry);
576 signal(SIGPIPE, signal_entry_recoverable);
577 signal(SIGUSR2, signal_dump);
581 void BC_Signals::signal_handler(int signum)
583 printf("BC_Signals::signal_handler\n");
587 const char* BC_Signals::sig_to_str(int number)
589 return signal_titles[number];
592 #define TOTAL_TRACES 16
594 void BC_Signals::new_trace(const char *text)
596 if(!global_signals) return;
597 pthread_mutex_lock(lock);
600 if(execution_table.size >= TOTAL_TRACES)
602 overwrite_table(&execution_table, strdup(text));
603 // clear_table(&execution_table, 1);
607 append_table(&execution_table, strdup(text));
609 pthread_mutex_unlock(lock);
612 void BC_Signals::new_trace(const char *file, const char *function, int line)
614 char string[BCTEXTLEN];
615 snprintf(string, BCTEXTLEN, "%s: %s: %d", file, function, line);
619 void BC_Signals::delete_traces()
621 if(!global_signals) return;
622 pthread_mutex_lock(lock);
623 clear_table(&execution_table, 0);
624 pthread_mutex_unlock(lock);
627 // no canceling with lock held
628 void BC_Signals::lock_locks(const char *s)
630 pthread_mutex_lock(lock);
631 last_lock_thread = pthread_self();
633 last_lock_location = 0;
636 void BC_Signals::unlock_locks()
638 pthread_mutex_unlock(lock);
641 #define TOTAL_LOCKS 256
643 int BC_Signals::set_lock(void *ptr,
645 const char *location)
647 if(!global_signals) return 0;
648 bc_locktrace_t *table = 0;
651 pthread_mutex_lock(lock);
652 last_lock_thread = pthread_self();
653 last_lock_title = title;
654 last_lock_location = location;
655 if(lock_table.size >= TOTAL_LOCKS)
656 clear_table(&lock_table, 0);
658 // Put new lock entry
659 table = new_bc_locktrace(ptr, title, location);
660 append_table(&lock_table, table);
661 id_return = table->id;
663 pthread_mutex_unlock(lock);
667 void BC_Signals::set_lock2(int table_id)
669 if(!global_signals) return;
671 bc_locktrace_t *table = 0;
672 pthread_mutex_lock(lock);
673 for(int i = lock_table.size - 1; i >= 0; i--)
675 table = (bc_locktrace_t*)lock_table.values[i];
676 // Got it. Hasn't been unlocked/deleted yet.
677 if(table->id == table_id)
680 table->tid = pthread_self();
681 pthread_mutex_unlock(lock);
685 pthread_mutex_unlock(lock);
688 void BC_Signals::unset_lock2(int table_id)
690 if(!global_signals) return;
692 bc_locktrace_t *table = 0;
693 pthread_mutex_lock(lock);
694 for(int i = lock_table.size - 1; i >= 0; i--)
696 table = (bc_locktrace_t*)lock_table.values[i];
697 if(table->id == table_id)
699 clear_table_entry(&lock_table, i, 1);
703 pthread_mutex_unlock(lock);
706 void BC_Signals::unset_lock(void *ptr)
708 if(!global_signals) return;
710 bc_locktrace_t *table = 0;
711 pthread_mutex_lock(lock);
713 // Take off currently held entry
714 for(int i = 0; i < lock_table.size; i++)
716 table = (bc_locktrace_t*)lock_table.values[i];
717 if(table->ptr == ptr)
721 clear_table_entry(&lock_table, i, 1);
727 pthread_mutex_unlock(lock);
731 void BC_Signals::unset_all_locks(void *ptr)
733 if(!global_signals) return;
734 pthread_mutex_lock(lock);
735 // Take off previous lock entry
736 for(int i = 0; i < lock_table.size; )
738 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
739 if(table->ptr == ptr)
741 clear_table_entry(&lock_table, i, 1);
746 pthread_mutex_unlock(lock);
749 void BC_Signals::clear_locks_tid(pthread_t tid)
751 if(!global_signals) return;
752 pthread_mutex_lock(lock);
753 // Take off previous lock entry
754 for(int i = 0; i < lock_table.size; )
756 bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
757 if(table->tid == tid)
759 clear_table_entry(&lock_table, i, 1);
764 pthread_mutex_unlock(lock);
768 void BC_Signals::enable_memory()
773 void BC_Signals::disable_memory()
779 void BC_Signals::set_buffer(int size, void *ptr, const char* location)
781 if(!global_signals) return;
782 if(!trace_memory) return;
784 //printf("BC_Signals::set_buffer %p %s\n", ptr, location);
785 pthread_mutex_lock(lock);
786 append_table(&memory_table, new_bc_buffertrace(size, ptr, location));
787 pthread_mutex_unlock(lock);
790 int BC_Signals::unset_buffer(void *ptr)
792 if(!global_signals) return 0;
793 if(!trace_memory) return 0;
796 pthread_mutex_lock(lock);
797 for(int i = 0; i < memory_table.size; i++)
799 if(((bc_buffertrace_t*)memory_table.values[i])->ptr == ptr)
801 //printf("BC_Signals::unset_buffer %p\n", ptr);
802 clear_table_entry(&memory_table, i, 1);
808 pthread_mutex_unlock(lock);
809 // fprintf(stderr, "BC_Signals::unset_buffer buffer %p not found.\n", ptr);
814 #include <ucontext.h>
815 #include <sys/wait.h>
829 static void handle_dump(int n, siginfo_t * info, void *sc)
831 uncatch_segv(); uncatch_intr();
832 signal(SIGSEGV, SIG_DFL);
833 signal(SIGINT, SIG_DFL);
834 // gotta be root, or the dump is worthless
836 if( uid != 0 ) return;
837 ucontext_t *uc = (ucontext_t *)sc;
838 int pid = getpid(), tid = gettid();
839 struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
840 uint8_t *ip = (uint8_t *)c->IP;
841 fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
842 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
843 (void*)ip, pid, tid);
846 if( BC_Signals::trap_path ) {
847 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
851 fprintf(stderr,"writing debug data to %s\n", fn);
852 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
853 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
854 (void*)c->IP, pid, tid);
857 strcpy(fn, "stdout");
861 fprintf(fp,"created on %s", ctime(&t));
862 struct passwd *pw = getpwuid(uid);
864 fprintf(fp," by %d:%d %s(%s)\n",
865 pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
867 fprintf(fp,"\nCPUS: %d\n", BC_Resources::get_machine_cpus());
868 fprintf(fp,"\nCPUINFO:\n"); bc_copy_textfile(32, fp,"/proc/cpuinfo");
869 fprintf(fp,"\nTHREADS:\n"); Thread::dump_threads(fp);
870 fprintf(fp,"\nTRACES:\n"); BC_Signals::dump_traces(fp);
871 fprintf(fp,"\nLOCKS:\n"); BC_Signals::dump_locks(fp);
872 fprintf(fp,"\nBUFFERS:\n"); BC_Signals::dump_buffers(fp);
873 if( BC_Signals::trap_hook ) {
874 fprintf(fp,"\nMAIN HOOK:\n");
875 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
877 fprintf(fp,"\nVERSION:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/version");
878 fprintf(fp,"\nMEMINFO:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
879 fprintf(fp,"\nMAPS:\n"); bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
881 if( tid > 0 && tid != pid )
882 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
884 sprintf(proc_mem,"/proc/%d/mem",pid);
885 int pfd = open(proc_mem,O_RDONLY);
887 fprintf(fp,"\nCODE:\n");
888 for( int i=-32; i<32; ) {
889 uint8_t v; void *vp = (void *)(ip + i);
890 if( !(i & 7) ) fprintf(fp,"%p: ", vp);
891 if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
892 fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
893 if( !(++i & 7) ) fprintf(fp,"\n");
899 fprintf(fp,"err opening: %s, %m\n", proc_mem);
902 if( fp != stdout ) fclose(fp);
903 char cmd[1024], *cp = cmd;
904 cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
905 "-ex \"thread apply all info registers\" "
906 "-ex \"thread apply all bt full\" "
907 "-ex \"quit\"", pid, pid);
909 cp += sprintf(cp," >> \"%s\"", fn);
910 cp += sprintf(cp," 2>&1");
911 //printf("handle_dump:: pid=%d, cmd='%s' fn='%s'\n",pid,cmd,fn);
914 fprintf(stderr,"** can't start gdb, dump abondoned\n");
919 fprintf(stderr,"** dump complete\n");
922 char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
923 execvp(argv[0], &argv[0]);
932 // void* operator new(size_t size)
934 // //printf("new 1 %d\n", size);
935 // void *result = malloc(size);
936 // BUFFER(size, result, "new");
937 // //printf("new 2 %d\n", size);
941 // void* operator new[](size_t size)
943 // //printf("new [] 1 %d\n", size);
944 // void *result = malloc(size);
945 // BUFFER(size, result, "new []");
946 // //printf("new [] 2 %d\n", size);
950 // void operator delete(void *ptr)
952 // //printf("delete 1 %p\n", ptr);
954 // //printf("delete 2 %p\n", ptr);
958 // void operator delete[](void *ptr)
960 // //printf("delete [] 1 %p\n", ptr);
963 // //printf("delete [] 2 %p\n", ptr);