]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
(fixes bug 289939 wish: complete monitor cmd 'leak_check' with details
authorPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Thu, 26 Jan 2012 23:13:52 +0000 (23:13 +0000)
committerPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Thu, 26 Jan 2012 23:13:52 +0000 (23:13 +0000)
 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

17 files changed:
NEWS
coregrind/m_gdbserver/README_DEVELOPERS
coregrind/m_machine.c
gdbserver_tests/Makefile.am
gdbserver_tests/filter_gdb
gdbserver_tests/mcblocklistsearch.stderr.exp [new file with mode: 0644]
gdbserver_tests/mcblocklistsearch.stderrB.exp [new file with mode: 0644]
gdbserver_tests/mcblocklistsearch.stdinB.gdb [new file with mode: 0644]
gdbserver_tests/mcblocklistsearch.vgtest [new file with mode: 0644]
gdbserver_tests/mchelp.stdoutB.exp
include/pub_tool_machine.h
memcheck/docs/mc-manual.xml
memcheck/mc_errors.c
memcheck/mc_include.h
memcheck/mc_leakcheck.c
memcheck/mc_main.c
memcheck/mc_malloc_wrappers.c

diff --git a/NEWS b/NEWS
index 2bb6cfb4730ad2569552843f729d0018ffeed58e..79fd6cb1c44a763d1be457d5ffdf29229d0bcc46 100644 (file)
--- a/NEWS
+++ b/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)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 88aa6175bbc0275911839da70cee9ff32b60230b..55d0790f66bb20e58e4dff547b1170e983e2a33e 100644 (file)
@@ -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 
 
index bbfaa1f8368415c8d71a2926a031a608d4b03f15..7043aea9e31453203926dd09dc12044aa84a1140 100644 (file)
@@ -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);
       }
    }
 }
index 2891c8ba146e93c7c539fc21f2b87ca40fa0371e..310baedb79c7bfff2d108fb7900390c7adce5d01 100644 (file)
@@ -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 \
index be74c787cfe06ab79cdedea6fdaf65ab6518ded8..6f16dcf4ccb51b8b626afb31c6f5af9d018cf3ad 100755 (executable)
@@ -10,7 +10,7 @@ $dir/filter_stderr                                                  |
 $dir/../tests/filter_addresses                                      |
 
 # memcheck stuff
-$dir/filter_memcheck_monitor                                        |
+$dir/filter_memcheck_monitor "$@"                                   |
 
 
 # Anonymise or remove :
diff --git a/gdbserver_tests/mcblocklistsearch.stderr.exp b/gdbserver_tests/mcblocklistsearch.stderr.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/gdbserver_tests/mcblocklistsearch.stderrB.exp b/gdbserver_tests/mcblocklistsearch.stderrB.exp
new file mode 100644 (file)
index 0000000..eb4c7e4
--- /dev/null
@@ -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
diff --git a/gdbserver_tests/mcblocklistsearch.stdinB.gdb b/gdbserver_tests/mcblocklistsearch.stdinB.gdb
new file mode 100644 (file)
index 0000000..19fe0e5
--- /dev/null
@@ -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
diff --git a/gdbserver_tests/mcblocklistsearch.vgtest b/gdbserver_tests/mcblocklistsearch.vgtest
new file mode 100644 (file)
index 0000000..bced1cd
--- /dev/null
@@ -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
index a12b4c4e5f4abc2b9a81ee127ff136f371db6bfb..20b2e7673414a43d3a45fba56be004181d2bbbfc 100644 (file)
@@ -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
index c86fff004ca249b11725c1288ff2d4138cd3dc2d..49c700d3e151793595889f42d18812d42c3cca4a 100644 (file)
@@ -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
index 64817e94cd63c902798a9638fbc4f7c184f89360..3503b59790ea7ea1577c9d46e6e6e5a67b644a2e 100644 (file)
@@ -1372,7 +1372,7 @@ $10 = 0x0
     (default 1) bytes at &lt;addr&gt; has the specified accessibility.
     It then outputs a description of &lt;addr&gt;. 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 &lt;loss_record_nr&gt; </varname>
+    shows the list of blocks belonging to &lt;loss_record_nr&gt;.
+    </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 &lt;addr&gt; [&lt;len&gt;]</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 &gt; 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>
index dedeeccddad93344cb0398d6edcf6bf226a27a4d..7c09804f2f59a7edd051fe0b42e2c748777b6958 100644 (file)
@@ -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;
       }
 
index 21ec0df15c283ca73a64a24b8d7f618b703aaefc..91921d22c560f30b5daa0d3b2d9b47c2747b24e3 100644 (file)
@@ -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);
                           
 
 /*------------------------------------------------------------*/
index 55a5b7bb1fe010cb847da97cf335b566f91e81d1..e500375b738b865ecbeafafa616e5909895189d0 100644 (file)
@@ -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);
+
 }
 
 /*--------------------------------------------------------------------*/
index e6dd6a4c2aa7850f4bd1d1e046b87b2d96cc874f..fcbfe352278d682756ab0b67e15b955645cbb567 100644 (file)
@@ -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;
index fc37e609be1f0950c643cd33c01e13249c74d448..bf67571ffd36c441c15f32641a947f4e3897ece6 100644 (file)
@@ -1018,6 +1018,12 @@ void MC_(print_malloc_stats) ( void )
    );
 }
 
+SizeT MC_(get_cmalloc_n_frees) ( void )
+{
+   return cmalloc_n_frees;
+}
+
+
 /*--------------------------------------------------------------------*/
 /*--- end                                                          ---*/
 /*--------------------------------------------------------------------*/