| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| |
| /* system headers */ |
| #include "system.h" |
| |
| #define MAX_STACK_FRAMES 256 |
| |
| #if defined( MACOSX ) |
| |
| #if defined( INTEL ) |
| #include "backtrace.h" |
| #define INCLUDE_BACKTRACE |
| #define STACKTYPE "MacOsX_X86" |
| #endif /* INTEL */ |
| |
| #endif /* MACOSX */ |
| |
| #ifdef LINUX |
| #include <execinfo.h> |
| #include <link.h> |
| #define INCLUDE_BACKTRACE |
| #define STACKTYPE "Linux" |
| #endif |
| |
| #ifdef SOLARIS |
| |
| #include "backtrace.h" |
| #define INCLUDE_BACKTRACE |
| |
| #if defined( SPARC ) |
| #define STACKTYPE "Solaris_Sparc" |
| #elif defined( INTEL ) |
| #define STACKTYPE "Solaris_X86" |
| #else |
| #define STACKTYPE "Solaris_Unknown" |
| #endif |
| |
| #endif /* defined SOLARIS */ |
| |
| #include <osl/diagnose.h> |
| #include <osl/mutex.h> |
| #include <osl/signal.h> |
| #include <osl/process.h> |
| #include <osl/thread.h> |
| #include <rtl/bootstrap.h> |
| #include <rtl/digest.h> |
| |
| #include "file_path_helper.h" |
| |
| #define ACT_IGNORE 1 |
| #define ACT_EXIT 2 |
| #define ACT_SYSTEM 3 |
| #define ACT_HIDE 4 |
| #ifdef SAL_ENABLE_CRASH_REPORT |
| # define ACT_ABORT 5 |
| #else |
| # define ACT_ABORT ACT_SYSTEM |
| #endif |
| |
| #define MAX_PATH_LEN 2048 |
| |
| typedef struct _oslSignalHandlerImpl |
| { |
| oslSignalHandlerFunction Handler; |
| void* pData; |
| struct _oslSignalHandlerImpl* pNext; |
| } oslSignalHandlerImpl; |
| |
| static struct SignalAction |
| { |
| int Signal; |
| int Action; |
| void (*Handler)(int); |
| } Signals[] = |
| { |
| { SIGHUP, ACT_IGNORE, NULL }, /* hangup */ |
| { SIGINT, ACT_EXIT, NULL }, /* interrupt (rubout) */ |
| { SIGQUIT, ACT_EXIT, NULL }, /* quit (ASCII FS) */ |
| { SIGILL, ACT_SYSTEM, NULL }, /* illegal instruction (not reset when caught) */ |
| /* changed from ACT_ABOUT to ACT_SYSTEM to try and get collector to run*/ |
| { SIGTRAP, ACT_ABORT, NULL }, /* trace trap (not reset when caught) */ |
| #if ( SIGIOT != SIGABRT ) |
| { SIGIOT, ACT_ABORT, NULL }, /* IOT instruction */ |
| #endif |
| { SIGABRT, ACT_ABORT, NULL }, /* used by abort, replace SIGIOT in the future */ |
| #ifdef SIGEMT |
| { SIGEMT, ACT_SYSTEM, NULL }, /* EMT instruction */ |
| /* changed from ACT_ABORT to ACT_SYSTEM to remove handler*/ |
| /* SIGEMT may also be used by the profiler - so it is probably not a good |
| plan to have the new handler use this signal*/ |
| #endif |
| { SIGFPE, ACT_ABORT, NULL }, /* floating point exception */ |
| { SIGKILL, ACT_SYSTEM, NULL }, /* kill (cannot be caught or ignored) */ |
| { SIGBUS, ACT_ABORT, NULL }, /* bus error */ |
| { SIGSEGV, ACT_ABORT, NULL }, /* segmentation violation */ |
| #ifdef SIGSYS |
| { SIGSYS, ACT_ABORT, NULL }, /* bad argument to system call */ |
| #endif |
| { SIGPIPE, ACT_HIDE, NULL }, /* write on a pipe with no one to read it */ |
| { SIGALRM, ACT_EXIT, NULL }, /* alarm clock */ |
| { SIGTERM, ACT_EXIT, NULL }, /* software termination signal from kill */ |
| { SIGUSR1, ACT_SYSTEM, NULL }, /* user defined signal 1 */ |
| { SIGUSR2, ACT_SYSTEM, NULL }, /* user defined signal 2 */ |
| { SIGCHLD, ACT_SYSTEM, NULL }, /* child status change */ |
| #ifdef SIGPWR |
| { SIGPWR, ACT_IGNORE, NULL }, /* power-fail restart */ |
| #endif |
| { SIGWINCH, ACT_IGNORE, NULL }, /* window size change */ |
| { SIGURG, ACT_EXIT, NULL }, /* urgent socket condition */ |
| #ifdef SIGPOLL |
| { SIGPOLL, ACT_EXIT, NULL }, /* pollable event occured */ |
| #endif |
| { SIGSTOP, ACT_SYSTEM, NULL }, /* stop (cannot be caught or ignored) */ |
| { SIGTSTP, ACT_SYSTEM, NULL }, /* user stop requested from tty */ |
| { SIGCONT, ACT_SYSTEM, NULL }, /* stopped process has been continued */ |
| { SIGTTIN, ACT_SYSTEM, NULL }, /* background tty read attempted */ |
| { SIGTTOU, ACT_SYSTEM, NULL }, /* background tty write attempted */ |
| { SIGVTALRM, ACT_EXIT, NULL }, /* virtual timer expired */ |
| { SIGPROF, ACT_SYSTEM, NULL }, /* profiling timer expired */ |
| /*Change from ACT_EXIT to ACT_SYSTEM for SIGPROF is so that profiling signals do |
| not get taken by the new handler - the new handler does not pass on context |
| information which causes 'collect' to crash. This is a way of avoiding |
| what looks like a bug in the new handler*/ |
| { SIGXCPU, ACT_ABORT, NULL }, /* exceeded cpu limit */ |
| { SIGXFSZ, ACT_ABORT, NULL } /* exceeded file size limit */ |
| }; |
| const int NoSignals = sizeof(Signals) / sizeof(struct SignalAction); |
| |
| static sal_Bool bErrorReportingEnabled = sal_True; |
| static sal_Bool bInitSignal = sal_False; |
| static oslMutex SignalListMutex; |
| static oslSignalHandlerImpl* SignalList; |
| static sal_Bool bDoHardKill = sal_False; |
| static sal_Bool bSetSEGVHandler = sal_False; |
| static sal_Bool bSetWINCHHandler = sal_False; |
| static sal_Bool bSetILLHandler = sal_False; |
| |
| static void SignalHandlerFunction(int); |
| |
| static void getExecutableName_Impl (rtl_String ** ppstrProgName) |
| { |
| rtl_uString * ustrProgFile = 0; |
| osl_getExecutableFile (&ustrProgFile); |
| if (ustrProgFile) |
| { |
| rtl_uString * ustrProgName = 0; |
| osl_systemPathGetFileNameOrLastDirectoryPart (ustrProgFile, &ustrProgName); |
| if (ustrProgName != 0) |
| { |
| rtl_uString2String ( |
| ppstrProgName, |
| rtl_uString_getStr (ustrProgName), rtl_uString_getLength (ustrProgName), |
| osl_getThreadTextEncoding(), |
| OUSTRING_TO_OSTRING_CVTFLAGS); |
| rtl_uString_release (ustrProgName); |
| } |
| rtl_uString_release (ustrProgFile); |
| } |
| } |
| |
| static sal_Bool is_soffice_Impl (void) |
| { |
| sal_Int32 idx = -1; |
| rtl_String * strProgName = 0; |
| |
| getExecutableName_Impl (&strProgName); |
| if (strProgName) |
| { |
| idx = rtl_str_indexOfStr (rtl_string_getStr (strProgName), "soffice"); |
| rtl_string_release (strProgName); |
| } |
| return (idx != -1); |
| } |
| |
| static sal_Bool InitSignal() |
| { |
| int i; |
| struct sigaction act; |
| struct sigaction oact; |
| sigset_t unset; |
| |
| if (is_soffice_Impl()) |
| { |
| sal_uInt32 argi; |
| sal_uInt32 argc; |
| rtl_uString *ustrCommandArg = 0; |
| |
| argc = osl_getCommandArgCount(); |
| for ( argi = 0; argi < argc; argi++ ) |
| { |
| if (osl_Process_E_None == osl_getCommandArg (argi, &ustrCommandArg)) |
| { |
| if (0 == rtl_ustr_ascii_compare (rtl_uString_getStr (ustrCommandArg), "-bean")) |
| { |
| bDoHardKill = sal_True; |
| break; |
| } |
| } |
| } |
| if (ustrCommandArg) |
| { |
| rtl_uString_release (ustrCommandArg); |
| ustrCommandArg = 0; |
| } |
| |
| // WORKAROUND FOR SEGV HANDLER CONFLICT |
| // |
| // the java jit needs SIGSEGV for proper work |
| // and we need SIGSEGV for the office crashguard |
| // |
| // TEMPORARY SOLUTION: |
| // the office sets the signal handler during startup |
| // java can than overwrite it, if needed |
| bSetSEGVHandler = sal_True; |
| |
| // WORKAROUND FOR WINCH HANDLER (SEE ABOVE) |
| bSetWINCHHandler = sal_True; |
| |
| // WORKAROUND FOR ILLEGAL INSTRUCTION HANDLER (SEE ABOVE) |
| bSetILLHandler = sal_True; |
| } |
| |
| SignalListMutex = osl_createMutex(); |
| |
| act.sa_handler = SignalHandlerFunction; |
| act.sa_flags = SA_RESTART; |
| |
| sigfillset(&(act.sa_mask)); |
| |
| /* Initialize the rest of the signals */ |
| for (i = 0; i < NoSignals; i++) |
| { |
| /* hack: stomcatd is attaching JavaVM wich dont work with an sigaction(SEGV) */ |
| if ((bSetSEGVHandler || Signals[i].Signal != SIGSEGV) |
| && (bSetWINCHHandler || Signals[i].Signal != SIGWINCH) |
| && (bSetILLHandler || Signals[i].Signal != SIGILL)) |
| { |
| if (Signals[i].Action != ACT_SYSTEM) |
| { |
| if (Signals[i].Action == ACT_HIDE) |
| { |
| struct sigaction ign; |
| |
| ign.sa_handler = SIG_IGN; |
| ign.sa_flags = 0; |
| sigemptyset(&ign.sa_mask); |
| |
| if (sigaction(Signals[i].Signal, &ign, &oact) == 0) |
| Signals[i].Handler = oact.sa_handler; |
| else |
| Signals[i].Handler = SIG_DFL; |
| } |
| else |
| if (sigaction(Signals[i].Signal, &act, &oact) == 0) |
| Signals[i].Handler = oact.sa_handler; |
| else |
| Signals[i].Handler = SIG_DFL; |
| } |
| } |
| } |
| |
| /* Clear signal mask inherited from parent process (on Mac OS X, upon a |
| crash soffice re-execs itself from within the signal handler, so the |
| second soffice would have the guilty signal blocked and would freeze upon |
| encountering a similar crash again): */ |
| if (sigemptyset(&unset) < 0 || |
| pthread_sigmask(SIG_SETMASK, &unset, NULL) < 0) |
| { |
| OSL_TRACE("sigemptyset or pthread_sigmask failed"); |
| } |
| |
| return sal_True; |
| } |
| |
| static sal_Bool DeInitSignal() |
| { |
| int i; |
| struct sigaction act; |
| |
| act.sa_flags = 0; |
| sigemptyset(&(act.sa_mask)); |
| |
| /* Initialize the rest of the signals */ |
| for (i = NoSignals - 1; i >= 0; i--) |
| if (Signals[i].Action != ACT_SYSTEM) |
| { |
| act.sa_handler = Signals[i].Handler; |
| |
| sigaction(Signals[i].Signal, &act, NULL); |
| } |
| |
| osl_destroyMutex(SignalListMutex); |
| |
| return sal_False; |
| } |
| |
| #if defined (SAL_ENABLE_CRASH_REPORT) && defined(INCLUDE_BACKTRACE) |
| |
| /*****************************************************************************/ |
| /* Generate MD5 checksum */ |
| /*****************************************************************************/ |
| |
| static sal_uInt32 calc_md5_checksum( const char *filename, sal_uInt8 *pChecksum, sal_uInt32 nChecksumLen ) |
| { |
| sal_uInt32 nBytesProcessed = 0; |
| |
| FILE *fp = fopen( filename, "r" ); |
| |
| if ( fp ) |
| { |
| rtlDigest digest = rtl_digest_createMD5(); |
| |
| if ( digest ) |
| { |
| size_t nBytesRead; |
| sal_uInt8 buffer[4096]; |
| rtlDigestError error = rtl_Digest_E_None; |
| |
| while ( rtl_Digest_E_None == error && |
| 0 != (nBytesRead = fread( buffer, 1, sizeof(buffer), fp )) ) |
| { |
| error = rtl_digest_updateMD5( digest, buffer, nBytesRead ); |
| nBytesProcessed += nBytesRead; |
| } |
| |
| if ( rtl_Digest_E_None == error ) |
| { |
| error = rtl_digest_getMD5( digest, pChecksum, nChecksumLen ); |
| } |
| |
| if ( rtl_Digest_E_None != error ) |
| nBytesProcessed = 0; |
| |
| rtl_digest_destroyMD5( digest ); |
| } |
| |
| fclose( fp ); |
| } |
| |
| return nBytesProcessed; |
| } |
| |
| /*****************************************************************************/ |
| /* Call crash reporter */ |
| /*****************************************************************************/ |
| |
| /* Helper function to encode and write a string to a stream */ |
| |
| static int fputs_xml( const char *string, FILE *stream ) |
| { |
| int result = 0; |
| |
| while ( result >= 0 && *string ) |
| { |
| switch( *string ) |
| { |
| case '&': |
| result = fputs( "&", stream ); |
| break; |
| case '<': |
| result = fputs( "<", stream ); |
| break; |
| case '>': |
| result = fputs( ">", stream ); |
| break; |
| default: |
| result = fputc( *string, stream ); |
| break; |
| } |
| |
| string++; |
| } |
| |
| return result; |
| } |
| #endif |
| |
| /* Create intermediate files and run crash reporter */ |
| |
| #define REPORTENV_PARAM "-crashreportenv:" |
| |
| #if defined SAL_ENABLE_CRASH_REPORT && defined INCLUDE_BACKTRACE && \ |
| defined LINUX |
| |
| typedef struct |
| { |
| const char *name; |
| ElfW(Off) offset; |
| } dynamic_entry; |
| |
| static int |
| callback(struct dl_phdr_info *info, size_t size, void *data) |
| { |
| const ElfW(Phdr) *pDynamic = NULL; |
| |
| if (size == sizeof(struct dl_phdr_info)) |
| { |
| int i; |
| for (i = 0; i < info->dlpi_phnum; ++i) |
| { |
| if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) |
| { |
| pDynamic = &(info->dlpi_phdr[i]); |
| break; |
| } |
| } |
| } |
| |
| if (pDynamic) |
| { |
| char buffer[100]; |
| int len; |
| char exe[PATH_MAX]; |
| const char *dsoname = info->dlpi_name; |
| |
| dynamic_entry* entry = (dynamic_entry*)data; |
| |
| if (strcmp(dsoname, "") == 0) |
| { |
| snprintf(buffer, sizeof(buffer), "/proc/%d/exe", getpid()); |
| if ((len = readlink(buffer, exe, PATH_MAX)) != -1) |
| { |
| exe[len] = '\0'; |
| dsoname = exe; |
| } |
| } |
| |
| if (strcmp(dsoname, entry->name) == 0) |
| { |
| entry->offset = pDynamic->p_offset; |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| /* Get the location of the .dynamic section offset for the given elf file. |
| * i.e. same as the "Offset" value shown for DYNAMIC from readelf -l foo |
| * |
| * We want to know this value so that if the binaries have been modifed |
| * by prelink then we can still process the call stack on server side |
| * by comparing this value to that of an "un-prelinked but known to be |
| * otherwise equivalent" version of those binaries and adjust the call |
| * stack addresses by the differences between .dynamic addresses so as |
| * to be able to map the prelinked addresses back to the unprelinked |
| * addresses |
| * |
| * cmc@openoffice.org |
| */ |
| static ElfW(Off) |
| dynamic_section_offset(const char *name) |
| { |
| dynamic_entry entry; |
| |
| entry.name = name; |
| entry.offset = 0; |
| |
| dl_iterate_phdr(callback, &entry); |
| |
| return entry.offset; |
| } |
| #endif |
| |
| static int ReportCrash( int Signal ) |
| { |
| #ifdef SAL_ENABLE_CRASH_REPORT |
| static sal_Bool bCrashReporterExecuted = sal_False; |
| sal_Bool bAutoCrashReport = sal_False; |
| |
| sal_uInt32 argi; |
| sal_uInt32 argc; |
| rtl_uString *ustrCommandArg = NULL; |
| |
| if ( !bErrorReportingEnabled ) |
| return -1; |
| |
| argc = osl_getCommandArgCount(); |
| |
| for ( argi = 0; argi < argc; argi++ ) |
| { |
| if ( osl_Process_E_None == osl_getCommandArg( argi, &ustrCommandArg ) ) |
| { |
| if ( 0 == rtl_ustr_ascii_compare( rtl_uString_getStr( ustrCommandArg ), "-nocrashreport" ) ) |
| { |
| rtl_uString_release( ustrCommandArg ); |
| return -1; |
| } |
| else if ( 0 == rtl_ustr_ascii_compare( rtl_uString_getStr( ustrCommandArg ), "-autocrashreport" ) ) |
| { |
| bAutoCrashReport = sal_True; |
| } |
| else if ( 0 == rtl_ustr_ascii_shortenedCompare_WithLength( |
| rtl_uString_getStr( ustrCommandArg ), rtl_uString_getLength( ustrCommandArg ), |
| REPORTENV_PARAM, strlen(REPORTENV_PARAM) ) |
| ) |
| { |
| rtl_uString *ustrEnvironment = NULL; |
| rtl_String *strEnv = NULL; |
| |
| rtl_uString_newFromStr( &ustrEnvironment, rtl_uString_getStr( ustrCommandArg ) + strlen(REPORTENV_PARAM) ); |
| |
| if ( ustrEnvironment ) |
| { |
| rtl_uString2String( |
| &strEnv, |
| rtl_uString_getStr( ustrEnvironment ), rtl_uString_getLength( ustrEnvironment ), |
| osl_getThreadTextEncoding(), OUSTRING_TO_OSTRING_CVTFLAGS |
| ); |
| |
| if ( strEnv ) |
| { |
| putenv( rtl_string_getStr( strEnv ) ); |
| rtl_string_release( strEnv ); |
| } |
| |
| rtl_uString_release( ustrEnvironment ); |
| } |
| |
| } |
| |
| } |
| } |
| |
| if ( ustrCommandArg ) |
| rtl_uString_release( ustrCommandArg ); |
| |
| if ( !bCrashReporterExecuted ) |
| { |
| int i; |
| /* struct sigaction act; */ |
| |
| for (i = 0; i < NoSignals; i++) |
| { |
| if (Signals[i].Signal == Signal && Signals[i].Action == ACT_ABORT ) |
| { |
| int ret; |
| char szShellCmd[512] = { '\0' }; |
| char *pXMLTempName = NULL; |
| char *pStackTempName = NULL; |
| char *pChecksumTempName = NULL; |
| |
| #ifdef INCLUDE_BACKTRACE |
| char szXMLTempNameBuffer[L_tmpnam]; |
| char szChecksumTempNameBuffer[L_tmpnam]; |
| char szStackTempNameBuffer[L_tmpnam]; |
| |
| void *stackframes[MAX_STACK_FRAMES]; |
| int iFrame; |
| int nFrames = backtrace( stackframes, sizeof(stackframes)/sizeof(stackframes[0])); |
| |
| FILE *xmlout = NULL, *stackout = NULL, *checksumout = NULL; |
| int fdxml, fdstk, fdchksum; |
| |
| strncpy( szXMLTempNameBuffer, P_tmpdir, sizeof(szXMLTempNameBuffer) ); |
| strncat( szXMLTempNameBuffer, "/crxmlXXXXXX", sizeof(szXMLTempNameBuffer) ); |
| |
| strncpy( szStackTempNameBuffer, P_tmpdir, sizeof(szStackTempNameBuffer) ); |
| strncat( szStackTempNameBuffer, "/crstkXXXXXX", sizeof(szStackTempNameBuffer) ); |
| |
| strncpy( szChecksumTempNameBuffer, P_tmpdir, sizeof(szChecksumTempNameBuffer) ); |
| strncat( szChecksumTempNameBuffer, "/crchkXXXXXX", sizeof(szChecksumTempNameBuffer) ); |
| |
| fdxml = mkstemp(szXMLTempNameBuffer); |
| fdstk = mkstemp(szStackTempNameBuffer); |
| fdchksum = mkstemp(szChecksumTempNameBuffer); |
| |
| xmlout = fdopen( fdxml , "w" ); |
| stackout = fdopen( fdstk , "w" ); |
| checksumout = fdopen( fdchksum, "w" ); |
| |
| pXMLTempName = szXMLTempNameBuffer; |
| pStackTempName = szStackTempNameBuffer; |
| pChecksumTempName = szChecksumTempNameBuffer; |
| |
| |
| if ( xmlout && stackout && checksumout ) |
| { |
| fprintf( xmlout, "<errormail:Stack type=\"%s\">\n", STACKTYPE ); |
| |
| fprintf( checksumout, "<errormail:Checksums type=\"MD5\">\n" ); |
| |
| for ( iFrame = 0; iFrame < nFrames; iFrame++ ) |
| { |
| Dl_info dl_info; |
| |
| fprintf( stackout, "0x%" SAL_PRIxUINTPTR ":", |
| SAL_INT_CAST(sal_uIntPtr, stackframes[iFrame]) ); |
| |
| fprintf( xmlout, "<errormail:StackInfo pos=\"%d\" ip=\"0x%" SAL_PRIxUINTPTR "\"", |
| iFrame, |
| SAL_INT_CAST(sal_uIntPtr, stackframes[iFrame]) |
| ); |
| |
| memset( &dl_info, 0, sizeof(dl_info) ); |
| |
| /* dladdr may fail */ |
| if ( dladdr( stackframes[iFrame], &dl_info) ) |
| { |
| const char *dli_fname = NULL; |
| char *dli_fdir = NULL; |
| char szDirectory[PATH_MAX]; |
| char szCanonicDirectory[PATH_MAX]; |
| |
| /* Don't expect that dladdr filled all members of dl_info */ |
| |
| dli_fname = dl_info.dli_fname ? strrchr( dl_info.dli_fname, '/' ) : NULL; |
| if ( dli_fname ) |
| { |
| ++dli_fname; |
| memcpy( szDirectory, dl_info.dli_fname, dli_fname - dl_info.dli_fname ); |
| szDirectory[dli_fname - dl_info.dli_fname] = 0; |
| |
| dli_fdir = realpath( szDirectory, szCanonicDirectory ) ? szCanonicDirectory : szDirectory; |
| |
| if ( *dli_fdir && dli_fdir[ strlen(dli_fdir) - 1 ] != '/' ) |
| strcat( dli_fdir, "/" ); |
| } |
| else |
| dli_fname = dl_info.dli_fname; |
| |
| /* create checksum of library on stack */ |
| if ( dli_fname ) |
| { |
| sal_uInt8 checksum[RTL_DIGEST_LENGTH_MD5]; |
| |
| sal_uInt32 nBytesProcessed = calc_md5_checksum( |
| dl_info.dli_fname, checksum, sizeof(checksum) ); |
| if ( nBytesProcessed ) |
| { |
| int j; |
| |
| fprintf( checksumout, "<errormail:Checksum sum=\"0x" ); |
| for ( j = 0; j < 16; fprintf( checksumout, "%02X", checksum[j++] ) ); |
| fprintf( checksumout, |
| "\" bytes=\"%lu\" file=\"%s\"/>\n", |
| SAL_INT_CAST( |
| unsigned long, nBytesProcessed), |
| dli_fname ); |
| } |
| } |
| |
| if ( dl_info.dli_fbase && dl_info.dli_fname ) |
| { |
| #ifdef LINUX |
| ElfW(Off) dynamic_offset = dynamic_section_offset(dl_info.dli_fname); |
| fprintf( stackout, " 0x%" SAL_PRI_SIZET "x:", dynamic_offset); |
| #endif |
| |
| fprintf( stackout, " %s + 0x%" SAL_PRI_PTRDIFFT "x", |
| dl_info.dli_fname, |
| (char*)stackframes[iFrame] - (char*)dl_info.dli_fbase |
| ); |
| |
| fprintf( xmlout, " rel=\"0x%" SAL_PRI_PTRDIFFT "x\"", (char *)stackframes[iFrame] - (char *)dl_info.dli_fbase ); |
| if ( dli_fname ) |
| fprintf( xmlout, " name=\"%s\"", dli_fname ); |
| |
| if ( dli_fdir ) |
| fprintf( xmlout, " path=\"%s\"", dli_fdir ); |
| |
| #ifdef LINUX |
| fprintf( xmlout, " dynamicoffset=\"0x%" SAL_PRI_SIZET "x\"", dynamic_offset ); |
| #endif |
| } |
| else |
| fprintf( stackout, " ????????" ); |
| |
| if ( dl_info.dli_sname && dl_info.dli_saddr ) |
| { |
| fputs( " (", stackout ); |
| fputs_xml( dl_info.dli_sname, stackout ); |
| fprintf( stackout, " + 0x%" SAL_PRI_PTRDIFFT "x)", |
| (char*)stackframes[iFrame] - (char*)dl_info.dli_saddr ); |
| |
| fputs( " ordinal=\"", xmlout ); |
| fputs_xml( dl_info.dli_sname, xmlout ); |
| fprintf( xmlout, "+0x%" SAL_PRI_PTRDIFFT "x\"", |
| (char *)stackframes[iFrame] - (char *)dl_info.dli_saddr ); |
| } |
| |
| } |
| else /* dladdr failed */ |
| { |
| fprintf( stackout, " ????????" ); |
| } |
| |
| fprintf( stackout, "\n" ); |
| fprintf( xmlout, "/>\n" ); |
| |
| } |
| |
| fprintf( xmlout, "</errormail:Stack>\n" ); |
| fprintf( checksumout, "</errormail:Checksums>\n" ); |
| } |
| else |
| { |
| pXMLTempName = NULL; |
| pStackTempName = NULL; |
| pChecksumTempName = NULL; |
| } |
| |
| if ( stackout ) |
| fclose( stackout ); |
| if ( xmlout ) |
| fclose( xmlout ); |
| if ( checksumout ) |
| fclose( checksumout ); |
| |
| if ( pXMLTempName && pChecksumTempName && pStackTempName ) |
| #endif /* INCLUDE_BACKTRACE */ |
| { |
| rtl_uString * crashrep_url = NULL; |
| rtl_uString * crashrep_path = NULL; |
| rtl_String * crashrep_path_system = NULL; |
| rtl_string2UString( |
| &crashrep_url, |
| RTL_CONSTASCII_USTRINGPARAM( |
| "$OOO_BASE_DIR/program/crashrep"), |
| OSTRING_TO_OUSTRING_CVTFLAGS); |
| rtl_bootstrap_expandMacros(&crashrep_url); |
| osl_getSystemPathFromFileURL(crashrep_url, &crashrep_path); |
| rtl_uString2String( |
| &crashrep_path_system, |
| rtl_uString_getStr(crashrep_path), |
| rtl_uString_getLength(crashrep_path), |
| osl_getThreadTextEncoding(), |
| (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
| | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)); |
| rtl_uString_release(crashrep_url); |
| rtl_uString_release(crashrep_path); |
| #if defined INCLUDE_BACKTRACE && (defined LINUX || defined MACOSX) |
| snprintf( szShellCmd, sizeof(szShellCmd)/sizeof(szShellCmd[0]), |
| "%s -p %d -s %d -xml %s -chksum %s -stack %s -noui%s", |
| rtl_string_getStr(crashrep_path_system), |
| getpid(), |
| Signal, |
| pXMLTempName, |
| pChecksumTempName, |
| pStackTempName, |
| bAutoCrashReport ? " -send" : "" ); |
| #elif defined INCLUDE_BACKTRACE && defined SOLARIS |
| snprintf( szShellCmd, sizeof(szShellCmd)/sizeof(szShellCmd[0]), |
| "%s -p %d -s %d -xml %s -chksum %s -noui%s", |
| rtl_string_getStr(crashrep_path_system), |
| getpid(), |
| Signal, |
| pXMLTempName, |
| pChecksumTempName, |
| bAutoCrashReport ? " -send" : "" ); |
| #else |
| snprintf( szShellCmd, sizeof(szShellCmd)/sizeof(szShellCmd[0]), |
| "%s -p %d -s %d -noui%s", |
| rtl_string_getStr(crashrep_path_system), |
| getpid(), Signal, bAutoCrashReport ? " -send" : "" ); |
| #endif |
| rtl_string_release(crashrep_path_system); |
| } |
| |
| ret = szShellCmd[0] == '\0' ? -1 : system( szShellCmd ); |
| |
| if ( pXMLTempName ) |
| unlink( pXMLTempName ); |
| |
| if ( pStackTempName ) |
| unlink( pStackTempName ); |
| |
| if ( pChecksumTempName ) |
| unlink( pChecksumTempName ); |
| |
| if ( -1 != ret ) |
| { |
| bCrashReporterExecuted = sal_True; |
| return 1; |
| } |
| else |
| return -1; |
| |
| } |
| } |
| |
| return 0; |
| } |
| |
| return 1; |
| #else /* defined SAL_ENABLE_CRASH_REPORT */ |
| /* the utility crash_report is not build, so do the same as when |
| the option -nocrashreport is used */ |
| (void) Signal; // avoid warnings |
| return -1; |
| #endif /* defined SAL_ENABLE_CRASH_REPORT */ |
| } |
| |
| static void PrintStack( int sig ) |
| { |
| #if ! defined(MACOSX) || defined(INCLUDE_BACKTRACE) |
| void *buffer[MAX_STACK_FRAMES]; |
| int size = backtrace( buffer, sizeof(buffer) / sizeof(buffer[0]) ); |
| #endif |
| |
| fprintf( stderr, "\n\nFatal exception: Signal %d\n", sig ); |
| |
| #if defined(MACOSX) && ! defined(INCLUDE_BACKTRACE) |
| fprintf( stderr, "Please turn on Enable Crash Reporting and\nAutomatic Display of Crashlogs in the Console application\n" ); |
| #else |
| if ( size > 0 ) |
| { |
| fputs( "Stack:\n", stderr ); |
| backtrace_symbols_fd( buffer, size, fileno(stderr) ); |
| } |
| #endif |
| } |
| |
| static oslSignalAction CallSignalHandler(oslSignalInfo *pInfo) |
| { |
| oslSignalHandlerImpl* pHandler = SignalList; |
| oslSignalAction Action = osl_Signal_ActCallNextHdl; |
| |
| while (pHandler != NULL) |
| { |
| if ((Action = pHandler->Handler(pHandler->pData, pInfo)) |
| != osl_Signal_ActCallNextHdl) |
| break; |
| |
| pHandler = pHandler->pNext; |
| } |
| |
| return Action; |
| } |
| |
| void CallSystemHandler(int Signal) |
| { |
| int i; |
| struct sigaction act; |
| |
| for (i = 0; i < NoSignals; i++) |
| { |
| if (Signals[i].Signal == Signal) |
| break; |
| } |
| |
| if (i < NoSignals) |
| { |
| if ((Signals[i].Handler == NULL) || |
| (Signals[i].Handler == SIG_DFL) || |
| (Signals[i].Handler == SIG_IGN) || |
| (Signals[i].Handler == SIG_ERR)) |
| { |
| switch (Signals[i].Action) |
| { |
| case ACT_EXIT: /* terminate */ |
| /* prevent dumping core on exit() */ |
| _exit(255); |
| break; |
| |
| case ACT_ABORT: /* terminate witch core dump */ |
| ReportCrash( Signal ); |
| act.sa_handler = SIG_DFL; |
| act.sa_flags = 0; |
| sigemptyset(&(act.sa_mask)); |
| sigaction(SIGABRT, &act, NULL); |
| PrintStack( Signal ); |
| abort(); |
| break; |
| |
| case ACT_IGNORE: /* ignore */ |
| break; |
| |
| default: /* should never happen */ |
| OSL_ASSERT(0); |
| } |
| } |
| else |
| (*Signals[i].Handler)(Signal); |
| } |
| } |
| |
| |
| /*****************************************************************************/ |
| /* SignalHandlerFunction */ |
| /*****************************************************************************/ |
| void SignalHandlerFunction(int Signal) |
| { |
| oslSignalInfo Info; |
| struct sigaction act; |
| |
| Info.UserSignal = Signal; |
| Info.UserData = NULL; |
| |
| switch (Signal) |
| { |
| case SIGBUS: |
| case SIGILL: |
| case SIGSEGV: |
| case SIGIOT: |
| #if ( SIGIOT != SIGABRT ) |
| case SIGABRT: |
| #endif |
| Info.Signal = osl_Signal_AccessViolation; |
| break; |
| |
| case -1: |
| Info.Signal = osl_Signal_IntegerDivideByZero; |
| break; |
| |
| case SIGFPE: |
| Info.Signal = osl_Signal_FloatDivideByZero; |
| break; |
| |
| case SIGINT: |
| case SIGTERM: |
| case SIGQUIT: |
| case SIGHUP: |
| Info.Signal = osl_Signal_Terminate; |
| break; |
| |
| default: |
| Info.Signal = osl_Signal_System; |
| break; |
| } |
| |
| ReportCrash( Signal ); |
| |
| /* Portal Demo HACK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ |
| if (bDoHardKill && (Info.Signal == osl_Signal_AccessViolation)) |
| _exit(255); |
| /* Portal Demo HACK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ |
| |
| |
| switch (CallSignalHandler(&Info)) |
| { |
| case osl_Signal_ActCallNextHdl: |
| CallSystemHandler(Signal); |
| break; |
| |
| case osl_Signal_ActAbortApp: |
| ReportCrash( Signal ); |
| act.sa_handler = SIG_DFL; |
| act.sa_flags = 0; |
| sigemptyset(&(act.sa_mask)); |
| sigaction(SIGABRT, &act, NULL); |
| PrintStack( Signal ); |
| abort(); |
| break; |
| |
| case osl_Signal_ActKillApp: |
| /* prevent dumping core on exit() */ |
| _exit(255); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /*****************************************************************************/ |
| /* osl_addSignalHandler */ |
| /*****************************************************************************/ |
| oslSignalHandler SAL_CALL osl_addSignalHandler(oslSignalHandlerFunction Handler, void* pData) |
| { |
| oslSignalHandlerImpl* pHandler; |
| |
| OSL_ASSERT(Handler != NULL); |
| if ( Handler == 0 ) |
| { |
| return 0; |
| } |
| |
| if (! bInitSignal) |
| bInitSignal = InitSignal(); |
| |
| pHandler = (oslSignalHandlerImpl*) calloc(1, sizeof(oslSignalHandlerImpl)); |
| |
| if (pHandler != NULL) |
| { |
| pHandler->Handler = Handler; |
| pHandler->pData = pData; |
| |
| osl_acquireMutex(SignalListMutex); |
| |
| pHandler->pNext = SignalList; |
| SignalList = pHandler; |
| |
| osl_releaseMutex(SignalListMutex); |
| |
| return (pHandler); |
| } |
| |
| return (NULL); |
| } |
| |
| /*****************************************************************************/ |
| /* osl_removeSignalHandler */ |
| /*****************************************************************************/ |
| sal_Bool SAL_CALL osl_removeSignalHandler(oslSignalHandler Handler) |
| { |
| oslSignalHandlerImpl *pHandler, *pPrevious = NULL; |
| |
| OSL_ASSERT(Handler != NULL); |
| |
| if (! bInitSignal) |
| bInitSignal = InitSignal(); |
| |
| osl_acquireMutex(SignalListMutex); |
| |
| pHandler = SignalList; |
| |
| while (pHandler != NULL) |
| { |
| if (pHandler == Handler) |
| { |
| if (pPrevious) |
| pPrevious->pNext = pHandler->pNext; |
| else |
| SignalList = pHandler->pNext; |
| |
| osl_releaseMutex(SignalListMutex); |
| |
| if (SignalList == NULL) |
| bInitSignal = DeInitSignal(); |
| |
| free(pHandler); |
| |
| return (sal_True); |
| } |
| |
| pPrevious = pHandler; |
| pHandler = pHandler->pNext; |
| } |
| |
| osl_releaseMutex(SignalListMutex); |
| |
| return (sal_False); |
| } |
| |
| /*****************************************************************************/ |
| /* osl_raiseSignal */ |
| /*****************************************************************************/ |
| oslSignalAction SAL_CALL osl_raiseSignal(sal_Int32 UserSignal, void* UserData) |
| { |
| oslSignalInfo Info; |
| oslSignalAction Action; |
| |
| if (! bInitSignal) |
| bInitSignal = InitSignal(); |
| |
| osl_acquireMutex(SignalListMutex); |
| |
| Info.Signal = osl_Signal_User; |
| Info.UserSignal = UserSignal; |
| Info.UserData = UserData; |
| |
| Action = CallSignalHandler(&Info); |
| |
| osl_releaseMutex(SignalListMutex); |
| |
| return (Action); |
| } |
| |
| /*****************************************************************************/ |
| /* osl_setErrorReporting */ |
| /*****************************************************************************/ |
| sal_Bool SAL_CALL osl_setErrorReporting( sal_Bool bEnable ) |
| { |
| sal_Bool bOld = bErrorReportingEnabled; |
| bErrorReportingEnabled = bEnable; |
| |
| return bOld; |
| } |