From 0307c6dcaaf5bf9de9327f0acf15002a6e30457a Mon Sep 17 00:00:00 2001 From: Philippe Waroquiers Date: Wed, 2 Sep 2015 21:26:34 +0000 Subject: [PATCH] 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 --- NEWS | 12 ++- gdbserver_tests/mchelp.stdoutB.exp | 12 ++- memcheck/docs/mc-manual.xml | 13 ++- memcheck/mc_include.h | 15 ++-- memcheck/mc_leakcheck.c | 110 +++++++++++++++---------- memcheck/mc_main.c | 126 ++++++++++++++++++++++++----- 6 files changed, 214 insertions(+), 74 deletions(-) diff --git a/NEWS b/NEWS index 8a79369c00..ea3fa2f070 100644 --- a/NEWS +++ b/NEWS @@ -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 ' 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 ' + 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 77d1eb182a..6d6bdb146b 100644 --- a/gdbserver_tests/mchelp.stdoutB.exp +++ b/gdbserver_tests/mchelp.stdoutB.exp @@ -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 [unlimited*|limited ] + block_list |.. + [unlimited*|limited ] + [heuristics heur1,heur2,...] after a leak search, shows the list of blocks of + (or of the range ..). + With heuristics, only shows the blocks found via heur1,heur2,... * = defaults who_points_at [] shows places pointing inside (default 1) bytes at @@ -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 [unlimited*|limited ] + block_list |.. + [unlimited*|limited ] + [heuristics heur1,heur2,...] after a leak search, shows the list of blocks of + (or of the range ..). + With heuristics, only shows the blocks found via heur1,heur2,... * = defaults who_points_at [] shows places pointing inside (default 1) bytes at diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml index 69cf90d765..39c032b79d 100644 --- a/memcheck/docs/mc-manual.xml +++ b/memcheck/docs/mc-manual.xml @@ -1903,12 +1903,19 @@ Address 0x8049E28 len 1 defined - block_list <loss_record_nr> - [unlimited*|limited <max_blocks>] - shows the list of blocks belonging to <loss_record_nr>. + block_list <loss_record_nr>|<loss_record_nr_from>..<loss_record_nr_to> + [unlimited*|limited <max_blocks>] + [heuristics heur1,heur2,...] + + shows the list of blocks belonging to + <loss_record_nr> (or to the loss records range + <loss_record_nr_from>..<loss_record_nr_to>). The nr of blocks to print can be controlled using the limited 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 heur1,heur2,... + heuristics. A leak search merges the allocated blocks in loss records : diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h index 876f0b64e2..3aeceb5647 100644 --- a/memcheck/mc_include.h +++ b/memcheck/mc_include.h @@ -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. diff --git a/memcheck/mc_leakcheck.c b/memcheck/mc_leakcheck.c index e2f752bce9..e39302291f 100644 --- a/memcheck/mc_leakcheck.c +++ b/memcheck/mc_leakcheck.c @@ -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]; - - // (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); + 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; + } + + // 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 2902c6b7e8..838a296629 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -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 [unlimited*|limited ]\n" +" block_list |..\n" +" [unlimited*|limited ]\n" +" [heuristics heur1,heur2,...]\n" " after a leak search, shows the list of blocks of \n" +" (or of the range ..).\n" +" With heuristics, only shows the blocks found via heur1,heur2,...\n" " * = defaults\n" " who_points_at []\n" " shows places pointing inside (default 1) bytes at \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: + [.. ] + (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 ..\n"); + return False; + } + *from = VG_(strtoull10) (wl, &endptr); + if (endptr == wl) { + VG_(gdb_printf) ("invalid integer or slice ..\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 ..\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 ..\n"); + return False; + } + + if (*from > *to) { + VG_(gdb_printf) (" cannot be bigger than " + "in slice ..\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; -- 2.47.2