一个arm平台工程段错误的排查

某 arm 工控机在试点测试时发现,一旦有数据到,工控机界面会就卡住,测试人员此问题必现,经查日志为段错误,多次在相同代码位置出错。除去周末,经过一天多时间,解决了问题,虽然没有很难的技术问题,但过程还是值得记录的。

由于本文没有技术含量,请谨慎按需阅读。

起因

上周五早上,测试人员(很不幸,我还无资格称之为同事)正在去试点路上,打电话给我,说要更新软件版本,于是我拉取代码树上最新的release分支。基于手上没有 arm 的编译环境,于是打包发给他,让他在测试机上编译并测试。后来才知道,那是目前唯一一台集编译和测试于一体的一体机。编译好后,指导如何更新,近中午时反馈出现段错误,因为有其它事,到下午时才找日志看。由于那个工程我没有参与,所以找相应开发人员看,经查,问题定位到一个初始化语句std::string foobar = "";,四只眼睛分析半天没结论。经商量,把试点的机器搬回来,在实验室重现,再跟踪调试。

排查及解决

问题定位

咨询测试人员,反馈说在实验室相同场景测试时没有遇到问题。由于实验室均为x86机器,也没错误,只能加打印调试。

先是在出错那一行代码前面加上一些语句,测试,发现出错的行号变化了,依然指向那个string语句。再仔细观察代码,发现这个string类型变量在函数后面赋值给另一个数组(char buff[]),于是怀疑数组容量不够,将组装后的string变量长度打印,发现超过了数组长度。将长度加大,测试,一切正常。使用工程封装的log函数打印,发现URL地址的中文输出有乱码,但用printf输出正常。URL的转义是通过调用curl库函数curl_easy_escape实现的,中文字符会带了百分号%,怀疑是封装log函数问题,限于时间,没有再研究。不过可能是个隐患,日后再议。

解决方法

将最后组装的buffer数组容量改大即可。

重现代码

下面模拟实际工程的函数布局,舍去无关的代码,以突显问题。

注:最后的output会越界。

#include 
#include 

#include 

int main()
{
    const char* url = "http://172.168.168.188:18080/api/new/v2/queryAVeryloongApiforveryloongremoteipserver?";
    char foo1[256]={0};
    
    int a = 100;
    int b = 1;
    int c = a+b+1+1+b+3+a;
    
    std::string strData = "";
    
    for (int i=0; i < 3; i++)
    {
        strData.append("&");
        strData.append("10000");
        strData.append("=");
        strData.append("103");
    }
    char output[96]={0};
    
    sprintf(output, "%s?%s", url, strData.c_str());
    printf("url=%s\n", output);
   
    return 0;
}

经测试,只有x86上正常,其它2个平台段错误。从业多年的直觉认为,x86上应该是看上去正常,实际不正常(肯定是越界了),但一直如此使用,百思不得其解。

在x86工控机上的测试结果:

$ file a.out 
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=3e7c0c704230b08e49f529a6924397e0dfd358ba, not stripped

$ ./a.out 
url=http://172.168.168.188:18080/api/new/v2/queryAVeryloongApiforveryloongremoteipserver??&10000=103&10000=103&10000=103

在arm工控机上的测试结果:

$ file a.out 
a.out: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 3.7.0, not stripped

$ ./a.out 
url=http://172.168.168.188:18080/api/new/v2/queryAVeryloongApiforveryloongremoteipserver??&10000=103&10000=103&10000=103
段错误(吐核)

在loongarch工控机上的测试结果:

$ file a.out 
a.out: ELF 64-bit LSB executable, LoongArch-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld.so.1, for GNU/Linux 4.15.0, BuildID[sha1]=7d61ce6d3a50850e045dcdaab48585f88bbca4f7, not stripped

$ ./a.out 
url=http://172.168.168.188:18080/api/new/v2/queryAVeryloongApiforveryloongremoteipserver??&10000=103&10000=103&10000=103
总线错误

小结

最近断续地接触国产化适配,有点小心得。

与很多人认为的只要改个宏定义,交叉编译一下就行不同,我对适配的事不敢太乐观。上面往往认为三两天就能做完,但实操起来就很麻烦。相同的代码,在不同的架构(x86和arm、mips)上表现不一定相同。

目前较大的问题有下面几个:

  • 很多工程代码已经有数年历史,有些函数已无可考,有的代码用了当时的第三方版本,如果贸然更新,或使用新操作系统新编译器,则可能导致编译不通过。
  • 宏定义修改的确简单,但工程代码里,对于系统位数和系统架构两个不同概念没有区分,所以要十分小心。比如,指针长度,在32位和64位系统中,值就不同。
  • 代码存在隐藏的bug,如段错误。一方面,不同架构平台对个别段错误类型的处理方式不同(如本文所遇到的问题,在x86上是不存在的),没有条件很难测试到位。另一方面,审查代码也很难显式看出来。
  • 工程代码有大量的编译警告,但似乎没有人修正,从笔者经验看,修正警告也会带来隐患。

你可能感兴趣的:(技术杂铺,开发语言,arm开发,国产化适配,c++)