779d7e40922ff8919bc2e190ce2dea1fee967f20
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bcsignals.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "bcsignals.h"
23 #include "bcwindowbase.h"
24 #include "bccmodels.h"
25 #include "bckeyboard.h"
26 #include "bcresources.h"
27 #include "cstrdup.h"
28 #include "filesystem.h"
29
30 #include <ctype.h>
31 #include <dirent.h>
32 #ifndef NO_BTRACE
33 #include <execinfo.h>
34 #endif
35 #include <fcntl.h>
36 #include <pwd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <sys/ioctl.h>
43 #ifndef NO_PRCTL
44 #include <sys/prctl.h>
45 #endif
46 #include <sys/types.h>
47
48 BC_Signals* BC_Signals::global_signals = 0;
49 static int signal_done = 0;
50
51 static struct sigaction old_segv = {0, }, old_intr = {0, };
52 static void handle_dump(int n, siginfo_t * info, void *sc);
53
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;
59
60 static void uncatch_sig(int sig, struct sigaction &old)
61 {
62         struct sigaction act;
63         sigaction(sig, &old, &act);
64         old.sa_handler = 0;
65 }
66
67 static void catch_sig(int sig, struct sigaction &old)
68 {
69         struct sigaction act;
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));
74 }
75
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); }
80
81 void BC_Signals::set_trap_path(const char *path)
82 {
83         trap_path = path;
84 }
85
86 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
87 {
88         trap_data = data;
89         trap_hook = hook;
90 }
91
92 void BC_Signals::set_catch_segv(bool v) {
93         if( v == trap_sigsegv ) return;
94         if( v ) catch_segv();
95         else uncatch_segv();
96         trap_sigsegv = v;
97 }
98
99 void BC_Signals::set_catch_intr(bool v) {
100         if( v == trap_sigintr ) return;
101         if( v ) catch_intr();
102         else uncatch_intr();
103         trap_sigintr = v;
104 }
105
106 static void bc_copy_textfile(int lines, FILE *ofp, const char *fmt,...)
107 {
108         va_list ap;    va_start(ap, fmt);
109         char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
110         va_end(ap);
111         FILE *ifp = fopen(bfr,"r");
112         if( !ifp ) return;
113         while( --lines >= 0 && fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
114         fclose(ifp);
115 }
116
117 static void bc_list_openfiles(int lines, FILE *ofp, const char *fmt,...)
118 {
119         va_list ap;    va_start(ap, fmt);
120         char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
121         va_end(ap);
122         DIR *dir  = opendir(bfr);
123         if( !dir ) return;
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];
129                 struct stat st;
130                 snprintf(path, sizeof(path), "%s/%s", bfr, fn);
131                 if( !stat(path,&st) ) {
132                         int typ = 0;
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);
143                         if( len > 0 ) {
144                                 link[len] = 0;
145                                 fprintf(ofp, "\t-> %s", link);
146                         }
147                 }
148                 snprintf(path, sizeof(path), "%sinfo/%s", bfr, fn);
149                 FILE *fp = fopen(path,"r");  int64_t pos;
150                 if( fp ) {
151                         while( fgets(link, sizeof(link), fp) ) {
152                                 if( sscanf(link, "pos:%jd", &pos) == 1 ) {
153                                         fprintf(ofp, "\tpos: %jd", pos);
154                                         break;
155                                 }
156                         }
157                         fclose(fp);
158                 }
159                 fprintf(ofp, "\n");
160         }
161         closedir(dir);
162 }
163
164 // Can't use Mutex because it would be recursive
165 static pthread_mutex_t *handler_lock = 0;
166
167
168 static const char* signal_titles[] =
169 {
170         "NULL",
171         "SIGHUP",
172         "SIGINT",
173         "SIGQUIT",
174         "SIGILL",
175         "SIGTRAP",
176         "SIGABRT",
177         "SIGBUS",
178         "SIGFPE",
179         "SIGKILL",
180         "SIGUSR1",
181         "SIGSEGV",
182         "SIGUSR2",
183         "SIGPIPE",
184         "SIGALRM",
185         "SIGTERM"
186 };
187
188 void BC_Signals::dump_stack(FILE *fp)
189 {
190 #ifndef NO_BTRACE
191         void *buffer[256];
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++)
196         {
197                 fprintf(fp, "%s\n", result[i]);
198         }
199 #endif
200 }
201
202 // Kill subprocesses
203 void BC_Signals::kill_subs()
204 {
205 // List /proc directory
206         struct dirent64 *new_filename;
207         struct stat ostat;
208         char path[BCTEXTLEN], string[BCTEXTLEN];
209         DIR *dirstream = opendir("/proc");
210         if( !dirstream ) return;
211         pid_t ppid = getpid();
212
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
217                 if( *ptr ) continue;
218
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");
225                 if( !fd ) continue;
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;
233                 *ptr = 0;
234
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);
243                 }
244                 fclose(fd);
245         }
246 }
247
248 static void signal_entry(int signum)
249 {
250         signal(signum, SIG_DFL);
251
252         pthread_mutex_lock(handler_lock);
253         int done = signal_done;
254         signal_done = 1;
255         pthread_mutex_unlock(handler_lock);
256         if( done ) exit(0);
257
258         printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
259                 signal_titles[signum], getpid(), execution_table.size);
260
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();
266
267 // Call user defined signal handler
268         BC_Signals::global_signals->signal_handler(signum);
269
270         abort();
271 }
272
273 static void signal_entry_recoverable(int signum)
274 {
275         printf("signal_entry_recoverable: got %s my pid=%d\n",
276                 signal_titles[signum],
277                 getpid());
278 }
279
280 #ifndef NO_PRCTL
281 // used to terminate child processes when program terminates
282 static void handle_exit(int signum)
283 {
284 //printf("child %d exit\n", getpid());
285         exit(0);
286 }
287 #endif
288
289 void BC_Signals::set_sighup_exit(int enable)
290 {
291 #ifndef NO_PRCTL
292         if( 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
297                 setpgid(0, 0);
298                 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
299         }
300         else {
301                 signal(SIGHUP, signal_entry);
302                 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
303         }
304 #endif
305 }
306
307 BC_Signals::BC_Signals()
308 {
309 }
310 BC_Signals::~BC_Signals()
311 {
312   BC_CModels::bcxfer_stop_slicers();
313 }
314
315
316 int dump_xerror = -1;
317
318 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
319 {
320         char string[1024];
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;
328         }
329         if( dump_xerror )
330                 dump_stack();
331         return 0;
332 }
333
334
335 void BC_Signals::initialize(const char *trap_path)
336 {
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);
343         initialize2();
344 }
345
346 void BC_Signals::terminate()
347 {
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);
359 }
360
361 // kill SIGUSR2
362 void BC_Signals::signal_dump(int signum)
363 {
364         BC_KeyboardHandler::kill_grabs();
365         dump();
366         signal(SIGUSR2, signal_dump);
367 }
368
369
370
371
372
373
374 void BC_Signals::initialize2()
375 {
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);
381         catch_segv();
382         signal(SIGTERM, signal_entry);
383         signal(SIGFPE, signal_entry);
384         signal(SIGPIPE, signal_entry_recoverable);
385         signal(SIGUSR2, signal_dump);
386 }
387
388
389 void BC_Signals::signal_handler(int signum)
390 {
391 printf("BC_Signals::signal_handler\n");
392 //      exit(0);
393 }
394
395 const char* BC_Signals::sig_to_str(int number)
396 {
397         return signal_titles[number];
398 }
399
400
401 #ifndef NO_CTX
402 #include <ucontext.h>
403 #include <sys/wait.h>
404 #include "thread.h"
405
406 #if __i386__
407 #define IP eip
408 #define sigregs_t sigcontext
409
410 static void reg_dump(FILE *fp,sigregs_t *rp)
411 {
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);
434         fprintf(fp,"\n");
435 }
436 #endif
437
438 #if __x86_64__
439 #define IP rip
440 #define sigregs_t sigcontext
441
442 static void reg_dump(FILE *fp,sigregs_t *rp)
443 {
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);
470         fprintf(fp,"\n");
471 }
472
473 #endif
474
475 #if __powerpc__ || __powerpc64__ || __powerpc64le__
476 #include <asm/ptrace.h>
477 #define IP nip
478 #define sigregs_t pt_regs
479 static void reg_dump(FILE *fp,sigregs_t *rp) {}
480 #endif
481
482 #ifndef IP
483 #error gotta have IP
484 #endif
485
486 // HAVE_CTX
487 #endif
488
489 static void handle_dump(int n, siginfo_t * info, void *sc)
490 {
491         uncatch_segv();  uncatch_intr();
492         signal(SIGSEGV, SIG_DFL);
493         signal(SIGINT, SIG_DFL);
494         // gotta be root, or the dump is worthless
495         int uid = getuid();
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();
500         void *ip = 0;
501 #ifndef NO_CTX
502         ucontext_t *uc = (ucontext_t *)sc;
503         struct sigregs_t *c = (struct sigregs_t *)&uc->uc_mcontext;
504         ip = (void *)c->IP;
505 #endif
506         fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
507                 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
508                 ip, pid, tid);
509         FILE *fp = 0;
510         char fn[PATH_MAX];
511         if( BC_Signals::trap_path ) {
512                 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
513                 fp = fopen(fn,"w");
514         }
515         if( fp ) {
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",
519                         ip, pid, tid);
520         }
521         else {
522                 strcpy(fn, "stdout");
523                 fp = stdout;
524         }
525         time_t t;  time(&t);
526         fprintf(fp,"created on %s", ctime(&t));
527         struct passwd *pw = getpwuid(uid);
528         if( pw ) {
529                 fprintf(fp,"        by %d:%d %s(%s)\n",
530                         pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
531         }
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);
543         }
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);
549 #ifndef NO_CTX
550         char proc_mem[64];
551         if( tid > 0 && tid != pid )
552                 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
553         else
554                 sprintf(proc_mem,"/proc/%d/mem",pid);
555         int pfd = open(proc_mem,O_RDONLY);
556         if( pfd >= 0 ) {
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");
564                 }
565                 fprintf(fp,"\n");
566                 close(pfd);
567         }
568         else
569                 fprintf(fp,"err opening: %s, %m\n", proc_mem);
570         reg_dump(fp, c);
571 #endif
572         fprintf(fp,"\n\n");
573         if( fp != stdout ) fclose(fp);
574 #ifndef NO_GDB
575         char cmd[1024], *cp = cmd;
576         cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
577                 "-ex \"thread apply all info registers\" "
578                 "-ex \"thread apply all bt full\" "
579                 "-ex \"quit\"", pid, pid);
580         if( fp != stdout )
581                 cp += sprintf(cp," >> \"%s\"", fn);
582         cp += sprintf(cp," 2>&1");
583 //printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
584         pid = vfork();
585         if( pid < 0 ) {
586                 fprintf(stderr,"** can't start gdb, dump abondoned\n");
587                 return;
588         }
589         if( pid > 0 ) {
590                 waitpid(pid,0,0);
591                 fprintf(stderr,"** dump complete\n");
592                 return;
593         }
594         char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
595         execvp(argv[0], &argv[0]);
596 #endif
597 }
598