我的程序很少发生崩溃,只有在触发
STR
时才会偶尔发生(STR
是在QNX
操作系统中暂停程序并恢复程序)。以下回溯令人困惑,因为它表明 qnx_slog2::log_output()
正在调用自身,这是不可能的,因为相应的代码不是递归函数。
下面的回溯真的让我很困惑。
qnx_slog2::log_output()
调用自身,这是不可能的,因为相应的代码不是递归函数。this
时,qnx_slog2::log_output
的地址为0x2,这不是实例的有效地址。(gdb) bt
#0 0x0000003ae09b5cc0 in ?? ()
#1 0x0000001b5319cf64 in qnx_slog2::log_output (this=0x2, level=1, fmt=0x3ae09c9138 ,level=1)
at /home/jhone/qnx_slog2.hpp:137
#2 0x0000001b5319cf64 in qnx_slog2::log_output (this=0x1b531e9048 <gnx slog2::get log()::slog2instance>, level=1,
fmt=0x2ec2c7fbd0 "[st_slog2] 0MS-E oms_result_sender.cpp:174 operator()() soa ges dynamic rect width:0, height:0,x:0,y", level=1)
at /home/jhone/qnx_slog2.hpp:137
#3 0x0000003aed61fcc in malloc_lock.constprop.4 ()
from //home/jhone/publish/lib/libc.so.5
#4 0x6c757365725f736d in ??()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
这是代码 qnx_slog2.hpp:第 128 至 149 行
void log_output(short level, const char* fmt, ...) {
if (true == log_block(level)) {
return;
}
std::unique_lock<std::mutex> lock(lock_);
va_list args;
va_start(args, fmt);
switch (log_type_) {
case LOG_TYPE_QNX:
if ((fmt != nullptr) && (match_level(level) > 0) && (*fmt != '\0')) { //line 137
vslog2f(nullptr, log_id_, match_level(level), fmt, args);
}
break;
case LOG_TYPE_PRINTF: {
memset(print_buffer_, 0, sizeof(print_buffer_));
vsnprintf(print_buffer_, sizeof(print_buffer_), fmt, args);
log_print(level);
break;
}
}
va_end(args);
}
根据官方文档,
vslog2f
是线程安全的。
感谢@Retired Ninja 的提示。 初始化实例时首先调用
slog2_set_default_buffer
。这是相应的代码片段:
class qnx_slog2 {
private:
short log_type_ = LOG_TYPE_QNX;
short log_level_ = LOG_DEBUG;
int log_page_num_ = 4;
int log_id_ = 116;
std::mutex lock_;
char print_buffer_[1024] = {0};
slog2_buffer_t buf = {0};
public:
bool init() {
if (log_type_ != LOG_TYPE_QNX) {
return true;
}
std::unique_lock<std::mutex> lock(lock_);
slog2_buffer_set_config_t config{};
config.buffer_set_name = __progname;
config.num_buffers = 1;
config.verbosity_level = SLOG2_DEBUG1;
config.buffer_config[0].buffer_name = LOG_TAG;
config.buffer_config[0].num_pages =
log_page_num_; // one page 4KB
if (0 == slog2_register(&config, &buf, SLOG2_DISCARD_NEWLINE)) {
slog2_set_default_buffer(buf);
return true;
}
return false;
}
//omit not important code
}
回复格式说明符与参数类型不匹配的潜在问题。
fmt
库 生成的,该库被 spdlog
等广泛使用。换句话说,正如回溯指示 qnx_slog2::log_output
的输入参数始终是: short level
,然后是 const char*
,仅此而已。 下面是一个简单的代码片段,以便于理解。
std::string log_msg = fmt::format(" soa ges dynamic rect width:0, height:0,x:0,y", width, height, loc_x, loc_y);
qnx_slog2::get_instance().log_output(log_level, log_msg.c_str());
有人可以阐明如何逐步解决这个问题吗?我真的不知道先做什么。
我始终确保我的 C++ 代码和遗留“C”样式编码(或任何其他语言)之间有一个 C++ 适配器层。这样我就可以确保尽可能长时间地使用所有最新的 C++ 工具。
#include <format>
#include <string_view>
// following two includes are only there to be able
// to simulate "C" style logging api
#include <stdio.h>
#include <stdarg.h>
// legacy "C" style API for logging
void log_output_legacy(short level, const char* fmt, ...)
{
va_list argList;
va_start(argList, fmt);
vprintf(fmt, argList);
va_end(argList);
}
// Your new variadic log formatting function
// making use of the std::vformat
template<typename... args_t>
void log(short level, std::string_view fmt, args_t&&... args)
{
auto message = std::vformat(fmt,std::make_format_args(args...));
// use the formatted string
log_output_legacy(level,"%i : %s", level, message.c_str());
}
// setup an abstract base class for reporting back to a log system
// Side note logging directly from your code is not recommended, make sure you can do something with dependency injection.
// I call it reporting, because logging is just one possible implementation
class report_itf
{
public:
virtual ~report_itf() = default;
virtual void report_something_bad_happened() const noexcept = 0;
};
// for unit testing do not log at all
// to prevent tests becoming dependent on
// correct logging configuration etc.
class no_reporting_t : public report_itf
{
public:
virtual void report_something_bad_happened() const noexcept override {}
};
// for production code
class legacy_reporting_t : public report_itf
{
public:
virtual void report_something_bad_happened() const noexcept override
{
log(2, "something bad happened\n");
}
};
// when code needs to log, inject the report interface
class some_class_t
{
public:
some_class_t( report_itf& reporting) :
m_reporting{reporting}
{
};
void do_something()
{
m_reporting.report_something_bad_happened();
}
private:
const report_itf& m_reporting;
};
int main()
{
log(1,"{} {} {}\n", "hello", "world!", 123);
legacy_reporting_t reporting; // for unit test use no_reporting
some_class_t some_class{reporting};
some_class.do_something();
}