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
This commit is contained in:
Philippe Waroquiers 2015-09-02 21:26:34 +00:00
parent d0cf6ac4fa
commit 0307c6dcaa
6 changed files with 213 additions and 73 deletions

12
NEWS
View File

@ -48,10 +48,14 @@ X86/MacOSX 10.10 and 10.11 and AMD64/MacOSX 10.10 and 10.11.
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

View File

@ -41,8 +41,12 @@ memcheck monitor commands:
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 @@ memcheck monitor commands:
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>

View File

@ -1903,12 +1903,19 @@ Address 0x8049E28 len 1 defined
</listitem>
<listitem>
<para><varname>block_list &lt;loss_record_nr&gt;
[unlimited*|limited &lt;max_blocks&gt;]</varname>
shows the list of blocks belonging to &lt;loss_record_nr&gt;.
<para><varname>block_list &lt;loss_record_nr&gt;|&lt;loss_record_nr_from&gt;..&lt;loss_record_nr_to&gt;
[unlimited*|limited &lt;max_blocks&gt;]
[heuristics heur1,heur2,...]
</varname>
shows the list of blocks belonging to
<varname>&lt;loss_record_nr&gt;</varname> (or to the loss records range
<varname>&lt;loss_record_nr_from&gt;..&lt;loss_record_nr_to&gt;</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 :

View File

@ -460,11 +460,16 @@ extern UInt MC_(leak_search_gen);
// 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.

View File

@ -1544,10 +1544,15 @@ static void print_clique (Int clique, UInt level, UInt *remaining)
}
}
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 @@ Bool MC_(print_block_list) ( UInt loss_record_nr, UInt max_blocks)
}
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;

View File

@ -6032,8 +6032,12 @@ static void print_monitor_help ( void )
" 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 @@ static void gdb_xb (Addr address, SizeT szB, Int res[])
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 @@ static Bool handle_gdb_monitor_command (ThreadId tid, HChar *req)
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 @@ static Bool handle_gdb_monitor_command (ThreadId tid, HChar *req)
}
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;