当前位置:网站首页>Valgrind详细教程(1) Memcheck
Valgrind详细教程(1) Memcheck
2022-07-17 00:10:00 【tissar】
Valgrind详细教程(1) Memcheck
一、简介
Memcheck是Valgrind的王牌,它用于C/C++程序的内存错误检测:
- 非法访问内存(堆、栈、内存段错误)
- 引用未初始化的变量
- 非法释放对内存(重复释放、释放与申请不匹配)
- 内存重叠错误
- 内存泄露
- 错误地申请内存
此外,Memcheck还可以用于内存树的分析。
二、非法访问内存
2.1 代码
int main( void )
{
int *ptr = 0;
*ptr = 0;
return 0;
}
2.2 执行
$ gcc test.c -g
$ ./a.out
Segmentation fault (core dumped)
2.3 调试
$ valgrind ./a.out
···
==2681== Invalid write of size 4
==2681== at 0x10860A: main (test.c:4)
==2681== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==2681== ==2681== ==2681== Process terminating with default action of signal 11 (SIGSEGV) ==2681== Access not within mapped region at address 0x0 ==2681== at 0x10860A: main (test.c:4) ==2681== If you believe this happened as a result of a stack ==2681== overflow in your program's main thread (unlikely but
==2681== possible), you can try to increase the size of the
==2681== main thread stack using the --main-stacksize= flag.
==2681== The main thread stack size used in this run was 8388608.
···
test.c的第4行:非法访问地址为0x0的变量。这一错误导致程序被信号SIGSEGV终止。
注意:
- 编译时需要带调试选项
- 由于memcheck是默认工具,因此省略
--tool=<name>memcheck
三、引用未初始化的变量
3.1 代码(一)
#include <stdio.h>
int main( void )
{
int x;
printf("x = %d\n", x);
return 0;
}
3.2 调试(一)
$ valgrind ./a.out
···
==2698== Conditional jump or move depends on uninitialised value(s)
==2698== at 0x4E988DA: vfprintf (vfprintf.c:1642)
==2698== by 0x4EA0F25: printf (printf.c:33)
==2698== by 0x108667: main (test.c:6)
==2698==
==2698== Use of uninitialised value of size 8
==2698== at 0x4E9486B: _itoa_word (_itoa.c:179)
==2698== by 0x4E97F0D: vfprintf (vfprintf.c:1642)
==2698== by 0x4EA0F25: printf (printf.c:33)
==2698== by 0x108667: main (test.c:6)
···
test.c的第6行:在函数 printf() 中访问了一个未初始化的变量。
3.3 代码(二)
#include <stdlib.h>
int main( void )
{
int *arr = malloc(sizeof(int));
exit(arr[0]);
return 0;
}
3.4 调试(二)
$ valgrind ./a.out
···
==2707== Syscall param exit_group(status) contains uninitialised byte(s)
==2707== at 0x4F20E06: _Exit (_exit.c:31)
==2707== by 0x4E7F111: __run_exit_handlers (exit.c:132)
==2707== by 0x4E7F139: exit (exit.c:139)
==2707== by 0x1086AC: main (test.c:6)
···
test.c的第6行:在系统调用 exit() 中访问了一个未初始化的变量。
四、非法释放对内存
4.1 代码(一)
#include <stdlib.h>
int main( void )
{
int *ptr = malloc(10);
free(ptr);
free(ptr);
return 0;
}
4.2 调试(一)
$ valgrind ./a.out
···
==2715== Invalid free() / delete / delete[] / realloc()
==2715== at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2715== by 0x1086B7: main (test.c:7)
==2715== Address 0x522d040 is 0 bytes inside a block of size 10 free'd ==2715== at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==2715== by 0x1086AB: main (test.c:6) ==2715== Block was alloc'd at
==2715== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2715== by 0x10869B: main (test.c:5)
···
内存在 test.c 的第5行分配,第6行正确释放,而第7行发生非法释放。
4.3 代码(二)
#include <cstdlib>
int main(void)
{
int *ptr = (int*)malloc(10);
delete ptr;
return 0;
}
4.4 调试(二)
$ valgrind ./a.out
···
==2265== Mismatched free() / delete / delete []
==2265== at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2265== by 0x1086F0: main (test.cpp:6)
==2265== Address 0x5b7dc80 is 0 bytes inside a block of size 10 alloc'd
==2265== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2265== by 0x1086DB: main (test.cpp:5)
···
内存在 test.c 的第5行分配 (malloc),第6行发生非法释放 (free)。
五、内存重叠错误
5.1 代码
#include <stdio.h>
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n)
{
char *d=dest;
const char *s=src;
for( int i=0; i<n; ++i) {
d[i] = s[i];
}
return (void*)0;
}
int main(void)
{
char arr[16];
memset(arr, 0, sizeof(arr));
strcpy(arr, "overlap");
printf("%s\n", arr);
memcpy(arr+3, arr, strlen(arr));
printf("%s\n", arr);
return 0;
}
5.2 想要达到的输出
overlap
oveoverlap
5.3 发生内存重叠时的输出
overlap
oveoveoveo
5.4 Valgrind输出示例
==27492== Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)
==27492== at 0x40026CDC: memcpy (mc_replace_strmem.c:71)
==27492== by 0x804865A: main (overlap.c:40)
5.5 改正方法
将memcpy函数修改为下面代码即可
void *memcpy(void *dest, const void *src, size_t n)
{
char *arr = malloc(n);
char *d=dest;
const char *s=src;
for( int i=0; i<n; ++i) {
arr[i] = s[i];
}
for( int i=0; i<n; ++i) {
d[i] = arr[i];
}
return (void*)0;
}
通过先将src复制一份,达到防止内存覆盖错误的目的。
六、内存泄露
6.1 代码
#include <stdlib.h>
int main( void )
{
char *ptr = malloc(100);
ptr = malloc(50);
free(ptr);
return 0;
}
6.2 调试
$ valgrind --tool=memcheck --leak-check=full ./a.out
···
==2646== HEAP SUMMARY:
==2646== in use at exit: 100 bytes in 1 blocks
==2646== total heap usage: 2 allocs, 1 frees, 150 bytes allocated
==2646==
==2646== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2646== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2646== by 0x10869B: main (test.c:5)
==2646==
==2646== LEAK SUMMARY:
==2646== definitely lost: 100 bytes in 1 blocks
==2646== indirectly lost: 0 bytes in 0 blocks
==2646== possibly lost: 0 bytes in 0 blocks
==2646== still reachable: 0 bytes in 0 blocks
==2646== suppressed: 0 bytes in 0 blocks
···
第5行:malloc() 申请的内存泄露了。很明显,两个 malloc() 只有一个 free() 与之对应。
Leak Summary 各项解析:
| 类型 | 解析 |
|---|---|
| definitely lost | 确切泄露 |
| indirectly lost | 间接泄露 |
| possibly lost | 可能泄露 |
| still reachable | 未free,但尚可引用 |
七、错误地申请内存
7.1 代码
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
int val = -1;
char *ptr = malloc(val);
printf("ptr = %p \n", ptr);
if( ptr != 0 ) free(ptr);
return 0;
}
7.2 调试
$ valgrind ./a.out
···
==2885== Argument 'size' of function malloc has a fishy (possibly negative) value: -1
==2885== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2885== by 0x1086F2: main (test.c:8)
==2885==
ptr = (nil)
···
第6行:向系统申请负空间是不明智的。此时 malloc() 返回一个空指针。因此,检查 malloc() 返回的指针是很有必要的!同理,检查 malloc() 的入参也很有必要!
边栏推荐
猜你喜欢

MapReduce environment preparation

The popularity of NFT IP licensing is rising, and the era of nft2.0 is coming?

Punch in 10 interview questions every day - JVM article

如何建设实时开发平台,深入释放企业实时数据价值?

NFT数字藏品平台有哪些?哪些平台值得珍藏?

面试官问:Redis 突然变慢了如何排查?

一文揭秘育碧等传统游戏大厂的NFT策略

走好数据中台最后一公里,为什么说数据服务API是数据中台的标配?

Classification and use of express Middleware

TCP and UDP, TCP server and client, UDP server and client
随机推荐
基于CSI的通信感知一体化设计:问题、挑战和展望
玩转集群配置中心,一文带你了解Taier控制台
4章 性能平台GodEye源码分析-监控模块
欢迎进入Hensen_的博客目录(全站式导航)
NFT单月万倍神话,元宇宙之门的奥秘是什么?
binary search
wkwebview白屏
通信感知一体化应用场景、关键技术和网络架构
nodejs-uuid
【文献阅读】TENET: A Framework for Modeling Tensor Dataflow Based on Relation-centric Notation
07 BTC mining
5G专网在智慧医疗中的应用
未成年人数字安全保护的问题与对策
IPFs file persistence operation
数据指标体系如何搭建才最有效,从0到1带你快速入门丨02期直播回顾
Cento7 installs mysql5.5 and upgrades 5.7
大数据开源项目,一站式全自动化全生命周期运维管家ChengYing(承影)走向何方?
Today, the code farmer girl made notes about the life cycle and practiced the dynamic clock
手把手带你从零开始完整开发经典游戏【俄罗斯方块】,全部逻辑只用不到200行代码。
js数组处理【splice 实现数组的删除、插入、替换】