Valgrind能够帮助发现代码中的细节问题,比如malloc的地址用delete删除而非free(gcc有时不会报错)。
// https://blog.csdn.net/qd1308504206/article/details/103273447 - [https://blog.csdn.net/ysuncn/article/details/1796540]https://blog.csdn.net/ysuncn/article/details/1796540)
#include
using namespace std;
bool Init(int* p)
{
*p = 1;
return true;
}
int main()
{
int *p = NULL;
{
int a = 10;
p = &a;
}
bool bl = Init(p);//访问了非法的栈空间,【栈空间已经释放】
cout << *p << endl;
//上面小例子,部分编译器会正确输出结果,但是并不代表上述情况是正确的。
//是因为编译器还没有来得及完全释放临时变量,导致结果可能正确。
//但是实际项目中,系统非常复杂。内存的申请和释放非常的频繁。所以复杂的系统才更容易重现上述问题。
return 0;
}
Valgrind是一款用于检测和调试C/C++程序的工具,它可以检测出程序中可能存在的内存泄漏、内存错误和线程错误等问题。以下是Valgrind的基本使用方式:
sudo apt-get install valgrind
gcc -g myprogram.c -o myprogram
其中,-g
选项是为了生成调试信息。
valgrind
命令,并指定待运行的可执行文件。例如:valgrind ./myprogram
Valgrind将会启动你的程序,并在运行过程中进行内存和线程错误的检测。
需要注意的是,Valgrind的输出信息非常详细,可以运行valgrind --help
来查看更多的选项和参数,以及如何过滤和解析输出。此外,Valgrind还提供了一系列的工具和选项,用于进行内存和线程错误的调试和分析,如Memcheck、Cachegrind、Helgrind等。你可以根据需要选择相应的工具和选项进行使用。
要使用Valgrind查看程序运行需要多少内存,您可以按照以下步骤进行操作:
安装Valgrind:根据您的操作系统类型(例如Linux、Windows、Mac等),选择合适的版本,并按照安装指南进行安装。
在终端中输入以下命令使用Valgrind运行您的程序,并查看内存使用情况:
valgrind --tool=memcheck --leak-check=full ./your_program
其中,your_program
是您要运行的程序的可执行文件名。--tool=memcheck
指定使用Memcheck工具来检查内存使用情况,--leak-check=full
指定把内存泄漏信息输出到终端。您可以根据需要调整其他Valgrind选项。
请注意,Valgrind对程序的运行速度有一定影响,因此在性能敏感的情况下,请慎重选择使用Valgrind进行内存分析。
==60337== Conditional jump or move depends on uninitialised value(s)
==60337== at 0x4B1BA1F: __sin_fma (s_sin.c:212)
==60337== by 0x11EF7C: ggml_vec_relu_f32 (ggml.c:3558)
==60337== by 0x135C94: ggml_compute_forward_relu_f32 (ggml.c:10343)
==60337== by 0x135CDE: ggml_compute_forward_relu (ggml.c:10356)
==60337== by 0x14912F: ggml_compute_forward_unary (ggml.c:14915)
==60337== by 0x14BA8C: ggml_compute_forward (ggml.c:15779)
==60337== by 0x14F642: ggml_graph_compute_thread (ggml.c:17116)
==60337== by 0x150C6B: ggml_graph_compute (ggml.c:17630)
==60337== by 0x150EBD: ggml_graph_compute_with_ctx (ggml.c:17680)
==60337== by 0x114583: ggml_opt_adam (main.cpp:599)
==60337== by 0x114F1E: main (main.cpp:887)
==60337== Use of uninitialised value of size 8
==60337== at 0x4852990: memmove (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==60337== by 0x4C0522F: __printf_fp_l (printf_fp.c:535)
==60337== by 0x4C209AC: __printf_fp_spec (vfprintf-internal.c:354)
==60337== by 0x4C209AC: __vfprintf_internal (vfprintf-internal.c:1558)
==60337== by 0x4C0A81E: printf (printf.c:33)
==60337== by 0x114BDC: ggml_opt_adam (main.cpp:700)
==60337== by 0x114F1E: main (main.cpp:887)
==60337== Syscall param write(buf) points to uninitialised byte(s)
==60337== at 0x4CBEA37: write (write.c:26)
==60337== by 0x4C34F6C: _IO_file_write@@GLIBC_2.2.5 (fileops.c:1180)
==60337== by 0x4C36A60: new_do_write (fileops.c:448)
==60337== by 0x4C36A60: _IO_new_do_write (fileops.c:425)
==60337== by 0x4C36A60: _IO_do_write@@GLIBC_2.2.5 (fileops.c:422)
==60337== by 0x4C35754: _IO_new_file_xsputn (fileops.c:1243)
==60337== by 0x4C35754: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1196)
==60337== by 0x4C20049: outstring_func (vfprintf-internal.c:239)
==60337== by 0x4C20049: __vfprintf_internal (vfprintf-internal.c:1593)
==60337== by 0x4C0A81E: printf (printf.c:33)
==60337== by 0x114BDC: ggml_opt_adam (main.cpp:700)
==60337== by 0x114F1E: main (main.cpp:887)
==60337== Address 0x4de8cad is 45 bytes inside a block of size 1,024 alloc'd
==60337== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==60337== by 0x4C28C23: _IO_file_doallocate (filedoalloc.c:101)
==60337== by 0x4C37D5F: _IO_doallocbuf (genops.c:347)
==60337== by 0x4C36FDF: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:744)
==60337== by 0x4C35754: _IO_new_file_xsputn (fileops.c:1243)
==60337== by 0x4C35754: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1196)
==60337== by 0x4C2A056: fwrite (iofwrite.c:39)
==60337== by 0x49B5B34: std::basic_ostream >& std::__ostream_insert >(std::basic_ostream >&, char const*, long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30)
==60337== by 0x49B5E8A: std::basic_ostream >& std::operator<< >(std::basic_ostream >&, char const*) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30)
==60337== by 0x112D41: get_random_tensor(ggml_context*, int, long*, float, float) (main.cpp:104)
==60337== by 0x1133A3: hf_getcompute_graph() (main.cpp:193)
==60337== by 0x114FD0: __static_initialization_and_destruction_0(int, int) (main.cpp:244)
==60337== by 0x11524B: _GLOBAL__sub_I__Z5frandv (main.cpp:895)
==60337==
==60337== HEAP SUMMARY:
==60337== in use at exit: 104,857,616 bytes in 3 blocks
==60337== total heap usage: 8 allocs, 5 frees, 104,931,418 bytes allocated
==60337==
==60337== LEAK SUMMARY:
==60337== definitely lost: 12 bytes in 1 blocks
==60337== indirectly lost: 0 bytes in 0 blocks
==60337== possibly lost: 0 bytes in 0 blocks
==60337== still reachable: 104,857,604 bytes in 2 blocks
==60337== suppressed: 0 bytes in 0 blocks
==60337== Rerun with --leak-check=full to see details of leaked memory
==60337==
==60337== Use --track-origins=yes to see where uninitialised values come from
==60337== For lists of detected and suppressed errors, rerun with: -s
==60337== ERROR SUMMARY: 1032601 errors from 126 contexts (suppressed: 0 from 0)
用valgrind对代码进行内存检测的时候,如果提示“Conditional jump or move depends on uninitialised value(s)”,有可能是某些变量未初始化造成的。
例如我遇到的两处这样的提示,一处是由于 struct tm 结构体未初始化,另一处是由于 char tmp[512]未初始化造成的。要初始化,只需memset即可,这样做之后,valgrind不再会提示有问题。
请在90%以上的时间里相信valgrind,而不是坚持自己的代码不需要做任何改动。
————————————————
版权声明:本文为CSDN博主「codelast.com」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/learnhard/article/details/5615135
#include
using namespace std;
struct test{
bool just_initialized;
// char mem_buffer[3];
// char mem_buffer[1000];
};
void bugfunc()
{
struct test * opt ;
opt = (struct test *) alloca(sizeof(struct test));
//opt = (struct test *) malloc(sizeof(struct test));
}
int main()
{
bugfunc();
return 0;
}
#include
using namespace std;
struct context{
int just_initialized;
char mem_buffer[30];
};
struct hf_app{
public:
struct context * opt ;
hf_app(){
this->opt = (struct context *) alloca(sizeof(struct context));
this->opt->just_initialized = 6;
if (this->opt->just_initialized) {
printf("invalid read of size mhf_app--%d\n",this->opt->just_initialized);
}
}
};
void func(){
cout<<"Hello World"<just_initialized) {
printf("invalid read of size mhf_app--%d\n",mhf_app.opt->just_initialized);
}
}
int main()
{
// ggml_opt_context* opt_test_20230909 = (struct context *) alloca(sizeof(struct ggml_opt_context));
// if (opt_test_20230909->just_initialized) {
// printf("invalid read of size opt_test_20230909");
//
// }
func();
return 0;
}
/home/pdd/valgrind/untitled/cmake-build-debug/untitled
Hello World
invalid read of size mhf_app--6
invalid read of size mhf_app--6
Process finished with exit code 0
"3072 bytes below stack pointer"是一个警告信息,它通常在编译器或调试器中出现,并指示在当前的代码中,栈指针以下的内存区域被使用了3072字节。
栈是一种用于存储局部变量、函数调用和其他执行上下文的内存区域。栈指针(stack pointer)是一个寄存器,指示当前栈的顶部位置。
这个警告出现的原因可能是因为在当前代码中发生了栈溢出(stack overflow)。栈溢出意味着程序使用了超过栈空间所能容纳的内存,可能导致不可预测的行为和程序崩溃。
警告提示指出,栈指针以下的3072字节内存已被使用,可能是因为函数调用或大量的局部变量等操作导致的。这可能是一个潜在的问题,因为继续向下使用栈内存可能触发栈溢出。
为了解决这个问题,可以考虑以下几点:
优化代码:检查是否有过多的递归调用或者大量的局部变量占用栈空间,如果可能的话,尝试缩减栈帧的大小,减少栈上数据的使用。
增加栈空间:根据编译器和操作系统的支持,可以尝试增加栈的大小。这可以通过调整编译器选项或在程序中显式分配更大的栈来实现。
动态内存分配:如果需要处理大量的数据或者递归层次很深,可以考虑使用动态内存分配(如堆内存)来替代栈内存,以避免栈溢出问题。
需要注意的是,栈溢出可能会导致程序崩溃并具有安全风险,请在遇到此类警告时及时处理和修复。
低地址
| mem_buffer |
| | -> 3000 字节
| |
| PAD | -> 0-4 字节(填充字节)
| |
| just_initialized | -> 4 字节
高地址
#include
#include
#include
using namespace std;
struct context{
// int just_initialized;
char mem_buffer[3000];
int just_initialized;
};
struct hf_app{
public:
struct context * opt ;
hf_app(){
this->opt = (struct context *) alloca(sizeof(struct context));
this->opt->just_initialized = 6;
memset(this->opt->mem_buffer,'a',sizeof(char)*20);
if (this->opt->just_initialized) {
printf("invalid read of size mhf_app--%d\n",this->opt->just_initialized);
}
}
};
void func(){
cout<<"Hello World"<just_initialized = true;
if (mhf_app.opt->just_initialized) {
printf("invalid read of size mhf_app--%d\n",mhf_app.opt->just_initialized);
}
printf("invalid read of size mhf_app--%d\n",mhf_app.opt->just_initialized);
}
int main()
{
// ggml_opt_context* opt_test_20230909 = (struct context *) alloca(sizeof(struct ggml_opt_context));
// if (opt_test_20230909->just_initialized) {
// printf("invalid read of size opt_test_20230909");
//
// }
func();
return 0;
}
有许多工具可以用来计算C++程序的内存占用。以下是一些常用的工具:
Valgrind:Valgrind是一个功能强大的开源工具集,其中包括一个内存分析工具Memcheck。Memcheck能够检测和报告程序中的内存错误,并提供详细的堆栈跟踪信息。它可以帮助识别内存泄漏和其他内存相关问题。
GCC的Gcov:Gcov是GCC编译器的一个插件,用于代码覆盖率分析。它可以生成程序执行期间每个代码行的统计数据,包括内存分配和释放。这可以帮助你了解程序中哪些部分占用了最多的内存。
Massif:Massif是Valgrind工具集的一部分,用于生成堆栈分配信息。它可以显示程序在不同时间点的堆栈使用情况,以及每个内存分配的大小。这可以帮助你找到内存使用的高峰和泄漏。
Visual Studio的内存分析工具:Visual Studio是Windows平台上的一种流行的集成开发环境。它提供了一套内存分析工具,可以帮助你分析程序的内存使用情况、检测内存泄漏和优化内存占用。
Google的Perftools:Perftools是Google开发的一套性能分析工具。其中包括Heap Profiler,可以帮助你分析程序的内存使用情况,并找出内存泄漏和内存使用过多的地方。
这些工具都可以根据你的需求和平台选择使用。它们提供了不同的功能和输出格式,所以你可以根据自己的喜好选择合适的工具。
ctrl+shift+p : show performace monitor
valgrind 的使用及错误信息分析
一个 Valgrind Address is on Thread’s 1 stack 搞笑场景
【c++ valgrind gdb内存泄漏-哔哩哔哩】 https://b23.tv/BQaYheI
https://blog.csdn.net/weixin_45590473/article/details/113057545