diff --git a/CMakeLists.txt b/CMakeLists.txt index ad7a161..80263d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ set( sources src/json_rpc_tcp_connection.cpp src/json_rpc_tcp_server.cpp src/json_rpc_error_object.cpp + src/error_report.cpp src/value.cpp src/lexical_cast.cpp src/spin_lock.cpp diff --git a/include/fc/error_report.hpp b/include/fc/error_report.hpp new file mode 100644 index 0000000..9d59e4e --- /dev/null +++ b/include/fc/error_report.hpp @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include +#include + +namespace fc { + + /** + * Represents one stack frame within an error_report. + */ + class error_frame { + public: + error_frame( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value m ); + error_frame(){} + error_frame(const error_frame& ); + error_frame(error_frame&& ); + + error_frame& operator=(const error_frame& ); + error_frame& operator=(error_frame&& ); + + fc::string desc; + fc::string file; + int64_t line; + fc::string method; + uint64_t time; + fc::optional meta; + }; + typedef fc::vector error_context; + + /** + * This class is used for rich error reporting that captures relevant errors the + * whole way up the stack. By using FC_THROW_REPORT(...) and FC_REPORT_PUSH( e, ...) + * you can capture the file, line, and method where the error was caught / rethrown. + */ + class error_report { + public: + error_report(); + error_report( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta = fc::value() ); + + error_frame& current(); + error_report& pop_frame(); + error_report& push_frame( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta = fc::value() ); + error_report& append( const error_report& e ); + + error_context stack; ///< Human readable stack of what we were atempting to do. + }; + +} // namespace fc + +#include +FC_REFLECT( fc::error_frame, (desc)(file)(line)(method)(time)(meta) ) +FC_REFLECT( fc::error_report, (stack) ) + +#include +#define FC_REPORT( X, ... ) fc::error_report X( __FILE__, __LINE__, __func__, __VA_ARGS__ ) +#define FC_THROW_REPORT( ... ) FC_THROW( fc::error_report( __FILE__, __LINE__, __func__, __VA_ARGS__ )) +#define FC_REPORT_CURRENT(ER, ... ) (ER).pop_frame().push_frame( __FILE__, __LINE__, __func__, __VA_ARGS__ ) +#define FC_REPORT_PUSH( ER, ... ) (ER).push_frame( __FILE__, __LINE__, __func__, __VA_ARGS__ ); +#define FC_REPORT_POP(ER) (ER).pop_frame() diff --git a/src/error_report.cpp b/src/error_report.cpp new file mode 100644 index 0000000..577543d --- /dev/null +++ b/src/error_report.cpp @@ -0,0 +1,73 @@ +#include + +namespace fc { + +error_frame::error_frame( const fc::string& f, uint64_t l, const fc::string& m, const fc::string& d, fc::value met ) +:desc(d),file(f),line(l),method(m),meta(fc::move(met)){} + +error_report::error_report() +{ +} +error_frame::error_frame(const fc::error_frame& e) +:desc(e.desc),file(e.file),line(e.line),method(e.method),time(e.time),meta(e.meta){} + +error_frame::error_frame(fc::error_frame&& e) +:desc(fc::move(e.desc)), + file(fc::move(e.file)), + line(e.line), + method(fc::move(e.method)), + time(e.time), + meta(fc::move(e.meta)) + {} + +fc::error_frame& fc::error_frame::operator=(const fc::error_frame& f ) { + auto tmp = f; + fc_swap( tmp, *this ); + return *this; +} +fc::error_frame& fc::error_frame::operator=(fc::error_frame&& e ) +{ + desc=fc::move(e.desc); + file=fc::move(e.file); + line=fc::move(e.line); + method=fc::move(e.method); + time=e.time; + meta=fc::move(e.meta); + return *this; +} + +error_report::error_report( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta ) +{ + push_frame( file, line, method, desc, meta ); +} + + +fc::error_frame& error_report::current() +{ + if( !stack.size() ) stack.resize(1); + return stack.back(); +} + +fc::error_report& error_report::pop_frame() +{ + stack.pop_back(); + return *this; +} + +fc::error_report& error_report::push_frame( const fc::string& file, uint64_t line, const fc::string& method, const fc::string& desc, fc::value meta ) +{ + stack.push_back( fc::error_frame( file, line, method, desc, meta ) ); + return *this; +} + +fc::error_report& error_report::append( const error_report& e ) +{ + // TODO: what to do about the 'failure...?' + stack.reserve( stack.size()+e.stack.size()); + for( uint32_t i = 0; i < e.stack.size(); ++i ) { + stack.push_back( e.stack[i] ); + } + return *this; +} + +} // namespace fc diff --git a/src/json_rpc_connection.cpp b/src/json_rpc_connection.cpp index 32ca5ab..6f00d0b 100644 --- a/src/json_rpc_connection.cpp +++ b/src/json_rpc_connection.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,12 @@ namespace fc { namespace json { auto id = value_cast(id_itr->val); try { send_result( id, smeth->second->call(params) ); + } catch ( fc::error_report& eo ) { + if( eo.stack.size() ) { + send_error( id, error_object( eo.current().desc, fc::value(eo) ) ); + } else { + send_error( id, error_object( "error report", fc::value(eo) ) ); + } } catch ( const fc::json::error_object& eo ) { send_error( id, eo ); } catch ( ... ) {