1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Helper Library
3  * -------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * 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, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief System handler handler override
22  *//*--------------------------------------------------------------------*/
23 
24 #include "qpCrashHandler.h"
25 #include "qpDebugOut.h"
26 
27 #include "deThread.h"
28 #include "deMemory.h"
29 #include "deString.h"
30 #include "deMutex.h"
31 
32 #include <stdio.h>
33 #include <stdarg.h>
34 #include <stdlib.h>
35 
36 #if (DE_OS == DE_OS_UNIX)
37 #	include <unistd.h>
38 #	include <execinfo.h>
39 #	include <errno.h>
40 #	include <inttypes.h>
41 #endif
42 
43 #if 0
44 #	define DBGPRINT(X) qpPrintf X
45 #else
46 #	define DBGPRINT(X)
47 #endif
48 
49 /* Crash info write helper. */
writeInfoFormat(qpWriteCrashInfoFunc writeFunc,void * userPtr,const char * format,...)50 static void writeInfoFormat (qpWriteCrashInfoFunc writeFunc, void* userPtr, const char* format, ...)
51 {
52 	char		buf[256];
53 	va_list		ap;
54 
55 	va_start(ap, format);
56 	vsnprintf(buf, sizeof(buf), format, ap);
57 	va_end(ap);
58 
59 	writeFunc(userPtr, buf);
60 }
61 
62 /* Shared crash info. */
63 typedef enum qpCrashType_e
64 {
65 	QP_CRASHTYPE_SEGMENTATION_FAULT = 0,
66 	QP_CRASHTYPE_ASSERT,
67 	QP_CRASHTYPE_UNHANDLED_EXCEPTION,
68 	QP_CRASHTYPE_OTHER,
69 
70 	QP_CRASHTYPE_LAST
71 } qpCrashType;
72 
73 typedef struct qpCrashInfo_s
74 {
75 	qpCrashType						type;
76 	const char*						message;
77 	const char*						file;
78 	int								line;
79 } qpCrashInfo;
80 
qpCrashInfo_init(qpCrashInfo * info)81 static void qpCrashInfo_init (qpCrashInfo* info)
82 {
83 	info->type		= QP_CRASHTYPE_LAST;
84 	info->message	= DE_NULL;
85 	info->file		= DE_NULL;
86 	info->line		= 0;
87 }
88 
qpCrashInfo_set(qpCrashInfo * info,qpCrashType type,const char * message,const char * file,int line)89 static void qpCrashInfo_set (qpCrashInfo* info, qpCrashType type, const char* message, const char* file, int line)
90 {
91 	info->type		= type;
92 	info->message	= message;
93 	info->file		= file;
94 	info->line		= line;
95 }
96 
qpCrashInfo_write(qpCrashInfo * info,qpWriteCrashInfoFunc writeInfo,void * userPtr)97 static void qpCrashInfo_write (qpCrashInfo* info, qpWriteCrashInfoFunc writeInfo, void* userPtr)
98 {
99 	switch (info->type)
100 	{
101 		case QP_CRASHTYPE_SEGMENTATION_FAULT:
102 			writeInfoFormat(writeInfo, userPtr, "Segmentation fault: '%s'\n", info->message);
103 			break;
104 
105 		case QP_CRASHTYPE_UNHANDLED_EXCEPTION:
106 			writeInfoFormat(writeInfo, userPtr, "Unhandled exception: '%s'\n", info->message);
107 			break;
108 
109 		case QP_CRASHTYPE_ASSERT:
110 			writeInfoFormat(writeInfo, userPtr, "Assertion '%s' failed at %s:%d\n",
111 							info->message,
112 							info->file,
113 							info->line);
114 			break;
115 
116 		case QP_CRASHTYPE_OTHER:
117 		default:
118 			writeInfoFormat(writeInfo, userPtr, "Crash: '%s'\n", info->message);
119 			break;
120 	}
121 }
122 
defaultWriteInfo(void * userPtr,const char * infoString)123 static void defaultWriteInfo (void* userPtr, const char* infoString)
124 {
125 	DE_UNREF(userPtr);
126 	qpPrintf("%s", infoString);
127 }
128 
defaultCrashHandler(qpCrashHandler * crashHandler,void * userPtr)129 static void defaultCrashHandler (qpCrashHandler* crashHandler, void* userPtr)
130 {
131 	DE_UNREF(userPtr);
132 	qpCrashHandler_writeCrashInfo(crashHandler, defaultWriteInfo, DE_NULL);
133 	qpDief("Test process crashed");
134 }
135 
136 #if (DE_OS == DE_OS_WIN32) && (DE_COMPILER == DE_COMPILER_MSC)
137 
138 #define WIN32_LEAN_AND_MEAN
139 #include <windows.h>
140 
141 /* DbgHelp.h generates C4091 */
142 #pragma warning (push)
143 #pragma warning (disable: 4091)
144 #include <DbgHelp.h>
145 #pragma warning (pop)
146 
147 struct qpCrashHandler_s
148 {
149 	qpCrashHandlerFunc				crashHandlerFunc;
150 	void*							handlerUserPointer;
151 
152 	deMutex							crashHandlerLock;
153 	qpCrashInfo						crashInfo;
154 	deUintptr						crashAddress;
155 
156 	LPTOP_LEVEL_EXCEPTION_FILTER	oldExceptionFilter;
157 };
158 
159 qpCrashHandler*		g_crashHandler = DE_NULL;
160 
unhandledExceptionFilter(struct _EXCEPTION_POINTERS * info)161 static LONG WINAPI unhandledExceptionFilter (struct _EXCEPTION_POINTERS* info)
162 {
163 	qpCrashType crashType	= QP_CRASHTYPE_LAST;
164 	const char*	reason		= DE_NULL;
165 
166 	/* Skip breakpoints. */
167 	if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
168 	{
169 		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): breakpoint\n"));
170 		return EXCEPTION_CONTINUE_SEARCH;
171 	}
172 
173 	/* If no handler present (how could that be?), don't handle. */
174 	if (g_crashHandler == DE_NULL)
175 	{
176 		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): no crash handler registered\n"));
177 		return EXCEPTION_CONTINUE_SEARCH;
178 	}
179 
180 	/* If we have a debugger, let it handle the exception. */
181 	if (IsDebuggerPresent())
182 	{
183 		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): debugger present\n"));
184 		return EXCEPTION_CONTINUE_SEARCH;
185 	}
186 
187 	/* Acquire crash handler lock. Otherwise we might get strange behavior when multiple threads enter crash handler simultaneously. */
188 	deMutex_lock(g_crashHandler->crashHandlerLock);
189 
190 	/* Map crash type. */
191 	switch (info->ExceptionRecord->ExceptionCode)
192 	{
193 		case EXCEPTION_ACCESS_VIOLATION:
194 			crashType	= QP_CRASHTYPE_SEGMENTATION_FAULT;
195 			reason		= "Access violation";
196 			break;
197 
198 		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
199 			crashType	= QP_CRASHTYPE_SEGMENTATION_FAULT;
200 			reason		= "Array bounds exceeded";
201 			break;
202 
203 		case EXCEPTION_ILLEGAL_INSTRUCTION:
204 			crashType	= QP_CRASHTYPE_OTHER;
205 			reason		= "Illegal instruction";
206 			break;
207 
208 		case EXCEPTION_STACK_OVERFLOW:
209 			crashType	= QP_CRASHTYPE_OTHER;
210 			reason		= "Stack overflow";
211 			break;
212 
213 		default:
214 			/* \todo [pyry] Others. */
215 			crashType	= QP_CRASHTYPE_OTHER;
216 			reason		= "";
217 			break;
218 	}
219 
220 	/* Store reason. */
221 	qpCrashInfo_set(&g_crashHandler->crashInfo, crashType, reason, __FILE__, __LINE__);
222 
223 	/* Store win32-specific crash info. */
224 	g_crashHandler->crashAddress = (deUintptr)info->ExceptionRecord->ExceptionAddress;
225 
226 	/* Handle the crash. */
227 	DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): handled quietly\n"));
228 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
229 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
230 
231 	/* Release lock. */
232 	deMutex_unlock(g_crashHandler->crashHandlerLock);
233 
234 	return EXCEPTION_EXECUTE_HANDLER;
235 }
236 
assertFailureCallback(const char * expr,const char * file,int line)237 static void assertFailureCallback (const char* expr, const char* file, int line)
238 {
239 	/* Don't execute crash handler function if debugger is present. */
240 	if (IsDebuggerPresent())
241 	{
242 		DBGPRINT(("qpCrashHandler::assertFailureCallback(): debugger present\n"));
243 		return;
244 	}
245 
246 	/* Acquire crash handler lock. */
247 	deMutex_lock(g_crashHandler->crashHandlerLock);
248 
249 	/* Store info. */
250 	qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
251 	g_crashHandler->crashAddress = 0;
252 
253 	/* Handle the crash. */
254 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
255 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
256 
257 	/* Release lock. */
258 	deMutex_unlock(g_crashHandler->crashHandlerLock);
259 }
260 
qpCrashHandler_create(qpCrashHandlerFunc handlerFunc,void * userPointer)261 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
262 {
263 	/* Allocate & initialize. */
264 	qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
265 	DBGPRINT(("qpCrashHandler::create() -- Win32\n"));
266 	if (!handler)
267 		return handler;
268 
269 	DE_ASSERT(g_crashHandler == DE_NULL);
270 
271 	handler->crashHandlerFunc	= handlerFunc ? handlerFunc : defaultCrashHandler;
272 	handler->handlerUserPointer	= userPointer;
273 
274 	/* Create lock for crash handler. \note Has to be recursive or otherwise crash in assert failure causes deadlock. */
275 	{
276 		deMutexAttributes attr;
277 		attr.flags = DE_MUTEX_RECURSIVE;
278 		handler->crashHandlerLock = deMutex_create(&attr);
279 
280 		if (!handler->crashHandlerLock)
281 		{
282 			deFree(handler);
283 			return DE_NULL;
284 		}
285 	}
286 
287 	qpCrashInfo_init(&handler->crashInfo);
288 	handler->crashAddress		= 0;
289 
290 	/* Unhandled exception filter. */
291 	handler->oldExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter);
292 
293 	/* Prevent nasty error dialog. */
294 	SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
295 
296 	/* DE_ASSERT callback. */
297 	deSetAssertFailureCallback(assertFailureCallback);
298 
299 	g_crashHandler = handler;
300 	return handler;
301 }
302 
qpCrashHandler_destroy(qpCrashHandler * handler)303 void qpCrashHandler_destroy (qpCrashHandler* handler)
304 {
305 	DBGPRINT(("qpCrashHandler::destroy()\n"));
306 
307 	DE_ASSERT(g_crashHandler == handler);
308 
309 	deSetAssertFailureCallback(DE_NULL);
310 	SetUnhandledExceptionFilter(handler->oldExceptionFilter);
311 
312 	g_crashHandler = DE_NULL;
313 	deFree(handler);
314 }
315 
316 enum
317 {
318 	MAX_NAME_LENGTH = 64
319 };
320 
qpCrashHandler_writeCrashInfo(qpCrashHandler * handler,qpWriteCrashInfoFunc writeInfo,void * userPtr)321 void qpCrashHandler_writeCrashInfo (qpCrashHandler* handler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
322 {
323 	void*			addresses[32];
324 	HANDLE			process;
325 
326 	/* Symbol info struct. */
327 	deUint8			symInfoStorage[sizeof(SYMBOL_INFO)+MAX_NAME_LENGTH];
328 	SYMBOL_INFO*	symInfo			= (SYMBOL_INFO*)symInfoStorage;
329 
330 	DE_STATIC_ASSERT(sizeof(TCHAR) == sizeof(deUint8));
331 
332 	/* Write basic info. */
333 	qpCrashInfo_write(&handler->crashInfo, writeInfo, userPtr);
334 
335 	/* Acquire process handle and initialize symbols. */
336 	process = GetCurrentProcess();
337 
338 	/* Write backtrace. */
339 	if (SymInitialize(process, NULL, TRUE))
340 	{
341 		int batchStart		= 0;
342 		int globalFrameNdx	= 0;
343 
344 		/* Initialize symInfo. */
345 		deMemset(symInfo, 0, sizeof(symInfoStorage));
346 		symInfo->SizeOfStruct	= sizeof(SYMBOL_INFO);
347 		symInfo->MaxNameLen		= MAX_NAME_LENGTH;
348 
349 		/* Print address and symbol where crash happened. */
350 		if (handler->crashAddress != 0)
351 		{
352 			BOOL symInfoOk = SymFromAddr(process, (DWORD64)handler->crashAddress, 0, symInfo);
353 
354 			writeInfoFormat(writeInfo, userPtr, "  at %p %s%s\n", handler->crashAddress,
355 							symInfoOk ? symInfo->Name : "(unknown)",
356 							symInfoOk ? "()" : "");
357 		}
358 
359 		writeInfo(userPtr, "Backtrace:\n");
360 
361 		for (;;)
362 		{
363 			int curFrame;
364 			int numInBatch;
365 
366 			/* Get one batch. */
367 			numInBatch = CaptureStackBackTrace(batchStart, DE_LENGTH_OF_ARRAY(addresses), addresses, NULL);
368 
369 			for (curFrame = 0; curFrame < numInBatch; curFrame++)
370 			{
371 				BOOL symInfoOk = SymFromAddr(process, (DWORD64)addresses[curFrame], 0, symInfo);
372 
373 				writeInfoFormat(writeInfo, userPtr, "  %2d: %p %s%s\n", globalFrameNdx++, addresses[curFrame],
374 								symInfoOk ? symInfo->Name : "(unknown)",
375 								symInfoOk ? "()" : "");
376 			}
377 
378 			batchStart += numInBatch;
379 
380 			/* Check if we hit end of stack trace. */
381 			if (numInBatch == 0 || numInBatch < DE_LENGTH_OF_ARRAY(addresses))
382 				break;
383 		}
384 	}
385 }
386 
387 #else /* posix / generic implementation */
388 
389 #if defined(QP_USE_SIGNAL_HANDLER)
390 #	include <signal.h>
391 #endif
392 
393 #if defined(QP_USE_SIGNAL_HANDLER)
394 
395 typedef struct SignalInfo_s
396 {
397 	int				signalNum;
398 	qpCrashType		type;
399 	const char*		name;
400 } SignalInfo;
401 
402 static const SignalInfo s_signals[] =
403 {
404 	{ SIGABRT,		QP_CRASHTYPE_UNHANDLED_EXCEPTION,	"SIGABRT"	},
405 	{ SIGILL,		QP_CRASHTYPE_OTHER,					"SIGILL"	},
406 	{ SIGSEGV,		QP_CRASHTYPE_SEGMENTATION_FAULT,	"SIGSEGV"	},
407 	{ SIGFPE,		QP_CRASHTYPE_OTHER,					"SIGFPE"	},
408 	{ SIGBUS,		QP_CRASHTYPE_SEGMENTATION_FAULT,	"SIGBUS"	},
409 	{ SIGPIPE,		QP_CRASHTYPE_OTHER,					"SIGPIPE"	}
410 };
411 
412 #endif /* QP_USE_SIGNAL_HANDLER */
413 
414 struct qpCrashHandler_s
415 {
416 	qpCrashHandlerFunc		crashHandlerFunc;
417 	void*					handlerUserPointer;
418 
419 	qpCrashInfo				crashInfo;
420 	int						crashSignal;
421 
422 #if defined(QP_USE_SIGNAL_HANDLER)
423 	struct sigaction		oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)];
424 #endif
425 };
426 
427 qpCrashHandler* g_crashHandler = DE_NULL;
428 
assertFailureCallback(const char * expr,const char * file,int line)429 static void assertFailureCallback (const char* expr, const char* file, int line)
430 {
431 	/* Store info. */
432 	qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
433 
434 	/* Handle the crash. */
435 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
436 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
437 }
438 
439 #if defined(QP_USE_SIGNAL_HANDLER)
440 
getSignalInfo(int sigNum)441 static const SignalInfo* getSignalInfo (int sigNum)
442 {
443 	int ndx;
444 	for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++)
445 	{
446 		if (s_signals[ndx].signalNum == sigNum)
447 			return &s_signals[ndx];
448 	}
449 	return DE_NULL;
450 }
451 
signalHandler(int sigNum)452 static void signalHandler (int sigNum)
453 {
454 	const SignalInfo*	info	= getSignalInfo(sigNum);
455 	qpCrashType			type	= info ? info->type : QP_CRASHTYPE_OTHER;
456 	const char*			name	= info ? info->name : "Unknown signal";
457 
458 	qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0);
459 
460 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
461 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
462 }
463 
464 #endif /* QP_USE_SIGNAL_HANDLER */
465 
qpCrashHandler_create(qpCrashHandlerFunc handlerFunc,void * userPointer)466 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
467 {
468 	/* Allocate & initialize. */
469 	qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
470 	DBGPRINT(("qpCrashHandler::create()\n"));
471 	if (!handler)
472 		return handler;
473 
474 	DE_ASSERT(g_crashHandler == DE_NULL);
475 
476 	handler->crashHandlerFunc	= handlerFunc ? handlerFunc : defaultCrashHandler;
477 	handler->handlerUserPointer	= userPointer;
478 
479 	qpCrashInfo_init(&handler->crashInfo);
480 
481 	g_crashHandler = handler;
482 
483 	/* DE_ASSERT callback. */
484 	deSetAssertFailureCallback(assertFailureCallback);
485 
486 #if defined(QP_USE_SIGNAL_HANDLER)
487 	/* Register signal handlers. */
488 	{
489 		struct sigaction	action;
490 		int					sigNdx;
491 
492 		sigemptyset(&action.sa_mask);
493 		action.sa_handler	= signalHandler;
494 		action.sa_flags		= 0;
495 
496 		for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
497 			sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]);
498 	}
499 #endif
500 
501 	return handler;
502 }
503 
qpCrashHandler_destroy(qpCrashHandler * handler)504 void qpCrashHandler_destroy (qpCrashHandler* handler)
505 {
506 	DBGPRINT(("qpCrashHandler::destroy()\n"));
507 
508 	DE_ASSERT(g_crashHandler == handler);
509 
510 	deSetAssertFailureCallback(DE_NULL);
511 
512 #if defined(QP_USE_SIGNAL_HANDLER)
513 	/* Restore old handlers. */
514 	{
515 		int sigNdx;
516 		for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
517 			sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL);
518 	}
519 #endif
520 
521 	g_crashHandler = DE_NULL;
522 
523 	deFree(handler);
524 }
525 
526 #if (DE_PTR_SIZE  == 8)
527 #	define PTR_FMT "0x%016"
528 #elif (DE_PTR_SIZE  == 4)
529 #	define PTR_FMT "0x%08"
530 #else
531 #	error Unknwon DE_PTR_SIZE
532 #endif
533 
qpCrashHandler_writeCrashInfo(qpCrashHandler * crashHandler,qpWriteCrashInfoFunc writeInfo,void * userPtr)534 void qpCrashHandler_writeCrashInfo (qpCrashHandler* crashHandler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
535 {
536 	qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr);
537 
538 #if (DE_OS == DE_OS_UNIX)
539 	{
540 		char	tmpFileName[]	= "backtrace-XXXXXX";
541 		int		tmpFile			= mkstemp(tmpFileName);
542 
543 		if (tmpFile == -1)
544 		{
545 			writeInfoFormat(writeInfo, userPtr, "Failed to create tmpfile '%s' for the backtrace %s.", tmpFileName, strerror(errno));
546 			return;
547 		}
548 		else
549 		{
550 			void*	symbols[32];
551 			int		symbolCount;
552 			int		symbolNdx;
553 
554 			/* Remove file from filesystem. */
555 			remove(tmpFileName);
556 
557 			symbolCount = backtrace(symbols, DE_LENGTH_OF_ARRAY(symbols));
558 			backtrace_symbols_fd(symbols, symbolCount, tmpFile);
559 
560 			if (lseek(tmpFile, 0, SEEK_SET) < 0)
561 			{
562 				writeInfoFormat(writeInfo, userPtr, "Failed to seek to the beginning of the trace file %s.", strerror(errno));
563 				close(tmpFile);
564 				return;
565 			}
566 
567 			for (symbolNdx = 0; symbolNdx < symbolCount; symbolNdx++)
568 			{
569 				char	nameBuffer[256];
570 				size_t	symbolNameLength = 0;
571 				char	c;
572 
573 				{
574 					const int ret = snprintf(nameBuffer, DE_LENGTH_OF_ARRAY(nameBuffer), PTR_FMT  PRIXPTR " : ", (uintptr_t)symbols[symbolNdx]);
575 
576 					if (ret < 0)
577 					{
578 						writeInfoFormat(writeInfo, userPtr, "Failed to print symbol pointer.");
579 						symbolNameLength = 0;
580 					}
581 					else if (ret >= DE_LENGTH_OF_ARRAY(nameBuffer))
582 					{
583 						symbolNameLength = DE_LENGTH_OF_ARRAY(nameBuffer) - 1;
584 						nameBuffer[DE_LENGTH_OF_ARRAY(nameBuffer) - 1] = '\0';
585 					}
586 					else
587 						symbolNameLength = ret;
588 				}
589 
590 				for (;;)
591 				{
592 					if (read(tmpFile, &c, 1) == 1)
593 					{
594 						if (c == '\n')
595 						{
596 							/* Flush nameBuffer and move to next symbol. */
597 							nameBuffer[symbolNameLength] = '\0';
598 							writeInfo(userPtr, nameBuffer);
599 							break;
600 						}
601 						else
602 						{
603 							/* Add character to buffer if there is still space left. */
604 							if (symbolNameLength+1 < DE_LENGTH_OF_ARRAY(nameBuffer))
605 							{
606 								nameBuffer[symbolNameLength] = c;
607 								symbolNameLength++;
608 							}
609 						}
610 					}
611 					else
612 					{
613 						/* Flush nameBuffer. */
614 						nameBuffer[symbolNameLength] = '\0';
615 						writeInfo(userPtr, nameBuffer);
616 
617 						/* Temp file ended unexpectedly? */
618 						writeInfoFormat(writeInfo, userPtr, "Unexpected EOF reading backtrace file '%s'", tmpFileName);
619 						close(tmpFile);
620 						tmpFile = -1;
621 
622 						break;
623 					}
624 				}
625 
626 				if (tmpFile == -1)
627 					break;
628 			}
629 
630 			if (tmpFile != -1)
631 				close(tmpFile);
632 		}
633 	}
634 #endif
635 }
636 
637 #endif /* generic */
638