Linux编译器freeCode@IT

gcc编译选项march=native引起的加班

2019-03-07  本文已影响158人  宠娃的码农

      今天是来新公司第一次加班到十点多,是因为编译器选项march=native引起的,特意记录一下。

【问题现象】

      我们发布了一个组件(以静态库.a的形式发布)给产品,产品切换新组件编译到进程中后,进程启动不起来。

【问题定位】

  1 进程启动过程中挂掉,肯定是优先分析coredump。可是我们的产品是不允许生成core文件,而是自研了一套记录进程临终遗言的机制.

2。去取对应的临终遗言文件,没有生成。为什么没生成不知道,找对应项目组的同学看估计又要扯皮好久。

3 分析我们的更改过程,最近的一个大特性是消除二进制差异,这个会修改二进制文件,可能是这个原因。去掉这个工具,依然不同。排除了二进制消除工具的影响。

4 编译环境的安装过程是产品提供的 总共需要十步左右安装,考虑可能是某一步骤影响的。逐步重新环境,每一步都自己对比发现所有步骤都装完了二进制还是一样的 但是和产品的不一样。说明和编译工具不相关。

5 怀疑是两套环境谁改了配置,对比了编译环境的bin etc lib等目录 完全一样的。

6 同样的代码在两个编译环境拷贝了一份 编译出来还是不同 和代码也没关系

7  此时已经晚上九点了,距离班车发车还有十分钟,娃娃也给我发了视频过来了,没有思路,真想回家。但是作为部门的首席架构师,产品明天要过点评审,我还是要有点职业精神。把代码进行精简到一个函数 然后直接用编译器编译 生成的二进制不同。

8 这时候分析了下,怀疑编译器的bug或者编译器的配置了,但是无从排查。求助了os专家和编译器专家,编译参数有十几个,先排除下编译选项的影响。专家比较有经验,二分法排除编译参数,找到了march=native。编译器专家介绍 也上网查了资料.x86不同系列的处理器有该系列特有的一些指令集,march指定cpu的系列,gcc会优化代码尽可能编译成所指定处理器对应的指令集。通过cpuinfo可以看到我们两套编译环境的cpu是不一样,所以生成的机器指令是不一样的。我们生成的组件编译到产品的进程,在产品的机器上运行,使用了机器不能识别的指令或者特权指令,cpu就会发出指令异常信号,将进程干掉。问题得以定位。终于可以下班了。

9 问题定位了 在路上总结了此文,并开始想解决方案。分析下为什么引入这个编译选项,去掉这个编译选项或者换成产品的编译选项的影响,为什么不是使用的产品的编译选项。换编译选项这样的重大变更,流程应该怎样控制。为什么这个问题到了产品的测试那里才暴露出来,我们在质量保证上有没有提升的地方。其他的选项有没有隐患,如何验证。

10 这次定位过程持续了两三天 还是有很多需要改进的地方。

1)必现问题,协调产品测试环境,想办法打开coredump,应该很早就能捕获到指令异常错误。

2)公司内部开发的进程临终遗言工具为什么没有记录下来,是不是对指令异常的信号没有做处理,可以反馈下给他们。

3)应该把原生工具包在两个环境部署一下开始直接编译,说不定立即发现不一样,而不是怀疑编译工具包的安装导致,当然这点可能有些事后诸葛亮。

感想:

      没想到,来做网管业务开发还能触发cpu指令集这样底层的问题。好在朕当年在核心网os千锤百炼,虽然不精通,但是也知道有这么些东西。现在理解了cpu+os+编译器+网络+数据库+数据结构是程序员的内功心法,也许平时看不到,关键时刻用出来,能解决关键问题。

【推荐一些书】

很感谢在华为核心网平台的日子,老大为我们争取到看书也做到了绩效考核里面.看了不少书,也成长的很快。推荐一些方面我们看的经典书,不过羞愧的是我基本没有一本看完更不用说精通。这些还是要看起来的,要把自己书架上买的百本经典书都好好看看。

gcc编译选项march=native引起的加班 gcc编译选项march=native引起的加班 gcc编译选项march=native引起的加班 gcc编译选项march=native引起的加班 gcc编译选项march=native引起的加班 gcc编译选项march=native引起的加班 gcc编译选项march=native引起的加班 gcc编译选项march=native引起的加班 gcc编译选项march=native引起的加班

【文末彩蛋】

在定位问题中看到老婆发的视频,真高兴。女儿能走几步了,儿子那么疼爱自己妹妹。人生得一子一女,还有何求。

发一个看的好为文,作为看完的福利

前几天在运行一个模型训练工具的时候,发现了一个Illegal instruction (core dumped)的错误。话说这种错误以前没怎么见过。这是一个开源的项目,我是运行的从同事那边拿来的工具,就会这样,但是我自己下载源码重新编译后运行一切正常。于是就查了查这个Illegal instruction是怎么回事,毕竟这货不比segmentfault常见。

查阅显示,illegal instruction,即SIGILL, 是POSIX标准中提供的一类错误。 从名字上看,SIGILL是启动的某个进程中的某一句不能被CPU识别成正确的指令。 此类错误是由操作系统发送给进程的,在进程试图执行一些形式错误、未知或者特权指令时操作系统会使用SIGILL信号终止程序。果然如名字一样,非法指令。那么,在什么情况下会出现这种错误呢,一般来说,有两种可能,一是将数据错误地写进了代码段,导致将本不是指令的数据当成指令去执行;第二种可能是编译时指定的CPU架构与实际运行的机器不一致。这里主要讲讲第二种。

由于CPU架构的演进,CPU指令集一直在不断地拓展,SSE、SSE2、SSE3、SSE42、AVX、AVX2等。不同的CPU能支持的指令集是不一样的,如果编译程序时指定了使用新的CPU架构进行编译,则该程序在老的CPU上运行时,其指令就不能被执行,从而引发Illegal instruction错误。查阅gcc资料,gcc是提供了一个march编译选项来指定所使用的CPU架构的。如果不清楚自己的CPU类型,可以使用gcc -c -Q -march=native --help=target | grep march来查看:

music@ds01:~$ gcc -c -Q -march=native --help=target | grep march

  -march=                            corei7-avx

music@ds01:~$

如果在编译时指定了-march参数,gcc将不会再用兼容的指令去编译,而是根据指定的CPU架构,采用其特定的指令集如AVX去生成二进制代码。因此,当你确定所编译的程序只会在特定的环境中运行时,可以使用-march参数来指定CPU架构,这样编译器就可以根据你的CPU架构进行指令上的优化,而这个指定带来的结果就是,如果你将程序放在其他机器上运行,有可能得到Illegal instruction的运行错误。

上一篇下一篇

猜你喜欢

热点阅读