2 * author: David Frascone
4 * eCrash Implementation
6 * eCrash will allow you to capture stack traces in the
7 * event of a crash, and write those traces to disk, stdout,
8 * or any other file handle.
10 * modified to integrate closer into citadel by Wilfried Goesgens
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
37 #include <sys/types.h>
40 #include <libcitadel.h>
42 #include "sysdep_decls.h"
45 #include "citserver.h"
48 #define NIY() printf("function not implemented yet!\n");
51 static eCrashParameters gbl_params;
53 static int gbl_backtraceEntries;
54 static void **gbl_backtraceBuffer;
55 static char **gbl_backtraceSymbols;
56 static int gbl_backtraceDoneFlag = 0;
58 static void *stack_frames[50];
59 static size_t size, NThread;
60 static char **strings;
61 static char StaticBuf[SIZ];
64 * Private structures for our thread list
66 typedef struct thread_list_node{
70 sighandler_t oldHandler;
71 struct thread_list_node *Next;
74 static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
75 static ThreadListNode *ThreadList = NULL;
77 /*********************************************************************
78 *********************************************************************
79 ** P R I V A T E F U N C T I O N S
80 *********************************************************************
81 ********************************************************************/
85 * Insert a node into our threadList
87 * @param name Text string indicating our thread
88 * @param thread Our Thread Id
89 * @param signo Signal to create backtrace with
90 * @param old_handler Our old handler for signo
92 * @returns zero on success
94 static int addThreadToList(char *name, pthread_t thread,int signo,
95 sighandler_t old_handler)
99 node = malloc(sizeof(ThreadListNode));
100 if (!node) return -1;
102 DPRINTF(ECRASH_DEBUG_VERBOSE,
103 "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
104 node->threadName = strdup(name);
105 node->thread = thread;
106 node->backtraceSignal = signo;
107 node->oldHandler = old_handler;
109 /* And, add it to the list */
110 pthread_mutex_lock(&ThreadListMutex);
111 node->Next = ThreadList;
113 pthread_mutex_unlock(&ThreadListMutex);
120 * Remove a node from our threadList
122 * @param thread Our Thread Id
124 * @returns zero on success
126 static int removeThreadFromList(pthread_t thread)
128 ThreadListNode *Probe, *Prev=NULL;
129 ThreadListNode *Removed = NULL;
131 DPRINTF(ECRASH_DEBUG_VERBOSE,
132 "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
133 pthread_mutex_lock(&ThreadListMutex);
134 for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
135 if (Probe->thread == thread) {
136 // We found it! Unlink it and move on!
138 if (Prev == NULL) { // head of list
139 ThreadList = Probe->Next;
141 // Prev != null, so we need to link around ourselves.
142 Prev->Next = Probe->Next;
144 Removed->Next = NULL;
150 pthread_mutex_unlock(&ThreadListMutex);
152 // Now, if something is in Removed, free it, and return success
154 DPRINTF(ECRASH_DEBUG_VERBOSE,
155 " Found %s -- removing\n", Removed->threadName);
156 // Reset the signal handler
157 signal(Removed->backtraceSignal, Removed->oldHandler);
159 // And free the allocated memory
160 free (Removed->threadName);
165 DPRINTF(ECRASH_DEBUG_VERBOSE,
167 return -1; // Not Found
169 } // removeThreadFromList
172 * Print out a line of output to all our destinations
174 * One by one, output a line of text to all of our output destinations.
176 * Return failure if we fail to output to any of them.
178 * @param format Normal printf style vararg format
180 * @returns nothing// bytes written, or error on failure.
182 static void outputPrintf(char *format, ...)
186 va_start(ap, format);
188 snprintf (StaticBuf, SIZ, format, ap);
189 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
195 * Dump our backtrace into a global location
197 * This function will dump out our backtrace into our
198 * global holding area.
201 static void createGlobalBacktrace( void )
204 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
205 for (NThread = 0; NThread < size; NThread++)
207 snprintf (StaticBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
208 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
211 strings = backtrace_symbols(stack_frames, size);
212 for (NThread = 0; NThread < size; NThread++) {
213 if (strings != NULL) {
214 // vsyslogs printf compliance sucks.
215 snprintf (StaticBuf, SIZ, "RAW: %p ", strings[NThread]);
216 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
219 } /* createGlobalBacktrace */
220 static void outputRawtrace( void )
223 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
224 for (NThread = 0; NThread < size; NThread++)
226 snprintf (StaticBuf, SIZ, "RAW: %p ", stack_frames[NThread]);
227 syslog( LOG_CRIT|LOG_NDELAY|LOG_MAIL, StaticBuf);
229 } /* createGlobalBacktrace */
232 * Print out (to all the fds, etc), or global backtrace
234 static void outputGlobalBacktrace ( void )
238 for (i=0; i < gbl_backtraceEntries; i++) {
239 if (gbl_backtraceSymbols != FALSE) {
240 outputPrintf("* Frame %02x: %s\n",
241 i, gbl_backtraceSymbols[i]);
243 outputPrintf("* Frame %02x: %p\n", i,
244 gbl_backtraceBuffer[i]);
247 } // outputGlobalBacktrace
250 * Output our current stack's backtrace
252 static void outputBacktrace( void )
254 createGlobalBacktrace();
255 outputGlobalBacktrace();
256 } /* outputBacktrace */
258 static void outputBacktraceThreads( void )
260 ThreadListNode *probe;
263 // When we're backtracing, don't worry about the mutex . . hopefully
264 // we're in a safe place.
266 for (probe=ThreadList; probe; probe=probe->Next) {
267 gbl_backtraceDoneFlag = 0;
268 pthread_kill(probe->thread, probe->backtraceSignal);
269 for (i=0; i < gbl_params.threadWaitTime; i++) {
270 if (gbl_backtraceDoneFlag)
274 if (gbl_backtraceDoneFlag) {
275 outputPrintf("* Backtrace of \"%s\" (0x%08x)\n",
276 probe->threadName, (unsigned int)probe->thread);
277 outputGlobalBacktrace();
279 outputPrintf("* Error: unable to get backtrace of \"%s\" (0x%08x)\n",
280 probe->threadName, (unsigned int)probe->thread);
284 } // outputBacktraceThreads
288 * Handle signals (crash signals)
290 * This function will catch all crash signals, and will output the
293 * It will physically write (and sync) the current thread's information
294 * before it attempts to send signals to other threads.
296 * @param signum Signal received.
298 static void crash_handler(int signo)
301 outputPrintf("*********************************************************\n");
302 outputPrintf("* eCrash Crash Handler\n");
303 outputPrintf("*********************************************************\n");
305 outputPrintf("* Got a crash! signo=%d\n", signo);
307 outputPrintf("* Offending Thread's Backtrace:\n");
312 if (gbl_params.dumpAllThreads != FALSE) {
313 outputBacktraceThreads();
317 outputPrintf("*********************************************************\n");
318 outputPrintf("* eCrash Crash Handler\n");
319 outputPrintf("*********************************************************\n");
325 * Handle signals (bt signals)
327 * This function shoudl be called to generate a crashdump into our
328 * global area. Once the dump has been completed, this function will
329 * return after tickling a global. Since mutexes are not async
330 * signal safe, the main thread, after signaling us to generate our
331 * own backtrace, will sleep for a few seconds waiting for us to complete.
333 * @param signum Signal received.
335 static void bt_handler(int signo)
337 createGlobalBacktrace();
338 gbl_backtraceDoneFlag=1;
342 * Validate a passed-in symbol table
344 * For now, just print it out (if verbose), and make sure it's
345 * sorted and none of the pointers are zero.
347 static int ValidateSymbolTable( void )
351 unsigned long lastAddress =0;
353 // Get out of here if the table is empty
354 if (!gbl_params.symbolTable) return 0;
356 // Dump it in verbose mode
357 DPRINTF(ECRASH_DEBUG_VERBOSE,
358 "Symbol Table Provided with %d symbols\n",
359 gbl_params.symbolTable->numSymbols);
360 for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
361 // Dump it in verbose mode
362 DPRINTF(ECRASH_DEBUG_VERBOSE,
364 gbl_params.symbolTable->symbols[i].function,
365 gbl_params.symbolTable->symbols[i].address);
367 (unsigned long)gbl_params.symbolTable->symbols[i].address) {
368 DPRINTF(ECRASH_DEBUG_ERROR,
369 "Error: symbol table is not sorted (last=%p, current=%p)\n",
371 gbl_params.symbolTable->symbols[i].address);
379 } // ValidateSymbolTable
381 /*********************************************************************
382 *********************************************************************
383 ** P U B L I C F U N C T I O N S
384 *********************************************************************
385 ********************************************************************/
390 * This function must be called before calling any other eCrash
391 * functions. It sets up the global behavior of the system, and
392 * registers the calling thread for crash dumps.
394 * @param params Our input parameters. The passed in structure will be copied.
396 * @return Zero on success.
398 int eCrash_Init(eCrashParameters *params)
402 #ifdef DO_SIGNALS_RIGHT
404 struct sigaction act;
407 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
409 // Allocate our backtrace area
410 gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
412 #ifdef DO_SIGNALS_RIGHT
413 sigemptyset(&blocked);
414 act.sa_sigaction = crash_handler;
415 act.sa_mask = blocked;
416 act.sa_flags = SA_SIGINFO;
419 if (params != NULL) {
420 // Make ourselves a global copy of params.
421 gbl_params = *params;
422 gbl_params.filename = strdup(params->filename);
424 // Set our defaults, if they weren't specified
425 if (gbl_params.maxStackDepth == 0 )
426 gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
428 if (gbl_params.defaultBacktraceSignal == 0 )
429 gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
431 if (gbl_params.threadWaitTime == 0 )
432 gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
434 if (gbl_params.debugLevel == 0 )
435 gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
437 // Copy our symbol table
438 if (gbl_params.symbolTable) {
439 DPRINTF(ECRASH_DEBUG_VERBOSE,
440 "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
441 gbl_params.symbolTable->numSymbols);
442 // Make a copy of our symbol table
443 gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
444 memcpy(gbl_params.symbolTable, params->symbolTable,
445 sizeof(eCrashSymbolTable));
447 // Now allocate / copy the actual table.
448 gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
449 gbl_params.symbolTable->numSymbols);
450 memcpy(gbl_params.symbolTable->symbols,
451 params->symbolTable->symbols,
452 sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
454 ValidateSymbolTable();
457 // And, finally, register for our signals
458 for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
459 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
460 " Catching signal[%d] %d\n", sigIndex,
461 gbl_params.signals[sigIndex]);
463 // I know there's a better way to catch signals with pthreads.
464 // I'll do it later TODO
465 signal(gbl_params.signals[sigIndex], crash_handler);
468 DPRINTF(ECRASH_DEBUG_ERROR, " Error: Null Params!\n");
471 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
476 * UnInitialize eCrash.
478 * This function may be called to de-activate eCrash, release the
479 * signal handlers, and free any memory allocated by eCrash.
481 * @return Zero on success.
483 int eCrash_Uninit( void )
488 } /* eCrash_Uninit */
491 * Register a thread for backtracing on crash.
493 * This function must be called by any thread wanting it's stack
494 * dumped in the event of a crash. The thread my specify what
495 * signal should be used, or the default, SIGUSR1 will be used.
497 * @param signo Signal to use to generate dump (default: SIGUSR1)
499 * @return Zero on success.
501 int eCrash_RegisterThread(char *name, int signo)
503 sighandler_t old_handler;
505 // Register for our signal
507 signo = gbl_params.defaultBacktraceSignal;
510 old_handler = signal(signo, bt_handler);
511 return addThreadToList(name, pthread_self(), signo, old_handler);
513 } /* eCrash_RegisterThread */
516 * Un-register a thread for stack dumps.
518 * This function may be called to un-register any previously
521 * @return Zero on success.
523 int eCrash_UnregisterThread( void )
525 return removeThreadFromList(pthread_self());
526 } /* eCrash_UnregisterThread */