libs-gsweb/GSWeb.framework/stacktrace.m
David Ayers 4381de6b49 * GSWeb.framework/GSWUtils.h (NSTimeIntervalSleep): Declare.
* GSWeb.framework/GSWGenericContainer.m
        ([GSWGenericContainer takeValuesFromRequest:inContext:]): Don't
        try to return value in method returning void.
        * GSWeb.framework/GSWHyperlink.m
        ([GSWHyperlink invokeActionForRequest:inContext:]): Add cast
        to silence compiler on OS X.
        * GSWeb.framework/GSWResourceManager.m
        ([GSWResourceManager _applicationGSWBundle]): Remove obsolete
        logging.
        * GSWeb.framework/NSData+Compress.m ([NSData deflate]):
        Remove dependancy on GNUstep extenstions.
        * GSWeb.framework/stacktrace.m: Add test for __MACH__.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gsweb/trunk@18696 72102866-910b-0410-8b05-ffd578937521
2004-02-27 17:01:02 +00:00

855 lines
19 KiB
Objective-C

/*************************************************************************
*
* $Id$
*
* Copyright (c) 1998 by Bjorn Reese <breese@mail1.stofanet.dk>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
*
************************************************************************
*
* 1999/08/19 - breese
* - Cleaned up the interface
*
* 1999/08/06 - breese
* - Added U_STACK_TRACE for HP/UX
*
* 1999/01/24 - breese
* - Added GCC_DumpStack
*
* 1998/12/21 - breese
* - Fixed include files and arguments for waitpid()
* - Made an AIX workaround for waitpid() exiting with EINTR
* - Added a missing 'quit' command for OSF dbx
*
************************************************************************/
#define USE_BUILTIN
#if defined(unix) || defined(__unix) || defined(__xlC__) || defined(__NetBSD__)
# define PLATFORM_UNIX
#elif defined(WIN32) || defined(_WIN32)
# define PLATFORM_WIN32
#endif
#if defined(_AIX) || defined(__xlC__)
# define PLATFORM_AIX
#elif defined(__FreeBSD__)
# define PLATFORM_FREEBSD
#elif defined(hpux) || defined(__hpux) || defined(_HPUX_SOURCE)
# define PLATFORM_HPUX
#elif defined(sgi) || defined(mips) || defined(_SGI_SOURCE)
# define PLATFORM_IRIX
#elif defined(__osf__)
# define PLATFORM_OSF
#elif defined(M_I386) || defined(_SCO_DS) || defined(_SCO_C_DIALECT)
# define PLATFORM_SCO
#elif defined(sun) || defined(__sun__) || defined(__SUNPRO_C)
# if defined(__SVR4) || defined(__svr4__)
# define PLATFORM_SOLARIS
# endif
#elif defined(__MACH__)
# define PLATFORM_MACH
#endif
/* ANSI C includes */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <signal.h>
#if defined(PLATFORM_UNIX) || defined(PLATFORM_MACH)
# include <unistd.h>
# include <sys/types.h>
# include <sys/wait.h>
# if defined(PLATFORM_IRIX) && defined(USE_BUILTIN)
/* Compile with -DUSE_BUILTIN and -lexc */
# include <libexc.h>
# elif defined(PLATFORM_HPUX) && defined(USE_BUILTIN)
/* Compile with -DUSE_BUILTIN and -lcl */
extern void U_STACK_TRACE(void);
# endif
#endif
#include "stacktrace.h"
#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (! FALSE)
#endif
#define SYS_ERROR -1
#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif
#define MAX_BUFFER_SIZE 512
#if defined(__GNUC__)
/* Change the code if ADDRESSLIST_SIZE is increased */
# define ADDRESSLIST_SIZE 20
#endif
/*************************************************************************
* Globals
*
* We cannot pass custom arguments to signal handlers so we store
* them as global variables (but limit their scope to this file.)
*/
static const char *global_progname;
static int global_output = STDOUT_FILENO;
#if defined(PLATFORM_UNIX)
/*************************************************************************
* my_pclose [private]
*/
static void my_pclose(int fd, int pid)
{
close(fd);
/* Make sure the the child process has terminated */
(void)kill(pid, SIGTERM);
}
/*************************************************************************
* my_popen [private]
*/
static int my_popen(const char *command, pid_t *pid)
{
int rc;
int pipefd[2];
rc = pipe(pipefd);
if (SYS_ERROR != rc)
{
*pid = fork();
switch (*pid)
{
case SYS_ERROR:
rc = SYS_ERROR;
close(pipefd[0]);
close(pipefd[1]);
break;
case 0: /* Child */
close(pipefd[0]);
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup2(pipefd[1], STDOUT_FILENO);
dup2(pipefd[1], STDERR_FILENO);
/*
* The System() call assumes that /bin/sh is
* always available, and so will we.
*/
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
_exit(EXIT_FAILURE);
break;
default: /* Parent */
close(pipefd[1]);
rc = pipefd[0];
break;
} /* switch */
}
return rc;
}
/*************************************************************************
* my_getline [private]
*/
static int my_getline(int fd, char *buffer, int max)
{
char c;
int i = 0;
do {
if (read(fd, &c, 1) < 1)
return 0;
if (i < max)
buffer[i++] = c;
} while (c != '\n');
buffer[i] = (char)0;
return i;
}
/*************************************************************************
* GCC_DumpStack [private]
*
*
* This code is still experimental.
*
* Stackbased arrays are used to prevent allocating from the heap.
* Warning: sprintf/sscanf are not ASync safe. They were used for
* convenience.
*
* 'nm' is used because it is most widespread
* GNU: nm [-B]
* Solaris: nm -x -p
* IRIX: nm -x -B (but __builtin_return_address() always returns NULL)
* AIX: nm -x -B
* OSF/1: nm -B
* SCO/OpenServer: nm -x -p
* HP/UX nm -x -p
*/
#if defined(__GNUC__) && defined(USE_BUILTIN)
typedef struct {
unsigned long realAddress;
unsigned long closestAddress;
char name[MAX_BUFFER_SIZE + 1];
char type;
} address_T;
static void GCC_DumpStack(void)
{
int i;
void *p = &p; /* dummy start value */
address_T syms[ADDRESSLIST_SIZE + 1];
char buffer[MAX_BUFFER_SIZE];
int fd;
pid_t pid;
unsigned long addr;
unsigned long highestAddress;
unsigned long lowestAddress;
char type;
char *pname;
char name[MAX_BUFFER_SIZE];
int number;
write(global_output,
"GCC_DumpStack-001\n", strlen("GCC_DumpStack-001\n"));
for (i = 0; p; i++)
{
/*
* This is based on code by Steve Coleman <steve.colemanjhuapl.edu>
*
* __builtin_return_address() only accepts a constant as argument.
*/
switch (i)
{
case 0:
p = __builtin_return_address(0);
break;
case 1:
p = __builtin_return_address(1);
break;
case 2:
p = __builtin_return_address(2);
break;
case 3:
p = __builtin_return_address(3);
break;
case 4:
p = __builtin_return_address(4);
break;
case 5:
p = __builtin_return_address(5);
break;
case 6:
p = __builtin_return_address(6);
break;
case 7:
p = __builtin_return_address(7);
break;
case 8:
p = __builtin_return_address(8);
break;
case 9:
p = __builtin_return_address(9);
break;
case 10:
p = __builtin_return_address(10);
break;
case 11:
p = __builtin_return_address(11);
break;
case 12:
p = __builtin_return_address(12);
break;
case 13:
p = __builtin_return_address(13);
break;
case 14:
p = __builtin_return_address(14);
break;
case 15:
p = __builtin_return_address(15);
break;
case 16:
p = __builtin_return_address(16);
break;
case 17:
p = __builtin_return_address(17);
break;
case 18:
p = __builtin_return_address(18);
break;
case 19:
p = __builtin_return_address(19);
break;
default:
/* Change ADDRESSLIST_SIZE if more are added */
p = NULL;
break;
}
if ((p) && (i < ADDRESSLIST_SIZE))
{
syms[i].realAddress = (unsigned long)p;
syms[i].closestAddress = 0;
syms[i].name[0] = (char)0;
syms[i].type = ' ';
}
else
{
syms[i].realAddress = 0;
break; /* for */
}
} /* for */
write(global_output,
"GCC_DumpStack-002\n", strlen("GCC_DumpStack-002\n"));
/* First find out if we are using GNU or vendor nm */
number = 0;
strcpy(buffer, "nm -V 2>/dev/null | grep GNU | wc -l");
write(global_output,
"GCC_DumpStack-002b\n", strlen("GCC_DumpStack-002b\n"));
fd = my_popen(buffer, &pid);
if (SYS_ERROR != fd)
{
write(global_output,
"GCC_DumpStack-002c\n", strlen("GCC_DumpStack-002c\n"));
if (my_getline(fd, buffer, sizeof(buffer)))
{
sscanf(buffer, "%d", &number);
}
my_pclose(fd, pid);
}
write(global_output,
"GCC_DumpStack-002d\n", strlen("GCC_DumpStack-002d\n"));
if (number == 0) /* vendor nm */
{
# if defined(PLATFORM_SOLARIS) || defined(PLATFORM_SCO) || defined(PLATFORM_HPUX)
strcpy(buffer, "nm -x -p ");
# elif defined(PLATFORM_AIX) || defined(PLATFORM_IRIX) || defined(PLATFORM_OSF)
strcpy(buffer, "nm -x -B ");
# else
strcpy(buffer, "nm -B ");
# endif
}
else /* GNU nm */
strcpy(buffer, "nm -B ");
write(global_output,
"GCC_DumpStack-002e\n", strlen("GCC_DumpStack-002e\n"));
strcat(buffer, global_progname);
write(global_output,
"GCC_DumpStack-003\n", strlen("GCC_DumpStack-003\n"));
lowestAddress = ULONG_MAX;
highestAddress = 0;
fd = my_popen(buffer, &pid);
if (SYS_ERROR != fd)
{
while (my_getline(fd, buffer, sizeof(buffer)))
{
if (buffer[0] == '\n')
continue;
if (3 == sscanf(buffer, "%lx %c %s", &addr, &type, name))
{
if ((type == 't') || type == 'T')
{
if (addr == 0)
continue; /* while */
if (addr < lowestAddress)
lowestAddress = addr;
if (addr > highestAddress)
highestAddress = addr;
for (i = 0; syms[i].realAddress != 0; i++)
{
if ((addr <= syms[i].realAddress) &&
(addr > syms[i].closestAddress))
{
syms[i].closestAddress = addr;
strncpy(syms[i].name, name, MAX_BUFFER_SIZE);
syms[i].name[MAX_BUFFER_SIZE] = (char)0;
syms[i].type = type;
}
}
}
}
}
my_pclose(fd, pid);
for (i = 0; syms[i].realAddress != 0; i++)
{
if ((syms[i].name[0] == (char)0) ||
(syms[i].realAddress <= lowestAddress) ||
(syms[i].realAddress >= highestAddress))
{
sprintf(buffer, "[%d] 0x%08lx ???\n", i, syms[i].realAddress);
}
else
{
sprintf(buffer, "[%d] 0x%08lx <%s + 0x%lx> %c\n",
i,
syms[i].realAddress,
syms[i].name,
syms[i].realAddress - syms[i].closestAddress,
syms[i].type);
}
write(global_output, buffer, strlen(buffer));
}
}
write(global_output,
"GCC_DumpStack-004\n", strlen("GCC_DumpStack-004\n"));
}
#endif
/*************************************************************************
* DumpStack [private]
*/
static int DumpStack(char *format, ...)
{
int gotSomething = FALSE;
int fd;
pid_t pid;
int status = EXIT_FAILURE;
int rc;
va_list args;
char *buffer;
char cmd[MAX_BUFFER_SIZE];
char buf[MAX_BUFFER_SIZE];
/*
* Please note that vsprintf() is not ASync safe (ie. cannot safely
* be used from a signal handler.) If this proves to be a problem
* then the cmd string can be built by more basic functions such as
* strcpy, strcat, and a homemade integer-to-ascii function.
*/
va_start(args, format);
vsprintf(cmd, format, args);
va_end(args);
//MG
write(global_output,
"EEE\n", strlen("EEE\n"));
fd = my_popen(cmd, &pid);
if (SYS_ERROR != fd)
{
//MG
write(global_output,
"FFF\n", strlen("FFF\n"));
/*
* Wait for the child to exit. This must be done
* to make the debugger attach successfully.
* The output from the debugger is buffered on
* the pipe.
*
* AIX needs the looping hack
*/
do
{
rc = waitpid(pid, &status, 0);
}
while ((SYS_ERROR == rc) && (EINTR == errno));
//MG
write(global_output,
"GGG\n", strlen("GGG\n"));
if ((WIFEXITED(status)) && (WEXITSTATUS(status) == EXIT_SUCCESS))
{
//MG
write(global_output,
"III\n", strlen("III\n"));
while (my_getline(fd, buf, sizeof(buf)))
{
buffer = buf;
if (! gotSomething)
{
write(global_output, "Output from ",
strlen("Output from "));
strtok(cmd, " ");
write(global_output, cmd, strlen(cmd));
write(global_output, "\n", strlen("\n"));
gotSomething = TRUE;
}
if ('\n' == buf[strlen(buf)-1])
{
buf[strlen(buf)-1] = (char)0;
}
write(global_output, buffer, strlen(buffer));
write(global_output, "\n", strlen("\n"));
}
}
//MG
write(global_output,
"JJJ\n", strlen("JJJ\n"));
my_pclose(fd, pid);
}
//MG
write(global_output,
"HHH\n", strlen("HHH\n"));
return gotSomething;
}
#endif /* PLATFORM_UNIX */
/*************************************************************************
* StackTrace
*/
void StackTrace(void)
{
# if defined(__GNUC__) && defined(USE_BUILTIN)
//MG
write(global_output,
"GCC/BUILTIN\n", strlen("GCC/BUILTIN\n"));
# endif
#if defined(PLATFORM_UNIX)
/*
* In general dbx seems to do a better job than gdb.
*
* Different dbx implementations require different flags/commands.
*/
# if defined(PLATFORM_AIX)
if (DumpStack("dbx -a %d 2>/dev/null <<EOF\n"
"where\n"
"detach\n"
"EOF\n",
(int)getpid()))
return;
if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
"set prompt\n"
"where\n"
"detach\n"
"quit\n"
"EOF\n",
global_progname, (int)getpid()))
return;
# elif defined(PLATFORM_FREEBSD)
/*
* FreeBSD insists on sending a SIGSTOP to the process we
* attach to, so we let the debugger send a SIGCONT to that
* process after we have detached.
*/
if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
"set prompt\n"
"where\n"
"detach\n"
"shell kill -CONT %d\n"
"quit\n"
"EOF\n",
global_progname, (int)getpid(), (int)getpid()))
return;
# elif defined(PLATFORM_HPUX)
/*
* HP decided to call their debugger xdb.
*
* This does not seem to work properly yet. The debugger says
* "Note: Stack traces may not be possible until you are
* stopped in user code." on HP-UX 09.01
*
* -L = line-oriented interface.
* "T [depth]" gives a stacktrace with local variables.
* The final "y" is confirmation to the quit command.
*/
if (DumpStack("xdb -P %d -L %s 2>&1 <<EOF\n"
"T 50\n"
"q\ny\n"
"EOF\n",
(int)getpid(), global_progname))
return;
if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
"set prompt\n"
"where\n"
"detach\n"
"quit\n"
"EOF\n",
global_progname, (int)getpid()))
return;
# if defined(PLATFORM_HPUX) && defined(USE_BUILTIN)
U_STACK_TRACE();
return;
# endif
# elif defined(PLATFORM_IRIX)
/*
* "set $page=0" drops hold mode
* "dump ." displays the contents of the variables
*/
if (DumpStack("dbx -p %d 2>/dev/null <<EOF\n"
"set \\$page=0\n"
"where\n"
# if !defined(__GNUC__)
/* gcc does not generate this information */
"dump .\n"
# endif
"detach\n"
"EOF\n",
(int)getpid()))
return;
# if defined(USE_BUILTIN)
if (trace_back_stack_and_print())
return;
# endif
if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
"set prompt\n"
"echo --- Stacktrace\\n\n"
"where\n"
"echo --- Symbols\\n\n"
"frame 5\n" /* Skip signal handler frames */
"set \\$x = 50\n"
"while (\\$x)\n" /* Print local variables for each frame */
"info locals\n"
"up\n"
"set \\$x--\n"
"end\n"
"echo ---\\n\n"
"detach\n"
"quit\n"
"EOF\n",
global_progname, (int)getpid()))
return;
# elif defined(PLATFORM_OSF)
if (DumpStack("dbx -pid %d %s 2>/dev/null <<EOF\n"
"where\n"
"detach\n"
"quit\n"
"EOF\n",
(int)getpid(), global_progname))
return;
if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
"set prompt\n"
"where\n"
"detach\n"
"quit\n"
"EOF\n",
global_progname, (int)getpid()))
return;
# elif defined(PLATFORM_SCO)
//MG
write(global_output,
"DDDD\n", strlen("DDDD\n"));
/*
* SCO OpenServer dbx is like a catch-22. The 'detach' command
* depends on whether ptrace(S) support detaching or not. If it
* is supported then 'detach' must be used, otherwise the process
* will be killed upon dbx exit. If it isn't supported then 'detach'
* will cause the process to be killed. We do not want it to be
* killed.
*
* Out of two evils, the omission of 'detach' was chosen because
* it worked on our system.
*/
if (DumpStack("dbx %s %d 2>/dev/null <<EOF\n"
"where\n"
"quit\nEOF\n",
global_progname, (int)getpid()))
return;
if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
"set prompt\n"
"where\n"
"detach\n"
"quit\n"
"EOF\n",
global_progname, (int)getpid()))
return;
# elif defined(PLATFORM_SOLARIS)
if (DumpStack("dbx %s %d 2>/dev/null <<EOF\n"
"where\n"
"detach\n"
"EOF\n",
global_progname, (int)getpid()))
return;
if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
"set prompt\n"
"echo --- Stacktrace\\n\n"
"where\n"
"echo --- Symbols\\n\n"
"frame 5\n" /* Skip signal handler frames */
"set \\$x = 50\n"
"while (\\$x)\n" /* Print local variables for each frame */
"info locals\n"
"up\n"
"set \\$x--\n"
"end\n"
"echo ---\\n\n"
"detach\n"
"quit\n"
"EOF\n",
global_progname, (int)getpid()))
return;
if (DumpStack("/usr/proc/bin/pstack %d",
(int)getpid()))
return;
/*
* Other Unices (AIX, HPUX, SCO) also have adb, but
* they seem unable to attach to a running process.
*/
if (DumpStack("adb %s 2>&1 <<EOF\n"
"0t%d:A\n" /* Attach to pid */
"\\$c\n" /* print stacktrace */
":R\n" /* Detach */
"\\$q\n" /* Quit */
"EOF\n",
global_progname, (int)getpid()))
return;
# else /* All other Unix platforms */
/*
* TODO: SCO/UnixWare 7 must be something like (not tested)
* debug -i c <pid> <<EOF\nstack -f 4\nquit\nEOF\n
*/
//MG
write(global_output,
"AAAA\n", strlen("AAAA\n"));
# if !defined(__GNUC__)
//MG
write(global_output,
"BBBB\n", strlen("BBBB\n"));
if (DumpStack("dbx %s %d 2>/dev/null <<EOF\n"
"where\n"
"detach\n"
"EOF\n",
global_progname, (int)getpid()))
return;
# endif
//MG
write(global_output,
"CCCC\n", strlen("CCCC\n"));
if (DumpStack("gdb -q %s %d 2>/dev/null <<EOF\n"
"set prompt\n"
"echo --- Stacktrace\\n\n"
"where\n"
"echo --- Symbols\\n\n"
"frame 4\n"
"set \\$x = 50\n"
"while (\\$x)\n" /* Print local variables for each frame */
"info locals\n"
"up\n"
"set \\$x--\n"
"end\n"
"echo ---\\n\n"
"detach\n"
"quit\n"
"EOF\n",
global_progname, (int)getpid()))
return;
# endif
# if defined(__GNUC__) && defined(USE_BUILTIN)
//MG
write(global_output,
"GCC_DumpStack\n", strlen("GCC_DumpStack\n"));
GCC_DumpStack();
//MG
write(global_output,
"GCC_DumpStack AAA\n", strlen("GCC_DumpStack AAA\n"));
# endif
write(global_output,
"No debugger found\n", strlen("No debugger found\n"));
#elif defined(PLATFORM_WIN32)
/* Use StackWalk() */
#endif
}
/*************************************************************************
* StackTraceInit
*/
void StackTraceInit(const char *in_name, int in_handle)
{
global_progname = in_name;
global_output = (in_handle == -1) ? STDOUT_FILENO : in_handle;
}
/*************************************************************************
* test
*/
#if defined(STANDALONE)
void CrashHandler(int sig)
{
/* Reinstall default handler to prevent race conditions */
signal(sig, SIG_DFL);
/* Print the stack trace */
StackTrace();
/* And exit because we may have corrupted the internal
* memory allocation lists. Use abort() if we want to
* generate a core dump. */
_exit(EXIT_FAILURE);
}
void Crash(void)
{
/* Force a crash */
strcpy(NULL, "");
}
int main(int argc, char *argv[])
{
struct sigaction sact;
StackTraceInit(argv[0], -1);
sigemptyset(&sact.sa_mask);
sact.sa_flags = 0;
sact.sa_handler = CrashHandler;
sigaction(SIGSEGV, &sact, NULL);
sigaction(SIGBUS, &sact, NULL);
Crash();
return EXIT_SUCCESS;
}
#endif