pytorch/c10/util/Exception.cpp
Edward Yang a058e938f9 Refactor error msg stack handling, add TORCH_RETHROW (#37101)
Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/37101

Fixes #36954.

The basic concept is to streamline the process of rethrowing
c10::Error with extra error information.  This is in a few
steps:

- I completely remodeled the Error data type and the internal
  invariants.  Instead of manually adding in newlines, the
  message stack formatting process is responsible for inserting
  newlines and spacing as necessary.  Call sites are then
  modified to respect the new API model.
- TORCH_RETHROW macro is added, which adds context to an error
  message and then rethrows it.

New internal assert failure looks like:

```
0 INTERNAL ASSERT FAILED at ../c10/test/util/exception_test.cpp:64, please report a bug to PyTorch.
Exception raised from TestBody at ../c10/test/util/exception_test.cpp:64 (most recent call first):
frame #0: <unknown function> + 0x6aab9 (0x7ff611d3aab9 in /data/users/ezyang/pytorch-tmp/build/lib/libc10.so)
frame #1: ...
```

Error message with context looks like:

```
This is an error
  This is context 1
  This is context 2
```

Signed-off-by: Edward Z. Yang <ezyang@fb.com>

Test Plan: Imported from OSS

Differential Revision: D21202891

Pulled By: ezyang

fbshipit-source-id: 361cadd16bc52e5886dba08e79277771ada76169
2020-05-04 11:56:45 -07:00

142 lines
3.6 KiB
C++

#include <c10/util/Exception.h>
#include <c10/util/Backtrace.h>
#include <c10/util/Type.h>
#include <c10/util/Logging.h>
#include <iostream>
#include <sstream>
#include <numeric>
#include <string>
namespace c10 {
Error::Error(std::string msg, std::string backtrace, const void* caller)
: msg_(std::move(msg)), backtrace_(std::move(backtrace)), caller_(caller) {
refresh_what();
}
// PyTorch-style error message
// Error::Error(SourceLocation source_location, const std::string& msg)
// NB: This is defined in Logging.cpp for access to GetFetchStackTrace
// Caffe2-style error message
Error::Error(
const char* file,
const uint32_t line,
const char* condition,
const std::string& msg,
const std::string& backtrace,
const void* caller)
: Error(
str("[enforce fail at ",
detail::StripBasename(file),
":",
line,
"] ",
condition,
". ",
msg),
backtrace,
caller) {}
std::string Error::compute_what(bool include_backtrace) const {
std::ostringstream oss;
oss << msg_;
if (context_.size() == 1) {
// Fold error and context in one line
oss << " (" << context_[0] << ")";
} else {
for (const auto& c : context_) {
oss << "\n " << c;
}
}
if (include_backtrace) {
oss << "\n" << backtrace_;
}
return oss.str();
}
void Error::refresh_what() {
what_ = compute_what(/*include_backtrace*/ true);
what_without_backtrace_ = compute_what(/*include_backtrace*/ false);
}
void Error::add_context(std::string new_msg) {
context_.push_back(std::move(new_msg));
// TODO: Calling add_context O(n) times has O(n^2) cost. We can fix
// this perf problem by populating the fields lazily... if this ever
// actually is a problem.
// NB: If you do fix this, make sure you do it in a thread safe way!
// what() is almost certainly expected to be thread safe even when
// accessed across multiple threads
refresh_what();
}
namespace Warning {
namespace {
WarningHandler* getBaseHandler() {
static WarningHandler base_warning_handler_ = WarningHandler();
return &base_warning_handler_;
};
class ThreadWarningHandler {
public:
ThreadWarningHandler() = delete;
static WarningHandler* get_handler() {
if (!warning_handler_) {
warning_handler_ = getBaseHandler();
}
return warning_handler_;
}
static void set_handler(WarningHandler* handler) {
warning_handler_ = handler;
}
private:
static thread_local WarningHandler* warning_handler_;
};
thread_local WarningHandler* ThreadWarningHandler::warning_handler_ = nullptr;
}
void warn(SourceLocation source_location, const std::string& msg, const bool verbatim) {
ThreadWarningHandler::get_handler()->process(source_location, msg, verbatim);
}
void set_warning_handler(WarningHandler* handler) noexcept(true) {
ThreadWarningHandler::set_handler(handler);
}
WarningHandler* get_warning_handler() noexcept(true) {
return ThreadWarningHandler::get_handler();
}
} // namespace Warning
void WarningHandler::process(
const SourceLocation& source_location,
const std::string& msg,
const bool /*verbatim*/) {
LOG_AT_FILE_LINE(WARNING, source_location.file, source_location.line)
<< "Warning: " << msg << " (function " << source_location.function << ")";
}
std::string GetExceptionString(const std::exception& e) {
#ifdef __GXX_RTTI
return demangle(typeid(e).name()) + ": " + e.what();
#else
return std::string("Exception (no RTTI available): ") + e.what();
#endif // __GXX_RTTI
}
} // namespace c10