令人困惑的回溯表明存在不符合预期的递归调用

问题描述 投票:0回答:1

我的程序很少发生崩溃,只有在触发

STR
时才会偶尔发生(
STR
是在
QNX
操作系统中暂停程序并恢复程序)。以下回溯令人困惑,因为它表明
qnx_slog2::log_output()
正在调用自身,这是不可能的,因为相应的代码不是递归函数。

下面的回溯真的让我很困惑。

  1. 回溯表明
    qnx_slog2::log_output()
    调用自身,这是不可能的,因为相应的代码不是递归函数。
  2. 第二次调用
    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++ segmentation-fault crash gdb qnx
1个回答
0
投票

我始终确保我的 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();
}
© www.soinside.com 2019 - 2024. All rights reserved.