Merge pull request #8501 from ClickHouse/stack-trace-in-std-exception

Calculate stack trace for std::exception (experimental)
This commit is contained in:
alexey-milovidov 2020-01-03 00:22:12 +03:00 committed by GitHub
commit 03be29eddb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 130 additions and 68 deletions

View File

@ -210,7 +210,7 @@ set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -g3 -ggdb3
if (COMPILER_CLANG)
# Exception unwinding doesn't work in clang release build without this option
# TODO investigate if contrib/libcxxabi is out of date
# TODO investigate that
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
endif ()

2
contrib/libcxx vendored

@ -1 +1 @@
Subproject commit f7c63235238a71b7e0563fab8c7c5ec1b54831f6
Subproject commit a8c453300879d0bf255f9d5959d42e2c8aac1bfb

View File

@ -47,6 +47,11 @@ add_library(cxx ${SRCS})
target_include_directories(cxx SYSTEM BEFORE PUBLIC $<BUILD_INTERFACE:${LIBCXX_SOURCE_DIR}/include>)
target_compile_definitions(cxx PRIVATE -D_LIBCPP_BUILDING_LIBRARY -DLIBCXX_BUILDING_LIBCXXABI)
# Enable capturing stack traces for all exceptions.
if (USE_UNWIND)
target_compile_definitions(cxx PUBLIC -DSTD_EXCEPTION_HAS_STACK_TRACE=1)
endif ()
target_compile_options(cxx PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-nostdinc++>)
check_cxx_compiler_flag(-Wreserved-id-macro HAVE_WARNING_RESERVED_ID_MACRO)

View File

@ -32,6 +32,11 @@ target_compile_definitions(cxxabi PRIVATE -D_LIBCPP_BUILDING_LIBRARY)
target_compile_options(cxxabi PRIVATE -nostdinc++ -fno-sanitize=undefined -Wno-macro-redefined) # If we don't disable UBSan, infinite recursion happens in dynamic_cast.
target_link_libraries(cxxabi PUBLIC ${EXCEPTION_HANDLING_LIBRARY})
# Enable capturing stack traces for all exceptions.
if (USE_UNWIND)
target_compile_definitions(cxxabi PUBLIC -DSTD_EXCEPTION_HAS_STACK_TRACE=1)
endif ()
install(
TARGETS cxxabi
EXPORT global

View File

@ -300,7 +300,7 @@ private:
&& std::string::npos == embedded_stack_trace_pos)
{
std::cerr << "Stack trace:" << std::endl
<< e.getStackTrace().toString();
<< e.getStackTraceString();
}
/// If exception code isn't zero, we should return non-zero return code anyway.
@ -791,7 +791,7 @@ private:
if (config().getBool("stacktrace", false))
std::cerr << "Stack trace:" << std::endl
<< e.getStackTrace().toString() << std::endl;
<< e.getStackTraceString() << std::endl;
std::cerr << std::endl;

View File

@ -115,7 +115,7 @@ void ODBCHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Ne
catch (const Exception & ex)
{
process_error("Invalid 'columns' parameter in request body '" + ex.message() + "'");
LOG_WARNING(log, ex.getStackTrace().toString());
LOG_WARNING(log, ex.getStackTraceString());
return;
}

View File

@ -337,7 +337,7 @@ void PerformanceTest::runQueries(
{
statistics.exception = "Code: " + std::to_string(e.code()) + ", e.displayText() = " + e.displayText();
LOG_WARNING(log, "Code: " << e.code() << ", e.displayText() = " << e.displayText()
<< ", Stack trace:\n\n" << e.getStackTrace().toString());
<< ", Stack trace:\n\n" << e.getStackTraceString());
}
if (!statistics.got_SIGINT)

View File

@ -112,7 +112,7 @@ void TCPHandler::runImpl()
{
Exception e("Database " + backQuote(default_database) + " doesn't exist", ErrorCodes::UNKNOWN_DATABASE);
LOG_ERROR(log, "Code: " << e.code() << ", e.displayText() = " << e.displayText()
<< ", Stack trace:\n\n" << e.getStackTrace().toString());
<< ", Stack trace:\n\n" << e.getStackTraceString());
sendException(e, connection_context.getSettingsRef().calculate_text_stack_trace);
return;
}
@ -158,7 +158,7 @@ void TCPHandler::runImpl()
/** An exception during the execution of request (it must be sent over the network to the client).
* The client will be able to accept it, if it did not happen while sending another packet and the client has not disconnected yet.
*/
std::unique_ptr<Exception> exception;
std::optional<DB::Exception> exception;
bool network_error = false;
bool send_exception_with_stack_trace = connection_context.getSettingsRef().calculate_text_stack_trace;
@ -280,7 +280,7 @@ void TCPHandler::runImpl()
catch (const Exception & e)
{
state.io.onException();
exception.reset(e.clone());
exception.emplace(e);
if (e.code() == ErrorCodes::UNKNOWN_PACKET_FROM_CLIENT)
throw;
@ -298,22 +298,22 @@ void TCPHandler::runImpl()
* We will try to send exception to the client in any case - see below.
*/
state.io.onException();
exception = std::make_unique<Exception>(e.displayText(), ErrorCodes::POCO_EXCEPTION);
exception.emplace(Exception::CreateFromPoco, e);
}
catch (const Poco::Exception & e)
{
state.io.onException();
exception = std::make_unique<Exception>(e.displayText(), ErrorCodes::POCO_EXCEPTION);
exception.emplace(Exception::CreateFromPoco, e);
}
catch (const std::exception & e)
{
state.io.onException();
exception = std::make_unique<Exception>(e.what(), ErrorCodes::STD_EXCEPTION);
exception.emplace(Exception::CreateFromSTD, e);
}
catch (...)
{
state.io.onException();
exception = std::make_unique<Exception>("Unknown exception", ErrorCodes::UNKNOWN_EXCEPTION);
exception.emplace("Unknown exception", ErrorCodes::UNKNOWN_EXCEPTION);
}
try

View File

@ -138,7 +138,6 @@ namespace ErrorCodes
extern const int FUNCTION_IS_SPECIAL = 129;
extern const int CANNOT_READ_ARRAY_FROM_TEXT = 130;
extern const int TOO_LARGE_STRING_SIZE = 131;
extern const int CANNOT_CREATE_TABLE_FROM_METADATA = 132;
extern const int AGGREGATE_FUNCTION_DOESNT_ALLOW_PARAMETERS = 133;
extern const int PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS = 134;
extern const int ZERO_ARRAY_OR_TUPLE_INDEX = 135;
@ -474,7 +473,6 @@ namespace ErrorCodes
extern const int NOT_ENOUGH_PRIVILEGES = 497;
extern const int LIMIT_BY_WITH_TIES_IS_NOT_SUPPORTED = 498;
extern const int S3_ERROR = 499;
extern const int CANNOT_CREATE_DICTIONARY_FROM_METADATA = 500;
extern const int CANNOT_CREATE_DATABASE = 501;
extern const int CANNOT_SIGQUEUE = 502;
extern const int AGGREGATE_FUNCTION_THROW = 503;

View File

@ -25,6 +25,55 @@ namespace ErrorCodes
extern const int NOT_IMPLEMENTED;
}
Exception::Exception()
{
}
Exception::Exception(const std::string & msg, int code)
: Poco::Exception(msg, code)
{
}
Exception::Exception(CreateFromPocoTag, const Poco::Exception & exc)
: Poco::Exception(exc.displayText(), ErrorCodes::POCO_EXCEPTION)
{
#ifdef STD_EXCEPTION_HAS_STACK_TRACE
set_stack_trace(exc.get_stack_trace_frames(), exc.get_stack_trace_size());
#endif
}
Exception::Exception(CreateFromSTDTag, const std::exception & exc)
: Poco::Exception(String(typeid(exc).name()) + ": " + String(exc.what()), ErrorCodes::STD_EXCEPTION)
{
#ifdef STD_EXCEPTION_HAS_STACK_TRACE
set_stack_trace(exc.get_stack_trace_frames(), exc.get_stack_trace_size());
#endif
}
std::string getExceptionStackTraceString(const std::exception & e)
{
#ifdef STD_EXCEPTION_HAS_STACK_TRACE
return StackTrace::toString(e.get_stack_trace_frames(), 0, e.get_stack_trace_size());
#else
if (const auto * db_exception = dynamic_cast<const Exception *>(&e))
return db_exception->getStackTraceString();
return {};
#endif
}
std::string Exception::getStackTraceString() const
{
#ifdef STD_EXCEPTION_HAS_STACK_TRACE
return StackTrace::toString(get_stack_trace_frames(), 0, get_stack_trace_size());
#else
return trace.toString();
#endif
}
std::string errnoToString(int code, int e)
{
const size_t buf_size = 128;
@ -141,6 +190,7 @@ std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded
{
stream << "Poco::Exception. Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code()
<< ", e.displayText() = " << e.displayText()
<< (with_stacktrace ? getExceptionStackTraceString(e) : "")
<< (with_extra_info ? getExtraExceptionInfo(e) : "")
<< " (version " << VERSION_STRING << VERSION_OFFICIAL;
}
@ -157,8 +207,9 @@ std::string getCurrentExceptionMessage(bool with_stacktrace, bool check_embedded
name += " (demangling status: " + toString(status) + ")";
stream << "std::exception. Code: " << ErrorCodes::STD_EXCEPTION << ", type: " << name << ", e.what() = " << e.what()
<< (with_extra_info ? getExtraExceptionInfo(e) : "")
<< ", version = " << VERSION_STRING << VERSION_OFFICIAL;
<< (with_stacktrace ? getExceptionStackTraceString(e) : "")
<< (with_extra_info ? getExtraExceptionInfo(e) : "")
<< ", version = " << VERSION_STRING << VERSION_OFFICIAL;
}
catch (...) {}
}
@ -261,7 +312,7 @@ std::string getExceptionMessage(const Exception & e, bool with_stacktrace, bool
stream << "Code: " << e.code() << ", e.displayText() = " << text;
if (with_stacktrace && !has_embedded_stack_trace)
stream << ", Stack trace (when copying this message, always include the lines below):\n\n" << e.getStackTrace().toString();
stream << ", Stack trace (when copying this message, always include the lines below):\n\n" << e.getStackTraceString();
}
catch (...) {}

View File

@ -22,13 +22,14 @@ namespace ErrorCodes
class Exception : public Poco::Exception
{
public:
Exception() {} /// For deferred initialization.
Exception(const std::string & msg, int code) : Poco::Exception(msg, code) {}
Exception(const std::string & msg, const Exception & nested_exception, int code)
: Poco::Exception(msg, nested_exception, code), trace(nested_exception.trace) {}
Exception();
Exception(const std::string & msg, int code);
enum CreateFromPocoTag { CreateFromPoco };
Exception(CreateFromPocoTag, const Poco::Exception & exc) : Poco::Exception(exc.displayText(), ErrorCodes::POCO_EXCEPTION) {}
enum CreateFromSTDTag { CreateFromSTD };
Exception(CreateFromPocoTag, const Poco::Exception & exc);
Exception(CreateFromSTDTag, const std::exception & exc);
Exception * clone() const override { return new Exception(*this); }
void rethrow() const override { throw *this; }
@ -38,15 +39,20 @@ public:
/// Add something to the existing message.
void addMessage(const std::string & arg) { extendedMessage(arg); }
const StackTrace & getStackTrace() const { return trace; }
std::string getStackTraceString() const;
private:
#ifndef STD_EXCEPTION_HAS_STACK_TRACE
StackTrace trace;
#endif
const char * className() const throw() override { return "DB::Exception"; }
};
std::string getExceptionStackTraceString(const std::exception & e);
/// Contains an additional member `saved_errno`. See the throwFromErrno function.
class ErrnoException : public Exception
{

View File

@ -328,3 +328,13 @@ std::string StackTrace::toString() const
static SimpleCache<decltype(toStringImpl), &toStringImpl> func_cached;
return func_cached(frames, offset, size);
}
std::string StackTrace::toString(void ** frames_, size_t offset, size_t size)
{
StackTrace::Frames frames_copy{};
for (size_t i = 0; i < size; ++i)
frames_copy[i] = frames_[i];
static SimpleCache<decltype(toStringImpl), &toStringImpl> func_cached;
return func_cached(frames_copy, offset, size);
}

View File

@ -41,6 +41,8 @@ public:
const Frames & getFrames() const;
std::string toString() const;
static std::string toString(void ** frames, size_t offset, size_t size);
void toStringEveryLine(std::function<void(const std::string &)> callback) const;
protected:

View File

@ -57,6 +57,6 @@ catch (const Exception & e)
std::cerr << e.what() << ", " << e.displayText() << std::endl
<< std::endl
<< "Stack trace:" << std::endl
<< e.getStackTrace().toString();
<< e.getStackTraceString();
return 1;
}

View File

@ -23,7 +23,6 @@ namespace ErrorCodes
extern const int TABLE_ALREADY_EXISTS;
extern const int UNKNOWN_TABLE;
extern const int UNSUPPORTED_METHOD;
extern const int CANNOT_CREATE_TABLE_FROM_METADATA;
extern const int LOGICAL_ERROR;
}
@ -255,10 +254,10 @@ StoragePtr DatabaseLazy::loadTable(const Context & context, const String & table
return it->second.table = table;
}
}
catch (const Exception & e)
catch (Exception & e)
{
throw Exception("Cannot create table from metadata file " + table_metadata_path + ". Error: " + DB::getCurrentExceptionMessage(true),
e, DB::ErrorCodes::CANNOT_CREATE_TABLE_FROM_METADATA);
e.addMessage("Cannot create table from metadata file " + table_metadata_path);
throw;
}
}

View File

@ -36,7 +36,6 @@ namespace DB
namespace ErrorCodes
{
extern const int CANNOT_CREATE_TABLE_FROM_METADATA;
extern const int CANNOT_CREATE_DICTIONARY_FROM_METADATA;
extern const int EMPTY_LIST_OF_COLUMNS_PASSED;
extern const int CANNOT_PARSE_TEXT;
@ -66,13 +65,10 @@ namespace
= createTableFromAST(query, database_name, database.getTableDataPath(query), context, has_force_restore_data_flag);
database.attachTable(table_name, table);
}
catch (const Exception & e)
catch (Exception & e)
{
throw Exception(
"Cannot attach table '" + query.table + "' from query " + serializeAST(query)
+ ". Error: " + DB::getCurrentExceptionMessage(true),
e,
DB::ErrorCodes::CANNOT_CREATE_TABLE_FROM_METADATA);
e.addMessage("Cannot attach table '" + backQuote(query.table) + "' from query " + serializeAST(query));
throw;
}
}
@ -87,13 +83,10 @@ namespace
{
database.attachDictionary(query.table, context);
}
catch (const Exception & e)
catch (Exception & e)
{
throw Exception(
"Cannot create dictionary '" + query.table + "' from query " + serializeAST(query)
+ ". Error: " + DB::getCurrentExceptionMessage(true),
e,
DB::ErrorCodes::CANNOT_CREATE_DICTIONARY_FROM_METADATA);
e.addMessage("Cannot attach table '" + backQuote(query.table) + "' from query " + serializeAST(query));
throw;
}
}
@ -142,10 +135,10 @@ void DatabaseOrdinary::loadStoredObjects(
total_dictionaries += create_query->is_dictionary;
}
}
catch (const Exception & e)
catch (Exception & e)
{
throw Exception(
"Cannot parse definition from metadata file " + full_path + ". Error: " + DB::getCurrentExceptionMessage(true), e, ErrorCodes::CANNOT_PARSE_TEXT);
e.addMessage("Cannot parse definition from metadata file " + full_path);
throw;
}
});

View File

@ -124,6 +124,10 @@ public:
t1.join();
t2.join();
}
else if (mode == "throw exception")
{
std::vector<int>().at(0);
}
else if (mode == "access context")
{
(void)context.getCurrentQueryId();

View File

@ -965,7 +965,7 @@ void readException(Exception & e, ReadBuffer & buf, const String & additional_me
String name;
String message;
String stack_trace;
bool has_nested = false;
bool has_nested = false; /// Obsolete
readBinary(code, buf);
readBinary(name, buf);
@ -986,14 +986,7 @@ void readException(Exception & e, ReadBuffer & buf, const String & additional_me
if (!stack_trace.empty())
out << " Stack trace:\n\n" << stack_trace;
if (has_nested)
{
Exception nested;
readException(nested, buf);
e = Exception(out.str(), nested, code);
}
else
e = Exception(out.str(), code);
e = Exception(out.str(), code);
}
void readAndThrowException(ReadBuffer & buf, const String & additional_message)

View File

@ -48,7 +48,6 @@ void formatUUID(std::reverse_iterator<const UInt8 *> src16, UInt8 * dst36)
}
void writeException(const Exception & e, WriteBuffer & buf, bool with_stack_trace)
{
writeBinary(e.code(), buf);
@ -56,14 +55,11 @@ void writeException(const Exception & e, WriteBuffer & buf, bool with_stack_trac
writeBinary(e.displayText(), buf);
if (with_stack_trace)
writeBinary(e.getStackTrace().toString(), buf);
writeBinary(e.getStackTraceString(), buf);
else
writeBinary(String(), buf);
bool has_nested = e.nested() != nullptr;
bool has_nested = false;
writeBinary(has_nested, buf);
if (has_nested)
writeException(Exception(Exception::CreateFromPoco, *e.nested()), buf, with_stack_trace);
}
}

View File

@ -129,9 +129,9 @@ static void setExceptionStackTrace(QueryLogElement & elem)
{
throw;
}
catch (const Exception & e)
catch (const std::exception & e)
{
elem.stack_trace = e.getStackTrace().toString();
elem.stack_trace = getExceptionStackTraceString(e);
}
catch (...) {}
}

View File

@ -97,6 +97,6 @@ catch (const Exception & e)
std::cerr << e.what() << ", " << e.displayText() << std::endl
<< std::endl
<< "Stack trace:" << std::endl
<< e.getStackTrace().toString();
<< e.getStackTraceString();
return 1;
}

View File

@ -55,6 +55,6 @@ catch (const Exception & e)
std::cerr << e.what() << ", " << e.displayText() << std::endl
<< std::endl
<< "Stack trace:" << std::endl
<< e.getStackTrace().toString();
<< e.getStackTraceString();
return 1;
}

View File

@ -54,7 +54,7 @@ try
catch (const Exception & e)
{
std::cerr << e.what() << ", " << e.displayText() << ": " << std::endl
<< e.getStackTrace().toString() << std::endl;
<< e.getStackTraceString() << std::endl;
throw;
}
catch (Poco::Exception & e)

View File

@ -112,7 +112,7 @@ try
catch (const Exception & e)
{
std::cerr << e.what() << ", " << e.displayText() << ": " << std::endl
<< e.getStackTrace().toString() << std::endl;
<< e.getStackTraceString() << std::endl;
throw;
}
catch (Poco::Exception & e)

View File

@ -133,7 +133,7 @@ int main(int argc, char ** argv)
std::cerr << e.what() << ", " << e.message() << std::endl
<< std::endl
<< "Stack trace:" << std::endl
<< e.getStackTrace().toString()
<< e.getStackTraceString()
<< std::endl;
throw;
}

View File

@ -53,7 +53,7 @@ int main(int argc, char ** argv)
std::cerr << e.what() << ", " << e.message() << std::endl
<< std::endl
<< "Stack trace:" << std::endl
<< e.getStackTrace().toString()
<< e.getStackTraceString()
<< std::endl;
throw;
}