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 open source 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>
43 #define NIY() printf("function not implemented yet!\n");
46 static eCrashParameters gbl_params;
48 static int gbl_backtraceEntries;
49 static void **gbl_backtraceBuffer;
50 static char **gbl_backtraceSymbols;
51 static int gbl_backtraceDoneFlag = 0;
53 static void *stack_frames[50];
54 static size_t size, NThread;
55 static char **strings;
58 * Private structures for our thread list
60 typedef struct thread_list_node{
64 sighandler_t oldHandler;
65 struct thread_list_node *Next;
68 static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
69 static ThreadListNode *ThreadList = NULL;
71 /*********************************************************************
72 *********************************************************************
73 ** P R I V A T E F U N C T I O N S
74 *********************************************************************
75 ********************************************************************/
79 * Insert a node into our threadList
81 * @param name Text string indicating our thread
82 * @param thread Our Thread Id
83 * @param signo Signal to create backtrace with
84 * @param old_handler Our old handler for signo
86 * @returns zero on success
88 static int addThreadToList(char *name, pthread_t thread,int signo,
89 sighandler_t old_handler)
93 node = malloc(sizeof(ThreadListNode));
96 DPRINTF(ECRASH_DEBUG_VERBOSE,
97 "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
98 node->threadName = strdup(name);
99 node->thread = thread;
100 node->backtraceSignal = signo;
101 node->oldHandler = old_handler;
103 /* And, add it to the list */
104 pthread_mutex_lock(&ThreadListMutex);
105 node->Next = ThreadList;
107 pthread_mutex_unlock(&ThreadListMutex);
114 * Remove a node from our threadList
116 * @param thread Our Thread Id
118 * @returns zero on success
120 static int removeThreadFromList(pthread_t thread)
122 ThreadListNode *Probe, *Prev=NULL;
123 ThreadListNode *Removed = NULL;
125 DPRINTF(ECRASH_DEBUG_VERBOSE,
126 "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
127 pthread_mutex_lock(&ThreadListMutex);
128 for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
129 if (Probe->thread == thread) {
130 // We found it! Unlink it and move on!
132 if (Prev == NULL) { // head of list
133 ThreadList = Probe->Next;
135 // Prev != null, so we need to link around ourselves.
136 Prev->Next = Probe->Next;
138 Removed->Next = NULL;
144 pthread_mutex_unlock(&ThreadListMutex);
146 // Now, if something is in Removed, free it, and return success
148 DPRINTF(ECRASH_DEBUG_VERBOSE,
149 " Found %s -- removing\n", Removed->threadName);
150 // Reset the signal handler
151 signal(Removed->backtraceSignal, Removed->oldHandler);
153 // And free the allocated memory
154 free (Removed->threadName);
159 DPRINTF(ECRASH_DEBUG_VERBOSE,
161 return -1; // Not Found
163 } // removeThreadFromList
166 * Print out a line of output to all our destinations
168 * One by one, output a line of text to all of our output destinations.
170 * Return failure if we fail to output to any of them.
172 * @param format Normal printf style vararg format
174 * @returns nothing// bytes written, or error on failure.
176 static void outputPrintf(char *format, ...)
180 va_start(ap, format);
182 vsyslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, format, ap);
188 * Dump our backtrace into a global location
190 * This function will dump out our backtrace into our
191 * global holding area.
194 static void createGlobalBacktrace( void )
197 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
198 for (NThread = 0; NThread < size; NThread++)
200 syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p ", stack_frames[NThread]);
202 strings = backtrace_symbols(stack_frames, size);
203 for (NThread = 0; NThread < size; NThread++) {
204 if (strings != NULL) {
205 syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p ", strings[NThread]);
208 } /* createGlobalBacktrace */
209 static void outputRawtrace( void )
212 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
213 for (NThread = 0; NThread < size; NThread++)
215 syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p ", stack_frames[NThread]);
217 } /* createGlobalBacktrace */
220 * Print out (to all the fds, etc), or global backtrace
222 static void outputGlobalBacktrace ( void )
226 for (i=0; i < gbl_backtraceEntries; i++) {
227 if (gbl_backtraceSymbols != FALSE) {
228 outputPrintf("* Frame %02x: %s\n",
229 i, gbl_backtraceSymbols[i]);
231 outputPrintf("* Frame %02x: %p\n", i,
232 gbl_backtraceBuffer[i]);
235 } // outputGlobalBacktrace
238 * Output our current stack's backtrace
240 static void outputBacktrace( void )
242 createGlobalBacktrace();
243 outputGlobalBacktrace();
244 } /* outputBacktrace */
246 static void outputBacktraceThreads( void )
248 ThreadListNode *probe;
251 // When we're backtracing, don't worry about the mutex . . hopefully
252 // we're in a safe place.
254 for (probe=ThreadList; probe; probe=probe->Next) {
255 gbl_backtraceDoneFlag = 0;
256 pthread_kill(probe->thread, probe->backtraceSignal);
257 for (i=0; i < gbl_params.threadWaitTime; i++) {
258 if (gbl_backtraceDoneFlag)
262 if (gbl_backtraceDoneFlag) {
263 outputPrintf("* Backtrace of \"%s\" (0x%08x)\n",
264 probe->threadName, (unsigned int)probe->thread);
265 outputGlobalBacktrace();
267 outputPrintf("* Error: unable to get backtrace of \"%s\" (0x%08x)\n",
268 probe->threadName, (unsigned int)probe->thread);
272 } // outputBacktraceThreads
276 * Handle signals (crash signals)
278 * This function will catch all crash signals, and will output the
281 * It will physically write (and sync) the current thread's information
282 * before it attempts to send signals to other threads.
284 * @param signum Signal received.
286 static void crash_handler(int signo)
289 outputPrintf("*********************************************************\n");
290 outputPrintf("* eCrash Crash Handler\n");
291 outputPrintf("*********************************************************\n");
293 outputPrintf("* Got a crash! signo=%d\n", signo);
295 outputPrintf("* Offending Thread's Backtrace:\n");
300 if (gbl_params.dumpAllThreads != FALSE) {
301 outputBacktraceThreads();
305 outputPrintf("*********************************************************\n");
306 outputPrintf("* eCrash Crash Handler\n");
307 outputPrintf("*********************************************************\n");
313 * Handle signals (bt signals)
315 * This function shoudl be called to generate a crashdump into our
316 * global area. Once the dump has been completed, this function will
317 * return after tickling a global. Since mutexes are not async
318 * signal safe, the main thread, after signaling us to generate our
319 * own backtrace, will sleep for a few seconds waiting for us to complete.
321 * @param signum Signal received.
323 static void bt_handler(int signo)
325 createGlobalBacktrace();
326 gbl_backtraceDoneFlag=1;
330 * Validate a passed-in symbol table
332 * For now, just print it out (if verbose), and make sure it's
333 * sorted and none of the pointers are zero.
335 static int ValidateSymbolTable( void )
339 unsigned long lastAddress =0;
341 // Get out of here if the table is empty
342 if (!gbl_params.symbolTable) return 0;
344 // Dump it in verbose mode
345 DPRINTF(ECRASH_DEBUG_VERBOSE,
346 "Symbol Table Provided with %d symbols\n",
347 gbl_params.symbolTable->numSymbols);
348 for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
349 // Dump it in verbose mode
350 DPRINTF(ECRASH_DEBUG_VERBOSE,
352 gbl_params.symbolTable->symbols[i].function,
353 gbl_params.symbolTable->symbols[i].address);
355 (unsigned long)gbl_params.symbolTable->symbols[i].address) {
356 DPRINTF(ECRASH_DEBUG_ERROR,
357 "Error: symbol table is not sorted (last=%p, current=%p)\n",
359 gbl_params.symbolTable->symbols[i].address);
367 } // ValidateSymbolTable
369 /*********************************************************************
370 *********************************************************************
371 ** P U B L I C F U N C T I O N S
372 *********************************************************************
373 ********************************************************************/
378 * This function must be called before calling any other eCrash
379 * functions. It sets up the global behavior of the system, and
380 * registers the calling thread for crash dumps.
382 * @param params Our input parameters. The passed in structure will be copied.
384 * @return Zero on success.
386 int eCrash_Init(eCrashParameters *params)
390 #ifdef DO_SIGNALS_RIGHT
392 struct sigaction act;
395 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
397 // Allocate our backtrace area
398 gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
400 #ifdef DO_SIGNALS_RIGHT
401 sigemptyset(&blocked);
402 act.sa_sigaction = crash_handler;
403 act.sa_mask = blocked;
404 act.sa_flags = SA_SIGINFO;
407 if (params != NULL) {
408 // Make ourselves a global copy of params.
409 gbl_params = *params;
410 gbl_params.filename = strdup(params->filename);
412 // Set our defaults, if they weren't specified
413 if (gbl_params.maxStackDepth == 0 )
414 gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
416 if (gbl_params.defaultBacktraceSignal == 0 )
417 gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
419 if (gbl_params.threadWaitTime == 0 )
420 gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
422 if (gbl_params.debugLevel == 0 )
423 gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
425 // Copy our symbol table
426 if (gbl_params.symbolTable) {
427 DPRINTF(ECRASH_DEBUG_VERBOSE,
428 "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
429 gbl_params.symbolTable->numSymbols);
430 // Make a copy of our symbol table
431 gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
432 memcpy(gbl_params.symbolTable, params->symbolTable,
433 sizeof(eCrashSymbolTable));
435 // Now allocate / copy the actual table.
436 gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
437 gbl_params.symbolTable->numSymbols);
438 memcpy(gbl_params.symbolTable->symbols,
439 params->symbolTable->symbols,
440 sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
442 ValidateSymbolTable();
445 // And, finally, register for our signals
446 for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
447 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
448 " Catching signal[%d] %d\n", sigIndex,
449 gbl_params.signals[sigIndex]);
451 // I know there's a better way to catch signals with pthreads.
452 // I'll do it later TODO
453 signal(gbl_params.signals[sigIndex], crash_handler);
456 DPRINTF(ECRASH_DEBUG_ERROR, " Error: Null Params!\n");
459 DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
464 * UnInitialize eCrash.
466 * This function may be called to de-activate eCrash, release the
467 * signal handlers, and free any memory allocated by eCrash.
469 * @return Zero on success.
471 int eCrash_Uninit( void )
476 } /* eCrash_Uninit */
479 * Register a thread for backtracing on crash.
481 * This function must be called by any thread wanting it's stack
482 * dumped in the event of a crash. The thread my specify what
483 * signal should be used, or the default, SIGUSR1 will be used.
485 * @param signo Signal to use to generate dump (default: SIGUSR1)
487 * @return Zero on success.
489 int eCrash_RegisterThread(char *name, int signo)
491 sighandler_t old_handler;
493 // Register for our signal
495 signo = gbl_params.defaultBacktraceSignal;
498 old_handler = signal(signo, bt_handler);
499 return addThreadToList(name, pthread_self(), signo, old_handler);
501 } /* eCrash_RegisterThread */
504 * Un-register a thread for stack dumps.
506 * This function may be called to un-register any previously
509 * @return Zero on success.
511 int eCrash_UnregisterThread( void )
513 return removeThreadFromList(pthread_self());
514 } /* eCrash_UnregisterThread */