Factorises the address code description and printing
of memcheck and helgrind in a common module:
  pub_tool_addrinfo.h pub_core_addrinfo.h m_addrinfo.c

At the same time, the factorised code is made usable by other
tools also (and is used by the gdbserver command 'v.info location'
which replaces the helgrind 'describe addr' introduced 1 week ago
and which is now callable by all tools).

The new address description code can describe more addresses
(e.g. for memcheck, if the block is not on the free list anymore,
but is in an arena free list, this will also be described).

Similarly, helgrind address description can now describe more addresses
when --read-var-info=no is given (e.g. global symbols are
described, or addresses on the stack are described as
being on the stack, freed blocks in the arena free list are
described, ...).
See e.g. the change in helgrind/tests/annotate_rwlock.stderr.exp
or locked_vs_unlocked2.stderr.exp

The patch touches many files, but is basically a lot of improvements
in helgrind output files.
The code changes are mostly refactorisation of existing code.




git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13965 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am
index c5cc61d..bd632f9 100644
--- a/coregrind/Makefile.am
+++ b/coregrind/Makefile.am
@@ -158,6 +158,7 @@
 #----------------------------------------------------------------------------
 
 noinst_HEADERS = \
+	pub_core_addrinfo.h	\
 	pub_core_aspacehl.h	\
 	pub_core_aspacemgr.h	\
 	pub_core_basics.h	\
@@ -271,6 +272,7 @@
 endif
 
 COREGRIND_SOURCES_COMMON = \
+	m_addrinfo.c \
 	m_cache.c \
 	m_commandline.c \
 	m_clientstate.c \
diff --git a/coregrind/m_addrinfo.c b/coregrind/m_addrinfo.c
new file mode 100644
index 0000000..bcf6913
--- /dev/null
+++ b/coregrind/m_addrinfo.c
@@ -0,0 +1,354 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Obtaining information about an address.                      ---*/
+/*---                                                 m_addrinfo.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2013 OpenWorks Ltd
+      info@open-works.co.uk
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_xarray.h"
+#include "pub_core_debuginfo.h"
+#include "pub_core_execontext.h"
+#include "pub_core_addrinfo.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_machine.h"
+#include "pub_core_options.h"
+
+void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai )
+{
+   ThreadId   tid;
+   Addr       stack_min, stack_max;
+   VgSectKind sect;
+
+   /* -- Perhaps the variable type/location data describes it? -- */
+   ai->Addr.Variable.descr1
+      = VG_(newXA)( VG_(malloc), "mc.da.descr1",
+                    VG_(free), sizeof(HChar) );
+   ai->Addr.Variable.descr2
+      = VG_(newXA)( VG_(malloc), "mc.da.descr2",
+                    VG_(free), sizeof(HChar) );
+
+   (void) VG_(get_data_description)( ai->Addr.Variable.descr1,
+                                     ai->Addr.Variable.descr2, a );
+   /* If there's nothing in descr1/2, free them.  Why is it safe to to
+      VG_(indexXA) at zero here?  Because VG_(get_data_description)
+      guarantees to zero terminate descr1/2 regardless of the outcome
+      of the call.  So there's always at least one element in each XA
+      after the call.
+   */
+   if (0 == VG_(strlen)( VG_(indexXA)( ai->Addr.Variable.descr1, 0 ))) {
+      VG_(deleteXA)( ai->Addr.Variable.descr1 );
+      ai->Addr.Variable.descr1 = NULL;
+   }
+   if (0 == VG_(strlen)( VG_(indexXA)( ai->Addr.Variable.descr2, 0 ))) {
+      VG_(deleteXA)( ai->Addr.Variable.descr2 );
+      ai->Addr.Variable.descr2 = NULL;
+   }
+   /* Assume (assert) that VG_(get_data_description) fills in descr1
+      before it fills in descr2 */
+   if (ai->Addr.Variable.descr1 == NULL)
+      vg_assert(ai->Addr.Variable.descr2 == NULL);
+   /* So did we get lucky? */
+   if (ai->Addr.Variable.descr1 != NULL) {
+      ai->tag = Addr_Variable;
+      return;
+   }
+   /* -- Have a look at the low level data symbols - perhaps it's in
+      there. -- */
+   VG_(memset)( &ai->Addr.DataSym.name,
+                0, sizeof(ai->Addr.DataSym.name));
+   if (VG_(get_datasym_and_offset)(
+             a, &ai->Addr.DataSym.name[0],
+             sizeof(ai->Addr.DataSym.name)-1,
+             &ai->Addr.DataSym.offset )) {
+      ai->tag = Addr_DataSym;
+      vg_assert( ai->Addr.DataSym.name
+                    [ sizeof(ai->Addr.DataSym.name)-1 ] == 0);
+      return;
+   }
+   /* -- Perhaps it's on a thread's stack? -- */
+   VG_(thread_stack_reset_iter)(&tid);
+   while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) {
+      if (stack_min - VG_STACK_REDZONE_SZB <= a && a <= stack_max) {
+         ai->tag            = Addr_Stack;
+         ai->Addr.Stack.tid = tid;
+         return;
+      }
+   }
+
+   /* -- Maybe it is in one of the m_mallocfree.c arenas. --  */
+   {
+      AddrArenaInfo aai;
+      VG_(describe_arena_addr) ( a, &aai );
+      if (aai.name != NULL) {
+         ai->tag = Addr_Block;
+         if (aai.aid == VG_AR_CLIENT)
+            ai->Addr.Block.block_kind 
+               = aai.free ? Block_ClientArenaFree : Block_ClientArenaMallocd;
+         else
+            ai->Addr.Block.block_kind 
+               = aai.free 
+                  ? Block_ValgrindArenaFree :  Block_ValgrindArenaMallocd;
+         ai->Addr.Block.block_desc = aai.name;
+         ai->Addr.Block.block_szB = aai.block_szB;
+         ai->Addr.Block.rwoffset = aai.rwoffset;
+         ai->Addr.Block.allocated_at = VG_(null_ExeContext)();
+         ai->Addr.Block.freed_at = VG_(null_ExeContext)();
+         return;
+      }
+   }
+
+   /* -- last ditch attempt at classification -- */
+   vg_assert( sizeof(ai->Addr.SectKind.objname) > 4 );
+   VG_(memset)( &ai->Addr.SectKind.objname, 
+                0, sizeof(ai->Addr.SectKind.objname));
+   VG_(strcpy)( ai->Addr.SectKind.objname, "???" );
+   sect = VG_(DebugInfo_sect_kind)( &ai->Addr.SectKind.objname[0],
+                                    sizeof(ai->Addr.SectKind.objname)-1, a);
+   if (sect != Vg_SectUnknown) {
+      ai->tag = Addr_SectKind;
+      ai->Addr.SectKind.kind = sect;
+      vg_assert( ai->Addr.SectKind.objname
+                    [ sizeof(ai->Addr.SectKind.objname)-1 ] == 0);
+      return;
+   }
+   /* -- Clueless ... -- */
+   ai->tag = Addr_Unknown;
+   return;
+}
+
+void VG_(clear_addrinfo) ( AddrInfo* ai)
+{
+   switch (ai->tag) {
+      case Addr_Unknown:
+          break;
+
+      case Addr_Stack: 
+          break;
+
+      case Addr_Block:
+         break;
+
+      case Addr_DataSym:
+         break;
+
+      case Addr_Variable:
+         if (ai->Addr.Variable.descr1 != NULL) {
+            VG_(deleteXA)( ai->Addr.Variable.descr1 );
+            ai->Addr.Variable.descr1 = NULL;
+         }
+         if (ai->Addr.Variable.descr2 != NULL) {
+            VG_(deleteXA)( ai->Addr.Variable.descr2 );
+            ai->Addr.Variable.descr2 = NULL;
+         }
+         break;
+
+      case Addr_SectKind:
+         break;
+
+      default:
+         VG_(core_panic)("VG_(clear_addrinfo)");
+   }
+
+   ai->tag = Addr_Undescribed;
+}
+
+static Bool is_arena_BlockKind(BlockKind bk)
+{
+   switch (bk) {
+      case Block_Mallocd:
+      case Block_Freed:
+      case Block_MempoolChunk:
+      case Block_UserG:                return False;
+
+      case Block_ClientArenaMallocd:
+      case Block_ClientArenaFree:
+      case Block_ValgrindArenaMallocd:
+      case Block_ValgrindArenaFree:    return True;
+
+      default:                         vg_assert (0);
+   }
+}
+
+static void pp_addrinfo_WRK ( Addr a, AddrInfo* ai, Bool mc, Bool maybe_gcc )
+{
+   const HChar* xpre  = VG_(clo_xml) ? "  <auxwhat>" : " ";
+   const HChar* xpost = VG_(clo_xml) ? "</auxwhat>"  : "";
+
+   vg_assert (!maybe_gcc || mc); // maybe_gcc can only be given in mc mode.
+
+   switch (ai->tag) {
+      case Addr_Unknown:
+         if (maybe_gcc) {
+            VG_(emit)( "%sAddress 0x%llx is just below the stack ptr.  "
+                       "To suppress, use: --workaround-gcc296-bugs=yes%s\n",
+                       xpre, (ULong)a, xpost );
+	 } else {
+            VG_(emit)( "%sAddress 0x%llx "
+                       "is not stack'd, malloc'd or %s%s\n",
+                       xpre, 
+                       (ULong)a, 
+                       mc ? "(recently) free'd" : "on a free list",
+                       xpost );
+         }
+         break;
+
+      case Addr_Stack: 
+         VG_(emit)( "%sAddress 0x%llx is on thread %d's stack%s\n", 
+                    xpre, (ULong)a, ai->Addr.Stack.tid, xpost );
+         break;
+
+      case Addr_Block: {
+         SizeT    block_szB = ai->Addr.Block.block_szB;
+         PtrdiffT rwoffset  = ai->Addr.Block.rwoffset;
+         SizeT    delta;
+         const    HChar* relative;
+
+         if (rwoffset < 0) {
+            delta    = (SizeT)(-rwoffset);
+            relative = "before";
+         } else if (rwoffset >= block_szB) {
+            delta    = rwoffset - block_szB;
+            relative = "after";
+         } else {
+            delta    = rwoffset;
+            relative = "inside";
+         }
+         if (is_arena_BlockKind (ai->Addr.Block.block_kind))
+            VG_(emit)(
+               "%sAddress 0x%lx is %'lu bytes %s a%s block of size %'lu"
+               " in arena \"%s\"%s\n",
+               xpre,
+               a, delta,
+               relative,
+               ai->Addr.Block.block_kind==Block_ClientArenaMallocd
+                 || ai->Addr.Block.block_kind==Block_ValgrindArenaMallocd
+                 ? "" : "n unallocated",
+               block_szB,
+               ai->Addr.Block.block_desc,  // arena name
+               xpost
+            );
+         else
+            VG_(emit)(
+               "%sAddress 0x%lx is %'lu bytes %s a %s of size %'lu %s%s\n",
+               xpre,
+               a, delta,
+               relative,
+               ai->Addr.Block.block_desc,
+               block_szB,
+               ai->Addr.Block.block_kind==Block_Mallocd ? "alloc'd" 
+               : ai->Addr.Block.block_kind==Block_Freed ? "free'd" 
+                                                        : "client-defined",
+               xpost
+            );
+         if (ai->Addr.Block.block_kind==Block_Mallocd) {
+            VG_(pp_ExeContext)(ai->Addr.Block.allocated_at);
+            tl_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)());
+         }
+         else if (ai->Addr.Block.block_kind==Block_Freed) {
+            VG_(pp_ExeContext)(ai->Addr.Block.freed_at);
+            if (ai->Addr.Block.allocated_at != VG_(null_ExeContext)()) {
+               VG_(emit)(
+                  "%s block was alloc'd at%s\n",
+                  xpre,
+                  xpost
+               );
+               VG_(pp_ExeContext)(ai->Addr.Block.allocated_at);
+            }
+         }
+         else if (ai->Addr.Block.block_kind==Block_MempoolChunk
+                  || ai->Addr.Block.block_kind==Block_UserG) {
+            // client-defined
+            VG_(pp_ExeContext)(ai->Addr.Block.allocated_at);
+            tl_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)());
+            /* Nb: cannot have a freed_at, as a freed client-defined block
+               has a Block_Freed block_kind. */
+         } else {
+            // Client or Valgrind arena. At least currently, we never
+            // have stacktraces for these.
+            tl_assert (ai->Addr.Block.allocated_at == VG_(null_ExeContext)());
+            tl_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)());
+         }
+         
+         break;
+      }
+
+      case Addr_DataSym:
+         VG_(emit)( "%sAddress 0x%llx is %llu bytes "
+                    "inside data symbol \"%pS\"%s\n",
+                    xpre,
+                    (ULong)a,
+                    (ULong)ai->Addr.DataSym.offset,
+                    ai->Addr.DataSym.name,
+                    xpost );
+         break;
+
+      case Addr_Variable:
+         /* Note, no need for XML tags here, because descr1/2 will
+            already have <auxwhat> or <xauxwhat>s on them, in XML
+            mode. */
+         if (ai->Addr.Variable.descr1)
+            VG_(emit)( "%s%s\n",
+                       VG_(clo_xml) ? "  " : " ",
+                       (HChar*)VG_(indexXA)(ai->Addr.Variable.descr1, 0) );
+         if (ai->Addr.Variable.descr2)
+            VG_(emit)( "%s%s\n",
+                       VG_(clo_xml) ? "  " : " ",
+                       (HChar*)VG_(indexXA)(ai->Addr.Variable.descr2, 0) );
+         break;
+
+      case Addr_SectKind:
+         VG_(emit)( "%sAddress 0x%llx is in the %pS segment of %pS%s\n",
+                    xpre,
+                    (ULong)a,
+                    VG_(pp_SectKind)(ai->Addr.SectKind.kind),
+                    ai->Addr.SectKind.objname,
+                    xpost );
+         break;
+
+      default:
+         VG_(tool_panic)("mc_pp_AddrInfo");
+   }
+}
+
+void VG_(pp_addrinfo) ( Addr a, AddrInfo* ai )
+{
+   pp_addrinfo_WRK (a, ai, False /*mc*/, False /*maybe_gcc*/);
+}
+
+void VG_(pp_addrinfo_mc) ( Addr a, AddrInfo* ai, Bool maybe_gcc )
+{
+   pp_addrinfo_WRK (a, ai, True /*mc*/, maybe_gcc);
+}
+
+
+/*--------------------------------------------------------------------*/
+/*--- end                                             m_addrinfo.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/m_gdbserver/m_gdbserver.c b/coregrind/m_gdbserver/m_gdbserver.c
index 95edeaf..d1e7334 100644
--- a/coregrind/m_gdbserver/m_gdbserver.c
+++ b/coregrind/m_gdbserver/m_gdbserver.c
@@ -1404,7 +1404,7 @@
       return False;
 }
 
-void VG_(strtok_get_address_and_size) (Addr* address, 
+Bool VG_(strtok_get_address_and_size) (Addr* address, 
                                        SizeT* szB, 
                                        HChar **ssaveptr)
 {
@@ -1419,7 +1419,7 @@
       VG_(gdb_printf) ("missing or malformed address\n");
       *address = (Addr) 0;
       *szB = 0;
-      return;
+      return False;
    }
    ws = VG_(strtok_r) (NULL, " ", ssaveptr);
    if (ws == NULL) {
@@ -1451,8 +1451,9 @@
                        "hex 0x..... or dec ...... or binary .....b\n");
       *address = (Addr) 0;
       *szB = 0;
-      return;
+      return False;
    }
+   return True;
 }
 
 void VG_(gdbserver_status_output)(void)
diff --git a/coregrind/m_gdbserver/server.c b/coregrind/m_gdbserver/server.c
index 45498d6..35094bd 100644
--- a/coregrind/m_gdbserver/server.c
+++ b/coregrind/m_gdbserver/server.c
@@ -31,6 +31,8 @@
 #include "pub_core_syswrap.h"      // VG_(show_open_fds)
 #include "pub_core_scheduler.h"
 #include "pub_core_transtab.h"
+#include "pub_core_debuginfo.h"
+#include "pub_core_addrinfo.h"
 
 unsigned long cont_thread;
 unsigned long general_thread;
@@ -224,6 +226,7 @@
 "  v.wait [<ms>]           : sleep <ms> (default 0) then continue\n"
 "  v.info all_errors       : show all errors found so far\n"
 "  v.info last_error       : show last error found\n"
+"  v.info location <addr>  : show information about location <addr>\n"
 "  v.info n_errs_found [msg] : show the nr of errors found so far and the given msg\n"
 "  v.info open_fds         : show open file descriptors (only if --track-fds=yes)\n"
 "  v.kill                  : kill the Valgrind process\n"
@@ -341,7 +344,7 @@
       wcmd = strtok_r (NULL, " ", &ssaveptr);
       switch (kwdid = VG_(keyword_id) 
               ("all_errors n_errs_found last_error gdbserver_status memory"
-               " scheduler stats open_fds exectxt",
+               " scheduler stats open_fds exectxt location",
                wcmd, kwd_report_all)) {
       case -2:
       case -1: 
@@ -407,6 +410,31 @@
          VG_(print_ExeContext_stats) (True /* with_stacktraces */);
          ret = 1;
          break;
+      case  9: { /* location */
+         /* Note: we prefer 'v.info location' and not 'v.info address' as
+            v.info address is inconsistent with the GDB (native) 
+            command 'info address' which gives the address for a symbol.
+            GDB equivalent command of 'v.info location' is 'info symbol'. */
+         Addr address;
+         SizeT dummy_sz = 0x1234;
+         if (VG_(strtok_get_address_and_size) (&address, &dummy_sz, &ssaveptr)) {
+            // If tool provides location information, use that.
+            if (VG_(needs).info_location) {
+               VG_TDICT_CALL(tool_info_location, address);
+            } 
+            // If tool does not provide location information, use the common one.
+            // Also use the common to compare with tool when debug log is set.
+            if (!VG_(needs).info_location || VG_(debugLog_getLevel)() > 0 ) {
+               AddrInfo ai;
+               ai.tag = Addr_Undescribed;
+               VG_(describe_addr) (address, &ai);
+               VG_(pp_addrinfo) (address, &ai);
+               VG_(clear_addrinfo) (&ai);
+            }
+         }
+         ret = 1;
+         break;
+      }
       default:
          vg_assert(0);
       }
@@ -431,8 +459,7 @@
       
       ret = 1;
 
-      VG_(strtok_get_address_and_size) (&address, &verbosity, &ssaveptr);
-      if (address != (Addr) 0 || verbosity != 0) {
+      if (VG_(strtok_get_address_and_size) (&address, &verbosity, &ssaveptr)) {
          /* we need to force the output to log for the translation trace,
             as low level VEX tracing cannot be redirected to gdb. */
          int saved_command_output_to_log = command_output_to_log;
diff --git a/coregrind/m_libcprint.c b/coregrind/m_libcprint.c
index 450d67e..5aa8340 100644
--- a/coregrind/m_libcprint.c
+++ b/coregrind/m_libcprint.c
@@ -168,6 +168,25 @@
    return ret;
 }
 
+static UInt emit_WRK ( const HChar* format, va_list vargs )
+{
+   if (VG_(clo_xml)) {
+      return VG_(vprintf_xml)(format, vargs);
+   } else if (VG_(log_output_sink).fd == -2) {
+      return VG_(vprintf) (format, vargs);
+   } else {
+      return VG_(vmessage)(Vg_UserMsg, format, vargs);
+   }
+}
+UInt VG_(emit) ( const HChar* format, ... )
+{
+   UInt ret;
+   va_list vargs;
+   va_start(vargs, format);
+   ret = emit_WRK(format, vargs);
+   va_end(vargs);
+   return ret;
+}
 
 /* --------- sprintf --------- */
 
diff --git a/coregrind/m_mallocfree.c b/coregrind/m_mallocfree.c
index 411899b..2325a52 100644
--- a/coregrind/m_mallocfree.c
+++ b/coregrind/m_mallocfree.c
@@ -943,6 +943,33 @@
 }
 
 
+// Find the superblock containing the given address.
+// If superblock not found, return NULL.
+static
+Superblock* maybe_findSb ( Arena* a, Addr ad )
+{
+   SizeT min = 0;
+   SizeT max = a->sblocks_used;
+
+   while (min <= max) {
+      Superblock * sb; 
+      SizeT pos = min + (max - min)/2;
+      if (pos < 0 || pos >= a->sblocks_used)
+         return NULL;
+      sb = a->sblocks[pos];
+      if ((Addr)&sb->payload_bytes[0] <= ad
+          && ad < (Addr)&sb->payload_bytes[sb->n_payload_bytes]) {
+         return sb;
+      } else if ((Addr)&sb->payload_bytes[0] <= ad) {
+         min = pos + 1;
+      } else {
+         max = pos - 1;
+      }
+   }
+   return NULL;
+}
+
+
 /*------------------------------------------------------------*/
 /*--- Functions for working with freelists.                ---*/
 /*------------------------------------------------------------*/
@@ -1413,6 +1440,43 @@
    }
 }
 
+void VG_(describe_arena_addr) ( Addr a, AddrArenaInfo* aai )
+{
+   UInt i;
+   Superblock *sb;
+   Arena      *arena;
+
+   for (i = 0; i < VG_N_ARENAS; i++) {
+      if (i == VG_AR_CLIENT && !client_inited)
+         continue;
+      arena = arenaId_to_ArenaP(i);
+      sb = maybe_findSb( arena, a );
+      if (sb != NULL) {
+         Word   j;
+         SizeT  b_bszB;
+         Block *b = NULL;
+
+         aai->aid = i;
+         aai->name = arena->name;
+         for (j = 0; j < sb->n_payload_bytes; j += mk_plain_bszB(b_bszB)) {
+            b     = (Block*)&sb->payload_bytes[j];
+            b_bszB = get_bszB_as_is(b);
+            if (a < (Addr)b + mk_plain_bszB(b_bszB))
+               break;
+         }
+         vg_assert (b);
+         aai->block_szB = get_pszB(arena, b);
+         aai->rwoffset = a - (Addr)get_block_payload(arena, b);
+         aai->free = !is_inuse_block(b);
+         return;
+      }
+   }
+   aai->aid = 0;
+   aai->name = NULL;
+   aai->block_szB = 0;
+   aai->rwoffset = 0;
+   aai->free = False;
+}
 
 /*------------------------------------------------------------*/
 /*--- Creating and deleting blocks.                        ---*/
diff --git a/coregrind/m_tooliface.c b/coregrind/m_tooliface.c
index fc03507..d95462d 100644
--- a/coregrind/m_tooliface.c
+++ b/coregrind/m_tooliface.c
@@ -93,6 +93,7 @@
    .syscall_wrapper      = False,
    .sanity_checks        = False,
    .print_stats          = False,
+   .info_location        = False,
    .var_info	         = False,
    .malloc_replacement   = False,
    .xml_output           = False,
@@ -303,6 +304,14 @@
    VG_(tdict).tool_print_stats = print_stats;
 }
 
+void VG_(needs_info_location) (
+   void (*info_location)(Addr)
+)
+{
+   VG_(needs).info_location = True;
+   VG_(tdict).tool_info_location = info_location;
+}
+
 void VG_(needs_malloc_replacement)(
    void* (*malloc)               ( ThreadId, SizeT ),
    void* (*__builtin_new)        ( ThreadId, SizeT ),
diff --git a/coregrind/pub_core_addrinfo.h b/coregrind/pub_core_addrinfo.h
new file mode 100644
index 0000000..577c104
--- /dev/null
+++ b/coregrind/pub_core_addrinfo.h
@@ -0,0 +1,42 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Obtaining information about an address.  pub_core_addrinfo.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2014-2014  Philippe Waroquiers
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#ifndef __PUB_CORE_ADDRINFO_H
+#define __PUB_CORE_ADDRINFO_H
+
+#include "pub_tool_addrinfo.h"
+
+// No core-only exports;  everything in this module is visible to both
+// the core and tools.
+
+#endif   // __PUB_CORE_ADDRINFO_H
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                          ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/pub_core_mallocfree.h b/coregrind/pub_core_mallocfree.h
index 3267c8f..913952e 100644
--- a/coregrind/pub_core_mallocfree.h
+++ b/coregrind/pub_core_mallocfree.h
@@ -127,6 +127,23 @@
 
 extern void  VG_(print_arena_cc_analysis) ( void );
 
+typedef 
+   struct _AddrArenaInfo
+   AddrArenaInfo;
+
+struct _AddrArenaInfo {
+   ArenaId aid;
+   const HChar* name; // arena name, !NULL if Addr a points in an arena.
+   SizeT       block_szB;
+   PtrdiffT    rwoffset;
+   Bool        free;  // True if this is in the arena free zone.
+};
+/* If Addr a points in one of the allocation arenas, describes Addr a in *aai
+   otherwise sets *aai to 0/NULL/...
+   Note that no information is produced for addresses allocated with
+   VG_(arena_perm_malloc). */
+extern void VG_(describe_arena_addr) ( Addr a, /*OUT*/AddrArenaInfo* aai );
+
 #endif   // __PUB_CORE_MALLOCFREE_H
 
 /*--------------------------------------------------------------------*/
diff --git a/coregrind/pub_core_tooliface.h b/coregrind/pub_core_tooliface.h
index fd84ef1..5c12903 100644
--- a/coregrind/pub_core_tooliface.h
+++ b/coregrind/pub_core_tooliface.h
@@ -89,6 +89,7 @@
       Bool syscall_wrapper;
       Bool sanity_checks;
       Bool print_stats;
+      Bool info_location;
       Bool var_info;
       Bool malloc_replacement;
       Bool xml_output;
@@ -153,6 +154,9 @@
    // VG_(needs).print_stats
    void (*tool_print_stats)(void);
 
+   // VG_(needs).info_location
+   void (*tool_info_location)(Addr a);
+
    // VG_(needs).malloc_replacement
    void* (*tool_malloc)              (ThreadId, SizeT);
    void* (*tool___builtin_new)       (ThreadId, SizeT);