#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"
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;
static void mc_post_reg_write ( CorePart part, ThreadId tid,
PtrdiffT offset, SizeT size)
{
-# define MAX_REG_WRITE_SIZE 1408
+# define MAX_REG_WRITE_SIZE 1664
UChar area[MAX_REG_WRITE_SIZE];
tl_assert(size <= MAX_REG_WRITE_SIZE);
VG_(memset)(area, V_BITS8_DEFINED, size);
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;
}
}
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;
/*------------------------------------------------------------*/
Bool MC_(clo_partial_loads_ok) = False;
-Long MC_(clo_freelist_vol) = 10*1000*1000LL;
+Long MC_(clo_freelist_vol) = 20*1000*1000LL;
LeakCheckMode MC_(clo_leak_check) = LC_Summary;
VgRes MC_(clo_leak_resolution) = Vg_HighRes;
Bool MC_(clo_show_reachable) = False;
" --undef-value-errors=no|yes check for undefined value errors [yes]\n"
" --track-origins=no|yes show origins of undefined values? [no]\n"
" --partial-loads-ok=no|yes too hard to explain here; see manual [no]\n"
-" --freelist-vol=<number> volume of freed blocks queue [10000000]\n"
+" --freelist-vol=<number> volume of freed blocks queue [20000000]\n"
" --workaround-gcc296-bugs=no|yes self explanatory [no]\n"
" --ignore-ranges=0xPP-0xQQ[,0xRR-0xSS] assume given addresses are OK\n"
" --malloc-fill=<hexnumber> fill malloc'd areas with given value\n"
);
}
+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 ---*/
if (!VG_IS_TOOL_USERREQ('M','C',arg[0])
&& VG_USERREQ__MALLOCLIKE_BLOCK != arg[0]
+ && VG_USERREQ__RESIZEINPLACE_BLOCK != arg[0]
&& VG_USERREQ__FREELIKE_BLOCK != arg[0]
&& VG_USERREQ__CREATE_MEMPOOL != arg[0]
&& VG_USERREQ__DESTROY_MEMPOOL != arg[0]
&& 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]) {
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 */
MC_AllocCustom, MC_(malloc_list) );
return True;
}
+ case VG_USERREQ__RESIZEINPLACE_BLOCK: {
+ Addr p = (Addr)arg[1];
+ SizeT oldSizeB = arg[2];
+ SizeT newSizeB = arg[3];
+ UInt rzB = arg[4];
+
+ MC_(handle_resizeInPlace) ( tid, p, oldSizeB, newSizeB, rzB );
+ return True;
+ }
case VG_USERREQ__FREELIKE_BLOCK: {
Addr p = (Addr)arg[1];
UInt rzB = arg[2];
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)(
}
}
+/* 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");
VG_(details_copyright_author)(
"Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.");
VG_(details_bug_reports_to) (VG_BUGS_TO);
- VG_(details_avg_translation_sizeB) ( 556 );
+ VG_(details_avg_translation_sizeB) ( 640 );
VG_(basic_tool_funcs) (mc_post_clo_init,
MC_(instrument),
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)" );