summaryrefslogtreecommitdiff
path: root/memcheck
diff options
context:
space:
mode:
authorsewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9>2011-05-06 21:02:55 +0000
committersewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9>2011-05-06 21:02:55 +0000
commit3b290486cd4cd601b20e04340e593c9ed9717e5f (patch)
treeaa42d3aca15d82df7023cd2455bc2dbc8c34417e /memcheck
parent9943003f546be63b208d02ab57fd31b1c00b8aba (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.xml119
-rw-r--r--memcheck/mc_errors.c13
-rw-r--r--memcheck/mc_include.h3
-rw-r--r--memcheck/mc_main.c322
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 &lt;addr&gt; [&lt;len&gt;]</varname>
+ outputs the validity bits for the range of &lt;len&gt; (default 1)
+ bytes at &lt;addr&gt;. 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] &lt;addr&gt; [&lt;len&gt;]</varname>
+ marks the range of &lt;len&gt; (default 1) bytes at &lt;addr&gt;
+ 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] &lt;addr&gt;
+ [&lt;len&gt;]</varname> checks that the range of &lt;len&gt;
+ (default 1) bytes at &lt;addr&gt; has the given accessibility. It
+ then outputs a description of &lt;addr&gt;. 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)" );