一个在valgrind中运行的程序不是直接在CPU上执行。反之,它是运行在一个由valgrind提供的仿真CPU上。这就是当程序运行在valgrind中是,debugger不能不能调试的原因。
本小节描述GDB如何能够与algrind gdbserver交互,用来支持一个在valgrind环境下调试。使用这种方法,gdb可以提供与valgrind的core或者tool交互的功能,包含在Memcheck下增加leak search和Massif快照。
最简单的方法是用--vgdb-error=0来运行valgrind。然后按照on-screen上的指示(提供详细的指令来启动gdb,并且连接到你的程序)。如果你想用memcheck来debug一个程序,像这样运行程序。
valgrind --vgdb=yes --vgdb-error=0 prog在另一个shell,运行gdb
gdb prog
(gdb) target remote | vgdb
对于algrind gdbserver的基本使用,这个快速开始的方法已经足够了。下面的章节将会描述algrind和gdb联合使用提供的更高级的功能。注意,命令行参数 --vgdb=yes可以忽略,因为这是默认值。
root@ubuntu:~# valgrind --vgdb-error=0 ls
==25403== Memcheck, a memory error detector
==25403== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==25403== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==25403== Command: ls
==25403==
==25403== (action at startup) vgdb me ...
==25403==
==25403== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==25403== /path/to/gdb ls
==25403== and then give GDB the following command
==25403== target remote | /usr/local/lib/valgrind/../../bin/vgdb --pid=25403
==25403== --pid is optional if only one valgrind process is running
==25403==
eglibc-2.15 eglibc_2.15-0ubuntu10.4.diff.gz eglibc_2.15-0ubuntu10.4.dsc eglibc_2.15.orig.tar.gz perl valgrind-3.9.0
==25403==
==25403== HEAP SUMMARY:
==25403== in use at exit: 13,540 bytes in 12 blocks
==25403== total heap usage: 48 allocs, 36 frees, 48,823 bytes allocated
==25403==
==25403== LEAK SUMMARY:
==25403== definitely lost: 0 bytes in 0 blocks
==25403== indirectly lost: 0 bytes in 0 blocks
==25403== possibly lost: 0 bytes in 0 blocks
==25403== still reachable: 13,540 bytes in 12 blocks
==25403== suppressed: 0 bytes in 0 blocks
==25403== Rerun with --leak-check=full to see details of leaked memory
==25403==
==25403== For counts of detected and suppressed errors, rerun with: -v
==25403== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
为了debug一个运行在valgrind中的prog,必须确保通过--vgdb=yes或--vgdb=full来激活valgrind gdbserver。一个辅助的命令行选项,--vgdb-error=number,可以用来告诉gdbserver在到达某个指定的数目的error时把它激活。0告诉gdbserver在startup时就激活,这样你可以在process开始运行之前插入断点。例如
valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog
==2418== Memcheck, a memory error detector ==2418== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==2418== Using Valgrind-3.7.0.SVN and LibVEX; rerun with -h for copyright info ==2418== Command: ./prog ==2418== ==2418== (action at startup) vgdb me ...然后GDB(在另一个shell中)就可以连接Valgrind gdbserver了。前提,GDB必须以prog开始运行
gdb ./prog
(gdb) target remote | vgdb
(gdb) target remote | vgdb Remote debugging using | vgdb relaying data between gdb and process 2418 Reading symbols from /lib/ld-linux.so.2...done. Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so.debug...done. Loaded symbols for /lib/ld-linux.so.2 [Switching to Thread 2418] 0x001f2850 in _start () from /lib/ld-linux.so.2 (gdb)注意vgdb是valgrind自带的,不用额外安装
(gdb) target remote | vgdb
Remote debugging using | vgdb
no --pid= arg given and multiple valgrind pids found:
use --pid=2479 for valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog
use --pid=2481 for valgrind --tool=memcheck --vgdb=yes --vgdb-error=0 ./prog
use --pid=2483 for valgrind --vgdb=yes --vgdb-error=0 ./another_prog
Remote communication error: Resource temporarily unavailable.
(gdb) target remote | vgdb --pid=2479
Remote debugging using | vgdb --pid=2479
relaying data between gdb and process 2479
Reading symbols from /lib/ld-linux.so.2...done.
Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so.debug...done.
Loaded symbols for /lib/ld-linux.so.2
[Switching to Thread 2479]
0x001f2850 in _start () from /lib/ld-linux.so.2
(gdb)
valgrind --vgdb-error=0 --vgdb=yes prog # and then in another shell, run: vgdb --port=1234在dev环境中,执行下面命令
adb forward tcp:1234 tcp:1234 gdb prog (gdb) target remote :1234
Memcheck Monitor Commands
Callgrind Monitor Commands
Massif Monitor Commands
一个tool-specific monitor command的例子是memcheck monitor command:leak_check full reachable any。它要求一个完整的内存分配报告。使用gdb命令来获得这个报告:
(gdb) monitor leak_check full reachable any
GDB把leak_check命令发送到valgrind gdbserver。如果它识别出这个字符串是一个valgrind core monitor command,valgrind gdbserver将会执行monitor命令。如果没有识别出这个命令,它就假设这个字符串是tool-specific,将会交给tool执行。例如:
(gdb) monitor leak_check full reachable any ==2418== 100 bytes in 1 blocks are still reachable in loss record 1 of 1 ==2418== at 0x4006E9E: malloc (vg_replace_malloc.c:236) ==2418== by 0x804884F: main (prog.c:88) ==2418== ==2418== LEAK SUMMARY: ==2418== definitely lost: 0 bytes in 0 blocks ==2418== indirectly lost: 0 bytes in 0 blocks ==2418== possibly lost: 0 bytes in 0 blocks ==2418== still reachable: 100 bytes in 1 blocks ==2418== suppressed: 0 bytes in 0 blocks ==2418== (gdb)除了GDB命令,valgrind gdbserver也会接受缩写的monitor命令和参数,只要这些缩写不产生歧义。例如上面的leak_check命令可以写为:
(gdb) mo l f r a
(gdb) mo v. n v. can match v.set v.info v.wait v.kill v.translate v.do (gdb) mo v.i n n_errs_found 0 n_errs_shown 0 (vgdb-error 0) (gdb)除了从GDB发送monitor命令,你也可以从shell命令行发送命令。例如下面命令行,在shell中,将会在进程3415中执行同样的leak search。
vgdb --pid=3145 leak_check full reachable any vgdb --pid=3145 l f r a
注意在一个单独的vgdb命令后,valgrind gdbserver会自动继续程序的执行。有gdb发送的monitor commands不会导致程序的继续执行:程序有gdb命令控制,例如continue/next。
(gdb) info threads 4 Thread 6239 (tid 4 VgTs_Yielding) 0x001f2832 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2 * 3 Thread 6238 (tid 3 VgTs_Runnable) make_error (s=0x8048b76 "called from London") at prog.c:20 2 Thread 6237 (tid 2 VgTs_WaitSys) 0x001f2832 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2 1 Thread 6234 (tid 1 VgTs_Yielding) main (argc=1, argv=0xbedcc274) at prog.c:105 (gdb)
对于每个CPU寄存器,valgrind core维护两组shadow register。这些shadow registers可以通过给定gdb一个后缀s1/s2来获得相应的第一/第二组shadow register。例如,x86寄存器eax和它的两个shadows可以用下面的命令获得。
(gdb) p $eax $1 = 0 (gdb) p $eaxs1 $2 = 0 (gdb) p $eaxs2 $3 = 0 (gdb)
float shadow registers在gdb中有unsigned int值代替float值,由于使用这些shadow value来做bit的memcheck。
Intel/amd64 AVX寄存器ymm0~ymm15同样也有他们的shadow registers。然而,gdb把这些shadow alues表示成两个half寄存器。例如。ymm9的half registers是xmm9s1(第一组低位),ymm9hs1是(第一组高位),xmm9s2(第二组低位),ymm9hs2(第二组高位)。
注意half registers的命名规则:低位部分以x开始,高位部分以y开始并且以h+shadow后缀结束。
AVX shadow registers以这种特殊的形式出现的原因是gdb独立分别ymm registers的低位/高位部分。GDB不知道shadow half registers必须以联合的方式显示。
×寄存器和标志位
当一个gdbserver由于错误而停止,或者由于断点或单步,由于valgrind core做的优化,导致寄存器和标志位不能总是同步更新。默认值--vex-iropt-register-updates=unwindregs-at-mem-access确保寄存器需要做stack trace(典型PC/SP/FP)更新(例如异常点)。使用下面的值来禁止一些优化将会增加寄存器/标志位的准确性(但是也会影响性能,用memcheck为例)。
--vex-iropt-register-updates=allregs-at-mem-access
(+10%) 保证每次访问内存时标志位和寄存器都更新过。--vex-iropt-register-updates=allregs-at-each-insn
(+25%) 保证执行每条指令时标志位和寄存器都更新(gdb) p printf("process being debugged has pid %d\n", getpid()) $5 = 36 (gdb)注:运行结果不是在gdb中显示,而是在gdbserver中显示。
×改变寄存器值
只有当线程的状态时runnable或者yielding时,valgrind gdbserver才会改变线程的寄存器的值。在其他状态(例如WaitSys),不会改变寄存器的值。除了其他不谈,这意味着当一个线程处于system call中时,inferior call不会执行,因为valgrind gdbserver不实现重新开始system call。
Usage: vgdb [OPTION]... [[-c] COMMAND]...
vgdb
接收下面命令:
--pid=<number>
--vgdb-prefix
--wait=<number>
--max-invoke-ms=<number>
(gdb) set remotetimeout 6
--cmd-time-out=<number>
--port=<portnr>
# On the target computer, start your program under valgrind using valgrind --vgdb-error=0 prog # and then in another shell, run: vgdb --port=1234在另一台机器上gdb,执行:
gdb prog (gdb) target remote targetip:1234targetip是目标ip或hostname
-c
vgdb v.set log_output -c leak_check any
-l
-D
-d
help [debug]
instructs Valgrind's gdbserver to give the list of all monitor commands of the Valgrind core and of the tool. The optional "debug" argument tells to also give help for the monitor commands aimed at Valgrind internals debugging.
v.info all_errors
shows all errors found so far.
v.info last_error
shows the last error found.
v.info n_errs_found [msg]
用于显示到目前为止发现的错误,到目前为止显示nr个错了,并且当前的--vgdb-error参数。option msg(一个或多个words)是附加的。典型的,它用于在几个执行的测试程序中,在一个进程的输出文件中插入marker。用于将algrind产生的错误报告关联到产生这些错误的测试中。
v.info open_fds
shows the list of open file descriptors and details related to the file descriptor. This only works if --track-fds=yes
was given at Valgrind startup.
v.set {gdb_output | log_output | mixed_output}
allows redirection of the Valgrind output (e.g. the errors detected by the tool). The default setting is mixed_output
.
With mixed_output
, the Valgrind output goes to the Valgrind log (typically stderr) while the output of the interactive GDB monitor commands (e.g. v.info last_error
) is displayed by GDB.
With gdb_output
, both the Valgrind output and the interactive GDB monitor commands output are displayed by GDB.
With log_output
, both the Valgrind output and the interactive GDB monitor commands output go to the Valgrind log.
v.wait [ms (default 0)]
instructs Valgrind gdbserver to sleep "ms" milli-seconds and then continue. When sent from a standalone vgdb, if this is the last command, the Valgrind process will continue the execution of the guest process. The typical usage of this is to use vgdb to send a "no-op" command to a Valgrind gdbserver so as to continue the execution of the guest process.
v.kill
requests the gdbserver to kill the process. This can be used from a standalone vgdb to properly kill a Valgrind process which is currently expecting a vgdb connection.
v.set vgdb-error <errornr>
dynamically changes the value of the --vgdb-error
argument. A typical usage of this is to start with --vgdb-error=0
on the command line, then set a few breakpoints, set the vgdb-error value to a huge value and continue execution.
下面的命令用于调查在有错误/bug的情况下valgrind的行为或gdbserver
v.do expensive_sanity_check_general
executes various sanity checks. In particular, the sanity of the Valgrind heap is verified. This can be useful if you suspect that your program and/or Valgrind has a bug corrupting Valgrind data structure. It can also be used when a Valgrind tool reports a client error to the connected GDB, in order to verify the sanity of Valgrind before continuing the execution.
v.info gdbserver_status
shows the gdbserver status. In case of problems (e.g. of communications), this shows the values of some relevant Valgrind gdbserver internal variables. Note that the variables related to breakpoints and watchpoints (e.g. the number of breakpoint addresses and the number of watchpoints) will be zero, as GDB by default removes all watchpoints and breakpoints when execution stops, and re-inserts them when resuming the execution of the debugged process. You can change this GDB behaviour by using the GDB command set breakpoint always-inserted on
.
v.info memory [aspacemgr]
shows the statistics of Valgrind's internal heap management. If option --profile-heap=yes
was given, detailed statistics will be output. With the optional argument aspacemgr
. the segment list maintained by valgrind address space manager will be output. Note that this list of segments is always output on the Valgrind log.
(gdb) monitor v.info memory aspacemgr
--27375-- core : 8388608/ 8388608 max/curr mmap'd, 0/0 unsplit/split sb unmmap'd, 147696/ 128216 max/curr, 14664/ 7423296 totalloc-blocks/bytes, 15142 searches 4 rzB
--27375-- dinfo : 17973248/13873152 max/curr mmap'd, 4/2 unsplit/split sb unmmap'd, 16277440/11823864 max/curr, 114215/ 40973672 totalloc-blocks/bytes, 114523 searches 4 rzB
--27375-- client : 0/ 0 max/curr mmap'd, 0/0 unsplit/split sb unmmap'd, 0/ 0 max/curr, 0/ 0 totalloc-blocks/bytes, 0 searches 20 rzB
--27375-- demangle: 0/ 0 max/curr mmap'd, 0/0 unsplit/split sb unmmap'd, 0/ 0 max/curr, 0/ 0 totalloc-blocks/bytes, 0 searches 4 rzB
--27375-- ttaux : 65536/ 65536 max/curr mmap'd, 0/0 unsplit/split sb unmmap'd, 39312/ 27792 max/curr, 265/ 71208 totalloc-blocks/bytes, 265 searches 4 rzB
在gdbserver中显示更多log信息:
--27375:0:aspacem >>>
--27375:0:aspacem <<< SHOW_SEGMENTS: gdbserver v.info memory aspacemgr (65 segments, 12 segnames)
--27375:0:aspacem ( 0) /usr/local/lib/valgrind/memcheck-x86-linux
--27375:0:aspacem ( 1) /usr/bin/perl
--27375:0:aspacem ( 2) /lib/i386-linux-gnu/ld-2.15.so
……
--27375:0:aspacem 63: ANON 00bffdf000-00bfffffff 135168 rw---
--27375:0:aspacem 64: RSVN 00c0000000-00ffffffff 1024m ----- SmFixed
--27375:0:aspacem >>>
v.info exectxt
shows informations about the "executable contexts" (i.e. the stack traces) recorded by Valgrind. For some programs, Valgrind can record a very high number of such stack traces, causing a high memory usage. This monitor command shows all the recorded stack traces, followed by some statistics. This can be used to analyse the reason for having a big number of stack traces. Typically, you will use this command if v.info memory
has shown significant memory usage by the "exectxt" arena.
v.info scheduler
shows the state and stack trace for all threads, as known by Valgrind. This allows to compare the stack traces produced by the Valgrind unwinder with the stack traces produced by GDB+Valgrind gdbserver. Pay attention that GDB and Valgrind scheduler status have their own thread numbering scheme. To make the link between the GDB thread number and the corresponding Valgrind scheduler thread number, use the GDB command info threads
. The output of this command shows the GDB thread number and the valgrind 'tid'. The 'tid' is the thread number output by v.info scheduler
. When using the callgrind tool, the callgrind monitor command status
outputs internal callgrind information about the stack/call graph it maintains.
(gdb) monitor v.info scheduler
sched status:
running_tid=1
Thread 1: status = VgTs_Runnable
==27375== at 0x40011C0: ??? (in /lib/i386-linux-gnu/ld-2.15.so)
v.set debuglog <intvalue>
sets the Valgrind debug log level to <intvalue>. This allows to dynamically change the log level of Valgrind e.g. when a problem is detected.
v.translate <address> [<traceflags>]
shows the translation of the block containing address
with the given trace flags. The traceflags
value bit patterns have similar meaning to Valgrind's --trace-flags
option. It can be given in hexadecimal (e.g. 0x20) or decimal (e.g. 32) or in binary 1s and 0s bit (e.g. 0b00100000). The default value of the traceflags is 0b00100000, corresponding to "show after instrumentation". The output of this command always goes to the Valgrind log.The additional bit flag 0b100000000 (bit 8) has no equivalent in the --trace-flags
option. It enables tracing of the gdbserver specific instrumentation. Note that this bit 8 can only enable the addition of gdbserver instrumentation in the trace. Setting it to 0 will not disable the tracing of the gdbserver instrumentation if it is active for some other reason, for example because there is a breakpoint at this address or because gdbserver is in single stepping mode.