libcamera: base: backtrace: Use libunwind when available

libunwind is an alternative to glibc's backtrace() to extract a
backtrace. Use it when available to extend backtrace support to more
platforms.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
Laurent Pinchart 2021-09-24 05:05:51 +03:00
parent f8d76fe79b
commit a7c7f58d59
3 changed files with 73 additions and 3 deletions

View file

@ -26,6 +26,9 @@ public:
private: private:
LIBCAMERA_DISABLE_COPY(Backtrace) LIBCAMERA_DISABLE_COPY(Backtrace)
bool backtraceTrace();
bool unwindTrace();
std::vector<void *> backtrace_; std::vector<void *> backtrace_;
}; };

View file

@ -18,6 +18,15 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#if HAVE_UNWIND
/*
* Disable support for remote unwinding to enable a more optimized
* implementation.
*/
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#endif
#include <sstream> #include <sstream>
#include <libcamera/base/span.h> #include <libcamera/base/span.h>
@ -146,6 +155,20 @@ std::string DwflParser::stackEntry(const void *ip)
* It can later be converted to a string with toString(). * It can later be converted to a string with toString().
*/ */
Backtrace::Backtrace() Backtrace::Backtrace()
{
/* Try libunwind first and fall back to backtrace() if it fails. */
if (unwindTrace())
return;
backtraceTrace();
}
/*
* Avoid inlining to make sure that the Backtrace constructor adds exactly two
* calls to the stack, which are later skipped in toString().
*/
__attribute__((__noinline__))
bool Backtrace::backtraceTrace()
{ {
#if HAVE_BACKTRACE #if HAVE_BACKTRACE
backtrace_.resize(32); backtrace_.resize(32);
@ -153,10 +176,45 @@ Backtrace::Backtrace()
int num_entries = backtrace(backtrace_.data(), backtrace_.size()); int num_entries = backtrace(backtrace_.data(), backtrace_.size());
if (num_entries < 0) { if (num_entries < 0) {
backtrace_.clear(); backtrace_.clear();
return; return false;
} }
backtrace_.resize(num_entries); backtrace_.resize(num_entries);
return true;
#else
return false;
#endif
}
__attribute__((__noinline__))
bool Backtrace::unwindTrace()
{
#if HAVE_UNWIND
unw_context_t uc;
int ret = unw_getcontext(&uc);
if (ret)
return false;
unw_cursor_t cursor;
ret = unw_init_local(&cursor, &uc);
if (ret)
return false;
do {
unw_word_t ip;
ret = unw_get_reg(&cursor, UNW_REG_IP, &ip);
if (ret) {
backtrace_.push_back(nullptr);
continue;
}
backtrace_.push_back(reinterpret_cast<void *>(ip));
} while (unw_step(&cursor) > 0);
return true;
#else
return false;
#endif #endif
} }
@ -181,8 +239,11 @@ Backtrace::Backtrace()
*/ */
std::string Backtrace::toString(unsigned int skipLevels) const std::string Backtrace::toString(unsigned int skipLevels) const
{ {
/* Skip the first entry, corresponding to the Backtrace construction. */ /*
skipLevels += 1; * Skip the first two entries, corresponding to the Backtrace
* construction.
*/
skipLevels += 2;
if (backtrace_.size() <= skipLevels) if (backtrace_.size() <= skipLevels)
return std::string(); return std::string();

View file

@ -20,6 +20,7 @@ libcamera_base_sources = files([
]) ])
libdw = cc.find_library('libdw', required : false) libdw = cc.find_library('libdw', required : false)
libunwind = cc.find_library('libunwind', required : false)
if cc.has_header_symbol('execinfo.h', 'backtrace') if cc.has_header_symbol('execinfo.h', 'backtrace')
config_h.set('HAVE_BACKTRACE', 1) config_h.set('HAVE_BACKTRACE', 1)
@ -29,10 +30,15 @@ if libdw.found()
config_h.set('HAVE_DW', 1) config_h.set('HAVE_DW', 1)
endif endif
if libunwind.found()
config_h.set('HAVE_UNWIND', 1)
endif
libcamera_base_deps = [ libcamera_base_deps = [
dependency('threads'), dependency('threads'),
libatomic, libatomic,
libdw, libdw,
libunwind,
] ]
# Internal components must use the libcamera_base_private dependency to enable # Internal components must use the libcamera_base_private dependency to enable