Why doesn't valgrind spot the leak when program was compiled with gcc-5.2.0

后端 未结 2 665
野性不改
野性不改 2020-12-20 03:30

Today I was coding something and after I was done, I made a check with valgrind and I got a surprise.

If I compile my program on my Ubuntu (15.04 64BIT) with gcc-4.9

相关标签:
2条回答
  • 2020-12-20 04:03

    Seems that GCC 5.2.0 is able to detect that string2 is a constant "Hello" through the strcpy. So it just optimizes out string2 without allocating new memory chunk in the HEAP. My guess would be that string.h has the implementation of strcpy and strlen in the header itself.

    The best way to detect memory leaks is to compile without optimizations. Try recompiling it with -O0 instead of -O2. In this case the compiler will create the binary as close to your source code as possible.

    With this:

    printf("String2 = %s\n",string2);

    The leak is spotted:

    Here it seems that the compiler detects dependency on string2 so it doesn't optimize it out. Probably because the implementation of printf is not available at the compilation time of your source or maybe because printf uses variadic variable. But it is just my guess...

    0 讨论(0)
  • 2020-12-20 04:14

    Continuing our discussion from the comments in Will C automatically free memory with no pointers?, the difference in the valgrind output is the result of the compiler optimization -O2 optimizing the allocation out of your code. Why? Let's look at your code:

    string2 = malloc(33);
    strcpy (string2, "Hello");
    ...
    printf("Bye\n");
    

    While you have allocated memory for string2 and you have copied "Hello" to sting2, you never use string2 in the remainder of your code. Since there is no subsequent operation that relies on the memory pointed to by string2 or the value contained within it, the compiler is free to delete that code entirely from the final executable.

    In "optimizing", the compiler looks for ways it can make the code run more efficiently, with fewer instructions, while still providing the same functionality. Since nothing relies on the memory or value associated with string2, the compiler simply concludes the code can run faster and in fewer instructions if it just ignores the allocation and copy completely.

    (that is why as suggested in the other answer when you call printf using string2 the leak appears, the compiler cannot simply optimize the allocation and copy away, because the printf depends on the memory and value of string2)

    The key to verifying what is taking place is to look at the assembly code produced by the compiler (gcc -S produces the assembly file, add the option -masm=intel to tell the compiler to output the assembly in intel format instead of ATT)

    Let's start with optimizations disabled -O0. The salient part of the assembly produced is:

        .cfi_startproc
        push    rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        mov     rbp, rsp
        .cfi_def_cfa_register 6
        sub     rsp, 48
        mov     DWORD PTR [rbp-4], 0
        mov     QWORD PTR [rbp-16], 0
        mov     QWORD PTR [rbp-24], 0
        mov     QWORD PTR [rbp-32], OFFSET FLAT:.LC0
        mov     QWORD PTR [rbp-40], 0
        mov     edi, 33
        call    malloc                  ; the call to malloc is retained
        mov     QWORD PTR [rbp-40], rax
        mov     rax, QWORD PTR [rbp-40]
        mov     DWORD PTR [rax], 1819043144
        mov     WORD PTR [rax+4], 111
        mov     rax, QWORD PTR [rbp-32]
        mov     rdi, rax
        call    strlen
        mov     QWORD PTR [rbp-16], rax
        mov     rax, QWORD PTR [rbp-40]
        mov     rdi, rax
        call    strlen
        mov     QWORD PTR [rbp-24], rax
        mov     rax, QWORD PTR [rbp-16]
        cmp     rax, QWORD PTR [rbp-24]
        je      .L2
        mov     DWORD PTR [rbp-4], 5
        jmp     .L4
    

    Now, let's look at the optimized version (with gcc (GCC) 6.1.1 20160602 using the -Ofast optimization):

        .cfi_startproc
        sub     rsp, 8
        .cfi_def_cfa_offset 16
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        xor     eax, eax
        add     rsp, 8
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
    

    There is no malloc at all -- it has been optimized away. And true to what optimization should do, it runs in much fewer instructions.

    Now how valgrind reports what it sees will differ between valgrind versions and differ between OS's, but the bottom line is the same. If you declare, allocate, but never use any value, the compiler is free to optimize that declaration/allocation away, and one way to find out just what is happening, is to look at the assembly file. If you want to reproduce the assembly, the complete compile string used was:

    gcc -S -masm=intel -Ofast -o valgrindtest.asm valgrindtest.c
    

    Then just look at valgrindtest.asm. Hopefully this adds another piece of the puzzle for you.

    0 讨论(0)
提交回复
热议问题