简介

Valgrind是一个非常有用的开源工具集,主要用于Linux和其他类Unix操作系统上的代码调试、跟踪内存泄漏、性能分析以及各种内存错误等问题的诊断和解决。

Valgrind包含了多个工具,这些工具可以检测出内存泄漏、数组越界访问、使用未初始化的内存、传递未初始化的变量等等问题。同时,它还可以提供程序的时间性能的分析,以及调试信息的输出。

作为C, C++开发者,用了valgrind,妈妈再也不用担心你写的代码有内存泄漏了

怎么用

valgrind的用法非常简单,仅需要在你的执行命令前加上valgrind

$ valgrind ./your-program [your-program params]

如果你的程序是使用gcc编译的,在编译的时候加上-g选项,valgrind就可以给你更多的与错误相关的信息

看懂valgrind的错误提示

valgrind能诊断出来如下几种错误:

  • Invalid read
  • Invalid write
  • Conditional jumps
  • Syscall param points to unadressable bytes
  • Invalid free
  • Mismatched free
  • Fishy values

Invalid write

举个例子

int main(void) {
  char *str = malloc(sizeof(char) * 10);
  int i = 0;

  while (i < 15) {
    str[i] = '\0';
    i = i + 1;
  }
  free(str);
  return (0);
}

上面例子里, str指向了一块大小为10字节的内存,但是却在while循环里,设置了超过10字节的内容

$ gcc main.c -g
$ valgrind ./a.out
==18332== Memcheck, a memory error detector
==18332== Copyright (C) 2002-2017, and GNU GPL\'d, by Julian Seward et al.
==18332== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18332== Command: ./a.out
==18332==
==18332== Invalid write of size 1
==18332==    at 0x400553: main (test.c:7)
==18332==  Address 0x521004a is 0 bytes after a block of size 10 alloc\'d
==18332==    at 0x4C2EB6B: malloc (vg_replace_malloc.c:299)
==18332==    by 0x400538: main (test.c:3)
==18332==
==18332==
==18332== HEAP SUMMARY:
==18332==     in use at exit: 0 bytes in 0 blocks
==18332==   total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==18332==
==18332== All heap blocks were free'd -- no leaks are possible
==18332==
==18332== For counts of detected and suppressed errors, rerun with: -v
==18332== ERROR SUMMARY: 5 errors from 1 contexts (suppressed: 0 from 0)

“Invalid write” 意味着我们的程序将数据写到了不该写入的地方

Invalid read

int main(void) {
  int i;
  int *ptr = NULL;

  i = *ptr;
  return (0);
}

上面的代码将未初始化的指针ptr解引用,使用valgrind一起运行这段代码,会得到如下的错误

==26212== Invalid read of size 4
==26212==    at 0x400497: main (test.c:8)
==26212==  Address 0x0 is not stack\'d, malloc\'d or (recently) free\'d

Conditional jumps

int main(void) {
  int i;
  if (i == 0) {
    my_printf("Hello\n");
  }
  return (0);
}

使用valgrind诊断后,它会告诉你使用了会初始化的值来作为条件跳转

==28042== Conditional jump or move depends on uninitialised value(s)
==28042==    at 0x4004E3: main (test.c:5)

Syscall param points to unadressable bytes

int main(void) {
  int fd = open("test", O_RDONLY);
  char *buff = malloc(sizeof(char) * 3);
  free(buff);
  read(fd, buff, 2);
}

如上所示,buff所指向的内存在被free后,又被用来作为读取,此时valgrind就要敲你了

==32002== Syscall param read(buf) points to unaddressable byte(s)
==32002==    at 0x4F3B410: __read_nocancel (in /usr/lib64/libc-2.25.so)
==32002==    by 0x400605: main (test.c:11)
==32002==  Address 0x5210040 is 0 bytes inside a block of size 3 free\'d
==32002==    at 0x4C2FD18: free (vg_replace_malloc.c:530)
==32002==    by 0x4005EF: main (test.c:10)
==32002==  Block was alloc\'d at
==32002==    at 0x4C2EB6B: malloc (vg_replace_malloc.c:299)
==32002==    by 0x4005DF: main (test.c:8)

Invalid free

int main(void) {
  char *buff = malloc(sizeof(char) * 54);

  free(buff);
  free(buff);
  return (0);
}

这也是个很经典的内存释放问题,一个内存被多次释放,valgrind又要敲你了

==755== Invalid free() / delete / delete[] / realloc()
==755==    at 0x4C2FD18: free (vg_replace_malloc.c:530)
==755==    by 0x400554: main (test.c:10)
==755==  Address 0x5210040 is 0 bytes inside a block of size 54 free\'d
==755==    at 0x4C2FD18: free (vg_replace_malloc.c:530)
==755==    by 0x400548: main (test.c:9)
==755==  Block was alloc\'d at
==755==    at 0x4C2EB6B: malloc (vg_replace_malloc.c:299)
==755==    by 0x400538: main (test.c:7)

Mismatched free

int main() {
 char* p1 = (char*)malloc(sizeof(char)*4);
 delete p1;

 char* p2 = new char;
 free(p2);

 return 0;
}

用new分配的内存,用了free来释放(理应用delete); 用malloc分配的内存,用了delete来释放(理应用free)

==3281== Mismatched free() / delete / delete []
==3281==    at 0x4C2A51D: operator delete(void*) (vg_replace_malloc.c:586)
==3281==    by 0x400771: main (in /root/workspace/cpp/main)
==3281==  Address 0x5a02040 is 0 bytes inside a block of size 4 alloc'd
==3281==    at 0x4C28F73: malloc (vg_replace_malloc.c:309)
==3281==    by 0x400761: main (in /root/workspace/cpp/main)
==3281==
==3281== Mismatched free() / delete / delete []
==3281==    at 0x4C2A06D: free (vg_replace_malloc.c:540)
==3281==    by 0x40078B: main (in /root/workspace/cpp/main)
==3281==  Address 0x5a02090 is 0 bytes inside a block of size 1 alloc'd
==3281==    at 0x4C29593: operator new(unsigned long) (vg_replace_malloc.c:344)
==3281==    by 0x40077B: main (in /root/workspace/cpp/main)

Fishy values

int main() {
 int size = -10;
 char* p1 = (char*)malloc(sizeof(char)*size);

 return 0;
}

上面的程序中,不小心将需要malloc的内存大小设置为负数,valgrind可以帮我检测出来

==3378== Argument 'size' of function malloc has a fishy (possibly negative) value: -10
==3378==    at 0x4C28F73: malloc (vg_replace_malloc.c:309)
==3378==    by 0x400698: main (in /root/workspace/cpp/main)