/[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 361 - (show annotations) (download)
Wed Feb 9 01:22:18 2005 UTC (19 years, 2 months ago) by schoenebeck
File MIME type: text/plain
File size: 18172 byte(s)
* bunch of fixes for OSX (patch by Stephane Letz)

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

  ViewVC Help
Powered by ViewVC