[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