From patchwork Sun Oct 3 22:36:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 14028 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id BEAEBC3243 for ; Sun, 3 Oct 2021 22:36:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2E986691C2; Mon, 4 Oct 2021 00:36:17 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fkMk2Dz0"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 15C56691AB for ; Mon, 4 Oct 2021 00:36:15 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 97B58BBE for ; Mon, 4 Oct 2021 00:36:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1633300574; bh=khgFm8zw3UkfDQoi7PC5aiZoF4KM6T1HhcidNZwiCJc=; h=From:To:Subject:Date:In-Reply-To:References:From; b=fkMk2Dz0WRIJZYresQhtxMy2rbpaA7LfcvRusX/pOxC3Xr1h2L+Jb0ujgwbspiuW5 yCIUae0K/eFDbf00CTou/j5QdajrHbJP14xkbu4af/wA6XRmP+bw1GhacJB2rHe6QQ 2Stc8qZaD3mkzXGvbTwwP66G1iXUkiWZFdmvmvH8= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Mon, 4 Oct 2021 01:36:03 +0300 Message-Id: <20211003223606.20016-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20211003223606.20016-1-laurent.pinchart@ideasonboard.com> References: <20211003223606.20016-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 1/4] libcamera: base: Add Backtrace class X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Create a new class to abstract generation and access to call stack backtraces. The current implementation depends on the glibc backtrace() implementation and is copied from the logger. Future development will bring support for libunwind, transparently for the users of the class. The logger backtrace implementation is dropped, replaced by usage of the new Backtrace class. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- Changes since v1: - Print message when backtrace isn't available --- include/libcamera/base/backtrace.h | 34 +++++++++ include/libcamera/base/meson.build | 1 + meson.build | 4 -- src/libcamera/base/backtrace.cpp | 107 +++++++++++++++++++++++++++++ src/libcamera/base/log.cpp | 27 +++----- src/libcamera/base/meson.build | 5 ++ 6 files changed, 155 insertions(+), 23 deletions(-) create mode 100644 include/libcamera/base/backtrace.h create mode 100644 src/libcamera/base/backtrace.cpp diff --git a/include/libcamera/base/backtrace.h b/include/libcamera/base/backtrace.h new file mode 100644 index 000000000000..aefc76defa83 --- /dev/null +++ b/include/libcamera/base/backtrace.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Ideas on Board Oy + * + * backtrace.h - Call stack backtraces + */ +#ifndef __LIBCAMERA_BASE_BACKTRACE_H__ +#define __LIBCAMERA_BASE_BACKTRACE_H__ + +#include +#include + +#include + +#include + +namespace libcamera { + +class Backtrace +{ +public: + Backtrace(); + + std::string toString(unsigned int skipLevels = 0) const; + +private: + LIBCAMERA_DISABLE_COPY(Backtrace) + + std::vector backtrace_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_BASE_BACKTRACE_H__ */ diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build index 9feb4b9346d5..525aba9d2919 100644 --- a/include/libcamera/base/meson.build +++ b/include/libcamera/base/meson.build @@ -3,6 +3,7 @@ libcamera_base_include_dir = libcamera_include_dir / 'base' libcamera_base_headers = files([ + 'backtrace.h', 'bound_method.h', 'class.h', 'event_dispatcher.h', diff --git a/meson.build b/meson.build index a49c484fe64e..dfed01ba26bc 100644 --- a/meson.build +++ b/meson.build @@ -29,10 +29,6 @@ cc = meson.get_compiler('c') cxx = meson.get_compiler('cpp') config_h = configuration_data() -if cc.has_header_symbol('execinfo.h', 'backtrace') - config_h.set('HAVE_BACKTRACE', 1) -endif - if cc.has_header_symbol('unistd.h', 'issetugid') config_h.set('HAVE_ISSETUGID', 1) endif diff --git a/src/libcamera/base/backtrace.cpp b/src/libcamera/base/backtrace.cpp new file mode 100644 index 000000000000..c010a7e42e4d --- /dev/null +++ b/src/libcamera/base/backtrace.cpp @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2021, Ideas on Board Oy + * + * backtrace.h - Call stack backtraces + */ + +#include + +#if HAVE_BACKTRACE +#include +#include +#endif + +#include + +#include + +/** + * \file backtrace.h + * \brief Generate call stack backtraces + */ + +namespace libcamera { + +/** + * \class Backtrace + * \brief Representation of a call stack backtrace + * + * The Backtrace class represents a function call stack. Constructing an + * instance captures the call stack at the point the instance is constructed. + * The instance can later be used to access the call stack and to generate a + * human-readable representation with the toString() function. + * + * Depending on the platform, different backends can be used to generate the + * backtrace. The Backtrace class provides a best effort to capture accurate + * backtraces, but doesn't offer any guarantee of a particular backtrace format. + */ + +/** + * \brief Construct a backtrace + * + * The backtrace captures the call stack at the point where it is constructed. + * It can later be converted to a string with toString(). + */ +Backtrace::Backtrace() +{ +#if HAVE_BACKTRACE + backtrace_.resize(32); + + int num_entries = backtrace(backtrace_.data(), backtrace_.size()); + if (num_entries < 0) { + backtrace_.clear(); + return; + } + + backtrace_.resize(num_entries); +#endif +} + +/** + * \brief Convert a backtrace to a string representation + * \param[in] skipLevels Number of initial levels to skip in the backtrace + * + * The string representation of the backtrace is a multi-line string, with one + * line per call stack entry. The format of the entries isn't specified and is + * platform-dependent. + * + * The \a skipLevels parameter indicates how many initial entries to skip from + * the backtrace. This can be used to hide functions that wrap the construction + * of the Backtrace instance from the call stack. The Backtrace constructor + * itself is automatically skipped and never shown in the backtrace. + * + * If backtrace generation fails for any reason (usually because the platform + * doesn't support this feature), an empty string is returned. + * + * \return A string representation of the backtrace, or an empty string if + * backtrace generation isn't possible + */ +std::string Backtrace::toString(unsigned int skipLevels) const +{ + /* Skip the first entry, corresponding to the Backtrace construction. */ + skipLevels += 1; + + if (backtrace_.size() <= skipLevels) + return std::string(); + +#if HAVE_BACKTRACE + Span trace{ backtrace_ }; + trace = trace.subspan(skipLevels); + + char **strings = backtrace_symbols(trace.data(), trace.size()); + if (strings) { + std::ostringstream msg; + + for (unsigned int i = 0; i < trace.size(); ++i) + msg << strings[i] << std::endl; + + free(strings); + return msg.str(); + } +#endif + + return std::string(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/base/log.cpp b/src/libcamera/base/log.cpp index a3e3f9ea2712..64813b6607c5 100644 --- a/src/libcamera/base/log.cpp +++ b/src/libcamera/base/log.cpp @@ -8,9 +8,6 @@ #include #include -#if HAVE_BACKTRACE -#include -#endif #include #include #include @@ -23,6 +20,7 @@ #include +#include #include #include @@ -418,31 +416,22 @@ void Logger::write(const LogMessage &msg) */ void Logger::backtrace() { -#if HAVE_BACKTRACE std::shared_ptr output = std::atomic_load(&output_); if (!output) return; - void *buffer[32]; - int num_entries = ::backtrace(buffer, std::size(buffer)); - char **strings = backtrace_symbols(buffer, num_entries); - if (!strings) - return; - - std::ostringstream msg; - msg << "Backtrace:" << std::endl; - /* * Skip the first two entries that correspond to this function and * ~LogMessage(). */ - for (int i = 2; i < num_entries; ++i) - msg << strings[i] << std::endl; + std::string backtrace = Backtrace().toString(2); + if (backtrace.empty()) { + output->write("Backtrace not available\n"); + return; + } - output->write(msg.str()); - - free(strings); -#endif + output->write("Backtrace:\n"); + output->write(backtrace); } /** diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build index d17249a023e1..fc00296dfa8a 100644 --- a/src/libcamera/base/meson.build +++ b/src/libcamera/base/meson.build @@ -1,6 +1,7 @@ # SPDX-License-Identifier: CC0-1.0 libcamera_base_sources = files([ + 'backtrace.cpp', 'class.cpp', 'bound_method.cpp', 'event_dispatcher.cpp', @@ -18,6 +19,10 @@ libcamera_base_sources = files([ 'utils.cpp', ]) +if cc.has_header_symbol('execinfo.h', 'backtrace') + config_h.set('HAVE_BACKTRACE', 1) +endif + libcamera_base_deps = [ dependency('threads'), libatomic, From patchwork Sun Oct 3 22:36:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 14030 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 9847FC3243 for ; Sun, 3 Oct 2021 22:36:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7348F691C7; Mon, 4 Oct 2021 00:36:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="UrHi4tKC"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 5EBD8691B7 for ; Mon, 4 Oct 2021 00:36:15 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0087BD4C for ; Mon, 4 Oct 2021 00:36:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1633300575; bh=SF9crXhGs8tjbaI9tNhDKbJWGJGwCDdfJhZvgB67+Uw=; h=From:To:Subject:Date:In-Reply-To:References:From; b=UrHi4tKCjJeeDwgTsJ5Vj95oooOHxMvbYlRu7f4Ev2B3mjCN6gdOhiV/4AvxAC+KV yLwRaqfjoepabaH65CH4P1X39ps4uAtvrrlFWzUBr9eT/I+mDKVn3baUIPwjgULN/K aziiPrjIK4453u19aYnBhL7LasT6hXVPUMJPk2Dc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Mon, 4 Oct 2021 01:36:04 +0300 Message-Id: <20211003223606.20016-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20211003223606.20016-1-laurent.pinchart@ideasonboard.com> References: <20211003223606.20016-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/4] libcamera: base: backtrace: Use libdw to provide symbolic names X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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(fp0)).*fp()) std::__1::__invoke(void (libcamera::Thread::*&&)(), libcamera::Thread*&&)+0x77 (src/libcamera/base/libcamera-base.so [0x00007f9de5d159d7]) void std::__1::__thread_execute >, void (libcamera::Thread::*)(), libcamera::Thread*, 2ul>(std::__1::tuple >, void (libcamera::Thread::*)(), libcamera::Thread*>&, std::__1::__tuple_indices<2ul>)+0x3e (src/libcamera/base/libcamera-base.so [0x00007f9de5d1590e]) void* std::__1::__thread_proxy >, 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 Reviewed-by: Kieran Bingham Reviewed-by: Paul Elder --- 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 #endif +#ifdef HAVE_DW +#include +#include +#include +#endif + #include #include +#include /** * \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(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 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 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 From patchwork Sun Oct 3 22:36:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 14031 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id D7C49C3244 for ; Sun, 3 Oct 2021 22:36:19 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 46B2A691BA; Mon, 4 Oct 2021 00:36:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ERIOU/Mi"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id ADFEB691B8 for ; Mon, 4 Oct 2021 00:36:15 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 52D8DA2A for ; Mon, 4 Oct 2021 00:36:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1633300575; bh=LqgZH5BD3bW6HGSkDUq9YXSu/1Vq0OvdVOR6UZM2BOI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=ERIOU/MiyLs8/QAVVmE+crCDpa5uVQDCCaKYm3oHjsLbAZk7USUyAFBYZVaizVnck BCOK/Kic8zY6La2iCntkPXho89Bb3n0y2u1sP01a8u8SJOdWfjzmw7K0apX98bbuKD lB42Vw+Fg6v4nBeAyAk11GdfbJmlqRDtn+u4AXCI= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Mon, 4 Oct 2021 01:36:05 +0300 Message-Id: <20211003223606.20016-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20211003223606.20016-1-laurent.pinchart@ideasonboard.com> References: <20211003223606.20016-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 3/4] libcamera: base: backtrace: Use libunwind when available X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: Kieran Bingham --- Changes since v1: - Use __noinline__ attribute for backtraceTrace() and unwindTrace() --- include/libcamera/base/backtrace.h | 3 ++ src/libcamera/base/backtrace.cpp | 67 ++++++++++++++++++++++++++++-- src/libcamera/base/meson.build | 6 +++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/include/libcamera/base/backtrace.h b/include/libcamera/base/backtrace.h index aefc76defa83..58ccc14c8f81 100644 --- a/include/libcamera/base/backtrace.h +++ b/include/libcamera/base/backtrace.h @@ -26,6 +26,9 @@ public: private: LIBCAMERA_DISABLE_COPY(Backtrace) + bool backtraceTrace(); + bool unwindTrace(); + std::vector backtrace_; }; diff --git a/src/libcamera/base/backtrace.cpp b/src/libcamera/base/backtrace.cpp index 79e4a31f3d21..0aafc6a366c5 100644 --- a/src/libcamera/base/backtrace.cpp +++ b/src/libcamera/base/backtrace.cpp @@ -18,6 +18,15 @@ #include #endif +#if HAVE_UNWIND +/* + * Disable support for remote unwinding to enable a more optimized + * implementation. + */ +#define UNW_LOCAL_ONLY +#include +#endif + #include #include @@ -146,6 +155,20 @@ std::string DwflParser::stackEntry(const void *ip) * It can later be converted to a string with toString(). */ 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 backtrace_.resize(32); @@ -153,10 +176,45 @@ Backtrace::Backtrace() int num_entries = backtrace(backtrace_.data(), backtrace_.size()); if (num_entries < 0) { backtrace_.clear(); - return; + return false; } 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(ip)); + } while (unw_step(&cursor) > 0); + + return true; +#else + return false; #endif } @@ -181,8 +239,11 @@ Backtrace::Backtrace() */ 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) return std::string(); diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build index 4c44b9f55011..05fed7acf561 100644 --- a/src/libcamera/base/meson.build +++ b/src/libcamera/base/meson.build @@ -20,6 +20,7 @@ libcamera_base_sources = files([ ]) libdw = cc.find_library('libdw', required : false) +libunwind = cc.find_library('libunwind', required : false) if cc.has_header_symbol('execinfo.h', 'backtrace') config_h.set('HAVE_BACKTRACE', 1) @@ -29,10 +30,15 @@ if libdw.found() config_h.set('HAVE_DW', 1) endif +if libunwind.found() + config_h.set('HAVE_UNWIND', 1) +endif + libcamera_base_deps = [ dependency('threads'), libatomic, libdw, + libunwind, ] # Internal components must use the libcamera_base_private dependency to enable From patchwork Sun Oct 3 22:36:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 14032 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 53E26C3243 for ; Sun, 3 Oct 2021 22:36:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1ED5E691BD; Mon, 4 Oct 2021 00:36:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="A/lpWgb8"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 06DF9691AB for ; Mon, 4 Oct 2021 00:36:16 +0200 (CEST) Received: from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A01FABBE for ; Mon, 4 Oct 2021 00:36:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1633300575; bh=ki5qP9STRFGGy76kN6KZ6o68HKpO2X2OhTh2EvrumMM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=A/lpWgb8E68h7si/M7mmVpMAwfy8ss4igJLuP83d58SuJckTG4NAkIiUDr3NyNwtX 953YwMYD00rSzSfxz3qZqdMiZM6cbHLU3buC6yhBvktsNxBlEFGqXfEvtzDf+eREqF seRGg8aBhVZALLVJNJQGktgpBP+E1v6slQ6494qM= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Mon, 4 Oct 2021 01:36:06 +0300 Message-Id: <20211003223606.20016-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20211003223606.20016-1-laurent.pinchart@ideasonboard.com> References: <20211003223606.20016-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 4/4] libcamera: base: backtrace: Fallback to libunwind for symbolic names X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" libunwind has an API to provide symbolic names for functions. It's less optimal than using backtrace_symbols() or libdw, as it doesn't allow deferring the symbolic names lookup, but it can be usefull as a fallback if no other option is available. A sample backtrace when falling back to libunwind looks like libcamera::VimcCameraData::init()+0xbd libcamera::PipelineHandlerVimc::match(libcamera::DeviceEnumerator*)+0x3e0 libcamera::CameraManager::Private::createPipelineHandlers()+0x1a7 libcamera::CameraManager::Private::init()+0x98 libcamera::CameraManager::Private::run()+0x9f libcamera::Thread::startThread()+0xee decltype(*(std::__1::forward(fp0)).*fp()) std::__1::__invoke(void (libcamera::Thread::*&&)(), libcamera::Thread*&&)+0x77 void std::__1::__thread_execute >, void (libcamera::Thread::*)(), libcamera::Thread*, 2ul>(std::__1::tuple >, void (libcamera::Thread::*)(), libcamera::Thread*>&, std::__1::__tuple_indices<2ul>)+0x3e void* std::__1::__thread_proxy >, void (libcamera::Thread::*)(), libcamera::Thread*> >(void*)+0x62 start_thread+0xde ??? Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- include/libcamera/base/backtrace.h | 1 + src/libcamera/base/backtrace.cpp | 37 +++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/include/libcamera/base/backtrace.h b/include/libcamera/base/backtrace.h index 58ccc14c8f81..bb77c73b67e3 100644 --- a/include/libcamera/base/backtrace.h +++ b/include/libcamera/base/backtrace.h @@ -30,6 +30,7 @@ private: bool unwindTrace(); std::vector backtrace_; + std::vector backtraceText_; }; } /* namespace libcamera */ diff --git a/src/libcamera/base/backtrace.cpp b/src/libcamera/base/backtrace.cpp index 0aafc6a366c5..d93e5518670d 100644 --- a/src/libcamera/base/backtrace.cpp +++ b/src/libcamera/base/backtrace.cpp @@ -202,6 +202,12 @@ bool Backtrace::unwindTrace() return false; do { +#if HAVE_BACKTRACE || HAVE_DW + /* + * If backtrace() or libdw is available, they will be used in + * toString() to provide symbol information for the stack + * frames using the IP register value. + */ unw_word_t ip; ret = unw_get_reg(&cursor, UNW_REG_IP, &ip); if (ret) { @@ -210,6 +216,29 @@ bool Backtrace::unwindTrace() } backtrace_.push_back(reinterpret_cast(ip)); +#else + /* + * Otherwise, use libunwind to get the symbol information. As + * the libunwind API uses cursors, we can't store the IP values + * and delay symbol lookup to toString(). + */ + char symbol[256]; + unw_word_t offset = 0; + ret = unw_get_proc_name(&cursor, symbol, sizeof(symbol), &offset); + if (ret) { + backtraceText_.emplace_back("???\n"); + continue; + } + + std::ostringstream entry; + + char *name = abi::__cxa_demangle(symbol, nullptr, nullptr, nullptr); + entry << (name ? name : symbol); + free(name); + + entry << "+0x" << std::hex << offset << "\n"; + backtraceText_.emplace_back(entry.str()); +#endif } while (unw_step(&cursor) > 0); return true; @@ -245,9 +274,15 @@ std::string Backtrace::toString(unsigned int skipLevels) const */ skipLevels += 2; - if (backtrace_.size() <= skipLevels) + if (backtrace_.size() <= skipLevels && + backtraceText_.size() <= skipLevels) return std::string(); + if (!backtraceText_.empty()) { + Span trace{ backtraceText_ }; + return utils::join(trace.subspan(skipLevels), ""); + } + #if HAVE_DW DwflParser dwfl;