非void函数缺少返回值导致的程序崩溃

今天在rk3568上调试时出现崩溃:bus error(core dumped)。排查了一天都没找出原因,偶然把一句代码注释掉才摸清楚些许头绪,特此来记录一下。

1、rk3568上出现崩溃

在出现bus error(core dumped)这个崩溃后,通过gdb调试,运行到崩溃的地方时打印如下信息:

bash 复制代码
Thread 3 "QThread" received signal SIGBUS, Bus error.
[Switching to Thread 0x7fe2b201b0 (LWP 2009)]
0x0000007ff496ed1c in QString::reallocData(unsigned int, bool) ()
   from /usr/local/Qt-5.15.4/lib/libQt5Core.so.5

1、Thread 3发生了Bus error错误

2、崩溃发生在QString::reallocData函数

输入thread apply all bt命令进一步查看所有线程的堆栈信息

bash 复制代码
(gdb) thread apply all bt

Thread 4 (Thread 0x7fe231f1b0 (LWP 2010)):
#0  0x0000007fedaa80d8 in __GI___poll (fds=0x7fd8004a60, nfds=2, timeout=<optimized out>) at ../sysdeps/unix/sysv/linux/poll.c:41
#1  0x0000007fec667b38 in  () at /lib/aarch64-linux-gnu/libglib-2.0.so.0
#2  0x0000007fec667c5c in g_main_context_iteration () at /lib/aarch64-linux-gnu/libglib-2.0.so.0
#3  0x0000007ff4b283c4 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#4  0x0000007ff4acab60 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#5  0x0000007ff48d98b8 in QThread::exec() () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#6  0x0000007fe40d67fc in QDBusConnectionManager::run() () at /usr/local/Qt-5.15.4/lib/libQt5DBus.so.5
#7  0x0000007ff48dac28 in QThreadPrivate::start(void*) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#8  0x0000007feddf0624 in start_thread (arg=0x7ff48daae0 <QThreadPrivate::start(void*)>) at pthread_create.c:477
#9  0x0000007fedab166c in thread_start () at ../sysdeps/unix/sysv/linux/aarch64/clone.S:78

Thread 3 (Thread 0x7fe2b201b0 (LWP 2009)):
--Type <RET> for more, q to quit, c to continue without paging--
#0  0x0000007ff496ed1c in QString::reallocData(unsigned int, bool) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#1  0x0000007ff496f65c in QString::append(QChar const*, int) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#2  0x0000007ff4b6cd28 in QTextStream::operator<<(QString const&) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#3  0x000000555558323c in QDebug::operator<<(char const*) (this=0x7fe2b1f118, t=0x55556b9e10 <qt_meta_stringdata_Modbus+24> "Modbus") at /opt/EmbedSky/TQ3568/aarch64-embedsky-linux-gnu/sysroot/usr/local/Qt-5.15.4/include/QtCore/qdebug.h:159
#4  0x00000055555cbb84 in Modbus::read_reg(unsigned char, unsigned char, unsigned short, unsigned short) (this=0x55558ac250, slave_addr=7 '\a', funcode=4 '\004', reg_addr=0, nb=4) at ../../../../oass/OASS/GateWay/mechArm/modbus.cpp:152
#5  0x00000055555cfbf4 in MotorControl::checkHeadError() (this=0x55558ad020) at ../../../../oass/OASS/GateWay/mechArm/motorcontrol.cpp:510
#6  0x00000055555cd338 in MotorControl::control_Basic2(MotorControl::Coordinate_Axis, double, MotorControl::Operation_Cmd, MotorControl::Coordinate_Axis, double, MotorControl::Operation_Cmd) (this=0x55558ad020, axis1=MotorControl::Z, data1=0, cmd1=MotorControl::Reset, axis2=MotorControl::E, data2=0, cmd2=MotorControl::Reset) at ../../../../oass/OASS/GateWay/mechArm/motorcontrol.cpp:50
#7  0x00000055555cfd0c in MotorControl::AllReset() (this=0x55558ad020) at ../../../../oass/OASS/GateWay/mechArm/motorcontrol.cpp:524
#8  0x00000055555c3af8 in QtPrivate::FunctorCall<QtPrivate::IndexesList<>, QtPrivate::List<>, void, bool (MotorControl::*)()>::call(bool (MotorControl::*)(), MotorControl*, void**) (f=(bool (MotorControl::*)(class MotorControl * const)) 0x55555cfce0 <MotorControl::AllReset()>, o=0x55558ad020, arg=0x5555987798) at /opt/EmbedSky/TQ3568/aarch64-embedsky-linux-gnu/sysroot/usr/local/Qt-5.15.4/include/QtCore/qobjectdefs_impl.h:152
#9  0x00000055555c351c in QtPrivate::FunctionPointer<bool (MotorControl::*)()>::call<QtPrivate::List<>, void>(bool (MotorControl::*)(), MotorControl*, void**) (f=(bool (MotorControl::*)(class MotorControl * const)) 0x55555cfce0 <MotorControl::AllReset()>, o=0x55558ad020, arg=0x5555987798) at /opt/EmbedSky/TQ3568/aarch64-embedsky-linux-gnu/sysroot/usr/local/Qt-5.15.4/include/QtCore/qobjectdefs_impl.h:185
#10 0x00000055555c2bf8 in QtPrivate::QSlotObject<bool (MotorControl::*)(), QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) (which=1, this_=0x555590ff00, r=0x55558ad020, a=0x5555987798, ret=0x0) at /opt/EmbedSky/TQ3568/aarch64-embedsky-linux-gnu/sysroot/usr/local/Qt-5.15.4/include/QtCore/qobjectdefs_impl.h:418
#11 0x0000007ff4af9968 in QObject::event(QEvent*) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#12 0x0000007ff5d0c69c in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/local/Qt-5.15.4/lib/libQt5Widgets.so.5
#13 0x0000007ff5d15038 in QApplication::notify(QObject*, QEvent*) () at /usr/local/Qt-5.15.4/lib/libQt5Widgets.so.5
#14 0x0000007ff4acc2e0 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#15 0x0000007ff4acee70 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#16 0x0000007ff4b28eb8 in postEventSourceDispatch(_GSource*, int (*)(void*), void*) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#17 0x0000007fec66794c in g_main_context_dispatch () at /lib/aarch64-linux-gnu/libglib-2.0.so.0
#18 0x0000007fec667bbc in  () at /lib/aarch64-linux-gnu/libglib-2.0.so.0
#19 0x0000007fec667c5c in g_main_context_iteration () at /lib/aarch64-linux-gnu/libglib-2.0.so.0
#20 0x0000007ff4b28444 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#21 0x0000007ff4acab60 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#22 0x0000007ff48d98b8 in QThread::exec() () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#23 0x0000007ff48dac28 in QThreadPrivate::start(void*) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#24 0x0000007feddf0624 in start_thread (arg=0x7ff48daae0 <QThreadPrivate::start(void*)>) at pthread_create.c:477
#25 0x0000007fedab166c in thread_start () at ../sysdeps/unix/sysv/linux/aarch64/clone.S:78

Thread 2 (Thread 0x7fe3f831b0 (LWP 2008)):
#0  0x0000007fedaa80d8 in __GI___poll (fds=0x7fe3f82828, nfds=1, timeout=<optimized out>) at ../sysdeps/unix/sysv/linux/poll.c:41
#1  0x0000007fea78f7a8 in  () at /lib/aarch64-linux-gnu/libxcb.so.1
#2  0x0000007fea791484 in xcb_wait_for_event () at /lib/aarch64-linux-gnu/libxcb.so.1
#3  0x0000007fe41d8728 in QXcbEventQueue::run() () at /usr/local/Qt-5.15.4/lib/libQt5XcbQpa.so.5
#4  0x0000007ff48dac28 in QThreadPrivate::start(void*) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#5  0x0000007feddf0624 in start_thread (arg=0x7ff48daae0 <QThreadPrivate::start(void*)>) at pthread_create.c:477
#6  0x0000007fedab166c in thread_start () at ../sysdeps/unix/sysv/linux/aarch64/clone.S:78

Thread 1 (Thread 0x7fe4a9f010 (LWP 2005)):
#0  0x0000007fedaa80d8 in __GI___poll (fds=0x555590f730, nfds=1, timeout=<optimized out>) at ../sysdeps/unix/sysv/linux/poll.c:41
--Type <RET> for more, q to quit, c to continue without paging--
#1  0x0000007fec667b38 in  () at /lib/aarch64-linux-gnu/libglib-2.0.so.0
#2  0x0000007fec667c5c in g_main_context_iteration () at /lib/aarch64-linux-gnu/libglib-2.0.so.0
#3  0x0000007ff4b283c4 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#4  0x0000007ff4acab60 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#5  0x0000007ff4ad32f8 in QCoreApplication::exec() () at /usr/local/Qt-5.15.4/lib/libQt5Core.so.5
#6  0x0000005555614ab8 in main(int, char**) (argc=1, argv=0x7fffffef88) at ../../../../oass/OASS/mechArmTest/main.cpp:52
(gdb) 
(gdb)

之前的信息提示崩溃在Thread 3线程,查看Thread 3线程中的函数调用

Thread 3 (Thread 0x7fe2b201b0 (LWP 2009)):

#0 QString::reallocData(...)

#1 QString::append(...)

#2 QTextStream::operator<<(...)

#3 QDebug::operator<<(char const*)

#4 Modbus::read_reg(...)

#5 MotorControl::checkHeadError()

...

其中MotorControl::checkHeadError()和Modbus::read_reg(...)是自己写的函数,定位到Modbus::read_reg(...)函数的实现如下

cpp 复制代码
QByteArray Modbus::read_reg(uint8_t slave_addr, uint8_t funcode, uint16_t reg_addr, uint16_t nb)
{
    if(serialport == nullptr){
        InitSerialPort();
    }

    QVector<uint8_t> modbus_tx_buf;
    modbus_tx_buf.append(slave_addr);          //从设备地址
    modbus_tx_buf.append(funcode);             //功能码
    modbus_tx_buf.append(reg_addr >> 8);       //寄存器起地址(高8位)
    modbus_tx_buf.append(reg_addr & 0xFF);     //(低8位)
    modbus_tx_buf.append(nb >> 8);             //寄存器数量(高8位)
    modbus_tx_buf.append(nb & 0xFF);           //(低8位)

    //CRC
    uint16_t crc;
    uint16_t modbus_tx_length = modbus_tx_buf.size();
    uint8_t* modbus_tx_data = modbus_tx_buf.data();
    crc = mb_crc16(modbus_tx_data, modbus_tx_length);   //计算crc
    modbus_tx_buf.append(crc & 0xFF);
    modbus_tx_buf.append(crc >> 8);

    //QVector<uint8_t> -> QByteArray
    QByteArray sendData(reinterpret_cast<const char*>(modbus_tx_buf.constData()), modbus_tx_buf.size());
    myDebug() << "modbus Tx:" << sendData.toHex().toUpper();

    QByteArray revdata = serialport->writeAndRead(sendData);  //同步读写

    myDebug() << "modbus Rx:" << revdata.toHex().toUpper();

    return revdata;
}

在这个函数实现中排查了半天,还是找不出原因。于是只能在调用这个函数之前一步步将代码注释再来看是否崩溃。

突然,在把一个函数注释调后,居然正常了!这个函数的实现如下:

cpp 复制代码
QString MotorControl::failureMes(Operation_Result result)
{
    QString errormes;
    switch(result){
    case Operation_Result::failure:
        errormes =  QString("操作失败");break;
    case Operation_Result::overTime:
        errormes =  QString("读取超时");break;
    case Operation_Result::modbusError:
        errormes =  QString("modbus格式有误");break;
    case Operation_Result::emergencyStop:
        errormes =  QString("急停导致的操作失败");break;

    ......

    default:
        break;
    }
    myDebug() << "error code:" << result << " " << errormes;
}

非常简单的一个函数,只是根据枚举值打印错误的信息,一时半会还真看不出崩溃原因来。于是把这个函数实现丢给gpt问。他给出的结论是:这个函数要求返回值为QString类型,但是函数实现中没有返回值,这可能会导致程序崩溃!

2、验证:非void函数缺少返回值导致崩溃

以前还真没遇到过这样的问题,于是自己写一段代码测试验证一下

测试很简单,就是调用一个非void函数,但是函数的实现没有返回值

cpp 复制代码
QString MainWindow::func()
{
    QString mes = "run func()";
    qDebug() << mes;
}

点击界面按钮,执行函数。程序发生崩溃

崩溃信息:Segmentation fault (core dumped) (和上面的bus error不一样???)

gdb排查,打印的信息和上面的类似。但是上面的崩溃是触发了SIGBUS, Bus error;而测试的崩溃触发的是SIGSEGV, Segmentation fault

3、为什么?

为什么非void函数缺少返回值会导致崩溃?

查阅相关资料https://stackoverflow.com/questions/57163022/c-crash-on-a-non-void-function-doesnt-have-return-statement

后,

总结就是:在C++ 标准中,如果函数返回类型不是 void,则省略 return 语句会导致未定义行为。

结论:可以确定程序发生崩溃是因为非void函数缺少返回值。修改之后问题解决了。

4、一点疑问

但还是留下了两个疑问:

(1)为什么gdb查看堆栈信息时提示崩溃发生在QString::reallocData(...)函数?而且发生崩溃的QString MotorControl::failureMes(...)函数在堆栈信息中没有任何提示

(2)发生崩溃是在rk3568开发板上跑的,运行的是arm架构,崩溃的信息是bus error(总线错误);

而测试的崩溃是在虚拟机上跑的,崩溃的信息却是Segmentation fault(段错误)。出现这两个崩溃的原因一般是不一样的。但是测试下来崩溃都是因为函数没有返回值导致的,为什么同样的原因,却指向不同的崩溃信息?

对于gdb的使用以及崩溃问题的排查还不熟练,希望日后在工作中能逐渐弄清楚。

相关推荐
shyの同学1 小时前
Spring事务:为什么catch了异常,事务还是回滚了?
数据库·spring·事务·spring事务
xuanloyer1 小时前
oracle从入门到精通--物理存储结构
数据库·oracle
chenzhou__1 小时前
LinuxC语言并发程序笔记补充
linux·c语言·数据库·笔记·学习·进程
别或许1 小时前
14、使用C++连接MySQL及接口
数据库·mysql
阿里云云原生1 小时前
阿里云 ARMS 自定义指标采集:打破传统 APM 局限,实现业务可视化监控
数据库·阿里云·云原生·oracle·arms
lu9up1 小时前
业务表异常阻塞导致接口超时处理案例
数据库·性能优化
San30.1 小时前
从 Mobile First 到 AI First:用 Python 和大模型让数据库“开口说话”
数据库·人工智能·python
古城小栈1 小时前
PostgreSQL 【vs】 MySQL
数据库·mysql·postgresql
安全系统学习2 小时前
网络安全漏洞之React 框架分析
数据库·安全·web安全·网络安全