Index: Makefile.am =================================================================== --- Makefile.am (revision 16465) +++ Makefile.am (working copy) @@ -16,6 +16,10 @@ 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: cachegrind/cg_main.c =================================================================== --- cachegrind/cg_main.c (revision 16465) +++ cachegrind/cg_main.c (working copy) @@ -210,12 +210,14 @@ 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: callgrind/bb.c =================================================================== --- callgrind/bb.c (revision 16465) +++ callgrind/bb.c (working copy) @@ -199,7 +199,8 @@ 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: callgrind/dump.c =================================================================== --- callgrind/dump.c (revision 16465) +++ callgrind/dump.c (working copy) @@ -373,7 +373,8 @@ 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: callgrind/fn.c =================================================================== --- callgrind/fn.c (revision 16465) +++ callgrind/fn.c (working copy) @@ -434,17 +434,18 @@ 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 @@ 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 @@ 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: coregrind/m_addrinfo.c =================================================================== --- coregrind/m_addrinfo.c (revision 16465) +++ coregrind/m_addrinfo.c (working copy) @@ -86,7 +86,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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: coregrind/m_debuginfo/debuginfo.c =================================================================== --- coregrind/m_debuginfo/debuginfo.c (revision 16465) +++ coregrind/m_debuginfo/debuginfo.c (working copy) @@ -70,7 +70,11 @@ 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 @@ /*------------------------------------------------------------*/ +/*--- 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 @@ } +// 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 @@ 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,13 +435,20 @@ } -/* '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; @@ -315,11 +455,14 @@ 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 @@ -326,10 +469,18 @@ : "???", 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 @@ 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 @@ if (!found) break; anyFound = True; - discard_DebugInfo( curr ); + discard_or_archive_DebugInfo( curr ); } return anyFound; @@ -418,9 +570,9 @@ } -/* 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 @@ } if (!curr) break; - discard_DebugInfo( curr ); + discard_or_archive_DebugInfo( curr ); } } @@ -446,6 +598,7 @@ 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 @@ -452,6 +605,8 @@ 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); @@ -458,7 +613,7 @@ di->mark = False; } } - discard_marked_DebugInfos(); + discard_or_archive_marked_DebugInfos(); } @@ -470,6 +625,8 @@ 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 @@ di->next = debugInfo_list; debugInfo_list = di; } + vg_assert(!is_DebugInfo_archived(di)); return di; } @@ -723,6 +881,8 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ # 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 @@ 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 @@ /*------------------------------------------------------------*/ /*--- 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 @@ } /* 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } -/* 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 @@ 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 @@ 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 @@ (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; - if (UNLIKELY(se->sym_avma != a || se->isText != findText)) { + Sym_Name_CacheEnt* se = &sym_name_cache[hash]; + + 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 @@ /* 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 @@ 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 @@ 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,7 +2095,7 @@ 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; @@ -1905,7 +2102,7 @@ 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 @@ 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 @@ 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 @@ /* 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_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 @@ 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 @@ (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 @@ /* 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 @@ 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 @@ /* 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 @@ /* 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 @@ } /* 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // ??? 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 @@ /*--- ---*/ /*--------------------------------------------------------------*/ +/* 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 @@ *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 @@ 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 @@ 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_FPO_info) ( /*MOD*/Addr* ipP, /*MOD*/Addr* spP, /*MOD*/Addr* fpP, + DiEpoch ep, Addr min_accessible, Addr max_accessible ) { @@ -3049,6 +3297,9 @@ 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 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 @@ /* 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 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 @@ 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 @@ 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: coregrind/m_debuginfo/priv_storage.h =================================================================== --- coregrind/m_debuginfo/priv_storage.h (revision 16465) +++ coregrind/m_debuginfo/priv_storage.h (working copy) @@ -588,6 +588,36 @@ 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: coregrind/m_errormgr.c =================================================================== --- coregrind/m_errormgr.c (revision 16465) +++ coregrind/m_errormgr.c (working copy) @@ -136,16 +136,16 @@ 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 @@ { 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 @@ */ #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)(" %pS \n", buf); } else - if ( VG_(get_objname)(ip, &buf) ) { + if ( VG_(get_objname)(ep, ip, &buf) ) { VG_(printf_xml)(" %pS \n", buf); } else { VG_(printf_xml)(" * \n"); @@ -338,16 +341,16 @@ 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,7 +364,7 @@ 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"; @@ -368,8 +371,8 @@ 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 @@ 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 @@ // 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 @@ /* 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 @@ 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 @@ } /* 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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 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 @@ 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: coregrind/m_execontext.c =================================================================== --- coregrind/m_execontext.c (revision 16465) +++ coregrind/m_execontext.c (working copy) @@ -116,7 +116,7 @@ /*------------------------------------------------------------*/ -/*--- Exported functions. ---*/ +/*--- ExeContext functions. ---*/ /*------------------------------------------------------------*/ static ExeContext* record_ExeContext_wrk2 ( const Addr* ips, UInt n_ips ); @@ -169,7 +169,9 @@ 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 @@ } -/* 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 @@ 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: coregrind/m_gdbserver/m_gdbserver.c =================================================================== --- coregrind/m_gdbserver/m_gdbserver.c (revision 16465) +++ coregrind/m_gdbserver/m_gdbserver.c (working copy) @@ -142,14 +142,17 @@ 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: coregrind/m_gdbserver/server.c =================================================================== --- coregrind/m_gdbserver/server.c (revision 16465) +++ coregrind/m_gdbserver/server.c (working copy) @@ -195,6 +195,9 @@ 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 @@ } 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,7 +445,7 @@ &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. @@ -449,7 +452,7 @@ 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: coregrind/m_gdbserver/target.c =================================================================== --- coregrind/m_gdbserver/target.c (revision 16465) +++ coregrind/m_gdbserver/target.c (working copy) @@ -209,7 +209,10 @@ 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: coregrind/m_gdbserver/valgrind-low-arm.c =================================================================== --- coregrind/m_gdbserver/valgrind-low-arm.c (revision 16465) +++ coregrind/m_gdbserver/valgrind-low-arm.c (working copy) @@ -149,8 +149,12 @@ // 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: coregrind/m_gdbserver/valgrind-low-mips32.c =================================================================== --- coregrind/m_gdbserver/valgrind-low-mips32.c (revision 16465) +++ coregrind/m_gdbserver/valgrind-low-mips32.c (working copy) @@ -228,7 +228,11 @@ /* 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: coregrind/m_gdbserver/valgrind-low-mips64.c =================================================================== --- coregrind/m_gdbserver/valgrind-low-mips64.c (revision 16465) +++ coregrind/m_gdbserver/valgrind-low-mips64.c (working copy) @@ -229,7 +229,11 @@ /* 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: coregrind/m_libcassert.c =================================================================== --- coregrind/m_libcassert.c (revision 16465) +++ coregrind/m_libcassert.c (working copy) @@ -369,7 +369,7 @@ ); 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: coregrind/m_main.c =================================================================== --- coregrind/m_main.c (revision 16465) +++ coregrind/m_main.c (working copy) @@ -128,6 +128,10 @@ " --error-exitcode= exit code to return if errors found [0=disable]\n" " --error-markers=, 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 @@ 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: coregrind/m_options.c =================================================================== --- coregrind/m_options.c (revision 16465) +++ coregrind/m_options.c (working copy) @@ -129,6 +129,7 @@ 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: coregrind/m_redir.c =================================================================== --- coregrind/m_redir.c (revision 16465) +++ coregrind/m_redir.c (working copy) @@ -1859,8 +1859,9 @@ { 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]; @@ -1867,7 +1868,7 @@ 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: coregrind/m_sbprofile.c =================================================================== --- coregrind/m_sbprofile.c (revision 16465) +++ coregrind/m_sbprofile.c (working copy) @@ -74,6 +74,9 @@ 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 @@ 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 @@ 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 @@ 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: coregrind/m_scheduler/scheduler.c =================================================================== --- coregrind/m_scheduler/scheduler.c (revision 16465) +++ coregrind/m_scheduler/scheduler.c (working copy) @@ -2037,8 +2037,12 @@ 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: coregrind/m_signals.c =================================================================== --- coregrind/m_signals.c (revision 16465) +++ coregrind/m_signals.c (working copy) @@ -1878,7 +1878,7 @@ : 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: coregrind/m_stacktrace.c =================================================================== --- coregrind/m_stacktrace.c (revision 16465) +++ coregrind/m_stacktrace.c (working copy) @@ -446,6 +446,7 @@ /* 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 @@ 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,7 +1559,7 @@ } /* 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 ); @@ -1565,7 +1566,7 @@ if (VG_(clo_xml)) VG_(printf_xml)(" \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)(" \n"); @@ -1580,13 +1581,13 @@ 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 @@ // 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 @@ for (i = 0; i < n_ips; i++) // Act on the ip - action(i, ips[i], opaque); + action(i, ep, ips[i], opaque); } Index: coregrind/m_syswrap/syswrap-generic.c =================================================================== --- coregrind/m_syswrap/syswrap-generic.c (revision 16465) +++ coregrind/m_syswrap/syswrap-generic.c (working copy) @@ -540,7 +540,8 @@ { 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 @@ 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 @@ } } - 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, " \n"); Index: coregrind/m_tooliface.c =================================================================== --- coregrind/m_tooliface.c (revision 16465) +++ coregrind/m_tooliface.c (working copy) @@ -323,7 +323,7 @@ } void VG_(needs_info_location) ( - void (*info_location)(Addr) + void (*info_location)(DiEpoch, Addr) ) { VG_(needs).info_location = True; Index: coregrind/m_translate.c =================================================================== --- coregrind/m_translate.c (revision 16465) +++ coregrind/m_translate.c (working copy) @@ -1527,12 +1527,13 @@ 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) @@ -1539,12 +1540,12 @@ 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 @@ 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 @@ 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: coregrind/m_xtree.c =================================================================== --- coregrind/m_xtree.c (revision 16465) +++ coregrind/m_xtree.c (working copy) @@ -438,6 +438,9 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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: coregrind/pub_core_debuginfo.h =================================================================== --- coregrind/pub_core_debuginfo.h (revision 16465) +++ coregrind/pub_core_debuginfo.h (working copy) @@ -86,13 +86,13 @@ * 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 @@ -99,7 +99,8 @@ 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_(use_FPO_info) ( /*MOD*/Addr* ipP, /*MOD*/Addr* spP, /*MOD*/Addr* fpP, + DiEpoch ep, Addr min_accessible, Addr max_accessible ); @@ -217,7 +219,7 @@ /* 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 @@ 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: coregrind/pub_core_tooliface.h =================================================================== --- coregrind/pub_core_tooliface.h (revision 16465) +++ coregrind/pub_core_tooliface.h (working copy) @@ -156,7 +156,7 @@ 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: docs/xml/manual-core.xml =================================================================== --- docs/xml/manual-core.xml (revision 16465) +++ docs/xml/manual-core.xml (working copy) @@ -1219,6 +1219,19 @@ + + + + + + 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. + + + Index: drd/drd_error.c =================================================================== --- drd/drd_error.c (revision 16465) +++ drd/drd_error.c (working copy) @@ -139,12 +139,14 @@ " %pS\n" "
0x%lx
\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(" \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 @@ 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 @@ 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 @@ 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 @@ print_err_detail(" \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(" \n"); } else { @@ -322,7 +325,7 @@ 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 @@ 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 @@ 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 @@ " 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 @@ 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 @@ 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 @@ 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(" \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(" \n"); } @@ -397,7 +401,7 @@ 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,7 +413,8 @@ print_err_detail(" \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(" \n"); print_err_detail("%sLock on %s 0x%lx was held during %u ms" @@ -416,7 +421,7 @@ " (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 @@ 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 @@ 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 @@ print_err_detail("%sThe annotation macro %s has not yet been implemented" " in %ps%s\n", what_prefix, uicr->descr, "", 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 @@ print_err_detail("%sThe annotation macro %s has not yet been implemented" " in %ps%s\n", what_prefix, uicr->descr, "", 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: drd/drd_thread.c =================================================================== --- drd/drd_thread.c (revision 16465) +++ drd/drd_thread.c (working copy) @@ -1338,7 +1338,8 @@ 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: exp-bbv/bbv_main.c =================================================================== --- exp-bbv/bbv_main.c (revision 16465) +++ exp-bbv/bbv_main.c (working copy) @@ -346,6 +346,7 @@ 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 @@ 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: exp-dhat/dh_main.c =================================================================== --- exp-dhat/dh_main.c (revision 16465) +++ exp-dhat/dh_main.c (working copy) @@ -1146,7 +1146,7 @@ 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: exp-sgcheck/pc_common.c =================================================================== --- exp-sgcheck/pc_common.c (revision 16465) +++ exp-sgcheck/pc_common.c (working copy) @@ -322,7 +322,7 @@ 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 ); @@ -336,7 +336,7 @@ 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 @@ 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 ); @@ -372,7 +372,7 @@ 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 @@ 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", @@ -406,7 +406,8 @@ "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))); } else { @@ -414,7 +415,7 @@ 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 ); @@ -421,7 +422,8 @@ 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 @@ 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) { @@ -488,7 +490,8 @@ 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 { @@ -500,7 +503,8 @@ 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))); } } else { @@ -507,7 +511,7 @@ 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 @@ 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 @@ 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 @@ 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))); } 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 @@ 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 " @@ -598,7 +606,8 @@ 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) { @@ -608,7 +617,8 @@ 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))); } } else { @@ -615,7 +625,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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: exp-sgcheck/sg_main.c =================================================================== --- exp-sgcheck/sg_main.c (revision 16465) +++ exp-sgcheck/sg_main.c (working copy) @@ -1936,7 +1936,8 @@ 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: helgrind/hg_addrdescr.c =================================================================== --- helgrind/hg_addrdescr.c (revision 16465) +++ helgrind/hg_addrdescr.c (working copy) @@ -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 @@ 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,7 +101,7 @@ } } -Bool HG_(get_and_pp_addrdescr) (Addr addr) +Bool HG_(get_and_pp_addrdescr) (DiEpoch ep, Addr addr) { Bool ret; @@ -107,7 +108,7 @@ 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: helgrind/hg_addrdescr.h =================================================================== --- helgrind/hg_addrdescr.h (revision 16465) +++ helgrind/hg_addrdescr.h (working copy) @@ -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: helgrind/hg_errors.c =================================================================== --- helgrind/hg_errors.c (revision 16465) +++ helgrind/hg_errors.c (working copy) @@ -421,7 +421,8 @@ 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 @@ VG_(printf_xml)(" \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)("\n\n"); @@ -767,7 +769,8 @@ 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,7 +792,8 @@ if (lk->appeared_at) { emit( " Lock at %p was first observed\n", (void*)lk ); - VG_(pp_ExeContext)( lk->appeared_at ); + VG_(pp_ExeContextAndEpoch)( + VG_(tag_EC_with_current_epoch)( lk->appeared_at ) ); } } else { @@ -796,12 +800,13 @@ 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 @@ emit( " %d\n", (Int)xe->XE.Misc.thr->errmsg_index ); emit( " \n" ); - 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 )); } } else { @@ -953,11 +959,12 @@ 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 @@ emit( " %d\n", (Int)xe->XE.LockOrder.thr->errmsg_index ); emit( " \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( " 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( " 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 )); } announce_LockP ( xe->XE.LockOrder.shouldbe_earlier_lk ); announce_LockP ( xe->XE.LockOrder.shouldbe_later_lk ); @@ -1004,7 +1015,9 @@ "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"); } @@ -1011,7 +1024,7 @@ 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"); @@ -1018,11 +1031,15 @@ 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 @@ emit( " \n" ); 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) ); } else { @@ -1057,7 +1074,7 @@ 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,7 +1094,7 @@ emit( " %d\n", (Int)xe->XE.UnlockBogus.thr->errmsg_index ); emit( " \n" ); - VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + VG_(pp_ExeContextAndEpoch)( VG_(get_error_where)(err) ); } else { @@ -1084,7 +1101,7 @@ 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 @@ emit( " %d\n", (Int)xe->XE.UnlockForeign.owner->errmsg_index ); emit( " \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 @@ (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 @@ emit( " %d\n", (Int)xe->XE.UnlockUnlocked.thr->errmsg_index ); emit( " \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 @@ 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 @@ emit( " %d\n", (Int)xe->XE.Race.thr->errmsg_index ); emit( " \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 @@ emit( " %d\n", xe->XE.Race.h2_ct->errmsg_index); emit(" \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 @@ xe->XE.Race.h1_ct->errmsg_index ); emit(" \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( " (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" ); } @@ -1228,7 +1250,7 @@ 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 @@ 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 @@ "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 @@ 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: helgrind/hg_main.c =================================================================== --- helgrind/hg_main.c (revision 16465) +++ helgrind/hg_main.c (working copy) @@ -483,6 +483,7 @@ 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); @@ -489,7 +490,7 @@ 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 @@ 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 @@ 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: helgrind/libhb_core.c =================================================================== --- helgrind/libhb_core.c (revision 16465) +++ helgrind/libhb_core.c (working copy) @@ -4095,7 +4095,7 @@ 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: include/pub_tool_addrinfo.h =================================================================== --- include/pub_tool_addrinfo.h (revision 16465) +++ include/pub_tool_addrinfo.h (working copy) @@ -135,6 +135,7 @@ // (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 @@ 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 @@ 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: include/pub_tool_basics.h =================================================================== --- include/pub_tool_basics.h (revision 16465) +++ include/pub_tool_basics.h (working copy) @@ -129,6 +129,24 @@ /* 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: include/pub_tool_debuginfo.h =================================================================== --- include/pub_tool_debuginfo.h (revision 16465) +++ include/pub_tool_debuginfo.h (working copy) @@ -31,13 +31,22 @@ #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. Many functions below are returning a string in a HChar** argument. @@ -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 @@ 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 @@ 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 @@ 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,7 +157,7 @@ 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. @@ -154,7 +164,7 @@ 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 @@ 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 @@ 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 @@ /*====================================================================*/ -/*=== 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 @@ /* 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: include/pub_tool_errormgr.h =================================================================== --- include/pub_tool_errormgr.h (revision 16465) +++ include/pub_tool_errormgr.h (working copy) @@ -56,11 +56,11 @@ /* 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 @@ 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: include/pub_tool_execontext.h =================================================================== --- include/pub_tool_execontext.h (revision 16465) +++ include/pub_tool_execontext.h (working copy) @@ -30,8 +30,14 @@ #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 struct _ExeContext @@ -84,9 +90,6 @@ 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,11 +116,53 @@ // 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: include/pub_tool_options.h =================================================================== --- include/pub_tool_options.h (revision 16465) +++ include/pub_tool_options.h (working copy) @@ -249,7 +249,13 @@ /* 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 'format': Index: include/pub_tool_stacktrace.h =================================================================== --- include/pub_tool_stacktrace.h (revision 16465) +++ include/pub_tool_stacktrace.h (working copy) @@ -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 @@ /*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: include/pub_tool_tooliface.h =================================================================== --- include/pub_tool_tooliface.h (revision 16465) +++ include/pub_tool_tooliface.h (working copy) @@ -463,7 +463,7 @@ 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: lackey/lk_main.c =================================================================== --- lackey/lk_main.c (revision 16465) +++ lackey/lk_main.c (working copy) @@ -664,6 +664,7 @@ 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 @@ 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: massif/ms_main.c =================================================================== --- massif/ms_main.c (revision 16465) +++ massif/ms_main.c (working copy) @@ -520,8 +520,9 @@ // 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 @@ 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: memcheck/mc_errors.c =================================================================== --- memcheck/mc_errors.c (revision 16465) +++ memcheck/mc_errors.c (working copy) @@ -94,15 +94,15 @@ 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 @@ // 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 @@ 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 @@ 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 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 @@ if (VG_(clo_xml)) { emit( " Uninitialised value was created%s\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 @@ emit( " %u\n", lr->num_blocks); emit( " \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 @@ 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 @@ emit( " CoreMemError\n" ); emit( " %pS 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) ); } 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 @@ emit( " UninitValue\n" ); 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 ); } 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,9 +463,9 @@ emit( " UninitCondition\n" ); 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 ); } else { /* Could also show extra->Err.Cond.otag if debugging origin @@ -472,9 +472,9 @@ 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 @@ emit( " Syscall param %pS 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 ); } 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,12 +509,12 @@ 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", @@ -521,12 +521,12 @@ 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 @@ "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 ); } 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 @@ emit( " InvalidFree\n" ); 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 ); } 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 @@ if (xml) { emit( " MismatchedFree\n" ); 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); } 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 @@ 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, extra->Err.Addr.maybe_gcc ); @@ -605,7 +605,7 @@ 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 @@ emit( " InvalidJump\n" ); 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 ); } 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 @@ 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 @@ 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 @@ if (xml) { emit( " InvalidMemPool\n" ); 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 ); } 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,7 +695,7 @@ extra->Err.FishyValue.function_name, (SSizeT)extra->Err.FishyValue.value); emit( ""); - 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", @@ -702,7 +702,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ } /* 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 @@ 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 @@ // 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 @@ 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: memcheck/mc_include.h =================================================================== --- memcheck/mc_include.h (revision 16465) +++ memcheck/mc_include.h (working copy) @@ -67,17 +67,17 @@ 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 @@ /* 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 @@ /* 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 @@ start == size == 0. */ typedef struct { - Addr start; - SizeT size; - ExeContext* where; - HChar* desc; + Addr start; + SizeT size; + ExeContextAndEpoch where; + HChar* desc; } CGenBlock; Index: memcheck/mc_leakcheck.c =================================================================== --- memcheck/mc_leakcheck.c (revision 16465) +++ memcheck/mc_leakcheck.c (working copy) @@ -1060,6 +1060,8 @@ 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,7 +1141,7 @@ 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; @@ -1146,7 +1148,7 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ 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: memcheck/mc_main.c =================================================================== --- memcheck/mc_main.c (revision 16465) +++ memcheck/mc_main.c (working copy) @@ -6719,9 +6719,11 @@ 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 @@ 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 @@ (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 @@ 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 @@ } 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: memcheck/mc_malloc_wrappers.c =================================================================== --- memcheck/mc_malloc_wrappers.c (revision 16465) +++ memcheck/mc_malloc_wrappers.c (working copy) @@ -199,8 +199,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ 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 @@ 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: memcheck/tests/linux/Makefile.am =================================================================== --- memcheck/tests/linux/Makefile.am (revision 16465) +++ memcheck/tests/linux/Makefile.am (working copy) @@ -6,6 +6,10 @@ 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 @@ check_PROGRAMS = \ brk \ capget \ + dlclose_leak dlclose_leak_so.so \ ioctl-tiocsig \ getregset \ lsframe1 \ @@ -48,3 +53,15 @@ 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: memcheck/tests/linux/dlclose_leak-no-keep.stderr.exp =================================================================== --- memcheck/tests/linux/dlclose_leak-no-keep.stderr.exp (nonexistent) +++ memcheck/tests/linux/dlclose_leak-no-keep.stderr.exp (working copy) @@ -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: memcheck/tests/linux/dlclose_leak-no-keep.stdout.exp =================================================================== Index: memcheck/tests/linux/dlclose_leak-no-keep.vgtest =================================================================== --- memcheck/tests/linux/dlclose_leak-no-keep.vgtest (nonexistent) +++ memcheck/tests/linux/dlclose_leak-no-keep.vgtest (working copy) @@ -0,0 +1,3 @@ +prog: dlclose_leak +stderr_filter: ../filter_stderr +vgopts: --leak-check=yes --keep-debuginfo=no Index: memcheck/tests/linux/dlclose_leak.c =================================================================== --- memcheck/tests/linux/dlclose_leak.c (nonexistent) +++ memcheck/tests/linux/dlclose_leak.c (working copy) @@ -0,0 +1,32 @@ +/* Test reporting of memory leaks in objects that have been dlopen'ed. + * File: dlclose_leak.c */ + +#include +#include +#include +#include + +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: memcheck/tests/linux/dlclose_leak.stderr.exp =================================================================== --- memcheck/tests/linux/dlclose_leak.stderr.exp (nonexistent) +++ memcheck/tests/linux/dlclose_leak.stderr.exp (working copy) @@ -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: memcheck/tests/linux/dlclose_leak.stdout.exp =================================================================== Index: memcheck/tests/linux/dlclose_leak.vgtest =================================================================== --- memcheck/tests/linux/dlclose_leak.vgtest (nonexistent) +++ memcheck/tests/linux/dlclose_leak.vgtest (working copy) @@ -0,0 +1,3 @@ +prog: dlclose_leak +stderr_filter: ../filter_stderr +vgopts: --leak-check=yes --keep-debuginfo=yes Index: memcheck/tests/linux/dlclose_leak_so.c =================================================================== --- memcheck/tests/linux/dlclose_leak_so.c (nonexistent) +++ memcheck/tests/linux/dlclose_leak_so.c (working copy) @@ -0,0 +1,21 @@ +/* dlclose_leak_so.c */ + +#include + +/** 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: none/tests/cmdline1.stdout.exp =================================================================== --- none/tests/cmdline1.stdout.exp (revision 16465) +++ none/tests/cmdline1.stdout.exp (working copy) @@ -42,6 +42,10 @@ --error-exitcode= exit code to return if errors found [0=disable] --error-markers=, 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: none/tests/cmdline2.stdout.exp =================================================================== --- none/tests/cmdline2.stdout.exp (revision 16465) +++ none/tests/cmdline2.stdout.exp (working copy) @@ -42,6 +42,10 @@ --error-exitcode= exit code to return if errors found [0=disable] --error-markers=, 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]