xref: /trunk/main/sal/osl/w32/signal.cxx (revision 692f3634459b6acc70baa09d1b54bb8acaeb3a7c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 /* system headers */
23 #include "system.h"
24 #include <tchar.h>
25 
26 #include "file_url.h"
27 #include "path_helper.hxx"
28 
29 #include <osl/diagnose.h>
30 #include <osl/mutex.h>
31 #include <osl/signal.h>
32 #ifndef __MINGW32__
33 #include <DbgHelp.h>
34 #endif
35 #include <ErrorRep.h>
36 #include <systools/win32/uwinapi.h>
37 #include <eh.h>
38 #include <stdexcept>
39 
40 typedef struct _oslSignalHandlerImpl
41 {
42     oslSignalHandlerFunction      Handler;
43     void*                         pData;
44     struct _oslSignalHandlerImpl* pNext;
45 } oslSignalHandlerImpl;
46 
47 static sal_Bool               bErrorReportingEnabled = sal_True;
48 static sal_Bool               bInitSignal = sal_False;
49 static oslMutex               SignalListMutex;
50 static oslSignalHandlerImpl*  SignalList;
51 
52 static long WINAPI SignalHandlerFunction(LPEXCEPTION_POINTERS lpEP);
53 
54 static _invalid_parameter_handler pPreviousInvalidParameterHandler = NULL;
55 static void InvalidParameterHandlerFunction(
56     const wchar_t* expression,
57     const wchar_t* function,
58     const wchar_t* file,
59     unsigned int line,
60     uintptr_t pReserved );
61 
InitSignal(void)62 static sal_Bool InitSignal(void)
63 {
64     HMODULE hFaultRep;
65 
66     SignalListMutex = osl_createMutex();
67 
68     SetUnhandledExceptionFilter(SignalHandlerFunction);
69     if ( pPreviousInvalidParameterHandler == NULL )
70     {
71         pPreviousInvalidParameterHandler = _set_invalid_parameter_handler( InvalidParameterHandlerFunction );
72     }
73 
74     hFaultRep = LoadLibrary( "faultrep.dll" );
75     if ( hFaultRep )
76     {
77 #ifdef __MINGW32__
78 typedef BOOL (WINAPI *pfn_ADDEREXCLUDEDAPPLICATIONW)(LPCWSTR);
79 #endif
80         pfn_ADDEREXCLUDEDAPPLICATIONW       pfn = (pfn_ADDEREXCLUDEDAPPLICATIONW)GetProcAddress( hFaultRep, "AddERExcludedApplicationW" );
81         if ( pfn )
82             pfn( L"SOFFICE.EXE" );
83         FreeLibrary( hFaultRep );
84     }
85 
86     return sal_True;
87 }
88 
DeInitSignal(void)89 static sal_Bool DeInitSignal(void)
90 {
91     SetUnhandledExceptionFilter(NULL);
92 
93     if ( pPreviousInvalidParameterHandler )
94     {
95         _set_invalid_parameter_handler( pPreviousInvalidParameterHandler );
96         pPreviousInvalidParameterHandler = NULL;
97     }
98     osl_destroyMutex(SignalListMutex);
99 
100     return sal_False;
101 }
102 
CallSignalHandler(oslSignalInfo * pInfo)103 static oslSignalAction CallSignalHandler(oslSignalInfo *pInfo)
104 {
105     oslSignalHandlerImpl* pHandler = SignalList;
106     oslSignalAction Action = osl_Signal_ActCallNextHdl;
107 
108     while (pHandler != NULL)
109     {
110         if ((Action = pHandler->Handler(pHandler->pData, pInfo)) != osl_Signal_ActCallNextHdl)
111             break;
112 
113         pHandler = pHandler->pNext;
114     }
115 
116     return Action;
117 }
118 
119 /*****************************************************************************/
120 /* SignalHandlerFunction    */
121 /*****************************************************************************/
122 
123 #define REPORTENV_PARAM     "-crashreportenv:"
124 #define REPORTENV_PARAM2    "/crashreportenv:"
125 
ReportCrash(LPEXCEPTION_POINTERS lpEP)126 static BOOL ReportCrash( LPEXCEPTION_POINTERS lpEP )
127 {
128     BOOL    fSuccess = FALSE;
129     BOOL    fAutoReport = FALSE;
130     TCHAR   szBuffer[1024];
131     ::osl::LongPathBuffer< sal_Char > aPath( MAX_LONG_PATH );
132     LPTSTR  lpFilePart;
133     PROCESS_INFORMATION ProcessInfo;
134     STARTUPINFO StartupInfo;
135     int     argi;
136 
137     if ( !bErrorReportingEnabled )
138         return FALSE;
139 
140     /* Check if crash reporter was disabled by command line */
141 
142     for ( argi = 1; argi < __argc; argi++ )
143     {
144         if (
145             0 == stricmp( __argv[argi], "-nocrashreport" ) ||
146             0 == stricmp( __argv[argi], "/nocrashreport" )
147             )
148             return FALSE;
149         else if (
150             0 == stricmp( __argv[argi], "-autocrashreport" ) ||
151             0 == stricmp( __argv[argi], "/autocrashreport" )
152             )
153             fAutoReport = TRUE;
154         else if (
155             0 == strnicmp( __argv[argi], REPORTENV_PARAM, strlen(REPORTENV_PARAM) ) ||
156             0 == strnicmp( __argv[argi], REPORTENV_PARAM2, strlen(REPORTENV_PARAM2) )
157             )
158         {
159             const char *envparam = __argv[argi] + strlen(REPORTENV_PARAM);
160             const char *delim = strchr(envparam, '=' );
161 
162             if ( delim )
163             {
164                 CHAR    *lpVariable;
165                 CHAR    *lpValue;
166                 const char *variable = envparam;
167                 size_t variable_len = delim - envparam;
168                 const char *value = delim + 1;
169                 size_t value_len = strlen(envparam) - variable_len - 1;
170 
171                 if ( '\"' == *value )
172                 {
173                     const char *quote;
174 
175                     value++;
176                     value_len--;
177 
178                     quote = strchr( value, '\"' );
179                     if ( quote )
180                         value_len = quote - value;
181                 }
182 
183                 lpVariable = reinterpret_cast< CHAR* >( _alloca( variable_len + 1 ) );
184                 memcpy( lpVariable, variable, variable_len );
185                 lpVariable[variable_len] = 0;
186 
187                 lpValue = reinterpret_cast< CHAR* >( _alloca( value_len + 1) );
188                 memcpy( lpValue, value, value_len );
189                 lpValue[value_len] = 0;
190 
191                 SetEnvironmentVariable( lpVariable, lpValue );
192             }
193         }
194     }
195 
196     if ( SearchPath( NULL, TEXT( "crashrep.exe" ), NULL, aPath.getBufSizeInSymbols(), aPath, &lpFilePart ) )
197     {
198         ZeroMemory( &StartupInfo, sizeof(StartupInfo) );
199         StartupInfo.cb = sizeof(StartupInfo.cb);
200 
201 
202         sntprintf( szBuffer, elementsof(szBuffer),
203             _T("%s -p %u -excp 0x%p -t %u%s"),
204             static_cast<sal_Char*>( aPath ),
205             GetCurrentProcessId(),
206             lpEP,
207             GetCurrentThreadId(),
208             fAutoReport ? _T(" -noui -send") : _T(" -noui") );
209 
210         if (
211             CreateProcess(
212                 NULL,
213                 szBuffer,
214                 NULL,
215                 NULL,
216                 FALSE,
217 #ifdef UNICODE
218                 CREATE_UNICODE_ENVIRONMENT,
219 #else
220                 0,
221 #endif
222                 NULL, NULL, &StartupInfo, &ProcessInfo )
223             )
224         {
225             DWORD   dwExitCode;
226 
227             WaitForSingleObject( ProcessInfo.hProcess, INFINITE );
228             if ( GetExitCodeProcess( ProcessInfo.hProcess, &dwExitCode ) && 0 == dwExitCode )
229 
230             fSuccess = TRUE;
231 
232         }
233     }
234 
235     return fSuccess;
236 }
237 
238 /*****************************************************************************/
239 /* SignalHandlerFunction    */
240 /*****************************************************************************/
241 
IsWin95A(void)242 static BOOL WINAPI IsWin95A(void)
243 {
244     OSVERSIONINFO   ovi;
245 
246     ZeroMemory( &ovi, sizeof(ovi) );
247     ovi.dwOSVersionInfoSize = sizeof(ovi);
248 
249     if ( GetVersionEx( &ovi ) )
250         /* See MSDN January 2000 documentation of GetVersionEx */
251         return  (ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
252                 (ovi.dwMajorVersion <= 4) &&
253                 (ovi.dwMinorVersion == 0) &&
254                 (ovi.dwBuildNumber == 0x040003B6);
255 
256     /* Something went wrong. So assume we have an older operating prior Win95 */
257 
258     return TRUE;
259 }
260 
261 /* magic Microsoft C++ compiler exception constant */
262 #define EXCEPTION_MSC_CPP_EXCEPTION 0xe06d7363
263 
SignalHandlerFunction(LPEXCEPTION_POINTERS lpEP)264 static long WINAPI SignalHandlerFunction(LPEXCEPTION_POINTERS lpEP)
265 {
266     static sal_Bool     bNested = sal_False;
267     sal_Bool        bRaiseCrashReporter = sal_False;
268     oslSignalInfo   Info;
269     oslSignalAction Action;
270 
271     Info.UserSignal = lpEP->ExceptionRecord->ExceptionCode;
272     Info.UserData   = NULL;
273 
274     switch (lpEP->ExceptionRecord->ExceptionCode)
275     {
276         /* Transform unhandled exceptions into access violations.
277            Microsoft C++ compiler (add more for other compilers if necessary).
278          */
279         case EXCEPTION_MSC_CPP_EXCEPTION:
280         case EXCEPTION_ACCESS_VIOLATION:
281             Info.Signal = osl_Signal_AccessViolation;
282             bRaiseCrashReporter = sal_True;
283             break;
284 
285         case EXCEPTION_INT_DIVIDE_BY_ZERO:
286             Info.Signal = osl_Signal_IntegerDivideByZero;
287             bRaiseCrashReporter = sal_True;
288             break;
289 
290         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
291             Info.Signal = osl_Signal_FloatDivideByZero;
292             bRaiseCrashReporter = sal_True;
293             break;
294 
295         case EXCEPTION_BREAKPOINT:
296             Info.Signal = osl_Signal_DebugBreak;
297             break;
298 
299         default:
300             Info.Signal = osl_Signal_System;
301             bRaiseCrashReporter = sal_True;
302             break;
303     }
304 
305     if ( !bNested )
306     {
307         bNested = sal_True;
308 
309         if ( bRaiseCrashReporter && ReportCrash( lpEP ) || IsWin95A() )
310         {
311             CallSignalHandler(&Info);
312             Action = osl_Signal_ActKillApp;
313         }
314         else
315             Action = CallSignalHandler(&Info);
316     }
317     else
318         Action = osl_Signal_ActKillApp;
319 
320 
321     switch ( Action )
322     {
323         case osl_Signal_ActCallNextHdl:
324             return (EXCEPTION_CONTINUE_SEARCH);
325 
326         case osl_Signal_ActAbortApp:
327             return (EXCEPTION_EXECUTE_HANDLER);
328 
329         case osl_Signal_ActKillApp:
330             SetErrorMode(SEM_NOGPFAULTERRORBOX);
331             exit(255);
332             break;
333         default:
334             break;
335     }
336 
337     return (EXCEPTION_CONTINUE_EXECUTION);
338 }
339 
InvalidParameterHandlerFunction(const wchar_t * expression,const wchar_t * function,const wchar_t * file,unsigned int line,uintptr_t pReserved)340 static void InvalidParameterHandlerFunction(
341     const wchar_t* expression,
342     const wchar_t* function,
343     const wchar_t* file,
344     unsigned int line,
345     uintptr_t pReserved )
346 {
347     oslSignalInfo   Info;
348 
349     fwprintf(stderr, L"Invalid parameter detected in function %s.\n"
350              L"File: %s\n"
351              L"Line: %u\n"
352              L"Expression: %s\n"
353              L"pReserved: %p\n", function, file, line, expression, pReserved);
354 
355     Info.Signal = osl_Signal_AccessViolation;
356     Info.UserSignal = 0;
357     Info.UserData = NULL;
358     if ( ReportCrash( NULL ) || IsWin95A() )
359     {
360         CallSignalHandler(&Info);
361         abort(); // Equivalent of osl_Signal_ActAbortApp
362     } else {
363         // Equivalent of osl_Signal_ActKillApp
364         SetErrorMode(SEM_NOGPFAULTERRORBOX);
365         exit(255);
366     }
367     // We will never reach this point
368 }
369 
370 /*****************************************************************************/
371 /* osl_addSignalHandler */
372 /*****************************************************************************/
osl_addSignalHandler(oslSignalHandlerFunction Handler,void * pData)373 oslSignalHandler SAL_CALL osl_addSignalHandler(oslSignalHandlerFunction Handler, void* pData)
374 {
375     oslSignalHandlerImpl* pHandler;
376 
377     OSL_ASSERT(Handler != NULL);
378 
379     if (! bInitSignal)
380         bInitSignal = InitSignal();
381 
382     pHandler = reinterpret_cast< oslSignalHandlerImpl* >( calloc( 1, sizeof(oslSignalHandlerImpl) ) );
383 
384     if (pHandler != NULL)
385     {
386         pHandler->Handler = Handler;
387         pHandler->pData   = pData;
388 
389         osl_acquireMutex(SignalListMutex);
390 
391         pHandler->pNext = SignalList;
392         SignalList      = pHandler;
393 
394         osl_releaseMutex(SignalListMutex);
395 
396         return (pHandler);
397     }
398 
399     return (NULL);
400 }
401 
402 /*****************************************************************************/
403 /* osl_removeSignalHandler */
404 /*****************************************************************************/
osl_removeSignalHandler(oslSignalHandler Handler)405 sal_Bool SAL_CALL osl_removeSignalHandler(oslSignalHandler Handler)
406 {
407     oslSignalHandlerImpl *pHandler, *pPrevious = NULL;
408 
409     OSL_ASSERT(Handler != NULL);
410 
411     if (! bInitSignal)
412         bInitSignal = InitSignal();
413 
414     osl_acquireMutex(SignalListMutex);
415 
416     pHandler = SignalList;
417 
418     while (pHandler != NULL)
419     {
420         if (pHandler == Handler)
421         {
422             if (pPrevious)
423                 pPrevious->pNext = pHandler->pNext;
424             else
425                 SignalList = pHandler->pNext;
426 
427             osl_releaseMutex(SignalListMutex);
428 
429             if (SignalList == NULL)
430                 bInitSignal = DeInitSignal();
431 
432             free(pHandler);
433 
434             return (sal_True);
435         }
436 
437         pPrevious = pHandler;
438         pHandler  = pHandler->pNext;
439     }
440 
441     osl_releaseMutex(SignalListMutex);
442 
443     return (sal_False);
444 }
445 
446 /*****************************************************************************/
447 /* osl_raiseSignal */
448 /*****************************************************************************/
osl_raiseSignal(sal_Int32 UserSignal,void * UserData)449 oslSignalAction SAL_CALL osl_raiseSignal(sal_Int32 UserSignal, void* UserData)
450 {
451     oslSignalInfo   Info;
452     oslSignalAction Action;
453 
454     if (! bInitSignal)
455         bInitSignal = InitSignal();
456 
457     osl_acquireMutex(SignalListMutex);
458 
459     Info.Signal     = osl_Signal_User;
460     Info.UserSignal = UserSignal;
461     Info.UserData   = UserData;
462 
463     Action = CallSignalHandler(&Info);
464 
465     osl_releaseMutex(SignalListMutex);
466 
467     return (Action);
468 }
469 
470 /*****************************************************************************/
471 /* osl_setErrorReporting */
472 /*****************************************************************************/
473 
win_seh_translator(unsigned nSEHCode,_EXCEPTION_POINTERS * pExcPtrs)474 void win_seh_translator( unsigned nSEHCode, _EXCEPTION_POINTERS* pExcPtrs)
475 {
476     (void*)pExcPtrs; // currently unused, but useful inside a debugger
477     const char* pSEHName = NULL;
478     switch( nSEHCode)
479     {
480         case EXCEPTION_ACCESS_VIOLATION:         pSEHName = "SEH Exception: ACCESS VIOLATION"; break;
481         case EXCEPTION_DATATYPE_MISALIGNMENT:    pSEHName = "SEH Exception: DATATYPE MISALIGNMENT"; break;
482         case EXCEPTION_BREAKPOINT:               /*pSEHName = "SEH Exception: BREAKPOINT";*/ break;
483         case EXCEPTION_SINGLE_STEP:              /*pSEHName = "SEH Exception: SINGLE STEP";*/ break;
484         case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:    pSEHName = "SEH Exception: ARRAY BOUNDS EXCEEDED"; break;
485         case EXCEPTION_FLT_DENORMAL_OPERAND:     pSEHName = "SEH Exception: DENORMAL FLOAT OPERAND"; break;
486         case EXCEPTION_FLT_DIVIDE_BY_ZERO:       pSEHName = "SEH Exception: FLOAT DIVIDE_BY_ZERO"; break;
487         case EXCEPTION_FLT_INEXACT_RESULT:       pSEHName = "SEH Exception: FLOAT INEXACT RESULT"; break;
488         case EXCEPTION_FLT_INVALID_OPERATION:    pSEHName = "SEH Exception: INVALID FLOAT OPERATION"; break;
489         case EXCEPTION_FLT_OVERFLOW:             pSEHName = "SEH Exception: FLOAT OVERFLOW"; break;
490         case EXCEPTION_FLT_STACK_CHECK:          pSEHName = "SEH Exception: FLOAT STACK_CHECK"; break;
491         case EXCEPTION_FLT_UNDERFLOW:            pSEHName = "SEH Exception: FLOAT UNDERFLOW"; break;
492         case EXCEPTION_INT_DIVIDE_BY_ZERO:       pSEHName = "SEH Exception: INTEGER DIVIDE_BY_ZERO"; break;
493         case EXCEPTION_INT_OVERFLOW:             pSEHName = "SEH Exception: INTEGER OVERFLOW"; break;
494         case EXCEPTION_PRIV_INSTRUCTION:         pSEHName = "SEH Exception: PRIVILEDGED INSTRUCTION"; break;
495         case EXCEPTION_IN_PAGE_ERROR:            pSEHName = "SEH Exception: IN_PAGE_ERROR"; break;
496         case EXCEPTION_ILLEGAL_INSTRUCTION:      pSEHName = "SEH Exception: ILLEGAL INSTRUCTION"; break;
497         case EXCEPTION_NONCONTINUABLE_EXCEPTION: pSEHName = "SEH Exception: NONCONTINUABLE EXCEPTION"; break;
498         case EXCEPTION_STACK_OVERFLOW:           pSEHName = "SEH Exception: STACK OVERFLOW"; break;
499         case EXCEPTION_INVALID_DISPOSITION:      pSEHName = "SEH Exception: INVALID DISPOSITION"; break;
500         case EXCEPTION_GUARD_PAGE:               pSEHName = "SEH Exception: GUARD PAGE"; break;
501         case EXCEPTION_INVALID_HANDLE:           pSEHName = "SEH Exception: INVALID HANDLE"; break;
502 //      case EXCEPTION_POSSIBLE_DEADLOCK:        pSEHName = "SEH Exception: POSSIBLE DEADLOCK"; break;
503         default:                                 pSEHName = "Unknown SEH Exception"; break;
504     }
505 
506     if( pSEHName)
507         throw std::runtime_error( pSEHName);
508 }
509 
osl_setErrorReporting(sal_Bool bEnable)510 sal_Bool SAL_CALL osl_setErrorReporting( sal_Bool bEnable )
511 {
512     sal_Bool bOld = bErrorReportingEnabled;
513     bErrorReportingEnabled = bEnable;
514 
515     if( !bEnable) // if the crash reporter is disabled
516     {
517         // fall back to handle Window's SEH events as C++ exceptions
518         _set_se_translator( win_seh_translator);
519     }
520 
521     return bOld;
522 }
523 
524 /* vim: set noet sw=4 ts=4: */
525