libcamera: Print backtrace on fatal errors

When a fatal error occurs the program aborts, and all the logger
provides is the location of the line that caused the error. Extend this
with a full backtrace to help debugging.

The backtrace is generated using the backtrace() call, a GNU extension
to the C library. It is available in glibc and uClibc but not in musl.
Test for availability of the function to condition compilation of the
backtrace printing. Implementing backtrace support with musl is an
exercise left to the reader if desired.

The LogOutput class is extended to support writing string messages
directly to the output. Strings written directly will be considered as
LogDebug messages when written to the Syslog.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
Laurent Pinchart 2019-11-23 11:20:29 +02:00
parent a1225b838f
commit 442f516c62
2 changed files with 79 additions and 18 deletions

View file

@ -26,6 +26,10 @@ libcamera_version = libcamera_git_version.split('+')[0]
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
config_h = configuration_data() config_h = configuration_data()
if cc.has_header_symbol('execinfo.h', 'backtrace')
config_h.set('HAVE_BACKTRACE', 1)
endif
if cc.has_header_symbol('stdlib.h', 'secure_getenv', prefix : '#define _GNU_SOURCE') if cc.has_header_symbol('stdlib.h', 'secure_getenv', prefix : '#define _GNU_SOURCE')
config_h.set('HAVE_SECURE_GETENV', 1) config_h.set('HAVE_SECURE_GETENV', 1)
endif endif

View file

@ -7,6 +7,9 @@
#include "log.h" #include "log.h"
#if HAVE_BACKTRACE
#include <execinfo.h>
#endif
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <list> #include <list>
@ -108,10 +111,11 @@ public:
bool isValid() const; bool isValid() const;
void write(const LogMessage &msg); void write(const LogMessage &msg);
void write(const std::string &msg);
private: private:
void writeSyslog(const LogMessage &msg); void writeSyslog(LogSeverity severity, const std::string &msg);
void writeStream(const LogMessage &msg); void writeStream(const std::string &msg);
std::ostream *stream_; std::ostream *stream_;
LoggingTarget target_; LoggingTarget target_;
@ -181,33 +185,54 @@ bool LogOutput::isValid() const
*/ */
void LogOutput::write(const LogMessage &msg) void LogOutput::write(const LogMessage &msg)
{ {
std::string str;
switch (target_) { switch (target_) {
case LoggingTargetSyslog: case LoggingTargetSyslog:
writeSyslog(msg); str = std::string(log_severity_name(msg.severity())) + " "
+ msg.category().name() + " " + msg.fileInfo() + " "
+ msg.msg();
writeSyslog(msg.severity(), str);
break; break;
case LoggingTargetStream: case LoggingTargetStream:
case LoggingTargetFile: case LoggingTargetFile:
writeStream(msg); str = "[" + utils::time_point_to_string(msg.timestamp()) + "]"
+ log_severity_name(msg.severity()) + " "
+ msg.category().name() + " " + msg.fileInfo() + " "
+ msg.msg();
writeStream(str);
break; break;
default: default:
break; break;
} }
} }
void LogOutput::writeSyslog(const LogMessage &msg) /**
* \brief Write string to log output
* \param[in] str String to write
*/
void LogOutput::write(const std::string &str)
{ {
std::string str = std::string(log_severity_name(msg.severity())) + " " + switch (target_) {
msg.category().name() + " " + msg.fileInfo() + " " + case LoggingTargetSyslog:
msg.msg(); writeSyslog(LogDebug, str);
syslog(log_severity_to_syslog(msg.severity()), "%s", str.c_str()); break;
case LoggingTargetStream:
case LoggingTargetFile:
writeStream(str);
break;
default:
break;
}
} }
void LogOutput::writeStream(const LogMessage &msg) void LogOutput::writeSyslog(LogSeverity severity, const std::string &str)
{
syslog(log_severity_to_syslog(severity), "%s", str.c_str());
}
void LogOutput::writeStream(const std::string &str)
{ {
std::string str = "[" + utils::time_point_to_string(msg.timestamp()) +
"]" + log_severity_name(msg.severity()) + " " +
msg.category().name() + " " + msg.fileInfo() + " " +
msg.msg();
stream_->write(str.c_str(), str.size()); stream_->write(str.c_str(), str.size());
stream_->flush(); stream_->flush();
} }
@ -223,6 +248,7 @@ public:
static Logger *instance(); static Logger *instance();
void write(const LogMessage &msg); void write(const LogMessage &msg);
void backtrace();
int logSetFile(const char *path); int logSetFile(const char *path);
int logSetStream(std::ostream *stream); int logSetStream(std::ostream *stream);
@ -240,9 +266,6 @@ private:
void registerCategory(LogCategory *category); void registerCategory(LogCategory *category);
void unregisterCategory(LogCategory *category); void unregisterCategory(LogCategory *category);
void writeSyslog(const LogMessage &msg);
void writeStream(const LogMessage &msg);
std::unordered_set<LogCategory *> categories_; std::unordered_set<LogCategory *> categories_;
std::list<std::pair<std::string, LogSeverity>> levels_; std::list<std::pair<std::string, LogSeverity>> levels_;
@ -370,6 +393,38 @@ void Logger::write(const LogMessage &msg)
output->write(msg); output->write(msg);
} }
/**
* \brief Write a backtrace to the log
*/
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, ARRAY_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 method and
* ~LogMessage().
*/
for (int i = 2; i < num_entries; ++i)
msg << strings[i] << std::endl;
output->write(msg.str());
free(strings);
#endif
}
/** /**
* \brief Set the log file * \brief Set the log file
* \param[in] path Full path to the log file * \param[in] path Full path to the log file
@ -783,8 +838,10 @@ LogMessage::~LogMessage()
if (severity_ >= category_.severity()) if (severity_ >= category_.severity())
Logger::instance()->write(*this); Logger::instance()->write(*this);
if (severity_ == LogSeverity::LogFatal) if (severity_ == LogSeverity::LogFatal) {
Logger::instance()->backtrace();
std::abort(); std::abort();
}
} }
/** /**