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