From 931c72e9293d64a1797a6187dd0e744408c23d59 Mon Sep 17 00:00:00 2001 From: Gabriel Corona Date: Fri, 10 Jun 2016 13:54:52 +0200 Subject: [PATCH] [xbt] Move backtrace support for exceptions in a mixin class * mixin class to add backtraces to any exception; * code to display exceptions with name, what(), backtraces and nested exceptions; * demangle C++ names (class type names, function names). --- include/xbt/backtrace.hpp | 38 ++++++++++++++ include/xbt/ex.h | 9 ++-- include/xbt/exception.hpp | 84 +++++++++++++++++++++++++++++++ include/xbt/log.hpp | 22 ++++++++ include/xbt/string.hpp | 1 + include/xbt/virtu.h | 2 +- src/bindings/java/JavaContext.cpp | 12 ++--- src/simix/smx_private.h | 2 - src/xbt/backtrace.cpp | 46 ++++++++++++++++- src/xbt/backtrace_dummy.cpp | 4 +- src/xbt/backtrace_linux.cpp | 31 ++++++++---- src/xbt/ex.cpp | 66 +++--------------------- src/xbt/ex_interface.h | 4 -- src/xbt/exception.cpp | 72 ++++++++++++++++++++++++++ src/xbt/log.c | 1 + tools/cmake/DefinePackages.cmake | 4 ++ 16 files changed, 306 insertions(+), 92 deletions(-) create mode 100644 include/xbt/backtrace.hpp create mode 100644 include/xbt/exception.hpp create mode 100644 include/xbt/log.hpp create mode 100644 src/xbt/exception.cpp diff --git a/include/xbt/backtrace.hpp b/include/xbt/backtrace.hpp new file mode 100644 index 0000000000..ca3f282e52 --- /dev/null +++ b/include/xbt/backtrace.hpp @@ -0,0 +1,38 @@ +/* Copyright (c) 2005-2019. The SimGrid Team. + * All rights reserved. */ + +#ifndef SIMGRIX_XBT_BACKTRACE_HPP +#define SIMGRIX_XBT_BACKTRACE_HPP + +#include + +#include +#include +#include + +#include +#include + +namespace simgrid { +namespace xbt { + +/** Try to demangle a C++ name + * + * Return the origin string if this fails. + */ +XBT_PUBLIC() std::unique_ptr demangle(const char* name); + +/** Get the current backtrace */ +XBT_PUBLIC(std::vector) backtrace(); + +/* Translate the backtrace in a human friendly form + * + * Try ro resolve symbols and source code location. + */ +XBT_PUBLIC(std::vector) resolveBacktrace( + xbt_backtrace_location_t const* loc, std::size_t count); + +} +} + +#endif diff --git a/include/xbt/ex.h b/include/xbt/ex.h index 6a4e505cbd..d1e3fdf0f6 100644 --- a/include/xbt/ex.h +++ b/include/xbt/ex.h @@ -49,6 +49,7 @@ #include #include #include +#include #endif #include "xbt/base.h" @@ -99,7 +100,9 @@ typedef enum { } xbt_errcat_t; #ifdef __cplusplus -XBT_PUBLIC_CLASS xbt_ex : public std::runtime_error { +XBT_PUBLIC_CLASS xbt_ex : + public std::runtime_error, + public simgrid::xbt::WithContextException { public: xbt_ex() : std::runtime_error("") {} xbt_ex(const char* message) : std::runtime_error(message) {} @@ -107,13 +110,9 @@ public: xbt_errcat_t category; /**< category like HTTP (what went wrong) */ int value; /**< like errno (why did it went wrong) */ - /* throw point */ - std::string procname; /**< Name of the process who thrown this */ - int pid; /**< PID of the process who thrown this */ const char *file; /**< Thrown point */ int line; /**< Thrown point */ const char *func; /**< Thrown point */ - std::vector bt; /**< Backtrace */ }; #endif diff --git a/include/xbt/exception.hpp b/include/xbt/exception.hpp new file mode 100644 index 0000000000..c5d13584db --- /dev/null +++ b/include/xbt/exception.hpp @@ -0,0 +1,84 @@ +/* Copyright (c) 2005-2016. The SimGrid Team.All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + +#ifndef SIMGRID_XBT_EXCEPTION_HPP +#define SIMGRID_XBT_EXCEPTION_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include // xbt_procname +#include // xbt_getpid + +namespace simgrid { +namespace xbt { + +typedef std::vector Backtrace; + +/** A polymorphic mixin class for adding context to an exception */ +XBT_PUBLIC_CLASS WithContextException { +public: + WithContextException() : + backtrace_(simgrid::xbt::backtrace()), + procname_(xbt_procname()), + pid_(xbt_getpid()) + {} + WithContextException(Backtrace bt) : + backtrace_(std::move(bt)), + procname_(xbt_procname()), + pid_(xbt_getpid()) + {} + virtual ~WithContextException(); + Backtrace const& backtrace() const + { + return backtrace_; + } + int pid() const { return pid_; } + std::string const& processName() const { return procname_; } +private: + Backtrace backtrace_; + std::string procname_; /**< Name of the process who thrown this */ + int pid_; /**< PID of the process who thrown this */ +}; + +/** Internal class used to mixin the two classes */ +template +class WithContext : public E, public WithContextException +{ +public: + WithContext(E exception) + : E(std::move(exception)) {} + WithContext(E exception, Backtrace backtrace) + : E(std::move(exception)), WithContextException(std::move(backtrace)) {} + WithContext(E exception, WithContextException context) + : E(std::move(exception)), WithContextException(std::move(context)) {} + ~WithContext() override {} +}; + +/** Throw a given exception a context + * + * @param exception exception to throw + * @param backtrace backtrace to attach + */ +template +[[noreturn]] inline +typename std::enable_if< !std::is_base_of::value >::type +throwWithContext( + E exception, + // Thanks to the default argument, we are taking the backtrace in the caller: + Backtrace backtrace = simgrid::xbt::backtrace()) +{ + throw WithContext(std::move(exception), std::move(backtrace)); +} + +} +} + +#endif diff --git a/include/xbt/log.hpp b/include/xbt/log.hpp new file mode 100644 index 0000000000..781559d62a --- /dev/null +++ b/include/xbt/log.hpp @@ -0,0 +1,22 @@ +/* Copyright (c) 2016. The SimGrid Team. All rights reserved.*/ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + +#include +#include + +namespace simgrid { +namespace xbt { + +/** Display informations about an exception + * + * We display: the exception type, name, attached backtarces (if any) and + * the nested exception (if any). + */ +XBT_PUBLIC(void) logException( + e_xbt_log_priority_t priority, + const char* context, std::exception const& exception); + +} +} diff --git a/include/xbt/string.hpp b/include/xbt/string.hpp index 2fcd61ae51..9ffff030c1 100644 --- a/include/xbt/string.hpp +++ b/include/xbt/string.hpp @@ -11,6 +11,7 @@ #include #include +#include #if HAVE_MC diff --git a/include/xbt/virtu.h b/include/xbt/virtu.h index 2f140feef0..07b895d283 100644 --- a/include/xbt/virtu.h +++ b/include/xbt/virtu.h @@ -10,7 +10,7 @@ #define __XBT_VIRTU_H__ #include "xbt/misc.h" -#include "xbt/sysdep.h" +#include "xbt/base.h" #include "xbt/function_types.h" #include "xbt/dynar.h" diff --git a/src/bindings/java/JavaContext.cpp b/src/bindings/java/JavaContext.cpp index c6a3d481ac..9d0218dba1 100644 --- a/src/bindings/java/JavaContext.cpp +++ b/src/bindings/java/JavaContext.cpp @@ -84,16 +84,12 @@ JavaContext::JavaContext(std::function code, "See the Install section of simgrid-java documentation (in doc/install.html) for more on coroutines.", thread_amount, ex.what()); xbt_ex new_exception(str); - free(str); new_exception.category = ex.category; new_exception.value = ex.value; - new_exception.procname = ex.procname; - new_exception.file = ex.file; - new_exception.line = ex.line; - new_exception.func = ex.func; - new_exception.pid = ex.pid; - new_exception.bt = ex.bt; - throw new_exception; + new_exception.file = __FILE__; + new_exception.line = __LINE__; + new_exception.func = __func__; + std::throw_with_nested(std::move(new_exception)); } } else { this->thread = nullptr; diff --git a/src/simix/smx_private.h b/src/simix/smx_private.h index 333ea07d33..feb99909eb 100644 --- a/src/simix/smx_private.h +++ b/src/simix/smx_private.h @@ -90,8 +90,6 @@ XBT_PUBLIC(void) SIMIX_clean(void); xbt_ex e(msg); \ e.category = cat; \ e.value = val; \ - e.procname = xbt_procname(); \ - e.pid = xbt_getpid(); \ e.file = __FILE__; \ e.line = __LINE__; \ e.func = __func__; \ diff --git a/src/xbt/backtrace.cpp b/src/xbt/backtrace.cpp index f897e12adb..669856acda 100644 --- a/src/xbt/backtrace.cpp +++ b/src/xbt/backtrace.cpp @@ -1,9 +1,22 @@ /* Copyright (c) 2005-2016. The SimGrid Team. All rights reserved. */ +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + #include +#include + +#include + +// Try to detect and use the C++ intanium ABI for name demangling: +#ifdef __GXX_ABI_VERSION +#include +#endif -#include #include +#include +#include +#include #include "src/internal_config.h" @@ -22,6 +35,37 @@ void xbt_backtrace_display_current(void) xbt_backtrace_display(bt, used); } +namespace simgrid { +namespace xbt { + +std::unique_ptr demangle(const char* name) +{ +#ifdef __GXX_ABI_VERSION + int status; + auto res = std::unique_ptr( + abi::__cxa_demangle(name, NULL, NULL, &status), + std::free + ); + if (res != nullptr) + return res; + // We did not manage to resolve this. Probably because this is not a mangled + // symbol: +#endif + // Return the symbol: + return std::unique_ptr(xbt_strdup(name), std::free); +} + +std::vector backtrace() +{ + const std::size_t size = 10; + xbt_backtrace_location_t loc[size]; + size_t used = xbt_backtrace_current(loc, size); + return std::vector(loc, loc + used); +} + +} +} + #if HAVE_BACKTRACE && HAVE_EXECINFO_H && HAVE_POPEN && defined(ADDR2LINE) # include "src/xbt/backtrace_linux.cpp" #else diff --git a/src/xbt/backtrace_dummy.cpp b/src/xbt/backtrace_dummy.cpp index 4125fb7ac0..115b7db404 100644 --- a/src/xbt/backtrace_dummy.cpp +++ b/src/xbt/backtrace_dummy.cpp @@ -10,6 +10,8 @@ #include "src/xbt_modinter.h" #include "src/xbt/ex_interface.h" +#include + /* Module creation/destruction */ void xbt_backtrace_preinit(void) { @@ -33,7 +35,7 @@ namespace simgrid { namespace xbt { std::vector resolveBacktrace( - xbt_backtrace_location_t* loc, std::size_t count) + xbt_backtrace_location_t const* loc, std::size_t count) { return std::vector(); } diff --git a/src/xbt/backtrace_linux.cpp b/src/xbt/backtrace_linux.cpp index 19b15ab5d2..80cd509da3 100644 --- a/src/xbt/backtrace_linux.cpp +++ b/src/xbt/backtrace_linux.cpp @@ -1,7 +1,7 @@ /* backtrace_linux - backtrace displaying on linux platform */ /* This file is included by ex.cpp on need (have execinfo.h, popen & addrline)*/ -/* Copyright (c) 2008-2015. The SimGrid Team. +/* Copyright (c) 2008-2016. The SimGrid Team. * All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it @@ -19,6 +19,7 @@ /* This file is to be included in ex.cpp, so the following headers are not mandatory, but it's to make sure that eclipse see them too */ #include +#include #include "xbt/ex.h" #include "src/xbt/ex_interface.h" #include "xbt/log.h" @@ -114,6 +115,9 @@ static std::string get_binary_path() { struct stat stat_buf; + if (xbt_binary_name == nullptr) + return ""; + // We found it, we are happy: if (stat(xbt_binary_name, &stat_buf) == 0) return xbt_binary_name; @@ -144,23 +148,28 @@ static std::string get_binary_path() //FIXME: This code could be greatly improved/simplifyied with // http://cairo.sourcearchive.com/documentation/1.9.4/backtrace-symbols_8c-source.html std::vector resolveBacktrace( - xbt_backtrace_location_t* loc, std::size_t count) + xbt_backtrace_location_t const* loc, std::size_t count) { std::vector result; - /* no binary name, nothing to do */ - if (xbt_binary_name == NULL) - return result; - if (count == 0) return result; + if (xbt_binary_name == nullptr) + XBT_WARN("XBT not initialized, the backtrace will not be resolved."); + // Drop the first one: loc++; count--; char** backtrace_syms = backtrace_symbols(loc, count); std::string binary_name = get_binary_path(); + if (binary_name.empty()) { + for (std::size_t i = 0; i < count; i++) + result.push_back(simgrid::xbt::string_printf("%p", loc[i])); + return std::move(result); + } + // Create the system command for add2line: std::ostringstream stream; stream << ADDR2LINE << " -f -e " << binary_name << ' '; @@ -209,9 +218,10 @@ std::vector resolveBacktrace( } if (strcmp("??", line_func) != 0) { - XBT_DEBUG("Found static symbol %s() at %s", line_func, line_pos); + auto name = simgrid::xbt::demangle(line_func); + XBT_DEBUG("Found static symbol %s at %s", name.get(), line_pos); result.push_back(simgrid::xbt::string_printf( - "%s() at %s", line_func, line_pos + "%s at %s, %p", name.get(), line_pos, loc[i] )); } else { /* Damn. The symbol is in a dynamic library. Let's get wild */ @@ -312,9 +322,10 @@ std::vector resolveBacktrace( /* check whether the trick worked */ if (strcmp("??", line_func)) { - XBT_DEBUG("Found dynamic symbol %s() at %s", line_func, line_pos); + auto name = simgrid::xbt::demangle(line_func); + XBT_DEBUG("Found dynamic symbol %s at %s", name.get(), line_pos); result.push_back(simgrid::xbt::string_printf( - "%s() at %s", line_func, line_pos)); + "%s at %s, %p", name.get(), line_pos, loc[i])); } else { /* damn, nothing to do here. Let's print the raw address */ XBT_DEBUG("Dynamic symbol not found. Raw address = %s", backtrace_syms[i]); diff --git a/src/xbt/ex.cpp b/src/xbt/ex.cpp index 2fd2e3b2a2..2ba06986dd 100644 --- a/src/xbt/ex.cpp +++ b/src/xbt/ex.cpp @@ -45,9 +45,13 @@ #include #include +#include #include "src/internal_config.h" /* execinfo when available */ #include "xbt/ex.h" +#include "xbt/log.h" +#include "xbt/log.hpp" #include "xbt/backtrace.h" +#include "xbt/backtrace.hpp" #include "xbt/str.h" #include "xbt/synchro_core.h" #include "src/xbt_modinter.h" /* backtrace initialization headers */ @@ -57,6 +61,7 @@ #include "simgrid/simix.h" /* SIMIX_process_self_get_name() */ + XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_ex, xbt, "Exception mechanism"); xbt_ex::~xbt_ex() {} @@ -93,72 +98,13 @@ void xbt_throw( e.file = file; e.line = line; e.func = func; - e.procname = xbt_procname(); - e.pid = xbt_getpid(); throw e; } /** @brief shows an exception content and the associated stack if available */ void xbt_ex_display(xbt_ex_t * e) { - char *thrower = NULL; - if (e->pid != xbt_getpid()) - thrower = bprintf(" on process %d",e->pid); - - std::fprintf(stderr, "** SimGrid: UNCAUGHT EXCEPTION received on %s(%d): category: %s; value: %d\n" - "** %s\n" - "** Thrown by %s()%s\n", - xbt_binary_name, xbt_getpid(), xbt_ex_catname(e->category), e->value, e->what(), - e->procname.c_str(), thrower ? thrower : " in this process"); - XBT_CRITICAL("%s", e->what()); - xbt_free(thrower); - - if (xbt_initialized==0 || smx_cleaned) { - fprintf(stderr, "Ouch. SimGrid is not initialized yet, or already closing. No backtrace available.\n"); - return; /* Not started yet or already closing. Trying to generate a backtrace would probably fail */ - } - - std::vector backtrace = simgrid::xbt::resolveBacktrace( - e->bt.data(), e->bt.size()); - -#ifdef HAVE_BACKTRACE - if (!backtrace.empty()) { - /* We have everything to build neat backtraces */ - int cutpath = 0; - try { // We don't want to have an exception while checking how to deal with the one we already have, do we? - cutpath = xbt_cfg_get_boolean("exception/cutpath"); - } - catch(xbt_ex& e) { - // Nothing to do - } - - std::fprintf(stderr, "\n"); - for (std::string const& s : backtrace) { - - // TODO, move this logic into solveBacktrace - if (cutpath) { - // TODO, simplify this - char* p = xbt_strdup(s.c_str()); - xbt_str_rtrim(p, ":0123456789"); - char* filename = strrchr(p, '/')+1; - char* end_of_message = strrchr(p, ' '); - int length = strlen(p)-strlen(end_of_message); - char* dest = (char*) std::malloc(length); - std::memcpy(dest, &p[0], length); - dest[length] = 0; - std::fprintf(stderr, "%s %s\n", dest, filename); - std::free(dest); - std::free(p); - } - else { - std::fprintf(stderr, "%s\n", s.c_str()); - } - } - } else -#endif - std::fprintf(stderr, "\n" - "** In %s() at %s:%d\n" - "** (no backtrace available)\n", e->func, e->file, e->line); + simgrid::xbt::logException(xbt_log_priority_critical, "UNCAUGHT EXCEPTION", *e); } /** \brief returns a short name for the given exception category */ diff --git a/src/xbt/ex_interface.h b/src/xbt/ex_interface.h index cbff4ea2d0..3c7599e5f3 100644 --- a/src/xbt/ex_interface.h +++ b/src/xbt/ex_interface.h @@ -22,10 +22,6 @@ namespace simgrid { namespace xbt { -/* Change raw libc symbols to file names and line numbers */ -XBT_PUBLIC(std::vector) resolveBacktrace( - xbt_backtrace_location_t* loc, std::size_t count); - } } diff --git a/src/xbt/exception.cpp b/src/xbt/exception.cpp new file mode 100644 index 0000000000..6e9cf0c258 --- /dev/null +++ b/src/xbt/exception.cpp @@ -0,0 +1,72 @@ +/* Copyright (c) 2005-2016. The SimGrid Team. + * All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ + +#include +#include +#include + +#include +#include +#include +#include + +extern "C" { +XBT_LOG_EXTERNAL_CATEGORY(xbt); +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_exception, xbt, "Exceptions"); +} + +namespace simgrid { +namespace xbt { + +WithContextException::~WithContextException() {} + +void logException( + e_xbt_log_priority_t prio, + const char* context, std::exception const& exception) +{ + try { + auto name = simgrid::xbt::demangle(typeid(exception).name()); + + auto with_context = + dynamic_cast(&exception); + if (with_context != nullptr) + XBT_LOG(prio, "%s %s by %s/%d: %s", + context, name.get(), + with_context->processName().c_str(), with_context->pid(), + exception.what()); + else + XBT_LOG(prio, "%s %s: %s", context, name.get(), exception.what()); + + // Do we have a backtrace? + if (with_context != nullptr) { + auto backtrace = simgrid::xbt::resolveBacktrace( + with_context->backtrace().data(), with_context->backtrace().size()); + for (std::string const& s : backtrace) + XBT_LOG(prio, " -> %s", s.c_str()); + } + + // Do we have a nested exception? + auto with_nested = dynamic_cast(&exception); + if (with_nested == nullptr || with_nested->nested_ptr() == nullptr) + return; + try { + with_nested->rethrow_nested(); + } + catch (std::exception& nested_exception) { + logException(prio, "Caused by", nested_exception); + } + // We could catch nested_exception or WithContextException but we don't bother: + catch (...) { + XBT_LOG(prio, "Caused by an unknown exception"); + } + } + catch (...) { + // Don't log exceptions we got when trying to log exception + } +} + +} +} diff --git a/src/xbt/log.c b/src/xbt/log.c index 23482c3fb0..7c57168c03 100644 --- a/src/xbt/log.c +++ b/src/xbt/log.c @@ -107,6 +107,7 @@ static void xbt_log_connect_categories(void) XBT_LOG_CONNECT(xbt_dyn); XBT_LOG_CONNECT(xbt_ex); XBT_LOG_CONNECT(xbt_backtrace); + XBT_LOG_CONNECT(xbt_exception); XBT_LOG_CONNECT(xbt_fifo); XBT_LOG_CONNECT(xbt_graph); XBT_LOG_CONNECT(xbt_heap); diff --git a/tools/cmake/DefinePackages.cmake b/tools/cmake/DefinePackages.cmake index 87ef125abe..eb8ef777c7 100644 --- a/tools/cmake/DefinePackages.cmake +++ b/tools/cmake/DefinePackages.cmake @@ -252,6 +252,7 @@ set(XBT_SRC src/xbt/dict_elm.c src/xbt/dynar.cpp src/xbt/ex.cpp + src/xbt/exception.cpp src/xbt/fifo.c src/xbt/graph.c src/xbt/heap.c @@ -667,7 +668,9 @@ set(headers_to_install include/xbt/dynar.h include/xbt/dynar.hpp include/xbt/ex.h + include/xbt/exception.hpp include/xbt/backtrace.h + include/xbt/backtrace.hpp include/xbt/fifo.h include/xbt/file.h include/xbt/function_types.h @@ -678,6 +681,7 @@ set(headers_to_install include/xbt/lib.h include/xbt/Extendable.hpp include/xbt/log.h + include/xbt/log.hpp include/xbt/mallocator.h include/xbt/matrix.h include/xbt/memory.hpp -- 2.20.1