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

Contents of /linuxsampler/branches/release2_0_0/src/common/stacktrace.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2789 - (show annotations) (download)
Wed Jul 15 20:34:18 2015 UTC (8 years, 10 months ago) by schoenebeck
File MIME type: text/plain
File size: 18467 byte(s)
Created linuxsampler branch 'release2_0_0'.

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

  ViewVC Help
Powered by ViewVC