5

让我们考虑这个简单的测试程序:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
        char buf[256];
        int i;

        strcpy(buf,"Hello world!");
        i = strlen(buf);
        printf("Length of string is %d.\n",i);
        return 0;
}

使用 Intel c++ 编译器进行编译并打开优化 (O3) 时,我从 valgrind 收到以下错误:

==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4009EF: main (strtest.cpp:11)
==8727== Use of uninitialised value of size 8
==8727==    at 0x4FC61ED: _itoa_word (in /lib64/libc-2.4.so)
==8727==    by 0x4FC9317: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC61F7: _itoa_word (in /lib64/libc-2.4.so)
==8727==    by 0x4FC9317: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC9386: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC990F: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC82F2: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)

我正在使用最新版本的 valgrind (3.6.1)。关闭优化 (-O0) 时不会发生这种情况,g++ 也不会发生这种情况。但是,它出现在我迄今为止尝试过的所有英特尔编译器(11.0、11.1、12)中。

似乎错误与字符串函数的 SIMD 加速有关,如C 字符串 strlen 和 Valgrind中讨论的那样。

据说这是 valgrind 中的一个错误,现在已修复。但是,尽管使用了最新的 valgrind 版本,我仍然有这些错误。有人知道这方面的帮助吗?

4

2 回答 2

6

Valgrind 试图确定一个值是否依赖于初始化的内存,这通常不是一个容易处理的问题。Valgrind 通过跟踪设置了哪些位并允许它们级联来“尽最大努力”。有很多方法可以欺骗它。例如,我刚刚编写了这段代码:

#include <stdlib.h>

int main(int argc, char *argv[])
{
    unsigned *p = malloc(sizeof(unsigned));
    unsigned x = *p;
    free(p);
    unsigned f = x == 0;
    unsigned g = x == 1;
    return f & g;
}

旁注:严格来说,上面的程序在任何没有陷阱表示的平台上都是正确unsigned int的,这在我们的平台上是正确的(Valgrind 仅适用于 x86)。在这些平台上不调用未定义的行为,并且main由 C 标准保证在这些平台上返回 0。

引用: n1256:7.20.3.3:malloc返回值为“不确定”的对象。n1256 3.17.2:不确定值是“陷阱表示”或“未指定值”。请注意,在 x86 上,无符号整数没有陷阱表示。

根据 Valgrind 的说法,既没有f也没有g正确初始化,所以f & g也不能初始化。然而,结果总是零,任何有一点逻辑的人都会告诉你。Valgrind 不懂逻辑,它只是根据一些简单的规则来做一些事情。

这里可能发生的情况是英特尔的 C 编译器为您提供了一个优化版本,strlen该版本使用 SSE 或某种技巧,这导致 Valgrind 中的“未初始化”位在结果中被设置。当然,如果优化后的代码读过了初始化部分,buf然后执行了一系列类似上面的操作,这自然会发生。当然,它会做类似的事情,因为这样做会更快(并且在 x86 上读取数组末尾总是安全的,只要您不跨越页面边界)。你有一些选择。

  • 使用 Valgrind 和 Intel 的编译器时关闭优化。

  • 将代码添加到 Valgrind 以捕获这种特殊类型的错误。(Valgrind 已经有特殊情况了。)

  • 有条件地修改你的代码,让 Valgrind 给出正确的结果。例如,将其放在顶部:

    // This only fixes the error if sizeof(buf) is at least as large
    // as the largest multiple of 16 larger than strlen(buf)
    #if VALGRIND
    memset(buf, '\0', sizeof(buf));
    #endif
    
于 2011-09-17T18:47:15.723 回答
0

不要使用 valgrind 进行优化。

于 2011-09-17T18:16:46.050 回答