mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 10:05:29 +00:00
(fixes bug 289939 wish: complete monitor cmd 'leak_check' with details
about leaked or reachable blocks)
This patch implements two new memcheck gdbserver monitor commands:
block_list <loss_record_nr>
after a leak search, shows the list of blocks of <loss_record_nr>
who_points_at <addr> [<len>]
shows places pointing inside <len> (default 1) bytes at <addr>
(with len 1, only shows "start pointers" pointing exactly to <addr>,
with len > 1, will also show "interior pointers")
Compiled and reg-tested on f12/x86, deb5/amd64, f16/ppc64.
The 'block_list' command is implemented on top of the
lr_array/lc_chunks/lc_extras arrays used during the last leak search.
NB: no impact on the memory for the typical Valgrind usage where a leak
search is only done at the end of the run.
Printing the block_list of a loss record simply consists in scanning the
lc_chunks to find back the chunks corresponding to the loss record for which
block lists is requested.
The 'who_points_at' command is implemented by doing a scan similar to
(but simpler than) the leak search scan.
lc_scan_memory has been enhanced to have a mode to search for a specific
address, rather than to search for all allocated blocks.
VG_(apply_to_GP_regs) has been enhanced to also provide the ThreadId and
register name in the callback function.
The patch touches multiple files (but most changes are easy/trivial or factorise
existing code).
Most significant changes are in memcheck/mc_leakcheck.c :
* changed the LC_Extra struct to remember the clique for indirect leaks
(size of structure not changed).
* made lr_array a static global
* changed lc_scan_memory:
to have a search mode for a specific address (for who_points_at)
(for leak search) to pass a 'current clique' in addition to the clique
leader
so as to have a proper clique hierarchy for indirectly leaked blocks.
* print_results: reset values at the beginning of the print_result of the
next leak search, rather than at the end of print_results of the previous
leak search.
This allows to continue showing the same info for loss records till a new
leak search is done.
* new function print_clique which recursively prints a group of leaked
blocks, starting from the clique leader.
* new function MC_(print_block_list) : calls print_clique for each clique
leader found for the given loss record.
* static void scan_memory_root_set : code extracted from
MC_(detect_memory_leaks) (no relevant change)
* void MC_(who_points_at) : calls scan_memory_root_set, lc_scan_memory
and VG_(apply_to_GP_regs)(search_address_in_GP_reg) to search
pointers to the given address.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12357
This commit is contained in:
parent
af76b5a089
commit
ce806ed31f
6
NEWS
6
NEWS
@ -13,6 +13,11 @@ Release 3.8.0 (????)
|
||||
- reduction of memory use for applications allocating
|
||||
many blocks and/or having many partially defined bytes.
|
||||
|
||||
- Addition of GDB server monitor command 'block_list' that lists
|
||||
the addresses/sizes of the blocks of a leak search loss record.
|
||||
|
||||
- Addition of GDB server monitor command 'who_points_at' that lists
|
||||
the locations pointing at a block.
|
||||
|
||||
* ==================== OTHER CHANGES ====================
|
||||
|
||||
@ -43,6 +48,7 @@ where XXXXXX is the bug number as listed below.
|
||||
286374 Running cachegrind with --branch-sim=yes on 64-bit PowerPC program fails
|
||||
287858 VG_(strerror): unknown error
|
||||
289699 vgdb connection in relay mode erroneously closed due to buffer overrun
|
||||
289939 wish: complete monitor cmd 'leak_check' with details about leaked or reachable blocks
|
||||
|
||||
Release 3.7.0 (5 November 2011)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -305,11 +305,9 @@ TODO and/or additional nice things to have
|
||||
its arguments like the startup options of m_main.c and tool clo processing.
|
||||
|
||||
* have a memcheck monitor command
|
||||
who_points_at <address> | <loss_record_nr>
|
||||
that would describe the addresses where a pointer is found
|
||||
to address (or address leaked at loss_record_nr>)
|
||||
This would allow to interactively searching who is "keeping" a piece
|
||||
of memory.
|
||||
show_dangling_pointers [last_n_recently_released_blocks]
|
||||
showing which of the n last recently released blocks are still
|
||||
referenced. These references are (potential) dangling pointers.
|
||||
|
||||
* some GDBTD in the code
|
||||
|
||||
|
||||
@ -186,115 +186,115 @@ VG_(set_shadow_regs_area) ( ThreadId tid,
|
||||
}
|
||||
|
||||
|
||||
static void apply_to_GPs_of_tid(VexGuestArchState* vex, void (*f)(Addr))
|
||||
static void apply_to_GPs_of_tid(ThreadId tid, void (*f)(ThreadId, HChar*, Addr))
|
||||
{
|
||||
VexGuestArchState* vex = &(VG_(get_ThreadState)(tid)->arch.vex);
|
||||
#if defined(VGA_x86)
|
||||
(*f)(vex->guest_EAX);
|
||||
(*f)(vex->guest_ECX);
|
||||
(*f)(vex->guest_EDX);
|
||||
(*f)(vex->guest_EBX);
|
||||
(*f)(vex->guest_ESI);
|
||||
(*f)(vex->guest_EDI);
|
||||
(*f)(vex->guest_ESP);
|
||||
(*f)(vex->guest_EBP);
|
||||
(*f)(tid, "EAX", vex->guest_EAX);
|
||||
(*f)(tid, "ECX", vex->guest_ECX);
|
||||
(*f)(tid, "EDX", vex->guest_EDX);
|
||||
(*f)(tid, "EBX", vex->guest_EBX);
|
||||
(*f)(tid, "ESI", vex->guest_ESI);
|
||||
(*f)(tid, "EDI", vex->guest_EDI);
|
||||
(*f)(tid, "ESP", vex->guest_ESP);
|
||||
(*f)(tid, "EBP", vex->guest_EBP);
|
||||
#elif defined(VGA_amd64)
|
||||
(*f)(vex->guest_RAX);
|
||||
(*f)(vex->guest_RCX);
|
||||
(*f)(vex->guest_RDX);
|
||||
(*f)(vex->guest_RBX);
|
||||
(*f)(vex->guest_RSI);
|
||||
(*f)(vex->guest_RDI);
|
||||
(*f)(vex->guest_RSP);
|
||||
(*f)(vex->guest_RBP);
|
||||
(*f)(vex->guest_R8);
|
||||
(*f)(vex->guest_R9);
|
||||
(*f)(vex->guest_R10);
|
||||
(*f)(vex->guest_R11);
|
||||
(*f)(vex->guest_R12);
|
||||
(*f)(vex->guest_R13);
|
||||
(*f)(vex->guest_R14);
|
||||
(*f)(vex->guest_R15);
|
||||
(*f)(tid, "RAX", vex->guest_RAX);
|
||||
(*f)(tid, "RCX", vex->guest_RCX);
|
||||
(*f)(tid, "RDX", vex->guest_RDX);
|
||||
(*f)(tid, "RBX", vex->guest_RBX);
|
||||
(*f)(tid, "RSI", vex->guest_RSI);
|
||||
(*f)(tid, "RDI", vex->guest_RDI);
|
||||
(*f)(tid, "RSP", vex->guest_RSP);
|
||||
(*f)(tid, "RBP", vex->guest_RBP);
|
||||
(*f)(tid, "R8" , vex->guest_R8 );
|
||||
(*f)(tid, "R9" , vex->guest_R9 );
|
||||
(*f)(tid, "R10", vex->guest_R10);
|
||||
(*f)(tid, "R11", vex->guest_R11);
|
||||
(*f)(tid, "R12", vex->guest_R12);
|
||||
(*f)(tid, "R13", vex->guest_R13);
|
||||
(*f)(tid, "R14", vex->guest_R14);
|
||||
(*f)(tid, "R15", vex->guest_R15);
|
||||
#elif defined(VGA_ppc32) || defined(VGA_ppc64)
|
||||
(*f)(vex->guest_GPR0);
|
||||
(*f)(vex->guest_GPR1);
|
||||
(*f)(vex->guest_GPR2);
|
||||
(*f)(vex->guest_GPR3);
|
||||
(*f)(vex->guest_GPR4);
|
||||
(*f)(vex->guest_GPR5);
|
||||
(*f)(vex->guest_GPR6);
|
||||
(*f)(vex->guest_GPR7);
|
||||
(*f)(vex->guest_GPR8);
|
||||
(*f)(vex->guest_GPR9);
|
||||
(*f)(vex->guest_GPR10);
|
||||
(*f)(vex->guest_GPR11);
|
||||
(*f)(vex->guest_GPR12);
|
||||
(*f)(vex->guest_GPR13);
|
||||
(*f)(vex->guest_GPR14);
|
||||
(*f)(vex->guest_GPR15);
|
||||
(*f)(vex->guest_GPR16);
|
||||
(*f)(vex->guest_GPR17);
|
||||
(*f)(vex->guest_GPR18);
|
||||
(*f)(vex->guest_GPR19);
|
||||
(*f)(vex->guest_GPR20);
|
||||
(*f)(vex->guest_GPR21);
|
||||
(*f)(vex->guest_GPR22);
|
||||
(*f)(vex->guest_GPR23);
|
||||
(*f)(vex->guest_GPR24);
|
||||
(*f)(vex->guest_GPR25);
|
||||
(*f)(vex->guest_GPR26);
|
||||
(*f)(vex->guest_GPR27);
|
||||
(*f)(vex->guest_GPR28);
|
||||
(*f)(vex->guest_GPR29);
|
||||
(*f)(vex->guest_GPR30);
|
||||
(*f)(vex->guest_GPR31);
|
||||
(*f)(vex->guest_CTR);
|
||||
(*f)(vex->guest_LR);
|
||||
(*f)(tid, "GPR0" , vex->guest_GPR0 );
|
||||
(*f)(tid, "GPR1" , vex->guest_GPR1 );
|
||||
(*f)(tid, "GPR2" , vex->guest_GPR2 );
|
||||
(*f)(tid, "GPR3" , vex->guest_GPR3 );
|
||||
(*f)(tid, "GPR4" , vex->guest_GPR4 );
|
||||
(*f)(tid, "GPR5" , vex->guest_GPR5 );
|
||||
(*f)(tid, "GPR6" , vex->guest_GPR6 );
|
||||
(*f)(tid, "GPR7" , vex->guest_GPR7 );
|
||||
(*f)(tid, "GPR8" , vex->guest_GPR8 );
|
||||
(*f)(tid, "GPR9" , vex->guest_GPR9 );
|
||||
(*f)(tid, "GPR10", vex->guest_GPR10);
|
||||
(*f)(tid, "GPR11", vex->guest_GPR11);
|
||||
(*f)(tid, "GPR12", vex->guest_GPR12);
|
||||
(*f)(tid, "GPR13", vex->guest_GPR13);
|
||||
(*f)(tid, "GPR14", vex->guest_GPR14);
|
||||
(*f)(tid, "GPR15", vex->guest_GPR15);
|
||||
(*f)(tid, "GPR16", vex->guest_GPR16);
|
||||
(*f)(tid, "GPR17", vex->guest_GPR17);
|
||||
(*f)(tid, "GPR18", vex->guest_GPR18);
|
||||
(*f)(tid, "GPR19", vex->guest_GPR19);
|
||||
(*f)(tid, "GPR20", vex->guest_GPR20);
|
||||
(*f)(tid, "GPR21", vex->guest_GPR21);
|
||||
(*f)(tid, "GPR22", vex->guest_GPR22);
|
||||
(*f)(tid, "GPR23", vex->guest_GPR23);
|
||||
(*f)(tid, "GPR24", vex->guest_GPR24);
|
||||
(*f)(tid, "GPR25", vex->guest_GPR25);
|
||||
(*f)(tid, "GPR26", vex->guest_GPR26);
|
||||
(*f)(tid, "GPR27", vex->guest_GPR27);
|
||||
(*f)(tid, "GPR28", vex->guest_GPR28);
|
||||
(*f)(tid, "GPR29", vex->guest_GPR29);
|
||||
(*f)(tid, "GPR30", vex->guest_GPR30);
|
||||
(*f)(tid, "GPR31", vex->guest_GPR31);
|
||||
(*f)(tid, "CTR" , vex->guest_CTR );
|
||||
(*f)(tid, "LR" , vex->guest_LR );
|
||||
#elif defined(VGA_arm)
|
||||
(*f)(vex->guest_R0);
|
||||
(*f)(vex->guest_R1);
|
||||
(*f)(vex->guest_R2);
|
||||
(*f)(vex->guest_R3);
|
||||
(*f)(vex->guest_R4);
|
||||
(*f)(vex->guest_R5);
|
||||
(*f)(vex->guest_R6);
|
||||
(*f)(vex->guest_R8);
|
||||
(*f)(vex->guest_R9);
|
||||
(*f)(vex->guest_R10);
|
||||
(*f)(vex->guest_R11);
|
||||
(*f)(vex->guest_R12);
|
||||
(*f)(vex->guest_R13);
|
||||
(*f)(vex->guest_R14);
|
||||
(*f)(tid, "R0" , vex->guest_R0 );
|
||||
(*f)(tid, "R1" , vex->guest_R1 );
|
||||
(*f)(tid, "R2" , vex->guest_R2 );
|
||||
(*f)(tid, "R3" , vex->guest_R3 );
|
||||
(*f)(tid, "R4" , vex->guest_R4 );
|
||||
(*f)(tid, "R5" , vex->guest_R5 );
|
||||
(*f)(tid, "R6" , vex->guest_R6 );
|
||||
(*f)(tid, "R8" , vex->guest_R8 );
|
||||
(*f)(tid, "R9" , vex->guest_R9 );
|
||||
(*f)(tid, "R10", vex->guest_R10);
|
||||
(*f)(tid, "R11", vex->guest_R11);
|
||||
(*f)(tid, "R12", vex->guest_R12);
|
||||
(*f)(tid, "R13", vex->guest_R13);
|
||||
(*f)(tid, "R14", vex->guest_R14);
|
||||
#elif defined(VGA_s390x)
|
||||
(*f)(vex->guest_r0);
|
||||
(*f)(vex->guest_r1);
|
||||
(*f)(vex->guest_r2);
|
||||
(*f)(vex->guest_r3);
|
||||
(*f)(vex->guest_r4);
|
||||
(*f)(vex->guest_r5);
|
||||
(*f)(vex->guest_r6);
|
||||
(*f)(vex->guest_r7);
|
||||
(*f)(vex->guest_r8);
|
||||
(*f)(vex->guest_r9);
|
||||
(*f)(vex->guest_r10);
|
||||
(*f)(vex->guest_r11);
|
||||
(*f)(vex->guest_r12);
|
||||
(*f)(vex->guest_r13);
|
||||
(*f)(vex->guest_r14);
|
||||
(*f)(vex->guest_r15);
|
||||
(*f)(tid, "r0" , vex->guest_r0 );
|
||||
(*f)(tid, "r1" , vex->guest_r1 );
|
||||
(*f)(tid, "r2" , vex->guest_r2 );
|
||||
(*f)(tid, "r3" , vex->guest_r3 );
|
||||
(*f)(tid, "r4" , vex->guest_r4 );
|
||||
(*f)(tid, "r5" , vex->guest_r5 );
|
||||
(*f)(tid, "r6" , vex->guest_r6 );
|
||||
(*f)(tid, "r7" , vex->guest_r7 );
|
||||
(*f)(tid, "r8" , vex->guest_r8 );
|
||||
(*f)(tid, "r9" , vex->guest_r9 );
|
||||
(*f)(tid, "r10", vex->guest_r10);
|
||||
(*f)(tid, "r11", vex->guest_r11);
|
||||
(*f)(tid, "r12", vex->guest_r12);
|
||||
(*f)(tid, "r13", vex->guest_r13);
|
||||
(*f)(tid, "r14", vex->guest_r14);
|
||||
(*f)(tid, "r15", vex->guest_r15);
|
||||
#else
|
||||
# error Unknown arch
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void VG_(apply_to_GP_regs)(void (*f)(UWord))
|
||||
void VG_(apply_to_GP_regs)(void (*f)(ThreadId, HChar*, UWord))
|
||||
{
|
||||
ThreadId tid;
|
||||
|
||||
for (tid = 1; tid < VG_N_THREADS; tid++) {
|
||||
if (VG_(is_valid_tid)(tid)) {
|
||||
ThreadState* tst = VG_(get_ThreadState)(tid);
|
||||
apply_to_GPs_of_tid(&(tst->arch.vex), f);
|
||||
apply_to_GPs_of_tid(tid, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,10 @@ dist_noinst_SCRIPTS = \
|
||||
|
||||
EXTRA_DIST = \
|
||||
README_DEVELOPERS \
|
||||
mcblocklistsearch.stderr.exp \
|
||||
mcblocklistsearch.stdinB.gdb \
|
||||
mcblocklistsearch.vgtest \
|
||||
mcblocklistsearch.stderrB.exp \
|
||||
mcbreak.stderrB.exp \
|
||||
mcbreak.stderr.exp \
|
||||
mcbreak.stdinB.gdb \
|
||||
|
||||
@ -10,7 +10,7 @@ $dir/filter_stderr |
|
||||
$dir/../tests/filter_addresses |
|
||||
|
||||
# memcheck stuff
|
||||
$dir/filter_memcheck_monitor |
|
||||
$dir/filter_memcheck_monitor "$@" |
|
||||
|
||||
|
||||
# Anonymise or remove :
|
||||
|
||||
0
gdbserver_tests/mcblocklistsearch.stderr.exp
Normal file
0
gdbserver_tests/mcblocklistsearch.stderr.exp
Normal file
67
gdbserver_tests/mcblocklistsearch.stderrB.exp
Normal file
67
gdbserver_tests/mcblocklistsearch.stderrB.exp
Normal file
@ -0,0 +1,67 @@
|
||||
relaying data between gdb and process ....
|
||||
vgdb-error value changed from 0 to 999999
|
||||
Breakpoint 1 at 0x........: file leak-tree.c, line 42.
|
||||
Breakpoint 2 at 0x........: file leak-tree.c, line 67.
|
||||
Continuing.
|
||||
Breakpoint 1, f () at leak-tree.c:42
|
||||
42 t->l = mk(); // B
|
||||
Continuing.
|
||||
Breakpoint 2, main () at leak-tree.c:67
|
||||
67 PRINT_LEAK_COUNTS(stderr);
|
||||
Searching for pointers to 0x........
|
||||
*0x........ points at 0x........
|
||||
Address 0x........ is 0 bytes inside data symbol "t"
|
||||
full leak search
|
||||
16 bytes in 1 blocks are still reachable in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:41)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
16 bytes in 1 blocks are still reachable in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:42)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
16 bytes in 1 blocks are still reachable in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:45)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
16 bytes in 1 blocks are indirectly lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:46)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
16 bytes in 1 blocks are indirectly lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:47)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
16 bytes in 1 blocks are definitely lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:44)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
48 (16 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:43)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
block list 6 D
|
||||
16 bytes in 1 blocks are definitely lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:44)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
0x........[16]
|
||||
block list 7 C F G
|
||||
48 (16 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record ... of ...
|
||||
at 0x........: malloc (vg_replace_malloc.c:...)
|
||||
by 0x........: mk (leak-tree.c:28)
|
||||
by 0x........: f (leak-tree.c:43)
|
||||
by 0x........: main (leak-tree.c:63)
|
||||
0x........[16]
|
||||
0x........[16] indirect loss record 4
|
||||
0x........[16] indirect loss record 5
|
||||
monitor command request to kill this process
|
||||
Remote connection closed
|
||||
31
gdbserver_tests/mcblocklistsearch.stdinB.gdb
Normal file
31
gdbserver_tests/mcblocklistsearch.stdinB.gdb
Normal file
@ -0,0 +1,31 @@
|
||||
# connect gdb to Valgrind gdbserver:
|
||||
target remote | ./vgdb --wait=60 --vgdb-prefix=./vgdb-prefix-mcblocklistsearch
|
||||
echo vgdb launched process attached\n
|
||||
monitor v.set vgdb-error 999999
|
||||
#
|
||||
#
|
||||
# insert break after the allocation of A
|
||||
break leak-tree.c:42
|
||||
# insert break after returning from function f
|
||||
break leak-tree.c:67
|
||||
#
|
||||
# continue till //1break:
|
||||
continue
|
||||
# save the value of A
|
||||
set $0xA = t
|
||||
#
|
||||
# continue till 2nd break
|
||||
continue
|
||||
#
|
||||
# check who points at A
|
||||
eval "monitor who_points_at 0x%x 1", $0xA
|
||||
# do a leak check, and then list the blocks lost
|
||||
echo full leak search \n
|
||||
monitor leak_check full reachable any
|
||||
#
|
||||
echo block list 6 D \n
|
||||
monitor block_list 6
|
||||
echo block list 7 C F G \n
|
||||
monitor block_list 7
|
||||
monitor v.kill
|
||||
quit
|
||||
12
gdbserver_tests/mcblocklistsearch.vgtest
Normal file
12
gdbserver_tests/mcblocklistsearch.vgtest
Normal file
@ -0,0 +1,12 @@
|
||||
# test the memcheck block_list and search monitor commands.
|
||||
prog: ../memcheck/tests/leak-tree
|
||||
vgopts: --tool=memcheck --vgdb=yes --vgdb-error=0 --vgdb-prefix=./vgdb-prefix-mcblocklistsearch -q
|
||||
prereq: test -e gdb.eval
|
||||
stdout_filter: filter_make_empty
|
||||
stderr_filter: filter_make_empty
|
||||
progB: gdb
|
||||
argsB: --quiet -l 60 --nx 1>&2 ../memcheck/tests/leak-tree
|
||||
stdinB: mcblocklistsearch.stdinB.gdb
|
||||
stdoutB_filter: filter_make_empty
|
||||
stderrB_filter: filter_gdb
|
||||
stderrB_filter_args: leak-tree.c
|
||||
@ -28,6 +28,12 @@ memcheck monitor commands:
|
||||
Examples: leak_check
|
||||
leak_check summary any
|
||||
leak_check full reachable any limited 100
|
||||
block_list <loss_record_nr>
|
||||
after a leak search, shows the list of blocks of <loss_record_nr>
|
||||
who_points_at <addr> [<len>]
|
||||
shows places pointing inside <len> (default 1) bytes at <addr>
|
||||
(with len 1, only shows "start pointers" pointing exactly to <addr>,
|
||||
with len > 1, will also show "interior pointers")
|
||||
|
||||
general valgrind monitor commands:
|
||||
help [debug] : monitor command help. With debug: + debugging commands
|
||||
@ -67,5 +73,11 @@ memcheck monitor commands:
|
||||
Examples: leak_check
|
||||
leak_check summary any
|
||||
leak_check full reachable any limited 100
|
||||
block_list <loss_record_nr>
|
||||
after a leak search, shows the list of blocks of <loss_record_nr>
|
||||
who_points_at <addr> [<len>]
|
||||
shows places pointing inside <len> (default 1) bytes at <addr>
|
||||
(with len 1, only shows "start pointers" pointing exactly to <addr>,
|
||||
with len > 1, will also show "interior pointers")
|
||||
|
||||
monitor command request to kill this process
|
||||
|
||||
@ -121,7 +121,8 @@ void VG_(set_syscall_return_shadows) ( ThreadId tid,
|
||||
// current threads.
|
||||
// This is very Memcheck-specific -- it's used to find the roots when
|
||||
// doing leak checking.
|
||||
extern void VG_(apply_to_GP_regs)(void (*f)(UWord val));
|
||||
extern void VG_(apply_to_GP_regs)(void (*f)(ThreadId tid,
|
||||
HChar* regname, UWord val));
|
||||
|
||||
// This iterator lets you inspect each live thread's stack bounds.
|
||||
// Returns False at the end. 'tid' is the iterator and you can only
|
||||
|
||||
@ -1372,7 +1372,7 @@ $10 = 0x0
|
||||
(default 1) bytes at <addr> has the specified accessibility.
|
||||
It then outputs a description of <addr>. In the following
|
||||
example, a detailed description is available because the
|
||||
option <option>--read-var-info=yes</option> was given Valgrind at
|
||||
option <option>--read-var-info=yes</option> was given at Valgrind
|
||||
startup:
|
||||
</para>
|
||||
<programlisting><![CDATA[
|
||||
@ -1443,31 +1443,31 @@ Address 0x8049E28 len 1 defined
|
||||
there was no increase since the previous leak search.</para>
|
||||
<programlisting><![CDATA[
|
||||
(gdb) monitor leak_check full possibleleak increased
|
||||
==14729== 16 (+16) bytes in 1 (+1) blocks are possibly lost in loss record 13 of 16
|
||||
==14729== at 0x4006E9E: malloc (vg_replace_malloc.c:236)
|
||||
==14729== by 0x80484D5: mk (leak-cases.c:52)
|
||||
==14729== by 0x804855F: f (leak-cases.c:81)
|
||||
==14729== by 0x80488F5: main (leak-cases.c:107)
|
||||
==14729==
|
||||
==14729== LEAK SUMMARY:
|
||||
==14729== definitely lost: 32 (+0) bytes in 2 (+0) blocks
|
||||
==14729== indirectly lost: 16 (+0) bytes in 1 (+0) blocks
|
||||
==14729== possibly lost: 32 (+16) bytes in 2 (+1) blocks
|
||||
==14729== still reachable: 96 (+16) bytes in 6 (+1) blocks
|
||||
==14729== suppressed: 0 (+0) bytes in 0 (+0) blocks
|
||||
==14729== Reachable blocks (those to which a pointer was found) are not shown.
|
||||
==14729== To see them, add 'reachable any' args to leak_check
|
||||
==14729==
|
||||
==19520== 16 (+16) bytes in 1 (+1) blocks are possibly lost in loss record 9 of 12
|
||||
==19520== at 0x40070B4: malloc (vg_replace_malloc.c:263)
|
||||
==19520== by 0x80484D5: mk (leak-cases.c:52)
|
||||
==19520== by 0x804855F: f (leak-cases.c:81)
|
||||
==19520== by 0x80488E0: main (leak-cases.c:107)
|
||||
==19520==
|
||||
==19520== LEAK SUMMARY:
|
||||
==19520== definitely lost: 32 (+0) bytes in 2 (+0) blocks
|
||||
==19520== indirectly lost: 16 (+0) bytes in 1 (+0) blocks
|
||||
==19520== possibly lost: 32 (+16) bytes in 2 (+1) blocks
|
||||
==19520== still reachable: 96 (+16) bytes in 6 (+1) blocks
|
||||
==19520== suppressed: 0 (+0) bytes in 0 (+0) blocks
|
||||
==19520== Reachable blocks (those to which a pointer was found) are not shown.
|
||||
==19520== To see them, add 'reachable any' args to leak_check
|
||||
==19520==
|
||||
(gdb) mo l
|
||||
==14729== LEAK SUMMARY:
|
||||
==14729== definitely lost: 32 (+0) bytes in 2 (+0) blocks
|
||||
==14729== indirectly lost: 16 (+0) bytes in 1 (+0) blocks
|
||||
==14729== possibly lost: 32 (+0) bytes in 2 (+0) blocks
|
||||
==14729== still reachable: 96 (+0) bytes in 6 (+0) blocks
|
||||
==14729== suppressed: 0 (+0) bytes in 0 (+0) blocks
|
||||
==14729== Reachable blocks (those to which a pointer was found) are not shown.
|
||||
==14729== To see them, add 'reachable any' args to leak_check
|
||||
==14729==
|
||||
==19520== LEAK SUMMARY:
|
||||
==19520== definitely lost: 32 (+0) bytes in 2 (+0) blocks
|
||||
==19520== indirectly lost: 16 (+0) bytes in 1 (+0) blocks
|
||||
==19520== possibly lost: 32 (+0) bytes in 2 (+0) blocks
|
||||
==19520== still reachable: 96 (+0) bytes in 6 (+0) blocks
|
||||
==19520== suppressed: 0 (+0) bytes in 0 (+0) blocks
|
||||
==19520== Reachable blocks (those to which a pointer was found) are not shown.
|
||||
==19520== To see them, add 'reachable any' args to leak_check
|
||||
==19520==
|
||||
(gdb)
|
||||
]]></programlisting>
|
||||
<para>Note that when using Valgrind's gdbserver, it is not
|
||||
@ -1481,6 +1481,143 @@ Address 0x8049E28 len 1 defined
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>block_list <loss_record_nr> </varname>
|
||||
shows the list of blocks belonging to <loss_record_nr>.
|
||||
</para>
|
||||
|
||||
<para> A leak search merges the allocated blocks in loss records :
|
||||
a loss record re-groups all blocks having the same state (for
|
||||
example, Definitely Lost) and the same allocation backtrace.
|
||||
Each loss record is identified in the leak search result
|
||||
by a loss record number.
|
||||
The <varname>block_list</varname> command shows the loss record information
|
||||
followed by the addresses and sizes of the blocks which have been
|
||||
merged in the loss record.
|
||||
</para>
|
||||
|
||||
<para> If a directly lost block causes some other blocks to be indirectly
|
||||
lost, the block_list command will also show these indirectly lost blocks.
|
||||
The indirectly lost blocks will be indented according to the level of indirection
|
||||
between the directly lost block and the indirectly lost block(s).
|
||||
Each indirectly lost block is followed by the reference of its loss record.
|
||||
</para>
|
||||
|
||||
<para> The block_list command can be used on the results of a leak search as long
|
||||
as no block has been freed after this leak search: as soon as the program frees
|
||||
a block, a new leak search is needed before block_list can be used again.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In the below example, the program leaks a tree structure by losing the pointer to
|
||||
the block A (top of the tree).
|
||||
So, the block A is directly lost, causing an indirect
|
||||
loss of blocks B to G. The first block_list command shows the loss record of A
|
||||
(a definitely lost block with address 0x4028028, size 16). The addresses and sizes
|
||||
of the indirectly lost blocks due to block A are shown below the block A.
|
||||
The second command shows the details of one of the indirect loss records output
|
||||
by the first command.
|
||||
</para>
|
||||
<programlisting><![CDATA[
|
||||
A
|
||||
/ \
|
||||
B C
|
||||
/ \ / \
|
||||
D E F G
|
||||
]]></programlisting>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
(gdb) bt
|
||||
#0 main () at leak-tree.c:69
|
||||
(gdb) monitor leak_check full any
|
||||
==19552== 112 (16 direct, 96 indirect) bytes in 1 blocks are definitely lost in loss record 7 of 7
|
||||
==19552== at 0x40070B4: malloc (vg_replace_malloc.c:263)
|
||||
==19552== by 0x80484D5: mk (leak-tree.c:28)
|
||||
==19552== by 0x80484FC: f (leak-tree.c:41)
|
||||
==19552== by 0x8048856: main (leak-tree.c:63)
|
||||
==19552==
|
||||
==19552== LEAK SUMMARY:
|
||||
==19552== definitely lost: 16 bytes in 1 blocks
|
||||
==19552== indirectly lost: 96 bytes in 6 blocks
|
||||
==19552== possibly lost: 0 bytes in 0 blocks
|
||||
==19552== still reachable: 0 bytes in 0 blocks
|
||||
==19552== suppressed: 0 bytes in 0 blocks
|
||||
==19552==
|
||||
(gdb) monitor block_list 7
|
||||
==19552== 112 (16 direct, 96 indirect) bytes in 1 blocks are definitely lost in loss record 7 of 7
|
||||
==19552== at 0x40070B4: malloc (vg_replace_malloc.c:263)
|
||||
==19552== by 0x80484D5: mk (leak-tree.c:28)
|
||||
==19552== by 0x80484FC: f (leak-tree.c:41)
|
||||
==19552== by 0x8048856: main (leak-tree.c:63)
|
||||
==19552== 0x4028028[16]
|
||||
==19552== 0x4028068[16] indirect loss record 1
|
||||
==19552== 0x40280E8[16] indirect loss record 3
|
||||
==19552== 0x4028128[16] indirect loss record 4
|
||||
==19552== 0x40280A8[16] indirect loss record 2
|
||||
==19552== 0x4028168[16] indirect loss record 5
|
||||
==19552== 0x40281A8[16] indirect loss record 6
|
||||
(gdb) mo b 2
|
||||
==19552== 16 bytes in 1 blocks are indirectly lost in loss record 2 of 7
|
||||
==19552== at 0x40070B4: malloc (vg_replace_malloc.c:263)
|
||||
==19552== by 0x80484D5: mk (leak-tree.c:28)
|
||||
==19552== by 0x8048519: f (leak-tree.c:43)
|
||||
==19552== by 0x8048856: main (leak-tree.c:63)
|
||||
==19552== 0x40280A8[16]
|
||||
==19552== 0x4028168[16] indirect loss record 5
|
||||
==19552== 0x40281A8[16] indirect loss record 6
|
||||
(gdb)
|
||||
|
||||
]]></programlisting>
|
||||
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><varname>who_points_at <addr> [<len>]</varname>
|
||||
shows all the locations where a pointer to addr is found.
|
||||
If len is equal to 1, the command only shows the locations pointing
|
||||
exactly at addr (i.e. the "start pointers" to addr).
|
||||
If len is > 1, "interior pointers" pointing at the len first bytes
|
||||
will also be shown.
|
||||
</para>
|
||||
|
||||
<para>The locations searched for are the same as the locations
|
||||
used in the leak search. So, <varname>who_points_at</varname> can a.o.
|
||||
be used to show why the leak search still can reach a block, or can
|
||||
search for dangling pointers to a freed block.
|
||||
Each location pointing at addr (or pointing inside addr if interior pointers
|
||||
are being searched for) will be described.
|
||||
</para>
|
||||
|
||||
<para>In the below example, the pointers to the 'tree block A' (see example
|
||||
in command <varname>block_list</varname>) is shown before the tree was leaked.
|
||||
The descriptions are detailed as the option <option>--read-var-info=yes</option>
|
||||
was given at Valgrind startup. The second call shows the pointers (start and interior
|
||||
pointers) to block G. The block G (0x40281A8) is reachable via block C (0x40280a8)
|
||||
and register ECX of tid 1 (tid is the Valgrind thread id).
|
||||
It is "interior reachable" via the register EBX.
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
(gdb) monitor who_points_at 0x4028028
|
||||
==20852== Searching for pointers to 0x4028028
|
||||
==20852== *0x8049e20 points at 0x4028028
|
||||
==20852== Location 0x8049e20 is 0 bytes inside global var "t"
|
||||
==20852== declared at leak-tree.c:35
|
||||
(gdb) monitor who_points_at 0x40281A8 16
|
||||
==20852== Searching for pointers pointing in 16 bytes from 0x40281a8
|
||||
==20852== *0x40280ac points at 0x40281a8
|
||||
==20852== Address 0x40280ac is 4 bytes inside a block of size 16 alloc'd
|
||||
==20852== at 0x40070B4: malloc (vg_replace_malloc.c:263)
|
||||
==20852== by 0x80484D5: mk (leak-tree.c:28)
|
||||
==20852== by 0x8048519: f (leak-tree.c:43)
|
||||
==20852== by 0x8048856: main (leak-tree.c:63)
|
||||
==20852== tid 1 register ECX points at 0x40281a8
|
||||
==20852== tid 1 register EBX interior points at 2 bytes inside 0x40281a8
|
||||
(gdb)
|
||||
]]></programlisting>
|
||||
</listitem>
|
||||
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</sect1>
|
||||
|
||||
@ -447,6 +447,95 @@ char * MC_(snprintf_delta) (char * buf, Int size,
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void pp_LossRecord(UInt n_this_record, UInt n_total_records,
|
||||
LossRecord* lr, Bool xml)
|
||||
{
|
||||
// char arrays to produce the indication of increase/decrease in case
|
||||
// of delta_mode != LCD_Any
|
||||
char d_bytes[20];
|
||||
char d_direct_bytes[20];
|
||||
char d_indirect_bytes[20];
|
||||
char d_num_blocks[20];
|
||||
|
||||
MC_(snprintf_delta) (d_bytes, 20,
|
||||
lr->szB + lr->indirect_szB,
|
||||
lr->old_szB + lr->old_indirect_szB,
|
||||
MC_(detect_memory_leaks_last_delta_mode));
|
||||
MC_(snprintf_delta) (d_direct_bytes, 20,
|
||||
lr->szB,
|
||||
lr->old_szB,
|
||||
MC_(detect_memory_leaks_last_delta_mode));
|
||||
MC_(snprintf_delta) (d_indirect_bytes, 20,
|
||||
lr->indirect_szB,
|
||||
lr->old_indirect_szB,
|
||||
MC_(detect_memory_leaks_last_delta_mode));
|
||||
MC_(snprintf_delta) (d_num_blocks, 20,
|
||||
(SizeT) lr->num_blocks,
|
||||
(SizeT) lr->old_num_blocks,
|
||||
MC_(detect_memory_leaks_last_delta_mode));
|
||||
|
||||
if (xml) {
|
||||
emit(" <kind>%s</kind>\n", xml_leak_kind(lr->key.state));
|
||||
if (lr->indirect_szB > 0) {
|
||||
emit( " <xwhat>\n" );
|
||||
emit( " <text>%'lu%s (%'lu%s direct, %'lu%s indirect) bytes "
|
||||
"in %'u%s blocks"
|
||||
" are %s in loss record %'u of %'u</text>\n",
|
||||
lr->szB + lr->indirect_szB, d_bytes,
|
||||
lr->szB, d_direct_bytes,
|
||||
lr->indirect_szB, d_indirect_bytes,
|
||||
lr->num_blocks, d_num_blocks,
|
||||
str_leak_lossmode(lr->key.state),
|
||||
n_this_record, n_total_records );
|
||||
// Nb: don't put commas in these XML numbers
|
||||
emit( " <leakedbytes>%lu</leakedbytes>\n",
|
||||
lr->szB + lr->indirect_szB );
|
||||
emit( " <leakedblocks>%u</leakedblocks>\n", lr->num_blocks );
|
||||
emit( " </xwhat>\n" );
|
||||
} else {
|
||||
emit( " <xwhat>\n" );
|
||||
emit( " <text>%'lu%s bytes in %'u%s blocks"
|
||||
" are %s in loss record %'u of %'u</text>\n",
|
||||
lr->szB, d_direct_bytes,
|
||||
lr->num_blocks, d_num_blocks,
|
||||
str_leak_lossmode(lr->key.state),
|
||||
n_this_record, n_total_records );
|
||||
emit( " <leakedbytes>%ld</leakedbytes>\n", lr->szB);
|
||||
emit( " <leakedblocks>%d</leakedblocks>\n", lr->num_blocks);
|
||||
emit( " </xwhat>\n" );
|
||||
}
|
||||
VG_(pp_ExeContext)(lr->key.allocated_at);
|
||||
} else { /* ! if (xml) */
|
||||
if (lr->indirect_szB > 0) {
|
||||
emit(
|
||||
"%'lu%s (%'lu%s direct, %'lu%s indirect) bytes in %'u%s blocks"
|
||||
" are %s in loss record %'u of %'u\n",
|
||||
lr->szB + lr->indirect_szB, d_bytes,
|
||||
lr->szB, d_direct_bytes,
|
||||
lr->indirect_szB, d_indirect_bytes,
|
||||
lr->num_blocks, d_num_blocks,
|
||||
str_leak_lossmode(lr->key.state),
|
||||
n_this_record, n_total_records
|
||||
);
|
||||
} else {
|
||||
emit(
|
||||
"%'lu%s bytes in %'u%s blocks are %s in loss record %'u of %'u\n",
|
||||
lr->szB, d_direct_bytes,
|
||||
lr->num_blocks, d_num_blocks,
|
||||
str_leak_lossmode(lr->key.state),
|
||||
n_this_record, n_total_records
|
||||
);
|
||||
}
|
||||
VG_(pp_ExeContext)(lr->key.allocated_at);
|
||||
} /* if (xml) */
|
||||
}
|
||||
|
||||
void MC_(pp_LossRecord)(UInt n_this_record, UInt n_total_records,
|
||||
LossRecord* l)
|
||||
{
|
||||
pp_LossRecord (n_this_record, n_total_records, l, /* xml */ False);
|
||||
}
|
||||
|
||||
void MC_(pp_Error) ( Error* err )
|
||||
{
|
||||
const Bool xml = VG_(clo_xml); /* a shorthand */
|
||||
@ -717,84 +806,7 @@ void MC_(pp_Error) ( Error* err )
|
||||
UInt n_this_record = extra->Err.Leak.n_this_record;
|
||||
UInt n_total_records = extra->Err.Leak.n_total_records;
|
||||
LossRecord* lr = extra->Err.Leak.lr;
|
||||
// char arrays to produce the indication of increase/decrease in case
|
||||
// of delta_mode != LCD_Any
|
||||
char d_bytes[20];
|
||||
char d_direct_bytes[20];
|
||||
char d_indirect_bytes[20];
|
||||
char d_num_blocks[20];
|
||||
|
||||
MC_(snprintf_delta) (d_bytes, 20,
|
||||
lr->szB + lr->indirect_szB,
|
||||
lr->old_szB + lr->old_indirect_szB,
|
||||
MC_(detect_memory_leaks_last_delta_mode));
|
||||
MC_(snprintf_delta) (d_direct_bytes, 20,
|
||||
lr->szB,
|
||||
lr->old_szB,
|
||||
MC_(detect_memory_leaks_last_delta_mode));
|
||||
MC_(snprintf_delta) (d_indirect_bytes, 20,
|
||||
lr->indirect_szB,
|
||||
lr->old_indirect_szB,
|
||||
MC_(detect_memory_leaks_last_delta_mode));
|
||||
MC_(snprintf_delta) (d_num_blocks, 20,
|
||||
(SizeT) lr->num_blocks,
|
||||
(SizeT) lr->old_num_blocks,
|
||||
MC_(detect_memory_leaks_last_delta_mode));
|
||||
|
||||
if (xml) {
|
||||
emit(" <kind>%s</kind>\n", xml_leak_kind(lr->key.state));
|
||||
if (lr->indirect_szB > 0) {
|
||||
emit( " <xwhat>\n" );
|
||||
emit( " <text>%'lu%s (%'lu%s direct, %'lu%s indirect) bytes "
|
||||
"in %'u%s blocks"
|
||||
" are %s in loss record %'u of %'u</text>\n",
|
||||
lr->szB + lr->indirect_szB, d_bytes,
|
||||
lr->szB, d_direct_bytes,
|
||||
lr->indirect_szB, d_indirect_bytes,
|
||||
lr->num_blocks, d_num_blocks,
|
||||
str_leak_lossmode(lr->key.state),
|
||||
n_this_record, n_total_records );
|
||||
// Nb: don't put commas in these XML numbers
|
||||
emit( " <leakedbytes>%lu</leakedbytes>\n",
|
||||
lr->szB + lr->indirect_szB );
|
||||
emit( " <leakedblocks>%u</leakedblocks>\n", lr->num_blocks );
|
||||
emit( " </xwhat>\n" );
|
||||
} else {
|
||||
emit( " <xwhat>\n" );
|
||||
emit( " <text>%'lu%s bytes in %'u%s blocks"
|
||||
" are %s in loss record %'u of %'u</text>\n",
|
||||
lr->szB, d_direct_bytes,
|
||||
lr->num_blocks, d_num_blocks,
|
||||
str_leak_lossmode(lr->key.state),
|
||||
n_this_record, n_total_records );
|
||||
emit( " <leakedbytes>%ld</leakedbytes>\n", lr->szB);
|
||||
emit( " <leakedblocks>%d</leakedblocks>\n", lr->num_blocks);
|
||||
emit( " </xwhat>\n" );
|
||||
}
|
||||
VG_(pp_ExeContext)(lr->key.allocated_at);
|
||||
} else { /* ! if (xml) */
|
||||
if (lr->indirect_szB > 0) {
|
||||
emit(
|
||||
"%'lu%s (%'lu%s direct, %'lu%s indirect) bytes in %'u%s blocks"
|
||||
" are %s in loss record %'u of %'u\n",
|
||||
lr->szB + lr->indirect_szB, d_bytes,
|
||||
lr->szB, d_direct_bytes,
|
||||
lr->indirect_szB, d_indirect_bytes,
|
||||
lr->num_blocks, d_num_blocks,
|
||||
str_leak_lossmode(lr->key.state),
|
||||
n_this_record, n_total_records
|
||||
);
|
||||
} else {
|
||||
emit(
|
||||
"%'lu%s bytes in %'u%s blocks are %s in loss record %'u of %'u\n",
|
||||
lr->szB, d_direct_bytes,
|
||||
lr->num_blocks, d_num_blocks,
|
||||
str_leak_lossmode(lr->key.state),
|
||||
n_this_record, n_total_records
|
||||
);
|
||||
}
|
||||
VG_(pp_ExeContext)(lr->key.allocated_at);
|
||||
} /* if (xml) */
|
||||
pp_LossRecord (n_this_record, n_total_records, lr, xml);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -121,6 +121,8 @@ void MC_(make_mem_defined) ( Addr a, SizeT len );
|
||||
void MC_(copy_address_range_state) ( Addr src, Addr dst, SizeT len );
|
||||
|
||||
void MC_(print_malloc_stats) ( void );
|
||||
/* nr of free operations done */
|
||||
SizeT MC_(get_cmalloc_n_frees) ( void );
|
||||
|
||||
void* MC_(malloc) ( ThreadId tid, SizeT n );
|
||||
void* MC_(__builtin_new) ( ThreadId tid, SizeT n );
|
||||
@ -254,6 +256,7 @@ typedef
|
||||
}
|
||||
Reachedness;
|
||||
|
||||
|
||||
/* For VALGRIND_COUNT_LEAKS client request */
|
||||
extern SizeT MC_(bytes_leaked);
|
||||
extern SizeT MC_(bytes_indirect);
|
||||
@ -324,6 +327,15 @@ void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams * lcp);
|
||||
// maintains the lcp.deltamode given in the last call to detect_memory_leaks
|
||||
extern LeakCheckDeltaMode MC_(detect_memory_leaks_last_delta_mode);
|
||||
|
||||
// prints the list of blocks corresponding to the given loss_record_nr.
|
||||
// Returns True if loss_record_nr identifies a correct loss record from last leak search.
|
||||
// Returns False otherwise.
|
||||
Bool MC_(print_block_list) ( UInt loss_record_nr);
|
||||
|
||||
// Prints the addresses/registers/... at which a pointer to
|
||||
// the given range [address, address+szB[ is found.
|
||||
void MC_(who_points_at) ( Addr address, SizeT szB);
|
||||
|
||||
// if delta_mode == LCD_Any, prints in buf an empty string
|
||||
// otherwise prints a delta in the layout " (+%'lu)" or " (-%'lu)"
|
||||
extern char * MC_(snprintf_delta) (char * buf, Int size,
|
||||
@ -334,8 +346,9 @@ extern char * MC_(snprintf_delta) (char * buf, Int size,
|
||||
Bool MC_(is_valid_aligned_word) ( Addr a );
|
||||
Bool MC_(is_within_valid_secondary) ( Addr a );
|
||||
|
||||
void MC_(pp_LeakError)(UInt n_this_record, UInt n_total_records,
|
||||
LossRecord* l);
|
||||
// Prints as user msg a description of the given loss record.
|
||||
void MC_(pp_LossRecord)(UInt n_this_record, UInt n_total_records,
|
||||
LossRecord* l);
|
||||
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
|
||||
@ -426,19 +426,39 @@ typedef
|
||||
struct {
|
||||
UInt state:2; // Reachedness.
|
||||
UInt pending:1; // Scan pending.
|
||||
SizeT indirect_szB : (sizeof(SizeT)*8)-3; // If Unreached, how many bytes
|
||||
// are unreachable from here.
|
||||
union {
|
||||
SizeT indirect_szB : (sizeof(SizeT)*8)-3; // If Unreached, how many bytes
|
||||
// are unreachable from here.
|
||||
SizeT clique : (sizeof(SizeT)*8)-3; // if IndirectLeak, clique leader
|
||||
// to which it belongs.
|
||||
} IorC;
|
||||
}
|
||||
LC_Extra;
|
||||
|
||||
// An array holding pointers to every chunk we're checking. Sorted by address.
|
||||
// lc_chunks is initialised during leak search. It is kept after leak search
|
||||
// to support printing the list of blocks belonging to a loss record.
|
||||
// lc_chunk array can only be used validly till the next "free" operation
|
||||
// (as a free operation potentially destroys one or more chunks).
|
||||
// To detect lc_chunk is valid, we store the nr of frees operations done
|
||||
// when lc_chunk was build : lc_chunks (and lc_extras) stays valid as
|
||||
// long as no free operations has been done since lc_chunks building.
|
||||
static MC_Chunk** lc_chunks;
|
||||
// How many chunks we're dealing with.
|
||||
static Int lc_n_chunks;
|
||||
static SizeT lc_chunks_n_frees_marker;
|
||||
// This has the same number of entries as lc_chunks, and each entry
|
||||
// in lc_chunks corresponds with the entry here (ie. lc_chunks[i] and
|
||||
// lc_extras[i] describe the same block).
|
||||
static LC_Extra* lc_extras;
|
||||
|
||||
// chunks will be converted and merged in loss record, maintained in lr_table
|
||||
// lr_table elements are kept from one leak_search to another to implement
|
||||
// the "print new/changed leaks" client request
|
||||
static OSet* lr_table;
|
||||
// Array of sorted loss record (produced during last leak search).
|
||||
static LossRecord** lr_array;
|
||||
|
||||
|
||||
// DeltaMode used the last time we called detect_memory_leaks.
|
||||
// The recorded leak errors must be output using a logic based on this delta_mode.
|
||||
@ -446,11 +466,6 @@ static OSet* lr_table;
|
||||
LeakCheckDeltaMode MC_(detect_memory_leaks_last_delta_mode);
|
||||
|
||||
|
||||
// This has the same number of entries as lc_chunks, and each entry
|
||||
// in lc_chunks corresponds with the entry here (ie. lc_chunks[i] and
|
||||
// lc_extras[i] describe the same block).
|
||||
static LC_Extra* lc_extras;
|
||||
|
||||
// Records chunks that are currently being processed. Each element in the
|
||||
// stack is an index into lc_chunks and lc_extras. Its size is
|
||||
// 'lc_n_chunks' because in the worst case that's how many chunks could be
|
||||
@ -478,7 +493,6 @@ SizeT MC_(blocks_dubious) = 0;
|
||||
SizeT MC_(blocks_reachable) = 0;
|
||||
SizeT MC_(blocks_suppressed) = 0;
|
||||
|
||||
|
||||
// Determines if a pointer is to a chunk. Returns the chunk number et al
|
||||
// via call-by-reference.
|
||||
static Bool
|
||||
@ -587,7 +601,7 @@ lc_push_without_clique_if_a_chunk_ptr(Addr ptr, Bool is_prior_definite)
|
||||
}
|
||||
|
||||
static void
|
||||
lc_push_if_a_chunk_ptr_register(Addr ptr)
|
||||
lc_push_if_a_chunk_ptr_register(ThreadId tid, HChar* regname, Addr ptr)
|
||||
{
|
||||
lc_push_without_clique_if_a_chunk_ptr(ptr, /*is_prior_definite*/True);
|
||||
}
|
||||
@ -596,7 +610,7 @@ lc_push_if_a_chunk_ptr_register(Addr ptr)
|
||||
// before, push it onto the mark stack. Clique is the index of the
|
||||
// clique leader.
|
||||
static void
|
||||
lc_push_with_clique_if_a_chunk_ptr(Addr ptr, Int clique)
|
||||
lc_push_with_clique_if_a_chunk_ptr(Addr ptr, Int clique, Int cur_clique)
|
||||
{
|
||||
Int ch_no;
|
||||
MC_Chunk* ch;
|
||||
@ -614,7 +628,6 @@ lc_push_with_clique_if_a_chunk_ptr(Addr ptr, Int clique)
|
||||
// Note that, unlike reachable blocks, we currently don't distinguish
|
||||
// between start-pointers and interior-pointers here. We probably
|
||||
// should, though.
|
||||
ex->state = IndirectLeak;
|
||||
lc_push(ch_no, ch);
|
||||
|
||||
// Add the block to the clique, and add its size to the
|
||||
@ -622,28 +635,29 @@ lc_push_with_clique_if_a_chunk_ptr(Addr ptr, Int clique)
|
||||
// itself a clique leader, it isn't any more, so add its
|
||||
// indirect_szB to the new clique leader.
|
||||
if (VG_DEBUG_CLIQUE) {
|
||||
if (ex->indirect_szB > 0)
|
||||
if (ex->IorC.indirect_szB > 0)
|
||||
VG_(printf)(" clique %d joining clique %d adding %lu+%lu\n",
|
||||
ch_no, clique, (unsigned long)ch->szB,
|
||||
(unsigned long)ex->indirect_szB);
|
||||
(unsigned long)ex->IorC.indirect_szB);
|
||||
else
|
||||
VG_(printf)(" block %d joining clique %d adding %lu\n",
|
||||
ch_no, clique, (unsigned long)ch->szB);
|
||||
}
|
||||
|
||||
lc_extras[clique].indirect_szB += ch->szB;
|
||||
lc_extras[clique].indirect_szB += ex->indirect_szB;
|
||||
ex->indirect_szB = 0; // Shouldn't matter.
|
||||
lc_extras[clique].IorC.indirect_szB += ch->szB;
|
||||
lc_extras[clique].IorC.indirect_szB += ex->IorC.indirect_szB;
|
||||
ex->state = IndirectLeak;
|
||||
ex->IorC.clique = (SizeT) cur_clique;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lc_push_if_a_chunk_ptr(Addr ptr, Int clique, Bool is_prior_definite)
|
||||
lc_push_if_a_chunk_ptr(Addr ptr, Int clique, Int cur_clique, Bool is_prior_definite)
|
||||
{
|
||||
if (-1 == clique)
|
||||
lc_push_without_clique_if_a_chunk_ptr(ptr, is_prior_definite);
|
||||
else
|
||||
lc_push_with_clique_if_a_chunk_ptr(ptr, clique);
|
||||
lc_push_with_clique_if_a_chunk_ptr(ptr, clique, cur_clique);
|
||||
}
|
||||
|
||||
|
||||
@ -658,12 +672,38 @@ void scan_all_valid_memory_catcher ( Int sigNo, Addr addr )
|
||||
VG_MINIMAL_LONGJMP(memscan_jmpbuf);
|
||||
}
|
||||
|
||||
// lc_scan_memory has 2 modes:
|
||||
//
|
||||
// 1. Leak check mode (searched == 0).
|
||||
// -----------------------------------
|
||||
// Scan a block of memory between [start, start+len). This range may
|
||||
// be bogus, inaccessable, or otherwise strange; we deal with it. For each
|
||||
// valid aligned word we assume it's a pointer to a chunk a push the chunk
|
||||
// onto the mark stack if so.
|
||||
// clique is the "highest level clique" in which indirectly leaked blocks have
|
||||
// to be collected. cur_clique is the current "lower" level clique through which
|
||||
// the memory to be scanned has been found.
|
||||
// Example: in the below tree if A is leaked, the top level clique will
|
||||
// be A, while lower level cliques will be B and C.
|
||||
/*
|
||||
A
|
||||
/ \
|
||||
B C
|
||||
/ \ / \
|
||||
D E F G
|
||||
*/
|
||||
// Proper handling of top and lowest level clique allows block_list of a loss
|
||||
// record to describe the hierarchy of indirectly leaked blocks.
|
||||
//
|
||||
// 2. Search ptr mode (searched != 0).
|
||||
// -----------------------------------
|
||||
// In this mode, searches for pointers to a specific address range
|
||||
// In such a case, lc_scan_memory just scans [start..start+len[ for pointers to searched
|
||||
// and outputs the places where searched is found. It does not recursively scans the
|
||||
// found memory.
|
||||
static void
|
||||
lc_scan_memory(Addr start, SizeT len, Bool is_prior_definite, Int clique)
|
||||
lc_scan_memory(Addr start, SizeT len, Bool is_prior_definite, Int clique, Int cur_clique,
|
||||
Addr searched, SizeT szB)
|
||||
{
|
||||
Addr ptr = VG_ROUNDUP(start, sizeof(Addr));
|
||||
Addr end = VG_ROUNDDN(start+len, sizeof(Addr));
|
||||
@ -703,7 +743,18 @@ lc_scan_memory(Addr start, SizeT len, Bool is_prior_definite, Int clique)
|
||||
addr = *(Addr *)ptr;
|
||||
// If we get here, the scanned word is in valid memory. Now
|
||||
// let's see if its contents point to a chunk.
|
||||
lc_push_if_a_chunk_ptr(addr, clique, is_prior_definite);
|
||||
if (searched) {
|
||||
if (addr >= searched && addr < searched + szB) {
|
||||
if (addr == searched)
|
||||
VG_(umsg)("*%#lx points at %#lx\n", ptr, searched);
|
||||
else
|
||||
VG_(umsg)("*%#lx interior points at %lu bytes inside %#lx\n",
|
||||
ptr, (long unsigned) addr - searched, searched);
|
||||
MC_(pp_describe_addr) (ptr);
|
||||
}
|
||||
} else {
|
||||
lc_push_if_a_chunk_ptr(addr, clique, cur_clique, is_prior_definite);
|
||||
}
|
||||
} else if (0 && VG_DEBUG_LEAKCHECK) {
|
||||
VG_(printf)("%#lx not valid\n", ptr);
|
||||
}
|
||||
@ -735,7 +786,8 @@ static void lc_process_markstack(Int clique)
|
||||
is_prior_definite = ( Possible != lc_extras[top].state );
|
||||
|
||||
lc_scan_memory(lc_chunks[top]->data, lc_chunks[top]->szB,
|
||||
is_prior_definite, clique);
|
||||
is_prior_definite, clique, (clique == -1 ? -1 : top),
|
||||
/*searched*/ 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,6 +834,28 @@ static Int cmp_LossRecords(void* va, void* vb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// allocates or reallocates lr_array, and set its elements to the loss records
|
||||
// contains in lr_table.
|
||||
static Int get_lr_array_from_lr_table(void) {
|
||||
Int i, n_lossrecords;
|
||||
LossRecord* lr;
|
||||
|
||||
n_lossrecords = VG_(OSetGen_Size)(lr_table);
|
||||
|
||||
// (re-)create the array of pointers to the loss records.
|
||||
// lr_array is kept to allow producing the block list from gdbserver.
|
||||
if (lr_array != NULL)
|
||||
VG_(free)(lr_array);
|
||||
lr_array = VG_(malloc)("mc.pr.2", n_lossrecords * sizeof(LossRecord*));
|
||||
i = 0;
|
||||
VG_(OSetGen_ResetIter)(lr_table);
|
||||
while ( (lr = VG_(OSetGen_Next)(lr_table)) ) {
|
||||
lr_array[i++] = lr;
|
||||
}
|
||||
tl_assert(i == n_lossrecords);
|
||||
return n_lossrecords;
|
||||
}
|
||||
|
||||
|
||||
static void get_printing_rules(LeakCheckParams* lcp,
|
||||
LossRecord* lr,
|
||||
@ -840,7 +914,6 @@ static void get_printing_rules(LeakCheckParams* lcp,
|
||||
static void print_results(ThreadId tid, LeakCheckParams* lcp)
|
||||
{
|
||||
Int i, n_lossrecords, start_lr_output_scan;
|
||||
LossRecord** lr_array;
|
||||
LossRecord* lr;
|
||||
Bool is_suppressed;
|
||||
SizeT old_bytes_leaked = MC_(bytes_leaked); /* to report delta in summary */
|
||||
@ -866,6 +939,28 @@ static void print_results(ThreadId tid, LeakCheckParams* lcp)
|
||||
VG_(malloc), "mc.pr.1",
|
||||
VG_(free));
|
||||
|
||||
// If we have loss records from a previous search, reset values to have
|
||||
// proper printing of the deltas between previous search and this search.
|
||||
n_lossrecords = get_lr_array_from_lr_table();
|
||||
for (i = 0; i < n_lossrecords; i++) {
|
||||
if (lr_array[i]->num_blocks == 0)
|
||||
// remove from lr_table the old loss_records with 0 bytes found
|
||||
VG_(OSetGen_Remove) (lr_table, &lr_array[i]->key);
|
||||
else {
|
||||
// move the leak sizes to old_* and zero the current sizes
|
||||
// for next leak search
|
||||
lr_array[i]->old_szB = lr_array[i]->szB;
|
||||
lr_array[i]->old_indirect_szB = lr_array[i]->indirect_szB;
|
||||
lr_array[i]->old_num_blocks = lr_array[i]->num_blocks;
|
||||
lr_array[i]->szB = 0;
|
||||
lr_array[i]->indirect_szB = 0;
|
||||
lr_array[i]->num_blocks = 0;
|
||||
}
|
||||
}
|
||||
// lr_array now contains "invalid" loss records => free it.
|
||||
// lr_array will be re-created below with the kept and new loss records.
|
||||
VG_(free) (lr_array);
|
||||
lr_array = NULL;
|
||||
|
||||
// Convert the chunks into loss records, merging them where appropriate.
|
||||
for (i = 0; i < lc_n_chunks; i++) {
|
||||
@ -882,7 +977,8 @@ static void print_results(ThreadId tid, LeakCheckParams* lcp)
|
||||
// loss record's details in-situ. This is safe because we don't
|
||||
// change the elements used as the OSet key.
|
||||
old_lr->szB += ch->szB;
|
||||
old_lr->indirect_szB += ex->indirect_szB;
|
||||
if (ex->state == Unreached)
|
||||
old_lr->indirect_szB += ex->IorC.indirect_szB;
|
||||
old_lr->num_blocks++;
|
||||
} else {
|
||||
// No existing loss record matches this chunk. Create a new loss
|
||||
@ -890,7 +986,10 @@ static void print_results(ThreadId tid, LeakCheckParams* lcp)
|
||||
lr = VG_(OSetGen_AllocNode)(lr_table, sizeof(LossRecord));
|
||||
lr->key = lrkey;
|
||||
lr->szB = ch->szB;
|
||||
lr->indirect_szB = ex->indirect_szB;
|
||||
if (ex->state == Unreached)
|
||||
lr->indirect_szB = ex->IorC.indirect_szB;
|
||||
else
|
||||
lr->indirect_szB = 0;
|
||||
lr->num_blocks = 1;
|
||||
lr->old_szB = 0;
|
||||
lr->old_indirect_szB = 0;
|
||||
@ -898,16 +997,10 @@ static void print_results(ThreadId tid, LeakCheckParams* lcp)
|
||||
VG_(OSetGen_Insert)(lr_table, lr);
|
||||
}
|
||||
}
|
||||
n_lossrecords = VG_(OSetGen_Size)(lr_table);
|
||||
|
||||
// Create an array of pointers to the loss records.
|
||||
lr_array = VG_(malloc)("mc.pr.2", n_lossrecords * sizeof(LossRecord*));
|
||||
i = 0;
|
||||
VG_(OSetGen_ResetIter)(lr_table);
|
||||
while ( (lr = VG_(OSetGen_Next)(lr_table)) ) {
|
||||
lr_array[i++] = lr;
|
||||
}
|
||||
tl_assert(i == n_lossrecords);
|
||||
// (re-)create the array of pointers to the (new) loss records.
|
||||
n_lossrecords = get_lr_array_from_lr_table ();
|
||||
tl_assert(VG_(OSetGen_Size)(lr_table) == n_lossrecords);
|
||||
|
||||
// Sort the array by loss record sizes.
|
||||
VG_(ssort)(lr_array, n_lossrecords, sizeof(LossRecord*),
|
||||
@ -980,25 +1073,6 @@ static void print_results(ThreadId tid, LeakCheckParams* lcp)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n_lossrecords; i++)
|
||||
{
|
||||
if (lr->num_blocks == 0)
|
||||
// remove from lr_table the old loss_records with 0 bytes found
|
||||
VG_(OSetGen_Remove) (lr_table, &lr_array[i]->key);
|
||||
else
|
||||
{
|
||||
// move the leak sizes to old_* and zero the current sizes
|
||||
// for next leak search
|
||||
lr_array[i]->old_szB = lr_array[i]->szB;
|
||||
lr_array[i]->old_indirect_szB = lr_array[i]->indirect_szB;
|
||||
lr_array[i]->old_num_blocks = lr_array[i]->num_blocks;
|
||||
lr_array[i]->szB = 0;
|
||||
lr_array[i]->indirect_szB = 0;
|
||||
lr_array[i]->num_blocks = 0;
|
||||
}
|
||||
}
|
||||
VG_(free)(lr_array);
|
||||
|
||||
if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) {
|
||||
char d_bytes[20];
|
||||
char d_blocks[20];
|
||||
@ -1053,6 +1127,157 @@ static void print_results(ThreadId tid, LeakCheckParams* lcp)
|
||||
}
|
||||
}
|
||||
|
||||
// print recursively all indirectly leaked blocks collected in clique.
|
||||
static void print_clique (Int clique, UInt level)
|
||||
{
|
||||
Int ind;
|
||||
Int i, n_lossrecords;;
|
||||
|
||||
n_lossrecords = VG_(OSetGen_Size)(lr_table);
|
||||
|
||||
for (ind = 0; ind < lc_n_chunks; ind++) {
|
||||
LC_Extra* ind_ex = &(lc_extras)[ind];
|
||||
if (ind_ex->state == IndirectLeak && ind_ex->IorC.clique == (SizeT) clique) {
|
||||
MC_Chunk* ind_ch = lc_chunks[ind];
|
||||
LossRecord* ind_lr;
|
||||
LossRecordKey ind_lrkey;
|
||||
Int lr_i;
|
||||
ind_lrkey.state = ind_ex->state;
|
||||
ind_lrkey.allocated_at = ind_ch->where;
|
||||
ind_lr = VG_(OSetGen_Lookup)(lr_table, &ind_lrkey);
|
||||
for (lr_i = 0; lr_i < n_lossrecords; lr_i++)
|
||||
if (ind_lr == lr_array[lr_i])
|
||||
break;
|
||||
for (i = 0; i < level; i++)
|
||||
VG_(umsg)(" ");
|
||||
VG_(umsg)("%p[%lu] indirect loss record %d\n",
|
||||
(void *)ind_ch->data, (unsigned long)ind_ch->szB,
|
||||
lr_i+1); // lr_i+1 for user numbering.
|
||||
if (lr_i >= n_lossrecords)
|
||||
VG_(umsg)
|
||||
("error: no indirect loss record found for %p[%lu]?????\n",
|
||||
(void *)ind_ch->data, (unsigned long)ind_ch->szB);
|
||||
print_clique(ind, level+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bool MC_(print_block_list) ( UInt loss_record_nr)
|
||||
{
|
||||
Int i, n_lossrecords;
|
||||
LossRecord* lr;
|
||||
|
||||
if (lr_table == NULL || lc_chunks == NULL || lc_extras == NULL) {
|
||||
VG_(umsg)("Can't print block list : no valid leak search result\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
if (lc_chunks_n_frees_marker != MC_(get_cmalloc_n_frees)()) {
|
||||
VG_(umsg)("Can't print obsolete block list : redo a leak search first\n");
|
||||
return False;
|
||||
}
|
||||
|
||||
n_lossrecords = VG_(OSetGen_Size)(lr_table);
|
||||
if (loss_record_nr >= n_lossrecords)
|
||||
return False; // Invalid loss record nr.
|
||||
|
||||
tl_assert (lr_array);
|
||||
lr = lr_array[loss_record_nr];
|
||||
|
||||
// (re-)print the loss record details.
|
||||
// (+1 on loss_record_nr as user numbering for loss records starts at 1).
|
||||
MC_(pp_LossRecord)(loss_record_nr+1, n_lossrecords, lr);
|
||||
|
||||
// Match the chunks with loss records.
|
||||
for (i = 0; i < lc_n_chunks; i++) {
|
||||
MC_Chunk* ch = lc_chunks[i];
|
||||
LC_Extra* ex = &(lc_extras)[i];
|
||||
LossRecord* old_lr;
|
||||
LossRecordKey lrkey;
|
||||
lrkey.state = ex->state;
|
||||
lrkey.allocated_at = ch->where;
|
||||
|
||||
old_lr = VG_(OSetGen_Lookup)(lr_table, &lrkey);
|
||||
if (old_lr) {
|
||||
// We found an existing loss record matching this chunk.
|
||||
// If this is the loss record we are looking for, then output the pointer.
|
||||
if (old_lr == lr_array[loss_record_nr]) {
|
||||
VG_(umsg)("%p[%lu]\n",
|
||||
(void *)ch->data, (unsigned long) ch->szB);
|
||||
if (ex->state != Reachable) {
|
||||
// We can print the clique in all states, except Reachable.
|
||||
// In Unreached state, lc_chunk[i] is the clique leader.
|
||||
// In IndirectLeak, lc_chunk[i] might have been a clique leader
|
||||
// which was later collected in another clique.
|
||||
// For Possible, lc_chunk[i] might be the top of a clique
|
||||
// or an intermediate clique.
|
||||
print_clique(i, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No existing loss record matches this chunk ???
|
||||
VG_(umsg)("error: no loss record found for %p[%lu]?????\n",
|
||||
(void *)ch->data, (unsigned long) ch->szB);
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
// If searched = 0, scan memory root set, pushing onto the mark stack the blocks
|
||||
// encountered.
|
||||
// Otherwise (searched != 0), scan the memory root set searching for ptr pointing
|
||||
// inside [searched, searched+szB[.
|
||||
static void scan_memory_root_set(Addr searched, SizeT szB)
|
||||
{
|
||||
Int i;
|
||||
Int n_seg_starts;
|
||||
Addr* seg_starts = VG_(get_segment_starts)( &n_seg_starts );
|
||||
|
||||
tl_assert(seg_starts && n_seg_starts > 0);
|
||||
|
||||
lc_scanned_szB = 0;
|
||||
|
||||
// VG_(am_show_nsegments)( 0, "leakcheck");
|
||||
for (i = 0; i < n_seg_starts; i++) {
|
||||
SizeT seg_size;
|
||||
NSegment const* seg = VG_(am_find_nsegment)( seg_starts[i] );
|
||||
tl_assert(seg);
|
||||
|
||||
if (seg->kind != SkFileC && seg->kind != SkAnonC) continue;
|
||||
if (!(seg->hasR && seg->hasW)) continue;
|
||||
if (seg->isCH) continue;
|
||||
|
||||
// Don't poke around in device segments as this may cause
|
||||
// hangs. Exclude /dev/zero just in case someone allocated
|
||||
// memory by explicitly mapping /dev/zero.
|
||||
if (seg->kind == SkFileC
|
||||
&& (VKI_S_ISCHR(seg->mode) || VKI_S_ISBLK(seg->mode))) {
|
||||
HChar* dev_name = VG_(am_get_filename)( (NSegment*)seg );
|
||||
if (dev_name && 0 == VG_(strcmp)(dev_name, "/dev/zero")) {
|
||||
// Don't skip /dev/zero.
|
||||
} else {
|
||||
// Skip this device mapping.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (0)
|
||||
VG_(printf)("ACCEPT %2d %#lx %#lx\n", i, seg->start, seg->end);
|
||||
|
||||
// Scan the segment. We use -1 for the clique number, because this
|
||||
// is a root-set.
|
||||
seg_size = seg->end - seg->start + 1;
|
||||
if (VG_(clo_verbosity) > 2) {
|
||||
VG_(message)(Vg_DebugMsg,
|
||||
" Scanning root segment: %#lx..%#lx (%lu)\n",
|
||||
seg->start, seg->end, seg_size);
|
||||
}
|
||||
lc_scan_memory(seg->start, seg_size, /*is_prior_definite*/True,
|
||||
/*clique*/-1, /*cur_clique*/-1,
|
||||
searched, szB);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------*/
|
||||
/*--- Top-level entry point. ---*/
|
||||
/*------------------------------------------------------------*/
|
||||
@ -1066,16 +1291,22 @@ void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams* lcp)
|
||||
MC_(detect_memory_leaks_last_delta_mode) = lcp->deltamode;
|
||||
|
||||
// Get the chunks, stop if there were none.
|
||||
if (lc_chunks) {
|
||||
VG_(free)(lc_chunks);
|
||||
lc_chunks = NULL;
|
||||
}
|
||||
lc_chunks = find_active_chunks(&lc_n_chunks);
|
||||
lc_chunks_n_frees_marker = MC_(get_cmalloc_n_frees)();
|
||||
if (lc_n_chunks == 0) {
|
||||
tl_assert(lc_chunks == NULL);
|
||||
if (lr_table != NULL) {
|
||||
// forget the previous recorded LossRecords as next leak search will in any case
|
||||
// just create new leaks.
|
||||
// forget the previous recorded LossRecords as next leak search
|
||||
// can in any case just create new leaks.
|
||||
// Maybe it would be better to rather call print_result ?
|
||||
// (at least when leak decrease are requested)
|
||||
// (at least when leak decreases are requested)
|
||||
// This will then output all LossRecords with a size decreasing to 0
|
||||
VG_(OSetGen_Destroy) (lr_table);
|
||||
lr_table = NULL;
|
||||
}
|
||||
if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) {
|
||||
VG_(umsg)("All heap blocks were freed -- no leaks are possible\n");
|
||||
@ -1152,11 +1383,15 @@ void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams* lcp)
|
||||
}
|
||||
|
||||
// Initialise lc_extras.
|
||||
if (lc_extras) {
|
||||
VG_(free)(lc_extras);
|
||||
lc_extras = NULL;
|
||||
}
|
||||
lc_extras = VG_(malloc)( "mc.dml.2", lc_n_chunks * sizeof(LC_Extra) );
|
||||
for (i = 0; i < lc_n_chunks; i++) {
|
||||
lc_extras[i].state = Unreached;
|
||||
lc_extras[i].pending = False;
|
||||
lc_extras[i].indirect_szB = 0;
|
||||
lc_extras[i].IorC.indirect_szB = 0;
|
||||
}
|
||||
|
||||
// Initialise lc_markstack.
|
||||
@ -1174,52 +1409,7 @@ void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams* lcp)
|
||||
|
||||
// Scan the memory root-set, pushing onto the mark stack any blocks
|
||||
// pointed to.
|
||||
{
|
||||
Int n_seg_starts;
|
||||
Addr* seg_starts = VG_(get_segment_starts)( &n_seg_starts );
|
||||
|
||||
tl_assert(seg_starts && n_seg_starts > 0);
|
||||
|
||||
lc_scanned_szB = 0;
|
||||
|
||||
// VG_(am_show_nsegments)( 0, "leakcheck");
|
||||
for (i = 0; i < n_seg_starts; i++) {
|
||||
SizeT seg_size;
|
||||
NSegment const* seg = VG_(am_find_nsegment)( seg_starts[i] );
|
||||
tl_assert(seg);
|
||||
|
||||
if (seg->kind != SkFileC && seg->kind != SkAnonC) continue;
|
||||
if (!(seg->hasR && seg->hasW)) continue;
|
||||
if (seg->isCH) continue;
|
||||
|
||||
// Don't poke around in device segments as this may cause
|
||||
// hangs. Exclude /dev/zero just in case someone allocated
|
||||
// memory by explicitly mapping /dev/zero.
|
||||
if (seg->kind == SkFileC
|
||||
&& (VKI_S_ISCHR(seg->mode) || VKI_S_ISBLK(seg->mode))) {
|
||||
HChar* dev_name = VG_(am_get_filename)( (NSegment*)seg );
|
||||
if (dev_name && 0 == VG_(strcmp)(dev_name, "/dev/zero")) {
|
||||
// Don't skip /dev/zero.
|
||||
} else {
|
||||
// Skip this device mapping.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (0)
|
||||
VG_(printf)("ACCEPT %2d %#lx %#lx\n", i, seg->start, seg->end);
|
||||
|
||||
// Scan the segment. We use -1 for the clique number, because this
|
||||
// is a root-set.
|
||||
seg_size = seg->end - seg->start + 1;
|
||||
if (VG_(clo_verbosity) > 2) {
|
||||
VG_(message)(Vg_DebugMsg,
|
||||
" Scanning root segment: %#lx..%#lx (%lu)\n",
|
||||
seg->start, seg->end, seg_size);
|
||||
}
|
||||
lc_scan_memory(seg->start, seg_size, /*is_prior_definite*/True, -1);
|
||||
}
|
||||
}
|
||||
scan_memory_root_set(/*searched*/0, 0);
|
||||
|
||||
// Scan GP registers for chunk pointers.
|
||||
VG_(apply_to_GP_regs)(lc_push_if_a_chunk_ptr_register);
|
||||
@ -1256,7 +1446,7 @@ void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams* lcp)
|
||||
|
||||
// Push this Unreached block onto the stack and process it.
|
||||
lc_push(i, ch);
|
||||
lc_process_markstack(i);
|
||||
lc_process_markstack(/*clique*/i);
|
||||
|
||||
tl_assert(lc_markstack_top == -1);
|
||||
tl_assert(ex->state == Unreached);
|
||||
@ -1265,9 +1455,62 @@ void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckParams* lcp)
|
||||
|
||||
print_results( tid, lcp);
|
||||
|
||||
VG_(free) ( lc_chunks );
|
||||
VG_(free) ( lc_extras );
|
||||
VG_(free) ( lc_markstack );
|
||||
lc_markstack = NULL;
|
||||
// lc_chunks, lc_extras, lr_array and lr_table are kept (needed if user
|
||||
// calls MC_(print_block_list)). lr_table also used for delta leak reporting
|
||||
// between this leak search and the next leak search.
|
||||
}
|
||||
|
||||
static Addr searched_wpa;
|
||||
static SizeT searched_szB;
|
||||
static void
|
||||
search_address_in_GP_reg(ThreadId tid, HChar* regname, Addr addr_in_reg)
|
||||
{
|
||||
if (addr_in_reg >= searched_wpa
|
||||
&& addr_in_reg < searched_wpa + searched_szB) {
|
||||
if (addr_in_reg == searched_wpa)
|
||||
VG_(umsg)
|
||||
("tid %d register %s pointing at %#lx\n",
|
||||
tid, regname, searched_wpa);
|
||||
else
|
||||
VG_(umsg)
|
||||
("tid %d register %s interior pointing %lu bytes inside %#lx\n",
|
||||
tid, regname, (long unsigned) addr_in_reg - searched_wpa,
|
||||
searched_wpa);
|
||||
}
|
||||
}
|
||||
|
||||
void MC_(who_points_at) ( Addr address, SizeT szB)
|
||||
{
|
||||
MC_Chunk** chunks;
|
||||
Int n_chunks;
|
||||
Int i;
|
||||
|
||||
if (szB == 1)
|
||||
VG_(umsg) ("Searching for pointers to %#lx\n", address);
|
||||
else
|
||||
VG_(umsg) ("Searching for pointers pointing in %lu bytes from %#lx\n",
|
||||
szB, address);
|
||||
|
||||
// Scan memory root-set, searching for ptr pointing in address[szB]
|
||||
scan_memory_root_set(address, szB);
|
||||
|
||||
// Scan active malloc-ed chunks
|
||||
chunks = find_active_chunks(&n_chunks);
|
||||
for (i = 0; i < n_chunks; i++) {
|
||||
lc_scan_memory(chunks[i]->data, chunks[i]->szB,
|
||||
/*is_prior_definite*/True,
|
||||
/*clique*/-1, /*cur_clique*/-1,
|
||||
address, szB);
|
||||
}
|
||||
VG_(free) ( chunks );
|
||||
|
||||
// Scan GP registers for pointers to address range.
|
||||
searched_wpa = address;
|
||||
searched_szB = szB;
|
||||
VG_(apply_to_GP_regs)(search_address_in_GP_reg);
|
||||
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
@ -5027,6 +5027,12 @@ static void print_monitor_help ( void )
|
||||
" Examples: leak_check\n"
|
||||
" leak_check summary any\n"
|
||||
" leak_check full reachable any limited 100\n"
|
||||
" block_list <loss_record_nr>\n"
|
||||
" after a leak search, shows the list of blocks of <loss_record_nr>\n"
|
||||
" who_points_at <addr> [<len>]\n"
|
||||
" shows places pointing inside <len> (default 1) bytes at <addr>\n"
|
||||
" (with len 1, only shows \"start pointers\" pointing exactly to <addr>,\n"
|
||||
" with len > 1, will also show \"interior pointers\")\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
@ -5044,7 +5050,8 @@ static Bool handle_gdb_monitor_command (ThreadId tid, Char *req)
|
||||
starts with the same first letter(s) as an already existing
|
||||
command. This ensures a shorter abbreviation for the user. */
|
||||
switch (VG_(keyword_id)
|
||||
("help get_vbits leak_check make_memory check_memory",
|
||||
("help get_vbits leak_check make_memory check_memory "
|
||||
"block_list who_points_at",
|
||||
wcmd, kwd_report_duplicated_matches)) {
|
||||
case -2: /* multiple matches */
|
||||
return True;
|
||||
@ -5249,6 +5256,35 @@ static Bool handle_gdb_monitor_command (ThreadId tid, Char *req)
|
||||
return True;
|
||||
}
|
||||
|
||||
case 5: { /* block_list */
|
||||
Char* wl;
|
||||
Char *endptr;
|
||||
UInt lr_nr = 0;
|
||||
wl = VG_(strtok_r) (NULL, " ", &ssaveptr);
|
||||
lr_nr = VG_(strtoull10) (wl, &endptr);
|
||||
if (wl != NULL && *endptr != '\0') {
|
||||
VG_(gdb_printf) ("malformed integer\n");
|
||||
} else {
|
||||
// lr_nr-1 as what is shown to the user is 1 more than the index in lr_array.
|
||||
if (lr_nr == 0 || ! MC_(print_block_list) (lr_nr-1))
|
||||
VG_(gdb_printf) ("invalid loss record nr\n");
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
case 6: { /* who_points_at */
|
||||
Addr address;
|
||||
SizeT szB = 1;
|
||||
|
||||
VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr);
|
||||
if (address == (Addr) 0) {
|
||||
VG_(gdb_printf) ("Cannot search who points at 0x0\n");
|
||||
return True;
|
||||
}
|
||||
MC_(who_points_at) (address, szB);
|
||||
return True;
|
||||
}
|
||||
|
||||
default:
|
||||
tl_assert(0);
|
||||
return False;
|
||||
|
||||
@ -1018,6 +1018,12 @@ void MC_(print_malloc_stats) ( void )
|
||||
);
|
||||
}
|
||||
|
||||
SizeT MC_(get_cmalloc_n_frees) ( void )
|
||||
{
|
||||
return cmalloc_n_frees;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
/*--- end ---*/
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user