• <转>使用valgrind检查内存问题


    作者:gfree.wind@gmail.com
    博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net 
     
    本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
    ======================================================================================================
     
    工欲善其事,必先利其器。Valgrind作为一个免费且优秀的工具包,平时大部分人可能都是使用valgrind检测内存问题,如内存泄露,越界等。其实Valgrind的用途远不止于此,其实际上为一个工具包,除了检查内存问题以外,还有其它多项用途。我准备将其大致介绍一下。本不想再介绍Valgrind检测内存问题的用法的,但是又一想,毕竟这是Valgrind的一个最有名的用途,如果少了它,不免有些遗憾,所以还是把检查内存问题作为第一篇吧。
     
    请看一下代码:
    1. #include <stdlib.h>
    2. #include <stdio.h>
    3. #include <string.h>
    4. static void mem_leak1(void)
    5. {
    6.     char *p = malloc(1);
    7. }
    8. static void mem_leak2(void)
    9. {
    10.     FILE *fp = fopen("test.txt", "w");
    11. }
    12. static void mem_overrun1(void)
    13. {
    14.     char *p = malloc(1);
    15.     *(short*)p = 2;
    16.     free(p);
    17. }
    18. static void mem_overrun2(void)
    19. {
    20.     char array[5];
    21.     strcpy(array, "hello");
    22. }
    23. static void mem_double_free(void)
    24. {
    25.     char *p = malloc(1);
    26.     free(p);
    27.     free(p);
    28. }
    29. static void mem_use_wild_pointer(void)
    30. {
    31.     char *p = (void*)0x80184800;
    32.     *p = 1;
    33. }
    34. static void mem_free_wild_pointer(void)
    35. {
    36.     char *p;
    37.     free(p);
    38. }
    39. int main()
    40. {
    41.     mem_leak1();
    42.     mem_leak2();
    43.     mem_overrun1();
    44.     mem_overrun2();
    45.     mem_double_free();
    46.     //mem_use_wild_pointer();
    47.     mem_free_wild_pointer();
    48.     return 0;
    49. }
    这里一共列出了七种常见的内存问题:1. 动态内存泄露;2. 资源泄露,这里以文件描述符为例;3. 动态内存越界;4.数组内存越界;5.动态内存double free;6.使用野指针,即未初始化的指针;7.释放野指针,即未初始化的指针;其中由于本示例代码过于简单,第6中情况,使用野指针会直接导致crash,所以在main中,并没有真正的调用那个示例代码。由于valgrind只能检测执行到的代码,所以在后面的报告中,不会报告第6种错误情况。但是,在大型的项目中,有可能使用野指针并不会导致程序crash。另外上面的7中情况,有些情况严格的说,实际上可以归为一类。
     
    下面看怎样执行valgrind来检测内存错误:
    1. valgrind --track-fds=yes --leak-check=full --undef-value-errors=yes ./a.out
    上面那些option的具体含义,可以参加valgrind --help,其中有些option默认就是打开的,不过我习惯于明确的使用option,以示清晰。
     
    看执行后的报告:
    1. ==2326== Memcheck, a memory error detector
    2. ==2326== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
    3. ==2326== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
    4. ==2326== Command: ./a.out
    5. ==2326==
    6. /* 这里检测到了动态内存的越界,提示Invalid write。*/
    7. ==2326== Invalid write of size 2
    8. ==2326== at 0x80484B4: mem_overrun1 (in /home/fgao/works/test/a.out)
    9. ==2326== by 0x8048553: main (in /home/fgao/works/test/a.out)
    10. ==2326== Address 0x40211f0 is 0 bytes inside a block of size 1 alloc'd
    11. ==2326== at 0x4005BDC: malloc (vg_replace_malloc.c:195)
    12. ==2326== by 0x80484AD: mem_overrun1 (in /home/fgao/works/test/a.out)
    13. ==2326== by 0x8048553: main (in /home/fgao/works/test/a.out)
    14. ==2326==
     /* 这里检测到了double free问题,提示Invalid Free */
    1. ==2326== Invalid free() / delete / delete[]
    2. ==2326== at 0x40057F6: free (vg_replace_malloc.c:325)
    3. ==2326== by 0x8048514: mem_double_free (in /home/fgao/works/test/a.out)
    4. ==2326== by 0x804855D: main (in /home/fgao/works/test/a.out)
    5. ==2326== Address 0x4021228 is 0 bytes inside a block of size 1 free'd
    6. ==2326== at 0x40057F6: free (vg_replace_malloc.c:325)
    7. ==2326== by 0x8048509: mem_double_free (in /home/fgao/works/test/a.out)
    8. ==2326== by 0x804855D: main (in /home/fgao/works/test/a.out)
    9. ==2326==
    10. /* 这里检测到了未初始化变量 */
    11. ==2326== Conditional jump or move depends on uninitialised value(s)
    12. ==2326== at 0x40057B6: free (vg_replace_malloc.c:325)
    13. ==2326== by 0x804853C: mem_free_wild_pointer (in /home/fgao/works/test/a.out)
    14. ==2326== by 0x8048562: main (in /home/fgao/works/test/a.out)
    15. ==2326==
     /* 这里检测到了非法是否野指针 */
    1. ==2326== Invalid free() / delete / delete[]
    2. ==2326== at 0x40057F6: free (vg_replace_malloc.c:325)
    3. ==2326== by 0x804853C: mem_free_wild_pointer (in /home/fgao/works/test/a.out)
    4. ==2326== by 0x8048562: main (in /home/fgao/works/test/a.out)
    5. ==2326== Address 0x4021228 is 0 bytes inside a block of size 1 free'd
    6. ==2326== at 0x40057F6: free (vg_replace_malloc.c:325)
    7. ==2326== by 0x8048509: mem_double_free (in /home/fgao/works/test/a.out)
    8. ==2326== by 0x804855D: main (in /home/fgao/works/test/a.out)
    9. ==2326==
    10. ==2326==
    11. /* 
    12. 这里检测到了文件指针资源的泄露,下面提示说有4个文件描述符在退出时仍是打开的。
    13. 描述符0,1,2无需关心,通过报告,可以发现程序中自己明确打开的文件描述符没有关闭。
    14. */
    15. ==2326== FILE DESCRIPTORS: 4 open at exit.
    16. ==2326== Open file descriptor 3: test.txt
    17. ==2326== at 0x68D613: __open_nocancel (in /lib/libc-2.12.so)
    18. ==2326== by 0x61F8EC: __fopen_internal (in /lib/libc-2.12.so)
    19. ==2326== by 0x61F94B: fopen@@GLIBC_2.1 (in /lib/libc-2.12.so)
    20. ==2326== by 0x8048496: mem_leak2 (in /home/fgao/works/test/a.out)
    21. ==2326== by 0x804854E: main (in /home/fgao/works/test/a.out)
    22. ==2326==
    23. ==2326== Open file descriptor 2: /dev/pts/4
    24. ==2326== <inherited from parent>
    25. ==2326==
    26. ==2326== Open file descriptor 1: /dev/pts/4
    27. ==2326== <inherited from parent>
    28. ==2326==
    29. ==2326== Open file descriptor 0: /dev/pts/4
    30. ==2326== <inherited from parent>
    31. ==2326==
    32. ==2326==
    33. /* 堆信息的总结:一共调用4次alloc,4次free。之所以正好相等,因为上面有一函数少了free,有一个函数多了一个free */
    34. ==2326== HEAP SUMMARY:
    35. ==2326== in use at exit: 353 bytes in 2 blocks
    36. ==2326== total heap usage: 4 allocs, 4 frees, 355 bytes allocated
    37. ==2326==
    38. /* 检测到一个字节的内存泄露 */
    39. ==2326== 1 bytes in 1 blocks are definitely lost in loss record 1 of 2
    40. ==2326== at 0x4005BDC: malloc (vg_replace_malloc.c:195)
    41. ==2326== by 0x8048475: mem_leak1 (in /home/fgao/works/test/a.out)
    42. ==2326== by 0x8048549: main (in /home/fgao/works/test/a.out)
    43. ==2326==
    44. /* 内存泄露的总结 */
    45. ==2326== LEAK SUMMARY:
    46. ==2326== definitely lost: 1 bytes in 1 blocks
    47. ==2326== indirectly lost: 0 bytes in 0 blocks
    48. ==2326== possibly lost: 0 bytes in 0 blocks
    49. ==2326== still reachable: 352 bytes in 1 blocks
    50. ==2326== suppressed: 0 bytes in 0 blocks
    51. ==2326== Reachable blocks (those to which a pointer was found) are not shown.
    52. ==2326== To see them, rerun with: --leak-check=full --show-reachable=yes
    53. ==2326==
    54. ==2326== For counts of detected and suppressed errors, rerun with: -v
    55. ==2326== Use --track-origins=yes to see where uninitialised values come from
    56. ==2326== ERROR SUMMARY: 5 errors from 5 contexts (suppressed: 12 from 8)
     
    这个只是一个简单的示例程序,即使没有Valgrind,我们也可以很轻易的发现问题。但是在真实的项目中,当代码量达到万行,十万行,甚至百万行时。由于申请的内存可能不是在一个地方使用,不可避免的被传来传去。这时,如果光是看review代码来检查问题,可能很难找到根本原因。这时,使用Valgrind则可以很容易的发现问题所在。
     
    当然,Valgrind也不是万能的。我也遇到过Valgrind无法找到问题,反而我通过不断的review代码找到了症结。发现问题,解决问题,毕竟是末流。最好的方法,就是不引入内存问题。这可以通过良好的代码风格和设计来实现的。
     
    写代码不是那么容易的。要用心,把代码当作自己的作品,真心的去写好它。这样,自然而然的就会把代码写好。
     
  • 相关阅读:
    [ 9.10 ]CF每日一题系列—— 186A模拟处理字符串
    python查看删除你微信的账号
    python进阶之路4.2---装饰器
    python进阶之路4.1---生成器与迭代器
    Python进阶之路---1.4python数据类型-数字
    Python进阶之路---1.3python环境搭建
    Python进阶之路---1.2python版本差异
    Python进阶之路---1.1python简介
    麦进斗Magento2 SEO默认设置的完整策划
    如何修改Magento主题中的toplinks
  • 原文地址:https://www.cnblogs.com/issuelinux/p/4281204.html
Copyright © 2020-2023  润新知