Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / guicast / bcsignals.C
diff --git a/cinelerra-5.1/guicast/bcsignals.C b/cinelerra-5.1/guicast/bcsignals.C
new file mode 100644 (file)
index 0000000..e2a8b14
--- /dev/null
@@ -0,0 +1,935 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "bcsignals.h"
+#include "bcwindowbase.h"
+#include "bckeyboard.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <execinfo.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+
+BC_Signals* BC_Signals::global_signals = 0;
+static int signal_done = 0;
+static int table_id = 0;
+
+static bc_locktrace_t* new_bc_locktrace(void *ptr,
+       const char *title,
+       const char *location)
+{
+       bc_locktrace_t *result = (bc_locktrace_t*)malloc(sizeof(bc_locktrace_t));
+       result->ptr = ptr;
+       result->title = title;
+       result->location = location;
+       result->is_owner = 0;
+       result->id = table_id++;
+       result->tid = pthread_self();
+       return result;
+}
+
+
+static struct sigaction old_segv = {0, }, old_intr = {0, };
+static void handle_dump(int n, siginfo_t * info, void *sc);
+
+const char *BC_Signals::trap_path = 0;
+void *BC_Signals::trap_data = 0;
+void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
+bool BC_Signals::trap_sigsegv = false;
+bool BC_Signals::trap_sigintr = false;
+
+static void uncatch_sig(int sig, struct sigaction &old)
+{
+       struct sigaction act;
+       sigaction(sig, &old, &act);
+       old.sa_handler = 0;
+}
+
+static void catch_sig(int sig, struct sigaction &old)
+{
+       struct sigaction act;
+       memset(&act, 0, sizeof(act));
+       act.sa_sigaction = handle_dump;
+       act.sa_flags = SA_SIGINFO;
+       sigaction(sig, &act, (!old.sa_handler ? &old : 0));
+}
+
+static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
+static void catch_intr() { catch_sig(SIGINT, old_intr); }
+static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
+static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
+
+void BC_Signals::set_trap_path(const char *path)
+{
+       trap_path = path;
+}
+
+void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
+{
+       trap_data = data;
+       trap_hook = hook;
+}
+
+void BC_Signals::set_catch_segv(bool v) {
+       if( v == trap_sigsegv ) return;
+       if( v ) catch_segv();
+       else uncatch_segv();
+       v = trap_sigsegv;
+}
+
+void BC_Signals::set_catch_intr(bool v) {
+       if( v == trap_sigintr ) return;
+       if( v ) catch_intr();
+       else uncatch_intr();
+       v = trap_sigintr;
+}
+
+typedef struct
+{
+       int size;
+       void *ptr;
+       const char *location;
+} bc_buffertrace_t;
+
+static bc_buffertrace_t* new_bc_buffertrace(int size, void *ptr, const char *location)
+{
+       bc_buffertrace_t *result = (bc_buffertrace_t*)malloc(sizeof(bc_buffertrace_t));
+       result->size = size;
+       result->ptr = ptr;
+       result->location = location;
+       return result;
+}
+
+static void bc_copy_textfile(FILE *ofp, const char *fmt,...)
+{
+       va_list ap;    va_start(ap, fmt);
+       char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
+       va_end(ap);
+       FILE *ifp = fopen(bfr,"r");
+       if( !ifp ) return;
+       while( fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
+       fclose(ifp);
+}
+
+
+// Need our own table to avoid recursion with the memory manager
+typedef struct
+{
+       void **values;
+       int size;
+       int allocation;
+// This points to the next value to replace if the table wraps around
+       int current_value;
+} bc_table_t;
+
+static void* append_table(bc_table_t *table, void *ptr)
+{
+       if(table->allocation <= table->size)
+       {
+               if(table->allocation)
+               {
+                       int new_allocation = table->allocation * 2;
+                       void **new_values = (void**)calloc(new_allocation, sizeof(void*));
+                       memcpy(new_values, table->values, sizeof(void*) * table->size);
+                       free(table->values);
+                       table->values = new_values;
+                       table->allocation = new_allocation;
+               }
+               else
+               {
+                       table->allocation = 4096;
+                       table->values = (void**)calloc(table->allocation, sizeof(void*));
+               }
+       }
+
+       table->values[table->size++] = ptr;
+       return ptr;
+}
+
+// Replace item in table pointed to by current_value and advance
+// current_value
+static void* overwrite_table(bc_table_t *table, void *ptr)
+{
+       free(table->values[table->current_value]);
+       table->values[table->current_value++] = ptr;
+       if(table->current_value >= table->size) table->current_value = 0;
+       return 0;
+}
+
+static void clear_table(bc_table_t *table, int delete_objects)
+{
+       if(delete_objects)
+       {
+               for(int i = 0; i < table->size; i++)
+               {
+                       free(table->values[i]);
+               }
+       }
+       table->size = 0;
+}
+
+static void clear_table_entry(bc_table_t *table, int number, int delete_object)
+{
+       if(delete_object) free(table->values[number]);
+       for(int i = number; i < table->size - 1; i++)
+       {
+               table->values[i] = table->values[i + 1];
+       }
+       table->size--;
+}
+
+// Table of functions currently running.
+static bc_table_t execution_table = { 0, 0, 0, 0 };
+
+// Table of locked positions
+static bc_table_t lock_table = { 0, 0, 0, 0 };
+
+// Table of buffers
+static bc_table_t memory_table = { 0, 0, 0, 0 };
+
+static bc_table_t temp_files = { 0, 0, 0, 0 };
+
+// Can't use Mutex because it would be recursive
+static pthread_mutex_t *lock = 0;
+static pthread_mutex_t *handler_lock = 0;
+// incase lock set after task ends
+static pthread_t last_lock_thread = 0;
+static const char *last_lock_title = 0;
+static const char *last_lock_location = 0;
+// Don't trace memory until this is true to avoid initialization
+static int trace_memory = 0;
+
+
+static const char* signal_titles[] =
+{
+       "NULL",
+       "SIGHUP",
+       "SIGINT",
+       "SIGQUIT",
+       "SIGILL",
+       "SIGTRAP",
+       "SIGABRT",
+       "SIGBUS",
+       "SIGFPE",
+       "SIGKILL",
+       "SIGUSR1",
+       "SIGSEGV",
+       "SIGUSR2",
+       "SIGPIPE",
+       "SIGALRM",
+       "SIGTERM"
+};
+
+void BC_Signals::dump_stack(FILE *fp)
+{
+       void *buffer[256];
+       int total = backtrace (buffer, 256);
+       char **result = backtrace_symbols (buffer, total);
+       fprintf(fp, "BC_Signals::dump_stack\n");
+       for(int i = 0; i < total; i++)
+       {
+               fprintf(fp, "%s\n", result[i]);
+       }
+}
+
+// Kill subprocesses
+void BC_Signals::kill_subs()
+{
+// List /proc directory
+       DIR *dirstream;
+       struct dirent64 *new_filename;
+       struct stat ostat;
+       char path[BCTEXTLEN];
+       char string[BCTEXTLEN];
+
+       dirstream = opendir("/proc");
+       if(!dirstream) return;
+
+       while( (new_filename = readdir64(dirstream)) != 0 )
+       {
+// All digits are numbers
+               char *ptr = new_filename->d_name;
+               int got_alpha = 0;
+               while(*ptr)
+               {
+                       if(*ptr == '.' || isalpha(*ptr++))
+                       {
+                               got_alpha = 1;
+                               break;
+                       }
+               }
+
+               if(got_alpha) continue;
+
+// Must be a directory
+               sprintf(path, "/proc/%s", new_filename->d_name);
+               if(!stat(path, &ostat))
+               {
+                       if(S_ISDIR(ostat.st_mode))
+                       {
+// Read process stat
+                               strcat(path, "/stat");
+//printf("kill_subs %d %s\n", __LINE__, path);
+                               FILE *fd = fopen(path, "r");
+
+// Must search forwards because the file is 0 length
+                               if(fd)
+                               {
+                                       while(!feof(fd))
+                                       {
+                                               char c = fgetc(fd);
+//printf("kill_subs %d %d\n", __LINE__, c);
+                                               if(c == ')')
+                                               {
+// Search for 2 spaces
+                                                       int spaces = 0;
+                                                       while(!feof(fd) && spaces < 2)
+                                                       {
+                                                               c = fgetc(fd);
+                                                               if(c == ' ')
+                                                                       spaces++;
+                                                       }
+
+// Read in parent process
+                                                       ptr = string;
+                                                       while(!feof(fd))
+                                                       {
+                                                               *ptr = fgetc(fd);
+                                                               if(*ptr == ' ')
+                                                               {
+                                                                       *ptr = 0;
+                                                                       break;
+                                                               }
+                                                               ptr++;
+                                                       }
+
+// printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
+// __LINE__,
+// atoi(new_filename->d_name),
+// getpid(),
+// atoi(string));
+                                                       int parent_process = atoi(string);
+                                                       int child_process = atoi(new_filename->d_name);
+
+// Kill if we're the parent
+                                                       if(getpid() == parent_process)
+                                                       {
+//printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
+                                                               kill(child_process, SIGKILL);
+                                                       }
+                                               }
+                                       }
+
+                                       fclose(fd);
+                               }
+                       }
+               }
+       }
+}
+
+static void signal_entry(int signum)
+{
+       signal(signum, SIG_DFL);
+
+       pthread_mutex_lock(handler_lock);
+       if(signal_done)
+       {
+               pthread_mutex_unlock(handler_lock);
+               exit(0);
+       }
+
+       signal_done = 1;
+       pthread_mutex_unlock(handler_lock);
+
+
+       printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
+               signal_titles[signum],
+               getpid(),
+               execution_table.size);
+
+       BC_Signals::kill_subs();
+       BC_Signals::dump_traces();
+       BC_Signals::dump_locks();
+       BC_Signals::dump_buffers();
+       BC_Signals::delete_temps();
+
+// Call user defined signal handler
+       BC_Signals::global_signals->signal_handler(signum);
+
+       abort();
+}
+
+static void signal_entry_recoverable(int signum)
+{
+       printf("signal_entry_recoverable: got %s my pid=%d\n",
+               signal_titles[signum],
+               getpid());
+}
+
+// used to terminate child processes when program terminates
+static void handle_exit(int signum)
+{
+//printf("child %d exit\n", getpid());
+       exit(0);
+}
+
+void BC_Signals::set_sighup_exit(int enable)
+{
+       if( enable ) {
+// causes SIGHUP to be generated when parent dies
+               signal(SIGHUP, handle_exit);
+               prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
+// prevents ^C from signalling child when attached to gdb
+               setpgid(0, 0);
+               if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
+       }
+       else {
+               signal(SIGHUP, signal_entry);
+               prctl(PR_SET_PDEATHSIG, 0,0,0,0);
+       }
+}
+
+BC_Signals::BC_Signals()
+{
+}
+
+void BC_Signals::dump_traces(FILE *fp)
+{
+// Dump trace table
+       if(execution_table.size)
+       {
+               for(int i = execution_table.current_value; i < execution_table.size; i++)
+                       fprintf(fp,"    %s\n", (char*)execution_table.values[i]);
+               for(int i = 0; i < execution_table.current_value; i++)
+                       fprintf(fp,"    %s\n", (char*)execution_table.values[i]);
+       }
+
+}
+
+void BC_Signals::dump_locks(FILE *fp)
+{
+// Dump lock table
+#ifdef TRACE_LOCKS
+       fprintf(fp,"signal_entry: lock table size=%d\n", lock_table.size);
+       for(int i = 0; i < lock_table.size; i++)
+       {
+               bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
+               fprintf(fp,"    %p %s %s %p%s\n", table->ptr,
+                       table->title, table->location, (void*)table->tid,
+                       table->is_owner ? " *" : "");
+       }
+#endif
+}
+
+void BC_Signals::dump_buffers(FILE *fp)
+{
+#ifdef TRACE_MEMORY
+       pthread_mutex_lock(lock);
+// Dump buffer table
+       fprintf(fp,"BC_Signals::dump_buffers: buffer table size=%d\n", memory_table.size);
+       for(int i = 0; i < memory_table.size; i++)
+       {
+               bc_buffertrace_t *entry = (bc_buffertrace_t*)memory_table.values[i];
+               fprintf(fp,"    %d %p %s\n", entry->size, entry->ptr, entry->location);
+       }
+       pthread_mutex_unlock(lock);
+#endif
+}
+
+void BC_Signals::delete_temps()
+{
+       pthread_mutex_lock(lock);
+       if(temp_files.size) printf("BC_Signals::delete_temps: deleting %d temp files\n", temp_files.size);
+       for(int i = 0; i < temp_files.size; i++)
+       {
+               printf("    %s\n", (char*)temp_files.values[i]);
+               remove((char*)temp_files.values[i]);
+       }
+       pthread_mutex_unlock(lock);
+}
+
+void BC_Signals::reset_locks()
+{
+       pthread_mutex_unlock(lock);
+}
+
+void BC_Signals::set_temp(char *string)
+{
+       char *new_string = strdup(string);
+       append_table(&temp_files, new_string);
+}
+
+void BC_Signals::unset_temp(char *string)
+{
+       for(int i = 0; i < temp_files.size; i++)
+       {
+               if(!strcmp((char*)temp_files.values[i], string))
+               {
+                       clear_table_entry(&temp_files, i, 1);
+                       break;
+               }
+       }
+}
+
+
+int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
+{
+       char string[1024];
+       XGetErrorText(event->display, event->error_code, string, 1024);
+       fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d %s\n",
+               event->error_code, event->request_code, event->minor_code, string);
+       return 0;
+}
+
+
+void BC_Signals::initialize()
+{
+       BC_Signals::global_signals = this;
+       lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
+       handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
+       pthread_mutex_init(lock, 0);
+       pthread_mutex_init(handler_lock, 0);
+       old_err_handler = XSetErrorHandler(x_error_handler);
+       initialize2();
+}
+
+void BC_Signals::terminate()
+{
+       BC_Signals::global_signals = 0;
+       uncatch_segv();  uncatch_intr();
+       signal(SIGHUP, SIG_DFL);
+       signal(SIGINT, SIG_DFL);
+       signal(SIGQUIT, SIG_DFL);
+       signal(SIGTERM, SIG_DFL);
+       signal(SIGFPE, SIG_DFL);
+       signal(SIGPIPE, SIG_DFL);
+       signal(SIGUSR2, SIG_DFL);
+       XSetErrorHandler(old_err_handler);
+}
+
+// callable from debugger
+extern "C"
+void dump()
+{
+       BC_Signals::dump_traces();
+       BC_Signals::dump_locks();
+       BC_Signals::dump_buffers();
+}
+
+// kill SIGUSR2
+void BC_Signals::signal_dump(int signum)
+{
+       BC_KeyboardHandler::kill_grabs();
+       dump();
+       signal(SIGUSR2, signal_dump);
+}
+
+
+
+
+
+
+void BC_Signals::initialize2()
+{
+       signal(SIGHUP, signal_entry);
+       signal(SIGINT, signal_entry);
+       signal(SIGQUIT, signal_entry);
+       // SIGKILL cannot be stopped
+       // signal(SIGKILL, signal_entry);
+       catch_segv();
+       signal(SIGTERM, signal_entry);
+       signal(SIGFPE, signal_entry);
+       signal(SIGPIPE, signal_entry_recoverable);
+       signal(SIGUSR2, signal_dump);
+}
+
+
+void BC_Signals::signal_handler(int signum)
+{
+printf("BC_Signals::signal_handler\n");
+//     exit(0);
+}
+
+const char* BC_Signals::sig_to_str(int number)
+{
+       return signal_titles[number];
+}
+
+#define TOTAL_TRACES 16
+
+void BC_Signals::new_trace(const char *text)
+{
+       if(!global_signals) return;
+       pthread_mutex_lock(lock);
+
+// Wrap around
+       if(execution_table.size >= TOTAL_TRACES)
+       {
+               overwrite_table(&execution_table, strdup(text));
+//             clear_table(&execution_table, 1);
+       }
+       else
+       {
+               append_table(&execution_table, strdup(text));
+       }
+       pthread_mutex_unlock(lock);
+}
+
+void BC_Signals::new_trace(const char *file, const char *function, int line)
+{
+       char string[BCTEXTLEN];
+       snprintf(string, BCTEXTLEN, "%s: %s: %d", file, function, line);
+       new_trace(string);
+}
+
+void BC_Signals::delete_traces()
+{
+       if(!global_signals) return;
+       pthread_mutex_lock(lock);
+       clear_table(&execution_table, 0);
+       pthread_mutex_unlock(lock);
+}
+
+// no canceling with lock held
+void BC_Signals::lock_locks(const char *s)
+{
+       pthread_mutex_lock(lock);
+       last_lock_thread = pthread_self();
+       last_lock_title = s;
+       last_lock_location = 0;
+}
+
+void BC_Signals::unlock_locks()
+{
+       pthread_mutex_unlock(lock);
+}
+
+#define TOTAL_LOCKS 256
+
+int BC_Signals::set_lock(void *ptr,
+       const char *title,
+       const char *location)
+{
+       if(!global_signals) return 0;
+       bc_locktrace_t *table = 0;
+       int id_return = 0;
+
+       pthread_mutex_lock(lock);
+       last_lock_thread = pthread_self();
+       last_lock_title = title;
+       last_lock_location = location;
+       if(lock_table.size >= TOTAL_LOCKS)
+               clear_table(&lock_table, 0);
+
+// Put new lock entry
+       table = new_bc_locktrace(ptr, title, location);
+       append_table(&lock_table, table);
+       id_return = table->id;
+
+       pthread_mutex_unlock(lock);
+       return id_return;
+}
+
+void BC_Signals::set_lock2(int table_id)
+{
+       if(!global_signals) return;
+
+       bc_locktrace_t *table = 0;
+       pthread_mutex_lock(lock);
+       for(int i = lock_table.size - 1; i >= 0; i--)
+       {
+               table = (bc_locktrace_t*)lock_table.values[i];
+// Got it.  Hasn't been unlocked/deleted yet.
+               if(table->id == table_id)
+               {
+                       table->is_owner = 1;
+                       table->tid = pthread_self();
+                       pthread_mutex_unlock(lock);
+                       return;
+               }
+       }
+       pthread_mutex_unlock(lock);
+}
+
+void BC_Signals::unset_lock2(int table_id)
+{
+       if(!global_signals) return;
+
+       bc_locktrace_t *table = 0;
+       pthread_mutex_lock(lock);
+       for(int i = lock_table.size - 1; i >= 0; i--)
+       {
+               table = (bc_locktrace_t*)lock_table.values[i];
+               if(table->id == table_id)
+               {
+                       clear_table_entry(&lock_table, i, 1);
+                       break;
+               }
+       }
+       pthread_mutex_unlock(lock);
+}
+
+void BC_Signals::unset_lock(void *ptr)
+{
+       if(!global_signals) return;
+
+       bc_locktrace_t *table = 0;
+       pthread_mutex_lock(lock);
+
+// Take off currently held entry
+       for(int i = 0; i < lock_table.size; i++)
+       {
+               table = (bc_locktrace_t*)lock_table.values[i];
+               if(table->ptr == ptr)
+               {
+                       if(table->is_owner)
+                       {
+                               clear_table_entry(&lock_table, i, 1);
+                               break;
+                       }
+               }
+       }
+
+       pthread_mutex_unlock(lock);
+}
+
+
+void BC_Signals::unset_all_locks(void *ptr)
+{
+       if(!global_signals) return;
+       pthread_mutex_lock(lock);
+// Take off previous lock entry
+       for(int i = 0; i < lock_table.size; )
+       {
+               bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
+               if(table->ptr == ptr)
+               {
+                       clear_table_entry(&lock_table, i, 1);
+                       continue;
+               }
+               ++i;
+       }
+       pthread_mutex_unlock(lock);
+}
+
+void BC_Signals::clear_locks_tid(pthread_t tid)
+{
+       if(!global_signals) return;
+       pthread_mutex_lock(lock);
+// Take off previous lock entry
+       for(int i = 0; i < lock_table.size; )
+       {
+               bc_locktrace_t *table = (bc_locktrace_t*)lock_table.values[i];
+               if(table->tid == tid)
+               {
+                       clear_table_entry(&lock_table, i, 1);
+                       continue;
+               }
+               ++i;
+       }
+       pthread_mutex_unlock(lock);
+}
+
+
+void BC_Signals::enable_memory()
+{
+       trace_memory = 1;
+}
+
+void BC_Signals::disable_memory()
+{
+       trace_memory = 0;
+}
+
+
+void BC_Signals::set_buffer(int size, void *ptr, const char* location)
+{
+       if(!global_signals) return;
+       if(!trace_memory) return;
+
+//printf("BC_Signals::set_buffer %p %s\n", ptr, location);
+       pthread_mutex_lock(lock);
+       append_table(&memory_table, new_bc_buffertrace(size, ptr, location));
+       pthread_mutex_unlock(lock);
+}
+
+int BC_Signals::unset_buffer(void *ptr)
+{
+       if(!global_signals) return 0;
+       if(!trace_memory) return 0;
+
+       int ret = 1;
+       pthread_mutex_lock(lock);
+       for(int i = 0; i < memory_table.size; i++)
+       {
+               if(((bc_buffertrace_t*)memory_table.values[i])->ptr == ptr)
+               {
+//printf("BC_Signals::unset_buffer %p\n", ptr);
+                       clear_table_entry(&memory_table, i, 1);
+                       ret = 0;
+                       break;
+               }
+       }
+
+       pthread_mutex_unlock(lock);
+//     fprintf(stderr, "BC_Signals::unset_buffer buffer %p not found.\n", ptr);
+       return ret;
+}
+
+
+#include <ucontext.h>
+#include <sys/wait.h>
+#include "thread.h"
+
+#if __i386__
+#define IP eip
+#endif
+#if __x86_64__
+#define IP rip
+#endif
+#ifndef IP
+#error gotta have IP
+#endif
+
+
+static void handle_dump(int n, siginfo_t * info, void *sc)
+{
+       uncatch_segv();  uncatch_intr();
+       signal(SIGSEGV, SIG_DFL);
+       signal(SIGINT, SIG_DFL);
+       // gotta be root, or the dump is worthless
+       int uid = getuid();
+       if( uid != 0 ) return;
+       ucontext_t *uc = (ucontext_t *)sc;
+       int pid = getpid(), tid = gettid();
+       struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
+       fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
+               n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
+               (void*)c->IP, pid, tid);
+       FILE *fp = 0;
+       char fn[PATH_MAX];
+       if( BC_Signals::trap_path ) {
+               snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
+               fp = fopen(fn,"w");
+       }
+       if( fp ) {
+               fprintf(stderr,"writing debug data to %s\n", fn);
+               fprintf(fp,"** %s at %p in pid %d, tid %d\n",
+                       n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
+                       (void*)c->IP, pid, tid);
+       }
+       else {
+               strcpy(fn, "stdout");
+               fp = stdout;
+       }
+       time_t t;  time(&t);
+       fprintf(fp,"created on %s", ctime(&t));
+       struct passwd *pw = getpwuid(uid);
+       if( pw ) {
+               fprintf(fp,"        by %d:%d %s(%s)\n",
+                       pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
+       }
+       fprintf(fp,"\nTHREADS:\n");  Thread::dump_threads(fp);
+       fprintf(fp,"\nTRACES:\n");   BC_Signals::dump_traces(fp);
+       fprintf(fp,"\nLOCKS:\n");    BC_Signals::dump_locks(fp);
+       fprintf(fp,"\nBUFFERS:\n");  BC_Signals::dump_buffers(fp);
+       if( BC_Signals::trap_hook ) {
+               fprintf(fp,"\nMAIN HOOK:\n");
+               BC_Signals::trap_hook(fp, BC_Signals::trap_data);
+       }
+       fprintf(fp,"\nVERSION:\n");  bc_copy_textfile(fp,"/proc/version");
+       fprintf(fp,"\nMEMINFO:\n");  bc_copy_textfile(fp,"/proc/meminfo");
+       fprintf(fp,"\nMAPS:\n");     bc_copy_textfile(fp,"/proc/%d/maps",pid);
+       fprintf(fp,"\n\n");
+       if( fp != stdout ) fclose(fp);
+       char cmd[1024], *cp = cmd;
+       cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
+               "-ex \"thread apply all info registers\" "
+               "-ex \"thread apply all bt full\" "
+               "-ex \"quit\"", pid, pid);
+       if( fp != stdout )
+               cp += sprintf(cp," >> \"%s\"", fn);
+       cp += sprintf(cp," 2>&1");
+//printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
+        pid = vfork();
+        if( pid < 0 ) {
+               fprintf(stderr,"** can't start gdb, dump abondoned\n");
+               return;
+       }
+       if( pid > 0 ) {
+               waitpid(pid,0,0);
+               fprintf(stderr,"** dump complete\n");
+               return;
+       }
+        char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
+        execvp(argv[0], &argv[0]);
+}
+
+
+
+
+
+#ifdef TRACE_MEMORY
+
+// void* operator new(size_t size)
+// {
+// //printf("new 1 %d\n", size);
+//     void *result = malloc(size);
+//     BUFFER(size, result, "new");
+// //printf("new 2 %d\n", size);
+//     return result;
+// }
+//
+// void* operator new[](size_t size)
+// {
+// //printf("new [] 1 %d\n", size);
+//     void *result = malloc(size);
+//     BUFFER(size, result, "new []");
+// //printf("new [] 2 %d\n", size);
+//     return result;
+// }
+//
+// void operator delete(void *ptr)
+// {
+// //printf("delete 1 %p\n", ptr);
+//     UNBUFFER(ptr);
+// //printf("delete 2 %p\n", ptr);
+//     free(ptr);
+// }
+//
+// void operator delete[](void *ptr)
+// {
+// //printf("delete [] 1 %p\n", ptr);
+//     UNBUFFER(ptr);
+//     free(ptr);
+// //printf("delete [] 2 %p\n", ptr);
+// }
+
+
+#endif