|
From: Philippe W. <phi...@sk...> - 2010-03-21 02:17:21
|
Hello,
I have resumed the work on gdbserver integration in valgrind.
I think it is now in a reasonable state, so it is time for
comments/feedback/suggestions/criticisms/...
(I hope that with a lot of these, this work could be integrated
in valgrind :).
In summary, the integration of gdbserver provides to the
valgrind users a fully debuggable application under valgrind,
with interactive usage of valgrind functionalities (e.g.
incremental search for new leaks).
So, below follows a more in-depth functional and technical
description. Feel free to react.
A coregrind/m_gdbserver module (based on gdbserver from
gdb 7.0.1) has been integrated in valgrind.
With this, a user can debug its process running under valgrind
or the user can send commands from the shell prompt to a running
valgrind process (this is similar to the callgrind_control etc
commands but generalised to be usable by all tools).
To debug a valgrind process, the user has to enter the
following command at gdb prompt:
(gdb) target remote | vgctl
vgctl (valgrind control) is a relay application between gdb
and the gdbserver in valgrind.
If there is more than one valgrind process running, vgctl
will report the list of running process, and will ask
to give a --pid=xxxxx argument.
gdb will then connect to the gdbserver inside the valgrind process.
After that, the user can debug its "valgrind-ified" application
similarly to doing native debugging. In particular, the following
are properly supported:
detach/reattach to a process
(limitation: it cannot attach to a valgrind process which
has all its threads blocked in a syscall : valgrind
must be "running" to have attach working.
Also, it might be needed to increase the gdb timeout
using gdb command 'set remotetimeout xxx' to allow
more time to the valgrind process to go out of a
system call).
threads (info threads, switch thread, ...)
backtrace etc
examine memory (print variable, function args, ...)
change memory (set a variable, etc)
breakpoints (set a breakpoint, delete breakpoint, ...)
watchpoints (both software and "hardware read/write/access"
watchpoints are implemented. software watchpoints
are very slow, "hardware watchpoint" is
implemented using memcheck addressibility)
next/step/until/stepi/...
...
In addition, thanks to what valgrind provides, gdbserver in
valgrind allows the addition of interesting "gdb" new
functionalities.
These new functionalities are provided through gdb monitor command.
To give such a command to the valgrind process,
under gdb, you type:
(gdb) monitor ...here a command understood by valgrind...
The following general valgrind commands have been added:
general valgrind monitor commands:
vg.set vgctl-error <errornr> : debug me at error >= <errornr>
vg.set debuglog <intvalue> : set the valgrind debug log level to <intvalue>
vg.set gdb_output : redirect further valgrind output to gdb
vg.set log_output : redirect further valgrind output to log
vg.info all_errors : show all errors found so far
vg.info n_errs_found : show the nr of errors found so far
vg.continue : let valgrind process continue (no effect under gdb)
Note:
(in all these commands and in the below memcheck commands,
it is not needed to give the complete command or arg value).
For example,
monitor vg.set debuglog 3
and
mo vg.s d 3
are equivalent: gdb will interpret mo as monitor.
Valgrind will similarly parse the rest and understand vg.s and d.
If the given characters are ambiguous, an error msg is returned
giving the possible choices)
vg.set vgctl-error will cause gdbserver to wait for gdb at errors
with an error nr >= to the given error nr.
vg.set debuglog allows to dynamically change the log level of valgrind.
vg.set gdb_output allows valgrind output to be displayed by gdb instead
of going to the process log. vg.set log_output resets this to the normal
log output of the process.
vg.info n_errs_found and all_errors shows the nr of errors found by
valgrind and the details about these errors.
vg.continue is not useful under gdb, it is only useful for "standalone" vgctl
of a valgrind process (see later).
It is also possible to add new commands to each valgrind tool.
The following commands have been added to memcheck:
memcheck monitor commands:
mc.get_vbits <addr> <len>
returns validity bits for <len> bytes at <addr>
bit values 0 = valid, 1 = invalid
Example: mc.get_vbits 0x8049c78 10
mc.make_memory [noaccess|undefined|defined|ifaddressabledefined] <addr> <len>
mark len bytes at addr with the specified accessibility
mc.check_memory [addressable|defined] <addr> <len>
check that len bytes at addr have the specified accessibility
mc.leak_check [increased*|changed|any] [reachable|leak*] [full*|summary]
* = defaults
Examples:
mc.leak_check
mc.leak_check any summary
The memcheck commands are basically corresponding to the equivalent
memcheck client requests.
mc.leak_check is however a (slightly updated) version of the patch
in bugzilla 206802:
The 2nd and 3rd parameter corresponds to the current valgrind client
requests leak search.
The first parameter (which is new) allows an incremental reporting
of "increased" leaks/reachable
or of "changed" leaks/reachable (i.e. increased or decreased)
or of "any" leaks/reachable (i.e. increased or decreased or unchanged)
As an example: two successive mc.leak_check can give the following:
(gdb) monitor mc.leak_check
==10047== 136 (+136) bytes in 1 (+1) blocks are possibly lost in loss record 1 of 3
==10047== at 0x4004F4D: calloc (vg_replace_malloc.c:467)
==10047== by 0x78F7FA: _dl_allocate_tls (dl-tls.c:300)
==10047== by 0x91D4C4: pthread_create@@GLIBC_2.1 (allocatestack.c:561)
==10047== by 0x8048829: main (t.c:99)
==10047==
==10047== 136 (+136) bytes in 1 (+1) blocks are possibly lost in loss record 2 of 3
==10047== at 0x4004F4D: calloc (vg_replace_malloc.c:467)
==10047== by 0x78F7FA: _dl_allocate_tls (dl-tls.c:300)
==10047== by 0x91D4C4: pthread_create@@GLIBC_2.1 (allocatestack.c:561)
==10047== by 0x804884D: main (t.c:100)
==10047==
==10047== 136 (+136) bytes in 1 (+1) blocks are possibly lost in loss record 3 of 3
==10047== at 0x4004F4D: calloc (vg_replace_malloc.c:467)
==10047== by 0x78F7FA: _dl_allocate_tls (dl-tls.c:300)
==10047== by 0x91D4C4: pthread_create@@GLIBC_2.1 (allocatestack.c:561)
==10047== by 0x8048871: main (t.c:101)
==10047==
==10047== LEAK SUMMARY:
==10047== definitely lost: 0 (+0) bytes in 0 (+0) blocks
==10047== indirectly lost: 0 (+0) bytes in 0 (+0) blocks
==10047== possibly lost: 15408 (+408) bytes in 13 (+3) blocks
==10047== still reachable: 0 (+0) bytes in 0 (+0) blocks
==10047== suppressed: 0 (+0) bytes in 0 (+0) blocks
==10047==
(gdb) monitor mc.leak_check
==10047== LEAK SUMMARY:
==10047== definitely lost: 0 (+0) bytes in 0 (+0) blocks
==10047== indirectly lost: 0 (+0) bytes in 0 (+0) blocks
==10047== possibly lost: 15408 (+0) bytes in 13 (+0) blocks
==10047== still reachable: 0 (+0) bytes in 0 (+0) blocks
==10047== suppressed: 0 (+0) bytes in 0 (+0) blocks
==10047==
(gdb)
(as the 2nd leak_check does not report any new leak, all deltas are 0).
When a "increased" or "changed" leak is requested, the output shows
the total size and blocks for the leaking stack trace + the delta compared
to the last leak search.
By setting break at the relevant places in your program and calling
mc.leak_check, this might help to detect the place at which the last
pointer to a piece of memory is lost.
All these monitor commands can also be given to a valgrind process
using vgctl outside of gdb. For example, to ask the valgrind
process 123 to produce memory leak output, you can type the
following at the shell prompt:
$ vgctl --pid=123 mc.leak_check
You can tell a valgrind process to stop on errors and
use
$ vgctl vg.continue
to tell it to continue.
The help usage of vgctl is the following:
$ vgctl --help
vgctl (valgrind control) has two usages
1. standalone to send a command to a running valgrind-ified process.
Usage: vgctl [--pid=...] [--vgctl=...] ...command to send ...
2. relay application between gdb and the gdbserver embedded in valgrind.
Usage: vgctl [--pid=...] [--vgctl=...]
--pid optional argument must be given if there are multiple running valgrind processes
--vgctl optional argument must be given to both valgrind and vgctl utility
if you want to use non standard FIFOs prefixes for the communication between
valgrind processes and vgctl
Examples:
1. as standalone utility:
to ask memcheck to do a full memory report (leak and reachable)
vgctl mc.leak_check any reachable full
to ask new or increased leaks since last leak search
vgctl mc.leak
to tell a valgrind process to continue
vgctl vg.continue
to ask the list of commands supported by the valgrind tool
vgctl help
Note: only the first differentiating characters are needed in a command
e.g. the 2 lines below are the same command
vgctl mc.l a r f
vgctl mc.leak_check any reachable full
2. as gdb relay application:
type the following at gdb command prompt:
(gdb) target remote | vgctl --pid=1234
then debug your program as usual
Note: if your valgrind process is very slow or blocked in a system call
you might first need to give the gdb command
(gdb) set remotetimeout <timeout_limit__in_seconds>
to allow valgrind to respond and/or let you unblock the system call
--------------------- Implementation notes
To allow gdbserver and vgctl to interact, 3 new arguments have been added
to valgrind:
--vgctl-check=<number> check for gdb/vgctl after <number> basic blocks [5000]
--vgctl-error=<number> wait for gdb/vgctl after <number> errors [999999999]
--vgctl=<prefix> prefix for gdb/vgctl FIFOs [/tmp/vgctl-pipe]
--vgctl-check ensures that valgrind checks at regular interval
if vgctl has requested something. This is implemented by polling
the a file descriptor with the select system call.
This might be improved by rather checking a piece of shared memory
between vgctl and valgrind.
--vgctl-error=0 allows to attach before any user code has run
(so as e.g. to put breakpoints at the beginning).
A unmodified tool that reports errors will interact properly
with gdbserver without any modifications.
However, breakpoints/watchpoint/next/step/... implies
relatively easy changes to the instrumenting function
(as long as gdbserver is not effictively debugging
a block, the instrumentation is similar to the current valgrind.
When gdbserver is "active" on a block (e.g. there is a breakpoint
or gdbserver is single stepping), this block must re-instrumented
with some additional dirty helper calls.
The above changes have been implemented on x86/fedora12 and tested manually
with a small specific test program + tested on firefox.
Regression tests of valgrind have been run (6 new tests failing
to examine, half of them looks to be trivial "help output changes").
I also implemented the amd64 changes, but had only access during
two hours to a 64 bit fedora12 system. So, I could just see it was
working on the small specific test program compiled in 64 bits.
And I could not retest this recently.
Other platforms not coded (but the work to support a new platform
is normally relatively small: basically, it implies to write
a conversion between the guest states registers and the gdb register
in the file valgrind-low.c.
I have a few technical questions, if someone could help for these,
that would be nice.
* I see that all exported symbols in valgrind have a unique prefix
created with VG_ or MC_ or ...
This is not done for the "gdb gdbserver code", where I have kept
the original names. Is this a problem ? I could not create
a "symbol" collision between the user symbol and the valgrind
core gdbserver symbol.
* to replace the calls to select by shared memory implies to
create a piece of shared memory and map it at a good place
that will not interact with the user process.
Is there any information about which address could be used for
that ?
* if all this is to be integrated in valgrind, I guess someone
with svn write access should volunteer to create a branch.
* there are some registers in x86 or amd64 that I could not
translate to VEX registers. Someone with a good knowledge
of these architectures might complete this (see in valgrind-low.c)
* removing the limitation of not being able to attach to a
process blocked in system call(s) would be nice but I have
no idea on how to do that.
* there are a lot of additional possible things such as:
- more commands (e.g. vg.suppression [generate|add|delete] for last error)
- some "generic breaks" such as break on all function calls entry/exit
- vg.set for more valgrind parameters
- implement the Vcont packet handling allowing to continue/stop
selectively some threads (would imply some changes to valgrind scheduler
thread state)
Philippe
|
|
From: Philippe W. <phi...@sk...> - 2010-03-23 23:59:14
|
I have just posted a patch on bugzilla https://bugs.kde.org/show_bug.cgi?id=214909 more info in coregrind/m_gdbserver/README_DEVELOPPERS Feedback/comments/... welcome Philippe |