summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2021-09-24 04:39:58 +0300
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2021-10-15 05:05:20 +0300
commitbae9d2bdb32ee8cdc717537ae498eee03a25543b (patch)
treec582f59b5bf2a00363d17e4982f64997b9a4ce29 /src
parentca5fb994091e7b3ba6db484ae0a58e2ecd00abbf (diff)
libcamera: base: Add Backtrace class
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@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Diffstat (limited to 'src')
-rw-r--r--src/libcamera/base/backtrace.cpp107
-rw-r--r--src/libcamera/base/log.cpp27
-rw-r--r--src/libcamera/base/meson.build5
3 files changed, 120 insertions, 19 deletions
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,