advanced error reporting / stack capture

This commit is contained in:
Daniel Larimer 2012-12-12 12:31:21 -05:00
parent 74709a4983
commit b35f6880c6
4 changed files with 141 additions and 0 deletions

View file

@ -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

View file

@ -0,0 +1,60 @@
#pragma once
#include <fc/vector.hpp>
#include <fc/string.hpp>
#include <fc/value.hpp>
#include <fc/optional.hpp>
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<fc::value> meta;
};
typedef fc::vector<error_frame> 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.hpp>
FC_REFLECT( fc::error_frame, (desc)(file)(line)(method)(time)(meta) )
FC_REFLECT( fc::error_report, (stack) )
#include <fc/exception.hpp>
#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()

73
src/error_report.cpp Normal file
View file

@ -0,0 +1,73 @@
#include <fc/error_report.hpp>
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

View file

@ -2,6 +2,7 @@
#include <fc/log.hpp>
#include <fc/thread.hpp>
#include <fc/error.hpp>
#include <fc/error_report.hpp>
#include <fc/json_rpc_error_object.hpp>
#include <unordered_map>
#include <string>
@ -72,6 +73,12 @@ namespace fc { namespace json {
auto id = value_cast<uint64_t>(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 ( ... ) {