Message ID | 20211003223606.20016-3-laurent.pinchart@ideasonboard.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Quoting Laurent Pinchart (2021-10-03 23:36:04) > libdw provides access to debugging information. This allows creating > better stack trace entries, with file names and line numbers, but also > with demangled symbols as the symbol name is available and can be passed > to abi::__cxa_demangle(). > > With libdw, the backtrace previously generated by backtrace_symbols() > > src/cam/../libcamera/libcamera.so(_ZN9libcamera14VimcCameraData4initEv+0xbd) [0x7f7dbb73222d] > src/cam/../libcamera/libcamera.so(_ZN9libcamera19PipelineHandlerVimc5matchEPNS_16DeviceEnumeratorE+0x3e0) [0x7f7dbb731c40] > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private22createPipelineHandlersEv+0x1a7) [0x7f7dbb5ea027] > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private4initEv+0x98) [0x7f7dbb5e9dc8] > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private3runEv+0x9f) [0x7f7dbb5e9c5f] > src/cam/../libcamera/base/libcamera-base.so(_ZN9libcamera6Thread11startThreadEv+0xee) [0x7f7dbb3e95be] > src/cam/../libcamera/base/libcamera-base.so(+0x4f9d7) [0x7f7dbb3ec9d7] > src/cam/../libcamera/base/libcamera-base.so(+0x4f90e) [0x7f7dbb3ec90e] > src/cam/../libcamera/base/libcamera-base.so(+0x4f2c2) [0x7f7dbb3ec2c2] > /lib64/libpthread.so.0(+0x7e8e) [0x7f7dbab65e8e] > /lib64/libc.so.6(clone+0x3f) [0x7f7dbb10b26f] > > becomes > > libcamera::VimcCameraData::init()+0xbd (src/libcamera/libcamera.so [0x00007f9de605b22d]) > libcamera::PipelineHandlerVimc::match(libcamera::DeviceEnumerator*)+0x3e0 (src/libcamera/libcamera.so [0x00007f9de605ac40]) > libcamera::CameraManager::Private::createPipelineHandlers()+0x1a7 (src/libcamera/libcamera.so [0x00007f9de5f13027]) > libcamera::CameraManager::Private::init()+0x98 (src/libcamera/libcamera.so [0x00007f9de5f12dc8]) > libcamera::CameraManager::Private::run()+0x9f (src/libcamera/libcamera.so [0x00007f9de5f12c5f]) > libcamera::Thread::startThread()+0xee (src/libcamera/base/libcamera-base.so [0x00007f9de5d125be]) > decltype(*(std::__1::forward<libcamera::Thread*>(fp0)).*fp()) std::__1::__invoke<void (libcamera::Thread::*)(), libcamera::Thread*, void>(void (libcamera::Thread::*&&)(), libcamera::Thread*&&)+0x77 (src/libcamera/base/libcamera-base.so [0x00007f9de5d159d7]) > void std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*, 2ul>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*>&, std::__1::__tuple_indices<2ul>)+0x3e (src/libcamera/base/libcamera-base.so [0x00007f9de5d1590e]) > void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*> >(void*)+0x62 (src/libcamera/base/libcamera-base.so [0x00007f9de5d152c2]) > start_thread+0xde (/var/tmp/portage/sys-libs/glibc-2.33-r1/work/glibc-2.33/nptl/pthread_create.c:482) > __clone+0x3f (../sysdeps/unix/sysv/linux/x86_64/clone.S:97) > > The stack entries related to libcamera are missing source file name and > line information, which will be investigated separately, but this is > still an improvement. > > Use libdw when available, falling back to backtrace_symbols() otherwise, > or if libdw fails for any reason. We still have the addresses printed, so there is no loss. Files and line numbers would be a great addition indeed, but they can still be obtained manually if required. (or often determined by looking at the next entry of the call stack to see what was called, but of course this isn't always unique). We might want to have a 'debug facilities dependecies' section added to the README.rst for libdw(-dev?) But either way... Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > --- > src/libcamera/base/backtrace.cpp | 120 +++++++++++++++++++++++++++++++ > src/libcamera/base/meson.build | 7 ++ > 2 files changed, 127 insertions(+) > > diff --git a/src/libcamera/base/backtrace.cpp b/src/libcamera/base/backtrace.cpp > index c010a7e42e4d..79e4a31f3d21 100644 > --- a/src/libcamera/base/backtrace.cpp > +++ b/src/libcamera/base/backtrace.cpp > @@ -12,9 +12,16 @@ > #include <stdlib.h> > #endif > > +#ifdef HAVE_DW > +#include <cxxabi.h> > +#include <elfutils/libdwfl.h> > +#include <unistd.h> > +#endif > + > #include <sstream> > > #include <libcamera/base/span.h> > +#include <libcamera/base/utils.h> > > /** > * \file backtrace.h > @@ -23,6 +30,101 @@ > > namespace libcamera { > > +namespace { > + > +#if HAVE_DW > +class DwflParser > +{ > +public: > + DwflParser(); > + ~DwflParser(); > + > + bool isValid() const { return valid_; } > + std::string stackEntry(const void *ip); > + > +private: > + Dwfl_Callbacks callbacks_; > + Dwfl *dwfl_; > + bool valid_; > +}; > + > +DwflParser::DwflParser() > + : callbacks_({}), dwfl_(nullptr), valid_(false) > +{ > + callbacks_.find_elf = dwfl_linux_proc_find_elf; > + callbacks_.find_debuginfo = dwfl_standard_find_debuginfo; > + > + dwfl_ = dwfl_begin(&callbacks_); > + if (!dwfl_) > + return; > + > + int ret = dwfl_linux_proc_report(dwfl_, getpid()); > + if (ret) > + return; > + > + ret = dwfl_report_end(dwfl_, nullptr, nullptr); > + if (ret) > + return; > + > + valid_ = true; > +} > + > +DwflParser::~DwflParser() > +{ > + if (dwfl_) > + dwfl_end(dwfl_); > +} > + > +std::string DwflParser::stackEntry(const void *ip) > +{ > + Dwarf_Addr addr = reinterpret_cast<Dwarf_Addr>(ip); > + > + Dwfl_Module *module = dwfl_addrmodule(dwfl_, addr); > + if (!module) > + return std::string(); > + > + std::ostringstream entry; > + > + GElf_Off offset; > + GElf_Sym sym; > + const char *symbol = dwfl_module_addrinfo(module, addr, &offset, &sym, > + nullptr, nullptr, nullptr); > + if (symbol) { > + char *name = abi::__cxa_demangle(symbol, nullptr, nullptr, nullptr); > + entry << (name ? name : symbol) << "+0x" << std::hex << offset > + << std::dec; > + free(name); > + } else { > + entry << "??? [" << utils::hex(addr) << "]"; > + } > + > + entry << " ("; > + > + Dwfl_Line *line = dwfl_module_getsrc(module, addr); > + if (line) { > + const char *filename; > + int lineNumber = 0; > + > + filename = dwfl_lineinfo(line, &addr, &lineNumber, nullptr, > + nullptr, nullptr); > + > + entry << (filename ? filename : "???") << ":" << lineNumber; > + } else { > + const char *filename = nullptr; > + > + dwfl_module_info(module, nullptr, nullptr, nullptr, nullptr, > + nullptr, &filename, nullptr); > + > + entry << (filename ? filename : "???") << " [" << utils::hex(addr) << "]"; > + } > + > + entry << ")"; > + return entry.str(); > +} > +#endif /* HAVE_DW */ > + > +} /* namespace */ > + > /** > * \class Backtrace > * \brief Representation of a call stack backtrace > @@ -85,6 +187,24 @@ std::string Backtrace::toString(unsigned int skipLevels) const > if (backtrace_.size() <= skipLevels) > return std::string(); > > +#if HAVE_DW > + DwflParser dwfl; > + > + if (dwfl.isValid()) { > + std::ostringstream msg; > + > + Span<void *const> trace{ backtrace_ }; > + for (const void *ip : trace.subspan(skipLevels)) { > + if (ip) > + msg << dwfl.stackEntry(ip) << std::endl; > + else > + msg << "???" << std::endl; > + } > + > + return msg.str(); > + } > +#endif > + > #if HAVE_BACKTRACE > Span<void *const> trace{ backtrace_ }; > trace = trace.subspan(skipLevels); > diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build > index fc00296dfa8a..4c44b9f55011 100644 > --- a/src/libcamera/base/meson.build > +++ b/src/libcamera/base/meson.build > @@ -19,13 +19,20 @@ libcamera_base_sources = files([ > 'utils.cpp', > ]) > > +libdw = cc.find_library('libdw', required : false) > + > if cc.has_header_symbol('execinfo.h', 'backtrace') > config_h.set('HAVE_BACKTRACE', 1) > endif > > +if libdw.found() > + config_h.set('HAVE_DW', 1) > +endif > + > libcamera_base_deps = [ > dependency('threads'), > libatomic, > + libdw, > ] > > # Internal components must use the libcamera_base_private dependency to enable > -- > Regards, > > Laurent Pinchart >
Hi Kieran, On Tue, Oct 12, 2021 at 11:40:37AM +0100, Kieran Bingham wrote: > Quoting Laurent Pinchart (2021-10-03 23:36:04) > > libdw provides access to debugging information. This allows creating > > better stack trace entries, with file names and line numbers, but also > > with demangled symbols as the symbol name is available and can be passed > > to abi::__cxa_demangle(). > > > > With libdw, the backtrace previously generated by backtrace_symbols() > > > > src/cam/../libcamera/libcamera.so(_ZN9libcamera14VimcCameraData4initEv+0xbd) [0x7f7dbb73222d] > > src/cam/../libcamera/libcamera.so(_ZN9libcamera19PipelineHandlerVimc5matchEPNS_16DeviceEnumeratorE+0x3e0) [0x7f7dbb731c40] > > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private22createPipelineHandlersEv+0x1a7) [0x7f7dbb5ea027] > > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private4initEv+0x98) [0x7f7dbb5e9dc8] > > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private3runEv+0x9f) [0x7f7dbb5e9c5f] > > src/cam/../libcamera/base/libcamera-base.so(_ZN9libcamera6Thread11startThreadEv+0xee) [0x7f7dbb3e95be] > > src/cam/../libcamera/base/libcamera-base.so(+0x4f9d7) [0x7f7dbb3ec9d7] > > src/cam/../libcamera/base/libcamera-base.so(+0x4f90e) [0x7f7dbb3ec90e] > > src/cam/../libcamera/base/libcamera-base.so(+0x4f2c2) [0x7f7dbb3ec2c2] > > /lib64/libpthread.so.0(+0x7e8e) [0x7f7dbab65e8e] > > /lib64/libc.so.6(clone+0x3f) [0x7f7dbb10b26f] > > > > becomes > > > > libcamera::VimcCameraData::init()+0xbd (src/libcamera/libcamera.so [0x00007f9de605b22d]) > > libcamera::PipelineHandlerVimc::match(libcamera::DeviceEnumerator*)+0x3e0 (src/libcamera/libcamera.so [0x00007f9de605ac40]) > > libcamera::CameraManager::Private::createPipelineHandlers()+0x1a7 (src/libcamera/libcamera.so [0x00007f9de5f13027]) > > libcamera::CameraManager::Private::init()+0x98 (src/libcamera/libcamera.so [0x00007f9de5f12dc8]) > > libcamera::CameraManager::Private::run()+0x9f (src/libcamera/libcamera.so [0x00007f9de5f12c5f]) > > libcamera::Thread::startThread()+0xee (src/libcamera/base/libcamera-base.so [0x00007f9de5d125be]) > > decltype(*(std::__1::forward<libcamera::Thread*>(fp0)).*fp()) std::__1::__invoke<void (libcamera::Thread::*)(), libcamera::Thread*, void>(void (libcamera::Thread::*&&)(), libcamera::Thread*&&)+0x77 (src/libcamera/base/libcamera-base.so [0x00007f9de5d159d7]) > > void std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*, 2ul>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*>&, std::__1::__tuple_indices<2ul>)+0x3e (src/libcamera/base/libcamera-base.so [0x00007f9de5d1590e]) > > void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*> >(void*)+0x62 (src/libcamera/base/libcamera-base.so [0x00007f9de5d152c2]) > > start_thread+0xde (/var/tmp/portage/sys-libs/glibc-2.33-r1/work/glibc-2.33/nptl/pthread_create.c:482) > > __clone+0x3f (../sysdeps/unix/sysv/linux/x86_64/clone.S:97) > > > > The stack entries related to libcamera are missing source file name and > > line information, which will be investigated separately, but this is > > still an improvement. > > > > Use libdw when available, falling back to backtrace_symbols() otherwise, > > or if libdw fails for any reason. > > We still have the addresses printed, so there is no loss. Files and line > numbers would be a great addition indeed, but they can still be obtained > manually if required. (or often determined by looking at the next > entry of the call stack to see what was called, but of course this isn't > always unique). > > We might want to have a 'debug facilities dependecies' section added to > the README.rst for libdw(-dev?) Good idea, I'll submit a separate patch for that. > But either way... > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > > --- > > src/libcamera/base/backtrace.cpp | 120 +++++++++++++++++++++++++++++++ > > src/libcamera/base/meson.build | 7 ++ > > 2 files changed, 127 insertions(+) > > > > diff --git a/src/libcamera/base/backtrace.cpp b/src/libcamera/base/backtrace.cpp > > index c010a7e42e4d..79e4a31f3d21 100644 > > --- a/src/libcamera/base/backtrace.cpp > > +++ b/src/libcamera/base/backtrace.cpp > > @@ -12,9 +12,16 @@ > > #include <stdlib.h> > > #endif > > > > +#ifdef HAVE_DW > > +#include <cxxabi.h> > > +#include <elfutils/libdwfl.h> > > +#include <unistd.h> > > +#endif > > + > > #include <sstream> > > > > #include <libcamera/base/span.h> > > +#include <libcamera/base/utils.h> > > > > /** > > * \file backtrace.h > > @@ -23,6 +30,101 @@ > > > > namespace libcamera { > > > > +namespace { > > + > > +#if HAVE_DW > > +class DwflParser > > +{ > > +public: > > + DwflParser(); > > + ~DwflParser(); > > + > > + bool isValid() const { return valid_; } > > + std::string stackEntry(const void *ip); > > + > > +private: > > + Dwfl_Callbacks callbacks_; > > + Dwfl *dwfl_; > > + bool valid_; > > +}; > > + > > +DwflParser::DwflParser() > > + : callbacks_({}), dwfl_(nullptr), valid_(false) > > +{ > > + callbacks_.find_elf = dwfl_linux_proc_find_elf; > > + callbacks_.find_debuginfo = dwfl_standard_find_debuginfo; > > + > > + dwfl_ = dwfl_begin(&callbacks_); > > + if (!dwfl_) > > + return; > > + > > + int ret = dwfl_linux_proc_report(dwfl_, getpid()); > > + if (ret) > > + return; > > + > > + ret = dwfl_report_end(dwfl_, nullptr, nullptr); > > + if (ret) > > + return; > > + > > + valid_ = true; > > +} > > + > > +DwflParser::~DwflParser() > > +{ > > + if (dwfl_) > > + dwfl_end(dwfl_); > > +} > > + > > +std::string DwflParser::stackEntry(const void *ip) > > +{ > > + Dwarf_Addr addr = reinterpret_cast<Dwarf_Addr>(ip); > > + > > + Dwfl_Module *module = dwfl_addrmodule(dwfl_, addr); > > + if (!module) > > + return std::string(); > > + > > + std::ostringstream entry; > > + > > + GElf_Off offset; > > + GElf_Sym sym; > > + const char *symbol = dwfl_module_addrinfo(module, addr, &offset, &sym, > > + nullptr, nullptr, nullptr); > > + if (symbol) { > > + char *name = abi::__cxa_demangle(symbol, nullptr, nullptr, nullptr); > > + entry << (name ? name : symbol) << "+0x" << std::hex << offset > > + << std::dec; > > + free(name); > > + } else { > > + entry << "??? [" << utils::hex(addr) << "]"; > > + } > > + > > + entry << " ("; > > + > > + Dwfl_Line *line = dwfl_module_getsrc(module, addr); > > + if (line) { > > + const char *filename; > > + int lineNumber = 0; > > + > > + filename = dwfl_lineinfo(line, &addr, &lineNumber, nullptr, > > + nullptr, nullptr); > > + > > + entry << (filename ? filename : "???") << ":" << lineNumber; > > + } else { > > + const char *filename = nullptr; > > + > > + dwfl_module_info(module, nullptr, nullptr, nullptr, nullptr, > > + nullptr, &filename, nullptr); > > + > > + entry << (filename ? filename : "???") << " [" << utils::hex(addr) << "]"; > > + } > > + > > + entry << ")"; > > + return entry.str(); > > +} > > +#endif /* HAVE_DW */ > > + > > +} /* namespace */ > > + > > /** > > * \class Backtrace > > * \brief Representation of a call stack backtrace > > @@ -85,6 +187,24 @@ std::string Backtrace::toString(unsigned int skipLevels) const > > if (backtrace_.size() <= skipLevels) > > return std::string(); > > > > +#if HAVE_DW > > + DwflParser dwfl; > > + > > + if (dwfl.isValid()) { > > + std::ostringstream msg; > > + > > + Span<void *const> trace{ backtrace_ }; > > + for (const void *ip : trace.subspan(skipLevels)) { > > + if (ip) > > + msg << dwfl.stackEntry(ip) << std::endl; > > + else > > + msg << "???" << std::endl; > > + } > > + > > + return msg.str(); > > + } > > +#endif > > + > > #if HAVE_BACKTRACE > > Span<void *const> trace{ backtrace_ }; > > trace = trace.subspan(skipLevels); > > diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build > > index fc00296dfa8a..4c44b9f55011 100644 > > --- a/src/libcamera/base/meson.build > > +++ b/src/libcamera/base/meson.build > > @@ -19,13 +19,20 @@ libcamera_base_sources = files([ > > 'utils.cpp', > > ]) > > > > +libdw = cc.find_library('libdw', required : false) > > + > > if cc.has_header_symbol('execinfo.h', 'backtrace') > > config_h.set('HAVE_BACKTRACE', 1) > > endif > > > > +if libdw.found() > > + config_h.set('HAVE_DW', 1) > > +endif > > + > > libcamera_base_deps = [ > > dependency('threads'), > > libatomic, > > + libdw, > > ] > > > > # Internal components must use the libcamera_base_private dependency to enable
Hi Laurent, On Mon, Oct 04, 2021 at 01:36:04AM +0300, Laurent Pinchart wrote: > libdw provides access to debugging information. This allows creating > better stack trace entries, with file names and line numbers, but also > with demangled symbols as the symbol name is available and can be passed > to abi::__cxa_demangle(). > > With libdw, the backtrace previously generated by backtrace_symbols() > > src/cam/../libcamera/libcamera.so(_ZN9libcamera14VimcCameraData4initEv+0xbd) [0x7f7dbb73222d] > src/cam/../libcamera/libcamera.so(_ZN9libcamera19PipelineHandlerVimc5matchEPNS_16DeviceEnumeratorE+0x3e0) [0x7f7dbb731c40] > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private22createPipelineHandlersEv+0x1a7) [0x7f7dbb5ea027] > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private4initEv+0x98) [0x7f7dbb5e9dc8] > src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private3runEv+0x9f) [0x7f7dbb5e9c5f] > src/cam/../libcamera/base/libcamera-base.so(_ZN9libcamera6Thread11startThreadEv+0xee) [0x7f7dbb3e95be] > src/cam/../libcamera/base/libcamera-base.so(+0x4f9d7) [0x7f7dbb3ec9d7] > src/cam/../libcamera/base/libcamera-base.so(+0x4f90e) [0x7f7dbb3ec90e] > src/cam/../libcamera/base/libcamera-base.so(+0x4f2c2) [0x7f7dbb3ec2c2] > /lib64/libpthread.so.0(+0x7e8e) [0x7f7dbab65e8e] > /lib64/libc.so.6(clone+0x3f) [0x7f7dbb10b26f] > > becomes > > libcamera::VimcCameraData::init()+0xbd (src/libcamera/libcamera.so [0x00007f9de605b22d]) > libcamera::PipelineHandlerVimc::match(libcamera::DeviceEnumerator*)+0x3e0 (src/libcamera/libcamera.so [0x00007f9de605ac40]) > libcamera::CameraManager::Private::createPipelineHandlers()+0x1a7 (src/libcamera/libcamera.so [0x00007f9de5f13027]) > libcamera::CameraManager::Private::init()+0x98 (src/libcamera/libcamera.so [0x00007f9de5f12dc8]) > libcamera::CameraManager::Private::run()+0x9f (src/libcamera/libcamera.so [0x00007f9de5f12c5f]) > libcamera::Thread::startThread()+0xee (src/libcamera/base/libcamera-base.so [0x00007f9de5d125be]) > decltype(*(std::__1::forward<libcamera::Thread*>(fp0)).*fp()) std::__1::__invoke<void (libcamera::Thread::*)(), libcamera::Thread*, void>(void (libcamera::Thread::*&&)(), libcamera::Thread*&&)+0x77 (src/libcamera/base/libcamera-base.so [0x00007f9de5d159d7]) > void std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*, 2ul>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*>&, std::__1::__tuple_indices<2ul>)+0x3e (src/libcamera/base/libcamera-base.so [0x00007f9de5d1590e]) > void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*> >(void*)+0x62 (src/libcamera/base/libcamera-base.so [0x00007f9de5d152c2]) > start_thread+0xde (/var/tmp/portage/sys-libs/glibc-2.33-r1/work/glibc-2.33/nptl/pthread_create.c:482) > __clone+0x3f (../sysdeps/unix/sysv/linux/x86_64/clone.S:97) > > The stack entries related to libcamera are missing source file name and > line information, which will be investigated separately, but this is > still an improvement. > > Use libdw when available, falling back to backtrace_symbols() otherwise, > or if libdw fails for any reason. > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> I agree with Kieran, that an addition to the dependencies page with a debug section would be good. Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> > --- > src/libcamera/base/backtrace.cpp | 120 +++++++++++++++++++++++++++++++ > src/libcamera/base/meson.build | 7 ++ > 2 files changed, 127 insertions(+) > > diff --git a/src/libcamera/base/backtrace.cpp b/src/libcamera/base/backtrace.cpp > index c010a7e42e4d..79e4a31f3d21 100644 > --- a/src/libcamera/base/backtrace.cpp > +++ b/src/libcamera/base/backtrace.cpp > @@ -12,9 +12,16 @@ > #include <stdlib.h> > #endif > > +#ifdef HAVE_DW > +#include <cxxabi.h> > +#include <elfutils/libdwfl.h> > +#include <unistd.h> > +#endif > + > #include <sstream> > > #include <libcamera/base/span.h> > +#include <libcamera/base/utils.h> > > /** > * \file backtrace.h > @@ -23,6 +30,101 @@ > > namespace libcamera { > > +namespace { > + > +#if HAVE_DW > +class DwflParser > +{ > +public: > + DwflParser(); > + ~DwflParser(); > + > + bool isValid() const { return valid_; } > + std::string stackEntry(const void *ip); > + > +private: > + Dwfl_Callbacks callbacks_; > + Dwfl *dwfl_; > + bool valid_; > +}; > + > +DwflParser::DwflParser() > + : callbacks_({}), dwfl_(nullptr), valid_(false) > +{ > + callbacks_.find_elf = dwfl_linux_proc_find_elf; > + callbacks_.find_debuginfo = dwfl_standard_find_debuginfo; > + > + dwfl_ = dwfl_begin(&callbacks_); > + if (!dwfl_) > + return; > + > + int ret = dwfl_linux_proc_report(dwfl_, getpid()); > + if (ret) > + return; > + > + ret = dwfl_report_end(dwfl_, nullptr, nullptr); > + if (ret) > + return; > + > + valid_ = true; > +} > + > +DwflParser::~DwflParser() > +{ > + if (dwfl_) > + dwfl_end(dwfl_); > +} > + > +std::string DwflParser::stackEntry(const void *ip) > +{ > + Dwarf_Addr addr = reinterpret_cast<Dwarf_Addr>(ip); > + > + Dwfl_Module *module = dwfl_addrmodule(dwfl_, addr); > + if (!module) > + return std::string(); > + > + std::ostringstream entry; > + > + GElf_Off offset; > + GElf_Sym sym; > + const char *symbol = dwfl_module_addrinfo(module, addr, &offset, &sym, > + nullptr, nullptr, nullptr); > + if (symbol) { > + char *name = abi::__cxa_demangle(symbol, nullptr, nullptr, nullptr); > + entry << (name ? name : symbol) << "+0x" << std::hex << offset > + << std::dec; > + free(name); > + } else { > + entry << "??? [" << utils::hex(addr) << "]"; > + } > + > + entry << " ("; > + > + Dwfl_Line *line = dwfl_module_getsrc(module, addr); > + if (line) { > + const char *filename; > + int lineNumber = 0; > + > + filename = dwfl_lineinfo(line, &addr, &lineNumber, nullptr, > + nullptr, nullptr); > + > + entry << (filename ? filename : "???") << ":" << lineNumber; > + } else { > + const char *filename = nullptr; > + > + dwfl_module_info(module, nullptr, nullptr, nullptr, nullptr, > + nullptr, &filename, nullptr); > + > + entry << (filename ? filename : "???") << " [" << utils::hex(addr) << "]"; > + } > + > + entry << ")"; > + return entry.str(); > +} > +#endif /* HAVE_DW */ > + > +} /* namespace */ > + > /** > * \class Backtrace > * \brief Representation of a call stack backtrace > @@ -85,6 +187,24 @@ std::string Backtrace::toString(unsigned int skipLevels) const > if (backtrace_.size() <= skipLevels) > return std::string(); > > +#if HAVE_DW > + DwflParser dwfl; > + > + if (dwfl.isValid()) { > + std::ostringstream msg; > + > + Span<void *const> trace{ backtrace_ }; > + for (const void *ip : trace.subspan(skipLevels)) { > + if (ip) > + msg << dwfl.stackEntry(ip) << std::endl; > + else > + msg << "???" << std::endl; > + } > + > + return msg.str(); > + } > +#endif > + > #if HAVE_BACKTRACE > Span<void *const> trace{ backtrace_ }; > trace = trace.subspan(skipLevels); > diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build > index fc00296dfa8a..4c44b9f55011 100644 > --- a/src/libcamera/base/meson.build > +++ b/src/libcamera/base/meson.build > @@ -19,13 +19,20 @@ libcamera_base_sources = files([ > 'utils.cpp', > ]) > > +libdw = cc.find_library('libdw', required : false) > + > if cc.has_header_symbol('execinfo.h', 'backtrace') > config_h.set('HAVE_BACKTRACE', 1) > endif > > +if libdw.found() > + config_h.set('HAVE_DW', 1) > +endif > + > libcamera_base_deps = [ > dependency('threads'), > libatomic, > + libdw, > ] > > # Internal components must use the libcamera_base_private dependency to enable > -- > Regards, > > Laurent Pinchart >
diff --git a/src/libcamera/base/backtrace.cpp b/src/libcamera/base/backtrace.cpp index c010a7e42e4d..79e4a31f3d21 100644 --- a/src/libcamera/base/backtrace.cpp +++ b/src/libcamera/base/backtrace.cpp @@ -12,9 +12,16 @@ #include <stdlib.h> #endif +#ifdef HAVE_DW +#include <cxxabi.h> +#include <elfutils/libdwfl.h> +#include <unistd.h> +#endif + #include <sstream> #include <libcamera/base/span.h> +#include <libcamera/base/utils.h> /** * \file backtrace.h @@ -23,6 +30,101 @@ namespace libcamera { +namespace { + +#if HAVE_DW +class DwflParser +{ +public: + DwflParser(); + ~DwflParser(); + + bool isValid() const { return valid_; } + std::string stackEntry(const void *ip); + +private: + Dwfl_Callbacks callbacks_; + Dwfl *dwfl_; + bool valid_; +}; + +DwflParser::DwflParser() + : callbacks_({}), dwfl_(nullptr), valid_(false) +{ + callbacks_.find_elf = dwfl_linux_proc_find_elf; + callbacks_.find_debuginfo = dwfl_standard_find_debuginfo; + + dwfl_ = dwfl_begin(&callbacks_); + if (!dwfl_) + return; + + int ret = dwfl_linux_proc_report(dwfl_, getpid()); + if (ret) + return; + + ret = dwfl_report_end(dwfl_, nullptr, nullptr); + if (ret) + return; + + valid_ = true; +} + +DwflParser::~DwflParser() +{ + if (dwfl_) + dwfl_end(dwfl_); +} + +std::string DwflParser::stackEntry(const void *ip) +{ + Dwarf_Addr addr = reinterpret_cast<Dwarf_Addr>(ip); + + Dwfl_Module *module = dwfl_addrmodule(dwfl_, addr); + if (!module) + return std::string(); + + std::ostringstream entry; + + GElf_Off offset; + GElf_Sym sym; + const char *symbol = dwfl_module_addrinfo(module, addr, &offset, &sym, + nullptr, nullptr, nullptr); + if (symbol) { + char *name = abi::__cxa_demangle(symbol, nullptr, nullptr, nullptr); + entry << (name ? name : symbol) << "+0x" << std::hex << offset + << std::dec; + free(name); + } else { + entry << "??? [" << utils::hex(addr) << "]"; + } + + entry << " ("; + + Dwfl_Line *line = dwfl_module_getsrc(module, addr); + if (line) { + const char *filename; + int lineNumber = 0; + + filename = dwfl_lineinfo(line, &addr, &lineNumber, nullptr, + nullptr, nullptr); + + entry << (filename ? filename : "???") << ":" << lineNumber; + } else { + const char *filename = nullptr; + + dwfl_module_info(module, nullptr, nullptr, nullptr, nullptr, + nullptr, &filename, nullptr); + + entry << (filename ? filename : "???") << " [" << utils::hex(addr) << "]"; + } + + entry << ")"; + return entry.str(); +} +#endif /* HAVE_DW */ + +} /* namespace */ + /** * \class Backtrace * \brief Representation of a call stack backtrace @@ -85,6 +187,24 @@ std::string Backtrace::toString(unsigned int skipLevels) const if (backtrace_.size() <= skipLevels) return std::string(); +#if HAVE_DW + DwflParser dwfl; + + if (dwfl.isValid()) { + std::ostringstream msg; + + Span<void *const> trace{ backtrace_ }; + for (const void *ip : trace.subspan(skipLevels)) { + if (ip) + msg << dwfl.stackEntry(ip) << std::endl; + else + msg << "???" << std::endl; + } + + return msg.str(); + } +#endif + #if HAVE_BACKTRACE Span<void *const> trace{ backtrace_ }; trace = trace.subspan(skipLevels); diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build index fc00296dfa8a..4c44b9f55011 100644 --- a/src/libcamera/base/meson.build +++ b/src/libcamera/base/meson.build @@ -19,13 +19,20 @@ libcamera_base_sources = files([ 'utils.cpp', ]) +libdw = cc.find_library('libdw', required : false) + if cc.has_header_symbol('execinfo.h', 'backtrace') config_h.set('HAVE_BACKTRACE', 1) endif +if libdw.found() + config_h.set('HAVE_DW', 1) +endif + libcamera_base_deps = [ dependency('threads'), libatomic, + libdw, ] # Internal components must use the libcamera_base_private dependency to enable
libdw provides access to debugging information. This allows creating better stack trace entries, with file names and line numbers, but also with demangled symbols as the symbol name is available and can be passed to abi::__cxa_demangle(). With libdw, the backtrace previously generated by backtrace_symbols() src/cam/../libcamera/libcamera.so(_ZN9libcamera14VimcCameraData4initEv+0xbd) [0x7f7dbb73222d] src/cam/../libcamera/libcamera.so(_ZN9libcamera19PipelineHandlerVimc5matchEPNS_16DeviceEnumeratorE+0x3e0) [0x7f7dbb731c40] src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private22createPipelineHandlersEv+0x1a7) [0x7f7dbb5ea027] src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private4initEv+0x98) [0x7f7dbb5e9dc8] src/cam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private3runEv+0x9f) [0x7f7dbb5e9c5f] src/cam/../libcamera/base/libcamera-base.so(_ZN9libcamera6Thread11startThreadEv+0xee) [0x7f7dbb3e95be] src/cam/../libcamera/base/libcamera-base.so(+0x4f9d7) [0x7f7dbb3ec9d7] src/cam/../libcamera/base/libcamera-base.so(+0x4f90e) [0x7f7dbb3ec90e] src/cam/../libcamera/base/libcamera-base.so(+0x4f2c2) [0x7f7dbb3ec2c2] /lib64/libpthread.so.0(+0x7e8e) [0x7f7dbab65e8e] /lib64/libc.so.6(clone+0x3f) [0x7f7dbb10b26f] becomes libcamera::VimcCameraData::init()+0xbd (src/libcamera/libcamera.so [0x00007f9de605b22d]) libcamera::PipelineHandlerVimc::match(libcamera::DeviceEnumerator*)+0x3e0 (src/libcamera/libcamera.so [0x00007f9de605ac40]) libcamera::CameraManager::Private::createPipelineHandlers()+0x1a7 (src/libcamera/libcamera.so [0x00007f9de5f13027]) libcamera::CameraManager::Private::init()+0x98 (src/libcamera/libcamera.so [0x00007f9de5f12dc8]) libcamera::CameraManager::Private::run()+0x9f (src/libcamera/libcamera.so [0x00007f9de5f12c5f]) libcamera::Thread::startThread()+0xee (src/libcamera/base/libcamera-base.so [0x00007f9de5d125be]) decltype(*(std::__1::forward<libcamera::Thread*>(fp0)).*fp()) std::__1::__invoke<void (libcamera::Thread::*)(), libcamera::Thread*, void>(void (libcamera::Thread::*&&)(), libcamera::Thread*&&)+0x77 (src/libcamera/base/libcamera-base.so [0x00007f9de5d159d7]) void std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*, 2ul>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*>&, std::__1::__tuple_indices<2ul>)+0x3e (src/libcamera/base/libcamera-base.so [0x00007f9de5d1590e]) void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (libcamera::Thread::*)(), libcamera::Thread*> >(void*)+0x62 (src/libcamera/base/libcamera-base.so [0x00007f9de5d152c2]) start_thread+0xde (/var/tmp/portage/sys-libs/glibc-2.33-r1/work/glibc-2.33/nptl/pthread_create.c:482) __clone+0x3f (../sysdeps/unix/sysv/linux/x86_64/clone.S:97) The stack entries related to libcamera are missing source file name and line information, which will be investigated separately, but this is still an improvement. Use libdw when available, falling back to backtrace_symbols() otherwise, or if libdw fails for any reason. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> --- src/libcamera/base/backtrace.cpp | 120 +++++++++++++++++++++++++++++++ src/libcamera/base/meson.build | 7 ++ 2 files changed, 127 insertions(+)