Skip to content
Permalink
Browse files

PR #39: Improve error handling and detection

  • Loading branch information...
kwi-dk committed Feb 22, 2019
2 parents 84d5034 + 843125c commit d61fb65d5ab8ffa2a3fb1cb97ccee3d4517a2d2f
Showing with 77 additions and 36 deletions.
  1. +46 −18 src/Common.cpp
  2. +3 −0 src/Common.hpp
  3. +26 −16 src/ExecWin32.cpp
  4. +1 −1 src/Main.cpp
  5. +1 −1 src/MemoryMappedFile.cpp
@@ -51,6 +51,28 @@ static bool DebuggerAttached()
#endif
}

static void NORETURN FlushAndAbort()
{
// The C standard does not require 'abort' to flush stream buffers.
// On Windows at least, an explicit 'fflush' is required for stderr messages to actually be shown.
fflush(NULL);
abort();
}

static void PrintErrno()
{
#if TUNDRA_WIN32
wchar_t buf[256];
DWORD lastError = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, sizeof(buf), NULL);
fprintf(stderr, "errno: %d (%s) GetLastError: %d (0x%08X): %ls\n", errno, strerror(errno), (int)lastError, (unsigned int)lastError, buf);
#else
fprintf(stderr, "errno: %d (%s)\n", errno, strerror(errno));
#endif
}

void InitCommon(void)
{
#if defined(TUNDRA_WIN32)
@@ -65,48 +87,54 @@ void InitCommon(void)

void NORETURN Croak(const char* fmt, ...)
{
fputs("tundra: error: ", stderr);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
if (DebuggerAttached())
abort();
FlushAndAbort();
else
exit(1);
}

void NORETURN CroakErrno(const char* fmt, ...)
{
fputs("tundra: error: ", stderr);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
#if TUNDRA_WIN32
wchar_t buf[256];
DWORD lastError = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, sizeof(buf), NULL);
fprintf(stderr, "errno: %d (%s) GetLastError %d (%s)\n", errno, strerror(errno), lastError, buf);
#else
fprintf(stderr, "errno: %d (%s)\n", errno, strerror(errno));
#endif
PrintErrno();
if (DebuggerAttached())
abort();
FlushAndAbort();
else
exit(1);
}

void NORETURN CroakAbort(const char* fmt, ...)
{
fputs("tundra: error: ", stderr);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
abort();
FlushAndAbort();
}

void NORETURN CroakErrnoAbort(const char* fmt, ...)
{
fputs("tundra: error: ", stderr);
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
PrintErrno();
FlushAndAbort();
}

uint32_t Djb2Hash(const char *str_)
@@ -247,10 +275,10 @@ void GetCwd(char* buffer, size_t buffer_size)
#if defined(TUNDRA_WIN32)
DWORD res = GetCurrentDirectoryA((DWORD)buffer_size, buffer);
if (0 == res || ((DWORD)buffer_size) <= res)
Croak("couldn't get working directory");
CroakErrno("couldn't get working directory");
#elif defined(TUNDRA_UNIX)
if (NULL == getcwd(buffer, buffer_size))
Croak("couldn't get working directory");
CroakErrno("couldn't get working directory");
#else
#error Unsupported platform
#endif
@@ -275,14 +303,14 @@ const char* GetExePath()
{
#if defined(TUNDRA_WIN32)
if (!GetModuleFileNameA(NULL, s_ExePath, (DWORD)sizeof s_ExePath))
Croak("couldn't get module filename");
CroakErrno("couldn't get module filename");
#elif defined(TUNDRA_APPLE)
uint32_t size = sizeof s_ExePath;
if (0 != _NSGetExecutablePath(s_ExePath, &size))
Croak("_NSGetExecutablePath failed");
#elif defined(TUNDRA_LINUX)
if (-1 == readlink("/proc/self/exe", s_ExePath, (sizeof s_ExePath) - 1))
Croak("couldn't read /proc/self/exe to get exe path: %s", strerror(errno));
CroakErrno("couldn't read /proc/self/exe to get exe path");
#elif defined(TUNDRA_FREEBSD)
size_t cb = sizeof s_ExePath;
int mib[4];
@@ -291,7 +319,7 @@ const char* GetExePath()
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
if (0 !=sysctl(mib, 4, s_ExePath, &cb, NULL, 0))
Croak("KERN_PROC_PATHNAME syscall failed");
CroakErrno("KERN_PROC_PATHNAME syscall failed");
#else
#error "unsupported platform"
#endif
@@ -52,6 +52,9 @@ void NORETURN CroakErrno(const char* fmt, ...);
// Abort the program with an error message on stderr
void NORETURN CroakAbort(const char* fmt, ...);

// Abort the program with an error message on stderr, also printing the errno/GetLastError() status
void NORETURN CroakErrnoAbort(const char* fmt, ...);

//-----------------------------------------------------------------------------
// Logging
//-----------------------------------------------------------------------------
@@ -50,7 +50,7 @@ static Mutex s_FdMutex;
//allocate one stdout and one stderr handle per job
static HANDLE s_TempFiles[kMaxBuildThreads];

static HANDLE GetOrCreateTempFileFor(int job_id)
static HANDLE GetOrCreateTempFileFor(int job_id, const char* command_that_just_finished)
{
HANDLE result = s_TempFiles[job_id];

@@ -71,8 +71,14 @@ static HANDLE GetOrCreateTempFileFor(int job_id)

if (INVALID_HANDLE_VALUE == result)
{
fprintf(stderr, "failed to create temporary file %s\n", temp_dir);
return INVALID_HANDLE_VALUE;
bool was_sharing_violation = GetLastError() == 0x00000020;
if (command_that_just_finished)
CroakErrno("failed to create temporary file: %s\n"
"The just completed command was: %s%s",
temp_dir, command_that_just_finished,
was_sharing_violation ? "\nMost likely, the build action spawned a lingering subprocess that is keeping stdout/stderr open (this is not allowed)." : "");
else
CroakErrno("failed to create temporary file: %s", temp_dir);
}

SetHandleInformation(result, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
@@ -83,7 +89,7 @@ static HANDLE GetOrCreateTempFileFor(int job_id)
return result;
}

static void CopyTempFileContentsIntoBufferAndPrepareFileForReuse(int job_id, OutputBufferData* outputBuffer, MemAllocHeap* heap)
static void CopyTempFileContentsIntoBufferAndPrepareFileForReuse(int job_id, const char* command_that_just_finished, OutputBufferData* outputBuffer, MemAllocHeap* heap)
{
HANDLE tempFile = s_TempFiles[job_id];

@@ -106,15 +112,19 @@ static void CopyTempFileContentsIntoBufferAndPrepareFileForReuse(int job_id, Out
DWORD spaceRemaining = (DWORD)outputBuffer->buffer_size - outputBuffer->cursor;
DWORD amountRead = 0;
if (!ReadFile(tempFile, outputBuffer->buffer + outputBuffer->cursor, spaceRemaining, &amountRead, NULL) || amountRead == 0)
CroakAbort("ReadFile from temporary file failed before we read all of its data");
CroakErrnoAbort("ReadFile from temporary file failed before we read all of its data");
processed += amountRead;
outputBuffer->cursor += amountRead;
}
outputBuffer->buffer[outputBuffer->cursor] = 0;

// Truncate the temporary file for reuse
SetFilePointer(tempFile, 0, NULL, FILE_BEGIN);
SetEndOfFile(tempFile);
// Close file (which should trigger its deletion).
if (!CloseHandle(tempFile))
CroakErrnoAbort("CloseHandle failed");
s_TempFiles[job_id] = 0;

// Immediately recreate the file. If a lingering process is keeping the file open, that is a bug, and we want to find out immediately.
GetOrCreateTempFileFor(job_id, command_that_just_finished);
}

static struct Win32EnvBinding
@@ -133,7 +143,7 @@ void ExecInit(void)
s_TundraPid = GetCurrentProcessId();

if (0 == GetTempPathA(sizeof(s_TemporaryDir), s_TemporaryDir))
Croak("error: couldn't get temporary directory path");
CroakErrno("couldn't get temporary directory path");

MutexInit(&s_FdMutex);

@@ -440,7 +450,7 @@ ExecResult ExecuteProcess(
void* attributeListAllocation = nullptr;
if (!stream_to_stdout)
{
sinfo.StartupInfo.hStdOutput = sinfo.StartupInfo.hStdError = GetOrCreateTempFileFor(job_id);
sinfo.StartupInfo.hStdOutput = sinfo.StartupInfo.hStdError = GetOrCreateTempFileFor(job_id, NULL);
sinfo.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
sinfo.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
creationFlags |= EXTENDED_STARTUPINFO_PRESENT;
@@ -459,7 +469,7 @@ ExecResult ExecuteProcess(

//this is pretty crazy, but this call is _supposed_ to fail, and give us the correct attributeListSize, so we verify the returncode !=0
if (InitializeProcThreadAttributeList(NULL, 1, 0, &attributeListSize))
CroakAbort("InitializeProcThreadAttributeList failed");
CroakErrnoAbort("InitializeProcThreadAttributeList failed");

attributeListAllocation = HeapAllocate(heap, attributeListSize);
sinfo.lpAttributeList = static_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(attributeListAllocation);
@@ -479,7 +489,7 @@ ExecResult ExecuteProcess(
CroakAbort("env block error; too big?\n");

if (!MultiByteToWideChar(CP_UTF8, 0, env_block, (int)env_block_length, env_block_wide, sizeof(env_block_wide)/sizeof(WCHAR)))
CroakAbort("Failed converting environment block to wide char\n");
CroakErrnoAbort("Failed converting environment block to wide char");

ExecResult result;
char new_cmd[8192];
@@ -497,16 +507,16 @@ ExecResult ExecuteProcess(

HANDLE job_object = CreateJobObject(NULL, NULL);
if (!job_object)
CroakErrno("ERROR: Couldn't create job object.");
CroakErrno("Couldn't create job object.");

WCHAR buffer_wide[sizeof(buffer) * 2];
if (!MultiByteToWideChar(CP_UTF8, 0, buffer, (int)sizeof(buffer), buffer_wide, sizeof(buffer_wide) / sizeof(WCHAR)))
CroakAbort("Failed converting buffer block to wide char\n");
CroakErrnoAbort("Failed converting buffer block to wide char");

PROCESS_INFORMATION pinfo;

if (!CreateProcessW(NULL, buffer_wide, NULL, NULL, enherit_handles, creationFlags, env_block_wide, NULL, &sinfo.StartupInfo, &pinfo))
CroakAbort("ERROR: Couldn't launch process. Win32 error = %d", (int)GetLastError());
CroakErrnoAbort("Couldn't launch process");

if (!stream_to_stdout)
{
@@ -524,7 +534,7 @@ ExecResult ExecuteProcess(
CleanupResponseFile(responseFile);

if (!stream_to_stdout)
CopyTempFileContentsIntoBufferAndPrepareFileForReuse(job_id, &result.m_OutputBuffer, heap);
CopyTempFileContentsIntoBufferAndPrepareFileForReuse(job_id, buffer, &result.m_OutputBuffer, heap);

CloseHandle(pinfo.hProcess);
CloseHandle(job_object);
@@ -341,7 +341,7 @@ int main(int argc, char* argv[])
if (options.m_WorkingDir)
{
if (!SetCwd(options.m_WorkingDir))
Croak("couldn't change directory to %s", options.m_WorkingDir);
CroakErrno("couldn't change directory to %s", options.m_WorkingDir);
}

if (options.m_ShowHelp)
@@ -73,7 +73,7 @@ void MmapFileUnmap(MemoryMappedFile* self)
TimingScope timing_scope(&g_Stats.m_MunmapCalls, &g_Stats.m_MunmapTimeCycles);

if (0 != munmap(self->m_Address, self->m_Size))
Croak("munmap(%p, %d) failed: %d", self->m_Address, (int) self->m_Size, errno);
CroakErrno("munmap(%p, %d) failed", self->m_Address, (int) self->m_Size);

close((int) self->m_SysData[0]);
}

0 comments on commit d61fb65

Please sign in to comment.
You can’t perform that action at this time.