summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libcamera/base/backtrace.h34
-rw-r--r--include/libcamera/base/meson.build1
-rw-r--r--meson.build4
-rw-r--r--src/libcamera/base/backtrace.cpp107
-rw-r--r--src/libcamera/base/log.cpp27
-rw-r--r--src/libcamera/base/meson.build5
6 files changed, 155 insertions, 23 deletions
diff --git a/include/libcamera/base/backtrace.h b/include/libcamera/base/backtrace.h
new file mode 100644
index 00000000..aefc76de
--- /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 9feb4b93..525aba9d 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 a49c484f..dfed01ba 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 00000000..c010a7e4
--- /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 a3e3f9ea..64813b66 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;
-
- output->write(msg.str());
+ std::string backtrace = Backtrace().toString(2);
+ if (backtrace.empty()) {
+ output->write("Backtrace not available\n");
+ return;
+ }
- 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 d17249a0..fc00296d 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,