perlbench导致SPEC 2006线束之外的段错误

时间:2016-10-24 13:29:13

标签: c perl debugging crash benchmarking

这可能过于具体,但可以在此处发布,因为它可能会帮助那些尝试在默认SPEC基准测试工具之外编译/运行SPEC 2006基准测试的其他人。 (我们这样做的原因是比较编译策略和代码覆盖率,而SPEC线束仅关注结果代码的性能)。

执行perlbench的ref运行时,基准测试会因分段错误而崩溃:

    Program received signal SIGSEGV, Segmentation fault.
0x00000000004f6868 in S_regmatch (prog=0x832144)

    at <path-to-spec>/CPU2006/400.perlbench/src/regexec.c:3024
3024            PL_reg_start_tmp[n] = locinput;
(gdb) bt
#0  0x00000000004f6868 in S_regmatch (prog=0x832144)
    at <path-to-spec>/CPU2006/400.perlbench/src/regexec.c:3024
#1  0x00000000004f22cf in S_regtry (prog=0x8320c0, startpos=0x831e70 "o")
    at <path-to-spec>/CPU2006/400.perlbench/src/regexec.c:2196
#2  0x00000000004eba71 in Perl_regexec_flags (prog=0x8320c0, stringarg=0x831e70 "o", strend=0x831e71 "", 
    strbeg=0x831e70 "o", minend=0, sv=0x7e2528, data=0x0, flags=3)
    at <path-to-spec>/CPU2006/400.perlbench/src/regexec.c:1910
#3  0x00000000004b33bb in Perl_pp_match ()
    at <path-to-spec>/CPU2006/400.perlbench/src/pp_hot.c:1340
#4  0x00000000004fcde4 in Perl_runops_standard ()
    at <path-to-spec>/CPU2006/400.perlbench/src/run.c:37
#5  0x000000000046bf57 in S_run_body (oldscope=1)
    at <path-to-spec>/CPU2006/400.perlbench/src/perl.c:2017
#6  0x000000000046b9f6 in perl_run (my_perl=0x7bf010)
    at <path-to-spec>/CPU2006/400.perlbench/src/perl.c:1934
#7  0x000000000047add2 in main (argc=4, argv=0x7fffffffe178, env=0x7fffffffe1a0)
    at <path-to-spec>/CPU2006/400.perlbench/src/perlmain.c:98

执行环境是64位Linux,并且使用最新的gcc和clang来观察行为。

导致此次崩溃的原因是什么?

3 个答案:

答案 0 :(得分:4)

段错误是由指出行上的变量n的垃圾值引起的。检查代码显示该值来自类型为

的对象的字段arg1
struct regnode_1 {
    U8  flags;
    U8  type;
    U16 next_off;
    U32 arg1;
};

检查对象的内存位置表明它没有打包,即next_offarg1之间有32位填充:

(gdb) x/16xb scan
0x7f4978:       0xde    0x2d    0x02    0x00    0x00    0x00    0x00    0x00
0x7f4980:       0x00    0x11    0x0d    0x00    0x00    0x00    0x00    0x00
(gdb) print/x n
$1 = 0xd1100

这是可疑的。 perlbench中正在进行指针和类型转换,因此类型大小假设可能在某处失败。使用multilib进行编译会产生一个工作基准,并检查内存是否验证没有填充。

将结构强制转换为位域可以在执行64位编译时修复崩溃:

struct regnode_1 {
    U8  flags : 8;
    U8  type : 8;
    U16 next_off : 16;
    U32 arg1 : 32;
};

答案 1 :(得分:2)

这就是我们的小调查进展:

起初我们认为这是一些填充问题,但正如彼得在Godbolt指出的那样,没有发生这样的事情。因此,结构的包装与否并没有改变任何东西。

然后,我怀疑Perl处理指针的方式(明显扭曲)。大多数演员都违反了标准规定的严格别名。由于分段错误发生在指针强制转换上,即:

struct regnode {
    U8  flags;
    U8  type;
    U16 next_off;
};

struct regnode_1 {
    U8  flags;
    U8  type;
    U16 next_off;
    U32 arg1;
};

但是,使用-fstrict-aliasing标志启用它并没有改变任何内容。尽管它有资格作为未定义的行为,但内存中没有重叠,因为当前正在解析的正则表达式的元素/节点在内存中单独布局。

更深入地检查有问题的switch块的LLVM IR,我在regexec.ll

中得到了这个
; truncated
%876 = load %struct.regnode*, %struct.regnode** %scan, align 8, !dbg !8005
%877 = bitcast %struct.regnode* %876 to %struct.regnode_1*, !dbg !8005
%arg11715 = getelementptr inbounds %struct.regnode_1, %struct.regnode_1* %877, i32 0, i32 3, !dbg !8005
%878 = load i64, i64* %arg11715, align 8, !dbg !8005
store i64 %878, i64* %n, align 8, !dbg !8006
; truncated

加载/存储指令使用64位整数,这意味着C中的指针被解释为指向8字节整数(而不是4)。因此,在当前正则表达式节点struct之外收集2个字节,以计算arg1的值。该值又用作数组索引,当数据超出数组范围时,最终会导致段错误。

返回跟踪,其中U32被解释为64位无符号整数。查看文件spec_config.h,条件编译(至少在我的机器中)指向以

开头的预处理程序块
#elif !defined(SPEC_CPU_GOOFY_DATAMODEL)

根据周围区域的代码注释,它应该与ILP32数据模型相对应(另请参阅this)。但是,U32TYPE被定义为unsigned long,在我的机器上是64位。

因此,修复方法是将定义更改为

#define U32TYPE uint32_t

,如this中所述,保证完全 32位(如果支持)。

答案 2 :(得分:2)

我想补充其他答案,说我们可以添加-DSPEC_LP64来解决段错误(在CPU2017中gcc)。如果SPEC小组将其添加到他们的常见问题解答中会很好。这似乎也适用于cactusADMpovraywrfCollecting Django Using cached Django-2.0.tar.gz Complete output from command python setup.py egg_info: Traceback (most recent call last): File "<string>", line 1, in <module> File "/private/var/folders/tn/d7vg_zcd5pq0cq3sltw0j7pc0000gn/T/pip-build-xIsFov/Django/setup.py", line 32, in <module> version = __import__('django').get_version() File "django/__init__.py", line 1, in <module> from django.utils.version import get_version File "django/utils/version.py", line 61, in <module> @functools.lru_cache() AttributeError: 'module' object has no attribute 'lru_cache' ---------------------------------------- Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/tn/d7vg_zcd5pq0cq3sltw0j7pc0000gn/T/pip-build-xIsFov/Django/

我们有一个python脚本为我们生成配置文件,我将与人们交谈,看看我是否可以分享我们目前为止为我们的编译器运行的内容。

编辑:无论如何,似乎都可以从外部访问,所以请转到:spec.py