From patchwork Fri Sep 24 10:23:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13920 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 3EF2EBDC71 for ; Fri, 24 Sep 2021 10:23:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C6AEB6918E; Fri, 24 Sep 2021 12:23:32 +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="D7vZAUYh"; 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 6678969188 for ; Fri, 24 Sep 2021 12:23:30 +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 F38F058B for ; Fri, 24 Sep 2021 12:23:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1632479010; bh=F2IZoMWoF4+WtC28hewYrMWvgMxM14jREP/nnwd5h0E=; h=From:To:Subject:Date:In-Reply-To:References:From; b=D7vZAUYhW2BxKfy1gzjel9XbIjs/QPYFPxkrSDeXMnBq/ekPSoPfk2dBgUI/uWYYw HP7GSrMhdOMHcH5J0E494RvElioOUl7P3kZ2XKy3m8Kh/OXOZMf1yVXd+Z28PR4EUM 1dMgf4ByH5XBHiPzNSEeqM094cCicO3mmqTaqmxU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Sep 2021 13:23:20 +0300 Message-Id: <20210924102323.26787-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210924102323.26787-1-laurent.pinchart@ideasonboard.com> References: <20210924102323.26787-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v1 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 --- 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 | 25 ++----- src/libcamera/base/meson.build | 5 ++ 6 files changed, 153 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..913f7ba71b03 --- /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 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..272c440589d4 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,20 @@ 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()) + 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 8e125744d823..85af01a19365 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 Fri Sep 24 10:23:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13921 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 6E9B5BDC71 for ; Fri, 24 Sep 2021 10:23:35 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 165886918A; Fri, 24 Sep 2021 12:23:34 +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="Mv0NbmSH"; 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 A8E9D687DD for ; Fri, 24 Sep 2021 12:23:30 +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 4D31345E for ; Fri, 24 Sep 2021 12:23:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1632479010; bh=fGGdyBzvJvEBL0jjFXK/4Fz29pbIasDtOqBedTq5wQI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=Mv0NbmSHyqtQqaJT3UYMenCEAw8fO5bTA6Pvm2EFhEB86ZLmnGUCOs7gxBrxXHv1s eij4gmagkUKh/sZ68l0PjlQ/VRqIOy1ic35wR5/UejmZ/XDxlrsSqE93SL07UUdmbL IrWCgJojRRB74BGtUzHVBQEKxYLuqxPR9UTXyYL4= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Sep 2021 13:23:21 +0300 Message-Id: <20210924102323.26787-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210924102323.26787-1-laurent.pinchart@ideasonboard.com> References: <20210924102323.26787-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v1 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 --- 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 913f7ba71b03..011f2e428d5d 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 85af01a19365..1fa894cf1896 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 Fri Sep 24 10:23:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13922 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 1FFD0BDC71 for ; Fri, 24 Sep 2021 10:23:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DD59769192; Fri, 24 Sep 2021 12:23:34 +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="G/M8QDbw"; 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 04DF569188 for ; Fri, 24 Sep 2021 12:23:31 +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 9D93158B for ; Fri, 24 Sep 2021 12:23:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1632479010; bh=evYR6+uZ6P7pXUXp9HzemyUQDU+nfKG1LrzBvG6LjtI=; h=From:To:Subject:Date:In-Reply-To:References:From; b=G/M8QDbwS9x7NtGDx3eR8fpcAulbvG23ef+FsPsB/I7FYz73KhQadrMGKrgGBBt7v P17128+SwwxhNSBahTRX5DcM6eFPnpNFpYcm7gdW+YjhBgNCMUb2zNV6RAYcodTbrW aEPRpOVe8QCMRtxf07ffLe9Hi+On79s6dzQUgrhk= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Sep 2021 13:23:22 +0300 Message-Id: <20210924102323.26787-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210924102323.26787-1-laurent.pinchart@ideasonboard.com> References: <20210924102323.26787-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v1 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 --- include/libcamera/base/backtrace.h | 3 ++ src/libcamera/base/backtrace.cpp | 61 ++++++++++++++++++++++++++++-- src/libcamera/base/meson.build | 6 +++ 3 files changed, 67 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 011f2e428d5d..40fa60d0f9bf 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,15 @@ 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(); +} + +bool Backtrace::backtraceTrace() { #if HAVE_BACKTRACE backtrace_.resize(32); @@ -153,10 +171,44 @@ 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 +} + +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 +233,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 1fa894cf1896..8d14a65648fc 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 Fri Sep 24 10:23:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 13923 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 1B4B0BDC71 for ; Fri, 24 Sep 2021 10:23:38 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C519169191; Fri, 24 Sep 2021 12:23:37 +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="ASlSd3Xi"; 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 570FC6918F for ; Fri, 24 Sep 2021 12:23:31 +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 ECD4745E for ; Fri, 24 Sep 2021 12:23:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1632479011; bh=DV65S1xYiEwTtJ61OiM65BaM26DK9IRxVwpdz4kQWdg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=ASlSd3XiPPbub3RJfEBjCS/jljYTCvJkkSNl5C0r2uZgO6bthTwrvmE+aOOVFYW2k g/N3+o9hfgN0Hn8vZMW22wfwzIm7B2UKZNmLd+aKfgJDeFRYEpSYM4nGPsjCoM/JUx Bw3sNj71aKVb4qQzbb5vP08imYwdfnYtQ8cSyDGs= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Sep 2021 13:23:23 +0300 Message-Id: <20210924102323.26787-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210924102323.26787-1-laurent.pinchart@ideasonboard.com> References: <20210924102323.26787-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v1 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 --- 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 40fa60d0f9bf..bbb0cc0e17da 100644 --- a/src/libcamera/base/backtrace.cpp +++ b/src/libcamera/base/backtrace.cpp @@ -196,6 +196,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) { @@ -204,6 +210,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; @@ -239,9 +268,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;