Enhance block_list memcheck gdbserver monitor command
Due to the (still to be done) default activation of --leak-check-heuristics=all,
improve the block_list monitor command for easier display of blocks
found reachable via heuristics.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15617 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/NEWS b/NEWS
index 8a79369..ea3fa2f 100644
--- a/NEWS
+++ b/NEWS
@@ -48,10 +48,14 @@
than get_vbits when you need to associate byte data value with
their corresponding validity bits.
- - The 'block_list' monitor command now accepts an optional argument
- 'limited <max_blocks>' to control the nr of block addresses
- printed. Also, if a block has been found using an heuristic, then
- 'block_list' will now show the heuristic after the block size.
+ - The 'block_list' monitor command has been enhanced:
+ o it can print a range of loss records
+ o it now accepts an optional argument 'limited <max_blocks>'
+ to control the nr of block printed.
+ o if a block has been found using an heuristic, then
+ 'block_list' now shows the heuristic after the block size.
+ o the loss records/blocks to print can be limited to the blocks
+ found via specified heuristics.
- The C helper functions used to instrument loads on x86-linux and
arm-linux (both 32-bit only) have been replaced by handwritten
diff --git a/gdbserver_tests/mchelp.stdoutB.exp b/gdbserver_tests/mchelp.stdoutB.exp
index 77d1eb1..6d6bdb1 100644
--- a/gdbserver_tests/mchelp.stdoutB.exp
+++ b/gdbserver_tests/mchelp.stdoutB.exp
@@ -41,8 +41,12 @@
leak_check summary any
leak_check full kinds indirect,possible
leak_check full reachable any limited 100
- block_list <loss_record_nr> [unlimited*|limited <max_blocks>]
+ block_list <loss_record_nr>|<loss_record_nr_from>..<loss_record_nr_to>
+ [unlimited*|limited <max_blocks>]
+ [heuristics heur1,heur2,...]
after a leak search, shows the list of blocks of <loss_record_nr>
+ (or of the range <loss_record_nr_from>..<loss_record_nr_to>).
+ With heuristics, only shows the blocks found via heur1,heur2,...
* = defaults
who_points_at <addr> [<len>]
shows places pointing inside <len> (default 1) bytes at <addr>
@@ -106,8 +110,12 @@
leak_check summary any
leak_check full kinds indirect,possible
leak_check full reachable any limited 100
- block_list <loss_record_nr> [unlimited*|limited <max_blocks>]
+ block_list <loss_record_nr>|<loss_record_nr_from>..<loss_record_nr_to>
+ [unlimited*|limited <max_blocks>]
+ [heuristics heur1,heur2,...]
after a leak search, shows the list of blocks of <loss_record_nr>
+ (or of the range <loss_record_nr_from>..<loss_record_nr_to>).
+ With heuristics, only shows the blocks found via heur1,heur2,...
* = defaults
who_points_at <addr> [<len>]
shows places pointing inside <len> (default 1) bytes at <addr>
diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml
index 69cf90d..39c032b 100644
--- a/memcheck/docs/mc-manual.xml
+++ b/memcheck/docs/mc-manual.xml
@@ -1903,12 +1903,19 @@
</listitem>
<listitem>
- <para><varname>block_list <loss_record_nr>
- [unlimited*|limited <max_blocks>]</varname>
- shows the list of blocks belonging to <loss_record_nr>.
+ <para><varname>block_list <loss_record_nr>|<loss_record_nr_from>..<loss_record_nr_to>
+ [unlimited*|limited <max_blocks>]
+ [heuristics heur1,heur2,...]
+ </varname>
+ shows the list of blocks belonging to
+ <varname><loss_record_nr></varname> (or to the loss records range
+ <varname><loss_record_nr_from>..<loss_record_nr_to></varname>).
The nr of blocks to print can be controlled using the
<varname>limited</varname> argument followed by the maximum nr
of blocks to output.
+ If one or more heuristics are given, only prints the loss records
+ and blocks found via one of the given <varname>heur1,heur2,...</varname>
+ heuristics.
</para>
<para> A leak search merges the allocated blocks in loss records :
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
index 876f0b6..3aeceb5 100644
--- a/memcheck/mc_include.h
+++ b/memcheck/mc_include.h
@@ -460,11 +460,16 @@
// 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.
-// (up to maximum max_blocks)
-// 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, UInt max_blocks);
+// prints the list of blocks corresponding to the given loss_record_nr slice
+// (from/to) (up to maximum max_blocks)
+// Returns True if loss_record_nr_from identifies a correct loss record
+// from last leak search, returns False otherwise.
+// Note that loss_record_nr_to can be bigger than the nr of loss records. All
+// loss records after from will then be examined and maybe printed.
+// If heuristics != 0, print only the loss records/blocks found via
+// one of the heuristics in the set.
+Bool MC_(print_block_list) ( UInt loss_record_nr_from, UInt loss_record_nr_to,
+ UInt max_blocks, UInt heuristics);
// Prints the addresses/registers/... at which a pointer to
// the given range [address, address+szB[ is found.
diff --git a/memcheck/mc_leakcheck.c b/memcheck/mc_leakcheck.c
index e2f752b..e393022 100644
--- a/memcheck/mc_leakcheck.c
+++ b/memcheck/mc_leakcheck.c
@@ -1544,10 +1544,15 @@
}
}
-Bool MC_(print_block_list) ( UInt loss_record_nr, UInt max_blocks)
+Bool MC_(print_block_list) ( UInt loss_record_nr_from,
+ UInt loss_record_nr_to,
+ UInt max_blocks,
+ UInt heuristics)
{
+ UInt loss_record_nr;
UInt i, n_lossrecords;
LossRecord* lr;
+ Bool lr_printed;
UInt remaining = max_blocks;
if (lr_table == NULL || lc_chunks == NULL || lc_extras == NULL) {
@@ -1561,52 +1566,75 @@
}
n_lossrecords = VG_(OSetGen_Size)(lr_table);
- if (loss_record_nr >= n_lossrecords)
- return False; // Invalid loss record nr.
+ if (loss_record_nr_from >= n_lossrecords)
+ return False; // Invalid starting loss record nr.
+
+ if (loss_record_nr_to >= n_lossrecords)
+ loss_record_nr_to = n_lossrecords - 1;
tl_assert (lr_array);
- lr = lr_array[loss_record_nr];
+
+ for (loss_record_nr = loss_record_nr_from;
+ loss_record_nr <= loss_record_nr_to && remaining > 0;
+ loss_record_nr++) {
+ lr = lr_array[loss_record_nr];
+ lr_printed = False;
+
+ /* If user asks to print a specific loss record, we print
+ the block details, even if no block will be shown for this lr.
+ If user asks to print a range of lr, we only print lr details
+ when at least one block is shown. */
+ if (loss_record_nr_from == loss_record_nr_to) {
+ /* (+1 on loss_record_nr as user numbering for loss records
+ starts at 1). */
+ MC_(pp_LossRecord)(loss_record_nr+1, n_lossrecords, lr);
+ lr_printed = True;
+ }
- // (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 && remaining > 0; 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 = MC_(allocated_at)(ch);
- // Match the chunks with loss records.
- for (i = 0; i < lc_n_chunks && remaining > 0; 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 = MC_(allocated_at)(ch);
+ 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, output the
+ // pointer.
+ if (old_lr == lr_array[loss_record_nr]
+ && (heuristics == 0 || HiS(ex->heuristic, heuristics))) {
+ if (!lr_printed) {
+ MC_(pp_LossRecord)(loss_record_nr+1, n_lossrecords, lr);
+ lr_printed = True;
+ }
- 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, output the pointer.
- if (old_lr == lr_array[loss_record_nr]) {
- if (ex->heuristic)
- VG_(umsg)("%p[%lu] (found via heuristic %s)\n",
- (void *)ch->data, (SizeT)ch->szB,
- pp_heuristic (ex->heuristic));
- else
- VG_(umsg)("%p[%lu]\n",
- (void *)ch->data, (SizeT)ch->szB);
- remaining--;
- 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, &remaining);
+ if (ex->heuristic)
+ VG_(umsg)("%p[%lu] (found via heuristic %s)\n",
+ (void *)ch->data, (SizeT)ch->szB,
+ pp_heuristic (ex->heuristic));
+ else
+ VG_(umsg)("%p[%lu]\n",
+ (void *)ch->data, (SizeT)ch->szB);
+ remaining--;
+ 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, &remaining);
+ }
}
+ } else {
+ // No existing loss record matches this chunk ???
+ VG_(umsg)("error: no loss record found for %p[%lu]?????\n",
+ (void *)ch->data, (SizeT)ch->szB);
}
- } else {
- // No existing loss record matches this chunk ???
- VG_(umsg)("error: no loss record found for %p[%lu]?????\n",
- (void *)ch->data, (SizeT)ch->szB);
}
}
return True;
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index 2902c6b..838a296 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -6032,8 +6032,12 @@
" leak_check summary any\n"
" leak_check full kinds indirect,possible\n"
" leak_check full reachable any limited 100\n"
-" block_list <loss_record_nr> [unlimited*|limited <max_blocks>]\n"
+" block_list <loss_record_nr>|<loss_record_nr_from>..<loss_record_nr_to>\n"
+" [unlimited*|limited <max_blocks>]\n"
+" [heuristics heur1,heur2,...]\n"
" after a leak search, shows the list of blocks of <loss_record_nr>\n"
+" (or of the range <loss_record_nr_from>..<loss_record_nr_to>).\n"
+" With heuristics, only shows the blocks found via heur1,heur2,...\n"
" * = defaults\n"
" who_points_at <addr> [<len>]\n"
" shows places pointing inside <len> (default 1) bytes at <addr>\n"
@@ -6064,6 +6068,81 @@
VG_(printf) ("\n"); // Terminate previous line
}
+
+/* Returns the address of the next non space character,
+ or address of the string terminator. */
+static HChar* next_non_space (HChar *s)
+{
+ while (*s && *s == ' ')
+ s++;
+ return s;
+}
+
+/* Parse an integer slice, i.e. a single integer or a range of integer.
+ Syntax is:
+ <integer>[..<integer> ]
+ (spaces are allowed before and/or after ..).
+ Return True if range correctly parsed, False otherwise. */
+static Bool VG_(parse_slice) (HChar* s, HChar** saveptr,
+ UInt *from, UInt *to)
+{
+ HChar* wl;
+ HChar *endptr;
+ endptr = NULL;////
+ wl = VG_(strtok_r) (s, " ", saveptr);
+
+ /* slice must start with an integer. */
+ if (wl == NULL) {
+ VG_(gdb_printf) ("expecting integer or slice <from>..<to>\n");
+ return False;
+ }
+ *from = VG_(strtoull10) (wl, &endptr);
+ if (endptr == wl) {
+ VG_(gdb_printf) ("invalid integer or slice <from>..<to>\n");
+ return False;
+ }
+
+ if (*endptr == '\0' && *next_non_space(*saveptr) != '.') {
+ /* wl token is an integer terminating the string
+ or else next token does not start with .
+ In both cases, the slice is a single integer. */
+ *to = *from;
+ return True;
+ }
+
+ if (*endptr == '\0') {
+ // iii .. => get the next token
+ wl = VG_(strtok_r) (NULL, " .", saveptr);
+ } else {
+ // It must be iii..
+ if (*endptr != '.' && *(endptr+1) != '.') {
+ VG_(gdb_printf) ("expecting slice <from>..<to>\n");
+ return False;
+ }
+ if ( *(endptr+2) == ' ') {
+ // It must be iii.. jjj => get the next token
+ wl = VG_(strtok_r) (NULL, " .", saveptr);
+ } else {
+ // It must be iii..jjj
+ wl = endptr+2;
+ }
+ }
+
+ *to = VG_(strtoull10) (wl, &endptr);
+ if (*endptr != '\0') {
+ VG_(gdb_printf) ("missing/wrong 'to' of slice <from>..<to>\n");
+ return False;
+ }
+
+ if (*from > *to) {
+ VG_(gdb_printf) ("<from> cannot be bigger than <to> "
+ "in slice <from>..<to>\n");
+ return False;
+ }
+
+ return True;
+}
+
/* return True if request recognised, False otherwise */
static Bool handle_gdb_monitor_command (ThreadId tid, HChar *req)
{
@@ -6316,22 +6395,19 @@
case 5: { /* block_list */
HChar* wl;
- HChar *endptr;
HChar *the_end;
- UInt lr_nr = 0;
+ UInt lr_nr_from = 0;
+ UInt lr_nr_to = 0;
- wl = VG_(strtok_r) (NULL, " ", &ssaveptr);
- if (wl != NULL)
- lr_nr = VG_(strtoull10) (wl, &endptr);
- if (wl == NULL || *endptr != '\0') {
- VG_(gdb_printf) ("malformed or missing integer\n");
- } else {
- UInt limit_blocks;
+ if (VG_(parse_slice) (NULL, &ssaveptr, &lr_nr_from, &lr_nr_to)) {
+ UInt limit_blocks = 999999999;
Int int_value;
-
- wl = VG_(strtok_r) (NULL, " ", &ssaveptr);
- if (wl != NULL) {
- switch (VG_(keyword_id) ("unlimited limited ",
+ UInt heuristics = 0;
+
+ for (wl = VG_(strtok_r) (NULL, " ", &ssaveptr);
+ wl != NULL;
+ wl = VG_(strtok_r) (NULL, " ", &ssaveptr)) {
+ switch (VG_(keyword_id) ("unlimited limited heuristics ",
wl, kwd_report_all)) {
case -2: return True;
case -1: return True;
@@ -6355,15 +6431,27 @@
}
limit_blocks = (UInt) int_value;
break;
+ case 2: /* heuristics */
+ wcmd = VG_(strtok_r) (NULL, " ", &ssaveptr);
+ if (wcmd == NULL
+ || !VG_(parse_enum_set)(MC_(parse_leak_heuristics_tokens),
+ True,/*allow_all*/
+ wcmd,
+ &heuristics)) {
+ VG_(gdb_printf) ("missing or malformed heuristics set\n");
+ return True;
+ }
+ break;
default:
tl_assert (0);
}
- } else {
- limit_blocks = 999999999;
}
- /* 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, limit_blocks))
+ /* substract 1 from lr_nr_from/lr_nr_to as what is shown to the user
+ is 1 more than the index in lr_array. */
+ if (lr_nr_from == 0 || ! MC_(print_block_list) (lr_nr_from-1,
+ lr_nr_to-1,
+ limit_blocks,
+ heuristics))
VG_(gdb_printf) ("invalid loss record nr\n");
}
return True;