diff options
author | sewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-05-06 21:02:55 +0000 |
---|---|---|
committer | sewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-05-06 21:02:55 +0000 |
commit | 3b290486cd4cd601b20e04340e593c9ed9717e5f (patch) | |
tree | aa42d3aca15d82df7023cd2455bc2dbc8c34417e /memcheck | |
parent | 9943003f546be63b208d02ab57fd31b1c00b8aba (diff) |
Implement a GDB server in Valgrind. See #214909.
(Philippe Waroquiers, philippe.waroquiers@skynet.be)
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11727 a5019735-40e9-0310-863c-91ae7b9d1cf9
Diffstat (limited to 'memcheck')
-rw-r--r-- | memcheck/docs/mc-manual.xml | 119 | ||||
-rw-r--r-- | memcheck/mc_errors.c | 13 | ||||
-rw-r--r-- | memcheck/mc_include.h | 3 | ||||
-rw-r--r-- | memcheck/mc_main.c | 322 |
4 files changed, 396 insertions, 61 deletions
diff --git a/memcheck/docs/mc-manual.xml b/memcheck/docs/mc-manual.xml index 433e2e05..8c6294c6 100644 --- a/memcheck/docs/mc-manual.xml +++ b/memcheck/docs/mc-manual.xml @@ -1270,7 +1270,126 @@ is:</para> </sect2> </sect1> +<sect1 id="mc-manual.monitor-commands" xreflabel="Memcheck Monitor Commands"> +<title>Memcheck Monitor Commands</title> +<para>The Memcheck tool provides monitor commands handled by the Valgrind +gdbserver (see <xref linkend="manual-core.gdbserver-commandhandling"/>). +</para> + +<itemizedlist> + <listitem> + <para><varname>mc.get_vbits <addr> [<len>]</varname> + outputs the validity bits for the range of <len> (default 1) + bytes at <addr>. The validity of each byte of the range is + given using two hexadecimal digits. These hexadecimal digits are + encoding the validity of each bit of the corresponding byte, using + 0 if the bit is valid and 1 if the bit is invalid. In the + following example, 'string10' is an array of 10 characters in + which one byte on two is undefined. If a byte is not addressable, + its validity bits are replaced by __. In the below example, the byte 6 + is not addressable.</para> +<programlisting><![CDATA[ +(gdb) p &string10 +$4 = (char (*)[10]) 0x8049e28 +(gdb) monitor mc.get_vbits 0x8049e28 10 +ff00ff00 ff__ff00 ff00 +(gdb) +]]></programlisting> + </listitem> + + <listitem> + <para><varname>mc.make_memory [noaccess|undefined|defined|ifaddressabledefined] <addr> [<len>]</varname> + marks the range of <len> (default 1) bytes at <addr> + with the given accessibility. Marking with 'noaccess' changes the + (A) bits of the range to be not addressable. Marking with + 'undefined' or 'defined' are changing the definedness of the + range. 'ifaddressabledefined' marks the range as defined but only + if the range is addressable. In the following example, the first + byte of the 'string10' is marked as defined. + </para> +<programlisting><![CDATA[ +(gdb) monitor mc.make_memory defined 0x8049e28 1 +(gdb) monitor mc.get_vbits 0x8049e28 10 +0000ff00 ff00ff00 ff00 +(gdb) +]]></programlisting> + </listitem> + <listitem> + <para><varname>mc.check_memory [addressable|defined] <addr> + [<len>]</varname> checks that the range of <len> + (default 1) bytes at <addr> has the given accessibility. It + then outputs a description of <addr>. In the below case, a + detailed description is given as the option --read-var-info=yes + was used to start Valgrind. + </para> +<programlisting><![CDATA[ +(gdb) monitor mc.check_memory defined 0x8049e28 1 +Address 0x8049E28 len 1 defined +==14698== Location 0x8049e28 is 0 bytes inside string10[0], +==14698== declared at prog.c:10, in frame #0 of thread 1 +(gdb) +]]></programlisting> + </listitem> + + <listitem> + <para><varname>mc.leak_check + [full*|summary] [reachable|leakpossible*|definiteleak]</varname> + starts a leak checking. The * in the arguments above indicates the + default value. </para> + + <para> If the first argument is 'summary', only a summary of + the leak search is given. + </para> + + <para>The second argument controls which entries are output + for a 'full' leak search. The value 'definiteleak' indicates to + output only the definitely leaked blocks. The value 'leakpossible' + will output in addition the possibly leaked blocks. The value + 'reachable' will output all blocks (reachable, possibly leaked, + definitely leaked). + </para> + <para>The below is an example of using the mc.leak_check monitor + command on the leak-cases Memcheck regression tests.</para> +<programlisting><![CDATA[ +(gdb) monitor mc.leak_check full leakpossible +==14729== 16 bytes in 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 bytes in 2 blocks +==14729== indirectly lost: 16 bytes in 1 blocks +==14729== possibly lost: 32 bytes in 2 blocks +==14729== still reachable: 96 bytes in 6 blocks +==14729== suppressed: 0 bytes in 0 blocks +==14729== Reachable blocks (those to which a pointer was found) are not shown. +==14729== To see them, rerun with: --leak-check=full --show-reachable=yes +==14729== +(gdb) mo mc.l +==14729== LEAK SUMMARY: +==14729== definitely lost: 32 bytes in 2 blocks +==14729== indirectly lost: 16 bytes in 1 blocks +==14729== possibly lost: 32 bytes in 2 blocks +==14729== still reachable: 96 bytes in 6 blocks +==14729== suppressed: 0 bytes in 0 blocks +==14729== Reachable blocks (those to which a pointer was found) are not shown. +==14729== To see them, rerun with: --leak-check=full --show-reachable=yes +==14729== +(gdb) +]]></programlisting> + <para>Note that when using the Valgrind gdbserver, it is not + needed to rerun with --leak-check=full --show-reachable=yes to see + the reachable blocks. You can obtain the same information without + rerunning by using the gdb command 'monitor mc.leak_check full + reachable' (or, using abbreviation: 'mo mc.l f r'). + </para> + </listitem> +</itemizedlist> + +</sect1> <sect1 id="mc-manual.clientreqs" xreflabel="Client requests"> <title>Client Requests</title> diff --git a/memcheck/mc_errors.c b/memcheck/mc_errors.c index e09d5147..e0133289 100644 --- a/memcheck/mc_errors.c +++ b/memcheck/mc_errors.c @@ -30,6 +30,7 @@ */ #include "pub_tool_basics.h" +#include "pub_tool_gdbserver.h" #include "pub_tool_hashtable.h" // For mc_include.h #include "pub_tool_libcbase.h" #include "pub_tool_libcassert.h" @@ -787,6 +788,9 @@ void MC_(record_address_error) ( ThreadId tid, Addr a, Int szB, if (MC_(in_ignored_range)(a)) return; + if (VG_(is_watched)( (isWrite ? write_watchpoint : read_watchpoint), a, szB)) + return; + # if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) /* AIX zero-page handling. On AIX, reads from page zero are, bizarrely enough, legitimate. Writes to page zero aren't, @@ -1182,6 +1186,15 @@ static void describe_addr ( Addr a, /*OUT*/AddrInfo* ai ) return; } +void MC_(pp_describe_addr) ( Addr a ) +{ + AddrInfo ai; + + ai.tag = Addr_Undescribed; + describe_addr (a, &ai); + mc_pp_AddrInfo (a, &ai, /* maybe_gcc */ False); +} + /* Fill in *origin_ec as specified by otag, or NULL it out if otag does not refer to a known origin. */ static void update_origin ( /*OUT*/ExeContext** origin_ec, diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h index ece6eac3..d6edbf2d 100644 --- a/memcheck/mc_include.h +++ b/memcheck/mc_include.h @@ -355,6 +355,9 @@ Bool MC_(record_leak_error) ( ThreadId tid, Bool print_record, Bool count_error ); +/* prints a description of address a */ +void MC_(pp_describe_addr) (Addr a); + /* Is this address in a user-specified "ignored range" ? */ Bool MC_(in_ignored_range) ( Addr a ); diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index 47122215..0cdb4a50 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -32,6 +32,7 @@ #include "pub_tool_basics.h" #include "pub_tool_aspacemgr.h" +#include "pub_tool_gdbserver.h" #include "pub_tool_hashtable.h" // For mc_include.h #include "pub_tool_libcbase.h" #include "pub_tool_libcassert.h" @@ -1049,67 +1050,17 @@ INLINE Bool MC_(in_ignored_range) ( Addr a ) return False; } - -/* Parse a 32- or 64-bit hex number, including leading 0x, from string - starting at *ppc, putting result in *result, and return True. Or - fail, in which case *ppc and *result are undefined, and return - False. */ - -static Bool isHex ( UChar c ) -{ - return ((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F')); -} - -static UInt fromHex ( UChar c ) -{ - if (c >= '0' && c <= '9') - return (UInt)c - (UInt)'0'; - if (c >= 'a' && c <= 'f') - return 10 + (UInt)c - (UInt)'a'; - if (c >= 'A' && c <= 'F') - return 10 + (UInt)c - (UInt)'A'; - /*NOTREACHED*/ - tl_assert(0); - return 0; -} - -static Bool parse_Addr ( UChar** ppc, Addr* result ) -{ - Int used, limit = 2 * sizeof(Addr); - if (**ppc != '0') - return False; - (*ppc)++; - if (**ppc != 'x') - return False; - (*ppc)++; - *result = 0; - used = 0; - while (isHex(**ppc)) { - UInt d = fromHex(**ppc); - tl_assert(d < 16); - *result = ((*result) << 4) | fromHex(**ppc); - (*ppc)++; - used++; - if (used > limit) return False; - } - if (used == 0) - return False; - return True; -} - -/* Parse two such numbers separated by a dash, or fail. */ +/* Parse two Addr separated by a dash, or fail. */ static Bool parse_range ( UChar** ppc, Addr* result1, Addr* result2 ) { - Bool ok = parse_Addr(ppc, result1); + Bool ok = VG_(parse_Addr) (ppc, result1); if (!ok) return False; if (**ppc != '-') return False; (*ppc)++; - ok = parse_Addr(ppc, result2); + ok = VG_(parse_Addr) (ppc, result2); if (!ok) return False; return True; @@ -4513,17 +4464,20 @@ static Int mc_get_or_set_vbits_for_client ( Addr a, Addr vbits, SizeT szB, - Bool setting /* True <=> set vbits, False <=> get vbits */ + Bool setting, /* True <=> set vbits, False <=> get vbits */ + Bool is_client_request /* True <=> real user request + False <=> internal call from gdbserver */ ) { SizeT i; Bool ok; UChar vbits8; - /* Check that arrays are addressible before doing any getting/setting. */ + /* Check that arrays are addressible before doing any getting/setting. + vbits to be checked only for real user request. */ for (i = 0; i < szB; i++) { if (VA_BITS2_NOACCESS == get_vabits2(a + i) || - VA_BITS2_NOACCESS == get_vabits2(vbits + i)) { + (is_client_request && VA_BITS2_NOACCESS == get_vabits2(vbits + i))) { return 3; } } @@ -4542,8 +4496,9 @@ static Int mc_get_or_set_vbits_for_client ( tl_assert(ok); ((UChar*)vbits)[i] = vbits8; } - // The bytes in vbits[] have now been set, so mark them as such. - MC_(make_mem_defined)(vbits, szB); + if (is_client_request) + // The bytes in vbits[] have now been set, so mark them as such. + MC_(make_mem_defined)(vbits, szB); } return 1; @@ -4974,6 +4929,220 @@ static void show_client_block_stats ( void ) ); } +static void print_monitor_help ( void ) +{ + VG_(gdb_printf) + ( +"\n" +"memcheck monitor commands:\n" +" mc.get_vbits <addr> [<len>]\n" +" returns validity bits for <len> (or 1) bytes at <addr>\n" +" bit values 0 = valid, 1 = invalid, __ = unaddressable byte\n" +" Example: mc.get_vbits 0x8049c78 10\n" +" mc.make_memory [noaccess|undefined\n" +" |defined|ifaddressabledefined] <addr> [<len>]\n" +" mark <len> (or 1) bytes at <addr> with the given accessibility\n" +" mc.check_memory [addressable|defined] <addr> [<len>]\n" +" check that <len> (or 1) bytes at <addr> have the given accessibility\n" +" and outputs a description of <addr>\n" +" mc.leak_check [full*|summary]\n" +" [reachable|leakpossible*|definiteleak]\n" +" * = defaults\n" +" Examples: mc.leak_check\n" +" mc.leak_check any summary\n" +"\n"); +} + +/* return True if request recognised, False otherwise */ +static Bool handle_gdb_monitor_command (ThreadId tid, Char *req) +{ + Char* wcmd; + Char s[VG_(strlen(req))]; /* copy for strtok_r */ + Char *ssaveptr; + + VG_(strcpy) (s, req); + + wcmd = VG_(strtok_r) (s, " ", &ssaveptr); + /* NB: if possible, avoid introducing a new command below which + starts with the same 4 first letters as an already existing + command. This ensures a shorter abbreviation for the user. */ + switch (VG_(keyword_id) + ("help mc.get_vbits mc.leak_check mc.make_memory mc.check_memory", + wcmd, kwd_report_duplicated_matches)) { + case -2: /* multiple matches */ + return True; + case -1: /* not found */ + return False; + case 0: /* help */ + print_monitor_help(); + return True; + case 1: { /* mc.get_vbits */ + Addr address; + SizeT szB = 1; + VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr); + if (szB != 0) { + UChar vbits; + Int i; + Int unaddressable = 0; + for (i = 0; i < szB; i++) { + Int res = mc_get_or_set_vbits_for_client + (address+i, (Addr) &vbits, 1, + False, /* get them */ + False /* is client request */ ); + if ((i % 32) == 0 && i != 0) + VG_(gdb_printf) ("\n"); + else if ((i % 4) == 0 && i != 0) + VG_(gdb_printf) (" "); + if (res == 1) { + VG_(gdb_printf) ("%02x", vbits); + } else { + tl_assert(3 == res); + unaddressable++; + VG_(gdb_printf) ("__"); + } + } + if ((i % 80) != 0) + VG_(gdb_printf) ("\n"); + if (unaddressable) { + VG_(gdb_printf) + ("Address %p len %ld has %d bytes unaddressable\n", + (void *)address, szB, unaddressable); + } + } + return True; + } + case 2: { /* mc.leak_check */ + Int err = 0; + Bool save_clo_show_reachable = MC_(clo_show_reachable); + Bool save_clo_show_possibly_lost = MC_(clo_show_possibly_lost); + Char* kw; + + LeakCheckMode mode; + + MC_(clo_show_reachable) = False; + mode = LC_Full; + + for (kw = VG_(strtok_r) (NULL, " ", &ssaveptr); + kw != NULL; + kw = VG_(strtok_r) (NULL, " ", &ssaveptr)) { + switch (VG_(keyword_id) + ("full summary " + "reachable leakpossible definiteleak", + kw, kwd_report_all)) { + case -2: err++; break; + case -1: err++; break; + case 0: mode = LC_Full; break; + case 1: mode = LC_Summary; break; + case 2: MC_(clo_show_reachable) = True; + MC_(clo_show_possibly_lost) = True; break; + case 3: MC_(clo_show_reachable) = False; + MC_(clo_show_possibly_lost) = True; break; + case 4: MC_(clo_show_reachable) = False; + MC_(clo_show_possibly_lost) = False; break; + default: tl_assert (0); + } + } + if (!err) + MC_(detect_memory_leaks)(tid, mode); + + MC_(clo_show_reachable) = save_clo_show_reachable; + MC_(clo_show_possibly_lost) = save_clo_show_possibly_lost; + return True; + } + + case 3: { /* mc.make_memory */ + Addr address; + SizeT szB = 1; + int kwdid = VG_(keyword_id) + ("noaccess undefined defined ifaddressabledefined", + VG_(strtok_r) (NULL, " ", &ssaveptr), kwd_report_all); + VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr); + if (address == (Addr) 0 && szB == 0) return True; + switch (kwdid) { + case -2: break; + case -1: break; + case 0: MC_(make_mem_noaccess) (address, szB); break; + case 1: make_mem_undefined_w_tid_and_okind ( address, szB, tid, + MC_OKIND_USER ); break; + case 2: MC_(make_mem_defined) ( address, szB ); break; + case 3: make_mem_defined_if_addressable ( address, szB ); break;; + default: tl_assert(0); + } + return True; + } + + case 4: { /* mc.check_memory */ + Addr address; + SizeT szB = 1; + Addr bad_addr; + UInt okind; + char* src; + UInt otag; + UInt ecu; + ExeContext* origin_ec; + MC_ReadResult res; + + int kwdid = VG_(keyword_id) + ("addressable defined", + VG_(strtok_r) (NULL, " ", &ssaveptr), kwd_report_all); + VG_(strtok_get_address_and_size) (&address, &szB, &ssaveptr); + if (address == (Addr) 0 && szB == 0) return True; + switch (kwdid) { + case -2: break; + case -1: break; + case 0: + if (is_mem_addressable ( address, szB, &bad_addr )) + VG_(gdb_printf) ("Address %p len %ld addressable\n", + (void *)address, szB); + else + VG_(gdb_printf) + ("Address %p len %ld not addressable:\nbad address %p\n", + (void *)address, szB, (void *) bad_addr); + MC_(pp_describe_addr) (address); + break; + case 1: res = is_mem_defined ( address, szB, &bad_addr, &otag ); + if (MC_AddrErr == res) + VG_(gdb_printf) + ("Address %p len %ld not addressable:\nbad address %p\n", + (void *)address, szB, (void *) bad_addr); + else if (MC_ValueErr == res) { + okind = otag & 3; + switch (okind) { + case MC_OKIND_STACK: + src = " was created by a stack allocation"; break; + case MC_OKIND_HEAP: + src = " was created by a heap allocation"; break; + case MC_OKIND_USER: + src = " was created by a client request"; break; + case MC_OKIND_UNKNOWN: + src = ""; break; + default: tl_assert(0); + } + VG_(gdb_printf) + ("Address %p len %ld not defined:\n" + "Uninitialised value at %p%s\n", + (void *)address, szB, (void *) bad_addr, src); + ecu = otag & ~3; + if (VG_(is_plausible_ECU)(ecu)) { + origin_ec = VG_(get_ExeContext_from_ECU)( ecu ); + VG_(pp_ExeContext)( origin_ec ); + } + } + else + VG_(gdb_printf) ("Address %p len %ld defined\n", + (void *)address, szB); + MC_(pp_describe_addr) (address); + break; + default: tl_assert(0); + } + return True; + } + + default: + tl_assert(0); + return False; + } +} /*------------------------------------------------------------*/ /*--- Client requests ---*/ @@ -4996,7 +5165,8 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) && VG_USERREQ__MEMPOOL_TRIM != arg[0] && VG_USERREQ__MOVE_MEMPOOL != arg[0] && VG_USERREQ__MEMPOOL_CHANGE != arg[0] - && VG_USERREQ__MEMPOOL_EXISTS != arg[0]) + && VG_USERREQ__MEMPOOL_EXISTS != arg[0] + && VG_USERREQ__GDB_MONITOR_COMMAND != arg[0]) return False; switch (arg[0]) { @@ -5074,12 +5244,16 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) case VG_USERREQ__GET_VBITS: *ret = mc_get_or_set_vbits_for_client - ( arg[1], arg[2], arg[3], False /* get them */ ); + ( arg[1], arg[2], arg[3], + False /* get them */, + True /* is client request */ ); break; case VG_USERREQ__SET_VBITS: *ret = mc_get_or_set_vbits_for_client - ( arg[1], arg[2], arg[3], True /* set them */ ); + ( arg[1], arg[2], arg[3], + True /* set them */, + True /* is client request */ ); break; case VG_USERREQ__COUNT_LEAKS: { /* count leaked bytes */ @@ -5215,6 +5389,14 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) return True; } + case VG_USERREQ__GDB_MONITOR_COMMAND: { + Bool handled = handle_gdb_monitor_command (tid, (Char*)arg[1]); + if (handled) + *ret = 1; + else + *ret = 0; + return handled; + } default: VG_(message)( @@ -5790,6 +5972,22 @@ static void mc_fini ( Int exitcode ) } } +/* mark the given addr/len unaddressable for watchpoint implementation + The PointKind will be handled at access time */ +static Bool mc_mark_unaddressable_for_watchpoint (PointKind kind, Bool insert, + Addr addr, SizeT len) +{ + /* GDBTD this is somewhat fishy. We might rather have to save the previous + accessibility and definedness in gdbserver so as to allow restoring it + properly. Currently, we assume that the user only watches things + which are properly addressable and defined */ + if (insert) + MC_(make_mem_noaccess) (addr, len); + else + MC_(make_mem_defined) (addr, len); + return True; +} + static void mc_pre_clo_init(void) { VG_(details_name) ("Memcheck"); @@ -5935,6 +6133,8 @@ static void mc_pre_clo_init(void) VG_(track_post_reg_write) ( mc_post_reg_write ); VG_(track_post_reg_write_clientcall_return)( mc_post_reg_write_clientcall ); + VG_(needs_watchpoint) ( mc_mark_unaddressable_for_watchpoint ); + init_shadow_memory(); MC_(malloc_list) = VG_(HT_construct)( "MC_(malloc_list)" ); MC_(mempool_list) = VG_(HT_construct)( "MC_(mempool_list)" ); |