diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h --- a/llvm/include/llvm/Support/Error.h +++ b/llvm/include/llvm/Support/Error.h @@ -14,6 +14,7 @@ #define LLVM_SUPPORT_ERROR_H #include "llvm-c/Error.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -25,6 +26,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Format.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -41,13 +43,28 @@ namespace llvm { +class ErrorInfoBase; + +namespace detail { +using ErrorTraceMap = DenseMap; +extern std::unique_ptr ErrorTraces; + +void clearErrorTrace(ErrorInfoBase &); + +void annotateErrorWithTrace(ErrorInfoBase &EI); + +} // namespace detail + class ErrorSuccess; /// Base class for error info classes. Do not extend this directly: Extend /// the ErrorInfo template subclass instead. class ErrorInfoBase { public: - virtual ~ErrorInfoBase() = default; + virtual ~ErrorInfoBase() { + if (detail::ErrorTraces) + detail::clearErrorTrace(*this); + } /// Print an error message to an output stream. virtual void log(raw_ostream &OS) const = 0; @@ -325,10 +342,18 @@ inline ErrorSuccess Error::success() { return ErrorSuccess(); } +/// Enable error tracing for debugging. +/// This function should be called during program initialization. +void enableErrorTracing(); + /// Make a Error instance representing failure using the given error info /// type. template Error make_error(ArgTs &&... Args) { - return Error(std::make_unique(std::forward(Args)...)); + + auto EI = std::make_unique(std::forward(Args)...); + if (detail::ErrorTraces) + detail::annotateErrorWithTrace(*EI); + return Error(std::move(EI)); } /// Base class for user error types. Users should declare their error types @@ -661,6 +686,8 @@ dbgs() << "Expected value was in success state. (Note: Expected " "values in success mode must still be checked prior to being " "destroyed).\n"; + dbgs() << "Expected value discarded from:\n"; + sys::PrintStackTrace(dbgs()); abort(); } #endif @@ -682,10 +709,14 @@ #endif }; -/// Report a serious error, calling any installed error handler. See -/// ErrorHandling.h. -LLVM_ATTRIBUTE_NORETURN void report_fatal_error(Error Err, - bool gen_crash_diag = true); +namespace detail { + +/// Utility function for cantFail. +LLVM_ATTRIBUTE_NORETURN +LLVM_ATTRIBUTE_NOINLINE +void fatalCantFailUnhandledError(Error, const char *); + +} // end namespace detail /// Report a fatal error if Err is a failure value. /// @@ -701,17 +732,8 @@ /// cantFail(foo(false)); /// @endcode inline void cantFail(Error Err, const char *Msg = nullptr) { - if (Err) { - if (!Msg) - Msg = "Failure value returned from cantFail wrapped call"; -#ifndef NDEBUG - std::string Str; - raw_string_ostream OS(Str); - OS << Msg << "\n" << Err; - Msg = OS.str().c_str(); -#endif - llvm_unreachable(Msg); - } + if (LLVM_UNLIKELY(Err)) + detail::fatalCantFailUnhandledError(std::move(Err), Msg); } /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and @@ -729,20 +751,11 @@ /// @endcode template T cantFail(Expected ValOrErr, const char *Msg = nullptr) { - if (ValOrErr) + + if (LLVM_LIKELY(ValOrErr)) return std::move(*ValOrErr); - else { - if (!Msg) - Msg = "Failure value returned from cantFail wrapped call"; -#ifndef NDEBUG - std::string Str; - raw_string_ostream OS(Str); - auto E = ValOrErr.takeError(); - OS << Msg << "\n" << E; - Msg = OS.str().c_str(); -#endif - llvm_unreachable(Msg); - } + else + detail::fatalCantFailUnhandledError(ValOrErr.takeError(), Msg); } /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and @@ -760,20 +773,10 @@ /// @endcode template T& cantFail(Expected ValOrErr, const char *Msg = nullptr) { - if (ValOrErr) + if (LLVM_LIKELY(ValOrErr)) return *ValOrErr; - else { - if (!Msg) - Msg = "Failure value returned from cantFail wrapped call"; -#ifndef NDEBUG - std::string Str; - raw_string_ostream OS(Str); - auto E = ValOrErr.takeError(); - OS << Msg << "\n" << E; - Msg = OS.str().c_str(); -#endif - llvm_unreachable(Msg); - } + else + detail::fatalCantFailUnhandledError(ValOrErr.takeError(), Msg); } /// Helper for testing applicability of, and applying, handlers for @@ -1341,6 +1344,9 @@ std::function GetExitCode; }; +/// Enable LLVM error tracing. +void enableErrorTracing(); + /// Conversion from Error to LLVMErrorRef for C error bindings. inline LLVMErrorRef wrap(Error Err) { return reinterpret_cast(Err.takePayload().release()); diff --git a/llvm/include/llvm/Support/InitLLVM.h b/llvm/include/llvm/Support/InitLLVM.h --- a/llvm/include/llvm/Support/InitLLVM.h +++ b/llvm/include/llvm/Support/InitLLVM.h @@ -28,6 +28,9 @@ // encoding, so that you can assume that command line arguments are // always encoded in UTF-8 on any platform. // +// 4. Checks the LLVM_ENABLE_ERROR_TRACING environment variable and enables +// backtraces for llvm::Error asserts. +// // InitLLVM calls llvm_shutdown() on destruction, which cleans up // ManagedStatic objects. namespace llvm { diff --git a/llvm/lib/Object/Object.cpp b/llvm/lib/Object/Object.cpp --- a/llvm/lib/Object/Object.cpp +++ b/llvm/lib/Object/Object.cpp @@ -16,6 +16,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/MachOUniversal.h" +#include "llvm/Support/ErrorHandling.h" using namespace llvm; using namespace object; diff --git a/llvm/lib/Support/Error.cpp b/llvm/lib/Support/Error.cpp --- a/llvm/lib/Support/Error.cpp +++ b/llvm/lib/Support/Error.cpp @@ -10,6 +10,8 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" + +#include #include using namespace llvm; @@ -50,6 +52,58 @@ namespace llvm { +namespace detail { +std::mutex ErrorTraceMutex; +std::unique_ptr ErrorTraces; + +void annotateErrorWithTrace(ErrorInfoBase &EIB) { + std::string Trace; + { + raw_string_ostream TraceStream(Trace); + sys::PrintStackTrace(TraceStream); + } + std::lock_guard Lock(ErrorTraceMutex); + (*ErrorTraces)[&EIB] = std::move(Trace); +} + +void printErrorTrace(raw_ostream &OS, ErrorInfoBase &EI) { + assert(ErrorTraces && + "printErrorTrace should not be called unless tracing is enabled"); + std::lock_guard Lock(ErrorTraceMutex); + if (ErrorTraces->count(&EI)) + OS << (*ErrorTraces)[&EI]; + else + OS << "No trace recorded for ErrorInfo " << &EI << "\n"; +} + +void clearErrorTrace(ErrorInfoBase &EI) { + assert(ErrorTraces && + "printErrorTrace should not be called unless tracing is enabled"); + std::lock_guard Lock(ErrorTraceMutex); + ErrorTraces->erase(&EI); +} + +void fatalCantFailUnhandledError(Error Err, const char *Msg) { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + std::string Str; + { + raw_string_ostream OS(Str); + OS << Msg << "\n" << Err << "\n"; + if (ErrorTraces) { + OS << "Error thrown from:\n"; + handleAllErrors(std::move(Err), + [&](ErrorInfoBase &EIB) { printErrorTrace(OS, EIB); }); + } + OS << "cantFail called from:\n"; + sys::PrintStackTrace(OS); + Msg = OS.str().c_str(); + } + llvm_unreachable(Msg); +} + +} // namespace detail + void ErrorInfoBase::anchor() {} char ErrorInfoBase::ID = 0; char ErrorList::ID = 0; @@ -58,6 +112,10 @@ char StringError::ID = 0; char FileError::ID = 0; +void enableErrorTracing() { + detail::ErrorTraces = std::make_unique(); +} + void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) { if (!E) return; @@ -68,7 +126,6 @@ }); } - std::error_code ErrorList::convertToErrorCode() const { return std::error_code(static_cast(ErrorErrorCode::MultipleErrors), *ErrorErrorCat); @@ -102,11 +159,17 @@ #if LLVM_ENABLE_ABI_BREAKING_CHECKS void Error::fatalUncheckedError() const { - dbgs() << "Program aborted due to an unhandled Error:\n"; + dbgs() << "Program aborted due to an unhandled Error. Error message:\n\""; if (getPtr()) { getPtr()->log(dbgs()); - dbgs() << "\n"; - }else + dbgs() << "\"\n"; + if (detail::ErrorTraces) { + dbgs() << "Error thrown from:\n"; + detail::printErrorTrace(dbgs(), *getPtr()); + } + dbgs() << "Error discarded from:\n"; + sys::PrintStackTrace(dbgs()); + } else dbgs() << "Error value was Success. (Note: Success values must still be " "checked prior to being destroyed).\n"; abort(); diff --git a/llvm/lib/Support/InitLLVM.cpp b/llvm/lib/Support/InitLLVM.cpp --- a/llvm/lib/Support/InitLLVM.cpp +++ b/llvm/lib/Support/InitLLVM.cpp @@ -29,6 +29,9 @@ sys::PrintStackTraceOnErrorSignal(Argv[0]); install_out_of_memory_new_handler(); + if (getenv("LLVM_ENABLE_ERROR_TRACING")) + enableErrorTracing(); + #ifdef _WIN32 // We use UTF-8 as the internal character encoding. On Windows, // arguments passed to main() may not be encoded in UTF-8. In order