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("%s: Not Implemented Yet!\n", __FUNCTION__)
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);
190 vsyslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, format, ap);
194 CtdlLogPrintf(CTDL_EMERG, format, ap);
201 * Dump our backtrace into a global location
203 * This function will dump out our backtrace into our
204 * global holding area.
207 static void createGlobalBacktrace( void )
210 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
212 for (NThread = 0; NThread < size; NThread++)
214 syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p ", stack_frames[NThread]);
217 for (NThread = 0; NThread < size; NThread++)
218 CtdlLogPrintf(1, "RAW: %p\n", stack_frames[NThread]);
219 strings = backtrace_symbols(stack_frames, size);
220 for (NThread = 0; NThread < size; NThread++) {
221 if (strings != NULL) {
223 {// vsyslogs printf compliance sucks.
224 syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p ", strings[NThread]);
227 CtdlLogPrintf(1, "%s\n", strings[NThread]);
230 } /* createGlobalBacktrace */
231 static void outputRawtrace( void )
234 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
236 for (NThread = 0; NThread < size; NThread++)
238 syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p ", stack_frames[NThread]);
241 for (NThread = 0; NThread < size; NThread++)
242 CtdlLogPrintf(1, "RAW: %p\n", stack_frames[NThread]);
243 } /* createGlobalBacktrace */
246 * Print out (to all the fds, etc), or global backtrace
248 static void outputGlobalBacktrace ( void )
252 for (i=0; i < gbl_backtraceEntries; i++) {
253 if (gbl_backtraceSymbols != FALSE) {
254 outputPrintf("* Frame %02x: %s\n",
255 i, gbl_backtraceSymbols[i]);
257 outputPrintf("* Frame %02x: %p\n", i,
258 gbl_backtraceBuffer[i]);
261 } // outputGlobalBacktrace
264 * Output our current stack's backtrace
266 static void outputBacktrace( void )
268 createGlobalBacktrace();
269 outputGlobalBacktrace();
270 } /* outputBacktrace */
272 static void outputBacktraceThreads( void )
274 ThreadListNode *probe;
277 // When we're backtracing, don't worry about the mutex . . hopefully
278 // we're in a safe place.
280 for (probe=ThreadList; probe; probe=probe->Next) {
281 gbl_backtraceDoneFlag = 0;
282 pthread_kill(probe->thread, probe->backtraceSignal);
283 for (i=0; i < gbl_params.threadWaitTime; i++) {
284 if (gbl_backtraceDoneFlag)
288 if (gbl_backtraceDoneFlag) {
289 outputPrintf("* Backtrace of \"%s\" (0x%08x)\n",
290 probe->threadName, (unsigned int)probe->thread);
291 outputGlobalBacktrace();
293 outputPrintf("* Error: unable to get backtrace of \"%s\" (0x%08x)\n",
294 probe->threadName, (unsigned int)probe->thread);
298 } // outputBacktraceThreads
302 * Handle signals (crash signals)
304 * This function will catch all crash signals, and will output the
307 * It will physically write (and sync) the current thread's information
308 * before it attempts to send signals to other threads.
310 * @param signum Signal received.
312 static void crash_handler(int signo)
315 outputPrintf("*********************************************************\n");
316 outputPrintf("* eCrash Crash Handler\n");
317 outputPrintf("*********************************************************\n");
319 outputPrintf("* Got a crash! signo=%d\n", signo);
321 outputPrintf("* Offending Thread's Backtrace:\n");
326 if (gbl_params.dumpAllThreads != FALSE) {
327 outputBacktraceThreads();
331 outputPrintf("*********************************************************\n");
332 outputPrintf("* eCrash Crash Handler\n");
333 outputPrintf("*********************************************************\n");
339 * Handle signals (bt signals)
341 * This function shoudl be called to generate a crashdump into our
342 * global area. Once the dump has been completed, this function will
343 * return after tickling a global. Since mutexes are not async
344 * signal safe, the main thread, after signaling us to generate our
345 * own backtrace, will sleep for a few seconds waiting for us to complete.
347 * @param signum Signal received.
349 static void bt_handler(int signo)
351 createGlobalBacktrace();
352 gbl_backtraceDoneFlag=1;
356 * Validate a passed-in symbol table
358 * For now, just print it out (if verbose), and make sure it's
359 * sorted and none of the pointers are zero.
361 static int ValidateSymbolTable( void )
365 unsigned long lastAddress =0;
367 // Get out of here if the table is empty
368 if (!gbl_params.symbolTable) return 0;
370 // Dump it in verbose mode
371 DPRINTF(ECRASH_DEBUG_VERBOSE,
372 "Symbol Table Provided with %d symbols\n",
373 gbl_params.symbolTable->numSymbols);
374 for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
375 // Dump it in verbose mode
376 DPRINTF(ECRASH_DEBUG_VERBOSE,
378 gbl_params.symbolTable->symbols[i].function,
379 gbl_params.symbolTable->symbols[i].address);
381 (unsigned long)gbl_params.symbolTable->symbols[i].address) {
382 DPRINTF(ECRASH_DEBUG_ERROR,
383 "Error: symbol table is not sorted (last=%p, current=%p)\n",
385 gbl_params.symbolTable->symbols[i].address);
393 } // ValidateSymbolTable
395 /*********************************************************************
396 *********************************************************************
397 ** P U B L I C F U N C T I O N S
398 *********************************************************************
399 ********************************************************************/
404 * This function must be called before calling any other eCrash
405 * functions. It sets up the global behavior of the system, and
406 * registers the calling thread for crash dumps.
408 * @param params Our input parameters. The passed in structure will be copied.
410 * @return Zero on success.
412 int eCrash_Init(eCrashParameters *params)
416 #ifdef DO_SIGNALS_RIGHT
418 struct sigaction act;
421 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
423 // Allocate our backtrace area
424 gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
426 #ifdef DO_SIGNALS_RIGHT
427 sigemptyset(&blocked);
428 act.sa_sigaction = crash_handler;
429 act.sa_mask = blocked;
430 act.sa_flags = SA_SIGINFO;
433 if (params != NULL) {
434 // Make ourselves a global copy of params.
435 gbl_params = *params;
436 gbl_params.filename = strdup(params->filename);
438 // Set our defaults, if they weren't specified
439 if (gbl_params.maxStackDepth == 0 )
440 gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
442 if (gbl_params.defaultBacktraceSignal == 0 )
443 gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
445 if (gbl_params.threadWaitTime == 0 )
446 gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
448 if (gbl_params.debugLevel == 0 )
449 gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
451 // Copy our symbol table
452 if (gbl_params.symbolTable) {
453 DPRINTF(ECRASH_DEBUG_VERBOSE,
454 "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
455 gbl_params.symbolTable->numSymbols);
456 // Make a copy of our symbol table
457 gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
458 memcpy(gbl_params.symbolTable, params->symbolTable,
459 sizeof(eCrashSymbolTable));
461 // Now allocate / copy the actual table.
462 gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
463 gbl_params.symbolTable->numSymbols);
464 memcpy(gbl_params.symbolTable->symbols,
465 params->symbolTable->symbols,
466 sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
468 ValidateSymbolTable();
471 // And, finally, register for our signals
472 for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
473 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
474 " Catching signal[%d] %d\n", sigIndex,
475 gbl_params.signals[sigIndex]);
477 // I know there's a better way to catch signals with pthreads.
478 // I'll do it later TODO
479 signal(gbl_params.signals[sigIndex], crash_handler);
482 DPRINTF(ECRASH_DEBUG_ERROR, " Error: Null Params!\n");
485 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
490 * UnInitialize eCrash.
492 * This function may be called to de-activate eCrash, release the
493 * signal handlers, and free any memory allocated by eCrash.
495 * @return Zero on success.
497 int eCrash_Uninit( void )
502 } /* eCrash_Uninit */
505 * Register a thread for backtracing on crash.
507 * This function must be called by any thread wanting it's stack
508 * dumped in the event of a crash. The thread my specify what
509 * signal should be used, or the default, SIGUSR1 will be used.
511 * @param signo Signal to use to generate dump (default: SIGUSR1)
513 * @return Zero on success.
515 int eCrash_RegisterThread(char *name, int signo)
517 sighandler_t old_handler;
519 // Register for our signal
521 signo = gbl_params.defaultBacktraceSignal;
524 old_handler = signal(signo, bt_handler);
525 return addThreadToList(name, pthread_self(), signo, old_handler);
527 } /* eCrash_RegisterThread */
530 * Un-register a thread for stack dumps.
532 * This function may be called to un-register any previously
535 * @return Zero on success.
537 int eCrash_UnregisterThread( void )
539 return removeThreadFromList(pthread_self());
540 } /* eCrash_UnregisterThread */