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 #include <DbgHelp.h>
141 
142 struct qpCrashHandler_s
143 {
144 	qpCrashHandlerFunc				crashHandlerFunc;
145 	void*							handlerUserPointer;
146 
147 	deMutex							crashHandlerLock;
148 	qpCrashInfo						crashInfo;
149 	deUintptr						crashAddress;
150 
151 	LPTOP_LEVEL_EXCEPTION_FILTER	oldExceptionFilter;
152 };
153 
154 qpCrashHandler*		g_crashHandler = DE_NULL;
155 
unhandledExceptionFilter(struct _EXCEPTION_POINTERS * info)156 static LONG WINAPI unhandledExceptionFilter (struct _EXCEPTION_POINTERS* info)
157 {
158 	qpCrashType crashType	= QP_CRASHTYPE_LAST;
159 	const char*	reason		= DE_NULL;
160 
161 	/* Skip breakpoints. */
162 	if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
163 	{
164 		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): breakpoint\n"));
165 		return EXCEPTION_CONTINUE_SEARCH;
166 	}
167 
168 	/* If no handler present (how could that be?), don't handle. */
169 	if (g_crashHandler == DE_NULL)
170 	{
171 		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): no crash handler registered\n"));
172 		return EXCEPTION_CONTINUE_SEARCH;
173 	}
174 
175 	/* If we have a debugger, let it handle the exception. */
176 	if (IsDebuggerPresent())
177 	{
178 		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): debugger present\n"));
179 		return EXCEPTION_CONTINUE_SEARCH;
180 	}
181 
182 	/* Acquire crash handler lock. Otherwise we might get strange behavior when multiple threads enter crash handler simultaneously. */
183 	deMutex_lock(g_crashHandler->crashHandlerLock);
184 
185 	/* Map crash type. */
186 	switch (info->ExceptionRecord->ExceptionCode)
187 	{
188 		case EXCEPTION_ACCESS_VIOLATION:
189 			crashType	= QP_CRASHTYPE_SEGMENTATION_FAULT;
190 			reason		= "Access violation";
191 			break;
192 
193 		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
194 			crashType	= QP_CRASHTYPE_SEGMENTATION_FAULT;
195 			reason		= "Array bounds exceeded";
196 			break;
197 
198 		case EXCEPTION_ILLEGAL_INSTRUCTION:
199 			crashType	= QP_CRASHTYPE_OTHER;
200 			reason		= "Illegal instruction";
201 			break;
202 
203 		case EXCEPTION_STACK_OVERFLOW:
204 			crashType	= QP_CRASHTYPE_OTHER;
205 			reason		= "Stack overflow";
206 			break;
207 
208 		default:
209 			/* \todo [pyry] Others. */
210 			crashType	= QP_CRASHTYPE_OTHER;
211 			reason		= "";
212 			break;
213 	}
214 
215 	/* Store reason. */
216 	qpCrashInfo_set(&g_crashHandler->crashInfo, crashType, reason, __FILE__, __LINE__);
217 
218 	/* Store win32-specific crash info. */
219 	g_crashHandler->crashAddress = (deUintptr)info->ExceptionRecord->ExceptionAddress;
220 
221 	/* Handle the crash. */
222 	DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): handled quietly\n"));
223 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
224 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
225 
226 	/* Release lock. */
227 	deMutex_unlock(g_crashHandler->crashHandlerLock);
228 
229 	return EXCEPTION_EXECUTE_HANDLER;
230 }
231 
assertFailureCallback(const char * expr,const char * file,int line)232 static void assertFailureCallback (const char* expr, const char* file, int line)
233 {
234 	/* Don't execute crash handler function if debugger is present. */
235 	if (IsDebuggerPresent())
236 	{
237 		DBGPRINT(("qpCrashHandler::assertFailureCallback(): debugger present\n"));
238 		return;
239 	}
240 
241 	/* Acquire crash handler lock. */
242 	deMutex_lock(g_crashHandler->crashHandlerLock);
243 
244 	/* Store info. */
245 	qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
246 	g_crashHandler->crashAddress = 0;
247 
248 	/* Handle the crash. */
249 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
250 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
251 
252 	/* Release lock. */
253 	deMutex_unlock(g_crashHandler->crashHandlerLock);
254 }
255 
qpCrashHandler_create(qpCrashHandlerFunc handlerFunc,void * userPointer)256 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
257 {
258 	/* Allocate & initialize. */
259 	qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
260 	DBGPRINT(("qpCrashHandler::create() -- Win32\n"));
261 	if (!handler)
262 		return handler;
263 
264 	DE_ASSERT(g_crashHandler == DE_NULL);
265 
266 	handler->crashHandlerFunc	= handlerFunc ? handlerFunc : defaultCrashHandler;
267 	handler->handlerUserPointer	= userPointer;
268 
269 	/* Create lock for crash handler. \note Has to be recursive or otherwise crash in assert failure causes deadlock. */
270 	{
271 		deMutexAttributes attr;
272 		attr.flags = DE_MUTEX_RECURSIVE;
273 		handler->crashHandlerLock = deMutex_create(&attr);
274 
275 		if (!handler->crashHandlerLock)
276 		{
277 			deFree(handler);
278 			return DE_NULL;
279 		}
280 	}
281 
282 	qpCrashInfo_init(&handler->crashInfo);
283 	handler->crashAddress		= 0;
284 
285 	/* Unhandled exception filter. */
286 	handler->oldExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter);
287 
288 	/* Prevent nasty error dialog. */
289 	SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
290 
291 	/* DE_ASSERT callback. */
292 	deSetAssertFailureCallback(assertFailureCallback);
293 
294 	g_crashHandler = handler;
295 	return handler;
296 }
297 
qpCrashHandler_destroy(qpCrashHandler * handler)298 void qpCrashHandler_destroy (qpCrashHandler* handler)
299 {
300 	DBGPRINT(("qpCrashHandler::destroy()\n"));
301 
302 	DE_ASSERT(g_crashHandler == handler);
303 
304 	deSetAssertFailureCallback(DE_NULL);
305 	SetUnhandledExceptionFilter(handler->oldExceptionFilter);
306 
307 	g_crashHandler = DE_NULL;
308 	deFree(handler);
309 }
310 
311 enum
312 {
313 	MAX_NAME_LENGTH = 64
314 };
315 
qpCrashHandler_writeCrashInfo(qpCrashHandler * handler,qpWriteCrashInfoFunc writeInfo,void * userPtr)316 void qpCrashHandler_writeCrashInfo (qpCrashHandler* handler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
317 {
318 	void*			addresses[32];
319 	HANDLE			process;
320 
321 	/* Symbol info struct. */
322 	deUint8			symInfoStorage[sizeof(SYMBOL_INFO)+MAX_NAME_LENGTH];
323 	SYMBOL_INFO*	symInfo			= (SYMBOL_INFO*)symInfoStorage;
324 
325 	DE_STATIC_ASSERT(sizeof(TCHAR) == sizeof(deUint8));
326 
327 	/* Write basic info. */
328 	qpCrashInfo_write(&handler->crashInfo, writeInfo, userPtr);
329 
330 	/* Acquire process handle and initialize symbols. */
331 	process = GetCurrentProcess();
332 
333 	/* Write backtrace. */
334 	if (SymInitialize(process, NULL, TRUE))
335 	{
336 		int batchStart		= 0;
337 		int globalFrameNdx	= 0;
338 
339 		/* Initialize symInfo. */
340 		deMemset(symInfo, 0, sizeof(symInfoStorage));
341 		symInfo->SizeOfStruct	= sizeof(SYMBOL_INFO);
342 		symInfo->MaxNameLen		= MAX_NAME_LENGTH;
343 
344 		/* Print address and symbol where crash happened. */
345 		if (handler->crashAddress != 0)
346 		{
347 			BOOL symInfoOk = SymFromAddr(process, (DWORD64)handler->crashAddress, 0, symInfo);
348 
349 			writeInfoFormat(writeInfo, userPtr, "  at %p %s%s\n", handler->crashAddress,
350 							symInfoOk ? symInfo->Name : "(unknown)",
351 							symInfoOk ? "()" : "");
352 		}
353 
354 		writeInfo(userPtr, "Backtrace:\n");
355 
356 		for (;;)
357 		{
358 			int curFrame;
359 			int numInBatch;
360 
361 			/* Get one batch. */
362 			numInBatch = CaptureStackBackTrace(batchStart, DE_LENGTH_OF_ARRAY(addresses), addresses, NULL);
363 
364 			for (curFrame = 0; curFrame < numInBatch; curFrame++)
365 			{
366 				BOOL symInfoOk = SymFromAddr(process, (DWORD64)addresses[curFrame], 0, symInfo);
367 
368 				writeInfoFormat(writeInfo, userPtr, "  %2d: %p %s%s\n", globalFrameNdx++, addresses[curFrame],
369 								symInfoOk ? symInfo->Name : "(unknown)",
370 								symInfoOk ? "()" : "");
371 			}
372 
373 			batchStart += numInBatch;
374 
375 			/* Check if we hit end of stack trace. */
376 			if (numInBatch == 0 || numInBatch < DE_LENGTH_OF_ARRAY(addresses))
377 				break;
378 		}
379 	}
380 }
381 
382 #else /* posix / generic implementation */
383 
384 #if defined(QP_USE_SIGNAL_HANDLER)
385 #	include <signal.h>
386 #endif
387 
388 #if defined(QP_USE_SIGNAL_HANDLER)
389 
390 typedef struct SignalInfo_s
391 {
392 	int				signalNum;
393 	qpCrashType		type;
394 	const char*		name;
395 } SignalInfo;
396 
397 static const SignalInfo s_signals[] =
398 {
399 	{ SIGABRT,		QP_CRASHTYPE_UNHANDLED_EXCEPTION,	"SIGABRT"	},
400 	{ SIGILL,		QP_CRASHTYPE_OTHER,					"SIGILL"	},
401 	{ SIGSEGV,		QP_CRASHTYPE_SEGMENTATION_FAULT,	"SIGSEGV"	},
402 	{ SIGFPE,		QP_CRASHTYPE_OTHER,					"SIGFPE"	},
403 	{ SIGBUS,		QP_CRASHTYPE_SEGMENTATION_FAULT,	"SIGBUS"	},
404 	{ SIGPIPE,		QP_CRASHTYPE_OTHER,					"SIGPIPE"	}
405 };
406 
407 #endif /* QP_USE_SIGNAL_HANDLER */
408 
409 struct qpCrashHandler_s
410 {
411 	qpCrashHandlerFunc		crashHandlerFunc;
412 	void*					handlerUserPointer;
413 
414 	qpCrashInfo				crashInfo;
415 	int						crashSignal;
416 
417 #if defined(QP_USE_SIGNAL_HANDLER)
418 	struct sigaction		oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)];
419 #endif
420 };
421 
422 qpCrashHandler* g_crashHandler = DE_NULL;
423 
assertFailureCallback(const char * expr,const char * file,int line)424 static void assertFailureCallback (const char* expr, const char* file, int line)
425 {
426 	/* Store info. */
427 	qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
428 
429 	/* Handle the crash. */
430 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
431 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
432 }
433 
434 #if defined(QP_USE_SIGNAL_HANDLER)
435 
getSignalInfo(int sigNum)436 static const SignalInfo* getSignalInfo (int sigNum)
437 {
438 	int ndx;
439 	for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++)
440 	{
441 		if (s_signals[ndx].signalNum == sigNum)
442 			return &s_signals[ndx];
443 	}
444 	return DE_NULL;
445 }
446 
signalHandler(int sigNum)447 static void signalHandler (int sigNum)
448 {
449 	const SignalInfo*	info	= getSignalInfo(sigNum);
450 	qpCrashType			type	= info ? info->type : QP_CRASHTYPE_OTHER;
451 	const char*			name	= info ? info->name : "Unknown signal";
452 
453 	qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0);
454 
455 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
456 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
457 }
458 
459 #endif /* QP_USE_SIGNAL_HANDLER */
460 
qpCrashHandler_create(qpCrashHandlerFunc handlerFunc,void * userPointer)461 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
462 {
463 	/* Allocate & initialize. */
464 	qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
465 	DBGPRINT(("qpCrashHandler::create()\n"));
466 	if (!handler)
467 		return handler;
468 
469 	DE_ASSERT(g_crashHandler == DE_NULL);
470 
471 	handler->crashHandlerFunc	= handlerFunc ? handlerFunc : defaultCrashHandler;
472 	handler->handlerUserPointer	= userPointer;
473 
474 	qpCrashInfo_init(&handler->crashInfo);
475 
476 	g_crashHandler = handler;
477 
478 	/* DE_ASSERT callback. */
479 	deSetAssertFailureCallback(assertFailureCallback);
480 
481 #if defined(QP_USE_SIGNAL_HANDLER)
482 	/* Register signal handlers. */
483 	{
484 		struct sigaction	action;
485 		int					sigNdx;
486 
487 		sigemptyset(&action.sa_mask);
488 		action.sa_handler	= signalHandler;
489 		action.sa_flags		= 0;
490 
491 		for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
492 			sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]);
493 	}
494 #endif
495 
496 	return handler;
497 }
498 
qpCrashHandler_destroy(qpCrashHandler * handler)499 void qpCrashHandler_destroy (qpCrashHandler* handler)
500 {
501 	DBGPRINT(("qpCrashHandler::destroy()\n"));
502 
503 	DE_ASSERT(g_crashHandler == handler);
504 
505 	deSetAssertFailureCallback(DE_NULL);
506 
507 #if defined(QP_USE_SIGNAL_HANDLER)
508 	/* Restore old handlers. */
509 	{
510 		int sigNdx;
511 		for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
512 			sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL);
513 	}
514 #endif
515 
516 	g_crashHandler = DE_NULL;
517 
518 	deFree(handler);
519 }
520 
521 #if (DE_PTR_SIZE  == 8)
522 #	define PTR_FMT "0x%016"
523 #elif (DE_PTR_SIZE  == 4)
524 #	define PTR_FMT "0x%08"
525 #else
526 #	error Unknwon DE_PTR_SIZE
527 #endif
528 
qpCrashHandler_writeCrashInfo(qpCrashHandler * crashHandler,qpWriteCrashInfoFunc writeInfo,void * userPtr)529 void qpCrashHandler_writeCrashInfo (qpCrashHandler* crashHandler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
530 {
531 	qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr);
532 
533 #if (DE_OS == DE_OS_UNIX)
534 	{
535 		char	tmpFileName[]	= "backtrace-XXXXXX";
536 		int		tmpFile			= mkstemp(tmpFileName);
537 
538 		if (tmpFile == -1)
539 		{
540 			writeInfoFormat(writeInfo, userPtr, "Failed to create tmpfile '%s' for the backtrace %s.", tmpFileName, strerror(errno));
541 			return;
542 		}
543 		else
544 		{
545 			void*	symbols[32];
546 			int		symbolCount;
547 			int		symbolNdx;
548 
549 			/* Remove file from filesystem. */
550 			remove(tmpFileName);
551 
552 			symbolCount = backtrace(symbols, DE_LENGTH_OF_ARRAY(symbols));
553 			backtrace_symbols_fd(symbols, symbolCount, tmpFile);
554 
555 			if (lseek(tmpFile, 0, SEEK_SET) < 0)
556 			{
557 				writeInfoFormat(writeInfo, userPtr, "Failed to seek to the beginning of the trace file %s.", strerror(errno));
558 				close(tmpFile);
559 				return;
560 			}
561 
562 			for (symbolNdx = 0; symbolNdx < symbolCount; symbolNdx++)
563 			{
564 				char	nameBuffer[256];
565 				size_t	symbolNameLength = 0;
566 				char	c;
567 
568 				{
569 					const int ret = snprintf(nameBuffer, DE_LENGTH_OF_ARRAY(nameBuffer), PTR_FMT  PRIXPTR " : ", (uintptr_t)symbols[symbolNdx]);
570 
571 					if (ret < 0)
572 					{
573 						writeInfoFormat(writeInfo, userPtr, "Failed to print symbol pointer.");
574 						symbolNameLength = 0;
575 					}
576 					else if (ret >= DE_LENGTH_OF_ARRAY(nameBuffer))
577 					{
578 						symbolNameLength = DE_LENGTH_OF_ARRAY(nameBuffer) - 1;
579 						nameBuffer[DE_LENGTH_OF_ARRAY(nameBuffer) - 1] = '\0';
580 					}
581 					else
582 						symbolNameLength = ret;
583 				}
584 
585 				for (;;)
586 				{
587 					if (read(tmpFile, &c, 1) == 1)
588 					{
589 						if (c == '\n')
590 						{
591 							/* Flush nameBuffer and move to next symbol. */
592 							nameBuffer[symbolNameLength] = '\0';
593 							writeInfo(userPtr, nameBuffer);
594 							break;
595 						}
596 						else
597 						{
598 							/* Add character to buffer if there is still space left. */
599 							if (symbolNameLength+1 < DE_LENGTH_OF_ARRAY(nameBuffer))
600 							{
601 								nameBuffer[symbolNameLength] = c;
602 								symbolNameLength++;
603 							}
604 						}
605 					}
606 					else
607 					{
608 						/* Flush nameBuffer. */
609 						nameBuffer[symbolNameLength] = '\0';
610 						writeInfo(userPtr, nameBuffer);
611 
612 						/* Temp file ended unexpectedly? */
613 						writeInfoFormat(writeInfo, userPtr, "Unexpected EOF reading backtrace file '%s'", tmpFileName);
614 						close(tmpFile);
615 						tmpFile = -1;
616 
617 						break;
618 					}
619 				}
620 
621 				if (tmpFile == -1)
622 					break;
623 			}
624 
625 			if (tmpFile != -1)
626 				close(tmpFile);
627 		}
628 	}
629 #endif
630 }
631 
632 #endif /* generic */
633