如何诊断代码段中成员变量随机置零?

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

我不能展示导致这个问题的整个代码(而且我实际上不可能这样做),我认为实际解决这个问题可能会超出这里的范围。所以我要问的问题是如何我可以诊断这个问题。

基本上我有一个看起来像这样的类(请注意我not 能够重现我在 MVCE 中遇到的问题,所以我只是大致展示我在做什么,以便我可以获得帮助我需要调试的工具):

#include <memory>
#include <array>
#include <semaphore> 
#include <thread> 

struct SharedDataStructure{
    SharedDataStructure(){
         for(auto& value : semaphore_array){
             value = std::make_unique<std::unique_ptr<std::counting_semaphore<2>>(2); 
         }
    }
    std::uint32_t get_latest_index(){... calculates some index}; 
    std::array<std::atomic<uint32_t>,16> atomics_member; 
    std::array<std::unique_ptr<std::counting_semaphore<2>, 16> semaphore_array; 
    std::uint32_t dummy0;

}


struct ThreadClass{
    ThreadClass(std::atomic<bool>& stop_flag, SharedDataStructure& shared_data_structure){
        auto thread_function = [&shared_data_structure, control_toke](){
            ...

            ... create socket
            
            std::array<mmsghdr, 1024> msgvec; 
            std::array< iovec, 1024> iovecs; 
            auto thread_socket = socket(...);
            ... //initialize  initilaize mmsghdr and iovecs up here
            ... //set thread_socket options, bind to (0.0.0.0) and some port here etc.. 
            timespec timeout = {}; 
            timeout.tv_sec = 1; 
            while(!stop_flag){
                // up until this point, shared_data_structure.semaphore_array is filled with what appear to be actual normal pointers. 
                auto packet_count = recvmmsg(thread_socket, msgvec.data(), msgvec.size(), 0, timeout); 
                // after this point, shared_data_structure.semaphore_array is filled with nullptrs, as if somethign zeroed all the values out. 
                for(std::size_t i = 0; i < packet_count; ++packet_count){
                    auto index = shared_data_structure.calculate_latest_index(); // in debugger this is 1, so no out of bounds
                    //causes segmentation fault, because for some reason now all the pointers in semaphore_array are nullptr...
                    shared_data_structure.semaphore_array[index].aquire(); 
                    //do something here. 
                    shared_data_structure.semaphore_array[index].release();
                }
            }
        }
        m_thread = std::thread(thread_function); 
    }
    ~ThreadClass(){
        m_thread.join(); 
    }
    std::thread m_thread; 

}

void create_thread_class(std::atomic<bool>& stop_flag){
    SharedDataStructure shared_data_structure;
    ThreadClass thread_class_0(stop_flag, shared_data_structure); 
    //ThreadClass thread_class_1(stop_flag, shared_data_structure); happens whether this is commented out or not. 
    while(!stop_flag.load()){
        //... at this point this just became an empty loop for debugging. 
    }
}
//invoke create_thread_class in a thread on it's own later. 

在这行代码之前:

 auto packet_count = recvmmsg(thread_socket, msgvec.data(), msgvec.size(), 0, timeout);

SharedDataStructure::semaphore_array
包含一堆“真实”指针,就像我初始化的一样。但是之后,所有的指针都变成了 nullptr。请注意,结构中的其他值似乎都没有受到影响。

我什至不知道如何调试它。很明显,

recvmmsg(...)
应该对未在其中使用的类的成员具有 zero 效果。我认为我正在调用某种未定义的行为,但我什至不知道如何找到它。结果看起来类似于缓冲区溢出,但我不明白这将如何影响堆栈变量(我不认为 recvmmsg 在堆栈上做了大量的事情?)。

如何诊断此类问题?

c++ gcc c++20 stack-overflow undefined-behavior
1个回答
0
投票

好的,我按照评论说的做了,发现问题了。首先,我使用 Clion,然后我尝试在那里使用 GDB,它有点工作,但它被卡住了。我会解释我做了什么。

所以我用了命令行。

我用过

(gdb) break my_file.h:[[line where for(std::size_t i = 0; i < packet_count; ++packet_count) is]]  

然后我做了

(gdb) watch shared_data_structure.semaphore_array[0]

然后我运行了程序并尝试观看。起初我得到了一些奇怪的结果。首先,当我单步执行

recvmmsg
函数时,它看起来原来是错误所在,它 jumped
thread_function
lambda 的函数参数,然后返回到
thread_socket
所在的位置。然后,在我再次操作之后,所有东西都在 gdb 中冻结了大约 30 秒(这可能是我放弃使用 Clion 的 GDB 界面的地方)。我认为这与我没有 glibc 的调试信息有关(它早些时候警告过我)但我没有能力在我的系统上安装调试信息的东西,所以虽然这对我有帮助,这不是一个选择。

冻结后,会出现一些 python 错误,关于它没有找到

std::array<std::unique_ptr<std::counting_semaphore<2>, std::default_deleter>, 16>

的构造函数调试表示

但是在它显示此时已经设置了值之后。所以手表工作了。

问题是,我用

bt
运行了一个堆栈跟踪,然后......它只是
recvmmesg
和程序的其余部分,没有更深的东西。

基本上

recvmmsg()
some_std_invoke_stuff()
some_std_thread_stuff()
... all irrelevant out of scope

再次,调试信息可能会有所帮助,但至少在这一点上我知道

recvmmsg
问题 100% 发生了。

之后我使用 Valgrind 检查代码,过了一会儿我看到了一堆错误,比如一堆连续值的“mmsghdr[633] 访问未初始化的字节”。所以我不得不诊断

mmsghdr
初始化。

我没有在上面包括这个,因为再一次,解决我的具体问题不是我想要的(我不得不凭记忆写这个,我不记得了),但基本上这就是初始化的样子:

std::array<mmsghdr, 1024> msgvec; 
std::array<iovec, 1024> iovecs; 
my::aligned_vector<std::byte, buffer_size * n> buffers; 

for(std::size_t i = 0; i < 1024; ++i){
    iovecs[i].iov_base         = (&buffers + (i * buffer_size));
    iovecs[i].iov_len          = buffer_size;
    msgs[i].msg_hdr.msg_iov    = &iovecs[i];
    msgs[i].msg_hdr.msg_iovlen = 1;
}

一开始我觉得可能是我的I迭代太多了,所以断言反对

for(std::size_t i = 0; i < 1024; ++i){
    assert(buffers.size() + (i * buffer_size + buffer_size)); 
    iovecs[i].iov_base         = (&buffers + (i * buffer_size));
    iovecs[i].iov_len          = buffer_size;
    msgs[i].msg_hdr.msg_iov    = &iovecs[i];
    msgs[i].msg_hdr.msg_iovlen = 1;
}

在我意识到真正的问题之前。

iov_base
是一个
void*
,它将接受任何类型的指针。这意味着
(&buffers + (i * buffer_size))
my::aligned_vector<std::byte, buffer_size * n>
的指针,而不是一堆字节。我不得不把它改成
(buffers.data() + (i * buffer_size))

for(std::size_t i = 0; i < 1024; ++i){
    MY_ASSERT(buffers.size() + (i * buffer_size + buffer_size)); 
    iovecs[i].iov_base         = static_cast<std::byte>(buffers.data() + (i * buffer_size));
    iovecs[i].iov_len          = buffer_size;
    msgs[i].msg_hdr.msg_iov    = &iovecs[i];
    msgs[i].msg_hdr.msg_iovlen = 1;
}

然后我的问题就解决了

© www.soinside.com 2019 - 2024. All rights reserved.