/* debug.c
*
* This file proides debug and run time error analysis support. Some of the
* settings are very performance intense and my be turned off during a release
* build.
*
* File begun on 2008-01-22 by RGerhards
*
* There is some in-depth documentation available in doc/dev_queue.html
* (and in the web doc set on http://www.rsyslog.com/doc). Be sure to read it
* if you are getting aquainted to the object.
*
* Copyright 2008 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
* Rsyslog is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rsyslog is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rsyslog. If not, see .
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
#include "config.h" /* autotools! */
#include
#include
#include
#include
#include
#include
#include
#include
#include "rsyslog.h"
#include "debug.h"
/* static data (some time to be replaced) */
int Debug; /* debug flag - read-only after startup */
int debugging_on = 0; /* read-only, except on sig USR1 */
static FILE *stddbg;
typedef struct dbgCallStack_s {
pthread_t thrd;
const char* callStack[100000];
int stackPtr;
struct dbgCallStack_s *pNext;
struct dbgCallStack_s *pPrev;
} dbgCallStack_t;
static dbgCallStack_t *dbgCallStackListRoot = NULL;
static dbgCallStack_t *dbgCallStackListLast = NULL;
static pthread_mutex_t mutCallStack = PTHREAD_MUTEX_INITIALIZER;
static pthread_key_t keyCallStack;
/* destructor for a call stack object */
static void dbgCallStackDestruct(void *arg)
{
dbgCallStack_t *pStack = (dbgCallStack_t*) arg;
dbgprintf("destructor for debug call stack %p called\n", pStack);
if(pStack->pPrev != NULL)
pStack->pPrev->pNext = pStack->pNext;
if(pStack->pNext != NULL)
pStack->pNext->pPrev = pStack->pPrev;
if(pStack == dbgCallStackListRoot)
dbgCallStackListRoot = pStack->pNext;
if(pStack == dbgCallStackListLast)
dbgCallStackListLast = pStack->pNext;
free(pStack);
}
/* print a thread's call stack
*/
static void dbgCallStackPrint(dbgCallStack_t *pStack)
{
int i;
/* TODO: mutex guard! */
dbgprintf("\nRecorded Call Order for Thread 0x%lx (%p):\n", (unsigned long) pStack->thrd, pStack);
for(i = 0 ; i < pStack->stackPtr ; i++) {
dbgprintf("%s()\n", pStack->callStack[i]);
}
dbgprintf("NOTE: not all calls may have been recorded.\n");
}
/* get ptr to call stack - if none exists, create a new stack
*/
static dbgCallStack_t *dbgGetCallStack(void)
{
dbgCallStack_t *pStack;
pthread_mutex_lock(&mutCallStack);
if((pStack = pthread_getspecific(keyCallStack)) == NULL) {
/* construct object */
pStack = calloc(1, sizeof(dbgCallStack_t));
pStack->thrd = pthread_self();
(void) pthread_setspecific(keyCallStack, pStack);
fprintf(stdout, "dbgGetCallStack Create thrd %lx, pstack %p, thrd %lx\n", (unsigned long) pthread_self(), pStack, pStack->thrd);
if(dbgCallStackListRoot == NULL) {
dbgCallStackListRoot = pStack;
dbgCallStackListLast = pStack;
} else {
pStack->pPrev = dbgCallStackListLast;
dbgCallStackListLast->pNext = pStack;
dbgCallStackListLast = pStack;
}
}
pthread_mutex_unlock(&mutCallStack);
return pStack;
}
/* handler for SIGSEGV - MUST terminiate the app, but does so in a somewhat
* more meaningful way.
* rgerhards, 2008-01-22
*/
void
sigsegvHdlr(int signum)
{
struct sigaction sigAct;
char *signame;
dbgCallStack_t *pStack;
if(signum == SIGSEGV) {
signame = " (SIGSEGV)";
} else {
signame = "";
}
dbgprintf("Signal %d%s occured, execution must be terminated %d.\n", signum, signame, SIGSEGV);
/* stack info */
for(pStack = dbgCallStackListRoot ; pStack != NULL ; pStack = pStack->pNext) {
dbgCallStackPrint(pStack);
}
fflush(stddbg);
/* re-instantiate original handler ... */
memset(&sigAct, 0, sizeof (sigAct));
sigemptyset(&sigAct.sa_mask);
sigAct.sa_handler = SIG_DFL;
sigaction(SIGSEGV, &sigAct, NULL);
/* and call it */
int ir = raise(signum);
printf("raise returns %d, errno %d: %s\n", ir, errno, strerror(errno));
/* we should never arrive here - but we provide some code just in case... */
dbgprintf("sigsegvHdlr: oops, returned from raise(), doing exit(), something really wrong...\n");
exit(1);
}
/* print some debug output */
void
dbgprintf(char *fmt, ...)
{
static pthread_t ptLastThrdID = 0;
static int bWasNL = 0;
va_list ap;
if ( !(Debug && debugging_on) )
return;
/* The bWasNL handler does not really work. It works if no thread
* switching occurs during non-NL messages. Else, things are messed
* up. Anyhow, it works well enough to provide useful help during
* getting this up and running. It is questionable if the extra effort
* is worth fixing it, giving the limited appliability.
* rgerhards, 2005-10-25
* I have decided that it is not worth fixing it - especially as it works
* pretty well.
* rgerhards, 2007-06-15
*/
if(ptLastThrdID != pthread_self()) {
if(!bWasNL) {
fprintf(stddbg, "\n");
bWasNL = 1;
}
ptLastThrdID = pthread_self();
}
if(bWasNL) {
fprintf(stddbg, "%8.8x: ", (unsigned int) pthread_self());
}
bWasNL = (*(fmt + strlen(fmt) - 1) == '\n') ? 1 : 0;
va_start(ap, fmt);
vfprintf(stddbg, fmt, ap);
va_end(ap);
fflush(stddbg);
return;
}
/* handler called when a function is entered
*/
void dbgEntrFunc(char* file, int line, const char* func)
{
dbgCallStack_t *pStack = dbgGetCallStack();
dbgprintf("%s:%d: %s: enter\n", file, line, func);
pStack->callStack[pStack->stackPtr++] = func;
dbgprintf("stack %d\n", pStack->stackPtr);
assert(pStack->stackPtr < (int) (sizeof(pStack->callStack) / sizeof(const char*)));
}
/* handler called when a function is exited
*/
void dbgExitFunc(char* file, int line, const char* func)
{
dbgCallStack_t *pStack = dbgGetCallStack();
dbgprintf("%s:%d: %s: exit\n", file, line, func);
pStack->stackPtr--;
assert(pStack->stackPtr > 0);
}
rsRetVal dbgClassInit(void)
{
stddbg = stdout;
(void) pthread_key_create(&keyCallStack, dbgCallStackDestruct);
return RS_RET_OK;
}
rsRetVal dbgClassExit(void)
{
pthread_key_delete(keyCallStack);
//(void) pthread_key_create(&keyCallStack, dbgCallStackDestructor);
return RS_RET_OK;
}
/*
* vi:set ai:
*/