1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-20 19:42:54 +02:00
llvm-mirror/lib/Support/ErrorHandling.cpp
Reid Kleckner 7b2e05b496 [Support] - Add bad alloc error handler for handling allocation malfunctions
Summary:
Patch by Klaus Kretzschmar

We would like to introduce a new type of llvm error handler for handling
bad alloc fault situations.  LLVM already provides a fatal error handler
for serious non-recoverable error situations which by default writes
some error information to stderr and calls exit(1) at the end (functions
are marked as 'noreturn').

For long running processes (e.g. a server application), exiting the
process is not an acceptable option, especially not when the system is
in a temporary resource bottleneck with a good chance to recover from
this fault situation. In such a situation you would rather throw an
exception to stop the current compilation and try to overcome the
resource bottleneck. The user should be aware of the problem of throwing
an exception in bad alloc situations, e.g. you must not do any
allocations in the unwind chain. This is especially true when adding
exceptions in existing unfamiliar code (as already stated in the comment
of the current fatal error handler)

So the new handler can also be used to distinguish from general fatal
error situations where recovering is no option.  It should be used in
cases where a clean unwind after the allocation is guaranteed.

This patch contains:
- A report_bad_alloc function which calls a user defined bad alloc
  error handler. If no user handler is registered the
  report_fatal_error function is called. This function is not marked as
  'noreturn'.
- A install/restore_bad_alloc_error_handler to install/restore the bad
  alloc handler.
- An example (in Mutex.cpp) where the report_bad_alloc function is
  called in case of a malloc returns a nullptr.

If this patch gets accepted we would create similar patches to fix
corresponding malloc/calloc usages in the llvm code.

Reviewers: chandlerc, greened, baldrick, rnk

Reviewed By: rnk

Subscribers: llvm-commits, MatzeB

Differential Revision: https://reviews.llvm.org/D34753

llvm-svn: 307673
2017-07-11 16:45:30 +00:00

251 lines
9.3 KiB
C++

//===- lib/Support/ErrorHandling.cpp - Callbacks for errors ---------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines an API used to indicate fatal error conditions. Non-fatal
// errors (most of them) should be handled through LLVMContext.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/ErrorHandling.h"
#include "llvm-c/ErrorHandling.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Config/config.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/MutexGuard.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/WindowsError.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstdlib>
#include <new>
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if defined(_MSC_VER)
# include <io.h>
# include <fcntl.h>
#endif
using namespace llvm;
static fatal_error_handler_t ErrorHandler = nullptr;
static void *ErrorHandlerUserData = nullptr;
static ManagedStatic<sys::Mutex> ErrorHandlerMutex;
static fatal_error_handler_t BadAllocErrorHandler = nullptr;
static void *BadAllocErrorHandlerUserData = nullptr;
static ManagedStatic<sys::Mutex> BadAllocErrorHandlerMutex;
void llvm::install_fatal_error_handler(fatal_error_handler_t handler,
void *user_data) {
llvm::MutexGuard Lock(*ErrorHandlerMutex);
assert(!ErrorHandler && "Error handler already registered!\n");
ErrorHandler = handler;
ErrorHandlerUserData = user_data;
}
void llvm::remove_fatal_error_handler() {
llvm::MutexGuard Lock(*ErrorHandlerMutex);
ErrorHandler = nullptr;
ErrorHandlerUserData = nullptr;
}
void llvm::report_fatal_error(const char *Reason, bool GenCrashDiag) {
report_fatal_error(Twine(Reason), GenCrashDiag);
}
void llvm::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
report_fatal_error(Twine(Reason), GenCrashDiag);
}
void llvm::report_fatal_error(StringRef Reason, bool GenCrashDiag) {
report_fatal_error(Twine(Reason), GenCrashDiag);
}
void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
llvm::fatal_error_handler_t handler = nullptr;
void* handlerData = nullptr;
{
// Only acquire the mutex while reading the handler, so as not to invoke a
// user-supplied callback under a lock.
llvm::MutexGuard Lock(*ErrorHandlerMutex);
handler = ErrorHandler;
handlerData = ErrorHandlerUserData;
}
if (handler) {
handler(handlerData, Reason.str(), GenCrashDiag);
} else {
// Blast the result out to stderr. We don't try hard to make sure this
// succeeds (e.g. handling EINTR) and we can't use errs() here because
// raw ostreams can call report_fatal_error.
SmallVector<char, 64> Buffer;
raw_svector_ostream OS(Buffer);
OS << "LLVM ERROR: " << Reason << "\n";
StringRef MessageStr = OS.str();
ssize_t written = ::write(2, MessageStr.data(), MessageStr.size());
(void)written; // If something went wrong, we deliberately just give up.
}
// If we reached here, we are failing ungracefully. Run the interrupt handlers
// to make sure any special cleanups get done, in particular that we remove
// files registered with RemoveFileOnSignal.
sys::RunInterruptHandlers();
exit(1);
}
void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler,
void *user_data) {
MutexGuard Lock(*BadAllocErrorHandlerMutex);
assert(!ErrorHandler && "Bad alloc error handler already registered!\n");
BadAllocErrorHandler = handler;
BadAllocErrorHandlerUserData = user_data;
}
void llvm::remove_bad_alloc_error_handler() {
MutexGuard Lock(*BadAllocErrorHandlerMutex);
BadAllocErrorHandler = nullptr;
BadAllocErrorHandlerUserData = nullptr;
}
void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
fatal_error_handler_t Handler = nullptr;
void *HandlerData = nullptr;
{
// Only acquire the mutex while reading the handler, so as not to invoke a
// user-supplied callback under a lock.
MutexGuard Lock(*BadAllocErrorHandlerMutex);
Handler = BadAllocErrorHandler;
HandlerData = BadAllocErrorHandlerUserData;
}
if (Handler) {
Handler(HandlerData, Reason, GenCrashDiag);
llvm_unreachable("bad alloc handler should not return");
}
#ifdef LLVM_ENABLE_EXCEPTIONS
// If exceptions are enabled, make OOM in malloc look like OOM in new.
throw std::bad_alloc();
#else
// Otherwise, fall back to the normal fatal error handler.
report_fatal_error("out of memory: " + Twine(Reason));
#endif
}
void llvm::llvm_unreachable_internal(const char *msg, const char *file,
unsigned line) {
// This code intentionally doesn't call the ErrorHandler callback, because
// llvm_unreachable is intended to be used to indicate "impossible"
// situations, and not legitimate runtime errors.
if (msg)
dbgs() << msg << "\n";
dbgs() << "UNREACHABLE executed";
if (file)
dbgs() << " at " << file << ":" << line;
dbgs() << "!\n";
abort();
#ifdef LLVM_BUILTIN_UNREACHABLE
// Windows systems and possibly others don't declare abort() to be noreturn,
// so use the unreachable builtin to avoid a Clang self-host warning.
LLVM_BUILTIN_UNREACHABLE;
#endif
}
static void bindingsErrorHandler(void *user_data, const std::string& reason,
bool gen_crash_diag) {
LLVMFatalErrorHandler handler =
LLVM_EXTENSION reinterpret_cast<LLVMFatalErrorHandler>(user_data);
handler(reason.c_str());
}
void LLVMInstallFatalErrorHandler(LLVMFatalErrorHandler Handler) {
install_fatal_error_handler(bindingsErrorHandler,
LLVM_EXTENSION reinterpret_cast<void *>(Handler));
}
void LLVMResetFatalErrorHandler() {
remove_fatal_error_handler();
}
#ifdef LLVM_ON_WIN32
#include <winerror.h>
// I'd rather not double the line count of the following.
#define MAP_ERR_TO_COND(x, y) \
case x: \
return make_error_code(errc::y)
std::error_code llvm::mapWindowsError(unsigned EV) {
switch (EV) {
MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied);
MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists);
MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device);
MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long);
MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied);
MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error);
MAP_ERR_TO_COND(ERROR_CANTREAD, io_error);
MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error);
MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied);
MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device);
MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty);
MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument);
MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device);
MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists);
MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device);
MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied);
MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device);
MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported);
MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument);
MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument);
MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available);
MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available);
MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument);
MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied);
MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory);
MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again);
MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error);
MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory);
MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error);
MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again);
MAP_ERR_TO_COND(ERROR_SEEK, io_error);
MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied);
MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open);
MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error);
MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied);
MAP_ERR_TO_COND(WSAEACCES, permission_denied);
MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor);
MAP_ERR_TO_COND(WSAEFAULT, bad_address);
MAP_ERR_TO_COND(WSAEINTR, interrupted);
MAP_ERR_TO_COND(WSAEINVAL, invalid_argument);
MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open);
MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long);
default:
return std::error_code(EV, std::system_category());
}
}
#endif