c++ summary 工具: 内存占用计算工具 Valgrind(暂记)

例子

        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使用方式

Valgrind是一款用于检测和调试C/C++程序的工具,它可以检测出程序中可能存在的内存泄漏、内存错误和线程错误等问题。以下是Valgrind的基本使用方式:

  1. 安装Valgrind:如果你使用的是Linux系统,可以通过包管理器直接安装Valgrind。例如,在Ubuntu中可以使用以下命令进行安装:
sudo apt-get install valgrind
  1. 编译程序:在使用Valgrind之前,需要先将你的C/C++程序编译成可执行文件。可以使用常规的编译器命令编译程序,例如:
gcc -g myprogram.c -o myprogram

其中,-g选项是为了生成调试信息。

  1. 运行Valgrind:使用Valgrind来运行你的程序,可以使用valgrind命令,并指定待运行的可执行文件。例如:
valgrind ./myprogram

Valgrind将会启动你的程序,并在运行过程中进行内存和线程错误的检测。

  1. 分析Valgrind输出:Valgrind运行结束后,会输出一份报告,其中包含了检测到的错误和警告信息。你可以根据报告中的提示,修复程序中的错误和问题。

需要注意的是,Valgrind的输出信息非常详细,可以运行valgrind --help来查看更多的选项和参数,以及如何过滤和解析输出。此外,Valgrind还提供了一系列的工具和选项,用于进行内存和线程错误的调试和分析,如Memcheck、Cachegrind、Helgrind等。你可以根据需要选择相应的工具和选项进行使用。

memcheck

要使用Valgrind查看程序运行需要多少内存,您可以按照以下步骤进行操作:

  1. 安装Valgrind:根据您的操作系统类型(例如Linux、Windows、Mac等),选择合适的版本,并按照安装指南进行安装。

  2. 在终端中输入以下命令使用Valgrind运行您的程序,并查看内存使用情况:

valgrind --tool=memcheck --leak-check=full ./your_program

其中,your_program是您要运行的程序的可执行文件名。--tool=memcheck指定使用Memcheck工具来检查内存使用情况,--leak-check=full指定把内存泄漏信息输出到终端。您可以根据需要调整其他Valgrind选项。

  1. 运行Valgrind后,它将会输出程序的运行结果和内存使用情况,包括内存泄漏等信息。您可以根据Valgrind的提示来分析和优化程序的内存使用。

请注意,Valgrind对程序的运行速度有一定影响,因此在性能敏感的情况下,请慎重选择使用Valgrind进行内存分析。

Conditional jump or move depends on uninitialised value

  • https://stackoverflow.com/questions/2612447/pinpointing-conditional-jump-or-move-depends-on-uninitialized-values-valgrin
==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)

–track-origins=yes

  • https://valgrind.org/docs/manual/mc-manual.html
用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

clion关于指针使用不当发生的错误 -var-create: unable to create variable object

  • https://www.cnblogs.com/secanter/p/14866691.html

c++ summary 工具: 内存占用计算工具 Valgrind(暂记)_第1张图片

缓冲区溢出

  • 缓冲区溢出:如果向栈上的缓冲区写入超过其边界rsp的数据,就可能导致数据溢出并覆盖相邻的栈帧或其他数据结构

example code

#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;
}

example_2

#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

c++ summary 工具: 内存占用计算工具 Valgrind(暂记)_第2张图片

3072 bytes below stack pointer

"3072 bytes below stack pointer"是一个警告信息,它通常在编译器或调试器中出现,并指示在当前的代码中,栈指针以下的内存区域被使用了3072字节。

栈是一种用于存储局部变量、函数调用和其他执行上下文的内存区域。栈指针(stack pointer)是一个寄存器,指示当前栈的顶部位置。

这个警告出现的原因可能是因为在当前代码中发生了栈溢出(stack overflow)。栈溢出意味着程序使用了超过栈空间所能容纳的内存,可能导致不可预测的行为和程序崩溃。

警告提示指出,栈指针以下的3072字节内存已被使用,可能是因为函数调用或大量的局部变量等操作导致的。这可能是一个潜在的问题,因为继续向下使用栈内存可能触发栈溢出。

为了解决这个问题,可以考虑以下几点:

  1. 优化代码:检查是否有过多的递归调用或者大量的局部变量占用栈空间,如果可能的话,尝试缩减栈帧的大小,减少栈上数据的使用。

  2. 增加栈空间:根据编译器和操作系统的支持,可以尝试增加栈的大小。这可以通过调整编译器选项或在程序中显式分配更大的栈来实现。

  3. 动态内存分配:如果需要处理大量的数据或者递归层次很深,可以考虑使用动态内存分配(如堆内存)来替代栈内存,以避免栈溢出问题。

需要注意的是,栈溢出可能会导致程序崩溃并具有安全风险,请在遇到此类警告时及时处理和修复。

example 3

c++ summary 工具: 内存占用计算工具 Valgrind(暂记)_第3张图片

  • 这个没有缓冲区溢出是因为,在存储时,just_initialized 存储的地址更高,并且在rsp之下。
低地址
|    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;
}

CG

有许多工具可以用来计算C++程序的内存占用。以下是一些常用的工具:

  1. Valgrind:Valgrind是一个功能强大的开源工具集,其中包括一个内存分析工具Memcheck。Memcheck能够检测和报告程序中的内存错误,并提供详细的堆栈跟踪信息。它可以帮助识别内存泄漏和其他内存相关问题。

  2. GCC的Gcov:Gcov是GCC编译器的一个插件,用于代码覆盖率分析。它可以生成程序执行期间每个代码行的统计数据,包括内存分配和释放。这可以帮助你了解程序中哪些部分占用了最多的内存。

  3. Massif:Massif是Valgrind工具集的一部分,用于生成堆栈分配信息。它可以显示程序在不同时间点的堆栈使用情况,以及每个内存分配的大小。这可以帮助你找到内存使用的高峰和泄漏。

  4. Visual Studio的内存分析工具:Visual Studio是Windows平台上的一种流行的集成开发环境。它提供了一套内存分析工具,可以帮助你分析程序的内存使用情况、检测内存泄漏和优化内存占用。

  5. Google的Perftools:Perftools是Google开发的一套性能分析工具。其中包括Heap Profiler,可以帮助你分析程序的内存使用情况,并找出内存泄漏和内存使用过多的地方。

这些工具都可以根据你的需求和平台选择使用。它们提供了不同的功能和输出格式,所以你可以根据自己的喜好选择合适的工具。

  • Chrome DevTools: Recorder
  • 【如何使用chrome开发这工具定位并解决内存泄漏问题?-哔哩哔哩】 https://b23.tv/byw2AJU

ctrl+shift+p : show performace monitor
c++ summary 工具: 内存占用计算工具 Valgrind(暂记)_第4张图片

  • 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

你可能感兴趣的:(语言学习笔记,c++)