/[svn]/linuxsampler/trunk/src/common/stacktrace.c
ViewVC logotype

Contents of /linuxsampler/trunk/src/common/stacktrace.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 270 - (show annotations) (download)
Fri Oct 8 20:30:25 2004 UTC (19 years, 6 months ago) by schoenebeck
File MIME type: text/plain
File size: 18152 byte(s)
stacktrace routines by Bjorn Reese, only slightly modified:
- header file adjusted to work with C++ applications
- deactivated separated piping of debugger output, as it didn't work out
  correctly in multithreaded application

1 /*************************************************************************
2 *
3 * $Id: stacktrace.c,v 1.1 2004-10-08 20:30:25 schoenebeck Exp $
4 *
5 * Copyright (c) 1998 by Bjorn Reese <breese@mail1.stofanet.dk>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
12 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
13 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
14 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
15 *
16 ************************************************************************
17 *
18 * 1999/08/19 - breese
19 * - Cleaned up the interface
20 *
21 * 1999/08/06 - breese
22 * - Added U_STACK_TRACE for HP/UX
23 *
24 * 1999/01/24 - breese
25 * - Added GCC_DumpStack
26 *
27 * 1998/12/21 - breese
28 * - Fixed include files and arguments for waitpid()
29 * - Made an AIX workaround for waitpid() exiting with EINTR
30 * - Added a missing 'quit' command for OSF dbx
31 *
32 ************************************************************************/
33
34 #if defined(unix) || defined(__unix) || defined(__xlC__)
35 # define PLATFORM_UNIX
36 #elif defined(WIN32) || defined(_WIN32)
37 # define PLATFORM_WIN32
38 #endif
39
40 #if defined(_AIX) || defined(__xlC__)
41 # define PLATFORM_AIX
42 #elif defined(__FreeBSD__)
43 # define PLATFORM_FREEBSD
44 #elif defined(hpux) || defined(__hpux) || defined(_HPUX_SOURCE)
45 # define PLATFORM_HPUX
46 #elif defined(sgi) || defined(mips) || defined(_SGI_SOURCE)
47 # define PLATFORM_IRIX
48 #elif defined(__osf__)
49 # define PLATFORM_OSF
50 #elif defined(M_I386) || defined(_SCO_DS) || defined(_SCO_C_DIALECT)
51 # define PLATFORM_SCO
52 #elif defined(sun) || defined(__sun__) || defined(__SUNPRO_C)
53 # if defined(__SVR4) || defined(__svr4__)
54 # define PLATFORM_SOLARIS
55 # endif
56 #endif
57
58 /* ANSI C includes */
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <stdarg.h>
62 #include <string.h>
63 #include <limits.h>
64 #include <errno.h>
65 #include <signal.h>
66 #if defined(PLATFORM_UNIX)
67 # include <unistd.h>
68 # include <sys/types.h>
69 # include <sys/wait.h>
70 # if defined(PLATFORM_IRIX) && defined(USE_BUILTIN)
71 /* Compile with -DUSE_BUILTIN and -lexc */
72 # include <libexc.h>
73 # elif defined(PLATFORM_HPUX) && defined(USE_BUILTIN)
74 /* Compile with -DUSE_BUILTIN and -lcl */
75 extern void U_STACK_TRACE(void);
76 # endif
77 #endif
78
79 #include "stacktrace.h"
80
81 #ifndef FALSE
82 # define FALSE (0 == 1)
83 # define TRUE (! FALSE)
84 #endif
85
86 #define SYS_ERROR -1
87
88 #ifndef EXIT_SUCCESS
89 # define EXIT_SUCCESS 0
90 #endif
91 #ifndef EXIT_FAILURE
92 # define EXIT_FAILURE 1
93 #endif
94
95 #define MAX_BUFFER_SIZE 512
96 #if defined(__GNUC__)
97 /* Change the code if ADDRESSLIST_SIZE is increased */
98 # define ADDRESSLIST_SIZE 20
99 #endif
100
101 /*************************************************************************
102 * Globals
103 *
104 * We cannot pass custom arguments to signal handlers so we store
105 * them as global variables (but limit their scope to this file.)
106 */
107 static const char *global_progname;
108 static int global_output = STDOUT_FILENO;
109
110
111 #if defined(PLATFORM_UNIX)
112 /*************************************************************************
113 * my_pclose [private]
114 */
115 static void my_pclose(int fd, int pid)
116 {
117 close(fd);
118 /* Make sure the the child process has terminated */
119 (void)kill(pid, SIGTERM);
120 }
121
122 /*************************************************************************
123 * my_popen [private]
124 */
125 static int my_popen(const char *command, pid_t *pid)
126 {
127 int rc;
128 //int pipefd[2];
129
130 //FIXME: deactivated separate piping of debugger output, as it caused problems in conjunction with threads
131 //rc = pipe(pipefd);
132 rc = SYS_ERROR;
133 //if (SYS_ERROR != rc)
134 {
135 *pid = fork();
136 switch (*pid)
137 {
138 case SYS_ERROR:
139 //rc = SYS_ERROR;
140 //close(pipefd[0]);
141 //close(pipefd[1]);
142 break;
143
144 case 0: /* Child */
145 //close(pipefd[0]);
146 //close(STDOUT_FILENO);
147 //close(STDERR_FILENO);
148 //dup2(pipefd[1], STDOUT_FILENO);
149 //dup2(pipefd[1], STDERR_FILENO);
150 /*
151 * The System() call assumes that /bin/sh is
152 * always available, and so will we.
153 */
154 execl("/bin/sh", "/bin/sh", "-c", command, NULL);
155 _exit(EXIT_FAILURE);
156 break;
157
158 default: /* Parent */
159 //close(pipefd[1]);
160 //rc = pipefd[0];
161 break;
162 } /* switch */
163 }
164 return rc;
165 }
166
167 /*************************************************************************
168 * my_getline [private]
169 */
170 static int my_getline(int fd, char *buffer, int max)
171 {
172 char c;
173 int i = 0;
174
175 do {
176 if (read(fd, &c, 1) < 1)
177 return 0;
178 if (i < max)
179 buffer[i++] = c;
180 } while (c != '\n');
181 buffer[i] = (char)0;
182 return i;
183 }
184
185 /*************************************************************************
186 * GCC_DumpStack [private]
187 *
188 *
189 * This code is still experimental.
190 *
191 * Stackbased arrays are used to prevent allocating from the heap.
192 * Warning: sprintf/sscanf are not ASync safe. They were used for
193 * convenience.
194 *
195 * 'nm' is used because it is most widespread
196 * GNU: nm [-B]
197 * Solaris: nm -x -p
198 * IRIX: nm -x -B (but __builtin_return_address() always returns NULL)
199 * AIX: nm -x -B
200 * OSF/1: nm -B
201 * SCO/OpenServer: nm -x -p
202 * HP/UX nm -x -p
203 */
204
205 #if defined(__GNUC__) && defined(USE_BUILTIN)
206
207 typedef struct {
208 unsigned long realAddress;
209 unsigned long closestAddress;
210 char name[MAX_BUFFER_SIZE + 1];
211 char type;
212 } address_T;
213
214
215 static void GCC_DumpStack(void)
216 {
217 int i;
218 void *p = &p; /* dummy start value */
219 address_T syms[ADDRESSLIST_SIZE + 1];
220 char buffer[MAX_BUFFER_SIZE];
221 int fd;
222 pid_t pid;
223 unsigned long addr;
224 unsigned long highestAddress;
225 unsigned long lowestAddress;
226 char type;
227 char *pname;
228 char name[MAX_BUFFER_SIZE];
229 int number;
230
231 for (i = 0; p; i++)
232 {
233 /*
234 * This is based on code by Steve Coleman <steve.colemanjhuapl.edu>
235 *
236 * __builtin_return_address() only accepts a constant as argument.
237 */
238 switch (i)
239 {
240 case 0:
241 p = __builtin_return_address(0);
242 break;
243 case 1:
244 p = __builtin_return_address(1);
245 break;
246 case 2:
247 p = __builtin_return_address(2);
248 break;
249 case 3:
250 p = __builtin_return_address(3);
251 break;
252 case 4:
253 p = __builtin_return_address(4);
254 break;
255 case 5:
256 p = __builtin_return_address(5);
257 break;
258 case 6:
259 p = __builtin_return_address(6);
260 break;
261 case 7:
262 p = __builtin_return_address(7);
263 break;
264 case 8:
265 p = __builtin_return_address(8);
266 break;
267 case 9:
268 p = __builtin_return_address(9);
269 break;
270 case 10:
271 p = __builtin_return_address(10);
272 break;
273 case 11:
274 p = __builtin_return_address(11);
275 break;
276 case 12:
277 p = __builtin_return_address(12);
278 break;
279 case 13:
280 p = __builtin_return_address(13);
281 break;
282 case 14:
283 p = __builtin_return_address(14);
284 break;
285 case 15:
286 p = __builtin_return_address(15);
287 break;
288 case 16:
289 p = __builtin_return_address(16);
290 break;
291 case 17:
292 p = __builtin_return_address(17);
293 break;
294 case 18:
295 p = __builtin_return_address(18);
296 break;
297 case 19:
298 p = __builtin_return_address(19);
299 break;
300 default:
301 /* Change ADDRESSLIST_SIZE if more are added */
302 p = NULL;
303 break;
304 }
305 if ((p) && (i < ADDRESSLIST_SIZE))
306 {
307 syms[i].realAddress = (unsigned long)p;
308 syms[i].closestAddress = 0;
309 syms[i].name[0] = (char)0;
310 syms[i].type = ' ';
311 }
312 else
313 {
314 syms[i].realAddress = 0;
315 break; /* for */
316 }
317 } /* for */
318
319
320 /* First find out if we are using GNU or vendor nm */
321 number = 0;
322 strcpy(buffer, "nm -V 2>/dev/null | grep GNU | wc -l");
323 fd = my_popen(buffer, &pid);
324 if (SYS_ERROR != fd)
325 {
326 if (my_getline(fd, buffer, sizeof(buffer)))
327 {
328 sscanf(buffer, "%d", &number);
329 }
330 my_pclose(fd, pid);
331 }
332 if (number == 0) /* vendor nm */
333 {
334 # if defined(PLATFORM_SOLARIS) || defined(PLATFORM_SCO) || defined(PLATFORM_HPUX)
335 strcpy(buffer, "nm -x -p ");
336 # elif defined(PLATFORM_AIX) || defined(PLATFORM_IRIX) || defined(PLATFORM_OSF)
337 strcpy(buffer, "nm -x -B ");
338 # else
339 strcpy(buffer, "nm -B ");
340 # endif
341 }
342 else /* GNU nm */
343 strcpy(buffer, "nm -B ");
344 strcat(buffer, global_progname);
345
346 lowestAddress = ULONG_MAX;
347 highestAddress = 0;
348 fd = my_popen(buffer, &pid);
349 if (SYS_ERROR != fd)
350 {
351 while (my_getline(fd, buffer, sizeof(buffer)))
352 {
353 if (buffer[0] == '\n')
354 continue;
355 if (3 == sscanf(buffer, "%lx %c %s", &addr, &type, name))
356 {
357 if ((type == 't') || type == 'T')
358 {
359 if (addr == 0)
360 continue; /* while */
361 if (addr < lowestAddress)
362 lowestAddress = addr;
363 if (addr > highestAddress)
364 highestAddress = addr;
365 for (i = 0; syms[i].realAddress != 0; i++)
366 {
367 if ((addr <= syms[i].realAddress) &&
368 (addr > syms[i].closestAddress))
369 {
370 syms[i].closestAddress = addr;
371 strncpy(syms[i].name, name, MAX_BUFFER_SIZE);
372 syms[i].name[MAX_BUFFER_SIZE] = (char)0;
373 syms[i].type = type;
374 }
375 }
376 }
377 }
378 }
379 my_pclose(fd, pid);
380
381 for (i = 0; syms[i].realAddress != 0; i++)
382 {
383 if ((syms[i].name[0] == (char)0) ||
384 (syms[i].realAddress <= lowestAddress) ||
385 (syms[i].realAddress >= highestAddress))
386 {
387 sprintf(buffer, "[%d] 0x%08lx ???\n", i, syms[i].realAddress);
388 }
389 else
390 {
391 sprintf(buffer, "[%d] 0x%08lx <%s + 0x%lx> %c\n",
392 i,
393 syms[i].realAddress,
394 syms[i].name,
395 syms[i].realAddress - syms[i].closestAddress,
396 syms[i].type);
397 }
398 write(global_output, buffer, strlen(buffer));
399 }
400 }
401 }
402 #endif
403
404 /*************************************************************************
405 * DumpStack [private]
406 */
407 static int DumpStack(char *format, ...)
408 {
409 int gotSomething = FALSE;
410 int fd;
411 pid_t pid;
412 int status = EXIT_FAILURE;
413 int rc;
414 va_list args;
415 char *buffer;
416 char cmd[MAX_BUFFER_SIZE];
417 char buf[MAX_BUFFER_SIZE];
418
419 /*
420 * Please note that vsprintf() is not ASync safe (ie. cannot safely
421 * be used from a signal handler.) If this proves to be a problem
422 * then the cmd string can be built by more basic functions such as
423 * strcpy, strcat, and a homemade integer-to-ascii function.
424 */
425 va_start(args, format);
426 vsprintf(cmd, format, args);
427 va_end(args);
428
429 fd = my_popen(cmd, &pid);
430 if (SYS_ERROR != fd)
431 {
432 /*
433 * Wait for the child to exit. This must be done
434 * to make the debugger attach successfully.
435 * The output from the debugger is buffered on
436 * the pipe.
437 *
438 * AIX needs the looping hack
439 */
440 do
441 {
442 rc = waitpid(pid, &status, 0);
443 }
444 while ((SYS_ERROR == rc) && (EINTR == errno));
445
446 if ((WIFEXITED(status)) && (WEXITSTATUS(status) == EXIT_SUCCESS))
447 {
448 while (my_getline(fd, buf, sizeof(buf)))
449 {
450 buffer = buf;
451 if (! gotSomething)
452 {
453 write(global_output, "Output from ",
454 strlen("Output from "));
455 strtok(cmd, " ");
456 write(global_output, cmd, strlen(cmd));
457 write(global_output, "\n", strlen("\n"));
458 gotSomething = TRUE;
459 }
460 if ('\n' == buf[strlen(buf)-1])
461 {
462 buf[strlen(buf)-1] = (char)0;
463 }
464 write(global_output, buffer, strlen(buffer));
465 write(global_output, "\n", strlen("\n"));
466 }
467 }
468 my_pclose(fd, pid);
469 }
470 return gotSomething;
471 }
472 #endif /* PLATFORM_UNIX */
473
474 /*************************************************************************
475 * StackTrace
476 */
477 void StackTrace(void)
478 {
479 #if defined(PLATFORM_UNIX)
480 /*
481 * In general dbx seems to do a better job than gdb.
482 *
483 * Different dbx implementations require different flags/commands.
484 */
485
486 # if defined(PLATFORM_AIX)
487
488 if (DumpStack("dbx -a %d 2>/dev/null <<EOF\n"
489 "where\n"
490 "detach\n"
491 "EOF\n",
492 (int)getpid()))
493 return;
494
495 if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
496 "set prompt\n"
497 "where\n"
498 "detach\n"
499 "quit\n"
500 "EOF\n",
501 global_progname, (int)getpid()))
502 return;
503
504 # elif defined(PLATFORM_FREEBSD)
505
506 /*
507 * FreeBSD insists on sending a SIGSTOP to the process we
508 * attach to, so we let the debugger send a SIGCONT to that
509 * process after we have detached.
510 */
511 if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
512 "set prompt\n"
513 "where\n"
514 "detach\n"
515 "shell kill -CONT %d\n"
516 "quit\n"
517 "EOF\n",
518 global_progname, (int)getpid(), (int)getpid()))
519 return;
520
521 # elif defined(PLATFORM_HPUX)
522
523 /*
524 * HP decided to call their debugger xdb.
525 *
526 * This does not seem to work properly yet. The debugger says
527 * "Note: Stack traces may not be possible until you are
528 * stopped in user code." on HP-UX 09.01
529 *
530 * -L = line-oriented interface.
531 * "T [depth]" gives a stacktrace with local variables.
532 * The final "y" is confirmation to the quit command.
533 */
534
535 if (DumpStack("xdb -P %d -L %s 2>&1 <<EOF\n"
536 "T 50\n"
537 "q\ny\n"
538 "EOF\n",
539 (int)getpid(), global_progname))
540 return;
541
542 if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
543 "set prompt\n"
544 "where\n"
545 "detach\n"
546 "quit\n"
547 "EOF\n",
548 global_progname, (int)getpid()))
549 return;
550
551 # if defined(PLATFORM_HPUX) && defined(USE_BUILTIN)
552 U_STACK_TRACE();
553 return;
554 # endif
555
556 # elif defined(PLATFORM_IRIX)
557
558 /*
559 * "set $page=0" drops hold mode
560 * "dump ." displays the contents of the variables
561 */
562 if (DumpStack("dbx -p %d 2>/dev/null <<EOF\n"
563 "set \\$page=0\n"
564 "where\n"
565 # if !defined(__GNUC__)
566 /* gcc does not generate this information */
567 "dump .\n"
568 # endif
569 "detach\n"
570 "EOF\n",
571 (int)getpid()))
572 return;
573
574 # if defined(USE_BUILTIN)
575 if (trace_back_stack_and_print())
576 return;
577 # endif
578
579 if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
580 "set prompt\n"
581 "echo --- Stacktrace\\n\n"
582 "where\n"
583 "echo --- Symbols\\n\n"
584 "frame 5\n" /* Skip signal handler frames */
585 "set \\$x = 50\n"
586 "while (\\$x)\n" /* Print local variables for each frame */
587 "info locals\n"
588 "up\n"
589 "set \\$x--\n"
590 "end\n"
591 "echo ---\\n\n"
592 "detach\n"
593 "quit\n"
594 "EOF\n",
595 global_progname, (int)getpid()))
596 return;
597
598 # elif defined(PLATFORM_OSF)
599
600 if (DumpStack("dbx -pid %d %s 2>/dev/null <<EOF\n"
601 "where\n"
602 "detach\n"
603 "quit\n"
604 "EOF\n",
605 (int)getpid(), global_progname))
606 return;
607
608 if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
609 "set prompt\n"
610 "where\n"
611 "detach\n"
612 "quit\n"
613 "EOF\n",
614 global_progname, (int)getpid()))
615 return;
616
617 # elif defined(PLATFORM_SCO)
618
619 /*
620 * SCO OpenServer dbx is like a catch-22. The 'detach' command
621 * depends on whether ptrace(S) support detaching or not. If it
622 * is supported then 'detach' must be used, otherwise the process
623 * will be killed upon dbx exit. If it isn't supported then 'detach'
624 * will cause the process to be killed. We do not want it to be
625 * killed.
626 *
627 * Out of two evils, the omission of 'detach' was chosen because
628 * it worked on our system.
629 */
630 if (DumpStack("dbx %s %d 2>/dev/null <<EOF\n"
631 "where\n"
632 "quit\nEOF\n",
633 global_progname, (int)getpid()))
634 return;
635
636 if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
637 "set prompt\n"
638 "where\n"
639 "detach\n"
640 "quit\n"
641 "EOF\n",
642 global_progname, (int)getpid()))
643 return;
644
645 # elif defined(PLATFORM_SOLARIS)
646
647 if (DumpStack("dbx %s %d 2>/dev/null <<EOF\n"
648 "where\n"
649 "detach\n"
650 "EOF\n",
651 global_progname, (int)getpid()))
652 return;
653
654 if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
655 "set prompt\n"
656 "echo --- Stacktrace\\n\n"
657 "where\n"
658 "echo --- Symbols\\n\n"
659 "frame 5\n" /* Skip signal handler frames */
660 "set \\$x = 50\n"
661 "while (\\$x)\n" /* Print local variables for each frame */
662 "info locals\n"
663 "up\n"
664 "set \\$x--\n"
665 "end\n"
666 "echo ---\\n\n"
667 "detach\n"
668 "quit\n"
669 "EOF\n",
670 global_progname, (int)getpid()))
671 return;
672
673 if (DumpStack("/usr/proc/bin/pstack %d",
674 (int)getpid()))
675 return;
676
677 /*
678 * Other Unices (AIX, HPUX, SCO) also have adb, but
679 * they seem unable to attach to a running process.
680 */
681 if (DumpStack("adb %s 2>&1 <<EOF\n"
682 "0t%d:A\n" /* Attach to pid */
683 "\\$c\n" /* print stacktrace */
684 ":R\n" /* Detach */
685 "\\$q\n" /* Quit */
686 "EOF\n",
687 global_progname, (int)getpid()))
688 return;
689
690 # else /* All other Unix platforms */
691
692 /*
693 * TODO: SCO/UnixWare 7 must be something like (not tested)
694 * debug -i c <pid> <<EOF\nstack -f 4\nquit\nEOF\n
695 */
696
697 # if !defined(__GNUC__)
698 if (DumpStack("dbx %s %d 2>/dev/null <<EOF\n"
699 "where\n"
700 "detach\n"
701 "EOF\n",
702 global_progname, (int)getpid()))
703 return;
704 # endif
705
706 if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
707 "set prompt\n"
708 "echo --- Stacktrace\\n\n"
709 "where\n"
710 "echo --- Symbols\\n\n"
711 "frame 4\n"
712 "set \\$x = 50\n"
713 "while (\\$x)\n" /* Print local variables for each frame */
714 "info locals\n"
715 "up\n"
716 "set \\$x--\n"
717 "end\n"
718 "echo ---\\n\n"
719 "detach\n"
720 "quit\n"
721 "EOF\n",
722 global_progname, (int)getpid()))
723 return;
724
725 # endif
726
727 # if defined(__GNUC__) && defined(USE_BUILTIN)
728
729 GCC_DumpStack();
730
731 # endif
732
733 write(global_output,
734 "No debugger found\n", strlen("No debugger found\n"));
735
736 #elif defined(PLATFORM_WIN32)
737 /* Use StackWalk() */
738 #endif
739 }
740
741 /*************************************************************************
742 * StackTraceInit
743 */
744 void StackTraceInit(const char *in_name, int in_handle)
745 {
746 global_progname = in_name;
747 global_output = (in_handle == -1) ? STDOUT_FILENO : in_handle;
748 }
749
750
751 /*************************************************************************
752 * test
753 */
754 #if defined(STANDALONE)
755
756 void CrashHandler(int sig)
757 {
758 /* Reinstall default handler to prevent race conditions */
759 signal(sig, SIG_DFL);
760 /* Print the stack trace */
761 StackTrace();
762 /* And exit because we may have corrupted the internal
763 * memory allocation lists. Use abort() if we want to
764 * generate a core dump. */
765 _exit(EXIT_FAILURE);
766 }
767
768
769 void Crash(void)
770 {
771 /* Force a crash */
772 strcpy(NULL, "");
773 }
774
775 int main(int argc, char *argv[])
776 {
777 struct sigaction sact;
778
779 StackTraceInit(argv[0], -1);
780
781 sigemptyset(&sact.sa_mask);
782 sact.sa_flags = 0;
783 sact.sa_handler = CrashHandler;
784 sigaction(SIGSEGV, &sact, NULL);
785 sigaction(SIGBUS, &sact, NULL);
786
787 Crash();
788 return EXIT_SUCCESS;
789 }
790 #endif

  ViewVC Help
Powered by ViewVC