// Berkeley Open Infrastructure for Network Computing // http://boinc.berkeley.edu // Copyright (C) 2005 University of California // // This is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; // either version 2.1 of the License, or (at your option) any later version. // // This software 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 Lesser General Public License for more details. // // To view the GNU Lesser General Public License visit // http://www.gnu.org/copyleft/lesser.html // or write to the Free Software Foundation, Inc., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Stuff related to stderr/stdout direction and exception handling; // used by both core client and by apps #if defined(_WIN32) && !defined(__STDWX_H__) && !defined(_BOINC_WIN_) && !defined(_AFX_STDAFX_H_) #include "boinc_win.h" #endif #ifdef __EMX__ #include #endif #ifndef _WIN32 #include "config.h" #include #include #include #include "config.h" #endif #ifdef __APPLE__ #include "mac_backtrace.h" #endif #ifdef __GLIBC__ #include #endif #include "app_ipc.h" #include "error_numbers.h" #include "filesys.h" #include "util.h" #include "parse.h" #include "diagnostics.h" #define MAX_STDERR_FILE_SIZE 2048*1024 #define MAX_STDOUT_FILE_SIZE 2048*1024 static int diagnostics_initialized = false; static int flags; static char stdout_log[256]; static char stdout_archive[256]; static FILE* stdout_file; static char stderr_log[256]; static char stderr_archive[256]; static FILE* stderr_file; static char boinc_dir[256]; static int boinc_proxy_enabled; static char boinc_proxy[256]; static char symstore[256]; static int aborted_via_gui; #ifdef _WIN32 int __cdecl boinc_message_reporting(int reportType, char *szMsg, int *retVal); #endif // stub function for initializing the diagnostics environment. // int boinc_init_diagnostics(int _flags) { int modified_flags = BOINC_DIAG_BOINCAPPLICATION | _flags; return diagnostics_init(modified_flags, BOINC_DIAG_STDOUT, BOINC_DIAG_STDERR); } // Used to cleanup the diagnostics environment. // int boinc_finish_diag() { return BOINC_SUCCESS; } // to setup an unhandled exception filter on Windows // int boinc_install_signal_handlers() { #ifdef _WIN32 SetUnhandledExceptionFilter(boinc_catch_signal); #else //_WIN32 // register handlers for fatal internal signals // so that they get reported in stderr.txt // Do NOT catch SIGQUIT because core client uses that to kill app // boinc_set_signal_handler(SIGILL, boinc_catch_signal); boinc_set_signal_handler(SIGABRT, boinc_catch_signal); boinc_set_signal_handler(SIGBUS, boinc_catch_signal); boinc_set_signal_handler(SIGSEGV, boinc_catch_signal); boinc_set_signal_handler(SIGSYS, boinc_catch_signal); boinc_set_signal_handler(SIGPIPE, boinc_catch_signal); #endif //_WIN32 return 0; } // initialize the diagnostics environment. // int diagnostics_init( int _flags, const char* stdout_prefix, const char* stderr_prefix ) { // Check to see if we have already been called if (diagnostics_initialized) { return ERR_INVALID_PARAM; } else { diagnostics_initialized = true; } // Setup initial values flags = _flags; snprintf(stdout_log, sizeof(stdout_log), "%s.txt", stdout_prefix); snprintf(stdout_archive, sizeof(stdout_archive), "%s.old", stdout_prefix); snprintf(stderr_log, sizeof(stderr_log), "%s.txt", stderr_prefix); snprintf(stderr_archive, sizeof(stderr_archive), "%s.old", stderr_prefix); strcpy(boinc_dir, ""); boinc_proxy_enabled = 0; strcpy(boinc_proxy, ""); strcpy(symstore, ""); // Check for invalid parameter combinations if ((flags & BOINC_DIAG_REDIRECTSTDERR) && (flags & BOINC_DIAG_REDIRECTSTDERROVERWRITE)) { return ERR_INVALID_PARAM; } if ((flags & BOINC_DIAG_REDIRECTSTDOUT) && (flags & BOINC_DIAG_REDIRECTSTDOUTOVERWRITE)) { return ERR_INVALID_PARAM; } // Archive any old stderr.txt and stdout.txt files, if requested if (flags & BOINC_DIAG_ARCHIVESTDERR) { boinc_copy(stderr_log, stderr_archive); } if (flags & BOINC_DIAG_ARCHIVESTDOUT) { boinc_copy(stdout_log, stdout_archive); } // Redirect stderr and/or stdout, if requested // if (flags & BOINC_DIAG_REDIRECTSTDERR) { stderr_file = freopen(stderr_log, "a", stderr); if (!stderr_file) { return ERR_FOPEN; } setbuf(stderr_file, 0); } if (flags & BOINC_DIAG_REDIRECTSTDERROVERWRITE) { stderr_file = freopen(stderr_log, "w", stderr); if (NULL == stderr_file) { return ERR_FOPEN; } } if (flags & BOINC_DIAG_REDIRECTSTDOUT) { stdout_file = freopen(stdout_log, "a", stdout); if (NULL == stdout_file) { return ERR_FOPEN; } } if (flags & BOINC_DIAG_REDIRECTSTDOUTOVERWRITE) { stdout_file = freopen(stdout_log, "w", stdout); if (NULL == stdout_file) { return ERR_FOPEN; } } #if defined(_WIN32) // Initialize the thread list structure // The data for this structure should be set by // boinc_init or boinc_init_graphics. diagnostics_init_thread_list(); diagnostics_init_unhandled_exception_monitor(); diagnostics_init_message_monitor(); #if defined(_DEBUG) _CrtSetReportHook(boinc_message_reporting); if (flags & BOINC_DIAG_MEMORYLEAKCHECKENABLED) { SET_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF); } if (flags & BOINC_DIAG_HEAPCHECKENABLED) { if (flags & BOINC_DIAG_HEAPCHECKEVERYALLOC) { SET_CRT_DEBUG_FIELD(_CRTDBG_CHECK_ALWAYS_DF); } else { SET_CRT_DEBUG_FIELD(_CRTDBG_CHECK_EVERY_1024_DF); } } #endif // defined(_DEBUG) #endif // defined(_WIN32) // Install unhandled exception filters and signal traps. if (BOINC_SUCCESS != boinc_install_signal_handlers()) { return ERR_SIGNAL_OP; } // Store the location of the BOINC directory for future use if (flags & BOINC_DIAG_BOINCAPPLICATION) { char buf[256]; char proxy_address[256]; int proxy_port; MIOFILE mf; FILE* p; strcpy(buf, ""); strcpy(proxy_address, ""); proxy_port = 0; p = fopen(INIT_DATA_FILE, "r"); if (!p) return ERR_FOPEN; mf.init_file(p); while(mf.fgets(buf, sizeof(buf))) { if (match_tag(buf, "")) break; else if (parse_str(buf, "", boinc_dir, 256)) continue; else if (parse_str(buf, "", symstore, 256)) continue; else if (match_tag(buf, "")) { boinc_proxy_enabled = true; continue; } else if (parse_str(buf, "", proxy_address, 256)) continue; else if (parse_int(buf, "", proxy_port)) continue; } fclose(p); if (boinc_proxy_enabled) { int buffer_used = snprintf(boinc_proxy, sizeof(boinc_proxy), "%s:%d", proxy_address, proxy_port); if ((sizeof(boinc_proxy) == buffer_used) || (-1 == buffer_used)) { boinc_proxy[255] = '\0'; } } } return BOINC_SUCCESS; } // has the diagnostics library been initialized? // int diagnostics_is_initialized(){ return diagnostics_initialized; } // return true if the specified flag is set. // int diagnostics_is_flag_set(int _flags) { return flags & _flags; } // return the location of the BOINC directory. // char* diagnostics_get_boinc_dir() { return boinc_dir; } // return the location of the Symbol Store. // char* diagnostics_get_symstore() { return symstore; } // do we need to worry about a proxy server? // int diagnostics_is_proxy_enabled() { return boinc_proxy_enabled; } // proxy server address and port // char* diagnostics_get_proxy() { return boinc_proxy; } // Set the value of the flag int diagnostics_set_aborted_via_gui() { aborted_via_gui = 1; return 0; } // Return the value of he flag int diagnostics_is_aborted_via_gui() { return aborted_via_gui; } // Cycle the log files at regular events. // int diagnostics_cycle_logs() { double f_size; fflush(stdout); fflush(stderr); // If the stderr.txt or stdout.txt files are too big, cycle them // if (flags & BOINC_DIAG_REDIRECTSTDERR) { #ifdef __EMX__ // OS/2 can't stat() open files! struct stat sbuf; fstat(fileno(stderr_file), &sbuf); f_size = (double)sbuf.st_size; #else file_size(stderr_log, f_size); #endif if (MAX_STDERR_FILE_SIZE < f_size) { fclose(stderr_file); boinc_copy(stderr_log, stderr_archive); stderr_file = freopen(stderr_log, "w", stderr); if (NULL == stderr_file) return ERR_FOPEN; } } if (flags & BOINC_DIAG_REDIRECTSTDOUT) { #ifdef __EMX__ // OS/2 can't stat() open files! struct stat sbuf; fstat(fileno(stdout_file), &sbuf); f_size = (double)sbuf.st_size; #else file_size(stdout_log, f_size); #endif if (MAX_STDOUT_FILE_SIZE < f_size) { fclose(stdout_file); boinc_copy(stdout_log, stdout_archive); stdout_file = freopen(stdout_log, "w", stdout); if (NULL == stdout_file) return ERR_FOPEN; } } return BOINC_SUCCESS; } #ifdef _WIN32 #ifdef _DEBUG // Trap ASSERTs and TRACEs from the CRT and spew them to stderr. // int __cdecl boinc_message_reporting(int reportType, char *szMsg, int *retVal){ (*retVal) = 0; switch(reportType){ case _CRT_WARN: case _CRT_ERROR: if (flags & BOINC_DIAG_TRACETOSTDERR) { fprintf(stderr, szMsg); fflush(stderr); } if (flags & BOINC_DIAG_TRACETOSTDOUT) { fprintf(stdout, szMsg); fflush(stdout); } break; case _CRT_ASSERT: fprintf(stderr, "ASSERT: %s\n", szMsg); fflush(stderr); (*retVal) = 1; break; } return(TRUE); } // Converts the BOINCTRACE macro into a single string and report it // to the CRT so it can be reported via the normal means. // void boinc_trace(const char *pszFormat, ...) { static char szBuffer[4096]; // Trace messages should only be reported if running as a standalone // application or told too. if ((flags & BOINC_DIAG_TRACETOSTDERR) || (flags & BOINC_DIAG_TRACETOSTDOUT)) { memset(szBuffer, 0, sizeof(szBuffer)); va_list ptr; va_start(ptr, pszFormat); vsnprintf(szBuffer, sizeof(szBuffer), pszFormat, ptr); va_end(ptr); _CrtDbgReport(_CRT_WARN, NULL, NULL, NULL, "TRACE[%d]: %s", GetCurrentThreadId(), szBuffer); } } // Converts the BOINCINFO macro into a single string and report it // to stderr so it can be reported via the normal means. // void boinc_info_debug(const char *pszFormat, ...){ static char szBuffer[4096]; memset(szBuffer, 0, sizeof(szBuffer)); va_list ptr; va_start(ptr, pszFormat); vsnprintf(szBuffer, sizeof(szBuffer), pszFormat, ptr); va_end(ptr); _CrtDbgReport(_CRT_WARN, NULL, NULL, NULL, "%s", szBuffer); } #else // _DEBUG // Converts the BOINCINFO macro into a single string and report it // to stderr so it can be reported via the normal means. // void boinc_info_release(const char *pszFormat, ...){ static char szBuffer[4096]; memset(szBuffer, 0, sizeof(szBuffer)); va_list ptr; va_start(ptr, pszFormat); vsnprintf(szBuffer, sizeof(szBuffer), pszFormat, ptr); va_end(ptr); fprintf(stderr, "%s", szBuffer); fflush(stderr); } #endif // _DEBUG #else // _WIN32 #ifdef _DEBUG // Converts the BOINCTRACE macro into a single string and report it // to the CRT so it can be reported via the normal means. // void boinc_trace(const char *pszFormat, ...) { static char szBuffer[4096]; // Trace messages should only be reported if running as a standalone // application or told too. if ((flags & BOINC_DIAG_TRACETOSTDERR) || (flags & BOINC_DIAG_TRACETOSTDOUT)) { memset(szBuffer, 0, sizeof(szBuffer)); va_list ptr; va_start(ptr, pszFormat); vsnprintf(szBuffer, sizeof(szBuffer), pszFormat, ptr); va_end(ptr); if (flags & BOINC_DIAG_TRACETOSTDERR) { fprintf(stderr, "TRACE: %s", szBuffer); fflush(stderr); } if (flags & BOINC_DIAG_TRACETOSTDOUT) { fprintf(stdout, "TRACE: %s", szBuffer); fflush(stdout); } } } // Converts the BOINCINFO macro into a single string and report it // to stderr so it can be reported via the normal means. // void boinc_info_debug(const char *pszFormat, ...){ static char szBuffer[4096]; memset(szBuffer, 0, sizeof(szBuffer)); va_list ptr; va_start(ptr, pszFormat); vsnprintf(szBuffer, sizeof(szBuffer), pszFormat, ptr); va_end(ptr); fprintf(stderr, "%s", szBuffer); fflush(stderr); } #else // _DEBUG // Converts the BOINCINFO macro into a single string and report it // to stderr so it can be reported via the normal means. // void boinc_info_release(const char *pszFormat, ...){ static char szBuffer[4096]; memset(szBuffer, 0, sizeof(szBuffer)); va_list ptr; va_start(ptr, pszFormat); vsnprintf(szBuffer, sizeof(szBuffer), pszFormat, ptr); va_end(ptr); fprintf(stderr, "%s", szBuffer); fflush(stderr); } #endif // _DEBUG #endif // _WIN32 // Diagnostics for POSIX Compatible systems. // #ifdef HAVE_SIGNAL_H // Set a signal handler only if it is not currently ignored // void boinc_set_signal_handler(int sig, void(*handler)(int)) { #ifdef HAVE_SIGACTION struct sigaction temp; sigaction(sig, NULL, &temp); if (temp.sa_handler != SIG_IGN) { temp.sa_handler = handler; // sigemptyset(&temp.sa_mask); sigaction(sig, &temp, NULL); } #else void (*temp)(int); temp = signal(sig, boinc_catch_signal); if (temp == SIG_IGN) { signal(sig, SIG_IGN); } #endif /* HAVE_SIGACTION */ } // Set a signal handler even if it is currently ignored // void boinc_set_signal_handler_force(int sig, void(*handler)(int)) { #ifdef HAVE_SIGACTION struct sigaction temp; sigaction(sig, NULL, &temp); temp.sa_handler = handler; // sigemptyset(&temp.sa_mask); sigaction(sig, &temp, NULL); #else void (*temp)(int); temp = signal(sig, boinc_catch_signal); signal(sig, SIG_IGN); #endif /* HAVE_SIGACTION */ } void boinc_catch_signal(int signal) { switch(signal) { case SIGHUP: fprintf(stderr, "SIGHUP: terminal line hangup\n"); return; case SIGINT: fprintf(stderr, "SIGINT: interrupt program\n"); break; case SIGILL: fprintf(stderr, "SIGILL: illegal instruction\n"); break; case SIGABRT: fprintf(stderr, "SIGABRT: abort called\n"); break; case SIGBUS: fprintf(stderr, "SIGBUS: bus error\n"); break; case SIGSEGV: fprintf(stderr, "SIGSEGV: segmentation violation\n"); break; case SIGSYS: fprintf(stderr, "SIGSYS: system call given invalid argument\n"); break; case SIGPIPE: fprintf(stderr, "SIGPIPE: write on a pipe with no reader\n"); return; default: fprintf(stderr, "unknown signal %d\n", signal); break; } #ifdef __GLIBC__ void *array[64]; size_t size; size = backtrace (array, 64); fprintf(stderr, "Stack trace (%d frames):\n", size); backtrace_symbols_fd(array, size, fileno(stderr)); #endif #ifdef __APPLE__ PrintBacktrace(); #endif fprintf(stderr, "\nExiting...\n"); exit(ERR_SIGNAL_CATCH); } #endif const char *BOINC_RCSID_4967ad204c = "$Id$";