【学习点滴】linux调试工具、cmake和网络抓包

本文详细介绍了Linux下的调试工具,如gdb的多进程和多线程调试,以及Valgrind、strace、核心转储配置等。此外,还涵盖了网络抓包工具如wireshark、tcpdump等的使用,以及cmake和makefile的编写。通过实例展示了如何利用这些工具进行程序调试和性能分析,是提升Linux开发技能的重要参考资料。
摘要由CSDN通过智能技术生成

目录

gdb

多进程调试

多线程调试:

gdb底层原理

Linux下查看服务器端的并发连接个数:

Valgrind

memcheck

strace

Linux下,绑定1024以下的端口需要root权限!

webbench 压测工具

wireshark 网络抓包记录

tcpdump

traceroute

iftop

iperf

perf

linux下各种日志

makefile 的编写

cmake 的编写

VIM的常用操作


这是一篇工具博客,记录了各种工具的使用和体验

gdb

编译cpp文件应使用g++

若运行错误程序不产生core文件,可使用

ulimit -a

来查看当前是否开启

先用#ulimit -a可以查看系统core文件的大小限制(第一行),core文件大小设置为0, 即没有打开core dump设置;我这个是改变了的。

user@ubuntu:~/usegdb$ ulimit -a
core file size          (blocks, -c) unlimited
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 8333
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 8333
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

临时改变方法:

使用#ulimit -c [kbytes]可以设置系统允许生成的core文件大小;

ulimit -c 0 不产生core文件

ulimit -c 100 设置core文件最大为100k

ulimit -c unlimited 不限制core文件大小

永久改变方法:

  #vi /etc/profile 然后,在profile中添加:

ulimit -c 1073741824

  (但是,若将产生的转储文件大小大于该数字时,将不会产生转储文件)

或者

ulimit -c unlimited

这样重启机器后生效了。 或者, 使用source命令使之马上生效。

 #source /etc/profile

 

更改core dump生成路径

因为core dump默认会生成在程序的工作目录,但是有些程序存在切换目录的情况,导致core dump生成的路径没有规律,

所以最好是自己建立一个文件夹,存放生成的core文件。

我建立一个 /data/coredump 文件夹,在根目录data里的coredump文件夹。

调用如下命令

echo /data/coredump/core.%e.%p> /proc/sys/kernel/core_pattern

将更改core文件生成路径,自动放在这个/data/coredump文件夹里。

%e表示程序名, %p表示进程id

设置 Core Dump 的核心转储文件目录和命名规则

/proc/sys/kernel/core_uses_pid 可以控制产生的 core 文件的文件名中是否添加 pid 作为扩展 ,如果添加则文件内容为 1 ,否则为 0

proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名 ,比如原来文件内容是 core-%e

可以这样修改 :

echo "/corefile/core-%e-%p-%t" > core_pattern

将会控制所产生的 core 文件会存放到 /corefile 目录下,产生的文件名为 core- 命令名 -pid- 时间戳

以下是参数列表 :

    %p - insert pid into filename 添加 pid

    %u - insert current uid into filename 添加当前 uid

    %g - insert current gid into filename 添加当前 gid

    %s - insert signal that caused the coredump into the filename 添加导致产生 core 的信号

    %t - insert UNIX time that the coredump occurred into filename 添加 core 文件生成时的 unix 时间

    %h - insert hostname where the coredump happened into filename 添加主机名

    %e - insert coredumping executable name into filename 添加命令名

core文件的使用

在core文件所在目录下键入:
gdb -c core   (-c指定core文件)
它会启动GNU的调试器,来调试core文件,并且会显示生成此core文件的程序名,中止此程序的信号等等
如果你已经知道是由什么程序生成此core文件的,比如MyServer崩溃了生成core.12345,那么用此指令调试:
gdb -c core MyServer

 

内核映像转储(dump core),内核映像转储是指将进程数据在内存的映像和进程在内核结构中的部分内容以一定格式转储到文件系统,并且进程退出执行,这样做的好处是为程序员 提供了方便,使得他们可以得到进程当时执行时的数据值,允许他们确定转储的原因,并且可以调试他们的程序。

 

gdb使用:

bt=back trace 打印堆栈信息:

首先信件一个bengkui.cpp文件写入如下程序

#include <iostream>
#include <stdio.h>

using namespace std;
void core_test1()
{
    int i = 0;
    //below will call segmentfault
    scanf("%d", i);
    printf("%d", i);
    
}


int main()
{
    core_test1();
    return 0;

该程序在core_test1()内部scanf的时候回崩溃,i前面应该加上&

编译的时候带上-g选项,这样才能用gdb调试core

g++ bengkui.cpp -g -o benkui

接着运行程序./bengkui

输入2就会触发  段错误(核心转储),

user@ubuntu:~/study$ ./bengkui 
2
段错误 (核心已转储)
user@ubuntu:~/study$ ll
总用量 584
drwxrwxr-x  3 user user   4096 Apr 24 20:09 ./
drwxr-xr-x 23 user user   4096 Apr 24 19:59 ../
-rwxrwxr-x  1 user user  19704 Apr 20 10:35 bengkui*
-rw-rw-r--  1 user user    228 Apr 20 10:35 bengkui.c
-rw-------  1 user user 561152 Apr 24 20:09 core
drwxrwxr-x  2 user user   4096 Feb 24 21:38 socket_demo/

 

然后用gdb调试这个程序

gdb ./bengkui core

在gdb下使用 r 来运行程序,出现段错误,然后bt即可看进程退出时的栈的内存状态

(gdb) r
Starting program: /home/user/study/bengkui 
2

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff76eade5 in _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>, 
    argptr=argptr@entry=0x7fffffffe238, errp=errp@entry=0x0) at vfscanf.c:1902
(gdb) bt
#0  0x00007ffff76eade5 in _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>, 
    argptr=argptr@entry=0x7fffffffe238, errp=errp@entry=0x0) at vfscanf.c:1902
#1  0x00007ffff76f587b in __scanf (format=<optimized out>) at scanf.c:33
#2  0x0000000000400769 in core_test1 () at bengkui.c:9
#3  0x0000000000400789 in main () at bengkui.c:17



(gdb) proc_info
Undefined command: "proc_info".  Try "help".
(gdb) info proc
process 2335
cmdline = '/home/user/study/bengkui'
cwd = '/home/user/study'
exe = '/home/user/study/bengkui'

 

 

GDB 常用操作

上边的程序比较简单,不需要另外的操作就能直接找到问题所在。现实却不是这样的,常常需要进行单步跟踪,设置断点之类的操作才能顺利定位问题。下边列出了GDB一些常用的操作。

  • 启动程序:run

     

  • 设置断点:b 行号|函数名                     如 b 22     或者   b main     多文件下打断点  b main.cpp:147

    b +offset/-offset    在当前行的前面或者后面的offset停住

    b filename:linenum   在某文件的某行打断点

    b filename:function 在某文件某个函数入口处停住

               b *address      在程序的运行地址处停住

    b           没有参数在下一行停住

    b where if condition 在满足条件的那一行停住

  • 删除断点:delete 断点编号

     

  • 禁用断点:disable 断点编号

     

  • 启用断点:enable 断点编号

     

  • 单步跟踪:next 也可以简写 n

     

  • 单步跟踪:step 也可以简写 s

            step count  一次性执行count步,有函数进入函数

    next count  一次性执行count步,不进入函数

    finish     运行函数,知道当前函数完成返回,并打印出函数返回时的堆栈地址、返回值和参数信息

    until      退出循环体

  • 打印变量:print 变量名字                     如print i

         x 按十六进制格式显示变量

         d 按十进制格式显示变量

         u 按十六进制格式显示无符号整型退出

          o 按八进制格式显示变量

           t 按二进制格式显示变量t 按二进制格式显示变量

           a 按十六进制格式显示变量

           c 按字符格式显示变量

            f 按浮点数格式显示变量

  • 设置变量:set var=value                如set  var i=1

     

  • 查看变量类型:ptype var

     

  • 顺序执行到结束:cont

     

  • 顺序执行到某一行: util lineno

     

  • 打印堆信息:bt

      frame x 切换到第x帧。其中x会在bt命令中显示,从0开始。0表示栈顶。简写为f。

      up/down x 往栈顶/栈底移动x帧。当不输入x时,默认为1。

      print x打印x的信息,x可以是变量,也可以是对象或者数组。简写为p。

      print */&x 打印x的内容/地址。

      call 调用函数。注意此命令需要一个正在运行的程序。

  • 查看寄存器的内容:info registers/all-registers
  • display 变量名   可以追踪变量的值                         对应undisplay  变量编号       可以取消追踪(info display查看)
  • info local           可以打印出当前所有的变量值
  • finish       跳出当前函数
  • u      跳出单次循环
  • del或d 断点编号(info b可以查看)     可以删除断点
  • ptype  变量名     可以看一个变量的类型
  • list 显示多行源代码      如: list -30 显示第30行为中心的10行代码         list 函数名   显示此函数为中心的10行代码     

                                             list - 显示之前的代码

  • watch    可设置观察点(watchpoint)。使用观察点可以使得当某表达式的值发生变化时,程序暂停执行。    如watch i

                                                 执行该命令前,必须保证程序已经运行

    watch  expr  为表达式expr设置一个观察点一旦表达式的值有变化,马上停止程序

    rwatch    expr  当表达式被读时,停住程序

    awctah  expr  当表达式的值被读或者背写时,停住程序

  • continue 或 c         继续执行(如从上个断点往下)
  • attach pid   可以调试正在运行的进程       (attach附着后,用正常的指令调试)
  • detach      断开附着,使得程序继续正常运行
  • exammine命令:

        查看内存地址的值。语法是:x/u addr

(gdb) attach 3273
Attaching to process 3273
[New LWP 3274]
[New LWP 3275]
[New LWP 3276]
[New LWP 3277]
[New LWP 3327]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007ff4a4285a13 in epoll_wait () at ../sysdeps/unix/syscall-template.S:84
84	../sysdeps/unix/syscall-template.S: 没有那个文件或目录.
(gdb) bt
#0  0x00007ff4a4285a13 in epoll_wait () at ../sysdeps/unix/syscall-template.S:84
#1  0x000000000040ba23 in Epoll::poll (this=0x7ff4a3b67010)
    at /home/user/WebServer-master/WebServer/Epoll.cpp:104
#2  0x000000000040c6d4 in EventLoop::loop (this=this@entry=0x7ffffcbedea0)
    at /home/user/WebServer-master/WebServer/EventLoop.cpp:119
#3  0x000000000040a2bc in main (argc=3, argv=0x7ffffcbee028)
    at /home/user/WebServer-master/WebServer/Main.cpp:53

ps -ef | grep "main"(main此处是可执行文件名)搜索到子进程pid

多进程调试

https://www.cnblogs.com/JN-PDD/p/6953136.html

1.单独调试子进程

 我们可以先运行程序,然后再另一终端使用ps -ef | grep "main"(main此处是可执行文件名)搜索到子进程pid,然后启动gdb,在将其附加(attach)到gdb调试器上

attach child-pid        使用该命令后,直接run即可,和调试普通程序就没区别了

dettach                     脱离进程

2.使用调试器选项follow-fork-mode

我们知道如果不设置任何选项,gdb默认调试父进程。调试器选项用法如下:

set follow-fork-mode mode     其中mode的可选值是parent和child,分别表示调试父进程和子进程。

info inferiors                            查询正在调试的进程

inferior processnum                切换进程 

默认设置下,在调试多进程程序时GDB只会调试主进程。但是GDB(>V7.0)支持多进程的分别以及同时调试,换句话说,GDB可以同时调试多个程序。只需要设置follow-fork-mode(默认值:parent)和detach-on-fork(默认值:on)即可。我们还可以使用catch fork指令,如果fork异常,会停止程序。

        follow-fork-mode  detach-on-fork   说明
        parent                     on               只调试主进程(GDB默认)
        child                        on               只调试子进程
        parent                     off               同时调试两个进程,gdb跟主进程,子进程block在fork位置
        child                        off               同时调试两个进程,gdb跟子进程,主进程block在fork位置

 设置方法:set follow-fork-mode [parent|child]   set detach-on-fork [on|off]

多线程调试:

    gdb调试一般有两种模式:all-stop模式和no-stop模式(gdb7.0之前不支持no-stop模式)。

    1.all-stop模式

        在这种模式下,当你的程序在gdb由于任何原因而停止,所有的线程都会停止,而不仅仅是当前的线程。一般来说,gdb不能单步所有的线程。因为线程调度是gdb无法控制的。无论什么时候当gdb停止你的程序,它都会自动切换到触发断点的那个线程。

    2.no-stop模式(网络编程常用)

        顾名思义,启动不关模式。当程序在gdb中停止,只有当前的线程会被停止,而其他线程将会继续运行。这时候step,next这些命令就只对当前线程起作用。

gdb支持的命里有两种类型:前台的(同步的)和后台(异步 )的。区别很简单,同步的在输出提示符之前会等待程序report一些线程已经终止的信息,异步则是直接返回。所以我们需要set target-async 1。set pagination off不要出现 Type <return> to continue 的提示信息 。最后一步是打开。

      下面是常用命令:

info threads                                       显示所有线程

thread <id>                                           切换到指定id的线程

break filename:linenum thread all     在所有线程相应行设置断点,注意如果主线程不会执行到该行,并且启动all-stop模式,主线程执行n或s会切换过去    如break file.c:100 thread all    在file.c文件第100行处为所有经过这里的线程设置断点。

对指定线程或者所有线程执行同样的操作,比如查看调用栈信息:thread apply ID1 ID2/all bt

set scheduler-locking off|on|step    
      在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,
      怎么只让被调试程序执行呢?
      通过这个命令就可以实现这个需求。
         off      不锁定任何线程,也就是所有线程都执行,这是默认值。
         on       只有当前被调试程序会执行。
         step     在单步的时候,除了next过一个函数的情况
                  (熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,
                  只有当前线程会执行。

show scheduler-locking                    显示当前模式

thread apply all command                每个线程执行同意命令,如bt。或者thread apply 1 3 bt,即线程1,3执行bt。

                                                            如thread apply 2 n  让线程2继续执行程序

主要是我们要用能用的上的,比如no-stop模式,一般多线程调试就很有用的。

使用示例:
线程产生通知:在产生新的线程时, gdb会给出提示信息
(gdb) r
Starting program: /root/thread 
[New Thread 1073951360 (LWP 12900)] 
[New Thread 1082342592 (LWP 12907)]---以下三个为新产生的线程
[New Thread 1090731072 (LWP 12908)]
[New Thread 1099119552 (LWP 12909)]

查看线程:使用info threads可以查看运行的线程。
(gdb) info threads
  4 Thread 1099119552 (LWP 12940)   0xffffe002 in ?? ()
  3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
  2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931)   main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)
注意,行首为gdb分配的线程ID号,对线程进行切换时,使用该ID号码。
另外,行首的星号标识了当前活动的线程
切换线程:
使用 thread THREADNUMBER 进行切换,THREADNUMBER 为上文提到的线程ID号。
下例显示将活动线程从 1 切换至 4。
(gdb) info threads
   4 Thread 1099119552 (LWP 12940)   0xffffe002 in ?? ()
   3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
   2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931)   main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb) thread 4
[Switching to thread 4 (Thread 1099119552 (LWP 12940))]#0   0xffffe002 in ?? ()
(gdb) info threads
* 4 Thread 1099119552 (LWP 12940)   0xffffe002 in ?? ()
   3 Thread 1090731072 (LWP 12939)   0xffffe002 in ?? ()
   2 Thread 1082342592 (LWP 12938)   0xffffe002 in ?? ()
   1 Thread 1073951360 (LWP 12931)   main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)
以上即为使用gdb提供的对多线程进行调试的一些基本命令。

 

待我好好做个实际调试的例子

 

大牛调试教程:可以好好研究研究

https://blog.csdn.net/wu_cai_/article/details/79669842

 

gdb底层原理

    gdb主要功能的实现依赖于一个系统函数ptrace,通过man手册可以了解到,ptrace可以让父进程观察和控制其子进程的检查、执行,改变其寄存器和内存的内容,主要应用于打断点(也是gdb的主要功能)和打印系统调用轨迹。

函数原型如下:

#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid,void *addr, void *data);

ptrace系统调用的request主要选项

  • PTRACE_TRACEME

表示本进程将被其父进程跟踪,交付给这个进程的所有信号(除SIGKILL之外),都将使其停止,父进程将通过wait()获知这一情况。

  • PTRACE_ATTACH

attach到一个指定的进程,使其成为当前进程跟踪的子进程,子进程的行为等同于它进行了一次PTRACE_TRACEME操作。但是,需要注意的是,虽然当前进程成为被跟踪进程的父进程,但是子进程使用getppid()的到的仍将是其原始父进程的pid。

    这下子gdb的attach功能也就明朗了。当你在gdb中使用attach命令来跟踪一个指定进程/线程的时候,gdb就自动成为改进程的父进程,而被跟踪的进程则使用了一次PTRACE_TRACEME,gdb也就顺理成章的接管了这个进程。

  • PTRACE_CONT

继续运行之前停止的子进程。可同时向子进程交付指定的信号。

更多参数请man ptrace。

gdb 2种调试方式

1)attach并调试一个已经运行的进程:
      确定需要进行调试的进程id
      运行gdb,输入attch pid,如:gdb 12345。gdb将对指定进行执行如下操作:ptrace(PTRACE_ATTACH,pid,0,0)
2)运行并调试一个新的进程
      运行gdb,通过命令行参数或file指定目标调试程序,如gdb ./test
      输入run命令,gdb执行下述操作:
      通过fork()系统调用创建一个新进程
      在新创建的子进程中调用ptrace(PTRACE_TRACEME,0,0,0)
      在子进程中通过execv()系统调用加载用户指定的可执行文件

 

gdb调试的基础—信号

 gdb调试的实现都是建立在信号的基础上的,在使用参数为PTRACE_TRACEME或PTRACE_ATTACH的ptrace系统调用建立调试关系后,交付给目标程序的任何信号首先都会被gdb截获。
  因此gdb可以先行对信号进行相应处理,并根据信号的属性决定是否要将信号交付给目标程序。

  • 1、设置断点:   

    断点原理:

  1)    断点的实现原理,就是在指定的位置插入断点指令,当被调试的程序运行到断点的时候,产生SIGTRAP信号。该信号被gdb捕获并进行断点命中判定,当gdb判断出这次SIGTRAP是断点命中之后就会转入等待用户输入进行下一步处理,否则继续。 

  2)    断点的设置原理: 在程序中设置断点,就是先将该位置的原来的指令保存,然后向该位置写入int 3。当执行到int 3的时候,发生软中断,内核会给子进程发出SIGTRAP信号,当然这个信号会被转发给父进程。然后用保存的指令替换int3,等待恢复运行。

  3)    断点命中判定:gdb把所有的断点位置都存放在一个链表中,命中判定即把被调试程序当前停止的位置和链表中的断点位置进行比较,看是断点产生的信号,还是无关信号。

  4)    条件断点的判定:原理同3),只是恢复断点处的指令后,再多加一步条件判断。若表达式为真,则触发断点。由于需要判断一次,因此加入条件断点后,不管有没有触发到条件断点,都会影响性能。在x86平台,某些硬件支持硬件断点,在条件断点处不插入int    3,而是插入一个其他指令,当程序走到这个地址的时候,不发出int 3信号,而是先去比较一下特定寄存器和某个地址的内容,再决定是否发送int 3。因此,当你的断点的位置会被程序频繁地“路过”时,尽量使用硬件断点,会对提高性能有帮助

  • 2、next单步调试:

  next指令可以实现单步调试,即每次只执行一行语句。一行语句可能对应多条及其指令,当执行next指令时,gdb会计算下一条语句对应的第一条指令的地址,然后控制目标程序走到该位置停止。ptrace本身支持单步功能,调用ptrace(PTRACE_SINGLESTEP,pid,...)即可。
这里写图片描述

 

 

 

 

Linux下查看服务器端的并发连接个数:

netstat -nat|grep ESTABLISHED|wc -l

这个返回的数字就是当前并发的连接数的了。

查看所有状态的个数:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
解释:

返回结果示例:

LAST_ACK 5   (正在等待处理的请求数)

SYN_RECV 30

ESTABLISHED 1597 (正常数据传输状态)

FIN_WAIT1 51

FIN_WAIT2 504

TIME_WAIT 1057 (处理完毕,等待超时结束的请求数)

 

状态:描述

CLOSED:无连接是活动的或正在进行

LISTEN:服务器在等待进入呼叫

SYN_RECV:一个连接请求已经到达,等待确认

SYN_SENT:应用已经开始,打开一个连接

ESTABLISHED:正常数据传输状态

FIN_WAIT1:应用说它已经完成

FIN_WAIT2:另一边已同意释放

ITMED_WAIT:等待所有分组死掉

CLOSING:两边同时尝试关闭

TIME_WAIT:另一边已初始化一个释放

LAST_ACK:等待所有分组死掉
 

 

 

 

Valgrind

一个进程的空间是开辟在内存上的,包括栈、堆都是在内存上,只是分区不同,因此调试内存泄露实际上用ps -aux也可以进行判断,此进程的内存占用量是否一直在增长

 

原文:https://blog.csdn.net/ydyang1126/article/details/72667411 

1. 什么是内存泄漏
内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的、大小任意的(内存块的大小可以在程序运行期决定)、使用完后必须显示释放的内存。应用程序一般使用malloc、realloc、new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块。否则,这块内存就不能被再次使用,造成这块内存泄漏。

2. 内存泄漏的检测
C++程序缺乏相应的手段来检测内存信息,只能使用top指令观察进程的动态内存总额。而且程序退出时,我们无法获知任何内存泄漏信息。

使用Linux命令回收内存:可以使用ps、kill两个命令检测内存使用情况和进行回收。 
在使用超级用户权限时使用命令“ps”,它会列出所有正在运行的程序名称和对应的进程号(PID)。 
kill命令的工作原理是向Linux操作系统的内核送出一个系统操作信号和程序的进程号(PID)。

3. Valgrind
3.1 Valgrind体系结构
Valgrind是一套Linux下,开源的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。Valgrind的体系结构如下图所示: 


Valgrind包括如下一些工具: 
1. Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。 
2. Callgrind。它主要用来检查程序中函数调用过程中出现的问题。 
3. Cachegrind。它主要用来检查程序中缓存使用出现的问题。 
4. Helgrind。它主要用来检查多线程程序中出现的竞争问题。 
5. Massif。它主要用来检查程序中堆栈使用中出现的问题。 
6. Extension。可以利用core提供的功能,自己编写特定的内存调试工具。

3.2 Valgrind.Memcheck 检测内存原理


Memcheck 能够检测出内存问题,关键在于其建立了两个全局表

1. Valid-Value 表
对于进程的整个地址空间中的每一个字节(byte),都有与之对应的 8 个 bits;对于 CPU 的每个寄存器,也有一个与之对应的 bit 向量。这些 bits 负责记录该字节或者寄存器值是否具有有效的、已初始化的值。

2. Valid-Address 表
对于进程整个地址空间中的每一个字节(byte),还有与之对应的 1 个 bit,负责记录该地址是否能够被读写。

检测原理:
当要读写内存中某个字节时,首先检查这个字节对应的 A bit。如果该A bit显示该位置是无效位置,memcheck 则报告读写错误。 
内核(core)类似于一个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节对应的 V bit 也被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 memcheck 会检查对应的V bits,如果该值尚未初始化,则会报告使用未初始化内存错误。

如何知道那些地址是合法的(内存已分配)?

  • 维护一张合法地址表(Valid-address (A) bits),当前所有可以合法读写(已分配)的地址在其中有对应的表项。该表通过以下措施维护
  • 全局数据(data, bss section)--在程序启动的时候标记为合法地址
  • 局部变量--监控sp(stack pointer)的变化,动态维护
  • 动态分配的内存--截获 分配/释放 内存的调用 :malloc, calloc, realloc, valloc, memalign, free, new, new
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值