How do I find where an exception was thrown in C++?

后端 未结 7 1264
[愿得一人]
[愿得一人] 2020-11-28 18:55

I have a program that throws an uncaught exception somewhere. All I get is a report of an exception being thrown, and no information as to where it was thrown. It seems illo

相关标签:
7条回答
  • 2020-11-28 19:33

    As you say, we can use 'catch throw' in gdb and call 'backtrace' for every single thrown exception. While that's usually too tedious to do manually, gdb allows automation of the process. That allows seeing the backtrace of all exceptions that are thrown, including the last uncaught one:

    gdb>

    set pagination off
    catch throw
    commands
    backtrace
    continue
    end
    run
    

    Without further manual intervention, this generates lots of backtraces, including one for the last uncaught exception:

    Catchpoint 1 (exception thrown), 0x00a30 in __cxa_throw () from libstdc++.so.6
    #0  0x0da30 in __cxa_throw () from /usr/.../libstdc++.so.6
    #1  0x021f2 in std::__throw_bad_weak_ptr () at .../shared_ptr_base.h:76
    [...]
    terminate called after throwing an instance of 'std::bad_weak_ptr'
      what():  bad_weak_ptr
    Program received signal SIGABRT, Aborted.
    

    Here's a great blog post wrapping this up: http://741mhz.com/throw-stacktrace [on archive.org]

    0 讨论(0)
  • 2020-11-28 19:33

    I've got code to do this in Windows/Visual Studio, let me know if you want an outline. Don't know how to do it for dwarf2 code though, a quick google suggests that there's a function _Unwind_Backtrace in libgcc that probably is part of what you need.

    0 讨论(0)
  • 2020-11-28 19:34

    You can create a macro like:

    #define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message) )
    

    ...and it will give you the location where the exception is thrown (admittedly not the stack trace). It's necessary for you to derive your exceptions from some base class that takes the above constructor.

    0 讨论(0)
  • 2020-11-28 19:37

    You can mark main tight places in your code as noexcept to locate an exception, then use libunwind (just add -lunwind to linker parameters) (tested with clang++ 3.6):

    demagle.hpp:

    #pragma once
    
    char const *
    get_demangled_name(char const * const symbol) noexcept;
    

    demangle.cpp:

    #include "demangle.hpp"
    
    #include <memory>
    
    #include <cstdlib>
    
    #include <cxxabi.h>
    
    namespace
    {
    
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wglobal-constructors"
    #pragma clang diagnostic ignored "-Wexit-time-destructors"
    std::unique_ptr< char, decltype(std::free) & > demangled_name{nullptr, std::free};
    #pragma clang diagnostic pop
    
    }
    
    char const *
    get_demangled_name(char const * const symbol) noexcept
    {
        if (!symbol) {
            return "<null>";
        }
        int status = -4;
        demangled_name.reset(abi::__cxa_demangle(symbol, demangled_name.release(), nullptr, &status));
        return ((status == 0) ? demangled_name.get() : symbol);
    }
    

    backtrace.hpp:

    #pragma once
    #include <ostream>
    
    void
    backtrace(std::ostream & _out) noexcept;
    

    backtrace.cpp:

    #include "backtrace.hpp"
    
    #include <iostream>
    #include <iomanip>
    #include <limits>
    #include <ostream>
    
    #include <cstdint>
    
    #define UNW_LOCAL_ONLY
    #include <libunwind.h>
    
    namespace
    {
    
    void
    print_reg(std::ostream & _out, unw_word_t reg) noexcept
    {
        constexpr std::size_t address_width = std::numeric_limits< std::uintptr_t >::digits / 4;
        _out << "0x" << std::setfill('0') << std::setw(address_width) << reg;
    }
    
    char symbol[1024];
    
    }
    
    void
    backtrace(std::ostream & _out) noexcept
    {
        unw_cursor_t cursor;
        unw_context_t context;
        unw_getcontext(&context);
        unw_init_local(&cursor, &context);
        _out << std::hex << std::uppercase;
        while (0 < unw_step(&cursor)) {
            unw_word_t ip = 0;
            unw_get_reg(&cursor, UNW_REG_IP, &ip);
            if (ip == 0) {
                break;
            }
            unw_word_t sp = 0;
            unw_get_reg(&cursor, UNW_REG_SP, &sp);
            print_reg(_out, ip);
            _out << ": (SP:";
            print_reg(_out, sp);
            _out << ") ";
            unw_word_t offset = 0;
            if (unw_get_proc_name(&cursor, symbol, sizeof(symbol), &offset) == 0) {
                _out << "(" << get_demangled_name(symbol) << " + 0x" << offset << ")\n\n";
            } else {
                _out << "-- error: unable to obtain symbol name for this frame\n\n";
            }
        }
        _out << std::flush;
    }
    

    backtrace_on_terminate.hpp:

    #include "demangle.hpp"
    #include "backtrace.hpp"
    
    #include <iostream>
    #include <type_traits>
    #include <exception>
    #include <memory>
    #include <typeinfo>
    
    #include <cstdlib>
    
    #include <cxxabi.h>
    
    namespace
    {
    
    [[noreturn]]
    void
    backtrace_on_terminate() noexcept;
    
    static_assert(std::is_same< std::terminate_handler, decltype(&backtrace_on_terminate) >{});
    
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wglobal-constructors"
    #pragma clang diagnostic ignored "-Wexit-time-destructors"
    std::unique_ptr< std::remove_pointer_t< std::terminate_handler >, decltype(std::set_terminate) & > terminate_handler{std::set_terminate(backtrace_on_terminate), std::set_terminate};
    #pragma clang diagnostic pop
    
    [[noreturn]]
    void
    backtrace_on_terminate() noexcept
    {
        std::set_terminate(terminate_handler.release()); // to avoid infinite looping if any
        backtrace(std::clog);
        if (std::exception_ptr ep = std::current_exception()) {
            try {
                std::rethrow_exception(ep);
            } catch (std::exception const & e) {
                std::clog << "backtrace: unhandled exception std::exception:what(): " << e.what() << std::endl;
            } catch (...) {
                if (std::type_info * et = abi::__cxa_current_exception_type()) {
                    std::clog << "backtrace: unhandled exception type: " << get_demangled_name(et->name()) << std::endl;
                } else {
                    std::clog << "backtrace: unhandled unknown exception" << std::endl;
                }
            }
        }
        std::_Exit(EXIT_FAILURE); // change to desired return code
    }
    
    }
    

    There is good article concerning the issue.

    0 讨论(0)
  • 2020-11-28 19:39

    You did not pass information about what OS / Compiler you use.

    In Visual Studio C++ Exceptions can be instrumented.

    See "Visual C++ Exception-Handling Instrumentation" on ddj.com

    My article "Postmortem Debugging", also on ddj.com includes code to use Win32 structured exception handling (used by the instrumentation) for logging etc.

    0 讨论(0)
  • 2020-11-28 19:43

    Check this thread, perhaps it helps:

    Catching all unhandled C++ exceptions?

    I made good experiences with that software:

    http://www.codeproject.com/KB/applications/blackbox.aspx

    It can print out a stack trace to a file for any unhandled exception.

    0 讨论(0)
提交回复
热议问题