非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的使用以及崩溃问题的排查还不熟练,希望日后在工作中能逐渐弄清楚。

相关推荐
科技小花23 分钟前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸24 分钟前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain26 分钟前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希1 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神1 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员1 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java1 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿2 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴2 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存
YOU OU2 小时前
三大范式和E-R图
数据库