[libcamera-devel] [PATCH v2 1/4] libcamera: base: Add Backtrace class
paul.elder at ideasonboard.com
paul.elder at ideasonboard.com
Mon Oct 18 05:42:18 CEST 2021
Hi Laurent,
On Mon, Oct 04, 2021 at 01:36:03AM +0300, Laurent Pinchart wrote:
> 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 <laurent.pinchart at ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder at ideasonboard.com>
> ---
> 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 <string>
> +#include <vector>
> +
> +#include <libcamera/base/private.h>
> +
> +#include <libcamera/base/class.h>
> +
> +namespace libcamera {
> +
> +class Backtrace
> +{
> +public:
> + Backtrace();
> +
> + std::string toString(unsigned int skipLevels = 0) const;
> +
> +private:
> + LIBCAMERA_DISABLE_COPY(Backtrace)
> +
> + std::vector<void *> 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 <libcamera/base/backtrace.h>
> +
> +#if HAVE_BACKTRACE
> +#include <execinfo.h>
> +#include <stdlib.h>
> +#endif
> +
> +#include <sstream>
> +
> +#include <libcamera/base/span.h>
> +
> +/**
> + * \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<void *const> 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 <libcamera/base/log.h>
>
> #include <array>
> -#if HAVE_BACKTRACE
> -#include <execinfo.h>
> -#endif
> #include <fstream>
> #include <iostream>
> #include <list>
> @@ -23,6 +20,7 @@
>
> #include <libcamera/logging.h>
>
> +#include <libcamera/base/backtrace.h>
> #include <libcamera/base/thread.h>
> #include <libcamera/base/utils.h>
>
> @@ -418,31 +416,22 @@ void Logger::write(const LogMessage &msg)
> */
> void Logger::backtrace()
> {
> -#if HAVE_BACKTRACE
> std::shared_ptr<LogOutput> 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,
> --
> Regards,
>
> Laurent Pinchart
>
More information about the libcamera-devel
mailing list