638 lines
16 KiB
C
638 lines
16 KiB
C
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program Helper Library
|
|
* -------------------------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed 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.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief System handler handler override
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "qpCrashHandler.h"
|
|
#include "qpDebugOut.h"
|
|
|
|
#include "deThread.h"
|
|
#include "deMemory.h"
|
|
#include "deString.h"
|
|
#include "deMutex.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
|
|
#if (DE_OS == DE_OS_UNIX)
|
|
# include <unistd.h>
|
|
# include <execinfo.h>
|
|
# include <errno.h>
|
|
# include <inttypes.h>
|
|
#endif
|
|
|
|
#if 0
|
|
# define DBGPRINT(X) qpPrintf X
|
|
#else
|
|
# define DBGPRINT(X)
|
|
#endif
|
|
|
|
/* Crash info write helper. */
|
|
static void writeInfoFormat (qpWriteCrashInfoFunc writeFunc, void* userPtr, const char* format, ...)
|
|
{
|
|
char buf[256];
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
vsnprintf(buf, sizeof(buf), format, ap);
|
|
va_end(ap);
|
|
|
|
writeFunc(userPtr, buf);
|
|
}
|
|
|
|
/* Shared crash info. */
|
|
typedef enum qpCrashType_e
|
|
{
|
|
QP_CRASHTYPE_SEGMENTATION_FAULT = 0,
|
|
QP_CRASHTYPE_ASSERT,
|
|
QP_CRASHTYPE_UNHANDLED_EXCEPTION,
|
|
QP_CRASHTYPE_OTHER,
|
|
|
|
QP_CRASHTYPE_LAST
|
|
} qpCrashType;
|
|
|
|
typedef struct qpCrashInfo_s
|
|
{
|
|
qpCrashType type;
|
|
const char* message;
|
|
const char* file;
|
|
int line;
|
|
} qpCrashInfo;
|
|
|
|
static void qpCrashInfo_init (qpCrashInfo* info)
|
|
{
|
|
info->type = QP_CRASHTYPE_LAST;
|
|
info->message = DE_NULL;
|
|
info->file = DE_NULL;
|
|
info->line = 0;
|
|
}
|
|
|
|
static void qpCrashInfo_set (qpCrashInfo* info, qpCrashType type, const char* message, const char* file, int line)
|
|
{
|
|
info->type = type;
|
|
info->message = message;
|
|
info->file = file;
|
|
info->line = line;
|
|
}
|
|
|
|
static void qpCrashInfo_write (qpCrashInfo* info, qpWriteCrashInfoFunc writeInfo, void* userPtr)
|
|
{
|
|
switch (info->type)
|
|
{
|
|
case QP_CRASHTYPE_SEGMENTATION_FAULT:
|
|
writeInfoFormat(writeInfo, userPtr, "Segmentation fault: '%s'\n", info->message);
|
|
break;
|
|
|
|
case QP_CRASHTYPE_UNHANDLED_EXCEPTION:
|
|
writeInfoFormat(writeInfo, userPtr, "Unhandled exception: '%s'\n", info->message);
|
|
break;
|
|
|
|
case QP_CRASHTYPE_ASSERT:
|
|
writeInfoFormat(writeInfo, userPtr, "Assertion '%s' failed at %s:%d\n",
|
|
info->message,
|
|
info->file,
|
|
info->line);
|
|
break;
|
|
|
|
case QP_CRASHTYPE_OTHER:
|
|
default:
|
|
writeInfoFormat(writeInfo, userPtr, "Crash: '%s'\n", info->message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void defaultWriteInfo (void* userPtr, const char* infoString)
|
|
{
|
|
DE_UNREF(userPtr);
|
|
qpPrintf("%s", infoString);
|
|
}
|
|
|
|
static void defaultCrashHandler (qpCrashHandler* crashHandler, void* userPtr)
|
|
{
|
|
DE_UNREF(userPtr);
|
|
qpCrashHandler_writeCrashInfo(crashHandler, defaultWriteInfo, DE_NULL);
|
|
qpDief("Test process crashed");
|
|
}
|
|
|
|
#if (DE_OS == DE_OS_WIN32) && (DE_COMPILER == DE_COMPILER_MSC)
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
/* DbgHelp.h generates C4091 */
|
|
#pragma warning (push)
|
|
#pragma warning (disable: 4091)
|
|
#include <DbgHelp.h>
|
|
#pragma warning (pop)
|
|
|
|
struct qpCrashHandler_s
|
|
{
|
|
qpCrashHandlerFunc crashHandlerFunc;
|
|
void* handlerUserPointer;
|
|
|
|
deMutex crashHandlerLock;
|
|
qpCrashInfo crashInfo;
|
|
deUintptr crashAddress;
|
|
|
|
LPTOP_LEVEL_EXCEPTION_FILTER oldExceptionFilter;
|
|
};
|
|
|
|
qpCrashHandler* g_crashHandler = DE_NULL;
|
|
|
|
static LONG WINAPI unhandledExceptionFilter (struct _EXCEPTION_POINTERS* info)
|
|
{
|
|
qpCrashType crashType = QP_CRASHTYPE_LAST;
|
|
const char* reason = DE_NULL;
|
|
|
|
/* Skip breakpoints. */
|
|
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
|
|
{
|
|
DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): breakpoint\n"));
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
/* If no handler present (how could that be?), don't handle. */
|
|
if (g_crashHandler == DE_NULL)
|
|
{
|
|
DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): no crash handler registered\n"));
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
/* If we have a debugger, let it handle the exception. */
|
|
if (IsDebuggerPresent())
|
|
{
|
|
DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): debugger present\n"));
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
/* Acquire crash handler lock. Otherwise we might get strange behavior when multiple threads enter crash handler simultaneously. */
|
|
deMutex_lock(g_crashHandler->crashHandlerLock);
|
|
|
|
/* Map crash type. */
|
|
switch (info->ExceptionRecord->ExceptionCode)
|
|
{
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
crashType = QP_CRASHTYPE_SEGMENTATION_FAULT;
|
|
reason = "Access violation";
|
|
break;
|
|
|
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
|
crashType = QP_CRASHTYPE_SEGMENTATION_FAULT;
|
|
reason = "Array bounds exceeded";
|
|
break;
|
|
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
crashType = QP_CRASHTYPE_OTHER;
|
|
reason = "Illegal instruction";
|
|
break;
|
|
|
|
case EXCEPTION_STACK_OVERFLOW:
|
|
crashType = QP_CRASHTYPE_OTHER;
|
|
reason = "Stack overflow";
|
|
break;
|
|
|
|
default:
|
|
/* \todo [pyry] Others. */
|
|
crashType = QP_CRASHTYPE_OTHER;
|
|
reason = "";
|
|
break;
|
|
}
|
|
|
|
/* Store reason. */
|
|
qpCrashInfo_set(&g_crashHandler->crashInfo, crashType, reason, __FILE__, __LINE__);
|
|
|
|
/* Store win32-specific crash info. */
|
|
g_crashHandler->crashAddress = (deUintptr)info->ExceptionRecord->ExceptionAddress;
|
|
|
|
/* Handle the crash. */
|
|
DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): handled quietly\n"));
|
|
if (g_crashHandler->crashHandlerFunc != DE_NULL)
|
|
g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
|
|
|
|
/* Release lock. */
|
|
deMutex_unlock(g_crashHandler->crashHandlerLock);
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
static void assertFailureCallback (const char* expr, const char* file, int line)
|
|
{
|
|
/* Don't execute crash handler function if debugger is present. */
|
|
if (IsDebuggerPresent())
|
|
{
|
|
DBGPRINT(("qpCrashHandler::assertFailureCallback(): debugger present\n"));
|
|
return;
|
|
}
|
|
|
|
/* Acquire crash handler lock. */
|
|
deMutex_lock(g_crashHandler->crashHandlerLock);
|
|
|
|
/* Store info. */
|
|
qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
|
|
g_crashHandler->crashAddress = 0;
|
|
|
|
/* Handle the crash. */
|
|
if (g_crashHandler->crashHandlerFunc != DE_NULL)
|
|
g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
|
|
|
|
/* Release lock. */
|
|
deMutex_unlock(g_crashHandler->crashHandlerLock);
|
|
}
|
|
|
|
qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
|
|
{
|
|
/* Allocate & initialize. */
|
|
qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
|
|
DBGPRINT(("qpCrashHandler::create() -- Win32\n"));
|
|
if (!handler)
|
|
return handler;
|
|
|
|
DE_ASSERT(g_crashHandler == DE_NULL);
|
|
|
|
handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler;
|
|
handler->handlerUserPointer = userPointer;
|
|
|
|
/* Create lock for crash handler. \note Has to be recursive or otherwise crash in assert failure causes deadlock. */
|
|
{
|
|
deMutexAttributes attr;
|
|
attr.flags = DE_MUTEX_RECURSIVE;
|
|
handler->crashHandlerLock = deMutex_create(&attr);
|
|
|
|
if (!handler->crashHandlerLock)
|
|
{
|
|
deFree(handler);
|
|
return DE_NULL;
|
|
}
|
|
}
|
|
|
|
qpCrashInfo_init(&handler->crashInfo);
|
|
handler->crashAddress = 0;
|
|
|
|
/* Unhandled exception filter. */
|
|
handler->oldExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter);
|
|
|
|
/* Prevent nasty error dialog. */
|
|
SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
|
|
|
|
/* DE_ASSERT callback. */
|
|
deSetAssertFailureCallback(assertFailureCallback);
|
|
|
|
g_crashHandler = handler;
|
|
return handler;
|
|
}
|
|
|
|
void qpCrashHandler_destroy (qpCrashHandler* handler)
|
|
{
|
|
DBGPRINT(("qpCrashHandler::destroy()\n"));
|
|
|
|
DE_ASSERT(g_crashHandler == handler);
|
|
|
|
deSetAssertFailureCallback(DE_NULL);
|
|
SetUnhandledExceptionFilter(handler->oldExceptionFilter);
|
|
|
|
g_crashHandler = DE_NULL;
|
|
deFree(handler);
|
|
}
|
|
|
|
enum
|
|
{
|
|
MAX_NAME_LENGTH = 64
|
|
};
|
|
|
|
void qpCrashHandler_writeCrashInfo (qpCrashHandler* handler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
|
|
{
|
|
void* addresses[32];
|
|
HANDLE process;
|
|
|
|
/* Symbol info struct. */
|
|
deUint8 symInfoStorage[sizeof(SYMBOL_INFO)+MAX_NAME_LENGTH];
|
|
SYMBOL_INFO* symInfo = (SYMBOL_INFO*)symInfoStorage;
|
|
|
|
DE_STATIC_ASSERT(sizeof(TCHAR) == sizeof(deUint8));
|
|
|
|
/* Write basic info. */
|
|
qpCrashInfo_write(&handler->crashInfo, writeInfo, userPtr);
|
|
|
|
/* Acquire process handle and initialize symbols. */
|
|
process = GetCurrentProcess();
|
|
|
|
/* Write backtrace. */
|
|
if (SymInitialize(process, NULL, TRUE))
|
|
{
|
|
int batchStart = 0;
|
|
int globalFrameNdx = 0;
|
|
|
|
/* Initialize symInfo. */
|
|
deMemset(symInfo, 0, sizeof(symInfoStorage));
|
|
symInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
|
|
symInfo->MaxNameLen = MAX_NAME_LENGTH;
|
|
|
|
/* Print address and symbol where crash happened. */
|
|
if (handler->crashAddress != 0)
|
|
{
|
|
BOOL symInfoOk = SymFromAddr(process, (DWORD64)handler->crashAddress, 0, symInfo);
|
|
|
|
writeInfoFormat(writeInfo, userPtr, " at %p %s%s\n", handler->crashAddress,
|
|
symInfoOk ? symInfo->Name : "(unknown)",
|
|
symInfoOk ? "()" : "");
|
|
}
|
|
|
|
writeInfo(userPtr, "Backtrace:\n");
|
|
|
|
for (;;)
|
|
{
|
|
int curFrame;
|
|
int numInBatch;
|
|
|
|
/* Get one batch. */
|
|
numInBatch = CaptureStackBackTrace(batchStart, DE_LENGTH_OF_ARRAY(addresses), addresses, NULL);
|
|
|
|
for (curFrame = 0; curFrame < numInBatch; curFrame++)
|
|
{
|
|
BOOL symInfoOk = SymFromAddr(process, (DWORD64)addresses[curFrame], 0, symInfo);
|
|
|
|
writeInfoFormat(writeInfo, userPtr, " %2d: %p %s%s\n", globalFrameNdx++, addresses[curFrame],
|
|
symInfoOk ? symInfo->Name : "(unknown)",
|
|
symInfoOk ? "()" : "");
|
|
}
|
|
|
|
batchStart += numInBatch;
|
|
|
|
/* Check if we hit end of stack trace. */
|
|
if (numInBatch == 0 || numInBatch < DE_LENGTH_OF_ARRAY(addresses))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else /* posix / generic implementation */
|
|
|
|
#if defined(QP_USE_SIGNAL_HANDLER)
|
|
# include <signal.h>
|
|
#endif
|
|
|
|
#if defined(QP_USE_SIGNAL_HANDLER)
|
|
|
|
typedef struct SignalInfo_s
|
|
{
|
|
int signalNum;
|
|
qpCrashType type;
|
|
const char* name;
|
|
} SignalInfo;
|
|
|
|
static const SignalInfo s_signals[] =
|
|
{
|
|
{ SIGABRT, QP_CRASHTYPE_UNHANDLED_EXCEPTION, "SIGABRT" },
|
|
{ SIGILL, QP_CRASHTYPE_OTHER, "SIGILL" },
|
|
{ SIGSEGV, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGSEGV" },
|
|
{ SIGFPE, QP_CRASHTYPE_OTHER, "SIGFPE" },
|
|
{ SIGBUS, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGBUS" },
|
|
{ SIGPIPE, QP_CRASHTYPE_OTHER, "SIGPIPE" }
|
|
};
|
|
|
|
#endif /* QP_USE_SIGNAL_HANDLER */
|
|
|
|
struct qpCrashHandler_s
|
|
{
|
|
qpCrashHandlerFunc crashHandlerFunc;
|
|
void* handlerUserPointer;
|
|
|
|
qpCrashInfo crashInfo;
|
|
int crashSignal;
|
|
|
|
#if defined(QP_USE_SIGNAL_HANDLER)
|
|
struct sigaction oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)];
|
|
#endif
|
|
};
|
|
|
|
qpCrashHandler* g_crashHandler = DE_NULL;
|
|
|
|
static void assertFailureCallback (const char* expr, const char* file, int line)
|
|
{
|
|
/* Store info. */
|
|
qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
|
|
|
|
/* Handle the crash. */
|
|
if (g_crashHandler->crashHandlerFunc != DE_NULL)
|
|
g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
|
|
}
|
|
|
|
#if defined(QP_USE_SIGNAL_HANDLER)
|
|
|
|
static const SignalInfo* getSignalInfo (int sigNum)
|
|
{
|
|
int ndx;
|
|
for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++)
|
|
{
|
|
if (s_signals[ndx].signalNum == sigNum)
|
|
return &s_signals[ndx];
|
|
}
|
|
return DE_NULL;
|
|
}
|
|
|
|
static void signalHandler (int sigNum)
|
|
{
|
|
const SignalInfo* info = getSignalInfo(sigNum);
|
|
qpCrashType type = info ? info->type : QP_CRASHTYPE_OTHER;
|
|
const char* name = info ? info->name : "Unknown signal";
|
|
|
|
qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0);
|
|
|
|
if (g_crashHandler->crashHandlerFunc != DE_NULL)
|
|
g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
|
|
}
|
|
|
|
#endif /* QP_USE_SIGNAL_HANDLER */
|
|
|
|
qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
|
|
{
|
|
/* Allocate & initialize. */
|
|
qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
|
|
DBGPRINT(("qpCrashHandler::create()\n"));
|
|
if (!handler)
|
|
return handler;
|
|
|
|
DE_ASSERT(g_crashHandler == DE_NULL);
|
|
|
|
handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler;
|
|
handler->handlerUserPointer = userPointer;
|
|
|
|
qpCrashInfo_init(&handler->crashInfo);
|
|
|
|
g_crashHandler = handler;
|
|
|
|
/* DE_ASSERT callback. */
|
|
deSetAssertFailureCallback(assertFailureCallback);
|
|
|
|
#if defined(QP_USE_SIGNAL_HANDLER)
|
|
/* Register signal handlers. */
|
|
{
|
|
struct sigaction action;
|
|
int sigNdx;
|
|
|
|
sigemptyset(&action.sa_mask);
|
|
action.sa_handler = signalHandler;
|
|
action.sa_flags = 0;
|
|
|
|
for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
|
|
sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]);
|
|
}
|
|
#endif
|
|
|
|
return handler;
|
|
}
|
|
|
|
void qpCrashHandler_destroy (qpCrashHandler* handler)
|
|
{
|
|
DBGPRINT(("qpCrashHandler::destroy()\n"));
|
|
|
|
DE_ASSERT(g_crashHandler == handler);
|
|
|
|
deSetAssertFailureCallback(DE_NULL);
|
|
|
|
#if defined(QP_USE_SIGNAL_HANDLER)
|
|
/* Restore old handlers. */
|
|
{
|
|
int sigNdx;
|
|
for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
|
|
sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL);
|
|
}
|
|
#endif
|
|
|
|
g_crashHandler = DE_NULL;
|
|
|
|
deFree(handler);
|
|
}
|
|
|
|
#if (DE_PTR_SIZE == 8)
|
|
# define PTR_FMT "0x%016"
|
|
#elif (DE_PTR_SIZE == 4)
|
|
# define PTR_FMT "0x%08"
|
|
#else
|
|
# error Unknwon DE_PTR_SIZE
|
|
#endif
|
|
|
|
void qpCrashHandler_writeCrashInfo (qpCrashHandler* crashHandler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
|
|
{
|
|
qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr);
|
|
|
|
#if (DE_OS == DE_OS_UNIX)
|
|
{
|
|
char tmpFileName[] = "backtrace-XXXXXX";
|
|
int tmpFile = mkstemp(tmpFileName);
|
|
|
|
if (tmpFile == -1)
|
|
{
|
|
writeInfoFormat(writeInfo, userPtr, "Failed to create tmpfile '%s' for the backtrace %s.", tmpFileName, strerror(errno));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
void* symbols[32];
|
|
int symbolCount;
|
|
int symbolNdx;
|
|
|
|
/* Remove file from filesystem. */
|
|
remove(tmpFileName);
|
|
|
|
symbolCount = backtrace(symbols, DE_LENGTH_OF_ARRAY(symbols));
|
|
backtrace_symbols_fd(symbols, symbolCount, tmpFile);
|
|
|
|
if (lseek(tmpFile, 0, SEEK_SET) < 0)
|
|
{
|
|
writeInfoFormat(writeInfo, userPtr, "Failed to seek to the beginning of the trace file %s.", strerror(errno));
|
|
close(tmpFile);
|
|
return;
|
|
}
|
|
|
|
for (symbolNdx = 0; symbolNdx < symbolCount; symbolNdx++)
|
|
{
|
|
char nameBuffer[256];
|
|
size_t symbolNameLength = 0;
|
|
char c;
|
|
|
|
{
|
|
const int ret = snprintf(nameBuffer, DE_LENGTH_OF_ARRAY(nameBuffer), PTR_FMT PRIXPTR " : ", (uintptr_t)symbols[symbolNdx]);
|
|
|
|
if (ret < 0)
|
|
{
|
|
writeInfoFormat(writeInfo, userPtr, "Failed to print symbol pointer.");
|
|
symbolNameLength = 0;
|
|
}
|
|
else if (ret >= DE_LENGTH_OF_ARRAY(nameBuffer))
|
|
{
|
|
symbolNameLength = DE_LENGTH_OF_ARRAY(nameBuffer) - 1;
|
|
nameBuffer[DE_LENGTH_OF_ARRAY(nameBuffer) - 1] = '\0';
|
|
}
|
|
else
|
|
symbolNameLength = ret;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (read(tmpFile, &c, 1) == 1)
|
|
{
|
|
if (c == '\n')
|
|
{
|
|
/* Flush nameBuffer and move to next symbol. */
|
|
nameBuffer[symbolNameLength] = '\0';
|
|
writeInfo(userPtr, nameBuffer);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* Add character to buffer if there is still space left. */
|
|
if (symbolNameLength+1 < DE_LENGTH_OF_ARRAY(nameBuffer))
|
|
{
|
|
nameBuffer[symbolNameLength] = c;
|
|
symbolNameLength++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Flush nameBuffer. */
|
|
nameBuffer[symbolNameLength] = '\0';
|
|
writeInfo(userPtr, nameBuffer);
|
|
|
|
/* Temp file ended unexpectedly? */
|
|
writeInfoFormat(writeInfo, userPtr, "Unexpected EOF reading backtrace file '%s'", tmpFileName);
|
|
close(tmpFile);
|
|
tmpFile = -1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tmpFile == -1)
|
|
break;
|
|
}
|
|
|
|
if (tmpFile != -1)
|
|
close(tmpFile);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif /* generic */
|