admin管理员组

文章数量:1537955

在日常开发中,遇到段错误是很常见的事,其中一种情况 Out of memory(OOM),内存泄露问题一般不好查找,但是可以借助工具来快速定位。

 这里介绍的工具是valgrind,这个工具在buildroot有提供,如下图,选中,然后重新编译

valgrind 工具

memcheck :检测程序中的内存问题,如内存泄漏,越界,非法指针等

callgrind:检测程序代码的运行时间和调用过程,以及分析程序性能。

cachegrind:分析CPU的cache命中率,丢失率,用于进行代码优化。

helgrind:用于检测多线程程序中出现的竞争问题

Massif:堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理和栈的大小。

先上个测试案例 1.c

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

void main()
{
	char *p = malloc(1024);
	char *p1 = malloc(512);
	char buf[3] = {0};
	strcpy(buf,"1234");
	printf("buf==%s\n",buf);
	p = NULL;
	while(1);
}

这个例子在给两个指针分配内存后,p直接指向了NULL,程序退出。

编译程序:

gcc 1.c -g -o 1,其中-g是为了保留符号表,可以定位到代码中具体某一行

valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --undef-value-errors=no --log-file=1.log  ./1

tool=memcheck 表示检测内存问题。

leak-check=full 表示完全检测内存泄漏

–log-file=log 表示信息会输入到log文件中(有时文件内容比较多,这样方便分析)

等程序退出或者通过kill 方式再查看log信息。

==17242== Memcheck, a memory error detector
==17242== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==17242== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==17242== Command: ./1
==17242== Parent PID: 12570
==17242== 
==17242== 
==17242== Process terminating with default action of signal 2 (SIGINT)
==17242==    at 0x4005CF: main (1.c:13)
==17242== 
==17242== HEAP SUMMARY:
==17242==     in use at exit: 1,536 bytes in 2 blocks
==17242==   total heap usage: 3 allocs, 1 frees, 2,560 bytes allocated
==17242== 
==17242== 512 bytes in 1 blocks are still reachable in loss record 1 of 2
==17242==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17242==    by 0x400594: main (1.c:8)
==17242== 
==17242== 1,024 bytes in 1 blocks are definitely lost in loss record 2 of 2
==17242==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17242==    by 0x400586: main (1.c:7)
==17242== 
==17242== LEAK SUMMARY:
==17242==    definitely lost: 1,024 bytes in 1 blocks
==17242==    indirectly lost: 0 bytes in 0 blocks
==17242==      possibly lost: 0 bytes in 0 blocks
==17242==    still reachable: 512 bytes in 1 blocks
==17242==         suppressed: 0 bytes in 0 blocks
==17242== 
==17242== For counts of detected and suppressed errors, rerun with: -v
==17242== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

 其中最重要的是 HEAP SUMMARY:  表示你的程序内存泄漏的大小

如上面显示有1536 bytes,后面就是泄露的详细信息,并且都有定位到行号。

LEAK SUMMARY关键字。表示内存泄漏的类型:

definitely lost:确定的内存泄漏,已经不能访问这块内存

indirectly lost:指向该内存的指针都位于内存泄露处

possibly lost:可能的内存泄露,仍然存在某个指针能够访问某快内存,但该指针指向的已经不是                         该内存首位置

still reachable:内存指针还在还有机会使用或者释放,指针指向的动态内存还没有被释放就退出了

通过ctrl+c退出了程序,512字节内存仍然由p1指向,但p指向NULL,所以p1部分为are still reachable,但p为are definitely lost。

valgrind还有更多强大的功能,具体可以查看 valgrind -h

关于内存还存在越界的问题。还是上面的案例,数组buf只有3个字节,但是strcpy的时候给了4个,造成越界损坏其它数据的可能性。

如何查找这种情况呢?

可以在编译的时候加上 -fsanitize=address 选项

如:gcc 1.c -fsanitize=address  -g -o 1

然后运行./1  ,就会发现

=================================================================
==32083==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd32182b33 at pc 0x7f55e23cc904 bp 0x7ffd32182af0 sp 0x7ffd32182298
WRITE of size 5 at 0x7ffd32182b33 thread T0
    #0 0x7f55e23cc903 in __asan_memcpy (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x8c903)
    #1 0x400993 in main /1.c:10
    #2 0x7f55e1f9683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #3 0x400818 in _start (/RD/alan.wang/1+0x400818)

Address 0x7ffd32182b33 is located in stack of thread T0 at offset 35 in frame
    #0 0x4008f5 in main /RD/alan.wang/1.c:6

  This frame has 1 object(s):
    [32, 35) 'buf' <== Memory access at offset 35 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow ??:0 __asan_memcpy
Shadow bytes around the buggy address:
  0x100026428510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100026428520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100026428530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100026428540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100026428550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100026428560: 00 00 f1 f1 f1 f1[03]f4 f4 f4 f3 f3 f3 f3 00 00
  0x100026428570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100026428580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100026428590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000264285a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000264285b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==32083==ABORTING

运行的时候发现越界,有地址被覆盖的提示,并且会终止程序。

本文标签: 内存快速Linux