4 * author: David Frascone
6 * eCrash Implementation
8 * eCrash will allow you to capture stack traces in the
9 * event of a crash, and write those traces to disk, stdout,
10 * or any other file handle.
12 * modified to integrate closer into citadel by Wilfried Goesgens
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
39 #include <sys/types.h>
42 #include <libcitadel.h>
44 #include "sysdep_decls.h"
47 #include "citserver.h"
50 #define NIY() printf("%s: Not Implemented Yet!\n", __FUNCTION__)
53 static eCrashParameters gbl_params;
55 static int gbl_backtraceEntries;
56 static void **gbl_backtraceBuffer;
57 static char **gbl_backtraceSymbols;
58 static int gbl_backtraceDoneFlag = 0;
60 static void *stack_frames[50];
61 static size_t size, NThread;
62 static char **strings;
63 static char StaticBuf[SIZ];
66 * Private structures for our thread list
68 typedef struct thread_list_node{
72 sighandler_t oldHandler;
73 struct thread_list_node *Next;
76 static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
77 static ThreadListNode *ThreadList = NULL;
79 /*********************************************************************
80 *********************************************************************
81 ** P R I V A T E F U N C T I O N S
82 *********************************************************************
83 ********************************************************************/
87 * Insert a node into our threadList
89 * @param name Text string indicating our thread
90 * @param thread Our Thread Id
91 * @param signo Signal to create backtrace with
92 * @param old_handler Our old handler for signo
94 * @returns zero on success
96 static int addThreadToList(char *name, pthread_t thread,int signo,
97 sighandler_t old_handler)
101 node = malloc(sizeof(ThreadListNode));
102 if (!node) return -1;
104 DPRINTF(ECRASH_DEBUG_VERBOSE,
105 "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
106 node->threadName = strdup(name);
107 node->thread = thread;
108 node->backtraceSignal = signo;
109 node->oldHandler = old_handler;
111 /* And, add it to the list */
112 pthread_mutex_lock(&ThreadListMutex);
113 node->Next = ThreadList;
115 pthread_mutex_unlock(&ThreadListMutex);
122 * Remove a node from our threadList
124 * @param thread Our Thread Id
126 * @returns zero on success
128 static int removeThreadFromList(pthread_t thread)
130 ThreadListNode *Probe, *Prev=NULL;
131 ThreadListNode *Removed = NULL;
133 DPRINTF(ECRASH_DEBUG_VERBOSE,
134 "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
135 pthread_mutex_lock(&ThreadListMutex);
136 for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
137 if (Probe->thread == thread) {
138 // We found it! Unlink it and move on!
140 if (Prev == NULL) { // head of list
141 ThreadList = Probe->Next;
143 // Prev != null, so we need to link around ourselves.
144 Prev->Next = Probe->Next;
146 Removed->Next = NULL;
152 pthread_mutex_unlock(&ThreadListMutex);
154 // Now, if something is in Removed, free it, and return success
156 DPRINTF(ECRASH_DEBUG_VERBOSE,
157 " Found %s -- removing\n", Removed->threadName);
158 // Reset the signal handler
159 signal(Removed->backtraceSignal, Removed->oldHandler);
161 // And free the allocated memory
162 free (Removed->threadName);
167 DPRINTF(ECRASH_DEBUG_VERBOSE,
169 return -1; // Not Found
171 } // removeThreadFromList
174 * Print out a line of output to all our destinations
176 * One by one, output a line of text to all of our output destinations.
178 * Return failure if we fail to output to any of them.
180 * @param format Normal printf style vararg format
182 * @returns nothing// bytes written, or error on failure.
184 static void outputPrintf(char *format, ...)
188 va_start(ap, format);
192 snprintf (StaticBuf, SIZ, format, ap);
193 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
196 CtdlLogPrintf(CTDL_EMERG, format, ap);
203 * Dump our backtrace into a global location
205 * This function will dump out our backtrace into our
206 * global holding area.
209 static void createGlobalBacktrace( void )
212 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
214 for (NThread = 0; NThread < size; NThread++)
216 snprintf (StaticBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
217 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
220 for (NThread = 0; NThread < size; NThread++)
221 CtdlLogPrintf(1, "RAW: %p\n", stack_frames[NThread]);
222 strings = backtrace_symbols(stack_frames, size);
223 for (NThread = 0; NThread < size; NThread++) {
224 if (strings != NULL) {
226 {// vsyslogs printf compliance sucks.
227 snprintf (StaticBuf, SIZ, "RAW: %p ", strings[NThread]);
228 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
231 CtdlLogPrintf(1, "%s\n", strings[NThread]);
234 } /* createGlobalBacktrace */
235 static void outputRawtrace( void )
238 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
240 for (NThread = 0; NThread < size; NThread++)
242 snprintf (StaticBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
243 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
246 for (NThread = 0; NThread < size; NThread++)
247 CtdlLogPrintf(1, "RAW: %p\n", stack_frames[NThread]);
248 } /* createGlobalBacktrace */
251 * Print out (to all the fds, etc), or global backtrace
253 static void outputGlobalBacktrace ( void )
257 for (i=0; i < gbl_backtraceEntries; i++) {
258 if (gbl_backtraceSymbols != FALSE) {
259 outputPrintf("* Frame %02x: %s\n",
260 i, gbl_backtraceSymbols[i]);
262 outputPrintf("* Frame %02x: %p\n", i,
263 gbl_backtraceBuffer[i]);
266 } // outputGlobalBacktrace
269 * Output our current stack's backtrace
271 static void outputBacktrace( void )
273 createGlobalBacktrace();
274 outputGlobalBacktrace();
275 } /* outputBacktrace */
277 static void outputBacktraceThreads( void )
279 ThreadListNode *probe;
282 // When we're backtracing, don't worry about the mutex . . hopefully
283 // we're in a safe place.
285 for (probe=ThreadList; probe; probe=probe->Next) {
286 gbl_backtraceDoneFlag = 0;
287 pthread_kill(probe->thread, probe->backtraceSignal);
288 for (i=0; i < gbl_params.threadWaitTime; i++) {
289 if (gbl_backtraceDoneFlag)
293 if (gbl_backtraceDoneFlag) {
294 outputPrintf("* Backtrace of \"%s\" (0x%08x)\n",
295 probe->threadName, (unsigned int)probe->thread);
296 outputGlobalBacktrace();
298 outputPrintf("* Error: unable to get backtrace of \"%s\" (0x%08x)\n",
299 probe->threadName, (unsigned int)probe->thread);
303 } // outputBacktraceThreads
307 * Handle signals (crash signals)
309 * This function will catch all crash signals, and will output the
312 * It will physically write (and sync) the current thread's information
313 * before it attempts to send signals to other threads.
315 * @param signum Signal received.
317 static void crash_handler(int signo)
320 outputPrintf("*********************************************************\n");
321 outputPrintf("* eCrash Crash Handler\n");
322 outputPrintf("*********************************************************\n");
324 outputPrintf("* Got a crash! signo=%d\n", signo);
326 outputPrintf("* Offending Thread's Backtrace:\n");
331 if (gbl_params.dumpAllThreads != FALSE) {
332 outputBacktraceThreads();
336 outputPrintf("*********************************************************\n");
337 outputPrintf("* eCrash Crash Handler\n");
338 outputPrintf("*********************************************************\n");
344 * Handle signals (bt signals)
346 * This function shoudl be called to generate a crashdump into our
347 * global area. Once the dump has been completed, this function will
348 * return after tickling a global. Since mutexes are not async
349 * signal safe, the main thread, after signaling us to generate our
350 * own backtrace, will sleep for a few seconds waiting for us to complete.
352 * @param signum Signal received.
354 static void bt_handler(int signo)
356 createGlobalBacktrace();
357 gbl_backtraceDoneFlag=1;
361 * Validate a passed-in symbol table
363 * For now, just print it out (if verbose), and make sure it's
364 * sorted and none of the pointers are zero.
366 static int ValidateSymbolTable( void )
370 unsigned long lastAddress =0;
372 // Get out of here if the table is empty
373 if (!gbl_params.symbolTable) return 0;
375 // Dump it in verbose mode
376 DPRINTF(ECRASH_DEBUG_VERBOSE,
377 "Symbol Table Provided with %d symbols\n",
378 gbl_params.symbolTable->numSymbols);
379 for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
380 // Dump it in verbose mode
381 DPRINTF(ECRASH_DEBUG_VERBOSE,
383 gbl_params.symbolTable->symbols[i].function,
384 gbl_params.symbolTable->symbols[i].address);
386 (unsigned long)gbl_params.symbolTable->symbols[i].address) {
387 DPRINTF(ECRASH_DEBUG_ERROR,
388 "Error: symbol table is not sorted (last=%p, current=%p)\n",
390 gbl_params.symbolTable->symbols[i].address);
398 } // ValidateSymbolTable
400 /*********************************************************************
401 *********************************************************************
402 ** P U B L I C F U N C T I O N S
403 *********************************************************************
404 ********************************************************************/
409 * This function must be called before calling any other eCrash
410 * functions. It sets up the global behavior of the system, and
411 * registers the calling thread for crash dumps.
413 * @param params Our input parameters. The passed in structure will be copied.
415 * @return Zero on success.
417 int eCrash_Init(eCrashParameters *params)
421 #ifdef DO_SIGNALS_RIGHT
423 struct sigaction act;
426 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
428 // Allocate our backtrace area
429 gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
431 #ifdef DO_SIGNALS_RIGHT
432 sigemptyset(&blocked);
433 act.sa_sigaction = crash_handler;
434 act.sa_mask = blocked;
435 act.sa_flags = SA_SIGINFO;
438 if (params != NULL) {
439 // Make ourselves a global copy of params.
440 gbl_params = *params;
441 gbl_params.filename = strdup(params->filename);
443 // Set our defaults, if they weren't specified
444 if (gbl_params.maxStackDepth == 0 )
445 gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
447 if (gbl_params.defaultBacktraceSignal == 0 )
448 gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
450 if (gbl_params.threadWaitTime == 0 )
451 gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
453 if (gbl_params.debugLevel == 0 )
454 gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
456 // Copy our symbol table
457 if (gbl_params.symbolTable) {
458 DPRINTF(ECRASH_DEBUG_VERBOSE,
459 "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
460 gbl_params.symbolTable->numSymbols);
461 // Make a copy of our symbol table
462 gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
463 memcpy(gbl_params.symbolTable, params->symbolTable,
464 sizeof(eCrashSymbolTable));
466 // Now allocate / copy the actual table.
467 gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
468 gbl_params.symbolTable->numSymbols);
469 memcpy(gbl_params.symbolTable->symbols,
470 params->symbolTable->symbols,
471 sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
473 ValidateSymbolTable();
476 // And, finally, register for our signals
477 for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
478 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
479 " Catching signal[%d] %d\n", sigIndex,
480 gbl_params.signals[sigIndex]);
482 // I know there's a better way to catch signals with pthreads.
483 // I'll do it later TODO
484 signal(gbl_params.signals[sigIndex], crash_handler);
487 DPRINTF(ECRASH_DEBUG_ERROR, " Error: Null Params!\n");
490 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
495 * UnInitialize eCrash.
497 * This function may be called to de-activate eCrash, release the
498 * signal handlers, and free any memory allocated by eCrash.
500 * @return Zero on success.
502 int eCrash_Uninit( void )
507 } /* eCrash_Uninit */
510 * Register a thread for backtracing on crash.
512 * This function must be called by any thread wanting it's stack
513 * dumped in the event of a crash. The thread my specify what
514 * signal should be used, or the default, SIGUSR1 will be used.
516 * @param signo Signal to use to generate dump (default: SIGUSR1)
518 * @return Zero on success.
520 int eCrash_RegisterThread(char *name, int signo)
522 sighandler_t old_handler;
524 // Register for our signal
526 signo = gbl_params.defaultBacktraceSignal;
529 old_handler = signal(signo, bt_handler);
530 return addThreadToList(name, pthread_self(), signo, old_handler);
532 } /* eCrash_RegisterThread */
535 * Un-register a thread for stack dumps.
537 * This function may be called to un-register any previously
540 * @return Zero on success.
542 int eCrash_UnregisterThread( void )
544 return removeThreadFromList(pthread_self());
545 } /* eCrash_UnregisterThread */