Bug 1429685 - Build a Valgrind package for Debian wheezy. r?dustin draft
authorMike Hommey <mh+mozilla@glandium.org>
Thu, 11 Jan 2018 16:06:45 +0900
changeset 718971 774e5fe2f2fea6870141cbf8437d86fd45f6f3a8
parent 718942 c5346b9635b589418f55f8fa977e08aa0034619a
child 745663 92a6de76d92898b91ca88e075bd194d2349e6fc6
push id95116
push userbmo:mh+mozilla@glandium.org
push dateThu, 11 Jan 2018 08:14:23 +0000
reviewersdustin
bugs1429685, 1382280
milestone59.0a1
Bug 1429685 - Build a Valgrind package for Debian wheezy. r?dustin Apply the patch from bug 1382280 (build/valgrind/valgrind-epochs.patch).
build/debian-packages/valgrind-wheezy.diff
taskcluster/ci/packages/kind.yml
new file mode 100644
--- /dev/null
+++ b/build/debian-packages/valgrind-wheezy.diff
@@ -0,0 +1,4821 @@
+diff -Nru valgrind-3.13.0/debian/changelog valgrind-3.13.0/debian/changelog
+--- valgrind-3.13.0/debian/changelog	2017-07-24 08:41:05.000000000 +0900
++++ valgrind-3.13.0/debian/changelog	2018-01-11 17:08:25.000000000 +0900
+@@ -1,3 +1,13 @@
++valgrind (1:3.13.0-1.deb7moz1) wheezy; urgency=medium
++
++  * Mozilla backport for wheezy.
++  * debian/patches/valgrind-epochs.patch: Apply patch from Julian Seward that
++    allows us to write a suppression for a leak in a library that gets
++    unloaded before shutdown.
++    Ref: https://bugs.kde.org/show_bug.cgi?id=79362
++
++ -- Mike Hommey <glandium@debian.org>  Thu, 11 Jan 2018 17:08:25 +0900
++
+ valgrind (1:3.13.0-1) unstable; urgency=medium
+ 
+   * New upstream release
+diff -Nru valgrind-3.13.0/debian/patches/series valgrind-3.13.0/debian/patches/series
+--- valgrind-3.13.0/debian/patches/series	2017-07-24 08:41:05.000000000 +0900
++++ valgrind-3.13.0/debian/patches/series	2018-01-11 17:08:25.000000000 +0900
+@@ -4,3 +4,4 @@
+ 07_fix-spelling-in-binary.patch
+ 08_fix-spelling-in-manpage.patch
+ 09_fix-armhf-detect.patch
++valgrind-epochs.patch
+diff -Nru valgrind-3.13.0/debian/patches/valgrind-epochs.patch valgrind-3.13.0/debian/patches/valgrind-epochs.patch
+--- valgrind-3.13.0/debian/patches/valgrind-epochs.patch	1970-01-01 09:00:00.000000000 +0900
++++ valgrind-3.13.0/debian/patches/valgrind-epochs.patch	2018-01-11 17:08:25.000000000 +0900
+@@ -0,0 +1,4792 @@
++Index: valgrind-3.13.0/Makefile.am
++===================================================================
++--- valgrind-3.13.0.orig/Makefile.am
+++++ valgrind-3.13.0/Makefile.am
++@@ -16,6 +16,10 @@ EXP_TOOLS = 	exp-sgcheck \
++ 		exp-bbv \
++ 		exp-dhat
++ 
+++#TOOLS = none memcheck
+++#EXP_TOOLS =
+++
+++
++ # Put docs last because building the HTML is slow and we want to get
++ # everything else working before we try it.
++ SUBDIRS = \
++Index: valgrind-3.13.0/cachegrind/cg_main.c
++===================================================================
++--- valgrind-3.13.0.orig/cachegrind/cg_main.c
+++++ valgrind-3.13.0/cachegrind/cg_main.c
++@@ -210,12 +210,14 @@ static HChar* get_perm_string(const HCha
++ static void get_debug_info(Addr instr_addr, const HChar **dir,
++                            const HChar **file, const HChar **fn, UInt* line)
++ {
+++   DiEpoch ep = VG_(current_DiEpoch)();
++    Bool found_file_line = VG_(get_filename_linenum)(
+++                             ep,
++                              instr_addr, 
++                              file, dir,
++                              line
++                           );
++-   Bool found_fn        = VG_(get_fnname)(instr_addr, fn);
+++   Bool found_fn        = VG_(get_fnname)(ep, instr_addr, fn);
++ 
++    if (!found_file_line) {
++       *file = "???";
++Index: valgrind-3.13.0/callgrind/bb.c
++===================================================================
++--- valgrind-3.13.0.orig/callgrind/bb.c
+++++ valgrind-3.13.0/callgrind/bb.c
++@@ -199,7 +199,8 @@ obj_node* obj_of_address(Addr addr)
++   DebugInfo* di;
++   PtrdiffT offset;
++ 
++-  di = VG_(find_DebugInfo)(addr);
+++  DiEpoch ep = VG_(current_DiEpoch)();
+++  di = VG_(find_DebugInfo)(ep, addr);
++   obj = CLG_(get_obj_node)( di );
++ 
++   /* Update symbol offset in object if remapped */
++Index: valgrind-3.13.0/callgrind/dump.c
++===================================================================
++--- valgrind-3.13.0.orig/callgrind/dump.c
+++++ valgrind-3.13.0/callgrind/dump.c
++@@ -373,7 +373,8 @@ Bool get_debug_pos(BBCC* bbcc, Addr addr
++ 	found_file_line = debug_cache_info[cachepos];
++     }
++     else {
++-	found_file_line = VG_(get_filename_linenum)(addr,
+++        DiEpoch ep = VG_(current_DiEpoch)();
+++	found_file_line = VG_(get_filename_linenum)(ep, addr,
++ 						    &file,
++ 						    &dir,
++ 						    &(p->line));
++Index: valgrind-3.13.0/callgrind/fn.c
++===================================================================
++--- valgrind-3.13.0.orig/callgrind/fn.c
+++++ valgrind-3.13.0/callgrind/fn.c
++@@ -434,17 +434,18 @@ Bool CLG_(get_debug_info)(Addr instr_add
++   
++   CLG_DEBUG(6, "  + get_debug_info(%#lx)\n", instr_addr);
++ 
+++  DiEpoch ep = VG_(current_DiEpoch)();
++   if (pDebugInfo) {
++-      *pDebugInfo = VG_(find_DebugInfo)(instr_addr);
+++      *pDebugInfo = VG_(find_DebugInfo)(ep, instr_addr);
++ 
++       // for generated code in anonymous space, pSegInfo is 0
++    }
++ 
++-   found_file_line = VG_(get_filename_linenum)(instr_addr,
+++   found_file_line = VG_(get_filename_linenum)(ep, instr_addr,
++ 					       file,
++ 					       dir,
++ 					       &line);
++-   found_fn = VG_(get_fnname)(instr_addr, fn_name);
+++   found_fn = VG_(get_fnname)(ep, instr_addr, fn_name);
++ 
++    if (!found_file_line && !found_fn) {
++      CLG_(stat).no_debug_BBs++;
++@@ -503,6 +504,7 @@ fn_node* CLG_(get_fn_node)(BB* bb)
++     CLG_(get_debug_info)(bb_addr(bb),
++                          &dirname, &filename, &fnname, &line_num, &di);
++ 
+++    DiEpoch ep = VG_(current_DiEpoch)();
++     if (0 == VG_(strcmp)(fnname, "???")) {
++ 	int p;
++         static HChar buf[32];  // for sure large enough
++@@ -521,7 +523,7 @@ fn_node* CLG_(get_fn_node)(BB* bb)
++         fnname = buf;
++     }
++     else {
++-      if (VG_(get_fnname_if_entry)(bb_addr(bb), &fnname))
+++      if (VG_(get_fnname_if_entry)(ep, bb_addr(bb), &fnname))
++ 	bb->is_entry = 1;
++     }
++ 
++Index: valgrind-3.13.0/coregrind/m_addrinfo.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_addrinfo.c
+++++ valgrind-3.13.0/coregrind/m_addrinfo.c
++@@ -86,7 +86,7 @@ static ThreadId find_tid_with_stack_cont
++    return VG_INVALID_THREADID;
++ }
++ 
++-void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai )
+++void VG_(describe_addr) ( DiEpoch ep, Addr a, /*OUT*/AddrInfo* ai )
++ {
++    VgSectKind sect;
++ 
++@@ -99,7 +99,7 @@ void VG_(describe_addr) ( Addr a, /*OUT*
++                     VG_(free), sizeof(HChar) );
++ 
++    (void) VG_(get_data_description)( ai->Addr.Variable.descr1,
++-                                     ai->Addr.Variable.descr2, a );
+++                                     ai->Addr.Variable.descr2, ep, a );
++    /* If there's nothing in descr1/2, free them.  Why is it safe to
++       VG_(indexXA) at zero here?  Because VG_(get_data_description)
++       guarantees to zero terminate descr1/2 regardless of the outcome
++@@ -127,7 +127,7 @@ void VG_(describe_addr) ( Addr a, /*OUT*
++       there. -- */
++    const HChar *name;
++    if (VG_(get_datasym_and_offset)(
++-             a, &name,
+++             ep, a, &name,
++              &ai->Addr.DataSym.offset )) {
++       ai->Addr.DataSym.name = VG_(strdup)("mc.da.dsname", name);
++       ai->tag = Addr_DataSym;
++@@ -148,6 +148,7 @@ void VG_(describe_addr) ( Addr a, /*OUT*
++             ai->tag            = Addr_Stack;
++             VG_(initThreadInfo)(&ai->Addr.Stack.tinfo);
++             ai->Addr.Stack.tinfo.tid = tid;
+++            ai->Addr.Stack.epoch = ep;
++             ai->Addr.Stack.IP = 0;
++             ai->Addr.Stack.frameNo = -1;
++             ai->Addr.Stack.stackPos = StackPos_stacked;
++@@ -196,9 +197,9 @@ void VG_(describe_addr) ( Addr a, /*OUT*
++          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.allocated_at = VG_(null_ExeContextAndEpoch)();
++          VG_(initThreadInfo) (&ai->Addr.Block.alloc_tinfo);
++-         ai->Addr.Block.freed_at = VG_(null_ExeContext)();
+++         ai->Addr.Block.freed_at = VG_(null_ExeContextAndEpoch)();
++          return;
++       }
++    }
++@@ -248,6 +249,7 @@ void VG_(describe_addr) ( Addr a, /*OUT*
++          ai->tag  = Addr_Stack;
++          VG_(initThreadInfo)(&ai->Addr.Stack.tinfo);
++          ai->Addr.Stack.tinfo.tid = tid;
+++         ai->Addr.Stack.epoch = ep;
++          ai->Addr.Stack.IP = 0;
++          ai->Addr.Stack.frameNo = -1;
++          vg_assert (stackPos != StackPos_stacked);
++@@ -447,20 +449,24 @@ static void pp_addrinfo_WRK ( Addr a, co
++             Bool haslinenum;
++             PtrdiffT offset;
++ 
++-            if (VG_(get_inst_offset_in_function)( ai->Addr.Stack.IP,
+++            if (VG_(get_inst_offset_in_function)( ai->Addr.Stack.epoch,
+++                                                  ai->Addr.Stack.IP,
++                                                   &offset))
++-               haslinenum = VG_(get_linenum) (ai->Addr.Stack.IP - offset,
+++               haslinenum = VG_(get_linenum) (ai->Addr.Stack.epoch,
+++                                              ai->Addr.Stack.IP - offset,
++                                               &linenum);
++             else
++                haslinenum = False;
++ 
++-            hasfile = VG_(get_filename)(ai->Addr.Stack.IP, &file);
+++            hasfile = VG_(get_filename)(ai->Addr.Stack.epoch,
+++                                        ai->Addr.Stack.IP, &file);
++ 
++             HChar strlinenum[16] = "";   // large enough
++             if (hasfile && haslinenum)
++                VG_(sprintf)(strlinenum, "%u", linenum);
++ 
++-            hasfn = VG_(get_fnname)(ai->Addr.Stack.IP, &fn);
+++            hasfn = VG_(get_fnname)(ai->Addr.Stack.epoch,
+++                                    ai->Addr.Stack.IP, &fn);
++ 
++             if (hasfn || hasfile)
++                VG_(emit)( "%sin frame #%d, created by %ps (%ps:%s)%s\n",
++@@ -533,32 +539,35 @@ static void pp_addrinfo_WRK ( Addr a, co
++                xpost
++             );
++          if (ai->Addr.Block.block_kind==Block_Mallocd) {
++-            VG_(pp_ExeContext)(ai->Addr.Block.allocated_at);
++-            vg_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)());
+++            VG_(pp_ExeContextAndEpoch)(ai->Addr.Block.allocated_at);
+++            vg_assert(
+++               VG_(is_null_ExeContextAndEpoch)(ai->Addr.Block.freed_at));
++          }
++          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_(pp_ExeContextAndEpoch)(ai->Addr.Block.freed_at);
+++            if (!VG_(is_null_ExeContextAndEpoch)(ai->Addr.Block.allocated_at)) {
++                VG_(emit)(
++                   "%sBlock was alloc'd at%s\n",
++                   xpre,
++                   xpost
++                );
++-               VG_(pp_ExeContext)(ai->Addr.Block.allocated_at);
+++               VG_(pp_ExeContextAndEpoch)(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);
++-            vg_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)());
+++            VG_(pp_ExeContextAndEpoch)(ai->Addr.Block.allocated_at);
+++            vg_assert(VG_(is_null_ExeContextAndEpoch)(ai->Addr.Block.freed_at));
++             /* 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.
++-            vg_assert (ai->Addr.Block.allocated_at == VG_(null_ExeContext)());
++-            vg_assert (ai->Addr.Block.freed_at == VG_(null_ExeContext)());
+++            vg_assert(VG_(is_null_ExeContextAndEpoch)
+++                         (ai->Addr.Block.allocated_at));
+++            vg_assert(VG_(is_null_ExeContextAndEpoch)
+++                         (ai->Addr.Block.freed_at));
++          }
++          if (ai->Addr.Block.alloc_tinfo.tnr || ai->Addr.Block.alloc_tinfo.tid)
++             VG_(emit)(
++@@ -603,7 +612,7 @@ static void pp_addrinfo_WRK ( Addr a, co
++          if (ai->Addr.SectKind.kind == Vg_SectText) {
++             /* To better describe the address in a text segment,
++                pp a dummy stacktrace made of this single address. */
++-            VG_(pp_StackTrace)( &a, 1 );
+++            VG_(pp_StackTrace)( VG_(current_DiEpoch)(), &a, 1 );
++          }
++          break;
++ 
++Index: valgrind-3.13.0/coregrind/m_debuginfo/debuginfo.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_debuginfo/debuginfo.c
+++++ valgrind-3.13.0/coregrind/m_debuginfo/debuginfo.c
++@@ -70,6 +70,10 @@
++    should-we-load-debuginfo-now? finite state machine. */
++ #define DEBUG_FSM 0
++ 
+++/* Set this to 1 to enable somewhat minimal debug printing for the
+++   debuginfo-epoch machinery. */
+++#define DEBUG_EPOCHS 0
+++
++ 
++ /*------------------------------------------------------------*/
++ /*--- The _svma / _avma / _image / _bias naming scheme     ---*/
++@@ -109,6 +113,116 @@ static void caches__invalidate (void);
++ 
++ 
++ /*------------------------------------------------------------*/
+++/*--- Epochs                                               ---*/
+++/*------------------------------------------------------------*/
+++
+++/* The DebugInfo epoch is incremented every time we either load debuginfo in
+++   response to an object mapping, or an existing DebugInfo becomes
+++   non-current (or will be discarded) due to an object unmap.  By storing,
+++   in each DebugInfo, the first and last epoch for which it is valid, we can
+++   unambiguously identify the set of DebugInfos which should be used to
+++   provide metadata for a code or data address, provided we know the epoch
+++   to which that address pertains.
+++
+++   Note, this isn't the same as the "handle_counter" below.  That only
+++   advances when new DebugInfos are created.  "current_epoch" advances both
+++   at DebugInfo created and destruction-or-making-non-current.
+++*/
+++
+++// The value zero is reserved for indicating an invalid epoch number.
+++static UInt current_epoch = 1;
+++
+++inline DiEpoch VG_(current_DiEpoch) ( void ) {
+++   DiEpoch dep; dep.n = current_epoch; return dep;
+++}
+++
+++static void advance_current_DiEpoch ( const HChar* msg ) {
+++   current_epoch++;
+++   if (DEBUG_EPOCHS)
+++      VG_(printf)("Advancing current epoch to %u due to %s\n",
+++                  current_epoch, msg);
+++}
+++
+++static inline Bool eq_DiEpoch ( DiEpoch dep1, DiEpoch dep2 ) {
+++   return dep1.n == dep2.n && /*neither is invalid*/dep1.n != 0;
+++}
+++
+++// Is this DebugInfo currently "allocated" (pre-use state, only FSM active) ?
+++static inline Bool is_DebugInfo_allocated ( const DebugInfo* di )
+++{
+++   if (is_DiEpoch_INVALID(di->first_epoch)
+++       && is_DiEpoch_INVALID(di->last_epoch)) {
+++      return True;
+++   } else {
+++      return False;
+++   }
+++}
+++
+++// Is this DebugInfo currently "active" (valid for the current epoch) ?
+++static inline Bool is_DebugInfo_active ( const DebugInfo* di )
+++{
+++   if (!is_DiEpoch_INVALID(di->first_epoch)
+++       && is_DiEpoch_INVALID(di->last_epoch)) {
+++      // Yes it is active.  Sanity check ..
+++      tl_assert(di->first_epoch.n <= current_epoch);
+++      return True;
+++   } else {
+++      return False;
+++   }
+++}
+++
+++// Is this DebugInfo currently "archived" ?
+++static inline Bool is_DebugInfo_archived ( const DebugInfo* di )
+++{
+++   if (!is_DiEpoch_INVALID(di->first_epoch)
+++       && !is_DiEpoch_INVALID(di->last_epoch)) {
+++      // Yes it is archived.  Sanity checks ..
+++      tl_assert(di->first_epoch.n <= di->last_epoch.n);
+++      tl_assert(di->last_epoch.n <= current_epoch);
+++      return True;
+++   } else {
+++      return False;
+++   }
+++}
+++
+++// Is this DebugInfo valid for the specified epoch?
+++static inline Bool is_DI_valid_for_epoch ( const DebugInfo* di, DiEpoch ep )
+++{
+++   // Stay sane
+++   vg_assert(ep.n > 0 && ep.n <= current_epoch);
+++
+++   Bool first_valid = !is_DiEpoch_INVALID(di->first_epoch);
+++   Bool last_valid  = !is_DiEpoch_INVALID(di->last_epoch);
+++
+++   if (first_valid) {
+++      if (last_valid) {
+++         // Both valid.  di is in Archived state.
+++         return di->first_epoch.n <= ep.n && ep.n <= di->last_epoch.n;
+++      } else {
+++         // First is valid, last is invalid.  di is in Active state.
+++         return di->first_epoch.n <= ep.n;
+++      }
+++   } else {
+++      if (last_valid) {
+++         // First is invalid, last is valid.  This is an impossible state.
+++         vg_assert(0);
+++         /*NOTREACHED*/
+++         return False;
+++      } else {
+++         // Neither is valid.  di is in Allocated state.
+++         return False;
+++      }
+++   }
+++
+++}
+++
+++static inline UInt ROL32 ( UInt x, UInt n )
+++{
+++   return (x << n) | (x >> (32-n));
+++}
+++
+++
+++/*------------------------------------------------------------*/
++ /*--- Root structure                                       ---*/
++ /*------------------------------------------------------------*/
++ 
++@@ -162,6 +276,23 @@ static void move_DebugInfo_one_step_forw
++ }
++ 
++ 
+++// Debugging helper for epochs
+++static void show_epochs ( const HChar* msg )
+++{
+++   if (DEBUG_EPOCHS) {
+++      DebugInfo* di;
+++      VG_(printf)("\nDebugInfo epoch display, requested by \"%s\"\n", msg);
+++      VG_(printf)("  Current epoch (note: 0 means \"invalid epoch\") = %u\n",
+++                  current_epoch);
+++      for (di = debugInfo_list; di; di = di->next) {
+++         VG_(printf)("  [di=%p]  first %u  last %u  %s\n", 
+++                     di, di->first_epoch.n, di->last_epoch.n, di->fsm.filename);
+++      }
+++      VG_(printf)("\n");
+++   }
+++}
+++
+++
++ /*------------------------------------------------------------*/
++ /*--- Notification (acquire/discard) helpers               ---*/
++ /*------------------------------------------------------------*/
++@@ -182,6 +313,8 @@ DebugInfo* alloc_DebugInfo( const HChar*
++ 
++    di = ML_(dinfo_zalloc)("di.debuginfo.aDI.1", sizeof(DebugInfo));
++    di->handle       = handle_counter++;
+++   di->first_epoch  = DiEpoch_INVALID();
+++   di->last_epoch   = DiEpoch_INVALID();
++    di->fsm.filename = ML_(dinfo_strdup)("di.debuginfo.aDI.2", filename);
++    di->fsm.maps     = VG_(newXA)(
++                          ML_(dinfo_zalloc), "di.debuginfo.aDI.3",
++@@ -302,34 +435,52 @@ static void free_DebugInfo ( DebugInfo*
++ }
++ 
++ 
++-/* 'si' is a member of debugInfo_list.  Find it, remove it from the
++-   list, notify m_redir that this has happened, and free all storage
++-   reachable from it.
+++/* 'di' is a member of debugInfo_list.  Find it, and either (remove it from
+++   the list and free all storage reachable from it) or archive it, notify
+++   m_redir that this has happened, and free all storage reachable from it.
+++
+++   Note that 'di' can't be archived.  Is a DebugInfo is archived then we
+++   want to hold on to it forever.  This is asserted for.
+++
+++   Note also, we don't advance the current epoch here.  That's the
+++   responsibility of some (non-immediate) caller.
++ */
++-static void discard_DebugInfo ( DebugInfo* di )
+++static void discard_or_archive_DebugInfo ( DebugInfo* di )
++ {
++-   const HChar* reason = "munmap";
+++   const HChar* reason  = "munmap";
+++   const Bool   archive = VG_(clo_keep_debuginfo);
++ 
++    DebugInfo** prev_next_ptr = &debugInfo_list;
++    DebugInfo*  curr          =  debugInfo_list;
++ 
++    while (curr) {
++       if (curr == di) {
++-         /* Found it;  remove from list and free it. */
+++         /* It must be active! */
+++         vg_assert( is_DebugInfo_active(di));
+++         /* Found it; (remove from list and free it), or archive it. */
++          if (curr->have_dinfo
++              && (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir)))
++             VG_(message)(Vg_DebugMsg, 
++-                         "Discarding syms at %#lx-%#lx in %s due to %s()\n",
+++                         "%s syms at %#lx-%#lx in %s due to %s()\n",
+++                         archive ? "Archiving" : "Discarding",
++                          di->text_avma, 
++                          di->text_avma + di->text_size,
++                          curr->fsm.filename ? curr->fsm.filename
++                                             : "???",
++                          reason);
++          vg_assert(*prev_next_ptr == curr);
++-         *prev_next_ptr = curr->next;
++-         if (curr->have_dinfo)
+++         if (!archive) {
+++            *prev_next_ptr = curr->next;
+++         }
+++         if (curr->have_dinfo) {
++             VG_(redir_notify_delete_DebugInfo)( curr );
++-         free_DebugInfo(curr);
+++         }
+++         if (archive) {
+++            /* Adjust the epoch markers appropriately. */
+++            di->last_epoch = VG_(current_DiEpoch)();
+++         } else {
+++            free_DebugInfo(curr);
+++         }
++          return;
++       }
++       prev_next_ptr = &curr->next;
++@@ -358,10 +509,11 @@ static Bool discard_syms_in_range ( Addr
++       while (True) {
++          if (curr == NULL)
++             break;
++-         if (curr->text_present
++-             && curr->text_size > 0
++-             && (start+length - 1 < curr->text_avma 
++-                 || curr->text_avma + curr->text_size - 1 < start)) {
+++         if (is_DebugInfo_archived(curr)
+++             || (curr->text_present
+++                 && curr->text_size > 0
+++                 && (start+length - 1 < curr->text_avma 
+++                     || curr->text_avma + curr->text_size - 1 < start))) {
++             /* no overlap */
++ 	 } else {
++ 	    found = True;
++@@ -372,7 +524,7 @@ static Bool discard_syms_in_range ( Addr
++ 
++       if (!found) break;
++       anyFound = True;
++-      discard_DebugInfo( curr );
+++      discard_or_archive_DebugInfo( curr );
++    }
++ 
++    return anyFound;
++@@ -418,9 +570,9 @@ static Bool do_DebugInfos_overlap ( cons
++ }
++ 
++ 
++-/* Discard all elements of debugInfo_list whose .mark bit is set.
+++/* Discard or archive all elements of debugInfo_list whose .mark bit is set.
++ */
++-static void discard_marked_DebugInfos ( void )
+++static void discard_or_archive_marked_DebugInfos ( void )
++ {
++    DebugInfo* curr;
++ 
++@@ -436,7 +588,7 @@ static void discard_marked_DebugInfos (
++       }
++ 
++       if (!curr) break;
++-      discard_DebugInfo( curr );
+++      discard_or_archive_DebugInfo( curr );
++ 
++    }
++ }
++@@ -446,19 +598,22 @@ static void discard_marked_DebugInfos (
++    Clearly diRef must have its mapping information set to something sane. */
++ static void discard_DebugInfos_which_overlap_with ( DebugInfo* diRef )
++ {
+++   vg_assert(is_DebugInfo_allocated(diRef));
++    DebugInfo* di;
++    /* Mark all the DebugInfos in debugInfo_list that need to be
++       deleted.  First, clear all the mark bits; then set them if they
++       overlap with siRef.  Since siRef itself is in this list we at
++       least expect its own mark bit to be set. */
++    for (di = debugInfo_list; di; di = di->next) {
+++      if (is_DebugInfo_archived(di))
+++         continue;
++       di->mark = do_DebugInfos_overlap( di, diRef );
++       if (di == diRef) {
++          vg_assert(di->mark);
++          di->mark = False;
++       }
++    }
++-   discard_marked_DebugInfos();
+++   discard_or_archive_marked_DebugInfos();
++ }
++ 
++ 
++@@ -470,6 +625,8 @@ static DebugInfo* find_or_create_DebugIn
++    DebugInfo* di;
++    vg_assert(filename);
++    for (di = debugInfo_list; di; di = di->next) {
+++      if (is_DebugInfo_archived(di))
+++         continue;
++       vg_assert(di->fsm.filename);
++       if (0==VG_(strcmp)(di->fsm.filename, filename))
++          break;
++@@ -480,6 +637,7 @@ static DebugInfo* find_or_create_DebugIn
++       di->next = debugInfo_list;
++       debugInfo_list = di;
++    }
+++   vg_assert(!is_DebugInfo_archived(di));
++    return di;
++ }
++ 
++@@ -723,6 +881,8 @@ static ULong di_notify_ACHIEVE_ACCEPT_ST
++    ULong di_handle;
++    Bool  ok;
++ 
+++   advance_current_DiEpoch("di_notify_ACHIEVE_ACCEPT_STATE");
+++
++    vg_assert(di->fsm.filename);
++    TRACE_SYMTAB("\n");
++    TRACE_SYMTAB("------ start ELF OBJECT "
++@@ -734,7 +894,8 @@ static ULong di_notify_ACHIEVE_ACCEPT_ST
++    /* We're going to read symbols and debug info for the avma
++       ranges specified in the _DebugInfoFsm mapping array. First
++       get rid of any other DebugInfos which overlap any of those
++-      ranges (to avoid total confusion). */
+++      ranges (to avoid total confusion).  But only those valid in
+++     the current epoch.  We don't want to discard archived DebugInfos. */
++    discard_DebugInfos_which_overlap_with( di );
++ 
++    /* The DebugInfoMappings that now exist in the FSM may involve
++@@ -765,6 +926,15 @@ static ULong di_notify_ACHIEVE_ACCEPT_ST
++          priv_storage.h. */
++       check_CFSI_related_invariants(di);
++       ML_(finish_CFSI_arrays)(di);
+++
+++      // Mark di's first epoch point as a valid epoch.  Because its
+++      // last_epoch value is still invalid, this changes di's state from
+++      // "allocated" to "active".
+++      vg_assert(is_DebugInfo_allocated(di));
+++      di->first_epoch = VG_(current_DiEpoch)();
+++      vg_assert(is_DebugInfo_active(di));
+++      show_epochs("di_notify_ACHIEVE_ACCEPT_STATE success");
+++
++       /* notify m_redir about it */
++       TRACE_SYMTAB("\n------ Notifying m_redir ------\n");
++       VG_(redir_notify_new_DebugInfo)( di );
++@@ -1077,8 +1247,11 @@ void VG_(di_notify_munmap)( Addr a, Size
++    Bool anyFound;
++    if (0) VG_(printf)("DISCARD %#lx %#lx\n", a, a+len);
++    anyFound = discard_syms_in_range(a, len);
++-   if (anyFound)
+++   if (anyFound) {
++       caches__invalidate();
+++      advance_current_DiEpoch("VG_(di_notify_munmap)");
+++      show_epochs("VG_(di_notify_munmap)");
+++   }
++ }
++ 
++ 
++@@ -1094,8 +1267,10 @@ void VG_(di_notify_mprotect)( Addr a, Si
++ #  endif
++    if (0 && !exe_ok) {
++       Bool anyFound = discard_syms_in_range(a, len);
++-      if (anyFound)
+++      if (anyFound) {
++          caches__invalidate();
+++         advance_current_DiEpoch("VG_(di_notify_mprotect)");
+++      }
++    }
++ }
++ 
++@@ -1395,6 +1570,7 @@ void VG_(di_notify_pdb_debuginfo)( Int f
++    caches__invalidate();
++    /* dump old info for this range, if any */
++    discard_syms_in_range( avma_obj, total_size );
+++   advance_current_DiEpoch("VG_(di_notify_pdb_debuginfo)");
++ 
++    { DebugInfo* di = find_or_create_DebugInfo_for(exename);
++ 
++@@ -1471,6 +1647,7 @@ DebugInfoMapping* ML_(find_rx_mapping) (
++ /*------------------------------------------------------------*/
++ /*--- Types and functions for inlined IP cursor            ---*/
++ /*------------------------------------------------------------*/
+++
++ struct _InlIPCursor {
++    Addr eip;             // Cursor used to describe calls at eip.
++    DebugInfo* di;        // DebugInfo describing inlined calls at eip
++@@ -1534,8 +1711,8 @@ Bool VG_(next_IIPC)(InlIPCursor *iipc)
++ }
++ 
++ /* Forward */
++-static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi,
++-                                           /*OUT*/Word* locno );
+++static void search_all_loctabs ( DiEpoch ep, Addr ptr,
+++                                 /*OUT*/DebugInfo** pdi, /*OUT*/Word* locno );
++ 
++ /* Returns the position after which eip would be inserted in inltab.
++    (-1 if eip should be inserted before position 0).
++@@ -1565,7 +1742,7 @@ static Word inltab_insert_pos (DebugInfo
++    return lo - 1;
++ }
++ 
++-InlIPCursor* VG_(new_IIPC)(Addr eip)
+++InlIPCursor* VG_(new_IIPC)(DiEpoch ep, Addr eip)
++ {
++    DebugInfo*  di;
++    Word        locno;
++@@ -1576,8 +1753,8 @@ InlIPCursor* VG_(new_IIPC)(Addr eip)
++    if (!VG_(clo_read_inline_info))
++       return NULL; // No way we can find inlined calls.
++ 
++-   /* Search the DebugInfo for eip */
++-   search_all_loctabs ( eip, &di, &locno );
+++   /* Search the DebugInfo for (ep, eip) */
+++   search_all_loctabs ( ep, eip, &di, &locno );
++    if (di == NULL || di->inltab_used == 0)
++       return NULL; // No di (with inltab) containing eip.
++ 
++@@ -1641,8 +1818,8 @@ void VG_(delete_IIPC)(InlIPCursor *iipc)
++    If findText==True,  only text symbols are searched for.
++    If findText==False, only data symbols are searched for.
++ */
++-static void search_all_symtabs ( Addr ptr, /*OUT*/DebugInfo** pdi,
++-                                           /*OUT*/Word* symno,
+++static void search_all_symtabs ( DiEpoch ep, Addr ptr,
+++                                 /*OUT*/DebugInfo** pdi, /*OUT*/Word* symno,
++                                  Bool findText )
++ {
++    Word       sno;
++@@ -1651,6 +1828,9 @@ static void search_all_symtabs ( Addr pt
++ 
++    for (di = debugInfo_list; di != NULL; di = di->next) {
++ 
+++      if (!is_DI_valid_for_epoch(di, ep))
+++         continue;
+++
++       if (findText) {
++          /* Consider any symbol in the r-x mapped area to be text.
++             See Comment_Regarding_Text_Range_Checks in storage.c for
++@@ -1698,15 +1878,17 @@ static void search_all_symtabs ( Addr pt
++ }
++ 
++ 
++-/* Search all loctabs that we know about to locate ptr.  If found, set
++-   *pdi to the relevant DebugInfo, and *locno to the loctab entry
+++/* Search all loctabs that we know about to locate ptr at epoch ep.  If
+++   *found, set pdi to the relevant DebugInfo, and *locno to the loctab entry
++    *number within that.  If not found, *pdi is set to NULL. */
++-static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi,
++-                                           /*OUT*/Word* locno )
+++static void search_all_loctabs ( DiEpoch ep, Addr ptr,
+++                                 /*OUT*/DebugInfo** pdi, /*OUT*/Word* locno )
++ {
++    Word       lno;
++    DebugInfo* di;
++    for (di = debugInfo_list; di != NULL; di = di->next) {
+++      if (!is_DI_valid_for_epoch(di, ep))
+++         continue;
++       if (di->text_present
++           && di->text_size > 0
++           && di->text_avma <= ptr 
++@@ -1729,19 +1911,22 @@ static void search_all_loctabs ( Addr pt
++ 
++ typedef
++    struct {
++-      Addr sym_avma;
+++      // (sym_epoch, sym_avma) are the hash table key.
+++      DiEpoch sym_epoch;
+++      Addr    sym_avma;
+++      // Fields below here are not part of the key.
++       const HChar* sym_name;
++       PtrdiffT offset : (sizeof(PtrdiffT)*8)-1; 
++       Bool isText : 1;
++    }
++    Sym_Name_CacheEnt;
++-/* Sym_Name_CacheEnt associates a queried address to the sym name found.
++-   By nature, if a sym name was found, it means the searched address
++-   stored in the cache is an avma (see e.g. search_all_symtabs).
++-   Note however that the caller is responsibe to work with 'avma'
++-   addresses e.g. when calling VG_(get_fnname) : m_debuginfo.c has
++-   no way to differentiate an 'svma a' from an 'avma a'. It is however
++-   unlikely that svma would percolate outside of this module. */
+++/* Sym_Name_CacheEnt associates a queried (epoch, address) pair to the sym
+++   name found.  By nature, if a sym name was found, it means the searched
+++   address stored in the cache is an avma (see e.g. search_all_symtabs).
+++   Note however that the caller is responsibe to work with 'avma' addresses
+++   e.g. when calling VG_(get_fnname) : m_debuginfo.c has no way to
+++   differentiate an 'svma a' from an 'avma a'. It is however unlikely that
+++   svma would percolate outside of this module. */
++ 
++ static Sym_Name_CacheEnt sym_name_cache[N_SYM_NAME_CACHE];
++ 
++@@ -1757,13 +1942,15 @@ static void sym_name_cache__invalidate (
++    sym_name_cache[0].sym_name = no_sym_name;
++ }
++ 
++-/* The whole point of this whole big deal: map a code address to a
++-   plausible symbol name.  Returns False if no idea; otherwise True.
+++/* The whole point of this whole big deal: map an (epoch, code address) pair
+++   to a plausible symbol name.  Returns False if no idea; otherwise True.
+++
++    Caller supplies buf.  If do_cxx_demangling is False, don't do
++    C++ demangling, regardless of VG_(clo_demangle) -- probably because the
++    call has come from VG_(get_fnname_raw)().  findText
++    indicates whether we're looking for a text symbol or a data symbol
++    -- caller must choose one kind or the other.
+++
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership
++    in pub_tool_debuginfo.h 
++    get_sym_name and the fact it calls the demangler is the main reason
++@@ -1772,22 +1959,32 @@ static void sym_name_cache__invalidate (
++    (1) the DebugInfo it belongs to is not discarded
++    (2) the demangler is not invoked again
++    Also, the returned string is owned by "somebody else". Callers must
++-   not free it or modify it.*/
+++   not free it or modify it. */
++ static
++ Bool get_sym_name ( Bool do_cxx_demangling, Bool do_z_demangling,
++                     Bool do_below_main_renaming,
++-                    Addr a, const HChar** buf,
+++                    DiEpoch ep, Addr a, const HChar** buf,
++                     Bool match_anywhere_in_sym, Bool show_offset,
++                     Bool findText, /*OUT*/PtrdiffT* offsetP )
++ {
++-   UWord         hash = a % N_SYM_NAME_CACHE;
++-   Sym_Name_CacheEnt* se =  &sym_name_cache[hash];
+++   // Compute the hash from 'ep' and 'a'.  The latter contains lots of
+++   // significant bits, but 'ep' is expected to be a small number, typically
+++   // less than 500.  So rotate it around a bit in the hope of spreading the
+++   // bits out somewhat.
+++   vg_assert(!is_DiEpoch_INVALID(ep));
+++   UWord hash = a ^ (UWord)(ep.n ^ ROL32(ep.n, 5) 
+++                                 ^ ROL32(ep.n, 13) ^ ROL32(ep.n, 19));
+++   hash %= N_SYM_NAME_CACHE;
+++
+++   Sym_Name_CacheEnt* se = &sym_name_cache[hash];
++ 
++-   if (UNLIKELY(se->sym_avma != a || se->isText != findText)) {
+++   if (UNLIKELY(se->sym_epoch.n != ep.n || se->sym_avma != a
+++                || se->isText != findText)) {
++       DebugInfo* di;
++       Word       sno;
++ 
++-      search_all_symtabs ( a, &di, &sno, findText );
+++      search_all_symtabs ( ep, a, &di, &sno, findText );
+++      se->sym_epoch = ep;
++       se->sym_avma = a;
++       se->isText = findText;
++       if (di == NULL || a == 0)
++@@ -1846,12 +2043,12 @@ Bool get_sym_name ( Bool do_cxx_demangli
++ /* ppc64be-linux only: find the TOC pointer (R2 value) that should be in
++    force at the entry point address of the function containing
++    guest_code_addr.  Returns 0 if not known. */
++-Addr VG_(get_tocptr) ( Addr guest_code_addr )
+++Addr VG_(get_tocptr) ( DiEpoch ep, Addr guest_code_addr )
++ {
++ #if defined(VGA_ppc64be) || defined(VGA_ppc64le)
++    DebugInfo* si;
++    Word       sno;
++-   search_all_symtabs ( guest_code_addr, 
+++   search_all_symtabs ( ep, guest_code_addr, 
++                         &si, &sno,
++                         True/*consider text symbols only*/ );
++    if (si == NULL) 
++@@ -1867,11 +2064,11 @@ Addr VG_(get_tocptr) ( Addr guest_code_a
++    match anywhere in function, but don't show offsets.
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership
++    in pub_tool_debuginfo.h */
++-Bool VG_(get_fnname) ( Addr a, const HChar** buf )
+++Bool VG_(get_fnname) ( DiEpoch ep, Addr a, const HChar** buf )
++ {
++    return get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True,
++                          /*below-main-renaming*/True,
++-                         a, buf,
+++                         ep, a, buf,
++                          /*match_anywhere_in_fun*/True, 
++                          /*show offset?*/False,
++                          /*text sym*/True,
++@@ -1882,11 +2079,11 @@ Bool VG_(get_fnname) ( Addr a, const HCh
++    match anywhere in function, and show offset if nonzero.
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership
++    in pub_tool_debuginfo.h */
++-Bool VG_(get_fnname_w_offset) ( Addr a, const HChar** buf )
+++Bool VG_(get_fnname_w_offset) ( DiEpoch ep, Addr a, const HChar** buf )
++ {
++    return get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True,
++                          /*below-main-renaming*/True,
++-                         a, buf,
+++                         ep, a, buf,
++                          /*match_anywhere_in_fun*/True, 
++                          /*show offset?*/True,
++                          /*text sym*/True,
++@@ -1898,14 +2095,14 @@ Bool VG_(get_fnname_w_offset) ( Addr a,
++    and don't show offsets.
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership
++    in pub_tool_debuginfo.h */
++-Bool VG_(get_fnname_if_entry) ( Addr a, const HChar** buf )
+++Bool VG_(get_fnname_if_entry) ( DiEpoch ep, Addr a, const HChar** buf )
++ {
++    const HChar *tmp;
++    Bool res;
++ 
++    res =  get_sym_name ( /*C++-demangle*/True, /*Z-demangle*/True,
++                          /*below-main-renaming*/True,
++-                         a, &tmp,
+++                         ep, a, &tmp,
++                          /*match_anywhere_in_fun*/False, 
++                          /*show offset?*/False,
++                          /*text sym*/True,
++@@ -1920,11 +2117,11 @@ Bool VG_(get_fnname_if_entry) ( Addr a,
++    offsets.
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership
++    in pub_tool_debuginfo.h  */
++-Bool VG_(get_fnname_raw) ( Addr a, const HChar** buf )
+++Bool VG_(get_fnname_raw) ( DiEpoch ep, Addr a, const HChar** buf )
++ {
++    return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False,
++                          /*below-main-renaming*/False,
++-                         a, buf,
+++                         ep, a, buf,
++                          /*match_anywhere_in_fun*/True, 
++                          /*show offset?*/False,
++                          /*text sym*/True,
++@@ -1936,14 +2133,22 @@ Bool VG_(get_fnname_raw) ( Addr a, const
++    don't show offsets.
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership
++    in pub_tool_debuginfo.h */
++-Bool VG_(get_fnname_no_cxx_demangle) ( Addr a, const HChar** buf,
+++Bool VG_(get_fnname_no_cxx_demangle) ( DiEpoch ep, Addr a, const HChar** buf,
++                                        const InlIPCursor* iipc )
++ {
+++   // FIXME JRS 28 June 2017: should we use 'iipc->di->first_epoch'
+++   // instead of 'ep' in the call to get_sym_name?  At least let's
+++   // assert that the DebugInfo that 'iipc' mentions is valid for the
+++   // specified epoch.
+++   if (iipc) {
+++      vg_assert(is_DI_valid_for_epoch(iipc->di, ep));
+++   }
+++
++    if (is_bottom(iipc)) {
++       // At the bottom (towards main), we describe the fn at eip.
++       return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/True,
++                             /*below-main-renaming*/True,
++-                            a, buf,
+++                            ep, a, buf,
++                             /*match_anywhere_in_fun*/True, 
++                             /*show offset?*/False,
++                             /*text sym*/True,
++@@ -1962,13 +2167,13 @@ Bool VG_(get_fnname_no_cxx_demangle) ( A
++ /* mips-linux only: find the offset of current address. This is needed for 
++    stack unwinding for MIPS.
++ */
++-Bool VG_(get_inst_offset_in_function)( Addr a,
+++Bool VG_(get_inst_offset_in_function)( DiEpoch ep, Addr a,
++                                        /*OUT*/PtrdiffT* offset )
++ {
++    const HChar *fnname;
++    return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False,
++                          /*below-main-renaming*/False,
++-                         a, &fnname,
+++                         ep, a, &fnname,
++                          /*match_anywhere_in_sym*/True, 
++                          /*show offset?*/False,
++                          /*text sym*/True,
++@@ -2000,13 +2205,13 @@ Vg_FnNameKind VG_(get_fnname_kind) ( con
++    }
++ }
++ 
++-Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( Addr ip )
+++Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( DiEpoch ep, Addr ip )
++ {
++    const HChar *buf;
++ 
++    // We don't demangle, because it's faster not to, and the special names
++    // we're looking for won't be mangled.
++-   if (VG_(get_fnname_raw) ( ip, &buf )) {
+++   if (VG_(get_fnname_raw) ( ep, ip, &buf )) {
++ 
++       return VG_(get_fnname_kind)(buf);
++    } else {
++@@ -2019,13 +2224,13 @@ Vg_FnNameKind VG_(get_fnname_kind_from_I
++    Also data_addr's offset from the symbol start is put into *offset.
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership
++    in pub_tool_debuginfo.h  */
++-Bool VG_(get_datasym_and_offset)( Addr data_addr,
+++Bool VG_(get_datasym_and_offset)( DiEpoch ep, Addr data_addr,
++                                   /*OUT*/const HChar** dname,
++                                   /*OUT*/PtrdiffT* offset )
++ {
++    return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/False,
++                        /*below-main-renaming*/False,
++-                       data_addr, dname,
+++                       ep, data_addr, dname,
++                        /*match_anywhere_in_sym*/True, 
++                        /*show offset?*/False,
++                        /*text sym*/False,
++@@ -2038,7 +2243,7 @@ Bool VG_(get_datasym_and_offset)( Addr d
++    (1) the DebugInfo it belongs to is not discarded
++    (2) the segment containing the address is not merged with another segment
++ */
++-Bool VG_(get_objname) ( Addr a, const HChar** objname )
+++Bool VG_(get_objname) ( DiEpoch ep, Addr a, const HChar** objname )
++ {
++    DebugInfo* di;
++    const NSegment *seg;
++@@ -2047,6 +2252,8 @@ Bool VG_(get_objname) ( Addr a, const HC
++    /* Look in the debugInfo_list to find the name.  In most cases we
++       expect this to produce a result. */
++    for (di = debugInfo_list; di != NULL; di = di->next) {
+++      if (!is_DI_valid_for_epoch(di, ep))
+++         continue;
++       if (di->text_present
++           && di->text_size > 0
++           && di->text_avma <= a 
++@@ -2059,8 +2266,13 @@ Bool VG_(get_objname) ( Addr a, const HC
++       the debugInfo_list, ask the address space manager whether it
++       knows the name of the file associated with this mapping.  This
++       allows us to print the names of exe/dll files in the stack trace
++-      when running programs under wine. */
++-   if ( (seg = VG_(am_find_nsegment)(a)) != NULL 
+++      when running programs under wine.
+++
+++      Restrict this to the case where 'ep' is the current epoch, though, so
+++      that we don't return information about this epoch when the caller was
+++      enquiring about a different one. */
+++   if ( eq_DiEpoch(ep, VG_(current_DiEpoch)())
+++        && (seg = VG_(am_find_nsegment)(a)) != NULL 
++         && (filename = VG_(am_get_filename)(seg)) != NULL ) {
++       *objname = filename;
++       return True;
++@@ -2070,12 +2282,14 @@ Bool VG_(get_objname) ( Addr a, const HC
++ 
++ /* Map a code address to its DebugInfo.  Returns NULL if not found.  Doesn't
++    require debug info. */
++-DebugInfo* VG_(find_DebugInfo) ( Addr a )
+++DebugInfo* VG_(find_DebugInfo) ( DiEpoch ep, Addr a )
++ {
++    static UWord n_search = 0;
++    DebugInfo* di;
++    n_search++;
++    for (di = debugInfo_list; di != NULL; di = di->next) {
+++      if (!is_DI_valid_for_epoch(di, ep))
+++         continue;
++       if (di->text_present
++           && di->text_size > 0
++           && di->text_avma <= a 
++@@ -2091,13 +2305,13 @@ DebugInfo* VG_(find_DebugInfo) ( Addr a
++ /* Map a code address to a filename.  Returns True if successful. The
++    returned string is persistent as long as the DebugInfo to which it
++    belongs is not discarded. */
++-Bool VG_(get_filename)( Addr a, const HChar** filename )
+++Bool VG_(get_filename)( DiEpoch ep, Addr a, const HChar** filename )
++ {
++    DebugInfo* si;
++    Word       locno;
++    UInt       fndn_ix;
++ 
++-   search_all_loctabs ( a, &si, &locno );
+++   search_all_loctabs ( ep, a, &si, &locno );
++    if (si == NULL) 
++       return False;
++    fndn_ix = ML_(fndn_ix) (si, locno);
++@@ -2106,11 +2320,11 @@ Bool VG_(get_filename)( Addr a, const HC
++ }
++ 
++ /* Map a code address to a line number.  Returns True if successful. */
++-Bool VG_(get_linenum)( Addr a, UInt* lineno )
+++Bool VG_(get_linenum)( DiEpoch ep, Addr a, UInt* lineno )
++ {
++    DebugInfo* si;
++    Word       locno;
++-   search_all_loctabs ( a, &si, &locno );
+++   search_all_loctabs ( ep, a, &si, &locno );
++    if (si == NULL) 
++       return False;
++    *lineno = si->loctab[locno].lineno;
++@@ -2121,7 +2335,7 @@ Bool VG_(get_linenum)( Addr a, UInt* lin
++ /* Map a code address to a filename/line number/dir name info.
++    See prototype for detailed description of behaviour.
++ */
++-Bool VG_(get_filename_linenum) ( Addr a, 
+++Bool VG_(get_filename_linenum) ( DiEpoch ep, Addr a, 
++                                  /*OUT*/const HChar** filename,
++                                  /*OUT*/const HChar** dirname,
++                                  /*OUT*/UInt* lineno )
++@@ -2130,7 +2344,7 @@ Bool VG_(get_filename_linenum) ( Addr a,
++    Word       locno;
++    UInt       fndn_ix;
++ 
++-   search_all_loctabs ( a, &si, &locno );
+++   search_all_loctabs ( ep, a, &si, &locno );
++    if (si == NULL) {
++       if (dirname) {
++          *dirname = "";
++@@ -2159,7 +2373,8 @@ Bool VG_(get_filename_linenum) ( Addr a,
++    Therefore specify "*" to search all the objects.  On TOC-afflicted
++    platforms, a symbol is deemed to be found only if it has a nonzero
++    TOC pointer.  */
++-Bool VG_(lookup_symbol_SLOW)(const HChar* sopatt, const HChar* name,
+++Bool VG_(lookup_symbol_SLOW)(DiEpoch ep,
+++                             const HChar* sopatt, const HChar* name,
++                              SymAVMAs* avmas)
++ {
++    Bool     require_pToc = False;
++@@ -2172,6 +2387,8 @@ Bool VG_(lookup_symbol_SLOW)(const HChar
++    for (si = debugInfo_list; si; si = si->next) {
++       if (debug)
++          VG_(printf)("lookup_symbol_SLOW: considering %s\n", si->soname);
+++      if (!is_DI_valid_for_epoch(si, ep))
+++         continue;
++       if (!VG_(string_match)(sopatt, si->soname)) {
++          if (debug)
++             VG_(printf)(" ... skip\n");
++@@ -2254,7 +2471,7 @@ putStrEsc( SizeT n, HChar** buf, SizeT *
++    return n;
++ }
++ 
++-const HChar* VG_(describe_IP)(Addr eip, const InlIPCursor *iipc)
+++const HChar* VG_(describe_IP)(DiEpoch ep, Addr eip, const InlIPCursor *iipc)
++ {
++    static HChar *buf = NULL;
++    static SizeT bufsiz = 0;
++@@ -2267,7 +2484,10 @@ const HChar* VG_(describe_IP)(Addr eip,
++    HChar ibuf[50];   // large enough
++    SizeT n = 0;
++ 
++-   vg_assert (!iipc || iipc->eip == eip);
+++   // An InlIPCursor is associated with one specific DebugInfo.  So if
+++   // it exists, make sure that it is valid for the specified DiEpoch.
+++   vg_assert (!iipc
+++              || (is_DI_valid_for_epoch(iipc->di, ep) && iipc->eip == eip));
++ 
++    const HChar *buf_fn;
++    const HChar *buf_obj;
++@@ -2282,8 +2502,8 @@ const HChar* VG_(describe_IP)(Addr eip,
++    if (is_bottom(iipc)) {
++       // At the bottom (towards main), we describe the fn at eip.
++       know_fnname = VG_(clo_sym_offsets)
++-                    ? VG_(get_fnname_w_offset) (eip, &buf_fn)
++-                    : VG_(get_fnname) (eip, &buf_fn);
+++                    ? VG_(get_fnname_w_offset) (ep, eip, &buf_fn)
+++                    : VG_(get_fnname) (ep, eip, &buf_fn);
++    } else {
++       const DiInlLoc *next_inl = iipc && iipc->next_inltab >= 0
++          ? & iipc->di->inltab[iipc->next_inltab]
++@@ -2301,12 +2521,12 @@ const HChar* VG_(describe_IP)(Addr eip,
++       // ??? Currently never showing an offset.
++    }
++ 
++-   know_objname = VG_(get_objname)(eip, &buf_obj);
+++   know_objname = VG_(get_objname)(ep, eip, &buf_obj);
++ 
++    if (is_top(iipc)) {
++       // The source for the highest level is in the loctab entry.
++       know_srcloc  = VG_(get_filename_linenum)(
++-                        eip, 
+++                        ep, eip, 
++                         &buf_srcloc, 
++                         &buf_dirname,
++                         &lineno 
++@@ -2459,6 +2679,20 @@ const HChar* VG_(describe_IP)(Addr eip,
++ /*---                                                        ---*/
++ /*--------------------------------------------------------------*/
++ 
+++/* Note that the CFI machinery pertains to unwinding the stack "right now".
+++   There is no support for unwinding stack images obtained from some time in
+++   the past.  That means that:
+++
+++   (1) We only deal with CFI from DebugInfos that are valid for the current
+++       debuginfo epoch.  Unlike in the rest of the file, there is no
+++       epoch-awareness.
+++
+++   (2) We assume that the CFI cache will be invalidated every time the the
+++       epoch changes.  This is done by ensuring (in the file above) that
+++       every call to advance_current_DiEpoch has a call to
+++       caches__invalidate alongside it.
+++*/
+++
++ /* Gather up all the constant pieces of info needed to evaluate
++    a CfiExpr into one convenient struct. */
++ typedef
++@@ -2579,6 +2813,9 @@ UWord evalCfiExpr ( const XArray* exprs,
++    *cfsi_mP to the cfsi_m pointer in that DebugInfo's cfsi_m_pool.
++ 
++    If not found, set *diP to (DebugInfo*)1 and *cfsi_mP to zero.
+++
+++   Per comments at the top of this section, we only look for CFI in
+++   DebugInfos that are valid for the current epoch.
++ */
++ __attribute__((noinline))
++ static void find_DiCfSI ( /*OUT*/DebugInfo** diP, 
++@@ -2594,10 +2831,15 @@ static void find_DiCfSI ( /*OUT*/DebugIn
++ 
++    if (0) VG_(printf)("search for %#lx\n", ip);
++ 
+++   DiEpoch curr_epoch = VG_(current_DiEpoch)();
+++
++    for (di = debugInfo_list; di != NULL; di = di->next) {
++       Word j;
++       n_steps++;
++ 
+++      if (!is_DI_valid_for_epoch(di, curr_epoch))
+++         continue;
+++
++       /* Use the per-DebugInfo summary address ranges to skip
++          inapplicable DebugInfos quickly. */
++       if (di->cfsi_used == 0)
++@@ -2605,6 +2847,11 @@ static void find_DiCfSI ( /*OUT*/DebugIn
++       if (ip < di->cfsi_minavma || ip > di->cfsi_maxavma)
++          continue;
++ 
+++      // This di must be active (because we have explicitly chosen not to
+++      // allow unwinding stacks that pertain to some past epoch).  It can't
+++      // be archived or not-yet-active.
+++      vg_assert(is_DebugInfo_active(di));
+++
++       /* It might be in this DebugInfo.  Search it. */
++       j = ML_(search_one_cfitab)( di, ip );
++       vg_assert(j >= -1 && j < (Word)di->cfsi_used);
++@@ -3032,6 +3279,7 @@ Bool VG_(use_CF_info) ( /*MOD*/D3UnwindR
++ Bool VG_(use_FPO_info) ( /*MOD*/Addr* ipP,
++                          /*MOD*/Addr* spP,
++                          /*MOD*/Addr* fpP,
+++                         DiEpoch ep,
++                          Addr min_accessible,
++                          Addr max_accessible )
++ {
++@@ -3049,6 +3297,9 @@ Bool VG_(use_FPO_info) ( /*MOD*/Addr* ip
++    for (di = debugInfo_list; di != NULL; di = di->next) {
++       n_steps++;
++ 
+++      if (!is_DI_valid_for_epoch(di, ep))
+++         continue;
+++
++       /* Use the per-DebugInfo summary address ranges to skip
++          inapplicable DebugInfos quickly. */
++       if (di->fpo == NULL)
++@@ -3552,6 +3803,7 @@ static void format_message ( /*MOD*/XArr
++ static 
++ Bool consider_vars_in_frame ( /*MOD*/XArray* /* of HChar */ dname1,
++                               /*MOD*/XArray* /* of HChar */ dname2,
+++                              DiEpoch ep,
++                               Addr data_addr,
++                               Addr ip, Addr sp, Addr fp,
++                               /* shown to user: */
++@@ -3570,6 +3822,8 @@ Bool consider_vars_in_frame ( /*MOD*/XAr
++    /* first, find the DebugInfo that pertains to 'ip'. */
++    for (di = debugInfo_list; di; di = di->next) {
++       n_steps++;
+++      if (!is_DI_valid_for_epoch(di, ep))
+++         continue;
++       /* text segment missing? unlikely, but handle it .. */
++       if (!di->text_present || di->text_size == 0)
++          continue;
++@@ -3687,7 +3941,7 @@ Bool consider_vars_in_frame ( /*MOD*/XAr
++ Bool VG_(get_data_description)( 
++         /*MOD*/ XArray* /* of HChar */ dname1,
++         /*MOD*/ XArray* /* of HChar */ dname2,
++-        Addr data_addr
+++        DiEpoch ep, Addr data_addr
++      )
++ {
++ #  define N_FRAMES 8
++@@ -3807,7 +4061,7 @@ Bool VG_(get_data_description)(
++    vg_assert(n_frames >= 0 && n_frames <= N_FRAMES);
++    for (j = 0; j < n_frames; j++) {
++       if (consider_vars_in_frame( dname1, dname2,
++-                                  data_addr,
+++                                  ep, data_addr,
++                                   ips[j], 
++                                   sps[j], fps[j], tid, j )) {
++          zterm_XA( dname1 );
++@@ -3834,7 +4088,7 @@ Bool VG_(get_data_description)(
++          equivalent kludge. */
++       if (j > 0 /* this is a non-innermost frame */
++           && consider_vars_in_frame( dname1, dname2,
++-                                     data_addr,
+++                                     ep, data_addr,
++                                      ips[j] + 1, 
++                                      sps[j], fps[j], tid, j )) {
++          zterm_XA( dname1 );
++Index: valgrind-3.13.0/coregrind/m_debuginfo/priv_storage.h
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_debuginfo/priv_storage.h
+++++ valgrind-3.13.0/coregrind/m_debuginfo/priv_storage.h
++@@ -588,6 +588,36 @@ struct _DebugInfo {
++       structure is allocated. */
++    ULong handle;
++ 
+++   /* The range of epochs for which this DebugInfo is valid.  These also
+++      divide the DebugInfo's lifetime into three parts:
+++
+++      (1) Allocated: but with only .fsm holding useful info -- in
+++          particular, not yet holding any debug info.
+++          .first_epoch == DebugInfoEpoch_INVALID
+++          .last_epoch  == DebugInfoEpoch_INVALID
+++
+++      (2) Active: containing debug info, and current.
+++          .first_epoch != DebugInfoEpoch_INVALID
+++          .last_epoch  == DebugInfoEpoch_INVALID
+++
+++      (3) Archived: containing debug info, but no longer current.
+++          .first_epoch != DebugInfoEpoch_INVALID
+++          .last_epoch  != DebugInfoEpoch_INVALID
+++
+++      State (2) corresponds to an object which is currently mapped.  When
+++      the object is unmapped, what happens depends on the setting of
+++      --keep-debuginfo:
+++      
+++      * when =no, the DebugInfo is removed from debugInfo_list and
+++        deleted.
+++
+++      * when =yes, the DebugInfo is retained in debugInfo_list, but its
+++        .last_epoch field is filled in, and current_epoch is advanced.  This
+++        effectively moves the DebugInfo into state (3).
+++   */
+++   DiEpoch first_epoch;
+++   DiEpoch last_epoch;
+++
++    /* Used for debugging only - indicate what stuff to dump whilst
++       reading stuff into the seginfo.  Are computed as early in the
++       lifetime of the DebugInfo as possible -- at the point when it is
++Index: valgrind-3.13.0/coregrind/m_errormgr.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_errormgr.c
+++++ valgrind-3.13.0/coregrind/m_errormgr.c
++@@ -136,16 +136,16 @@ struct _Error {
++    Int count;
++ 
++    // The tool-specific part
++-   ThreadId tid;           // Initialised by core
++-   ExeContext* where;      // Initialised by core
++-   ErrorKind ekind;        // Used by ALL.  Must be in the range (0..)
++-   Addr addr;              // Used frequently
++-   const HChar* string;    // Used frequently
++-   void* extra;            // For any tool-specific extras
+++   ThreadId tid;              // Initialised by core
+++   ExeContextAndEpoch where;  // Initialised by core
+++   ErrorKind ekind;           // Used by ALL.  Must be in the range (0..)
+++   Addr addr;                 // Used frequently
+++   const HChar* string;       // Used frequently
+++   void* extra;               // For any tool-specific extras
++ };
++ 
++ 
++-ExeContext* VG_(get_error_where) ( const Error* err )
+++ExeContextAndEpoch VG_(get_error_where) ( const Error* err )
++ {
++    return err->where;
++ }
++@@ -293,7 +293,10 @@ static Bool eq_Error ( VgRes res, const
++ {
++    if (e1->ekind != e2->ekind) 
++       return False;
++-   if (!VG_(eq_ExeContext)(res, e1->where, e2->where))
+++   // This comparison ignores the debuginfo epoch.  Result is that we
+++   // could conclude this error is the same as one from some other epoch.
+++   // I don't think that's a big deal in practice.
+++   if (!VG_(eq_ExeContext)(res, e1->where.ec, e2->where.ec))
++       return False;
++ 
++    switch (e1->ekind) {
++@@ -321,15 +324,15 @@ static Bool eq_Error ( VgRes res, const
++ */
++ #define ERRTXT_LEN   4096
++ 
++-static void printSuppForIp_XML(UInt n, Addr ip, void* uu_opaque)
+++static void printSuppForIp_XML(UInt n, DiEpoch ep, Addr ip, void* uu_opaque)
++ {
++    const HChar *buf;
++-   InlIPCursor* iipc = VG_(new_IIPC)(ip);
+++   InlIPCursor* iipc = VG_(new_IIPC)(ep, ip);
++    do {
++-      if ( VG_(get_fnname_no_cxx_demangle) (ip, &buf, iipc) ) {
+++      if ( VG_(get_fnname_no_cxx_demangle) (ep, ip, &buf, iipc) ) {
++          VG_(printf_xml)("    <sframe> <fun>%pS</fun> </sframe>\n", buf);
++       } else
++-      if ( VG_(get_objname)(ip, &buf) ) {
+++      if ( VG_(get_objname)(ep, ip, &buf) ) {
++          VG_(printf_xml)("    <sframe> <obj>%pS</obj> </sframe>\n", buf);
++       } else {
++          VG_(printf_xml)("    <sframe> <obj>*</obj> </sframe>\n");
++@@ -338,16 +341,16 @@ static void printSuppForIp_XML(UInt n, A
++    VG_(delete_IIPC)(iipc);
++ }
++ 
++-static void printSuppForIp_nonXML(UInt n, Addr ip, void* textV)
+++static void printSuppForIp_nonXML(UInt n, DiEpoch ep, Addr ip, void* textV)
++ {
++    const HChar *buf;
++    XArray* /* of HChar */ text = (XArray*)textV;
++-   InlIPCursor* iipc = VG_(new_IIPC)(ip);
+++   InlIPCursor* iipc = VG_(new_IIPC)(ep, ip);
++    do {
++-      if ( VG_(get_fnname_no_cxx_demangle) (ip, &buf, iipc) ) {
+++      if ( VG_(get_fnname_no_cxx_demangle) (ep, ip, &buf, iipc) ) {
++          VG_(xaprintf)(text, "   fun:%s\n", buf);
++       } else
++-      if ( VG_(get_objname)(ip, &buf) ) {
+++      if ( VG_(get_objname)(ep, ip, &buf) ) {
++          VG_(xaprintf)(text, "   obj:%s\n", buf);
++       } else {
++          VG_(xaprintf)(text, "   obj:*\n");
++@@ -361,15 +364,15 @@ static void printSuppForIp_nonXML(UInt n
++ static void gen_suppression(const Error* err)
++ {
++    const HChar* name;
++-   ExeContext* ec;
+++   ExeContextAndEpoch ece;
++    XArray* /* HChar */ text;
++ 
++    const HChar* dummy_name = "insert_a_suppression_name_here";
++ 
++    vg_assert(err);
++ 
++-   ec = VG_(get_error_where)(err);
++-   vg_assert(ec);
+++   ece = VG_(get_error_where)(err);
+++   vg_assert(ece.ec);
++ 
++    name = VG_TDICT_CALL(tool_get_error_name, err);
++    if (NULL == name) {
++@@ -408,12 +411,12 @@ static void gen_suppression(const Error*
++       VG_(xaprintf)(text, "   %s\n", xtra);
++ 
++    // Print stack trace elements
++-   UInt n_ips = VG_(get_ExeContext_n_ips)(ec);
+++   UInt n_ips = VG_(get_ExeContext_n_ips)(ece.ec);
++    vg_assert(n_ips > 0);
++    vg_assert(n_ips <= VG_DEEPEST_BACKTRACE);
++    VG_(apply_StackTrace)(printSuppForIp_nonXML,
++-                         text,
++-                         VG_(get_ExeContext_StackTrace)(ec),
+++                         text, ece.epoch,
+++                         VG_(get_ExeContext_StackTrace)(ece.ec),
++                          n_ips);
++ 
++    VG_(xaprintf)(text, "}\n");
++@@ -441,9 +444,9 @@ static void gen_suppression(const Error*
++ 
++       // Print stack trace elements
++       VG_(apply_StackTrace)(printSuppForIp_XML,
++-                            NULL,
++-                            VG_(get_ExeContext_StackTrace)(ec),
++-                            VG_(get_ExeContext_n_ips)(ec));
+++                            NULL, ece.epoch,
+++                            VG_(get_ExeContext_StackTrace)(ece.ec),
+++                            VG_(get_ExeContext_n_ips)(ece.ec));
++ 
++       // And now the cdata bit
++       // XXX FIXME!  properly handle the case where the raw text
++@@ -637,7 +640,7 @@ static void pp_Error ( const Error* err,
++ /* Construct an error */
++ static
++ void construct_error ( Error* err, ThreadId tid, ErrorKind ekind, Addr a,
++-                       const HChar* s, void* extra, ExeContext* where )
+++                       const HChar* s, void* extra, ExeContextAndEpoch where )
++ {
++    /* DO NOT MAKE unique_counter NON-STATIC */
++    static UInt unique_counter = 0;
++@@ -650,10 +653,7 @@ void construct_error ( Error* err, Threa
++    err->supp     = NULL;
++    err->count    = 1;
++    err->tid      = tid;
++-   if (NULL == where)
++-     err->where = VG_(record_ExeContext)( tid, 0 );
++-   else
++-      err->where = where;
+++   err->where    = where;
++ 
++    /* Tool-relevant parts */
++    err->ekind  = ekind;
++@@ -744,7 +744,10 @@ void VG_(maybe_record_error) ( ThreadId
++    }
++ 
++    /* Build ourselves the error */
++-   construct_error ( &err, tid, ekind, a, s, extra, NULL );
+++   { ExeContextAndEpoch ece
+++        = VG_(tag_EC_with_current_epoch)( VG_(record_ExeContext)( tid, 0 ) );
+++      construct_error ( &err, tid, ekind, a, s, extra, ece );
+++   }
++ 
++    /* First, see if we've got an error record matching this one. */
++    em_errlist_searches++;
++@@ -853,7 +856,8 @@ void VG_(maybe_record_error) ( ThreadId
++    Bool 'count_error' dictates whether to count the error in n_errs_found.
++ */
++ Bool VG_(unique_error) ( ThreadId tid, ErrorKind ekind, Addr a, const HChar* s,
++-                         void* extra, ExeContext* where, Bool print_error,
+++                         void* extra, ExeContextAndEpoch where,
+++                         Bool print_error,
++                          Bool allow_db_attach, Bool count_error )
++ {
++    Error err;
++@@ -1016,7 +1020,7 @@ void VG_(show_all_errors) (  Int verbosi
++       vg_assert(! xml);
++ 
++       if ((i+1 == VG_(clo_dump_error))) {
++-         StackTrace ips = VG_(get_ExeContext_StackTrace)(p_min->where);
+++         StackTrace ips = VG_(get_ExeContext_StackTrace)(p_min->where.ec);
++          VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to debugging*/,
++                           ips[0], /*debugging*/True, 0xFE/*verbosity*/,
++                           /*bbs_done*/0,
++@@ -1500,6 +1504,7 @@ static Bool supploc_IsQuery ( const void
++    allocations and the nr of debuginfo search. */
++ typedef
++    struct {
+++      DiEpoch epoch;  // used to interpret .ips
++       StackTrace ips; // stack trace we are lazily completing.
++       UWord n_ips; // nr of elements in ips.
++ 
++@@ -1595,9 +1600,11 @@ static void clearIPtoFunOrObjCompleter (
++                    su->sname,
++                    filename,
++                    su->sname_lineno);
++-      } else
+++      } else {
++          VG_(dmsg)("errormgr matching end no suppression matched:\n");
++-      VG_(pp_StackTrace) (ip2fo->ips, ip2fo->n_ips);
+++      }
+++      // JRS 27 July 2017: is it OK to use the current epoch here?
+++      VG_(pp_StackTrace) (ip2fo->epoch, ip2fo->ips, ip2fo->n_ips);
++       pp_ip2fo(ip2fo);
++    }
++    if (ip2fo->n_offsets_per_ip) VG_(free)(ip2fo->n_offsets_per_ip);
++@@ -1660,7 +1667,8 @@ static HChar* foComplete(IPtoFunOrObjCom
++          // up comparing "malloc" in the suppression against
++          // "_vgrZU_libcZdsoZa_malloc" in the backtrace, and the
++          // two of them need to be made to match.
++-         if (!VG_(get_fnname_no_cxx_demangle)(ip2fo->ips[ixInput],
+++         if (!VG_(get_fnname_no_cxx_demangle)(ip2fo->epoch,
+++                                              ip2fo->ips[ixInput],
++                                               &caller,
++                                               NULL))
++             caller = "???";
++@@ -1681,7 +1689,7 @@ static HChar* foComplete(IPtoFunOrObjCom
++             last_expand_pos_ips is the last offset in fun/obj where
++             ips[pos_ips] has been expanded. */
++ 
++-         if (!VG_(get_objname)(ip2fo->ips[pos_ips], &caller))
+++         if (!VG_(get_objname)(ip2fo->epoch, ip2fo->ips[pos_ips], &caller))
++             caller = "???";
++ 
++          // Have all inlined calls pointing at this object name
++@@ -1751,7 +1759,7 @@ static void expandInput (IPtoFunOrObjCom
++          const Addr IP = ip2fo->ips[ip2fo->n_ips_expanded];
++          InlIPCursor *iipc;
++ 
++-         iipc = VG_(new_IIPC)(IP);
+++         iipc = VG_(new_IIPC)(ip2fo->epoch, IP);
++          // The only thing we really need is the nr of inlined fn calls
++          // corresponding to the IP we will expand.
++          // However, computing this is mostly the same as finding
++@@ -1760,7 +1768,7 @@ static void expandInput (IPtoFunOrObjCom
++             const HChar *caller;
++             grow_offsets(ip2fo, ip2fo->n_expanded+1);
++             ip2fo->fun_offsets[ip2fo->n_expanded] = ip2fo->names_free;
++-            if (!VG_(get_fnname_no_cxx_demangle)(IP, 
+++            if (!VG_(get_fnname_no_cxx_demangle)(ip2fo->epoch, IP, 
++                                                  &caller,
++                                                  iipc))
++                caller = "???";
++@@ -1788,18 +1796,18 @@ static void expandInput (IPtoFunOrObjCom
++    }
++ }
++ 
++-static Bool haveInputInpC (void* inputCompleter, UWord ixInput )
+++static Bool haveInputInpC (void* inputCompleterV, UWord ixInput )
++ {
++-   IPtoFunOrObjCompleter* ip2fo = inputCompleter;
+++   IPtoFunOrObjCompleter* ip2fo = (IPtoFunOrObjCompleter*)inputCompleterV;
++    expandInput(ip2fo, ixInput);
++    return ixInput < ip2fo->n_expanded;
++ }
++ 
++ static Bool supp_pattEQinp ( const void* supplocV, const void* addrV,
++-                             void* inputCompleter, UWord ixInput )
+++                             void* inputCompleterV, UWord ixInput )
++ {
++-   const SuppLoc* supploc = supplocV; /* PATTERN */
++-   IPtoFunOrObjCompleter* ip2fo = inputCompleter;
+++   const SuppLoc* supploc = (const SuppLoc*)supplocV; /* PATTERN */
+++   IPtoFunOrObjCompleter* ip2fo = (IPtoFunOrObjCompleter*)inputCompleterV;
++    HChar* funobj_name; // Fun or Obj name.
++    Bool ret;
++ 
++@@ -1926,8 +1934,9 @@ static Supp* is_suppressible_error ( con
++    em_supplist_searches++;
++ 
++    /* Prepare the lazy input completer. */
++-   ip2fo.ips = VG_(get_ExeContext_StackTrace)(err->where);
++-   ip2fo.n_ips = VG_(get_ExeContext_n_ips)(err->where);
+++   ip2fo.epoch = err->where.epoch;
+++   ip2fo.ips = VG_(get_ExeContext_StackTrace)(err->where.ec);
+++   ip2fo.n_ips = VG_(get_ExeContext_n_ips)(err->where.ec);
++    ip2fo.n_ips_expanded = 0;
++    ip2fo.n_expanded = 0;
++    ip2fo.sz_offsets = 0;
++Index: valgrind-3.13.0/coregrind/m_execontext.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_execontext.c
+++++ valgrind-3.13.0/coregrind/m_execontext.c
++@@ -116,7 +116,7 @@ static ULong ec_cmpAlls;
++ 
++ 
++ /*------------------------------------------------------------*/
++-/*--- Exported functions.                                  ---*/
+++/*--- ExeContext functions.                                ---*/
++ /*------------------------------------------------------------*/
++ 
++ static ExeContext* record_ExeContext_wrk2 ( const Addr* ips, UInt n_ips );
++@@ -169,7 +169,9 @@ void VG_(print_ExeContext_stats) ( Bool
++          for (ec = ec_htab[i]; ec; ec = ec->chain) {
++             VG_(message)(Vg_DebugMsg, "   exectx: stacktrace ecu %u n_ips %u\n",
++                          ec->ecu, ec->n_ips);
++-            VG_(pp_StackTrace)( ec->ips, ec->n_ips );
+++            // FIXME JRS 27 July 2017: is a fake epoch here OK?
+++            DiEpoch ep = VG_(current_DiEpoch)();
+++            VG_(pp_StackTrace)( ep, ec->ips, ec->n_ips );
++          }
++       }
++       VG_(message)(Vg_DebugMsg, 
++@@ -202,13 +204,6 @@ void VG_(print_ExeContext_stats) ( Bool
++ }
++ 
++ 
++-/* Print an ExeContext. */
++-void VG_(pp_ExeContext) ( ExeContext* ec )
++-{
++-   VG_(pp_StackTrace)( ec->ips, ec->n_ips );
++-}
++-
++-
++ /* Compare two ExeContexts.  Number of callers considered depends on res. */
++ Bool VG_(eq_ExeContext) ( VgRes res, const ExeContext* e1,
++                           const ExeContext* e2 )
++@@ -544,12 +539,48 @@ ExeContext* VG_(make_ExeContext_from_Sta
++    return record_ExeContext_wrk2(ips, n_ips);
++ }
++ 
++-ExeContext* VG_(null_ExeContext) (void)
+++
+++/*------------------------------------------------------------*/
+++/*--- ExeContextAndEpoch functions.                        ---*/
+++/*------------------------------------------------------------*/
+++
+++ExeContextAndEpoch VG_(tag_EC_with_current_epoch)( ExeContext* ec )
+++{
+++   ExeContextAndEpoch ece;
+++   ece.ec    = ec;
+++   ece.epoch = VG_(current_DiEpoch)();
+++   return ece;
+++}
+++
+++ExeContextAndEpoch VG_(invalid_ExeContextAndEpoch) ( void )
+++{
+++   ExeContextAndEpoch ece;
+++   ece.ec      = NULL/*invalid ExeContext*/;
+++   ece.epoch.n = 0/*invalid epoch*/;
+++   return ece;
+++}
+++
+++
+++void VG_(pp_ExeContextAndEpoch) ( ExeContextAndEpoch ece )
+++{
+++   VG_(pp_StackTrace)( ece.epoch, ece.ec->ips, ece.ec->n_ips );
+++}
+++
+++ExeContextAndEpoch VG_(null_ExeContextAndEpoch) ( void )
++ {
++    init_ExeContext_storage();
++-   return null_ExeContext;
+++   ExeContextAndEpoch ece
+++      = mk_ExeContextAndEpoch(null_ExeContext, VG_(current_DiEpoch)());
+++   return ece;
++ }
++ 
+++Bool VG_(is_null_ExeContextAndEpoch)( ExeContextAndEpoch ece )
+++{
+++   init_ExeContext_storage();
+++   return ece.ec == null_ExeContext;
+++}
+++
+++
++ /*--------------------------------------------------------------------*/
++ /*--- end                                           m_execontext.c ---*/
++ /*--------------------------------------------------------------------*/
++Index: valgrind-3.13.0/coregrind/m_gdbserver/m_gdbserver.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_gdbserver/m_gdbserver.c
+++++ valgrind-3.13.0/coregrind/m_gdbserver/m_gdbserver.c
++@@ -142,14 +142,17 @@ static HChar* sym (Addr addr, Bool is_co
++    PtrdiffT offset;
++    if (w == 2) w = 0;
++ 
+++   // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
++    if (is_code) {
++       const HChar *name;
++-      name = VG_(describe_IP) (addr, NULL);
+++      name = VG_(describe_IP) (ep, addr, NULL);
++       if (buf[w]) VG_(free)(buf[w]);
++       buf[w] = VG_(strdup)("gdbserver sym", name);
++    } else {
++       const HChar *name;
++-      VG_(get_datasym_and_offset) (addr, &name, &offset);
+++      VG_(get_datasym_and_offset) (ep, addr, &name, &offset);
++       if (buf[w]) VG_(free)(buf[w]);
++       buf[w] = VG_(strdup)("gdbserver sym", name);
++    }
++Index: valgrind-3.13.0/coregrind/m_gdbserver/server.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_gdbserver/server.c
+++++ valgrind-3.13.0/coregrind/m_gdbserver/server.c
++@@ -195,6 +195,9 @@ int handle_gdb_valgrind_command (char *m
++    int   kwdid;
++    int int_value;
++ 
+++   // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
++    vg_assert (initial_valgrind_sink_saved);
++ 
++    strcpy (s, mon);
++@@ -334,7 +337,7 @@ int handle_gdb_valgrind_command (char *m
++          }
++          if (hostvisibility) {
++             const DebugInfo *tooldi 
++-               = VG_(find_DebugInfo) ((Addr)handle_gdb_valgrind_command);
+++               = VG_(find_DebugInfo) (ep, (Addr)handle_gdb_valgrind_command);
++             /* Normally, we should always find the tooldi. In case we
++                do not, suggest a 'likely somewhat working' address: */
++             const Addr tool_text_start
++@@ -442,14 +445,14 @@ int handle_gdb_valgrind_command (char *m
++                                                &dummy_sz, &ssaveptr)) {
++             // If tool provides location information, use that.
++             if (VG_(needs).info_location) {
++-               VG_TDICT_CALL(tool_info_location, address);
+++               VG_TDICT_CALL(tool_info_location, ep, address);
++             } 
++             // If tool does not provide location info, 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_(describe_addr) (ep, address, &ai);
++                VG_(pp_addrinfo) (address, &ai);
++                VG_(clear_addrinfo) (&ai);
++             }
++Index: valgrind-3.13.0/coregrind/m_gdbserver/target.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_gdbserver/target.c
+++++ valgrind-3.13.0/coregrind/m_gdbserver/target.c
++@@ -209,7 +209,10 @@ void gdbserver_process_exit_encountered
++ static
++ const HChar* sym (Addr addr)
++ {
++-   return VG_(describe_IP) (addr, NULL);
+++   // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
+++   return VG_(describe_IP) (ep, addr, NULL);
++ }
++ 
++ ThreadId vgdb_interrupted_tid = 0;
++Index: valgrind-3.13.0/coregrind/m_gdbserver/valgrind-low-arm.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_gdbserver/valgrind-low-arm.c
+++++ valgrind-3.13.0/coregrind/m_gdbserver/valgrind-low-arm.c
++@@ -149,8 +149,12 @@ Addr thumb_pc (Addr pc)
++       // the debug info with the bit0 set
++       // (why can't debug info do that for us ???)
++       // (why if this is a 4 bytes thumb instruction ???)
++-      if (VG_(get_fnname_raw) (pc | 1, &fnname)) {
++-         if (VG_(lookup_symbol_SLOW)( "*", fnname, &avmas )) {
+++
+++      // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++      const DiEpoch ep = VG_(current_DiEpoch)();
+++
+++      if (VG_(get_fnname_raw) (ep, pc | 1, &fnname)) {
+++         if (VG_(lookup_symbol_SLOW)( ep, "*", fnname, &avmas )) {
++             dlog (1, "fnname %s lookupsym %p => %p %s.\n",
++                   fnname, C2v(avmas.main), C2v(pc),
++                   (avmas.main & 1 ? "thumb" : "arm"));
++Index: valgrind-3.13.0/coregrind/m_gdbserver/valgrind-low-mips32.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_gdbserver/valgrind-low-mips32.c
+++++ valgrind-3.13.0/coregrind/m_gdbserver/valgrind-low-mips32.c
++@@ -228,7 +228,11 @@ static Addr mips_adjust_breakpoint_addre
++    /* Make sure we don't scan back before the beginning of the current
++       function, since we may fetch constant data or insns that look like
++       a jump. */
++-   if (VG_(get_inst_offset_in_function) (bpaddr, &offset)) {
+++
+++   // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
+++   if (VG_(get_inst_offset_in_function) (ep, bpaddr, &offset)) {
++       func_addr = bpaddr - offset;
++       if (func_addr > boundary && func_addr <= bpaddr)
++          boundary = func_addr;
++Index: valgrind-3.13.0/coregrind/m_gdbserver/valgrind-low-mips64.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_gdbserver/valgrind-low-mips64.c
+++++ valgrind-3.13.0/coregrind/m_gdbserver/valgrind-low-mips64.c
++@@ -229,7 +229,11 @@ static Addr mips_adjust_breakpoint_addre
++    /* Make sure we don't scan back before the beginning of the current
++       function, since we may fetch constant data or insns that look like
++       a jump. */
++-   if (VG_(get_inst_offset_in_function) (bpaddr, &offset)) {
+++
+++   // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
+++   if (VG_(get_inst_offset_in_function) (ep, bpaddr, &offset)) {
++       func_addr = bpaddr - offset;
++       if (func_addr > boundary && func_addr <= bpaddr)
++          boundary = func_addr;
++Index: valgrind-3.13.0/coregrind/m_libcassert.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_libcassert.c
+++++ valgrind-3.13.0/coregrind/m_libcassert.c
++@@ -369,7 +369,7 @@ static void show_sched_status_wrk ( Bool
++          );
++       VG_(printf)("\nhost stacktrace:\n"); 
++       VG_(clo_xml) = False;
++-      VG_(pp_StackTrace) (ips, n_ips);
+++      VG_(pp_StackTrace) (VG_(current_DiEpoch)(), ips, n_ips);
++       VG_(clo_xml) = save_clo_xml;
++    }
++ 
++Index: valgrind-3.13.0/coregrind/m_main.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_main.c
+++++ valgrind-3.13.0/coregrind/m_main.c
++@@ -128,6 +128,10 @@ static void usage_NORETURN ( Bool debug_
++ "    --error-exitcode=<number> exit code to return if errors found [0=disable]\n"
++ "    --error-markers=<begin>,<end> add lines with begin/end markers before/after\n"
++ "                              each error output in plain text mode [none]\n"
+++"    --keep-debuginfo=no|yes   Keep symbols etc for unloaded code [no]\n"
+++"                              This allows stack traces for memory leaks to\n"
+++"                              include file/line info for code that has been\n"
+++"                              dlclose'd (or similar)\n"
++ "    --show-below-main=no|yes  continue stack traces below main() [no]\n"
++ "    --default-suppressions=yes|no\n"
++ "                              load default suppressions [yes]\n"
++@@ -626,6 +630,7 @@ void main_process_cmd_line_options( void
++       else if VG_BOOL_CLO(arg, "--run-libc-freeres", VG_(clo_run_libc_freeres)) {}
++       else if VG_BOOL_CLO(arg, "--run-cxx-freeres",  VG_(clo_run_cxx_freeres)) {}
++       else if VG_BOOL_CLO(arg, "--show-below-main",  VG_(clo_show_below_main)) {}
+++      else if VG_BOOL_CLO(arg, "--keep-debuginfo",   VG_(clo_keep_debuginfo)) {}
++       else if VG_BOOL_CLO(arg, "--time-stamp",       VG_(clo_time_stamp)) {}
++       else if VG_BOOL_CLO(arg, "--track-fds",        VG_(clo_track_fds)) {}
++       else if VG_BOOL_CLO(arg, "--trace-children",   VG_(clo_trace_children)) {}
++Index: valgrind-3.13.0/coregrind/m_options.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_options.c
+++++ valgrind-3.13.0/coregrind/m_options.c
++@@ -129,6 +129,7 @@ Bool   VG_(clo_run_libc_freeres) = True;
++ Bool   VG_(clo_run_cxx_freeres) = True;
++ Bool   VG_(clo_track_fds)      = False;
++ Bool   VG_(clo_show_below_main)= False;
+++Bool   VG_(clo_keep_debuginfo) = False;
++ Bool   VG_(clo_show_emwarns)   = False;
++ Word   VG_(clo_max_stackframe) = 2000000;
++ UInt   VG_(clo_max_threads)    = MAX_THREADS_DEFAULT;
++Index: valgrind-3.13.0/coregrind/m_redir.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_redir.c
+++++ valgrind-3.13.0/coregrind/m_redir.c
++@@ -1859,15 +1859,16 @@ static void show_active ( const HChar* l
++ {
++    Bool ok;
++    const HChar *buf;
++- 
++-   ok = VG_(get_fnname_w_offset)(act->from_addr, &buf);
+++
+++   DiEpoch ep = VG_(current_DiEpoch)(); 
+++   ok = VG_(get_fnname_w_offset)(ep, act->from_addr, &buf);
++    if (!ok) buf = "???";
++    // Stash away name1
++    HChar name1[VG_(strlen)(buf) + 1];
++    VG_(strcpy)(name1, buf);
++ 
++    const HChar *name2;
++-   ok = VG_(get_fnname_w_offset)(act->to_addr, &name2);
+++   ok = VG_(get_fnname_w_offset)(ep, act->to_addr, &name2);
++    if (!ok) name2 = "???";
++ 
++    VG_(message)(Vg_DebugMsg, "%s0x%08lx (%-20s) %s-> (%04d.%d) 0x%08lx %s\n", 
++Index: valgrind-3.13.0/coregrind/m_sbprofile.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_sbprofile.c
+++++ valgrind-3.13.0/coregrind/m_sbprofile.c
++@@ -74,6 +74,9 @@ void show_SB_profile ( const SBProfEntry
++ 
++    VG_(printf)("Total score = %'llu\n\n", score_total);
++ 
+++   // FIXME JRS 28 July 2017: this is probably not right in general
+++   DiEpoch ep = VG_(current_DiEpoch)();
+++
++    /* Print an initial per-block summary. */
++    VG_(printf)("rank  ---cumulative---      -----self-----\n");
++    score_cumul = 0;
++@@ -84,7 +87,7 @@ void show_SB_profile ( const SBProfEntry
++          continue;
++ 
++       const HChar *name;
++-      VG_(get_fnname_w_offset)(tops[r].addr, &name);
+++      VG_(get_fnname_w_offset)(ep, tops[r].addr, &name);
++ 
++       score_here = tops[r].score;
++       score_cumul += score_here;
++@@ -123,7 +126,7 @@ void show_SB_profile ( const SBProfEntry
++             continue;
++ 
++          const HChar *name;
++-         VG_(get_fnname_w_offset)(tops[r].addr, &name);
+++         VG_(get_fnname_w_offset)(ep, tops[r].addr, &name);
++ 
++          score_here = tops[r].score;
++          score_cumul += score_here;
++@@ -159,7 +162,7 @@ void show_SB_profile ( const SBProfEntry
++             continue;
++ 
++          const HChar *name;
++-         VG_(get_fnname_w_offset)(tops[r].addr, &name);
+++         VG_(get_fnname_w_offset)(ep, tops[r].addr, &name);
++ 
++          score_here = tops[r].score;
++ 
++Index: valgrind-3.13.0/coregrind/m_scheduler/scheduler.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_scheduler/scheduler.c
+++++ valgrind-3.13.0/coregrind/m_scheduler/scheduler.c
++@@ -2037,8 +2037,12 @@ void do_client_request ( ThreadId tid )
++ 
++          VG_(memset)(buf64, 0, 64);
++          UInt linenum = 0;
+++
+++         // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++         const DiEpoch ep = VG_(current_DiEpoch)();
+++
++          Bool ok = VG_(get_filename_linenum)(
++-                      ip, &buf, NULL, &linenum
+++                      ep, ip, &buf, NULL, &linenum
++                    );
++          if (ok) {
++             /* For backward compatibility truncate the filename to
++Index: valgrind-3.13.0/coregrind/m_signals.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_signals.c
+++++ valgrind-3.13.0/coregrind/m_signals.c
++@@ -1878,7 +1878,7 @@ static void default_action(const vki_sig
++                       : VG_(record_depth_1_ExeContext)( tid,
++                                                         first_ip_delta );
++          vg_assert(ec);
++-         VG_(pp_ExeContext)( ec );
+++         VG_(pp_ExeContextAndEpoch)( VG_(tag_EC_with_current_epoch)(ec) );
++       }
++       if (sigNo == VKI_SIGSEGV 
++           && is_signal_from_kernel(tid, sigNo, info->si_code)
++Index: valgrind-3.13.0/coregrind/m_stacktrace.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_stacktrace.c
+++++ valgrind-3.13.0/coregrind/m_stacktrace.c
++@@ -446,6 +446,7 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId
++       /* And, similarly, try for MSVC FPO unwind info. */
++       if (FPO_info_present
++           && VG_(use_FPO_info)( &uregs.xip, &uregs.xsp, &uregs.xbp,
+++                                VG_(current_DiEpoch)(),
++                                 fp_min, fp_max ) ) {
++          if (debug) unwind_case = "MS";
++          if (do_stats) stats.MS++;
++@@ -1539,12 +1540,12 @@ UInt VG_(get_StackTrace) ( ThreadId tid,
++                                        stack_highest_byte);
++ }
++ 
++-static void printIpDesc(UInt n, Addr ip, void* uu_opaque)
+++static void printIpDesc(UInt n, DiEpoch ep, Addr ip, void* uu_opaque)
++ {
++-   InlIPCursor *iipc = VG_(new_IIPC)(ip);
+++   InlIPCursor *iipc = VG_(new_IIPC)(ep, ip);
++ 
++    do {
++-      const HChar *buf = VG_(describe_IP)(ip, iipc);
+++      const HChar *buf = VG_(describe_IP)(ep, ip, iipc);
++       if (VG_(clo_xml)) {
++          VG_(printf_xml)("    %s\n", buf);
++       } else {
++@@ -1558,14 +1559,14 @@ static void printIpDesc(UInt n, Addr ip,
++ }
++ 
++ /* Print a StackTrace. */
++-void VG_(pp_StackTrace) ( StackTrace ips, UInt n_ips )
+++void VG_(pp_StackTrace) ( DiEpoch ep, StackTrace ips, UInt n_ips )
++ {
++    vg_assert( n_ips > 0 );
++ 
++    if (VG_(clo_xml))
++       VG_(printf_xml)("  <stack>\n");
++ 
++-   VG_(apply_StackTrace)( printIpDesc, NULL, ips, n_ips );
+++   VG_(apply_StackTrace)( printIpDesc, NULL, ep, ips, n_ips );
++ 
++    if (VG_(clo_xml))
++       VG_(printf_xml)("  </stack>\n");
++@@ -1580,13 +1581,13 @@ void VG_(get_and_pp_StackTrace) ( Thread
++                             NULL/*array to dump SP values in*/,
++                             NULL/*array to dump FP values in*/,
++                             0/*first_ip_delta*/);
++-   VG_(pp_StackTrace)(ips, n_ips);
+++   VG_(pp_StackTrace)(VG_(current_DiEpoch)(), ips, n_ips);
++ }
++ 
++ void VG_(apply_StackTrace)(
++-        void(*action)(UInt n, Addr ip, void* opaque),
+++        void(*action)(UInt n, DiEpoch ep, Addr ip, void* opaque),
++         void* opaque,
++-        StackTrace ips, UInt n_ips
+++        DiEpoch ep, StackTrace ips, UInt n_ips
++      )
++ {
++    Int i;
++@@ -1597,7 +1598,7 @@ void VG_(apply_StackTrace)(
++       // or the last appearance of a below main function.
++       // Then decrease n_ips so as to not call action for the below main
++       for (i = n_ips - 1; i >= 0; i--) {
++-         Vg_FnNameKind kind = VG_(get_fnname_kind_from_IP)(ips[i]);
+++         Vg_FnNameKind kind = VG_(get_fnname_kind_from_IP)(ep, ips[i]);
++          if (Vg_FnNameMain == kind || Vg_FnNameBelowMain == kind)
++             n_ips = i + 1;
++          if (Vg_FnNameMain == kind)
++@@ -1607,7 +1608,7 @@ void VG_(apply_StackTrace)(
++ 
++    for (i = 0; i < n_ips; i++)
++       // Act on the ip
++-      action(i, ips[i], opaque);
+++      action(i, ep, ips[i], opaque);
++ }
++ 
++ 
++Index: valgrind-3.13.0/coregrind/m_syswrap/syswrap-generic.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_syswrap/syswrap-generic.c
+++++ valgrind-3.13.0/coregrind/m_syswrap/syswrap-generic.c
++@@ -540,7 +540,8 @@ typedef struct OpenFd
++ {
++    Int fd;                        /* The file descriptor */
++    HChar *pathname;               /* NULL if not a regular file or unknown */
++-   ExeContext *where;             /* NULL if inherited from parent */
+++   ExeContextAndEpoch where;      /* VG_(null_ExeContextAndEpoch)
+++                                     if inherited from parent */
++    struct OpenFd *next, *prev;
++ } OpenFd;
++ 
++@@ -614,7 +615,10 @@ void ML_(record_fd_open_with_given_name)
++ 
++    i->fd = fd;
++    i->pathname = VG_(strdup)("syswrap.rfdowgn.2", pathname);
++-   i->where = (tid == -1) ? NULL : VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
+++   i->where = (tid == -1)
+++                 ? VG_(null_ExeContextAndEpoch)()
+++                 : VG_(tag_EC_with_current_epoch)(
+++                      VG_(record_ExeContext)(tid, 0/*first_ip_delta*/));
++ }
++ 
++ // Record opening of an fd, and find its name.
++@@ -846,8 +850,8 @@ void VG_(show_open_fds) (const HChar* wh
++          }
++       }
++ 
++-      if(i->where) {
++-         VG_(pp_ExeContext)(i->where);
+++      if (!VG_(is_null_ExeContextAndEpoch)(i->where)) {
+++         VG_(pp_ExeContextAndEpoch)(i->where);
++          VG_(message)(Vg_UserMsg, "\n");
++       } else {
++          VG_(message)(Vg_UserMsg, "   <inherited from parent>\n");
++Index: valgrind-3.13.0/coregrind/m_tooliface.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_tooliface.c
+++++ valgrind-3.13.0/coregrind/m_tooliface.c
++@@ -323,7 +323,7 @@ void VG_(needs_print_stats) (
++ }
++ 
++ void VG_(needs_info_location) (
++-   void (*info_location)(Addr)
+++   void (*info_location)(DiEpoch, Addr)
++ )
++ {
++    VG_(needs).info_location = True;
++Index: valgrind-3.13.0/coregrind/m_translate.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_translate.c
+++++ valgrind-3.13.0/coregrind/m_translate.c
++@@ -1527,24 +1527,25 @@ Bool VG_(translate) ( ThreadId tid,
++       Bool ok;
++       const HChar *buf;
++       const HChar *name2;
+++      const DiEpoch ep = VG_(current_DiEpoch)();
++ 
++       /* Try also to get the soname (not the filename) of the "from"
++          object.  This makes it much easier to debug redirection
++          problems. */
++       const HChar* nraddr_soname = "???";
++-      DebugInfo*   nraddr_di     = VG_(find_DebugInfo)(nraddr);
+++      DebugInfo*   nraddr_di     = VG_(find_DebugInfo)(ep, nraddr);
++       if (nraddr_di) {
++          const HChar* t = VG_(DebugInfo_get_soname)(nraddr_di);
++          if (t)
++             nraddr_soname = t;
++       }
++ 
++-      ok = VG_(get_fnname_w_offset)(nraddr, &buf);
+++      ok = VG_(get_fnname_w_offset)(ep, nraddr, &buf);
++       if (!ok) buf = "???";
++       // Stash away name1
++       HChar name1[VG_(strlen)(buf) + 1];
++       VG_(strcpy)(name1, buf);
++-      ok = VG_(get_fnname_w_offset)(addr, &name2);
+++      ok = VG_(get_fnname_w_offset)(ep, addr, &name2);
++       if (!ok) name2 = "???";
++ 
++       VG_(message)(Vg_DebugMsg, 
++@@ -1561,7 +1562,8 @@ Bool VG_(translate) ( ThreadId tid,
++    if (VG_(clo_trace_flags) || debugging_translation) {
++       const HChar* objname = "UNKNOWN_OBJECT";
++       OffT         objoff  = 0;
++-      DebugInfo*   di      = VG_(find_DebugInfo)( addr );
+++      const DiEpoch ep     = VG_(current_DiEpoch)();
+++      DebugInfo*   di      = VG_(find_DebugInfo)( ep, addr );
++       if (di) {
++          objname = VG_(DebugInfo_get_filename)(di);
++          objoff  = addr - VG_(DebugInfo_get_text_bias)(di);
++@@ -1569,7 +1571,7 @@ Bool VG_(translate) ( ThreadId tid,
++       vg_assert(objname);
++  
++       const HChar *fnname;
++-      Bool ok = VG_(get_fnname_w_offset)(addr, &fnname);
+++      Bool ok = VG_(get_fnname_w_offset)(ep, addr, &fnname);
++       if (!ok) fnname = "UNKNOWN_FUNCTION";
++       VG_(printf)(
++          "==== SB %u (evchecks %llu) [tid %u] 0x%lx %s %s%c0x%lx\n",
++Index: valgrind-3.13.0/coregrind/m_xtree.c
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/m_xtree.c
+++++ valgrind-3.13.0/coregrind/m_xtree.c
++@@ -438,6 +438,9 @@ void VG_(XT_callgrind_print)
++    const HChar* filename_dir;
++    const HChar* filename_name;
++ 
+++   // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
++    if (fp == NULL)
++       return;
++ 
++@@ -501,7 +504,7 @@ void VG_(XT_callgrind_print)
++       // the strings  called_filename/called_fnname.
++ #define CALLED_FLF(n)                                                   \
++       if ((n) < 0                                                       \
++-          || !VG_(get_filename_linenum)(ips[(n)],                       \
+++          || !VG_(get_filename_linenum)(ep, ips[(n)],                   \
++                                         &filename_name,                 \
++                                         &filename_dir,                  \
++                                         &called_linenum)) {             \
++@@ -509,7 +512,7 @@ void VG_(XT_callgrind_print)
++          called_linenum = 0;                                            \
++       }                                                                 \
++       if ((n) < 0                                                       \
++-          || !VG_(get_fnname)(ips[(n)], &called_fnname)) {              \
+++          || !VG_(get_fnname)(ep, ips[(n)], &called_fnname)) {          \
++          called_fnname = "UnknownFn???";                                \
++       }                                                                 \
++       {                                                                 \
++@@ -554,7 +557,9 @@ void VG_(XT_callgrind_print)
++ 
++          if (0) {
++             VG_(printf)("entry img %s\n", img);
++-            VG_(pp_ExeContext)(xe->ec);
+++            // JRS 27 July 2017: it may be a hack to use the current epoch
+++            // here.  I don't know.
+++            VG_(pp_ExeContextAndEpoch)(VG_(tag_EC_with_current_epoch)(xe->ec));
++             VG_(printf)("\n");
++          }
++          xt->add_data_fn(xt->tmp_data, VG_(indexXA)(xt->data, xecu));
++@@ -762,11 +767,14 @@ static void ms_output_group (VgFile* fp,
++    ms_make_groups(depth+1, group->ms_ec, group->n_ec, sig_sz,
++                   &n_groups, &groups);
++ 
+++   // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
++    FP("%*s" "n%u: %ld %s\n", 
++       depth + 1, "",
++       n_groups, 
++       group->total,
++-      VG_(describe_IP)(group->ms_ec->ips[depth] - 1, NULL));
+++      VG_(describe_IP)(ep, group->ms_ec->ips[depth] - 1, NULL));
++    /* XTREE??? Massif original code removes 1 to get the IP description. I am
++       wondering if this is not something that predates revision r8818,
++       which introduced a -1 in the stack unwind (see m_stacktrace.c)
++@@ -963,6 +971,10 @@ Int VG_(XT_offset_main_or_below_main)(Ad
++       from there.
++       If no main is found, we will then do a search for main or
++       below main function till the top. */
+++
+++   // FIXME JRS 28 July 2017: HACK!  Is this correct?
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
++    static Int deepest_main = 0;
++    Vg_FnNameKind kind = Vg_FnNameNormal;
++    Int mbm = n_ips - 1; // Position of deepest main or below main.
++@@ -972,7 +984,7 @@ Int VG_(XT_offset_main_or_below_main)(Ad
++    for (i = n_ips - 1 - deepest_main;
++         i < n_ips;
++         i++) {
++-      mbmkind = VG_(get_fnname_kind_from_IP)(ips[i]);
+++      mbmkind = VG_(get_fnname_kind_from_IP)(ep, ips[i]);
++       if (mbmkind != Vg_FnNameNormal) {
++          mbm = i;
++          break;
++@@ -983,7 +995,7 @@ Int VG_(XT_offset_main_or_below_main)(Ad
++    for (i = mbm - 1;
++         i >= 0 && mbmkind != Vg_FnNameMain;
++         i--) {
++-      kind = VG_(get_fnname_kind_from_IP)(ips[i]);
+++      kind = VG_(get_fnname_kind_from_IP)(ep, ips[i]);
++       if (kind != Vg_FnNameNormal) {
++          mbm = i;
++          mbmkind = kind;
++Index: valgrind-3.13.0/coregrind/pub_core_debuginfo.h
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/pub_core_debuginfo.h
+++++ valgrind-3.13.0/coregrind/pub_core_debuginfo.h
++@@ -86,20 +86,21 @@ extern void VG_(di_discard_ALL_debuginfo
++  * It should only be used in cases where the names of interest will have
++  * particular (ie. non-mangled) forms, or the mangled form is acceptable. */
++ extern
++-Bool VG_(get_fnname_raw) ( Addr a, const HChar** buf );
+++Bool VG_(get_fnname_raw) ( DiEpoch ep, Addr a, const HChar** buf );
++ 
++ /* Like VG_(get_fnname), but without C++ demangling.  (But it does
++  Z-demangling and below-main renaming.)
++  iipc argument: same usage as in VG_(describe_IP) in pub_tool_debuginfo.h. */
++ extern
++-Bool VG_(get_fnname_no_cxx_demangle) ( Addr a, const HChar** buf,
+++Bool VG_(get_fnname_no_cxx_demangle) ( DiEpoch ep, Addr a, const HChar** buf,
++                                        const InlIPCursor* iipc );
++ 
++ /* mips-linux only: find the offset of current address. This is needed for 
++    stack unwinding for MIPS.
++ */
++ extern
++-Bool VG_(get_inst_offset_in_function)( Addr a, /*OUT*/PtrdiffT* offset );
+++Bool VG_(get_inst_offset_in_function)( DiEpoch ep, Addr a,
+++                                       /*OUT*/PtrdiffT* offset );
++ 
++ 
++ /* Use DWARF2/3 CFA information to do one step of stack unwinding.
++@@ -158,6 +159,7 @@ extern Bool VG_(FPO_info_present)(void);
++ extern Bool VG_(use_FPO_info) ( /*MOD*/Addr* ipP,
++                                 /*MOD*/Addr* spP,
++                                 /*MOD*/Addr* fpP,
+++                                DiEpoch ep,
++                                 Addr min_accessible,
++                                 Addr max_accessible );
++ 
++@@ -217,7 +219,7 @@ void VG_(DebugInfo_syms_getidx)  ( const
++ /* ppc64-linux only: find the TOC pointer (R2 value) that should be in
++    force at the entry point address of the function containing
++    guest_code_addr.  Returns 0 if not known. */
++-extern Addr VG_(get_tocptr) ( Addr guest_code_addr );
+++extern Addr VG_(get_tocptr) ( DiEpoch ep, Addr guest_code_addr );
++ 
++ /* Map a function name to its SymAVMAs.  Is done by
++    sequential search of all symbol tables, so is very slow.  To
++@@ -227,7 +229,8 @@ extern Addr VG_(get_tocptr) ( Addr guest
++    platforms, a symbol is deemed to be found only if it has a nonzero
++    TOC pointer.  */
++ extern
++-Bool VG_(lookup_symbol_SLOW)(const HChar* sopatt, const HChar* name,
+++Bool VG_(lookup_symbol_SLOW)(DiEpoch ep, 
+++                             const HChar* sopatt, const HChar* name,
++                              SymAVMAs* avmas);
++ 
++ #endif   // __PUB_CORE_DEBUGINFO_H
++Index: valgrind-3.13.0/coregrind/pub_core_tooliface.h
++===================================================================
++--- valgrind-3.13.0.orig/coregrind/pub_core_tooliface.h
+++++ valgrind-3.13.0/coregrind/pub_core_tooliface.h
++@@ -156,7 +156,7 @@ typedef struct {
++    void (*tool_print_stats)(void);
++ 
++    // VG_(needs).info_location
++-   void (*tool_info_location)(Addr a);
+++   void (*tool_info_location)(DiEpoch ep, Addr a);
++ 
++    // VG_(needs).malloc_replacement
++    void* (*tool_malloc)              (ThreadId, SizeT);
++Index: valgrind-3.13.0/docs/xml/manual-core.xml
++===================================================================
++--- valgrind-3.13.0.orig/docs/xml/manual-core.xml
+++++ valgrind-3.13.0/docs/xml/manual-core.xml
++@@ -1219,6 +1219,19 @@ that can report errors, e.g. Memcheck, b
++     </listitem>
++   </varlistentry>
++ 
+++  <varlistentry id="opt.keep-debuginfo" xreflabel="--keep-debuginfo">
+++    <term>
+++      <option><![CDATA[--keep-debuginfo=<yes|no> [default: no] ]]></option>
+++    </term>
+++    <listitem>
+++      <para>When enabled, keep symbols and all other debuginfo for unloaded
+++      code. This allows stack traces for memory leaks to include file/line
+++      info for code that has been dlclose'd (or similar).  Be careful with
+++      this, since it can lead to unbounded memory use for programs which
+++      repeatedly load and unload shard objects.</para>
+++    </listitem>
+++  </varlistentry>
+++  
++   <varlistentry id="opt.show-below-main" xreflabel="--show-below-main">
++     <term>
++       <option><![CDATA[--show-below-main=<yes|no> [default: no] ]]></option>
++Index: valgrind-3.13.0/drd/drd_error.c
++===================================================================
++--- valgrind-3.13.0.orig/drd/drd_error.c
+++++ valgrind-3.13.0/drd/drd_error.c
++@@ -139,12 +139,14 @@ static void first_observed(const Addr ob
++                           "    <what>%pS</what>\n"
++                           "    <address>0x%lx</address>\n",
++                           DRD_(clientobj_type_name)(cl->any.type), obj);
++-         VG_(pp_ExeContext)(cl->any.first_observed_at);
+++         VG_(pp_ExeContextAndEpoch)(
+++            VG_(tag_EC_with_current_epoch)(cl->any.first_observed_at));
++          print_err_detail("  </first_observed_at>\n");
++       } else {
++          print_err_detail("%s 0x%lx was first observed at:\n",
++                           DRD_(clientobj_type_name)(cl->any.type), obj);
++-         VG_(pp_ExeContext)(cl->any.first_observed_at);
+++         VG_(pp_ExeContextAndEpoch)(
+++            VG_(tag_EC_with_current_epoch)(cl->any.first_observed_at));
++       }
++    }
++ }
++@@ -161,6 +163,7 @@ void drd_report_data_race(const Error* c
++    const HChar* const indent = xml ? "  " : "";
++    AddrInfo ai;
++ 
+++   DiEpoch ep = VG_(current_DiEpoch)();
++    XArray* /* of HChar */ descr1
++       = VG_(newXA)( VG_(malloc), "drd.error.drdr2.1",
++                     VG_(free), sizeof(HChar) );
++@@ -172,7 +175,7 @@ void drd_report_data_race(const Error* c
++    tl_assert(dri->addr);
++    tl_assert(dri->size > 0);
++ 
++-   (void) VG_(get_data_description)(descr1, descr2, dri->addr);
+++   (void) VG_(get_data_description)(descr1, descr2, ep, dri->addr);
++    /* If there's nothing in descr1/2, free them.  Why is it safe to
++       VG_(indexXA) at zero here?  Because VG_(get_data_description)
++       guarantees to zero terminate descr1/2 regardless of the outcome
++@@ -202,7 +205,7 @@ void drd_report_data_race(const Error* c
++                     what_prefix, dri->access_type == eStore ? "store" : "load",
++                     dri->tid, dri->addr, dri->size, what_suffix);
++ 
++-   VG_(pp_ExeContext)(VG_(get_error_where)(err));
+++   VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(err));
++    if (descr1 != NULL) {
++       print_err_detail("%s%s\n", indent, (HChar*)VG_(indexXA)(descr1, 0));
++       if (descr2 != NULL)
++@@ -216,7 +219,7 @@ void drd_report_data_race(const Error* c
++          print_err_detail("  <allocation_context>\n");
++       else
++          print_err_detail(" Allocation context:\n");
++-      VG_(pp_ExeContext)(ai.lastchange);
+++      VG_(pp_ExeContextAndEpoch)(mk_ExeContextAndEpoch(ai.lastchange, ep));
++       if (xml)
++          print_err_detail("  </allocation_context>\n");
++    } else {
++@@ -322,7 +325,7 @@ static void drd_tool_error_pp(const Erro
++          print_err_detail("%sThe object at address 0x%lx is not a mutex.%s\n",
++                           what_prefix, p->mutex, what_suffix);
++       }
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       first_observed(p->mutex);
++       break;
++    }
++@@ -330,7 +333,7 @@ static void drd_tool_error_pp(const Erro
++       CondErrInfo* cdei =(CondErrInfo*)(VG_(get_error_extra)(e));
++       print_err_detail("%s%s: cond 0x%lx%s\n", what_prefix,
++                        VG_(get_error_string)(e), cdei->cond, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       first_observed(cdei->cond);
++       break;
++    }
++@@ -339,7 +342,7 @@ static void drd_tool_error_pp(const Erro
++       print_err_detail("%s%s: cond 0x%lx, mutex 0x%lx locked by thread %u%s\n",
++                        what_prefix, VG_(get_error_string)(e), cdi->cond,
++                        cdi->mutex, cdi->owner, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       first_observed(cdi->mutex);
++       break;
++    }
++@@ -349,7 +352,7 @@ static void drd_tool_error_pp(const Erro
++                        " has been signaled but the associated mutex 0x%lx is"
++                        " not locked by the signalling thread.%s\n",
++                        what_prefix, cei->cond, cei->mutex, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       first_observed(cei->cond);
++       first_observed(cei->mutex);
++       break;
++@@ -359,7 +362,7 @@ static void drd_tool_error_pp(const Erro
++       print_err_detail("%s%s: condition variable 0x%lx, mutexes 0x%lx and"
++                        " 0x%lx%s\n", what_prefix, VG_(get_error_string)(e),
++                        cwei->cond, cwei->mutex1, cwei->mutex2, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       first_observed(cwei->cond);
++       first_observed(cwei->mutex1);
++       first_observed(cwei->mutex2);
++@@ -370,7 +373,7 @@ static void drd_tool_error_pp(const Erro
++       tl_assert(sei);
++       print_err_detail("%s%s: semaphore 0x%lx%s\n", what_prefix,
++                        VG_(get_error_string)(e), sei->semaphore, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       first_observed(sei->semaphore);
++       break;
++    }
++@@ -379,13 +382,14 @@ static void drd_tool_error_pp(const Erro
++       tl_assert(bei);
++       print_err_detail("%s%s: barrier 0x%lx%s\n", what_prefix,
++                        VG_(get_error_string)(e), bei->barrier, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       if (bei->other_context) {
++          if (xml)
++             print_err_detail("  <confl_wait_call>\n");
++          print_err_detail("%sConflicting wait call by thread %u:%s\n",
++                           what_prefix, bei->other_tid, what_suffix);
++-         VG_(pp_ExeContext)(bei->other_context);
+++         VG_(pp_ExeContextAndEpoch)(
+++            VG_(tag_EC_with_current_epoch)(bei->other_context));
++          if (xml)
++             print_err_detail("  </confl_wait_call>\n");
++       }
++@@ -397,7 +401,7 @@ static void drd_tool_error_pp(const Erro
++       tl_assert(p);
++       print_err_detail("%s%s: rwlock 0x%lx.%s\n", what_prefix,
++                        VG_(get_error_string)(e), p->rwlock, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       first_observed(p->rwlock);
++       break;
++    }
++@@ -409,14 +413,15 @@ static void drd_tool_error_pp(const Erro
++          print_err_detail("  <acquired_at>\n");
++       else
++          print_err_detail("Acquired at:\n");
++-      VG_(pp_ExeContext)(p->acquired_at);
+++      VG_(pp_ExeContextAndEpoch)(
+++         VG_(tag_EC_with_current_epoch)(p->acquired_at));
++       if (xml)
++          print_err_detail("  </acquired_at>\n");
++       print_err_detail("%sLock on %s 0x%lx was held during %u ms"
++                        " (threshold: %u ms).%s\n", what_prefix,
++                        VG_(get_error_string)(e), p->synchronization_object,
++                        p->hold_time_ms, p->threshold_ms, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       first_observed(p->synchronization_object);
++       break;
++    }
++@@ -424,7 +429,7 @@ static void drd_tool_error_pp(const Erro
++       GenericErrInfo* gei = (GenericErrInfo*)(VG_(get_error_extra)(e));
++       print_err_detail("%s%s%s\n", what_prefix, VG_(get_error_string)(e),
++                        what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       if (gei->addr)
++ 	 first_observed(gei->addr);
++       break;
++@@ -433,7 +438,7 @@ static void drd_tool_error_pp(const Erro
++       InvalidThreadIdInfo* iti =(InvalidThreadIdInfo*)(VG_(get_error_extra)(e));
++       print_err_detail("%s%s 0x%llx%s\n", what_prefix, VG_(get_error_string)(e),
++                        iti->ptid, what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       break;
++    }
++    case UnimpHgClReq: {
++@@ -441,7 +446,7 @@ static void drd_tool_error_pp(const Erro
++       print_err_detail("%sThe annotation macro %s has not yet been implemented"
++                        " in %ps%s\n", what_prefix, uicr->descr,
++                        "<valgrind/helgrind.h>", what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       break;
++    }
++    case UnimpDrdClReq: {
++@@ -449,13 +454,13 @@ static void drd_tool_error_pp(const Erro
++       print_err_detail("%sThe annotation macro %s has not yet been implemented"
++                        " in %ps%s\n", what_prefix, uicr->descr,
++                        "<valgrind/drd.h>", what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       break;
++    }
++    default:
++       print_err_detail("%s%s%s\n", what_prefix, VG_(get_error_string)(e),
++                        what_suffix);
++-      VG_(pp_ExeContext)(VG_(get_error_where)(e));
+++      VG_(pp_ExeContextAndEpoch)(VG_(get_error_where)(e));
++       break;
++    }
++ }
++Index: valgrind-3.13.0/drd/drd_thread.c
++===================================================================
++--- valgrind-3.13.0.orig/drd/drd_thread.c
+++++ valgrind-3.13.0/drd/drd_thread.c
++@@ -1338,7 +1338,8 @@ static void show_call_stack(const DrdThr
++ 
++    if (vg_tid != VG_INVALID_THREADID) {
++       if (callstack)
++-         VG_(pp_ExeContext)(callstack);
+++         VG_(pp_ExeContextAndEpoch)(
+++            VG_(tag_EC_with_current_epoch)(callstack));
++       else
++          VG_(get_and_pp_StackTrace)(vg_tid, VG_(clo_backtrace_size));
++    } else {
++Index: valgrind-3.13.0/exp-bbv/bbv_main.c
++===================================================================
++--- valgrind-3.13.0.orig/exp-bbv/bbv_main.c
+++++ valgrind-3.13.0/exp-bbv/bbv_main.c
++@@ -346,6 +346,7 @@ static IRSB* bbv_instrument ( VgCallback
++    IRDirty  *di;
++    IRExpr   **argv, *arg1;
++    Int      regparms,opcode_type;
+++   DiEpoch  ep = VG_(current_DiEpoch)();
++ 
++       /* We don't handle a host/guest word size mismatch */
++    if (gWordTy != hWordTy) {
++@@ -392,8 +393,8 @@ static IRSB* bbv_instrument ( VgCallback
++       block_num++;
++          /* get function name and entry point information */
++       const HChar *fn_name;
++-      VG_(get_fnname)(origAddr, &fn_name);
++-      bbInfo->is_entry=VG_(get_fnname_if_entry)(origAddr, &fn_name);
+++      VG_(get_fnname)(ep, origAddr, &fn_name);
+++      bbInfo->is_entry=VG_(get_fnname_if_entry)(ep, origAddr, &fn_name);
++       bbInfo->fn_name =VG_(strdup)("bbv_strings", fn_name);
++          /* insert structure into table */
++       VG_(OSetGen_Insert)( instr_info_table, bbInfo );
++Index: valgrind-3.13.0/exp-dhat/dh_main.c
++===================================================================
++--- valgrind-3.13.0.orig/exp-dhat/dh_main.c
+++++ valgrind-3.13.0/exp-dhat/dh_main.c
++@@ -1146,7 +1146,7 @@ static void show_APInfo ( APInfo* api )
++              bufR, bufW,
++              api->n_reads, api->n_writes);
++ 
++-   VG_(pp_ExeContext)(api->ap);
+++   VG_(pp_ExeContextAndEpoch)(VG_(tag_EC_with_current_epoch)(api->ap));
++ 
++    if (api->histo && api->xsize_tag == Exactly) {
++       VG_(umsg)("\nAggregated access counts by offset:\n");
++Index: valgrind-3.13.0/exp-sgcheck/pc_common.c
++===================================================================
++--- valgrind-3.13.0.orig/exp-sgcheck/pc_common.c
+++++ valgrind-3.13.0/exp-sgcheck/pc_common.c
++@@ -322,7 +322,7 @@ void pc_pp_Error ( const Error* err )
++          emit( "  <what>Invalid %s of size %ld</what>\n",
++                xe->XE.SorG.sszB < 0 ? "write" : "read",
++                Word__abs(xe->XE.SorG.sszB) );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++          emit( "  <auxwhat>Address %#lx expected vs actual:</auxwhat>\n",
++                xe->XE.SorG.addr );
++@@ -336,7 +336,7 @@ void pc_pp_Error ( const Error* err )
++          emit( "Invalid %s of size %ld\n", 
++                xe->XE.SorG.sszB < 0 ? "write" : "read",
++                Word__abs(xe->XE.SorG.sszB) );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++          emit( " Address %#lx expected vs actual:\n", xe->XE.SorG.addr );
++          emit( " Expected: %s\n", &xe->XE.SorG.expect[0] );
++@@ -362,7 +362,7 @@ void pc_pp_Error ( const Error* err )
++             emit( "  <what>Invalid %s of size %ld</what>\n",
++                   readwrite(xe->XE.Heap.sszB),
++                   Word__abs(xe->XE.Heap.sszB) );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++             emit( "  <auxwhat>Address %#lx is not derived from "
++                   "any known block</auxwhat>\n", a );
++@@ -372,7 +372,7 @@ void pc_pp_Error ( const Error* err )
++             emit( "Invalid %s of size %ld\n",
++                   readwrite(xe->XE.Heap.sszB),
++                   Word__abs(xe->XE.Heap.sszB) );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++             emit( " Address %#lx is not derived from "
++                   "any known block\n", a );
++@@ -397,7 +397,7 @@ void pc_pp_Error ( const Error* err )
++                   how_invalid,
++                   readwrite(xe->XE.Heap.sszB),
++                   Word__abs(xe->XE.Heap.sszB) );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++             emit( "  <auxwhat>Address %#lx is %lu bytes %s "
++                      "the accessing pointer's</auxwhat>\n",
++@@ -406,7 +406,8 @@ void pc_pp_Error ( const Error* err )
++                      "a block of size %lu %s</auxwhat>\n",
++                   legit, Seg__size(vseg),
++                   Seg__is_freed(vseg) ? "free'd" : "alloc'd" );
++-            VG_(pp_ExeContext)(Seg__where(vseg));
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(Seg__where(vseg)));
++ 
++          } else {
++ 
++@@ -414,14 +415,15 @@ void pc_pp_Error ( const Error* err )
++                   how_invalid,
++                   readwrite(xe->XE.Heap.sszB),
++                   Word__abs(xe->XE.Heap.sszB) );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++             emit( " Address %#lx is %lu bytes %s the accessing pointer's\n",
++                   a, miss_size, place );
++             emit( " %slegitimate range, a block of size %lu %s\n",
++                   legit, Seg__size(vseg),
++                   Seg__is_freed(vseg) ? "free'd" : "alloc'd" );
++-            VG_(pp_ExeContext)(Seg__where(vseg));
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(Seg__where(vseg)));
++ 
++          }
++       }
++@@ -477,7 +479,7 @@ void pc_pp_Error ( const Error* err )
++ 
++          emit( "  <what>Invalid arguments to %s</what>\n",
++                xe->XE.Arith.opname );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++          if (seg1 != seg2) {
++             if (NONPTR == seg1) {
++@@ -488,7 +490,8 @@ void pc_pp_Error ( const Error* err )
++                emit( "  <auxwhat>First arg derived from address %#lx of "
++                      "%lu-byte block alloc'd</auxwhat>\n",
++                      Seg__addr(seg1), Seg__size(seg1) );
++-               VG_(pp_ExeContext)(Seg__where(seg1));
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(Seg__where(seg1)));
++             }
++             which = "Second arg";
++          } else {
++@@ -500,14 +503,15 @@ void pc_pp_Error ( const Error* err )
++             emit( "  <auxwhat>%s derived from address %#lx of "
++                   "%lu-byte block alloc'd</auxwhat>\n",
++                   which, Seg__addr(seg2), Seg__size(seg2) );
++-            VG_(pp_ExeContext)(Seg__where(seg2));
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(Seg__where(seg2)));
++          }
++ 
++       } else {
++ 
++          emit( "Invalid arguments to %s\n",
++                xe->XE.Arith.opname );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++          if (seg1 != seg2) {
++             if (NONPTR == seg1) {
++@@ -518,7 +522,8 @@ void pc_pp_Error ( const Error* err )
++                emit( " First arg derived from address %#lx of "
++                      "%lu-byte block alloc'd\n",
++                      Seg__addr(seg1), Seg__size(seg1) );
++-               VG_(pp_ExeContext)(Seg__where(seg1));
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(Seg__where(seg1)));
++             }
++             which = "Second arg";
++          } else {
++@@ -530,7 +535,8 @@ void pc_pp_Error ( const Error* err )
++             emit( " %s derived from address %#lx of "
++                   "%lu-byte block alloc'd\n",
++                   which, Seg__addr(seg2), Seg__size(seg2) );
++-            VG_(pp_ExeContext)(Seg__where(seg2));
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(Seg__where(seg2)));
++          }
++ 
++       }
++@@ -562,23 +568,25 @@ void pc_pp_Error ( const Error* err )
++ 
++             emit( "  <what>%s%s contains unaddressable byte(s)</what>\n",
++                   what, s );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++             emit( "  <auxwhat>Address %#lx is %lu bytes inside a "
++                   "%lu-byte block free'd</auxwhat>\n",
++                   lo, lo-Seg__addr(seglo), Seg__size(seglo) );
++-            VG_(pp_ExeContext)(Seg__where(seglo));
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(Seg__where(seglo)));
++ 
++          } else {
++ 
++             emit( " %s%s contains unaddressable byte(s)\n",
++                   what, s );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++             emit( " Address %#lx is %lu bytes inside a "
++                   "%lu-byte block free'd\n",
++                   lo, lo-Seg__addr(seglo), Seg__size(seglo) );
++-            VG_(pp_ExeContext)(Seg__where(seglo));
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(Seg__where(seglo)));
++ 
++          }
++ 
++@@ -589,7 +597,7 @@ void pc_pp_Error ( const Error* err )
++ 
++             emit( "  <what>%s%s is non-contiguous</what>\n",
++                   what, s );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++             if (UNKNOWN == seglo) {
++                emit( "  <auxwhat>First byte is "
++@@ -598,7 +606,8 @@ void pc_pp_Error ( const Error* err )
++                emit( "  <auxwhat>First byte (%#lx) is %lu bytes inside a "
++                      "%lu-byte block alloc'd</auxwhat>\n",
++                      lo, lo-Seg__addr(seglo), Seg__size(seglo) );
++-               VG_(pp_ExeContext)(Seg__where(seglo));
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(Seg__where(seglo)));
++             }
++    
++             if (UNKNOWN == seghi) {
++@@ -608,14 +617,15 @@ void pc_pp_Error ( const Error* err )
++                emit( "  <auxwhat>Last byte (%#lx) is %lu bytes inside a "
++                      "%lu-byte block alloc'd</auxwhat>\n",
++                      hi, hi-Seg__addr(seghi), Seg__size(seghi) );
++-               VG_(pp_ExeContext)(Seg__where(seghi));
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(Seg__where(seghi)));
++             }
++ 
++          } else {
++ 
++             emit( "%s%s is non-contiguous\n",
++                   what, s );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++    
++             if (UNKNOWN == seglo) {
++                emit( " First byte is not inside a known block\n" );
++@@ -623,7 +633,8 @@ void pc_pp_Error ( const Error* err )
++                emit( " First byte (%#lx) is %lu bytes inside a "
++                      "%lu-byte block alloc'd\n",
++                      lo, lo-Seg__addr(seglo), Seg__size(seglo) );
++-               VG_(pp_ExeContext)(Seg__where(seglo));
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(Seg__where(seglo)));
++             }
++    
++             if (UNKNOWN == seghi) {
++@@ -632,7 +643,8 @@ void pc_pp_Error ( const Error* err )
++                emit( " Last byte (%#lx) is %lu bytes inside a "
++                      "%lu-byte block alloc'd\n",
++                      hi, hi-Seg__addr(seghi), Seg__size(seghi) );
++-               VG_(pp_ExeContext)(Seg__where(seghi));
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(Seg__where(seghi)));
++             }
++ 
++          }
++@@ -650,6 +662,8 @@ void pc_pp_Error ( const Error* err )
++ UInt pc_update_Error_extra ( const Error* err )
++ {
++    XError *xe = (XError*)VG_(get_error_extra)(err);
+++   DiEpoch ep = VG_(get_error_where)(err).epoch;
+++
++    tl_assert(xe);
++    switch (xe->tag) {
++       case XE_SorG:
++@@ -675,7 +689,7 @@ UInt pc_update_Error_extra ( const Error
++          have_descr
++             = VG_(get_data_description)( xe->XE.Heap.descr1,
++                                          xe->XE.Heap.descr2,
++-                                         xe->XE.Heap.addr );
+++                                         ep, xe->XE.Heap.addr );
++ 
++          /* If there's nothing in descr1/2, free it.  Why is it safe to
++             to VG_(indexXA) at zero here?  Because
++@@ -699,7 +713,7 @@ UInt pc_update_Error_extra ( const Error
++          if (!have_descr) {
++             const HChar *name;
++             if (VG_(get_datasym_and_offset)(
++-                   xe->XE.Heap.addr, &name,
+++                   ep, xe->XE.Heap.addr, &name,
++                    &xe->XE.Heap.datasymoff )
++                ) {
++               xe->XE.Heap.datasym =
++Index: valgrind-3.13.0/exp-sgcheck/sg_main.c
++===================================================================
++--- valgrind-3.13.0.orig/exp-sgcheck/sg_main.c
+++++ valgrind-3.13.0/exp-sgcheck/sg_main.c
++@@ -1936,7 +1936,8 @@ void shadowStack_new_frame ( ThreadId ti
++      const HChar *fnname;
++      Bool ok;
++      Addr ip = ip_post_call_insn;
++-     ok = VG_(get_fnname_w_offset)( ip, &fnname );
+++     DiEpoch ep = VG_(current_DiEpoch)();
+++     ok = VG_(get_fnname_w_offset)( ep, ip, &fnname );
++      while (d > 0) {
++         VG_(printf)(" ");
++         d--;
++Index: valgrind-3.13.0/helgrind/hg_addrdescr.c
++===================================================================
++--- valgrind-3.13.0.orig/helgrind/hg_addrdescr.c
+++++ valgrind-3.13.0/helgrind/hg_addrdescr.c
++@@ -45,7 +45,7 @@
++ #include "hg_lock_n_thread.h"
++ #include "hg_addrdescr.h"            /* self */
++ 
++-void HG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai )
+++void HG_(describe_addr) ( DiEpoch ep, Addr a, /*OUT*/AddrInfo* ai )
++ {
++    tl_assert(ai->tag == Addr_Undescribed);
++ 
++@@ -75,13 +75,14 @@ void HG_(describe_addr) ( Addr a, /*OUT*
++       ai->Addr.Block.block_desc = "block";
++       ai->Addr.Block.block_szB  = hszB;
++       ai->Addr.Block.rwoffset   = (Word)(a) - (Word)(haddr);
++-      ai->Addr.Block.allocated_at = hctxt;
+++      ai->Addr.Block.allocated_at.ec = hctxt;
+++      ai->Addr.Block.allocated_at.epoch = ep;
++       VG_(initThreadInfo) (&ai->Addr.Block.alloc_tinfo);
++       ai->Addr.Block.alloc_tinfo.tnr = tnr;
++-      ai->Addr.Block.freed_at = VG_(null_ExeContext)();;
+++      ai->Addr.Block.freed_at = VG_(null_ExeContextAndEpoch)();
++    } else {
++       /* No block found. Search a non-heap block description. */
++-      VG_(describe_addr) (a, ai);
+++      VG_(describe_addr) (ep, a, ai);
++ 
++       /* In case ai contains a tid, set tnr to the corresponding helgrind
++          thread number. */
++@@ -100,14 +101,14 @@ void HG_(describe_addr) ( Addr a, /*OUT*
++    }
++ }
++ 
++-Bool HG_(get_and_pp_addrdescr) (Addr addr)
+++Bool HG_(get_and_pp_addrdescr) (DiEpoch ep, Addr addr)
++ {
++ 
++    Bool ret;
++    AddrInfo glai;
++ 
++    glai.tag = Addr_Undescribed;
++-   HG_(describe_addr) (addr, &glai);
+++   HG_(describe_addr) (ep, addr, &glai);
++    VG_(pp_addrinfo) (addr, &glai);
++    ret = glai.tag != Addr_Unknown;
++ 
++Index: valgrind-3.13.0/helgrind/hg_addrdescr.h
++===================================================================
++--- valgrind-3.13.0.orig/helgrind/hg_addrdescr.h
+++++ valgrind-3.13.0/helgrind/hg_addrdescr.h
++@@ -37,12 +37,12 @@
++    lock description, putting the result in ai.
++    This might allocate some memory in ai, to be cleared with
++    VG_(clear_addrinfo). */
++-extern void HG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai );
+++extern void HG_(describe_addr) ( DiEpoch ep, Addr a, /*OUT*/AddrInfo* ai );
++ 
++ /* Get a readable description of addr, then print it using HG_(pp_addrdescr)
++    using xml False and VG_(printf) to emit the characters.
++    Returns True if a description was found/printed, False otherwise. */
++-extern Bool HG_(get_and_pp_addrdescr) (Addr a);
+++extern Bool HG_(get_and_pp_addrdescr) (DiEpoch ep, Addr a);
++ 
++ /* For error creation/address description:
++    map 'data_addr' to a malloc'd chunk, if any.
++Index: valgrind-3.13.0/helgrind/hg_errors.c
++===================================================================
++--- valgrind-3.13.0.orig/helgrind/hg_errors.c
+++++ valgrind-3.13.0/helgrind/hg_errors.c
++@@ -421,7 +421,8 @@ UInt HG_(update_extra) ( const Error* er
++          VG_(printf)("HG_(update_extra): "
++                      "%d conflicting-event queries\n", xxx);
++ 
++-      HG_(describe_addr) (xe->XE.Race.data_addr, &xe->XE.Race.data_addrinfo);
+++      HG_(describe_addr) (VG_(get_error_where)(err).epoch,
+++                          xe->XE.Race.data_addr, &xe->XE.Race.data_addrinfo);
++ 
++       /* And poke around in the conflicting-event map, to see if we
++          can rustle up a plausible-looking conflicting memory access
++@@ -748,7 +749,8 @@ static Bool announce_one_thread ( Thread
++          VG_(printf_xml)("  <isrootthread></isrootthread>\n");
++       } else {
++          tl_assert(thr->created_at != NULL);
++-         VG_(pp_ExeContext)( thr->created_at );
+++         VG_(pp_ExeContextAndEpoch)(
+++            VG_(tag_EC_with_current_epoch)( thr->created_at ) );
++       }
++       VG_(printf_xml)("</announcethread>\n\n");
++ 
++@@ -767,7 +769,8 @@ static Bool announce_one_thread ( Thread
++          tl_assert(thr->created_at != NULL);
++          VG_(message)(Vg_UserMsg, "Thread #%d was created\n",
++                                   thr->errmsg_index);
++-         VG_(pp_ExeContext)( thr->created_at );
+++         VG_(pp_ExeContextAndEpoch)(
+++            VG_(tag_EC_with_current_epoch)( thr->created_at ) );
++       }
++       VG_(message)(Vg_UserMsg, "\n");
++ 
++@@ -789,19 +792,21 @@ static void announce_LockP ( Lock* lk )
++       if (lk->appeared_at) {
++          emit( "  <auxwhat>Lock at %p was first observed</auxwhat>\n",
++                (void*)lk );
++-         VG_(pp_ExeContext)( lk->appeared_at );
+++         VG_(pp_ExeContextAndEpoch)(
+++            VG_(tag_EC_with_current_epoch)( lk->appeared_at ) );
++       }
++ 
++    } else {
++       if (lk->appeared_at) {
++          VG_(umsg)( " Lock at %p was first observed\n",
++                     (void*)lk->guestaddr );
++-         VG_(pp_ExeContext)( lk->appeared_at );
+++         VG_(pp_ExeContextAndEpoch)(
+++            VG_(tag_EC_with_current_epoch)( lk->appeared_at ) );
++       } else {
++          VG_(umsg)( " Lock at %p : no stacktrace for first observation\n",
++                     (void*)lk->guestaddr );
++       }
++-      HG_(get_and_pp_addrdescr) (lk->guestaddr);
+++      HG_(get_and_pp_addrdescr) (VG_(current_DiEpoch)(), lk->guestaddr);
++       VG_(umsg)("\n");
++    }
++ }
++@@ -941,11 +946,12 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "    <hthreadid>%d</hthreadid>\n",
++                (Int)xe->XE.Misc.thr->errmsg_index );
++          emit( "  </xwhat>\n" );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          if (xe->XE.Misc.auxstr) {
++             emit("  <auxwhat>%s</auxwhat>\n", xe->XE.Misc.auxstr);
++             if (xe->XE.Misc.auxctx)
++-               VG_(pp_ExeContext)( xe->XE.Misc.auxctx );
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)( xe->XE.Misc.auxctx ));
++          }
++ 
++       } else {
++@@ -953,11 +959,12 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "Thread #%d: %s\n",
++                (Int)xe->XE.Misc.thr->errmsg_index,
++                xe->XE.Misc.errstr );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          if (xe->XE.Misc.auxstr) {
++             emit(" %s\n", xe->XE.Misc.auxstr);
++             if (xe->XE.Misc.auxctx)
++-               VG_(pp_ExeContext)( xe->XE.Misc.auxctx );
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)( xe->XE.Misc.auxctx ));
++          }
++ 
++       }
++@@ -978,17 +985,21 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "    <hthreadid>%d</hthreadid>\n",
++                (Int)xe->XE.LockOrder.thr->errmsg_index );
++          emit( "  </xwhat>\n" );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          if (xe->XE.LockOrder.shouldbe_earlier_ec
++              && xe->XE.LockOrder.shouldbe_later_ec) {
++             emit( "  <auxwhat>Required order was established by "
++                   "acquisition of lock at %p</auxwhat>\n",
++                   (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr );
++-            VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_earlier_ec );
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(
+++                  xe->XE.LockOrder.shouldbe_earlier_ec ));
++             emit( "  <auxwhat>followed by a later acquisition "
++                   "of lock at %p</auxwhat>\n",
++                   (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr );
++-            VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_later_ec );
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(
+++                  xe->XE.LockOrder.shouldbe_later_ec ));
++          }
++          announce_LockP ( xe->XE.LockOrder.shouldbe_earlier_lk );
++          announce_LockP ( xe->XE.LockOrder.shouldbe_later_lk );
++@@ -1004,25 +1015,31 @@ void HG_(pp_Error) ( const Error* err )
++                "acquisition of lock at %p\n",
++                (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr);
++          if (xe->XE.LockOrder.actual_earlier_ec) {
++-             VG_(pp_ExeContext)(xe->XE.LockOrder.actual_earlier_ec);
+++             VG_(pp_ExeContextAndEpoch)(
+++                VG_(tag_EC_with_current_epoch)(
+++                   xe->XE.LockOrder.actual_earlier_ec));
++          } else {
++             emit("   (stack unavailable)\n");
++          }
++          emit( "\n" );
++          emit(" followed by a later acquisition of lock at %p\n",
++               (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr);
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          if (xe->XE.LockOrder.shouldbe_earlier_ec
++              && xe->XE.LockOrder.shouldbe_later_ec) {
++             emit("\n");
++             emit( "Required order was established by "
++                   "acquisition of lock at %p\n",
++                   (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr );
++-            VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_earlier_ec );
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(
+++                   xe->XE.LockOrder.shouldbe_earlier_ec ));
++             emit( "\n" );
++             emit( " followed by a later acquisition of lock at %p\n",
++                   (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr );
++-            VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_later_ec );
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)(
+++                  xe->XE.LockOrder.shouldbe_later_ec ));
++          }
++          emit("\n");
++          announce_LockP ( xe->XE.LockOrder.shouldbe_earlier_lk );
++@@ -1048,7 +1065,7 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "  </xwhat>\n" );
++          emit( "  <what>with error code %ld (%s)</what>\n",
++                xe->XE.PthAPIerror.err, xe->XE.PthAPIerror.errstr );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++ 
++       } else {
++ 
++@@ -1057,7 +1074,7 @@ void HG_(pp_Error) ( const Error* err )
++                       xe->XE.PthAPIerror.fnname );
++          emit( "   with error code %ld (%s)\n",
++                xe->XE.PthAPIerror.err, xe->XE.PthAPIerror.errstr );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++ 
++       }
++ 
++@@ -1077,14 +1094,14 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "    <hthreadid>%d</hthreadid>\n",
++                (Int)xe->XE.UnlockBogus.thr->errmsg_index );
++          emit( "  </xwhat>\n" );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++ 
++       } else {
++ 
++          emit( "Thread #%d unlocked an invalid lock at %p\n",
++                (Int)xe->XE.UnlockBogus.thr->errmsg_index,
++                (void*)xe->XE.UnlockBogus.lock_ga );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++ 
++       }
++ 
++@@ -1109,7 +1126,7 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "    <hthreadid>%d</hthreadid>\n",
++                (Int)xe->XE.UnlockForeign.owner->errmsg_index );
++          emit( "  </xwhat>\n" );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          announce_LockP ( xe->XE.UnlockForeign.lock );
++ 
++       } else {
++@@ -1119,7 +1136,7 @@ void HG_(pp_Error) ( const Error* err )
++                (Int)xe->XE.UnlockForeign.thr->errmsg_index,
++                (void*)xe->XE.UnlockForeign.lock->guestaddr,
++                (Int)xe->XE.UnlockForeign.owner->errmsg_index );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          announce_LockP ( xe->XE.UnlockForeign.lock );
++ 
++       }
++@@ -1141,7 +1158,7 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "    <hthreadid>%d</hthreadid>\n",
++                (Int)xe->XE.UnlockUnlocked.thr->errmsg_index );
++          emit( "  </xwhat>\n" );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          announce_LockP ( xe->XE.UnlockUnlocked.lock);
++ 
++       } else {
++@@ -1149,7 +1166,7 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "Thread #%d unlocked a not-locked lock at %p\n",
++                (Int)xe->XE.UnlockUnlocked.thr->errmsg_index,
++                (void*)xe->XE.UnlockUnlocked.lock->guestaddr );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          announce_LockP ( xe->XE.UnlockUnlocked.lock);
++ 
++       }
++@@ -1179,7 +1196,7 @@ void HG_(pp_Error) ( const Error* err )
++          emit( "    <hthreadid>%d</hthreadid>\n",
++                (Int)xe->XE.Race.thr->errmsg_index );
++          emit( "  </xwhat>\n" );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++ 
++          if (xe->XE.Race.h2_ct) {
++             tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra
++@@ -1192,7 +1209,8 @@ void HG_(pp_Error) ( const Error* err )
++             emit( "    <hthreadid>%d</hthreadid>\n", 
++                   xe->XE.Race.h2_ct->errmsg_index);
++             emit("  </xauxwhat>\n");
++-            VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC );
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)( xe->XE.Race.h2_ct_accEC ));
++          }
++ 
++          if (xe->XE.Race.h1_ct) {
++@@ -1204,13 +1222,17 @@ void HG_(pp_Error) ( const Error* err )
++                   xe->XE.Race.h1_ct->errmsg_index );
++             emit("  </xauxwhat>\n");
++             if (xe->XE.Race.h1_ct_mbsegstartEC) {
++-               VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegstartEC );
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(
+++                     xe->XE.Race.h1_ct_mbsegstartEC ));
++             } else {
++                emit( "  <auxwhat>(the start of the thread)</auxwhat>\n" );
++             }
++             emit( "  <auxwhat>but before</auxwhat>\n" );
++             if (xe->XE.Race.h1_ct_mbsegendEC) {
++-               VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegendEC );
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(
+++                     xe->XE.Race.h1_ct_mbsegendEC ));
++             } else {
++                emit( "  <auxwhat>(the end of the thread)</auxwhat>\n" );
++             }
++@@ -1228,7 +1250,7 @@ void HG_(pp_Error) ( const Error* err )
++ 
++          tl_assert(xe->XE.Race.locksHeldW);
++          show_LockP_summary_textmode( xe->XE.Race.locksHeldW, "" );
++-         VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++         VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++ 
++          if (xe->XE.Race.h2_ct) {
++             tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra
++@@ -1240,7 +1262,8 @@ void HG_(pp_Error) ( const Error* err )
++                   xe->XE.Race.h2_ct_accSzB,
++                   xe->XE.Race.h2_ct->errmsg_index );
++             show_LockP_summary_textmode( xe->XE.Race.h2_ct_locksHeldW, "" );
++-            VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC );
+++            VG_(pp_ExeContextAndEpoch)(
+++               VG_(tag_EC_with_current_epoch)( xe->XE.Race.h2_ct_accEC ));
++          }
++ 
++          if (xe->XE.Race.h1_ct) {
++@@ -1248,13 +1271,17 @@ void HG_(pp_Error) ( const Error* err )
++                   "after\n",
++                   xe->XE.Race.h1_ct->errmsg_index );
++             if (xe->XE.Race.h1_ct_mbsegstartEC) {
++-               VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegstartEC );
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(
+++                     xe->XE.Race.h1_ct_mbsegstartEC ));
++             } else {
++                emit( "   (the start of the thread)\n" );
++             }
++             emit( " but before\n" );
++             if (xe->XE.Race.h1_ct_mbsegendEC) {
++-               VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegendEC );
+++               VG_(pp_ExeContextAndEpoch)(
+++                  VG_(tag_EC_with_current_epoch)(
+++                     xe->XE.Race.h1_ct_mbsegendEC ));
++             } else {
++                emit( "   (the end of the thread)\n" );
++             }
++@@ -1307,7 +1334,7 @@ void HG_(print_access) (StackTrace ips,
++       show_LockP_summary_textmode( locksHeldW_P, "" );
++       HG_(free) (locksHeldW_P);
++    }
++-   VG_(pp_StackTrace) (ips, n_ips);
+++   VG_(pp_StackTrace)( VG_(current_DiEpoch)(), ips, n_ips );
++    VG_(printf) ("\n");
++ }
++ 
++Index: valgrind-3.13.0/helgrind/hg_main.c
++===================================================================
++--- valgrind-3.13.0.orig/helgrind/hg_main.c
+++++ valgrind-3.13.0/helgrind/hg_main.c
++@@ -483,13 +483,14 @@ static void pp_Lock ( Int d, Lock* lk,
++                       Bool show_lock_addrdescr,
++                       Bool show_internal_data)
++ {
+++   DiEpoch ep = VG_(current_DiEpoch)();
++    space(d+0); 
++    if (show_internal_data)
++       VG_(printf)("Lock %p (ga %#lx) {\n", lk, lk->guestaddr);
++    else
++       VG_(printf)("Lock ga %#lx {\n", lk->guestaddr);
++    if (!show_lock_addrdescr 
++-       || !HG_(get_and_pp_addrdescr) ((Addr) lk->guestaddr))
+++       || !HG_(get_and_pp_addrdescr) (ep, (Addr) lk->guestaddr))
++       VG_(printf)("\n");
++       
++    if (sHOW_ADMIN) {
++@@ -4598,7 +4599,7 @@ static Bool is_in_dynamic_linker_shared_
++    DebugInfo* dinfo;
++    const HChar* soname;
++ 
++-   dinfo = VG_(find_DebugInfo)( ga );
+++   dinfo = VG_(find_DebugInfo)( VG_(current_DiEpoch)(), ga );
++    if (!dinfo) return False;
++ 
++    soname = VG_(DebugInfo_get_soname)(dinfo);
++@@ -5817,9 +5818,9 @@ static void hg_post_clo_init ( void )
++       VG_(XTMemory_Full_init)(VG_(XT_filter_1top_and_maybe_below_main));
++ }
++ 
++-static void hg_info_location (Addr a)
+++static void hg_info_location (DiEpoch ep, Addr a)
++ {
++-   (void) HG_(get_and_pp_addrdescr) (a);
+++   (void) HG_(get_and_pp_addrdescr) (ep, a);
++ }
++ 
++ static void hg_pre_clo_init ( void )
++Index: valgrind-3.13.0/helgrind/libhb_core.c
++===================================================================
++--- valgrind-3.13.0.orig/helgrind/libhb_core.c
+++++ valgrind-3.13.0/helgrind/libhb_core.c
++@@ -4095,7 +4095,7 @@ static void note_local_Kw_n_stack_for (
++       VG_(printf)("LOCAL Kw: thr %p,  Kw %llu,  ec %p\n",
++                   thr, pair.ull, pair.ec );
++    if (0)
++-      VG_(pp_ExeContext)(pair.ec);
+++      VG_(pp_ExeContextAndEpoch)(VG_(tag_EC_with_current_epoch)(pair.ec));
++ }
++ 
++ static Int cmp__ULong_n_EC__by_ULong ( const ULong_n_EC* pair1,
++Index: valgrind-3.13.0/include/pub_tool_addrinfo.h
++===================================================================
++--- valgrind-3.13.0.orig/include/pub_tool_addrinfo.h
+++++ valgrind-3.13.0/include/pub_tool_addrinfo.h
++@@ -135,6 +135,7 @@ struct _AddrInfo {
++       // (spoffset will be negative, as stacks are assumed growing down).
++       struct {
++          ThreadInfo tinfo;
+++         DiEpoch  epoch;
++          Addr     IP;
++          Int      frameNo;
++          StackPos stackPos;
++@@ -151,9 +152,9 @@ struct _AddrInfo {
++          const HChar* block_desc;   // "block","mempool","user-defined",arena
++          SizeT       block_szB;
++          PtrdiffT    rwoffset;
++-         ExeContext* allocated_at;  // might be null_ExeContext.
++-         ThreadInfo  alloc_tinfo;   // which thread did alloc this block.
++-         ExeContext* freed_at;      // might be null_ExeContext.
+++         ExeContextAndEpoch allocated_at; // might contain null_ExeContext.
+++         ThreadInfo         alloc_tinfo;  // which thread alloc'd this block.
+++         ExeContextAndEpoch freed_at;     // might contain null_ExeContext.
++       } Block;
++ 
++       // In a global .data symbol.  This holds
++@@ -204,7 +205,7 @@ struct _AddrInfo {
++    On entry, ai->tag must be equal to Addr_Undescribed.
++    This might allocate some memory, that can be cleared with
++    VG_(clear_addrinfo). */
++-extern void VG_(describe_addr) ( Addr a, /*OUT*/AddrInfo* ai );
+++extern void VG_(describe_addr) ( DiEpoch ep, Addr a, /*OUT*/AddrInfo* ai );
++ 
++ extern void VG_(clear_addrinfo) ( AddrInfo* ai);
++ 
++Index: valgrind-3.13.0/include/pub_tool_basics.h
++===================================================================
++--- valgrind-3.13.0.orig/include/pub_tool_basics.h
+++++ valgrind-3.13.0/include/pub_tool_basics.h
++@@ -129,6 +129,24 @@ typedef  struct { UWord uw1; UWord uw2;
++ /* ThreadIds are simply indices into the VG_(threads)[] array. */
++ typedef UInt ThreadId;
++ 
+++
+++/* You need a debuginfo epoch in order to convert an address into any source
+++   level entity, since that conversion depends on what objects were mapped
+++   in at the time.  An epoch is simply a monotonically increasing counter,
+++   which we wrap up in a struct so as to enable the C type system to
+++   distinguish it from other kinds of numbers.  m_debuginfo holds and
+++   maintains the current epoch number. */
+++typedef  struct { UInt n; }  DiEpoch;
+++
+++static inline DiEpoch DiEpoch_INVALID ( void ) {
+++   DiEpoch dep; dep.n = 0; return dep;
+++}
+++
+++static inline Bool is_DiEpoch_INVALID ( DiEpoch dep ) {
+++   return dep.n == 0;
+++}
+++
+++
++ /* Many data structures need to allocate and release memory.
++    The allocation/release functions must be provided by the caller.
++    The Alloc_Fn_t function must allocate a chunk of memory of size szB.
++Index: valgrind-3.13.0/include/pub_tool_debuginfo.h
++===================================================================
++--- valgrind-3.13.0.orig/include/pub_tool_debuginfo.h
+++++ valgrind-3.13.0/include/pub_tool_debuginfo.h
++@@ -31,11 +31,20 @@
++ #ifndef __PUB_TOOL_DEBUGINFO_H
++ #define __PUB_TOOL_DEBUGINFO_H
++ 
++-#include "pub_tool_basics.h"   // VG_ macro
+++#include "pub_tool_basics.h"   // VG_ macro, DiEpoch
++ #include "pub_tool_xarray.h"   // XArray
++ 
+++
++ /*====================================================================*/
++-/*=== Obtaining debug information                                  ===*/
+++/*=== Debuginfo epochs.                                            ===*/
+++/*====================================================================*/
+++
+++// This returns the current epoch.
+++DiEpoch VG_(current_DiEpoch)(void);
+++
+++
+++/*====================================================================*/
+++/*=== Obtaining information pertaining to source artefacts.        ===*/
++ /*====================================================================*/
++ 
++ /* IMPORTANT COMMENT about memory persistence and ownership.
++@@ -76,11 +85,11 @@
++    demangles C++ function names.  VG_(get_fnname_w_offset) is the
++    same, except it appends "+N" to symbol names to indicate offsets.
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership. */
++-extern Bool VG_(get_filename) ( Addr a, const HChar** filename );
++-extern Bool VG_(get_fnname)   ( Addr a, const HChar** fnname );
++-extern Bool VG_(get_linenum)  ( Addr a, UInt* linenum );
+++extern Bool VG_(get_filename) ( DiEpoch ep, Addr a, const HChar** filename );
+++extern Bool VG_(get_fnname)   ( DiEpoch ep, Addr a, const HChar** fnname );
+++extern Bool VG_(get_linenum)  ( DiEpoch ep, Addr a, UInt* linenum );
++ extern Bool VG_(get_fnname_w_offset)
++-                              ( Addr a, const HChar** fnname );
+++                              ( DiEpoch ep, Addr a, const HChar** fnname );
++ 
++ /* This one is the most general.  It gives filename, line number and
++    optionally directory name.  filename and linenum may not be NULL.
++@@ -95,7 +104,7 @@ extern Bool VG_(get_fnname_w_offset)
++    Returned value indicates whether any filename/line info could be
++    found. */
++ extern Bool VG_(get_filename_linenum)
++-                              ( Addr a, 
+++                              ( DiEpoch ep, Addr a, 
++                                 /*OUT*/const HChar** filename,
++                                 /*OUT*/const HChar** dirname,
++                                 /*OUT*/UInt* linenum );
++@@ -108,7 +117,8 @@ extern Bool VG_(get_filename_linenum)
++    of its symbols, this function will not be able to recognise function
++    entry points within it.
++    NOTE: See IMPORTANT COMMENT above about persistence and ownership. */
++-extern Bool VG_(get_fnname_if_entry) ( Addr a, const HChar** fnname );
+++extern Bool VG_(get_fnname_if_entry) ( DiEpoch ep, Addr a,
+++                                       const HChar** fnname );
++ 
++ typedef
++    enum {
++@@ -121,13 +131,13 @@ typedef
++ extern Vg_FnNameKind VG_(get_fnname_kind) ( const HChar* name );
++ 
++ /* Like VG_(get_fnname_kind), but takes a code address. */
++-extern Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( Addr ip );
+++extern Vg_FnNameKind VG_(get_fnname_kind_from_IP) ( DiEpoch ep, Addr ip );
++ 
++ /* Looks up data_addr in the collection of data symbols, and if found
++    puts its name (or as much as will fit) into dname[0 .. n_dname-1],
++    which is guaranteed to be zero terminated.  Also data_addr's offset
++    from the symbol start is put into *offset. */
++-extern Bool VG_(get_datasym_and_offset)( Addr data_addr,
+++extern Bool VG_(get_datasym_and_offset)( DiEpoch ep, Addr data_addr,
++                                          /*OUT*/const HChar** dname,
++                                          /*OUT*/PtrdiffT* offset );
++ 
++@@ -147,14 +157,14 @@ extern Bool VG_(get_datasym_and_offset)(
++ Bool VG_(get_data_description)( 
++         /*MOD*/ XArray* /* of HChar */ dname1v,
++         /*MOD*/ XArray* /* of HChar */ dname2v,
++-        Addr data_addr
+++        DiEpoch ep, Addr data_addr
++      );
++ 
++ /* Succeeds if the address is within a shared object or the main executable.
++    It first searches if Addr a belongs to the text segment of debug info.
++    If not found, it asks the address space manager whether it
++    knows the name of the file associated with this mapping. */
++-extern Bool VG_(get_objname)  ( Addr a, const HChar** objname );
+++extern Bool VG_(get_objname) ( DiEpoch ep, Addr a, const HChar** objname );
++ 
++ 
++ /* Cursor allowing to describe inlined function calls at an IP,
++@@ -169,7 +179,7 @@ typedef  struct _InlIPCursor InlIPCursor
++    eip can possibly corresponds to inlined function call(s).
++    To describe eip and the inlined function calls, the following must
++    be done:
++-       InlIPCursor *iipc = VG_(new_IIPC)(eip);
+++       InlIPCursor *iipc = VG_(new_IIPC)(ep, eip);
++        do {
++           buf = VG_(describe_IP)(eip, iipc);
++           ... use buf ...
++@@ -182,12 +192,16 @@ typedef  struct _InlIPCursor InlIPCursor
++    Note, that the returned string is allocated in a static buffer local to
++    VG_(describe_IP). That buffer will be overwritten with every invocation.
++    Therefore, callers need to possibly stash away the string.
+++
+++   Since this maps a code location to a source artefact (function names),
+++   new_IIPC requires a DiEpoch argument (ep) too.
++ */
++-extern const HChar* VG_(describe_IP)(Addr eip, const InlIPCursor* iipc);
+++extern const HChar* VG_(describe_IP)(DiEpoch ep, Addr eip,
+++                                     const InlIPCursor* iipc);
++ 
++ /* Builds a IIPC (Inlined IP Cursor) to describe eip and all the inlined calls
++    at eip. Such a cursor must be deleted after use using VG_(delete_IIPC). */
++-extern InlIPCursor* VG_(new_IIPC)(Addr eip);
+++extern InlIPCursor* VG_(new_IIPC)(DiEpoch ep, Addr eip);
++ /* Move the cursor to the next call to describe.
++    Returns True if there are still calls to describe.
++    False if nothing to describe anymore. */
++@@ -239,7 +253,7 @@ VG_(di_get_global_blocks_from_dihandle)
++ 
++ 
++ /*====================================================================*/
++-/*=== Obtaining debug information                                  ===*/
+++/*=== Obtaining information pertaining to shared objects.          ===*/
++ /*====================================================================*/
++ 
++ /* A way to make limited debuginfo queries on a per-mapped-object
++@@ -248,7 +262,7 @@ typedef  struct _DebugInfo  DebugInfo;
++ 
++ /* Returns NULL if the DebugInfo isn't found.  It doesn't matter if
++    debug info is present or not. */
++-DebugInfo* VG_(find_DebugInfo) ( Addr a );
+++DebugInfo* VG_(find_DebugInfo) ( DiEpoch ep, Addr a );
++ 
++ /* Fish bits out of DebugInfos. */
++ Addr          VG_(DebugInfo_get_text_avma)   ( const DebugInfo *di );
++Index: valgrind-3.13.0/include/pub_tool_errormgr.h
++===================================================================
++--- valgrind-3.13.0.orig/include/pub_tool_errormgr.h
+++++ valgrind-3.13.0/include/pub_tool_errormgr.h
++@@ -56,11 +56,11 @@ typedef
++ 
++ /* Useful in VG_(tdict).tool_error_matches_suppression(),
++  * VG_(tdict).tool_pp_Error(), etc */
++-ExeContext*  VG_(get_error_where)   ( const Error* err );
++-ErrorKind    VG_(get_error_kind)    ( const Error* err );
++-Addr         VG_(get_error_address) ( const Error* err );
++-const HChar* VG_(get_error_string)  ( const Error* err );
++-void*        VG_(get_error_extra)   ( const Error* err );
+++ExeContextAndEpoch  VG_(get_error_where)   ( const Error* err );
+++ErrorKind           VG_(get_error_kind)    ( const Error* err );
+++Addr                VG_(get_error_address) ( const Error* err );
+++const HChar*        VG_(get_error_string)  ( const Error* err );
+++void*               VG_(get_error_extra)   ( const Error* err );
++ 
++ /* Call this when an error occurs.  It will be recorded if it hasn't been
++    seen before.  If it has, the existing error record will have its count
++@@ -90,7 +90,7 @@ extern void VG_(maybe_record_error) ( Th
++    whether to add the error in the error total count (another mild hack). */
++ extern Bool VG_(unique_error) ( ThreadId tid, ErrorKind ekind,
++                                 Addr a, const HChar* s, void* extra,
++-                                ExeContext* where, Bool print_error,
+++                                ExeContextAndEpoch where, Bool print_error,
++                                 Bool allow_GDB_attach, Bool count_error );
++ 
++ /* Gets from fd (an opened suppression file) a non-blank, non-comment
++Index: valgrind-3.13.0/include/pub_tool_execontext.h
++===================================================================
++--- valgrind-3.13.0.orig/include/pub_tool_execontext.h
+++++ valgrind-3.13.0/include/pub_tool_execontext.h
++@@ -30,7 +30,13 @@
++ #ifndef __PUB_TOOL_EXECONTEXT_H
++ #define __PUB_TOOL_EXECONTEXT_H
++ 
++-#include "pub_tool_basics.h"   // ThreadID
+++#include "pub_tool_basics.h"     // ThreadID
+++#include "pub_tool_debuginfo.h"  // DiEpoch
+++
+++
+++/*====================================================================*/
+++/*=== ExeContext                                                   ===*/
+++/*====================================================================*/
++ 
++ // It's an abstract type.
++ typedef
++@@ -84,9 +90,6 @@ extern void VG_(apply_ExeContext)( void(
++ extern Bool VG_(eq_ExeContext) ( VgRes res, const ExeContext* e1,
++                                  const ExeContext* e2 );
++ 
++-// Print an ExeContext.
++-extern void VG_(pp_ExeContext) ( ExeContext* ec );
++-
++ // Get the 32-bit unique reference number for this ExeContext
++ // (the "ExeContext Unique").  Guaranteed to be nonzero and to be a
++ // multiple of four (iow, the lowest two bits are guaranteed to
++@@ -113,10 +116,52 @@ static inline Bool VG_(is_plausible_ECU)
++ // Make an ExeContext containing exactly the specified stack frames.
++ ExeContext* VG_(make_ExeContext_from_StackTrace)( const Addr* ips, UInt n_ips );
++ 
++-// Returns the "null" exe context. The null execontext is an artificial
++-// exe context, with a stack trace made of one Addr (the NULL address).
++-extern 
++-ExeContext* VG_(null_ExeContext) (void);
+++
+++/*====================================================================*/
+++/*=== ExeContextAndEpoch                                           ===*/
+++/*====================================================================*/
+++
+++/* A plain ExeContext is not generally symbolisable, since we also need to
+++   know which DebugInfo epoch it pertains to.  ExeContextAndEpoch pairs them
+++   up.  Note this is just two words, so passing it around by value is
+++   fine. */
+++typedef
+++   struct {
+++      ExeContext* ec;
+++      DiEpoch     epoch;
+++   }
+++   ExeContextAndEpoch;
+++
+++// A trivial constructor.
+++static inline ExeContextAndEpoch mk_ExeContextAndEpoch ( ExeContext* ec,
+++                                                         DiEpoch ep ) {
+++   ExeContextAndEpoch ece;
+++   ece.ec    = ec;
+++   ece.epoch = ep;
+++   return ece;
+++}
+++
+++// Generates a completely invalid ExeContextAndEpoch, with NULL for .ec and
+++// zero for .epoch.  Both values are invalid.
+++ExeContextAndEpoch VG_(invalid_ExeContextAndEpoch) ( void );
+++
+++// Takes an ExeContext and tags it with the current epoch, which is
+++// generally what we want to do.
+++ExeContextAndEpoch VG_(tag_EC_with_current_epoch)( ExeContext* ec );
+++
+++// Print an ExeContextAndEpoch.  We can't print a plain ExeContext
+++// because we can't symbolising it without knowing which debuginfo
+++// epoch it pertains to.
+++void VG_(pp_ExeContextAndEpoch) ( ExeContextAndEpoch ece );
+++
+++// Returns the "null" exe context tagged with the current debuginfo
+++// epoch. The null execontext is an artificial exe context, with a stack
+++// trace made of one Addr (the NULL address), and the current epoch.
+++ExeContextAndEpoch VG_(null_ExeContextAndEpoch) ( void );
+++
+++// Is this a value obtained from VG_(null_ExeContextAndEpoch) ?
+++Bool VG_(is_null_ExeContextAndEpoch)( ExeContextAndEpoch ece );
+++
++ 
++ #endif   // __PUB_TOOL_EXECONTEXT_H
++ 
++Index: valgrind-3.13.0/include/pub_tool_options.h
++===================================================================
++--- valgrind-3.13.0.orig/include/pub_tool_options.h
+++++ valgrind-3.13.0/include/pub_tool_options.h
++@@ -249,6 +249,12 @@ extern Int   VG_(clo_backtrace_size);
++ /* Continue stack traces below main()?  Default: NO */
++ extern Bool VG_(clo_show_below_main);
++ 
+++/* Keep symbols (and all other debuginfo) for code that is unloaded (dlclose
+++   or similar) so that stack traces can still give line/file info for
+++   previously captured stack traces.  e.g. ... showing where a block was
+++   allocated e.g. leaks of or accesses just outside a block. */
+++extern Bool VG_(clo_keep_debuginfo);
+++
++ 
++ /* Used to expand file names.  "option_name" is the option name, eg.
++    "--log-file".  'format' is what follows, eg. "cachegrind.out.%p".  In
++Index: valgrind-3.13.0/include/pub_tool_stacktrace.h
++===================================================================
++--- valgrind-3.13.0.orig/include/pub_tool_stacktrace.h
+++++ valgrind-3.13.0/include/pub_tool_stacktrace.h
++@@ -31,7 +31,7 @@
++ #ifndef __PUB_TOOL_STACKTRACE_H
++ #define __PUB_TOOL_STACKTRACE_H
++ 
++-#include "pub_tool_basics.h"   // Addr
+++#include "pub_tool_basics.h"   // Addr, DiEpoch
++ 
++ // The basic stack trace type:  just an array of code addresses.
++ typedef Addr* StackTrace;
++@@ -64,19 +64,19 @@ extern UInt VG_(get_StackTrace) ( Thread
++                                   /*OUT*/StackTrace fps,
++                                   Word first_ip_delta );
++ 
++-// Apply a function to every element in the StackTrace.  The parameter
++-// 'n' gives the index of the passed ip.  'opaque' is an arbitrary
++-// pointer provided to each invocation of 'action' (a poor man's
++-// closure).  Doesn't go below main() unless --show-below-main=yes is
++-// set.
+++// Apply a function to every element in the StackTrace.  The parameter 'n'
+++// gives the index of the passed ip.  'opaque' is an arbitrary pointer
+++// provided to each invocation of 'action' (a poor man's closure).  'ep' is
+++// the debuginfo epoch assumed to apply to all code addresses in the stack
+++// trace.  Doesn't go below main() unless --show-below-main=yes is set.
++ extern void VG_(apply_StackTrace)(
++-               void(*action)(UInt n, Addr ip, void* opaque),
+++               void(*action)(UInt n, DiEpoch ep, Addr ip, void* opaque),
++                void* opaque,
++-               StackTrace ips, UInt n_ips
+++               DiEpoch ep, StackTrace ips, UInt n_ips
++             );
++ 
++ // Print a StackTrace.
++-extern void VG_(pp_StackTrace) ( StackTrace ips, UInt n_ips );
+++extern void VG_(pp_StackTrace) ( DiEpoch ep, StackTrace ips, UInt n_ips );
++ 
++ // Gets and immediately prints a StackTrace.  Just a bit simpler than
++ // calling VG_(get_StackTrace)() then VG_(pp_StackTrace)().
++Index: valgrind-3.13.0/include/pub_tool_tooliface.h
++===================================================================
++--- valgrind-3.13.0.orig/include/pub_tool_tooliface.h
+++++ valgrind-3.13.0/include/pub_tool_tooliface.h
++@@ -463,7 +463,7 @@ extern void VG_(needs_print_stats) (
++    of an address ? */
++ extern void VG_(needs_info_location) (
++    // Get and pp information about Addr
++-   void (*info_location)(Addr)
+++   void (*info_location)(DiEpoch, Addr)
++ );
++ 
++ /* Do we need to see variable type and location information? */
++Index: valgrind-3.13.0/lackey/lk_main.c
++===================================================================
++--- valgrind-3.13.0.orig/lackey/lk_main.c
+++++ valgrind-3.13.0/lackey/lk_main.c
++@@ -664,6 +664,7 @@ IRSB* lk_instrument ( VgCallbackClosure*
++    Addr       iaddr = 0, dst;
++    UInt       ilen = 0;
++    Bool       condition_inverted = False;
+++   DiEpoch    ep = VG_(current_DiEpoch)();
++ 
++    if (gWordTy != hWordTy) {
++       /* We don't currently support this case. */
++@@ -750,7 +751,7 @@ IRSB* lk_instrument ( VgCallbackClosure*
++                tl_assert(clo_fnname);
++                tl_assert(clo_fnname[0]);
++                const HChar *fnname;
++-               if (VG_(get_fnname_if_entry)(st->Ist.IMark.addr, 
+++               if (VG_(get_fnname_if_entry)(ep, st->Ist.IMark.addr, 
++                                             &fnname)
++                    && 0 == VG_(strcmp)(fnname, clo_fnname)) {
++                   di = unsafeIRDirty_0_N( 
++Index: valgrind-3.13.0/massif/ms_main.c
++===================================================================
++--- valgrind-3.13.0.orig/massif/ms_main.c
+++++ valgrind-3.13.0/massif/ms_main.c
++@@ -520,8 +520,9 @@ void filter_IPs (Addr* ips, Int n_ips,
++    //  alloc function 'inside' a stacktrace e.g.
++    //    0x1 0x2 0x3 alloc func1 main
++    //  becomes   0x1 0x2 0x3 func1 main
+++   DiEpoch ep = VG_(current_DiEpoch)();
++    for (i = *top; i < n_ips; i++) {
++-      top_has_fnname = VG_(get_fnname)(ips[*top], &fnname);
+++      top_has_fnname = VG_(get_fnname)(ep, ips[*top], &fnname);
++       if (top_has_fnname &&  VG_(strIsMemberXA)(alloc_fns, fnname)) {
++          VERB(4, "filtering alloc fn %s\n", fnname);
++          (*top)++;
++@@ -576,7 +577,8 @@ static ExeContext* make_ec(ThreadId tid,
++    if (exclude_first_entry && n_ips > 0) {
++       const HChar *fnname;
++       VERB(4, "removing top fn %s from stacktrace\n", 
++-           VG_(get_fnname)(ips[0], &fnname) ? fnname : "???");
+++              VG_(get_fnname)(VG_(current_DiEpoch)(), ips[0], &fnname)
+++                 ? fnname : "???");
++       return VG_(make_ExeContext_from_StackTrace)(ips+1, n_ips-1);
++    } else
++       return VG_(make_ExeContext_from_StackTrace)(ips, n_ips);
++Index: valgrind-3.13.0/memcheck/mc_errors.c
++===================================================================
++--- valgrind-3.13.0.orig/memcheck/mc_errors.c
+++++ valgrind-3.13.0/memcheck/mc_errors.c
++@@ -94,15 +94,15 @@ struct _MC_Error {
++       struct {
++          SizeT szB;   // size of value in bytes
++          // Origin info
++-         UInt        otag;      // origin tag
++-         ExeContext* origin_ec; // filled in later
+++         UInt               otag;       // origin tag
+++         ExeContextAndEpoch origin_ece; // filled in later
++       } Value;
++ 
++       // Use of an undefined value in a conditional branch or move.
++       struct {
++          // Origin info
++-         UInt        otag;      // origin tag
++-         ExeContext* origin_ec; // filled in later
+++         UInt               otag;       // origin tag
+++         ExeContextAndEpoch origin_ece; // filled in later
++       } Cond;
++ 
++       // Addressability error in core (signal-handling) operation.
++@@ -127,8 +127,8 @@ struct _MC_Error {
++       // System call register input contains undefined bytes.
++       struct {
++          // Origin info
++-         UInt        otag;      // origin tag
++-         ExeContext* origin_ec; // filled in later
+++         UInt               otag;       // origin tag
+++         ExeContextAndEpoch origin_ece; // filled in later
++       } RegParam;
++ 
++       // System call memory input contains undefined/unaddressable bytes
++@@ -136,8 +136,8 @@ struct _MC_Error {
++          Bool     isAddrErr;  // Addressability or definedness error?
++          AddrInfo ai;
++          // Origin info
++-         UInt        otag;      // origin tag
++-         ExeContext* origin_ec; // filled in later
+++         UInt               otag;       // origin tag
+++         ExeContextAndEpoch origin_ece; // filled in later
++       } MemParam;
++ 
++       // Problem found from a client request like CHECK_MEM_IS_ADDRESSABLE.
++@@ -145,8 +145,8 @@ struct _MC_Error {
++          Bool     isAddrErr;  // Addressability or definedness error?
++          AddrInfo ai;
++          // Origin info
++-         UInt        otag;      // origin tag
++-         ExeContext* origin_ec; // filled in later
+++         UInt               otag;       // origin tag
+++         ExeContextAndEpoch origin_ece; // filled in later
++       } User;
++ 
++       // Program tried to free() something that's not a heap block (this
++@@ -279,10 +279,10 @@ static const HChar* pp_Reachedness_for_l
++    }
++ }
++ 
++-static void mc_pp_origin ( ExeContext* ec, UInt okind )
+++static void mc_pp_origin ( ExeContextAndEpoch ece, UInt okind )
++ {
++    const HChar* src = NULL;
++-   tl_assert(ec);
+++   tl_assert(ece.ec);
++ 
++    switch (okind) {
++       case MC_OKIND_STACK:   src = " by a stack allocation"; break;
++@@ -295,10 +295,10 @@ static void mc_pp_origin ( ExeContext* e
++    if (VG_(clo_xml)) {
++       emit( "  <auxwhat>Uninitialised value was created%s</auxwhat>\n",
++             src);
++-      VG_(pp_ExeContext)( ec );
+++      VG_(pp_ExeContextAndEpoch)( ece );
++    } else {
++       emit( " Uninitialised value was created%s\n", src);
++-      VG_(pp_ExeContext)( ec );
+++      VG_(pp_ExeContextAndEpoch)( ece );
++    }
++ }
++ 
++@@ -379,7 +379,7 @@ static void pp_LossRecord(UInt n_this_re
++          emit( "    <leakedblocks>%u</leakedblocks>\n", lr->num_blocks);
++          emit( "  </xwhat>\n" );
++       }
++-      VG_(pp_ExeContext)(lr->key.allocated_at);
+++      VG_(pp_ExeContextAndEpoch)(lr->key.allocated_at);
++    } else { /* ! if (xml) */
++       if (lr->indirect_szB > 0) {
++          emit(
++@@ -401,7 +401,7 @@ static void pp_LossRecord(UInt n_this_re
++             n_this_record, n_total_records
++          );
++       }
++-      VG_(pp_ExeContext)(lr->key.allocated_at);
+++      VG_(pp_ExeContextAndEpoch)(lr->key.allocated_at);
++    } /* if (xml) */
++ }
++ 
++@@ -427,11 +427,11 @@ void MC_(pp_Error) ( const Error* err )
++             emit( "  <kind>CoreMemError</kind>\n" );
++             emit( "  <what>%pS contains unaddressable byte(s)</what>\n",
++                   VG_(get_error_string)(err));
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          } else {
++             emit( "%s contains unaddressable byte(s)\n",
++                   VG_(get_error_string)(err));
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          }
++          break;
++       
++@@ -441,19 +441,19 @@ void MC_(pp_Error) ( const Error* err )
++             emit( "  <kind>UninitValue</kind>\n" );
++             emit( "  <what>Use of uninitialised value of size %lu</what>\n",
++                   extra->Err.Value.szB );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
++-            if (extra->Err.Value.origin_ec)
++-               mc_pp_origin( extra->Err.Value.origin_ec,
++-                            extra->Err.Value.otag & 3 );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
+++            if (extra->Err.Value.origin_ece.ec)
+++               mc_pp_origin( extra->Err.Value.origin_ece,
+++                             extra->Err.Value.otag & 3 );
++          } else {
++             /* Could also show extra->Err.Cond.otag if debugging origin
++                tracking */
++             emit( "Use of uninitialised value of size %lu\n",
++                   extra->Err.Value.szB );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
++-            if (extra->Err.Value.origin_ec)
++-               mc_pp_origin( extra->Err.Value.origin_ec,
++-                            extra->Err.Value.otag & 3 );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
+++            if (extra->Err.Value.origin_ece.ec)
+++               mc_pp_origin( extra->Err.Value.origin_ece,
+++                             extra->Err.Value.otag & 3 );
++          }
++          break;
++ 
++@@ -463,18 +463,18 @@ void MC_(pp_Error) ( const Error* err )
++             emit( "  <kind>UninitCondition</kind>\n" );
++             emit( "  <what>Conditional jump or move depends"
++                   " on uninitialised value(s)</what>\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
++-            if (extra->Err.Cond.origin_ec)
++-               mc_pp_origin( extra->Err.Cond.origin_ec,
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
+++            if (extra->Err.Cond.origin_ece.ec)
+++               mc_pp_origin( extra->Err.Cond.origin_ece,
++                              extra->Err.Cond.otag & 3 );
++          } else {
++             /* Could also show extra->Err.Cond.otag if debugging origin
++                tracking */
++             emit( "Conditional jump or move depends"
++                   " on uninitialised value(s)\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
++-            if (extra->Err.Cond.origin_ec)
++-               mc_pp_origin( extra->Err.Cond.origin_ec,
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
+++            if (extra->Err.Cond.origin_ece.ec)
+++               mc_pp_origin( extra->Err.Cond.origin_ece,
++                              extra->Err.Cond.otag & 3 );
++          }
++          break;
++@@ -486,16 +486,16 @@ void MC_(pp_Error) ( const Error* err )
++             emit( "  <what>Syscall param %pS contains "
++                   "uninitialised byte(s)</what>\n",
++                   VG_(get_error_string)(err) );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
++-            if (extra->Err.RegParam.origin_ec)
++-               mc_pp_origin( extra->Err.RegParam.origin_ec,
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
+++            if (extra->Err.RegParam.origin_ece.ec)
+++               mc_pp_origin( extra->Err.RegParam.origin_ece,
++                              extra->Err.RegParam.otag & 3 );
++          } else {
++             emit( "Syscall param %s contains uninitialised byte(s)\n",
++                   VG_(get_error_string)(err) );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
++-            if (extra->Err.RegParam.origin_ec)
++-               mc_pp_origin( extra->Err.RegParam.origin_ec,
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
+++            if (extra->Err.RegParam.origin_ece.ec)
+++               mc_pp_origin( extra->Err.RegParam.origin_ece,
++                              extra->Err.RegParam.otag & 3 );
++          }
++          break;
++@@ -509,24 +509,24 @@ void MC_(pp_Error) ( const Error* err )
++                   VG_(get_error_string)(err),
++                   extra->Err.MemParam.isAddrErr 
++                      ? "unaddressable" : "uninitialised" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)(VG_(get_error_address)(err),
++                                 &extra->Err.MemParam.ai, False);
++-            if (extra->Err.MemParam.origin_ec 
+++            if (extra->Err.MemParam.origin_ece.ec 
++                 && !extra->Err.MemParam.isAddrErr)
++-               mc_pp_origin( extra->Err.MemParam.origin_ec,
+++               mc_pp_origin( extra->Err.MemParam.origin_ece,
++                              extra->Err.MemParam.otag & 3 );
++          } else {
++             emit( "Syscall param %s points to %s byte(s)\n",
++                   VG_(get_error_string)(err),
++                   extra->Err.MemParam.isAddrErr 
++                      ? "unaddressable" : "uninitialised" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)(VG_(get_error_address)(err),
++                                 &extra->Err.MemParam.ai, False);
++-            if (extra->Err.MemParam.origin_ec 
+++            if (extra->Err.MemParam.origin_ece.ec 
++                 && !extra->Err.MemParam.isAddrErr)
++-               mc_pp_origin( extra->Err.MemParam.origin_ec,
+++               mc_pp_origin( extra->Err.MemParam.origin_ece,
++                              extra->Err.MemParam.otag & 3 );
++          }
++          break;
++@@ -540,21 +540,21 @@ void MC_(pp_Error) ( const Error* err )
++                   "during client check request</what>\n", 
++                    extra->Err.User.isAddrErr
++                       ? "Unaddressable" : "Uninitialised" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)(VG_(get_error_address)(err), &extra->Err.User.ai,
++                                 False);
++-            if (extra->Err.User.origin_ec && !extra->Err.User.isAddrErr)
++-               mc_pp_origin( extra->Err.User.origin_ec,
+++            if (extra->Err.User.origin_ece.ec && !extra->Err.User.isAddrErr)
+++               mc_pp_origin( extra->Err.User.origin_ece,
++                              extra->Err.User.otag & 3 );
++          } else {
++             emit( "%s byte(s) found during client check request\n", 
++                    extra->Err.User.isAddrErr
++                       ? "Unaddressable" : "Uninitialised" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)(VG_(get_error_address)(err), &extra->Err.User.ai,
++                                 False);
++-            if (extra->Err.User.origin_ec && !extra->Err.User.isAddrErr)
++-               mc_pp_origin( extra->Err.User.origin_ec,
+++            if (extra->Err.User.origin_ece.ec && !extra->Err.User.isAddrErr)
+++               mc_pp_origin( extra->Err.User.origin_ece,
++                              extra->Err.User.otag & 3 );
++          }
++          break;
++@@ -564,12 +564,12 @@ void MC_(pp_Error) ( const Error* err )
++             emit( "  <kind>InvalidFree</kind>\n" );
++             emit( "  <what>Invalid free() / delete / delete[]"
++                   " / realloc()</what>\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)( VG_(get_error_address)(err),
++                                  &extra->Err.Free.ai, False );
++          } else {
++             emit( "Invalid free() / delete / delete[] / realloc()\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)( VG_(get_error_address)(err),
++                                  &extra->Err.Free.ai, False );
++          }
++@@ -579,12 +579,12 @@ void MC_(pp_Error) ( const Error* err )
++          if (xml) {
++             emit( "  <kind>MismatchedFree</kind>\n" );
++             emit( "  <what>Mismatched free() / delete / delete []</what>\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)(VG_(get_error_address)(err),
++                                 &extra->Err.FreeMismatch.ai, False);
++          } else {
++             emit( "Mismatched free() / delete / delete []\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)(VG_(get_error_address)(err),
++                                 &extra->Err.FreeMismatch.ai, False);
++          }
++@@ -597,7 +597,7 @@ void MC_(pp_Error) ( const Error* err )
++             emit( "  <what>Invalid %s of size %lu</what>\n",
++                   extra->Err.Addr.isWrite ? "write" : "read",
++                   extra->Err.Addr.szB );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)( VG_(get_error_address)(err),
++                                  &extra->Err.Addr.ai,
++                                  extra->Err.Addr.maybe_gcc );
++@@ -605,7 +605,7 @@ void MC_(pp_Error) ( const Error* err )
++             emit( "Invalid %s of size %lu\n",
++                   extra->Err.Addr.isWrite ? "write" : "read",
++                   extra->Err.Addr.szB );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++ 
++             VG_(pp_addrinfo_mc)( VG_(get_error_address)(err),
++                                  &extra->Err.Addr.ai,
++@@ -618,12 +618,12 @@ void MC_(pp_Error) ( const Error* err )
++             emit( "  <kind>InvalidJump</kind>\n" );
++             emit( "  <what>Jump to the invalid address stated "
++                   "on the next line</what>\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)( VG_(get_error_address)(err), &extra->Err.Jump.ai,
++                                  False );
++          } else {
++             emit( "Jump to the invalid address stated on the next line\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)( VG_(get_error_address)(err), &extra->Err.Jump.ai,
++                                  False );
++          }
++@@ -644,7 +644,7 @@ void MC_(pp_Error) ( const Error* err )
++                      extra->Err.Overlap.dst, extra->Err.Overlap.src,
++                      extra->Err.Overlap.szB );
++             }
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          } else {
++             if (extra->Err.Overlap.szB == 0) {
++                emit( "Source and destination overlap in %s(%#lx, %#lx)\n",
++@@ -656,7 +656,7 @@ void MC_(pp_Error) ( const Error* err )
++                      extra->Err.Overlap.dst, extra->Err.Overlap.src,
++                      extra->Err.Overlap.szB );
++             }
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          }
++          break;
++ 
++@@ -666,12 +666,12 @@ void MC_(pp_Error) ( const Error* err )
++          if (xml) {
++             emit( "  <kind>InvalidMemPool</kind>\n" );
++             emit( "  <what>Illegal memory pool address</what>\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)( VG_(get_error_address)(err),
++                                  &extra->Err.IllegalMempool.ai, False );
++          } else {
++             emit( "Illegal memory pool address\n" );
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++             VG_(pp_addrinfo_mc)( VG_(get_error_address)(err),
++                                  &extra->Err.IllegalMempool.ai, False );
++          }
++@@ -695,14 +695,14 @@ void MC_(pp_Error) ( const Error* err )
++                   extra->Err.FishyValue.function_name,
++                   (SSizeT)extra->Err.FishyValue.value);
++             emit( "</what>");
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          } else {
++             emit( "Argument '%s' of function %s has a fishy "
++                   "(possibly negative) value: %ld\n",
++                   extra->Err.FishyValue.argument_name,
++                   extra->Err.FishyValue.function_name,
++                   (SSizeT)extra->Err.FishyValue.value);
++-            VG_(pp_ExeContext)( VG_(get_error_where)(err) );
+++            VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) );
++          }
++          break;
++ 
++@@ -773,9 +773,10 @@ void MC_(record_value_error) ( ThreadId
++    tl_assert( MC_(clo_mc_level) >= 2 );
++    if (otag > 0)
++       tl_assert( MC_(clo_mc_level) == 3 );
++-   extra.Err.Value.szB       = szB;
++-   extra.Err.Value.otag      = otag;
++-   extra.Err.Value.origin_ec = NULL;  /* Filled in later */
+++   extra.Err.Value.szB        = szB;
+++   extra.Err.Value.otag       = otag;
+++   extra.Err.Value.origin_ece = VG_(invalid_ExeContextAndEpoch)();
+++                                /* Filled in later */
++    VG_(maybe_record_error)( tid, Err_Value, /*addr*/0, /*s*/NULL, &extra );
++ }
++ 
++@@ -785,8 +786,9 @@ void MC_(record_cond_error) ( ThreadId t
++    tl_assert( MC_(clo_mc_level) >= 2 );
++    if (otag > 0)
++       tl_assert( MC_(clo_mc_level) == 3 );
++-   extra.Err.Cond.otag      = otag;
++-   extra.Err.Cond.origin_ec = NULL;  /* Filled in later */
+++   extra.Err.Cond.otag       = otag;
+++   extra.Err.Cond.origin_ece = VG_(invalid_ExeContextAndEpoch)();
+++                               /* Filled in later */
++    VG_(maybe_record_error)( tid, Err_Cond, /*addr*/0, /*s*/NULL, &extra );
++ }
++ 
++@@ -804,8 +806,9 @@ void MC_(record_regparam_error) ( Thread
++    tl_assert(VG_INVALID_THREADID != tid);
++    if (otag > 0)
++       tl_assert( MC_(clo_mc_level) == 3 );
++-   extra.Err.RegParam.otag      = otag;
++-   extra.Err.RegParam.origin_ec = NULL;  /* Filled in later */
+++   extra.Err.RegParam.otag       = otag;
+++   extra.Err.RegParam.origin_ece = VG_(invalid_ExeContextAndEpoch)();
+++                                 /* Filled in later */
++    VG_(maybe_record_error)( tid, Err_RegParam, /*addr*/0, msg, &extra );
++ }
++ 
++@@ -820,10 +823,11 @@ void MC_(record_memparam_error) ( Thread
++       tl_assert( MC_(clo_mc_level) == 3 );
++       tl_assert( !isAddrErr );
++    }
++-   extra.Err.MemParam.isAddrErr = isAddrErr;
++-   extra.Err.MemParam.ai.tag    = Addr_Undescribed;
++-   extra.Err.MemParam.otag      = otag;
++-   extra.Err.MemParam.origin_ec = NULL;  /* Filled in later */
+++   extra.Err.MemParam.isAddrErr  = isAddrErr;
+++   extra.Err.MemParam.ai.tag     = Addr_Undescribed;
+++   extra.Err.MemParam.otag       = otag;
+++   extra.Err.MemParam.origin_ece = VG_(invalid_ExeContextAndEpoch)();
+++                                 /* Filled in later */
++    VG_(maybe_record_error)( tid, Err_MemParam, a, msg, &extra );
++ }
++ 
++@@ -925,10 +929,11 @@ void MC_(record_user_error) ( ThreadId t
++       tl_assert( MC_(clo_mc_level) >= 2 );
++    }
++    tl_assert(VG_INVALID_THREADID != tid);
++-   extra.Err.User.isAddrErr = isAddrErr;
++-   extra.Err.User.ai.tag    = Addr_Undescribed;
++-   extra.Err.User.otag      = otag;
++-   extra.Err.User.origin_ec = NULL;  /* Filled in later */
+++   extra.Err.User.isAddrErr  = isAddrErr;
+++   extra.Err.User.ai.tag     = Addr_Undescribed;
+++   extra.Err.User.otag       = otag;
+++   extra.Err.User.origin_ece = VG_(invalid_ExeContextAndEpoch)();
+++                             /* Filled in later */
++    VG_(maybe_record_error)( tid, Err_User, a, /*s*/NULL, &extra );
++ }
++ 
++@@ -1053,7 +1058,7 @@ static Bool mempool_block_maybe_describe
++ 
++ /* Describe an address as best you can, for error messages,
++    putting the result in ai. */
++-static void describe_addr ( Addr a, /*OUT*/AddrInfo* ai )
+++static void describe_addr ( DiEpoch ep, Addr a, /*OUT*/AddrInfo* ai )
++ {
++    MC_Chunk*  mc;
++ 
++@@ -1121,28 +1126,30 @@ static void describe_addr ( Addr a, /*OU
++    }
++ 
++    /* No block found. Search a non-heap block description. */
++-   VG_(describe_addr) (a, ai);
+++   VG_(describe_addr) (ep, a, ai);
++ }
++ 
++-void MC_(pp_describe_addr) ( Addr a )
+++void MC_(pp_describe_addr) ( DiEpoch ep, Addr a )
++ {
++    AddrInfo ai;
++ 
++    ai.tag = Addr_Undescribed;
++-   describe_addr (a, &ai);
+++   describe_addr (ep, a, &ai);
++    VG_(pp_addrinfo_mc) (a, &ai, /* maybe_gcc */ False);
++    VG_(clear_addrinfo) (&ai);
++ }
++ 
++-/* Fill in *origin_ec as specified by otag, or NULL it out if otag
+++/* Fill in *origin_ece 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,
+++static void update_origin ( /*OUT*/ExeContextAndEpoch* origin_ece,
++                             UInt otag )
++ {
++    UInt ecu = otag & ~3;
++-   *origin_ec = NULL;
++    if (VG_(is_plausible_ECU)(ecu)) {
++-      *origin_ec = VG_(get_ExeContext_from_ECU)( ecu );
+++      *origin_ece
+++         = VG_(tag_EC_with_current_epoch)(VG_(get_ExeContext_from_ECU)( ecu ));
+++   } else {
+++      *origin_ece = VG_(invalid_ExeContextAndEpoch)();
++    }
++ }
++ 
++@@ -1150,6 +1157,7 @@ static void update_origin ( /*OUT*/ExeCo
++ UInt MC_(update_Error_extra)( const Error* err )
++ {
++    MC_Error* extra = VG_(get_error_extra)(err);
+++   DiEpoch   ep    = VG_(get_error_where)(err).epoch;
++ 
++    switch (VG_(get_error_kind)(err)) {
++    // These ones don't have addresses associated with them, and so don't
++@@ -1169,45 +1177,45 @@ UInt MC_(update_Error_extra)( const Erro
++    // origin tag.  Note that it is a kludge to assume that 
++    // a length-1 trace indicates a stack origin.  FIXME.
++    case Err_Value:
++-      update_origin( &extra->Err.Value.origin_ec,
+++      update_origin( &extra->Err.Value.origin_ece,
++                      extra->Err.Value.otag );
++       return sizeof(MC_Error);
++    case Err_Cond:
++-      update_origin( &extra->Err.Cond.origin_ec,
+++      update_origin( &extra->Err.Cond.origin_ece,
++                      extra->Err.Cond.otag );
++       return sizeof(MC_Error);
++    case Err_RegParam:
++-      update_origin( &extra->Err.RegParam.origin_ec,
+++      update_origin( &extra->Err.RegParam.origin_ece,
++                      extra->Err.RegParam.otag );
++       return sizeof(MC_Error);
++ 
++    // These ones always involve a memory address.
++    case Err_Addr:
++-      describe_addr ( VG_(get_error_address)(err),
+++      describe_addr ( ep, VG_(get_error_address)(err),
++                       &extra->Err.Addr.ai );
++       return sizeof(MC_Error);
++    case Err_MemParam:
++-      describe_addr ( VG_(get_error_address)(err),
+++      describe_addr ( ep, VG_(get_error_address)(err),
++                       &extra->Err.MemParam.ai );
++-      update_origin( &extra->Err.MemParam.origin_ec,
+++      update_origin( &extra->Err.MemParam.origin_ece,
++                      extra->Err.MemParam.otag );
++       return sizeof(MC_Error);
++    case Err_Jump:
++-      describe_addr ( VG_(get_error_address)(err),
+++      describe_addr ( ep, VG_(get_error_address)(err),
++                       &extra->Err.Jump.ai );
++       return sizeof(MC_Error);
++    case Err_User:
++-      describe_addr ( VG_(get_error_address)(err),
+++      describe_addr ( ep, VG_(get_error_address)(err),
++                       &extra->Err.User.ai );
++-      update_origin( &extra->Err.User.origin_ec,
+++      update_origin( &extra->Err.User.origin_ece,
++                      extra->Err.User.otag );
++       return sizeof(MC_Error);
++    case Err_Free:
++-      describe_addr ( VG_(get_error_address)(err),
+++      describe_addr ( ep, VG_(get_error_address)(err),
++                       &extra->Err.Free.ai );
++       return sizeof(MC_Error);
++    case Err_IllegalMempool:
++-      describe_addr ( VG_(get_error_address)(err),
+++      describe_addr ( ep, VG_(get_error_address)(err),
++                       &extra->Err.IllegalMempool.ai );
++       return sizeof(MC_Error);
++ 
++@@ -1252,7 +1260,7 @@ static Bool client_block_maybe_describe(
++          ai->Addr.Block.rwoffset   = (Word)(a) - (Word)(cgbs[i].start);
++          ai->Addr.Block.allocated_at = cgbs[i].where;
++          VG_(initThreadInfo) (&ai->Addr.Block.alloc_tinfo);
++-         ai->Addr.Block.freed_at = VG_(null_ExeContext)();;
+++         ai->Addr.Block.freed_at = VG_(null_ExeContextAndEpoch)();
++          return True;
++       }
++    }
++Index: valgrind-3.13.0/memcheck/mc_include.h
++===================================================================
++--- valgrind-3.13.0.orig/memcheck/mc_include.h
+++++ valgrind-3.13.0/memcheck/mc_include.h
++@@ -67,17 +67,17 @@ typedef
++       Addr         data;            // Address of the actual block.
++       SizeT        szB : (sizeof(SizeT)*8)-2; // Size requested; 30 or 62 bits.
++       MC_AllocKind allockind : 2;   // Which operation did the allocation.
++-      ExeContext*  where[0];
+++      ExeContextAndEpoch where[0];
++       /* Variable-length array. The size depends on MC_(clo_keep_stacktraces).
++          This array optionally stores the alloc and/or free stack trace. */
++    }
++    MC_Chunk;
++ 
++-/* Returns the execontext where the MC_Chunk was allocated/freed.
+++/* Returns the execontext and epoch where the MC_Chunk was allocated/freed.
++    Returns VG_(null_ExeContext)() if the execontext has not been recorded (due
++    to MC_(clo_keep_stacktraces) and/or because block not yet freed). */
++-ExeContext* MC_(allocated_at) (MC_Chunk*);
++-ExeContext* MC_(freed_at) (MC_Chunk*);
+++ExeContextAndEpoch MC_(allocated_at) (MC_Chunk*);
+++ExeContextAndEpoch MC_(freed_at) (MC_Chunk*);
++ 
++ /* Records and sets execontext according to MC_(clo_keep_stacktraces) */
++ void  MC_(set_allocated_at) (ThreadId, MC_Chunk*);
++@@ -432,8 +432,8 @@ typedef
++ /* When a LossRecord is put into an OSet, these elements represent the key. */
++ typedef
++    struct _LossRecordKey {
++-      Reachedness  state;        // LC_Extra.state value shared by all blocks.
++-      ExeContext*  allocated_at; // Where they were allocated.
+++      Reachedness         state;  // LC_Extra.state value shared by all blocks.
+++      ExeContextAndEpoch  allocated_at; // Where they were allocated.
++    } 
++    LossRecordKey;
++ 
++@@ -569,8 +569,8 @@ Bool MC_(record_fishy_value_error)  ( Th
++ /* Leak kinds tokens to call VG_(parse_enum_set). */
++ extern const HChar* MC_(parse_leak_kinds_tokens);
++ 
++-/* prints a description of address a */
++-void MC_(pp_describe_addr) (Addr a);
+++/* prints a description of address a in the specified debuginfo epoch */
+++void MC_(pp_describe_addr) ( DiEpoch ep, Addr a );
++ 
++ /* Is this address in a user-specified "ignored range" ? */
++ Bool MC_(in_ignored_range) ( Addr a );
++@@ -588,10 +588,10 @@ Bool MC_(in_ignored_range_below_sp) ( Ad
++    start == size == 0.  */
++ typedef
++    struct {
++-      Addr        start;
++-      SizeT       size;
++-      ExeContext* where;
++-      HChar*      desc;
+++      Addr               start;
+++      SizeT              size;
+++      ExeContextAndEpoch where;
+++      HChar*             desc;
++    } 
++    CGenBlock;
++ 
++Index: valgrind-3.13.0/memcheck/mc_leakcheck.c
++===================================================================
++--- valgrind-3.13.0.orig/memcheck/mc_leakcheck.c
+++++ valgrind-3.13.0/memcheck/mc_leakcheck.c
++@@ -1060,6 +1060,8 @@ lc_scan_memory(Addr start, SizeT len, Bo
++    const Addr end = VG_ROUNDDN(start+len, sizeof(Addr));
++    fault_catcher_t prev_catcher;
++ 
+++   const DiEpoch ep = VG_(current_DiEpoch)();
+++
++    if (VG_DEBUG_LEAKCHECK)
++       VG_(printf)("scan %#lx-%#lx (%lu)\n", start, end, len);
++ 
++@@ -1139,14 +1141,14 @@ lc_scan_memory(Addr start, SizeT len, Bo
++             if (addr >= searched && addr < searched + szB) {
++                if (addr == searched) {
++                   VG_(umsg)("*%#lx points at %#lx\n", ptr, searched);
++-                  MC_(pp_describe_addr) (ptr);
+++                  MC_(pp_describe_addr) (ep, ptr); // FIXME JRS: ep correct?
++                } else {
++                   Int ch_no;
++                   MC_Chunk *ch;
++                   LC_Extra *ex;
++                   VG_(umsg)("*%#lx interior points at %lu bytes inside %#lx\n",
++                             ptr, (long unsigned) addr - searched, searched);
++-                  MC_(pp_describe_addr) (ptr);
+++                  MC_(pp_describe_addr) (ep, ptr); // FIXME JRS: ep correct?
++                   if (lc_is_a_chunk_ptr(addr, &ch_no, &ch, &ex) ) {
++                      Int h;
++                      for (h = LchStdString; h < N_LEAK_CHECK_HEURISTICS; h++) {
++@@ -1203,13 +1205,17 @@ static Word cmp_LossRecordKey_LossRecord
++    // Compare on states first because that's fast.
++    if (a->state < b->state) return -1;
++    if (a->state > b->state) return  1;
++-   // Ok, the states are equal.  Now compare the locations, which is slower.
+++   // Also on epochs, for the same reason.
+++   if (a->allocated_at.epoch.n < b->allocated_at.epoch.n) return -1;
+++   if (a->allocated_at.epoch.n > b->allocated_at.epoch.n) return 1;
+++   // Ok, the states and epochs are equal.  Now compare the locations, which
+++   // is slower.
++    if (VG_(eq_ExeContext)(
++-            MC_(clo_leak_resolution), a->allocated_at, b->allocated_at))
+++            MC_(clo_leak_resolution), a->allocated_at.ec, b->allocated_at.ec))
++       return 0;
++    // Different locations.  Ordering is arbitrary, just use the ec pointer.
++-   if (a->allocated_at < b->allocated_at) return -1;
++-   if (a->allocated_at > b->allocated_at) return  1;
+++   if (a->allocated_at.ec < b->allocated_at.ec) return -1;
+++   if (a->allocated_at.ec > b->allocated_at.ec) return  1;
++    VG_(tool_panic)("bad LossRecord comparison");
++ }
++ 
++@@ -1231,10 +1237,15 @@ static Int cmp_LossRecords(const void* v
++    // possible.  So:  compare num_blocks.
++    if (lr_a->num_blocks < lr_b->num_blocks) return -1;
++    if (lr_a->num_blocks > lr_b->num_blocks) return  1;
+++   // Then epochs.
+++   if (lr_a->key.allocated_at.epoch.n < lr_b->key.allocated_at.epoch.n)
+++      return -1;
+++   if (lr_a->key.allocated_at.epoch.n > lr_b->key.allocated_at.epoch.n)
+++      return 1;
++    // Finally, compare ExeContext addresses... older ones are likely to have
++    // lower addresses.
++-   if (lr_a->key.allocated_at < lr_b->key.allocated_at) return -1;
++-   if (lr_a->key.allocated_at > lr_b->key.allocated_at) return  1;
+++   if (lr_a->key.allocated_at.ec < lr_b->key.allocated_at.ec) return -1;
+++   if (lr_a->key.allocated_at.ec > lr_b->key.allocated_at.ec) return  1;
++    return 0;
++ }
++ 
++@@ -1381,7 +1392,7 @@ static void XT_insert_lr (LossRecord* lr
++       xtl.xt_lr[i].vid[XT_Decrease].num_blocks 
++          = lr->old_num_blocks - lr->num_blocks;
++ 
++-   VG_(XT_add_to_ec)(leak_xt, lr->key.allocated_at, &xtl);
+++   VG_(XT_add_to_ec)(leak_xt, lr->key.allocated_at.ec, &xtl);
++ }
++ 
++ static void MC_(XT_Leak_sub) (void* from, const void* xtleak)
++@@ -2133,9 +2144,9 @@ void MC_(detect_memory_leaks) ( ThreadId
++             VG_(umsg)("Block 0x%lx..0x%lx overlaps with block 0x%lx..0x%lx\n",
++                       start1, end1, start2, end2);
++             VG_(umsg)("Blocks allocation contexts:\n"),
++-            VG_(pp_ExeContext)( MC_(allocated_at)(ch1));
+++            VG_(pp_ExeContextAndEpoch)( MC_(allocated_at)(ch1));
++             VG_(umsg)("\n"),
++-            VG_(pp_ExeContext)(  MC_(allocated_at)(ch2));
+++            VG_(pp_ExeContextAndEpoch)(  MC_(allocated_at)(ch2));
++             VG_(umsg)("This is usually caused by using ");
++             VG_(umsg)("VALGRIND_MALLOCLIKE_BLOCK in an inappropriate way.\n");
++             tl_assert (0);
++Index: valgrind-3.13.0/memcheck/mc_main.c
++===================================================================
++--- valgrind-3.13.0.orig/memcheck/mc_main.c
+++++ valgrind-3.13.0/memcheck/mc_main.c
++@@ -6719,9 +6719,11 @@ static Bool handle_gdb_monitor_command (
++       const HChar* src;
++       UInt otag;
++       UInt ecu;
++-      ExeContext* origin_ec;
+++      ExeContextAndEpoch origin_ece;
++       MC_ReadResult res;
++ 
+++      const DiEpoch ep = VG_(current_DiEpoch)();
+++
++       Int kwdid = VG_(keyword_id) 
++          ("addressable defined",
++           VG_(strtok_r) (NULL, " ", &ssaveptr), kwd_report_all);
++@@ -6738,7 +6740,8 @@ static Bool handle_gdb_monitor_command (
++             VG_(printf)
++                ("Address %p len %lu not addressable:\nbad address %p\n",
++                 (void *)address, szB, (void *) bad_addr);
++-         MC_(pp_describe_addr) (address);
+++         // FIXME JRS epoch ok?
+++         MC_(pp_describe_addr) (ep, address);
++          break;
++       case  1: /* defined */
++          res = is_mem_defined ( address, szB, &bad_addr, &otag );
++@@ -6765,14 +6768,16 @@ static Bool handle_gdb_monitor_command (
++                 (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 );
+++               origin_ece = VG_(tag_EC_with_current_epoch)(
+++                               VG_(get_ExeContext_from_ECU)( ecu ));
+++               VG_(pp_ExeContextAndEpoch)( origin_ece );
++             }
++          }
++          else
++             VG_(printf) ("Address %p len %lu defined\n",
++                          (void *)address, szB);
++-         MC_(pp_describe_addr) (address);
+++         // FIXME JRS epoch ok?
+++         MC_(pp_describe_addr) (ep, address);
++          break;
++       default: tl_assert(0);
++       }
++@@ -7049,7 +7054,9 @@ static Bool mc_handle_client_request ( T
++             cgbs[i].start = arg[1];
++             cgbs[i].size  = arg[2];
++             cgbs[i].desc  = VG_(strdup)("mc.mhcr.1", (HChar *)arg[3]);
++-            cgbs[i].where = VG_(record_ExeContext) ( tid, 0/*first_ip_delta*/ );
+++            cgbs[i].where = VG_(tag_EC_with_current_epoch)(
+++                               VG_(record_ExeContext) ( tid,
+++                                                        0/*first_ip_delta*/ ));
++             *ret = i;
++          } else
++             *ret = -1;
++@@ -7917,7 +7924,7 @@ static void mc_post_clo_init ( void )
++    }
++ 
++    MC_(chunk_poolalloc) = VG_(newPA)
++-      (sizeof(MC_Chunk) + MC_(n_where_pointers)() * sizeof(ExeContext*),
+++      (sizeof(MC_Chunk) + MC_(n_where_pointers)() * sizeof(ExeContextAndEpoch),
++        1000,
++        VG_(malloc),
++        "mc.cMC.1 (MC_Chunk pools)",
++Index: valgrind-3.13.0/memcheck/mc_malloc_wrappers.c
++===================================================================
++--- valgrind-3.13.0.orig/memcheck/mc_malloc_wrappers.c
+++++ valgrind-3.13.0/memcheck/mc_malloc_wrappers.c
++@@ -199,8 +199,8 @@ MC_Chunk* create_MC_Chunk ( ThreadId tid
++    mc->szB       = szB;
++    mc->allockind = kind;
++    switch ( MC_(n_where_pointers)() ) {
++-      case 2: mc->where[1] = 0; // fallback to 1
++-      case 1: mc->where[0] = 0; // fallback to 0
+++      case 2: mc->where[1] = VG_(invalid_ExeContextAndEpoch)(); // fall thru
+++      case 1: mc->where[0] = VG_(invalid_ExeContextAndEpoch)(); // fall thru
++       case 0: break;
++       default: tl_assert(0);
++    }
++@@ -268,30 +268,34 @@ static Bool live_block (MC_Chunk* mc)
++    return in_block_list ( MC_(malloc_list), mc );
++ }
++ 
++-ExeContext* MC_(allocated_at) (MC_Chunk* mc)
+++ExeContextAndEpoch MC_(allocated_at) (MC_Chunk* mc)
++ {
++    switch (MC_(clo_keep_stacktraces)) {
++-      case KS_none:            return VG_(null_ExeContext) ();
+++      case KS_none:            return VG_(null_ExeContextAndEpoch) ();
++       case KS_alloc:           return mc->where[0];
++-      case KS_free:            return VG_(null_ExeContext) ();
++-      case KS_alloc_then_free: return (live_block(mc) ?
++-                                       mc->where[0] : VG_(null_ExeContext) ());
+++      case KS_free:            return VG_(null_ExeContextAndEpoch) ();
+++      case KS_alloc_then_free: return live_block(mc) 
+++                                         ? mc->where[0]
+++                                         : VG_(null_ExeContextAndEpoch) ();
++       case KS_alloc_and_free:  return mc->where[0];
++       default: tl_assert (0);
++    }
++ }
++ 
++-ExeContext* MC_(freed_at) (MC_Chunk* mc)
+++ExeContextAndEpoch MC_(freed_at) (MC_Chunk* mc)
++ {
++    switch (MC_(clo_keep_stacktraces)) {
++-      case KS_none:            return VG_(null_ExeContext) ();
++-      case KS_alloc:           return VG_(null_ExeContext) ();
++-      case KS_free:            return (mc->where[0] ?
++-                                       mc->where[0] : VG_(null_ExeContext) ());
++-      case KS_alloc_then_free: return (live_block(mc) ?
++-                                       VG_(null_ExeContext) () : mc->where[0]);
++-      case KS_alloc_and_free:  return (mc->where[1] ?
++-                                       mc->where[1] : VG_(null_ExeContext) ());
+++      case KS_none:            return VG_(null_ExeContextAndEpoch) ();
+++      case KS_alloc:           return VG_(null_ExeContextAndEpoch) ();
+++      case KS_free:            return mc->where[0].ec
+++                                         ? mc->where[0]
+++                                         : VG_(null_ExeContextAndEpoch) ();
+++      case KS_alloc_then_free: return live_block(mc)
+++                                         ? VG_(null_ExeContextAndEpoch) ()
+++                                         : mc->where[0];
+++      case KS_alloc_and_free:  return mc->where[1].ec
+++                                         ? mc->where[1]
+++                                         : VG_(null_ExeContextAndEpoch) ();
++       default: tl_assert (0);
++    }
++ }
++@@ -306,15 +310,16 @@ void  MC_(set_allocated_at) (ThreadId ti
++       case KS_alloc_and_free:  break;
++       default: tl_assert (0);
++    }
++-   mc->where[0] = VG_(record_ExeContext) ( tid, 0/*first_ip_delta*/ );
+++   mc->where[0] = VG_(tag_EC_with_current_epoch)(
+++                     VG_(record_ExeContext) ( tid, 0/*first_ip_delta*/ ));
++    if (UNLIKELY(VG_(clo_xtree_memory) == Vg_XTMemory_Full))
++-       VG_(XTMemory_Full_alloc)(mc->szB, mc->where[0]);
+++       VG_(XTMemory_Full_alloc)(mc->szB, mc->where[0].ec);
++ }
++ 
++ void  MC_(set_freed_at) (ThreadId tid, MC_Chunk* mc)
++ {
++    Int pos;
++-   ExeContext* ec_free;
+++   ExeContextAndEpoch ec_free;
++ 
++    switch (MC_(clo_keep_stacktraces)) {
++       case KS_none:            return;
++@@ -333,9 +338,10 @@ void  MC_(set_freed_at) (ThreadId tid, M
++       Note: we are guaranteed to find the ec_alloc in mc->where[0], as
++       mc_post_clo_init verifies the consistency of --xtree-memory and
++       --keep-stacktraces. */
++-   ec_free = VG_(record_ExeContext) ( tid, 0/*first_ip_delta*/ );
+++   ec_free = VG_(tag_EC_with_current_epoch)(
+++                VG_(record_ExeContext) ( tid, 0/*first_ip_delta*/ ));
++    if (UNLIKELY(VG_(clo_xtree_memory) == Vg_XTMemory_Full))
++-       VG_(XTMemory_Full_free)(mc->szB, mc->where[0], ec_free);
+++       VG_(XTMemory_Full_free)(mc->szB, mc->where[0].ec, ec_free.ec);
++    if (LIKELY(pos >= 0))
++       mc->where[pos] = ec_free;
++ }
++@@ -391,7 +397,7 @@ void* MC_(new_block) ( ThreadId tid,
++    if (is_zeroed)
++       MC_(make_mem_defined)( p, szB );
++    else {
++-      UInt ecu = VG_(get_ECU_from_ExeContext)(MC_(allocated_at)(mc));
+++      UInt ecu = VG_(get_ECU_from_ExeContext)(MC_(allocated_at)(mc).ec);
++       tl_assert(VG_(is_plausible_ECU)(ecu));
++       MC_(make_mem_undefined_w_otag)( p, szB, ecu | MC_OKIND_HEAP );
++    }
++@@ -605,7 +611,7 @@ void* MC_(realloc) ( ThreadId tid, void*
++          // If the block has grown, we mark the grown area as undefined.
++          // We have to do that after VG_(HT_add_node) to ensure the ecu
++          // execontext is for a fully allocated block.
++-         ecu = VG_(get_ECU_from_ExeContext)(MC_(allocated_at)(new_mc));
+++         ecu = VG_(get_ECU_from_ExeContext)(MC_(allocated_at)(new_mc).ec);
++          tl_assert(VG_(is_plausible_ECU)(ecu));
++          MC_(make_mem_undefined_w_otag)( a_new+old_szB,
++                                          new_szB-old_szB,
++@@ -673,7 +679,7 @@ void MC_(handle_resizeInPlace)(ThreadId
++       return;
++ 
++    if (UNLIKELY(VG_(clo_xtree_memory) == Vg_XTMemory_Full))
++-       VG_(XTMemory_Full_resize_in_place)(oldSizeB,  newSizeB, mc->where[0]);
+++       VG_(XTMemory_Full_resize_in_place)(oldSizeB, newSizeB, mc->where[0].ec);
++ 
++    mc->szB = newSizeB;
++    if (newSizeB < oldSizeB) {
++@@ -887,7 +893,7 @@ check_mempool_sane(MC_Mempool* mp)
++                          chunks[i]->data, 
++                          chunks[i]->data + chunks[i]->szB);
++ 
++-            VG_(pp_ExeContext)(MC_(allocated_at)(chunks[i]));
+++            VG_(pp_ExeContextAndEpoch)(MC_(allocated_at)(chunks[i]));
++          }
++    }
++    VG_(free)(chunks);
++@@ -1147,7 +1153,7 @@ static void xtmemory_report_next_block(X
++    if (mc) {
++       xta->nbytes = mc->szB;
++       xta->nblocks = 1;
++-      *ec_alloc = MC_(allocated_at)(mc);
+++      *ec_alloc = MC_(allocated_at)(mc).ec;
++    } else
++       xta->nblocks = 0;
++ }
++Index: valgrind-3.13.0/memcheck/tests/linux/Makefile.am
++===================================================================
++--- valgrind-3.13.0.orig/memcheck/tests/linux/Makefile.am
+++++ valgrind-3.13.0/memcheck/tests/linux/Makefile.am
++@@ -6,6 +6,10 @@ dist_noinst_SCRIPTS = filter_stderr
++ EXTRA_DIST = \
++ 	brk.stderr.exp brk.vgtest \
++ 	capget.vgtest capget.stderr.exp capget.stderr.exp2 \
+++	dlclose_leak-no-keep.stderr.exp dlclose_leak-no-keep.stdout.exp \
+++	    dlclose_leak-no-keep.vgtest \
+++	dlclose_leak.stderr.exp dlclose_leak.stdout.exp \
+++	    dlclose_leak.vgtest \
++ 	ioctl-tiocsig.vgtest ioctl-tiocsig.stderr.exp \
++ 	lsframe1.vgtest lsframe1.stdout.exp lsframe1.stderr.exp \
++ 	lsframe2.vgtest lsframe2.stdout.exp lsframe2.stderr.exp \
++@@ -25,6 +29,7 @@ EXTRA_DIST = \
++ check_PROGRAMS = \
++ 	brk \
++ 	capget \
+++	dlclose_leak dlclose_leak_so.so \
++ 	ioctl-tiocsig \
++ 	getregset \
++ 	lsframe1 \
++@@ -48,3 +53,15 @@ AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
++ stack_switch_LDADD    = -lpthread
++ timerfd_syscall_LDADD = -lrt
++ 
+++# Build shared object for dlclose_leak
+++dlclose_leak_so_so_SOURCES = dlclose_leak_so.c
+++dlclose_leak_so_so_CFLAGS  = $(AM_CFLAGS) -fpic -g -O0
+++dlclose_leak_so_so_LDFLAGS  = -fpic $(AM_FLAG_M3264_PRI) -shared -Wl,-soname \
+++                              -Wl,dlclose_leak_so.so
+++
+++dlclose_leak_SOURCES          = dlclose_leak.c
+++dlclose_leak_DEPENDENCIES     = dlclose_leak_so.so
+++dlclose_leak_LDADD            = dlclose_leak_so.so
+++dlclose_leak_LDFLAGS          = $(AM_FLAG_M3264_PRI) \
+++                                -ldl \
+++                                -Wl,-rpath,$(top_builddir)/memcheck/tests/linux
++Index: valgrind-3.13.0/memcheck/tests/linux/dlclose_leak-no-keep.stderr.exp
++===================================================================
++--- /dev/null
+++++ valgrind-3.13.0/memcheck/tests/linux/dlclose_leak-no-keep.stderr.exp
++@@ -0,0 +1,30 @@
+++
+++Conditional jump or move depends on uninitialised value(s)
+++   ...
+++
+++Invalid read of size 1
+++   ...
+++ Address 0x........ is 1 bytes before a block of size 1 alloc'd
+++   at 0x........: malloc (vg_replace_malloc.c:...)
+++   ...
+++
+++done!
+++
+++HEAP SUMMARY:
+++    in use at exit: 1 bytes in 1 blocks
+++  total heap usage: 4 allocs, 3 frees, 123 bytes allocated
+++
+++1 bytes in 1 blocks are definitely lost in loss record ... of ...
+++   at 0x........: malloc (vg_replace_malloc.c:...)
+++   ...
+++
+++LEAK SUMMARY:
+++   definitely lost: 1 bytes in 1 blocks
+++   indirectly lost: 0 bytes in 0 blocks
+++     possibly lost: 0 bytes in 0 blocks
+++   still reachable: 0 bytes in 0 blocks
+++        suppressed: 0 bytes in 0 blocks
+++
+++For counts of detected and suppressed errors, rerun with: -v
+++Use --track-origins=yes to see where uninitialised values come from
+++ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
++Index: valgrind-3.13.0/memcheck/tests/linux/dlclose_leak-no-keep.vgtest
++===================================================================
++--- /dev/null
+++++ valgrind-3.13.0/memcheck/tests/linux/dlclose_leak-no-keep.vgtest
++@@ -0,0 +1,3 @@
+++prog: dlclose_leak
+++stderr_filter: ../filter_stderr
+++vgopts: --leak-check=yes --keep-debuginfo=no
++Index: valgrind-3.13.0/memcheck/tests/linux/dlclose_leak.c
++===================================================================
++--- /dev/null
+++++ valgrind-3.13.0/memcheck/tests/linux/dlclose_leak.c
++@@ -0,0 +1,32 @@
+++/*  Test reporting of memory leaks in objects that have been dlopen'ed.
+++ *   File:   dlclose_leak.c */
+++
+++#include <stdio.h>
+++#include <stdlib.h>
+++#include <dlfcn.h>
+++#include <assert.h>
+++
+++int (*jmp_on_uninit)(void);
+++char* (*alloc_1_byte)(void);
+++
+++int main(int argc, char** argv) {
+++    char* memToLeak;
+++    char x;
+++    void* handle = dlopen("./dlclose_leak_so.so", RTLD_NOW);
+++    if(!handle) {
+++        printf("FAILURE to dlopen dlclose_leak_so.so\n");
+++        return EXIT_FAILURE;
+++    }
+++    jmp_on_uninit = dlsym(handle,"jmp_on_uninit");
+++    //fprintf(stderr, "jmp_on_uninit: %p\n", jmp_on_uninit);
+++    assert(jmp_on_uninit);
+++    alloc_1_byte = dlsym(handle,"alloc_1_byte");
+++    //fprintf(stderr, "alloc_1_byte: %p\n", alloc_1_byte);
+++    assert(alloc_1_byte);
+++    (void)jmp_on_uninit();
+++    memToLeak = alloc_1_byte();
+++    dlclose(handle);
+++    x = memToLeak[-1];
+++    fprintf(stderr, "done!\n");
+++    return (EXIT_SUCCESS);
+++}
++Index: valgrind-3.13.0/memcheck/tests/linux/dlclose_leak.stderr.exp
++===================================================================
++--- /dev/null
+++++ valgrind-3.13.0/memcheck/tests/linux/dlclose_leak.stderr.exp
++@@ -0,0 +1,33 @@
+++
+++Conditional jump or move depends on uninitialised value(s)
+++   at 0x........: jmp_on_uninit (dlclose_leak_so.c:10)
+++   by 0x........: main (dlclose_leak.c:26)
+++
+++Invalid read of size 1
+++   at 0x........: main (dlclose_leak.c:29)
+++ Address 0x........ is 1 bytes before a block of size 1 alloc'd
+++   at 0x........: malloc (vg_replace_malloc.c:...)
+++   by 0x........: alloc_1_byte (dlclose_leak_so.c:20)
+++   by 0x........: main (dlclose_leak.c:27)
+++
+++done!
+++
+++HEAP SUMMARY:
+++    in use at exit: 1 bytes in 1 blocks
+++  total heap usage: 4 allocs, 3 frees, 123 bytes allocated
+++
+++1 bytes in 1 blocks are definitely lost in loss record ... of ...
+++   at 0x........: malloc (vg_replace_malloc.c:...)
+++   by 0x........: alloc_1_byte (dlclose_leak_so.c:20)
+++   by 0x........: main (dlclose_leak.c:27)
+++
+++LEAK SUMMARY:
+++   definitely lost: 1 bytes in 1 blocks
+++   indirectly lost: 0 bytes in 0 blocks
+++     possibly lost: 0 bytes in 0 blocks
+++   still reachable: 0 bytes in 0 blocks
+++        suppressed: 0 bytes in 0 blocks
+++
+++For counts of detected and suppressed errors, rerun with: -v
+++Use --track-origins=yes to see where uninitialised values come from
+++ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
++Index: valgrind-3.13.0/memcheck/tests/linux/dlclose_leak.vgtest
++===================================================================
++--- /dev/null
+++++ valgrind-3.13.0/memcheck/tests/linux/dlclose_leak.vgtest
++@@ -0,0 +1,3 @@
+++prog: dlclose_leak
+++stderr_filter: ../filter_stderr
+++vgopts: --leak-check=yes --keep-debuginfo=yes
++Index: valgrind-3.13.0/memcheck/tests/linux/dlclose_leak_so.c
++===================================================================
++--- /dev/null
+++++ valgrind-3.13.0/memcheck/tests/linux/dlclose_leak_so.c
++@@ -0,0 +1,21 @@
+++/* dlclose_leak_so.c */
+++
+++#include <stdlib.h>
+++
+++/** Makes a jump based on an uninitialized variable in order to make sure
+++ * errors reported while the dlopen'ed object is loaded work. */
+++int jmp_on_uninit(void) {
+++    int uninit[27];
+++    __asm__ __volatile("":::"cc","memory");
+++    if(uninit[13]) {
+++        return 1;
+++    } else {
+++        return 0;
+++    }
+++}
+++
+++/** Leak 1 byte of memory. This is to test the stack check reported after the
+++ *  object has been dlclose'd. */
+++char* alloc_1_byte(void) {
+++    return (char*)malloc(1);
+++}
++Index: valgrind-3.13.0/none/tests/cmdline1.stdout.exp
++===================================================================
++--- valgrind-3.13.0.orig/none/tests/cmdline1.stdout.exp
+++++ valgrind-3.13.0/none/tests/cmdline1.stdout.exp
++@@ -42,6 +42,10 @@ usage: valgrind [options] prog-and-args
++     --error-exitcode=<number> exit code to return if errors found [0=disable]
++     --error-markers=<begin>,<end> add lines with begin/end markers before/after
++                               each error output in plain text mode [none]
+++    --keep-debuginfo=no|yes   Keep symbols etc for unloaded code [no]
+++                              This allows stack traces for memory leaks to
+++                              include file/line info for code that has been
+++                              dlclose'd (or similar)
++     --show-below-main=no|yes  continue stack traces below main() [no]
++     --default-suppressions=yes|no
++                               load default suppressions [yes]
++Index: valgrind-3.13.0/none/tests/cmdline2.stdout.exp
++===================================================================
++--- valgrind-3.13.0.orig/none/tests/cmdline2.stdout.exp
+++++ valgrind-3.13.0/none/tests/cmdline2.stdout.exp
++@@ -42,6 +42,10 @@ usage: valgrind [options] prog-and-args
++     --error-exitcode=<number> exit code to return if errors found [0=disable]
++     --error-markers=<begin>,<end> add lines with begin/end markers before/after
++                               each error output in plain text mode [none]
+++    --keep-debuginfo=no|yes   Keep symbols etc for unloaded code [no]
+++                              This allows stack traces for memory leaks to
+++                              include file/line info for code that has been
+++                              dlclose'd (or similar)
++     --show-below-main=no|yes  continue stack traces below main() [no]
++     --default-suppressions=yes|no
++                               load default suppressions [yes]
--- a/taskcluster/ci/packages/kind.yml
+++ b/taskcluster/ci/packages/kind.yml
@@ -62,8 +62,19 @@ jobs:
       tarball:
         url: https://www.mercurial-scm.org/release/mercurial-4.4.2.tar.gz
         sha256: dc2f72caccd6b760226753d48c2f4e8889fe176a6b23ef50775caac55ce28b85
       pre-build-command: >-
         cp -r contrib/debian debian &&
         sed -i -e "s/__VERSION__/$(awk -F\" '$2 {print $2}' mercurial/__version__.py)-1.deb7moz1/" \
                -e "s/__DATE__/$(date --rfc-2822)/" \
                -e "s/__CODENAME__/wheezy/" debian/changelog
+
+  deb7-valgrind:
+    description: "Valgrind for Debian Wheezy"
+    treeherder:
+      symbol: Deb7(valgrind)
+    run:
+      using: debian-package
+      dsc:
+        url: http://snapshot.debian.org/archive/debian/20170725T040438Z/pool/main/v/valgrind/valgrind_3.13.0-1.dsc
+        sha256: ab84e017d1660efd30e9e0593a4c8b976aeda013cefb8c416dd284cc7222c11c
+      patch: valgrind-wheezy.diff